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.
- data/.ruby +59 -38
- data/.yardopts +7 -0
- data/DEMO.md +242 -0
- data/{HISTORY.rdoc → HISTORY.md} +28 -7
- data/LICENSE.txt +27 -0
- data/README.md +129 -0
- data/demo/01_annotations.md +81 -0
- data/demo/03_attributes.md +14 -0
- data/demo/04_methods.md +50 -0
- data/demo/05_variables.md +45 -0
- data/{qed → demo}/applique/ae.rb +0 -0
- data/demo/applique/anise.rb +1 -0
- data/{qed/toplevel/01_annotations.qed → demo/toplevel/01_annotations.md} +5 -9
- data/demo/toplevel/03_attributes.md +20 -0
- data/lib/anise.rb +28 -45
- data/lib/anise.yml +59 -38
- data/lib/anise/annotations.rb +132 -0
- data/lib/anise/annotations/store.rb +136 -0
- data/lib/anise/annotative.rb +7 -0
- data/lib/anise/annotative/attributes.rb +147 -0
- data/lib/anise/annotative/methods.rb +131 -0
- data/lib/anise/annotative/variables.rb +99 -0
- data/lib/anise/{module.rb → core_ext.rb} +30 -0
- data/lib/anise/universal.rb +6 -0
- data/lib/anise/version.rb +17 -0
- data/test/case_annotations.rb +173 -0
- data/test/case_attributes.rb +46 -0
- data/test/case_combined_usage.rb +341 -0
- data/test/case_methods.rb +36 -0
- data/test/case_variables.rb +22 -0
- data/test/helper.rb +2 -0
- metadata +99 -98
- data/APACHE2.txt +0 -204
- data/COPYING.rdoc +0 -17
- data/README.rdoc +0 -107
- data/lib/anise/annotation.rb +0 -175
- data/lib/anise/annotator.rb +0 -82
- data/lib/anise/attribute.rb +0 -138
- data/qed/01_annotations.qed +0 -26
- data/qed/02_annotation_added.rdoc +0 -60
- data/qed/03_attributes.rdoc +0 -16
- data/qed/04_annotator.rdoc +0 -49
- data/qed/toplevel/03_attributes.rdoc +0 -20
- data/test/suite.rb +0 -8
- data/test/test_anise.rb +0 -193
- data/test/test_anise_toplevel.rb +0 -194
- data/test/test_annotations.rb +0 -136
- data/test/test_annotations_module.rb +0 -132
- data/test/test_annotations_toplevel.rb +0 -131
- data/test/test_annotator.rb +0 -26
- data/test/test_annotator_toplevel.rb +0 -28
- data/test/test_attribute.rb +0 -37
- 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,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
|