1 module Annotatable
2
3 def self.included(base)
4 base.extend ClassMethods
5 end
6
7 module ClassMethods
8 def self.extended(base)
9 base.instance_eval do
10 alias :inherited_without_annotatable :inherited
11 alias :inherited :inherited_with_annotatable
12 end
13 end
14
15 def annotate(*attrs)
16 options = {}
17 options = attrs.pop if attrs.last.kind_of?(Hash)
18 options.symbolize_keys!
19 inherit = options[:inherit]
20 if inherit
21 @inherited_annotations ||= []
22 @inherited_annotations += attrs
23 end
24 attrs.each do |attr|
25 class_eval %{
26 def self.#{attr}(string = nil)
27 @#{attr} = string || @#{attr}
28 end
29 def #{attr}(string = nil)
30 self.class.#{attr}(string)
31 end
32 def self.#{attr}=(string = nil)
33 #{attr}(string)
34 end
35 def #{attr}=(string = nil)
36 self.class.#{attr}=(string)
37 end
38 }
39 end
40 attrs
41 end
42
43 def inherited_with_annotatable(subclass)
44 inherited_without_annotatable(subclass)
45 (["inherited_annotations"] + (@inherited_annotations || [])).each do |t|
46 ivar = "@#{t}"
47 subclass.instance_variable_set(ivar, instance_variable_get(ivar))
48 end
49 end
50 end
51
52 end
53
54 # We don't necessarily have ActiveSupport loaded yet!
55 class Hash
56 # Return a new hash with all keys converted to symbols.
57 def symbolize_keys
58 inject({}) do |options, (key, value)|
59 options[key.to_sym || key] = value
60 options
61 end
62 end
63
64 # Destructively convert all keys to symbols.
65 def symbolize_keys!
66 self.replace(self.symbolize_keys)
67 end
68 en