2010-07-01 7 views

Respuesta

27

Esto se ha adaptado de a piece of code I wrote in an answer to another question hace un par de semanas, aunque por supuesto es apenas original. Este es un conocido lenguaje Ruby, después de todo, que ha estado en uso durante muchos años, por lo menos desde rakes 's desc método.

module Annotations 
    def annotations(meth=nil) 
    return @__annotations__[meth] if meth 
    @__annotations__ 
    end 

    private 

    def method_added(m) 
    (@__annotations__ ||= {})[m] = @__last_annotation__ if @__last_annotation__ 
    @__last_annotation__ = nil 
    super 
    end 

    def method_missing(meth, *args) 
    return super unless /\A_/ =~ meth 
    @__last_annotation__ ||= {} 
    @__last_annotation__[meth[1..-1].to_sym] = args.size == 1 ? args.first : args 
    end 
end 

class Module 
    private 

    def annotate! 
    extend Annotations 
    end 
end 

Aquí hay un pequeño ejemplo:

class A 
    annotate! 

    _hello color: 'red', ancho: 23 
    _goodbye color: 'green', alto: -123 
    _foobar color: 'blew' 
    def m1; end 

    def m2; end 

    _foobar color: 'cyan' 
    def m3; end 
end 

Y, por supuesto, no hay código de Ruby estaría completa sin un banco de pruebas:

require 'test/unit' 
class TestAnnotations < Test::Unit::TestCase 
    def test_that_m1_is_annotated_with_hello_and_has_value_red 
    assert_equal 'red', A.annotations(:m1)[:hello][:color] 
    end 
    def test_that_m3_is_annotated_with_foobar_and_has_value_cyan 
    assert_equal 'cyan', A.annotations[:m3][:foobar][:color] 
    end 
    def test_that_m1_is_annotated_with_goodbye 
    assert A.annotations[:m1][:goodbye] 
    end 
    def test_that_all_annotations_are_there 
    annotations = { 
     m1: { 
     hello: { color: 'red', ancho: 23 }, 
     goodbye: { color: 'green', alto: -123 }, 
     foobar: { color: 'blew'    } 
     }, 
     m3: { 
     foobar: { color: 'cyan'    } 
     } 
    } 
    assert_equal annotations, A.annotations 
    end 
end 
+1

Su código es más fácil de usar: ¡no requiere que las anotaciones se declaren con anticipación! Una pregunta: ¿ Al almacenar los argumentos de la anotación qué @__last_annotation__[meth[1..-1].to_sym] = args.first en lugar de @__last_annotation__[meth.to_sym] = args no puedo coger la metanfetamina bot [1 ..- 1], o bien args.first (no sólo el primer elemento de es la matriz, que empaqueta los argumentos). – cibercitizen1

+1

@ cibercitizen1: El 'meth [1 ..- 1]' básicamente dice "todo menos el primer caracter del nombre del método", IOW elimina el guión bajo. El 'args.first' es solo porque solo quiero que el método tome un argumento, el hash con la anotación clave-valor-pares. Pero tengo que definir 'method_missing' para tomar una cantidad arbitraria de argumentos solo para poder enviarlos si no quiero manejar el método yo mismo (es decir, si no comienza con un guion bajo). Después de todo, podría haber otras definiciones de 'method_missing' en el sistema para otras DSL o para Rails o algo así. –

+0

@ cibercitizen1: Podría hacer algo más inteligente por supuesto, como comprobar si el tamaño es '1 'y luego desenvolverlo y dejarlo como una matriz. –

2

Ésta es la intención de uso:

Primero anotaciones en una clase.

class A 

    extend Annotations 

    extend MyAnnotations 

    create_annotation("_foobar") 

    _hello({:color=>'red', :ancho=>23}) 
    _goodbye({:color=>'green', :alto=>-123}) 
    _foobar({:color=>'blew'}) 
    def m1 
    end 

    def m2 
    end 

    _foobar({:color=>'cyan'}) 
    def m3 
    end 
end 

A continuación, le gustaría que examinamos annoations de A la siguiente manera:

anots = A.annotations 
puts anots.keys 

puts anots[:m1][:_hello][:color] 
puts anots[:m3][:_foobar][:color] 

puts anots[:m1].key?(:_goodbye) 

puts "---------------" 

anots.each do |met| # each annotated method 
    puts "-- annotated method --" 
    puts met[0] # method name 
    met[1].each do |a| # each annotation for the method 
    puts "-> " + a[0].to_s # annotation name 
    a[1].each do |par| # each pair: key-value 
     puts " key=" + par[0].to_s + " value=" + par[1].to_s 
    end 
    end 
end 

Bueno. Para ello, necesitará este módulo

module Annotations 

    @@annotation_list = {} 
    @@pending = {} 

    def method_added(met_sym) 
    #puts "-> adding " + met_sym.to_s + " to class + self.to_s 
    if @@pending.size > 0 
     #puts met_sym.to_s + " is annotated " 
     @@annotation_list[met_sym] = @@pending 
     #puts @@annotation_list 
    else 
     #puts met_sym.to_s + " is not annotated " 
    end 
    @@pending = {} 
    end 

    def annotate_method(a,b) 
    @@pending[a] = b 
    end 

    def create_annotation(anot_sym) 
    code = "def #{anot_sym.to_s}(val) 
     annotate_method(:#{anot_sym} ,val) 
     end" 
    instance_eval code 
    end 

    def annotations 
    return @@annotation_list 
    end 

end 

y se puede definir un conjunto de anotaciones en un módulo de la suya:

module MyAnnotations 

    def _goodbye(val) 
    annotate_method(:_goodbye, val) 
    end 

    def _hello(val) 
    annotate_method(:_hello, val) 
    end 
end 

o definir a la derecha en la clase que usted está anotando:

create_annotation("_foobar") 
2

Mi requisito es

En una página que muestra una lista de todos los métodos de instancia para una clase ABC, y no debe haber una descripción 1 línea también con todos los métodos

Ahora Yo no sé si es sólo conmigo o almacenar las descripciones de todos los métodos con sus nombres en una nueva tabla en la base de datos suena " súper LAME"

respuesta es - "Anotaciones"

Así es como yo lo hice -

  1. las anotaciones módulo dado por cibercitizen1
  2. código para incluir el módulo y activar la funcionalidad en la clase deseada

Clase abc.rb

class Abc 
    extend Annotations 
    create_annotation("_annotation") 

_annotation({:description=>"Info e-mail address"}) 
def info_email 
    APP_CONFIG['info_email'] 
end 

_annotation({:description=>"Location of order"}) 
def location 
    unless self.order.blank? 
     @location ||= self.order.location.description 
    end 
end 
  1. Código para mostrar las descripciones dadas (solamente, no el nombre del método) a través de la anotación de atributos de hash para la recogida de instance_methods, en la vista que tiene acceso a la clase Abc.

VISTA methods_list.html.erb

<html> 
<head> 
</head> 
<body> 
<% default_description = "Description not specified" %> 
    <% Abc.instance_methods.each do |method| %> 
    <span style="float:right"> 
    <%= (Abc.annotations[method.to_sym].present? 
    ? 
    (Abc.annotations[method.to_sym][:_annotation][:description].blank? 
    ? default_description : 
    Abc.annotations[method.to_sym][:_annotation][:description]) 
    : default_description) %> 
    </span> 

    <% end %> 
</body> 
</html> 

espero que ayude!