I love how meta-programmable Ruby is.
JRuby doesn't support annotations because Ruby doesn't support annotations. So what! We can extend Ruby to add something like annotations:
Given this, we can do things like the following:
class JPABean
def self.inherited(clazz)
@@annotations = {}
end
def self.anno(annotation)
@@last_annotation = annotation
end
def self.method_added(sym)
@@annotations[sym] = @@last_annotation
end
def self.get_annotation(sym)
return @@annotations[sym]
end
end
And here's some output from the resulting class:
class MyBean :sql => "SELECT * FROM stuff WHERE name = 'foo'"
def foo; end
anno :field => :hello_id
attr :hello
anno :field => :bar_in_table
attr_accessor :bar
end
So as you can see we really do have all the necessary requirements to annotate classes. Now what if we just had MyBean be a java.lang.Object extension and stuffed the annotations into the resulting generated class? We can already create real Java classes in this way, but with the above syntax and a little magic in the JRuby Java integration later, we've got annotation support in on Java classes too. This should enable things like Hibernate, JPA, and JUnit 4 to work with JRuby's Ruby-based classes. Or at least, I believe it to be possible. It just requires a little work to add annotation information to the resulting generated classes.
p MyBean.get_annotation(:foo)
p MyBean.get_annotation(:hello)
p MyBean.get_annotation(:bar=)
=>
{:sql=>"SELECT * FROM stuff WHERE name = 'foo'"}
{:field=>:hello_id}
{:field=>:bar_in_table}
I've planted the seed here and on the JRuby dev list...let's see if it germinates.
7 comments:
Hmm, I would prefer a syntax like this:
p MyBean.annotations[:foo]
So,
class JPABean
self.attr_accessor :annotations
end
class JPABean
def self.anno(annotation)
(@@last_annotation ||= {}).merge!(annotation)
end
def self.method_added(sym)
@@annotations[sym], @@last_annotation = @@last_annotation, {}
end
def self.get_annotation(sym)
return @@annotations[sym]
end
end
@zimba
Very nice! That should avoid overwriting the annotations. What if we expand the syntax a bit more like this:
def self.anno(*annotations)
(@@last_annotation ||= {}).merge! annotations
end
So this should let us do this:
anno :anno1 => 'My test anno1', :anno2 => 'My test anno2'
Also, we could expand this just a bit further, and alias the method:
alias :annotate, :anno
(since I rather like a full method name, rather than a cryptic abbreviation)
@daniel: This bit:
anno :anno1 => 'My test anno1', :anno2 => 'My test anno2'
Would actually just make `annotations' be an array with one hash. To achieve the effect you were probably going for, you'd need to do:
anno { :anno1 => 'My test anno1' }, { :anno2 => 'My test anno2' }
Where the {}s would be optional on the second hash. Otherwise, the original definition of anno would still capture both annotations as part of the same hash.
Looks pretty rockin' :-)
@antonio
Nope, I'm going for this:
anno {:anno1 => 'My Test anno1', :anno2 => 'My Test anno2'}
(curly-braces are obviously optional) Which, come to think of it would require a slightly different method signature:
def self.anno(annotation)
(@@last_annotation ||= {}).merge!(annotation)
end
Since annotation would then be a proper, multi element hash (something I sort of failed to realize on my first pass through the comments). :-) So, zimbatm was right all along...
"JRuby doesn't support annotations because Ruby doesn't support annotations."
This sounds like a jealousy in respect to groovy :)
Good starter implementation. Unfortunately it gets more complex when defining annotations in modules. Facets' Annotations addressed this. Will that work in JRuby? NOTE The lib will get an official independent release soon. That'll be a good time to try it out.
Post a Comment