anise 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.ruby +59 -38
  2. data/.yardopts +7 -0
  3. data/DEMO.md +242 -0
  4. data/{HISTORY.rdoc → HISTORY.md} +28 -7
  5. data/LICENSE.txt +27 -0
  6. data/README.md +129 -0
  7. data/demo/01_annotations.md +81 -0
  8. data/demo/03_attributes.md +14 -0
  9. data/demo/04_methods.md +50 -0
  10. data/demo/05_variables.md +45 -0
  11. data/{qed → demo}/applique/ae.rb +0 -0
  12. data/demo/applique/anise.rb +1 -0
  13. data/{qed/toplevel/01_annotations.qed → demo/toplevel/01_annotations.md} +5 -9
  14. data/demo/toplevel/03_attributes.md +20 -0
  15. data/lib/anise.rb +28 -45
  16. data/lib/anise.yml +59 -38
  17. data/lib/anise/annotations.rb +132 -0
  18. data/lib/anise/annotations/store.rb +136 -0
  19. data/lib/anise/annotative.rb +7 -0
  20. data/lib/anise/annotative/attributes.rb +147 -0
  21. data/lib/anise/annotative/methods.rb +131 -0
  22. data/lib/anise/annotative/variables.rb +99 -0
  23. data/lib/anise/{module.rb → core_ext.rb} +30 -0
  24. data/lib/anise/universal.rb +6 -0
  25. data/lib/anise/version.rb +17 -0
  26. data/test/case_annotations.rb +173 -0
  27. data/test/case_attributes.rb +46 -0
  28. data/test/case_combined_usage.rb +341 -0
  29. data/test/case_methods.rb +36 -0
  30. data/test/case_variables.rb +22 -0
  31. data/test/helper.rb +2 -0
  32. metadata +99 -98
  33. data/APACHE2.txt +0 -204
  34. data/COPYING.rdoc +0 -17
  35. data/README.rdoc +0 -107
  36. data/lib/anise/annotation.rb +0 -175
  37. data/lib/anise/annotator.rb +0 -82
  38. data/lib/anise/attribute.rb +0 -138
  39. data/qed/01_annotations.qed +0 -26
  40. data/qed/02_annotation_added.rdoc +0 -60
  41. data/qed/03_attributes.rdoc +0 -16
  42. data/qed/04_annotator.rdoc +0 -49
  43. data/qed/toplevel/03_attributes.rdoc +0 -20
  44. data/test/suite.rb +0 -8
  45. data/test/test_anise.rb +0 -193
  46. data/test/test_anise_toplevel.rb +0 -194
  47. data/test/test_annotations.rb +0 -136
  48. data/test/test_annotations_module.rb +0 -132
  49. data/test/test_annotations_toplevel.rb +0 -131
  50. data/test/test_annotator.rb +0 -26
  51. data/test/test_annotator_toplevel.rb +0 -28
  52. data/test/test_attribute.rb +0 -37
  53. data/test/test_attribute_toplevel.rb +0 -65
