How to simulate Java-like annotations in Ruby?

后端 未结 3 1037
太阳男子
太阳男子 2020-12-05 14:37

How to simulate Java-like annotations in ruby?

(We\'ll I have the answer, generalizing http://bens.me.uk/2009/java-style-annotations-in-ruby)

相关标签:
3条回答
  • 2020-12-05 14:47

    This is the intended usage:

    First you annotate a class.

    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
    

    Then you would like do inspect A's annoations like this:

    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
    

    Well. To do that, you will need this module

    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
    

    and you can define a set of annotations in a module of yours:

    module MyAnnotations
    
      def _goodbye(val)
        annotate_method(:_goodbye, val)
      end
    
      def _hello(val)
        annotate_method(:_hello, val)
      end
    end
    

    or define them right into the class you are annotating:

    create_annotation("_foobar")
    
    0 讨论(0)
  • 2020-12-05 15:04

    This is adapted from a piece of code I wrote in an answer to another question a couple of weeks ago, although it is of course hardly original. This is a well-known Ruby idiom, after all, which has been in use for many years, at least since rakes's desc method.

    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
    

    Here's a small example:

    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
    

    And of course no Ruby code would be complete without a testsuite:

    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
    
    0 讨论(0)
  • 2020-12-05 15:06

    My requirement is

    On a page I display a list of all instance_methods for a Class ABC, and there should be a 1 line description too with every method

    Now I don' know if it is just me or storing the descriptions for all those methods with their names in a new table in the DB sounds "Super LAME"

    Answer is - "Annotations"

    Here's how I did it -

    1. The module Annotations given by cibercitizen1
    2. Code to include the module and activate the functionality in the desired class

    Class 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. Code to display the given descriptions (only, not the method name) through the annotation attribute hash for the collection of instance_methods, in the view which has access to the class 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>
    

    Hope it helps!

    0 讨论(0)
提交回复
热议问题