anise 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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