@@ -0,0 +1,131 @@
1
+ module Anise
2
+
3
+ module Annotative
4
+
5
+ # TODO: Ensure thread-safety of <code>@_pending_annotations</code> variable.
6
+
7
+ # The Annotator::Method module allows for the creation of annotations
8
+ # which attach to the next method defined.
9
+ #
10
+ # This idiom of annotation-before-definition was popularized by Rake's
11
+ # `desc`/`task` pair. This module can be used to add similar capabilites
12
+ # to any class or module.
13
+ #
14
+ # class X
15
+ # extend Anise::Annotative::Methods
16
+ #
17
+ # def self.doc(string)
18
+ # method_annotation(:doc => string)
19
+ # end
20
+ #
21
+ # doc "See what I mean?"
22
+ #
23
+ # def see
24
+ # puts "Yes, I see!"
25
+ # end
26
+ # end
27
+ #
28
+ # X.ann(:see, :doc) #=> "See what I mean?"
29
+ #
30
+ # One can get a bit more control over the creation of annotations
31
+ # by using a block. In this case it is up the code to actually
32
+ # create the annotation.
33
+ #
34
+ # def self.doc(string)
35
+ # method_annotation do |meth|
36
+ # ann meth, :doc => string
37
+ # end
38
+ # end
39
+ #
40
+ # Note that the library uses the #method_added callback, so be sure to
41
+ # respect good practices of calling +super+ if you need to override
42
+ # this method.
43
+ #
44
+ module Methods
45
+
46
+ include Annotations
47
+
48
+ #
49
+ # This a temporary store used to create method annotations.
50
+ #
51
+ def self.pending_annotations
52
+ @_pending_annotations ||= Hash.new{ |h,k| h[k] = [] }
53
+ end
54
+
55
+ #
56
+ # Define a method annotation.
57
+ #
58
+ # @example
59
+ # method_annotator :doc
60
+ #
61
+ # @param name [Symbol]
62
+ # Name of annotation.
63
+ #
64
+ def method_annotator(name, &block)
65
+ (class << self; self; end).module_eval do
66
+ define_method(name) do |*args|
67
+ anns = { name => (args.size > 1 ? args : args.first) }
68
+ Methods.pending_annotations[self] << [anns, block]
69
+ end
70
+ end
71
+ end
72
+
73
+ #
74
+ #
75
+ #
76
+ def annotator(name, &block)
77
+ if name.to_s.start_with?('@')
78
+ if defined?(super)
79
+ super(name, &block)
80
+ else
81
+ raise ArgumentError, "not a valid method name -- #{name}"
82
+ end
83
+ else
84
+ method_annotator(name, &block)
85
+ end
86
+ end
87
+
88
+ #
89
+ # Setup a pending method annotation.
90
+ #
91
+ # @param [Hash] annotations
92
+ # The annotation settings.
93
+ #
94
+ def method_annotation(*args, &block)
95
+ anns = (Hash === args.last ? args.pop : {})
96
+ Methods.pending_annotations[self] << [anns, block]
97
+ end
98
+
99
+ #
100
+ # When a method is added, run all pending annotations.
101
+ #
102
+ # @param [Symbol] sym
103
+ # The name of the method added.
104
+ #
105
+ def method_added(sym)
106
+ annotations = Methods.pending_annotations[self]
107
+ annotations.each do |anns, block|
108
+ if block
109
+ block.call(sym)
110
+ else
111
+ anns.each do |name, value|
112
+ if name.to_s.index('/')
113
+ name, ns = name.to_s.split('/')
114
+ else
115
+ ns = :ann
116
+ end
117
+ ann(sym/ns, name=>value)
118
+ end
119
+ end
120
+ end
121
+ Methods.pending_annotations[self] = []
122
+ super if defined?(super)
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+
129
+ end
130
+
131
+ # Copyright (c) 2006 Rubyworks. All rights reserved. (BSD-2-Clause License)
@@ -0,0 +1,99 @@
1
+ module Anise
2
+
3
+ module Annotative
4
+
5
+ # I bet you never imagined Ruby could suport `@style` annotations.
6
+ # Well, I am here to tell you otherwise.
7
+ #
8
+ # The {VariableAnnotator} module allows class instance variable to be
9
+ # used method annotations which attach to the next defined method.
10
+ #
11
+ # class X
12
+ # extend Anise::Annotative::Variables
13
+ #
14
+ # variable_annotator :@doc
15
+ # variable_annotator :@returns
16
+ #
17
+ # @doc = "See what I mean?"
18
+ # @returns = NilClass
19
+ #
20
+ # def see
21
+ # puts "Yes, I see!"
22
+ # end
23
+ # end
24
+ #
25
+ # X.ann(:see, :@doc) #=> "See what I mean?"
26
+ #
27
+ # This library uses the #method_added callback, so be sure to respect
28
+ # good practices of calling +super+ if you need to override this method.
29
+ #
30
+ # **IMPORTANT!!!** This library is an interesting expirement, but it remains
31
+ # to be determined if it makes sense for general use.
32
+ #
33
+ module Variables
34
+
35
+ include Annotations
36
+
37
+ #
38
+ # Open method annotations.
39
+ #
40
+ # @example
41
+ # variable_annotator :@doc
42
+ #
43
+ # @param ns [Symbol]
44
+ # Annotator to use. Default is `:ann`.
45
+ #
46
+ def variable_annotator(iv, &block)
47
+ # TODO: should none iv raise an error instead?
48
+ iv = "@#{iv}".to_sym if iv.to_s !~ /^@/
49
+
50
+ # TODO: use an annotation to record the annotators
51
+ #ann(:variable_annotator, iv=>block)
52
+
53
+ @_variable_annotations ||= {}
54
+ @_variable_annotations[iv] = block
55
+ end
56
+
57
+ #
58
+ def annotator(iv, &block)
59
+ if not iv.to_s.start_with?('@')
60
+ if defined?(super)
61
+ super(iv, &block)
62
+ else
63
+ raise ArgumentError, "not a valid instance variable -- #{iv}"
64
+ end
65
+ else
66
+ variable_annotator(iv, ns, &block)
67
+ end
68
+ end
69
+
70
+ #
71
+ # When a method is added, run all pending annotations.
72
+ #
73
+ def method_added(sym)
74
+ @_variable_annotations ||= {}
75
+ @_variable_annotations.each do |iv, block|
76
+ if iv.to_s.index('/')
77
+ iv, ns = iv.to_s.split('/')
78
+ else
79
+ ns = :ann
80
+ end
81
+ value = instance_variable_get(iv)
82
+ if block
83
+ block.call(sym, value)
84
+ else
85
+ ann(sym/ns, iv=>value)
86
+ end
87
+ # TODO: can we undefine the instance variable?
88
+ instance_variable_set(iv, nil)
89
+ end
90
+ super(sym) if defined?(super)
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+
99
+ # Copyright (c) 2006 Rubyworks. All rights reserved. (BSD-2-Clause License)
@@ -1,7 +1,11 @@
1
+ #require 'facets/inheritor' # removed dependency
2
+
1
3
  class Module
