Как имитировать Java-подобные аннотации в Ruby?

18

Как имитировать Java-подобные аннотации в ruby?

(У меня будет ответ, обобщающий Ссылка )

    
задан cibercitizen1 01.07.2010 в 13:49
источник
  • Перед этим downvoting вы можете перейти к указанному URL-адресу и проверить, что мой ответ не является его копией, а упрощение, которое может быть полезно другим. (Благодаря) –  cibercitizen1 01.07.2010 в 14:07

3 ответа

28

Это адаптировано из фрагмента кода, который я написал в ответе на другой вопрос пару недель назад, хотя это, конечно, вряд ли оригинально. Это - это хорошо известная иудия Ruby, которая используется в течение многих лет, по крайней мере, с rakes desc .

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

Вот небольшой пример:

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

И, конечно, никакой код Ruby не будет завершен без testuite:

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
    
ответ дан Jörg W Mittag 01.07.2010 в 23:08
  • Ваш код проще в использовании: он не требует аннотации, которые должны быть объявлены заранее! Вопрос: Когда вы храните аргументы аннотации, почему <code> @__last_annotation__[meth[1..-1].to_sym] = args.first</code> вместо <code>@__last_annotation__[meth.to_sym] = args</code> , я не могу поймать bot meth [1 ..- 1] или либо args.first (это не только первый элемент массив, который содержит аргументы). –  cibercitizen1 02.07.2010 в 00:05
  • @ cibercitizen1: meth [1 ..- 1] в основном говорит «все, кроме первого символа имени метода», IOW удаляет символ подчеркивания. Args.first - это просто потому, что я хочу, чтобы метод принимал только один аргумент, хэш с парными ключ-знаками аннотации. Но я должен определить method_missing, чтобы принимать произвольное количество аргументов, чтобы я мог их отправлять, если я не хочу сам обрабатывать этот метод (т. Е. Если он не начинается с подчеркивания). В конце концов, в системе могут быть другие определения method_missing для других DSL или Rails или чего-то еще. –  Jörg W Mittag 02.07.2010 в 00:43
  • @ cibercitizen1: Конечно, вы могли бы сделать что-то более умное, например, проверить, равен ли размер 1, а затем развернуть его и в противном случае просто оставить его как массив. –  Jörg W Mittag 02.07.2010 в 00:44
  • @ Йорг, спасибо вам большое! –  cibercitizen1 02.07.2010 в 09:15
  • @jwill: потребовалось 8 лет для добавления аннотаций на Java, и они могут быть добавлены только Sun, а не кем-то еще. Мне потребовалось около 10 минут и 23 строки кода, чтобы добавить аннотации к Ruby, и любой может это сделать, вам не нужно полагаться на разработчика языка. Я думаю, это довольно круто. Если вы хотите меня убедить, попробуйте выполнить аннотации в Java менее чем за 30 строк. –  Jörg W Mittag 28.05.2015 в 02:31
Показать остальные комментарии
2

Это предполагаемое использование:

Сначала вы комментируете класс.

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 следующим образом:

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

Ну. Для этого вам понадобится этот модуль

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

, и вы можете определить набор аннотаций в своем модуле:

module MyAnnotations

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

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

или определите их прямо в класс, который вы аннотируете:

create_annotation("_foobar")
    
ответ дан cibercitizen1 01.07.2010 в 14:05
2

Мое требование

  

На странице я показываю список всех экземпляров класса экземпляров для класса ABC,   и должно быть также 1 строка описания с каждым методом

Теперь я не знаю, является ли это только мной или хранит описания для всех этих методов с их именами в новой таблице в звуках БД «Super LAME»

  

Ответ - «Аннотации»

Вот как я это сделал -

  1. Модуль Аннотации, данные cibercitizen1
  2. Код для включения модуля и активации функций в желаемом классе

Класс 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. Код, отображающий данные описания (только, а не имя метода) через хэш-атрибут аннотации для коллекции экземпляров instance_methods, в представлении, имеющем доступ к классу Abc.

VIEW 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>

Надеюсь, что это поможет!

    
ответ дан nitinr708 13.01.2011 в 08:47