4
+ #
2
5
  # Module extension to return attribute methods. These are all methods
3
6
  # that start with `attr_`. This method can be overriden in special cases
4
7
  # to work with attribute annotations.
8
+ #
5
9
  def attribute_methods
6
10
  list = []
7
11
  public_methods(true).each do |m|
@@ -17,3 +21,29 @@ class Module
17
21
  end
18
22
  end
19
23
 
24
+ class Symbol
25
+ #
26
+ # Create new combination symbol with slash.
27
+ #
28
+ # @example
29
+ # :foo/:bar #=> :'foo/bar'
30
+ #
31
+ def /(other)
32
+ "#{self}/#{other}".to_sym
33
+ end
34
+ end
35
+
36
+ class String
37
+ #
38
+ # Create new combination string with slash.
39
+ #
40
+ # @example
41
+ # 'foo'/'bar' #=> 'foo/bar'
42
+ #
43
+ def /(other)
44
+ "#{self}/#{other}"
45
+ end
46
+ end
47
+
48
+ # Copyright (c) 2006 Rubyworks. All rights reserved. (BSD-2-Clause License)
49
+
@@ -0,0 +1,6 @@
1
+ require 'anise'
2
+
3
+ class Module
4
+ include Anise::Annotations
5
+ end
6
+
@@ -0,0 +1,17 @@
1
+ module Anise
2
+
3
+ #
4
+ def self.metadata
5
+ @metadata ||= (
6
+ require 'yaml'
7
+ YAML.load(File.new(File.dirname(__FILE__) + '/../anise.yml'))
8
+ )
9
+ end
10
+
11
+ #
12
+ def self.const_missing(name)
13
+ metadata[name.to_s.downcase] || super(name)
14
+ end
15
+
16
+ end
17
+
@@ -0,0 +1,173 @@
1
+ testcase Anise::Annotations do
2
+
3
+ context "annotations can be defined" do
4
+
5
+ cX = Class.new do
6
+ extend Anise::Annotations
7
+ def x1 ; end
8
+ ann :x1, :a=>1
9
+ ann :x1, :b=>2
10
+ end
11
+
12
+ test "01" do
13
+ cX.ann(:x1,:a) == cX.ann(:x1,:a)
14
+ end
15
+
16
+ test "02" do
17
+ cX.ann(:x1,:a).object_id == cX.ann(:x1,:a).object_id
18
+ end
19
+
20
+ test "03" do
21
+ cX.ann(:x1,:a) == 1
22
+ end
23
+
24
+ test "04" do
25
+ cX.ann :x1, :a => 2
26
+ cX.ann(:x1,:a) == 2
27
+ end
28
+
29
+ end
30
+
31
+ context "parent annotations pass to subclass" do
32
+
33
+ cX = Class.new do
34
+ extend Anise::Annotations
35
+ def x1 ; end
36
+ ann :x1, :a=>1
37
+ ann :x1, :b=>2
38
+ end
39
+
40
+ cY = Class.new(cX)
41
+
42
+ test "01" do
43
+ cY.ann(:x1,:a) == cY.ann(:x1,:a)
44
+ end
45
+
46
+ test "02" do
47
+ cY.ann(:x1,:a).object_id == cY.ann(:x1,:a).object_id
48
+ end
49
+
50
+ test "03" do
51
+ cY.ann(:x1,:a) == 1
52
+ end
53
+
54
+ test "04" do
55
+ cY.ann(:x1,:b) == 2
56
+ end
57
+
58
+ test "05" do
59
+ cY.ann :x1,:a => 2
60
+ cY.ann(:x1,:a) == 2 &&
61
+ cY.ann(:x1,:b) == 2
62
+ end
63
+
64
+ end
65
+
66
+ context "subclass can override parent annotation" do
67
+
68
+ cX = Class.new do
69
+ extend Anise::Annotations
70
+ ann :foo, Integer
71
+ end
72
+
73
+ cY = Class.new(cX) do
74
+ ann :foo, String
75
+ end
76
+
77
+ test "01" do
78
+ cX.ann(:foo, :class) == Integer
79
+ end
80
+
81
+ test "02" do
82
+ cY.ann(:foo, :class) == String
83
+ end
84
+
85
+ end
86
+
87
+ context "subclass can override while parent also passes thru" do
88
+
89
+ cX = Class.new do
90
+ extend Anise::Annotations
91
+ ann :foo, :doc => "hello"
92
+ ann :foo, :bar => []
93
+ end
94
+
95
+ cY = Class.new(cX) do
96
+ ann :foo, :class=>String, :doc=>"bye"
97
+ end
98
+
99
+ test "01" do
100
+ cX.ann(:foo,:doc) == "hello"
101
+ end
102
+
103
+ test "02" do
104
+ cX.ann(:foo) == {:doc=>"hello", :bar=>[]}
105
+ end
106
+
107
+ test "03" do
108
+ cX.ann(:foo,:bar) << "1"
109
+ cX.ann(:foo,:bar) == ["1"]
110
+ end
111
+
112
+ test "04" do
113
+ cY.ann(:foo,:doc) == "bye"
114
+ end
115
+
116
+ test "05" do
117
+ #Y.ann(:foo,:bar) == nil
118
+ cY.ann(:foo,:bar) == ["1"]
119
+ end
120
+
121
+ test "06" do
122
+ cY.ann(:foo, :doc => "cap")
123
+ cY.ann(:foo, :doc) == "cap"
124
+ end
125
+
126
+ test "07" do
127
+ cY.ann!(:foo,:bar) << "2"
128
+
129
+ cY.ann(:foo,:bar) == ["1", "2"] &&
130
+ cY.ann(:foo,:bar) == ["1", "2"] &&
131
+ cX.ann(:foo,:bar) == ["1"]
132
+ end
133
+
134
+ end
135
+
136
+ context "example of using annotations for validation" do
137
+
138
+ cX = Class.new do
139
+ extend Anise::Annotations
140
+
141
+ attr :a
142
+
143
+ ann :@a, :valid => lambda{ |x| x.is_a?(Integer) }
144
+ ann :a, :class => Integer
145
+
146
+ def initialize(a)
147
+ @a = a
148
+ end
149
+
150
+ def validate
151
+ instance_variables.each do |iv|
152
+ if validator = self.class.ann(iv)[:valid]
153
+ value = instance_variable_get(iv)
154
+ unless validator.call(value)
155
+ raise "Invalid value #{value} for #{iv}"
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ test "annotation class" do
163
+ cX.ann(:a, :class) == Integer
164
+ end
165
+
166
+ test "annotation validate" do
167
+ r = cX.new(1)
168
+ r.validate
169
+ end
170
+
171
+ end
172
+
173
+ end