anise 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/anise/annotation.rb
DELETED
@@ -1,175 +0,0 @@
|
|
1
|
-
module Anise
|
2
|
-
|
3
|
-
# The Annotate module is the core of the Anise system. It provides the
|
4
|
-
# framework for annotating class or module related objects, typically
|
5
|
-
# symbols representing methods, with arbitrary metadata. These annotations
|
6
|
-
# do not do anything in themselves. They are simply data. But they can be
|
7
|
-
# put to good use. For instance an attribute validator might check for an
|
8
|
-
# annotation called :valid and test against it.
|
9
|
-
#
|
10
|
-
# == Synopsis
|
11
|
-
#
|
12
|
-
# require 'anise/annotation'
|
13
|
-
#
|
14
|
-
# class X
|
15
|
-
# include Anise::Annotation
|
16
|
-
#
|
17
|
-
# attr :a
|
18
|
-
#
|
19
|
-
# ann :a, :desc => "A Number"
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# X.ann(:a, :desc) #=> "A Number"
|
23
|
-
#
|
24
|
-
# As stated, annotations need not only annotate methods, they are
|
25
|
-
# arbitrary, so they can be used for any purpose. For example, we
|
26
|
-
# may want to annotate instance variables.
|
27
|
-
#
|
28
|
-
# class X
|
29
|
-
# include Anise::Annotation
|
30
|
-
#
|
31
|
-
# ann :@a, :valid => lambda{ |x| x.is_a?(Integer) }
|
32
|
-
#
|
33
|
-
# def validate
|
34
|
-
# instance_variables.each do |iv|
|
35
|
-
# if validator = self.class.ann(iv)[:valid]
|
36
|
-
# value = instance_variable_get(iv)
|
37
|
-
# unless validator.call(value)
|
38
|
-
# raise "Invalid value #{value} for #{iv}"
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
# end
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# Or, we could even annotate the class itself.
|
46
|
-
#
|
47
|
-
# class X
|
48
|
-
# include Anise::Annotate
|
49
|
-
#
|
50
|
-
# ann self, :valid => lambda{ |x| x.is_a?(Enumerable) }
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# Although annotations are arbitrary they are tied to the class or
|
54
|
-
# module they are defined against.
|
55
|
-
#
|
56
|
-
#--
|
57
|
-
# TODO: By using a global variable rather the definining a class
|
58
|
-
# instance variable for each class/module, it is possible to
|
59
|
-
# quicky scan all annotations for the entire system. To do
|
60
|
-
# the same without this would require scanning through
|
61
|
-
# the ObjectSpace. Should we do this?
|
62
|
-
# $annotations = Hash.new { |h,k| h[k] = {} }
|
63
|
-
#
|
64
|
-
# TODO: The ann(x).name notation is kind of nice. Would like to add that
|
65
|
-
# back-in if reasonable. This would require @annotations to be an
|
66
|
-
# OpenHash or OpenObject rather than just a Hash though.
|
67
|
-
#++
|
68
|
-
module Annotation
|
69
|
-
|
70
|
-
#
|
71
|
-
|
72
|
-
def self.included(base)
|
73
|
-
base.extend Aid
|
74
|
-
end
|
75
|
-
|
76
|
-
# Anise::Annotations Domain Language.
|
77
|
-
|
78
|
-
module Aid
|
79
|
-
|
80
|
-
# Lookup an annotation. Unlike +annotations[ref]+
|
81
|
-
# this provides a complete annotation <i>heritage</i>,
|
82
|
-
# pulling annotations of the same reference name
|
83
|
-
# from ancestor classes and modules.
|
84
|
-
|
85
|
-
def annotation(ref=nil)
|
86
|
-
return(@annotations ||= {}) if ref.nil?
|
87
|
-
|
88
|
-
ref = ref.to_sym
|
89
|
-
ann = {}
|
90
|
-
ancestors.reverse_each do |anc|
|
91
|
-
next unless anc < Annotation
|
92
|
-
if h = anc.annotations[ref]
|
93
|
-
ann.merge!(h)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
return ann
|
97
|
-
end
|
98
|
-
|
99
|
-
# Plural alias for #annotation.
|
100
|
-
|
101
|
-
alias_method :annotations, :annotation
|
102
|
-
|
103
|
-
# Set or read annotations.
|
104
|
-
|
105
|
-
def ann(ref, keys_or_class=nil, keys=nil)
|
106
|
-
return annotation(ref) unless keys_or_class or keys
|
107
|
-
|
108
|
-
if Class === keys_or_class
|
109
|
-
keys ||= {}
|
110
|
-
keys[:class] = keys_or_class
|
111
|
-
else
|
112
|
-
keys = keys_or_class
|
113
|
-
end
|
114
|
-
|
115
|
-
if Hash === keys
|
116
|
-
ref = ref.to_sym
|
117
|
-
keys = keys.inject({}){ |h,(k,v)| h[k.to_sym] = v; h} #rekey
|
118
|
-
annotations[ref] ||= {}
|
119
|
-
annotations[ref].update(keys)
|
120
|
-
# callback
|
121
|
-
annotation_added(ref) #if method_defined?(:annotation_added)
|
122
|
-
else
|
123
|
-
key = keys.to_sym
|
124
|
-
annotation(ref)[key]
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# To change an annotation's value in place for a given class or module
|
129
|
-
# it first must be duplicated, otherwise the change may effect annotations
|
130
|
-
# in the class or module's ancestors.
|
131
|
-
|
132
|
-
def ann!(ref, keys_or_class=nil, keys=nil)
|
133
|
-
#return annotation(ref) unless keys_or_class or keys
|
134
|
-
unless keys_or_class or keys
|
135
|
-
return annotations[ref] ||= {}
|
136
|
-
end
|
137
|
-
|
138
|
-
if Class === keys_or_class
|
139
|
-
keys ||= {}
|
140
|
-
keys[:class] = keys_or_class
|
141
|
-
else
|
142
|
-
keys = keys_or_class
|
143
|
-
end
|
144
|
-
|
145
|
-
if Hash === keys
|
146
|
-
ref = ref.to_sym
|
147
|
-
keys = keys.inject({}){ |h,(k,v)| h[k.to_sym] = v; h} #rekey
|
148
|
-
annotations[ref] ||= {}
|
149
|
-
annotations[ref].update(keys)
|
150
|
-
# callback
|
151
|
-
annotation_added(ref) #if method_defined?(:annotation_added)
|
152
|
-
else
|
153
|
-
key = keys.to_sym
|
154
|
-
annotations[ref] ||= {}
|
155
|
-
begin
|
156
|
-
annotations[ref][key] = annotation(ref)[key].dup
|
157
|
-
rescue TypeError
|
158
|
-
annotations[ref][key] = annotation(ref)[key]
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Callback method. This method is called for each new annotation.
|
164
|
-
|
165
|
-
def annotation_added(name)
|
166
|
-
super if defined?(super)
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
end
|
174
|
-
|
175
|
-
# Copyright (c) 2006-11-07 Thomas Sawyer
|
data/lib/anise/annotator.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
module Anise
|
2
|
-
require 'anise/annotation'
|
3
|
-
|
4
|
-
# = Annotator
|
5
|
-
#
|
6
|
-
# The Annotator module allows for the creation of <i>method annotations</i>
|
7
|
-
# which attach to the next method defined.
|
8
|
-
#
|
9
|
-
# This idiom of annotator-before-definition was popularized by
|
10
|
-
# Rake's desc/task pair. The Annotator module makes it very easy
|
11
|
-
# to add similar capabilites to any program.
|
12
|
-
#
|
13
|
-
# require 'anise/annotator'
|
14
|
-
#
|
15
|
-
# class X
|
16
|
-
# include Anise::Annotator
|
17
|
-
#
|
18
|
-
# annotator :doc
|
19
|
-
#
|
20
|
-
# doc "See what I mean?"
|
21
|
-
#
|
22
|
-
# def see
|
23
|
-
# puts "Yes, I see!"
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# X.ann(:see, :doc) #=> "See what I mean?"
|
28
|
-
#
|
29
|
-
# Note that the library uses the #method_added callback, so be sure to
|
30
|
-
# respect good practices of calling +super+ if you need to override
|
31
|
-
# this method while using Annotator.
|
32
|
-
#
|
33
|
-
#--
|
34
|
-
# TODO: Allow annotators to be inherited via module mixins.
|
35
|
-
#
|
36
|
-
# TODO: Ensure thread-safety of <code>@_pending_annotations</code> variable.
|
37
|
-
#++
|
38
|
-
module Annotator
|
39
|
-
|
40
|
-
#
|
41
|
-
def self.included(base)
|
42
|
-
base.send(:include, Annotation) #unless base.is_a?(Annotation)
|
43
|
-
base.extend Aid
|
44
|
-
end
|
45
|
-
|
46
|
-
module Aid
|
47
|
-
|
48
|
-
# Define an annotator.
|
49
|
-
def annotator(name, &block)
|
50
|
-
(class << self; self; end).module_eval do
|
51
|
-
define_method(name) do |*args|
|
52
|
-
@_pending_annotations ||= []
|
53
|
-
@_pending_annotations << [name, args, block]
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# When a method is added, run all pending annotations.
|
59
|
-
def method_added(sym)
|
60
|
-
@_pending_annotations ||= []
|
61
|
-
@_pending_annotations.each do |name, args, block|
|
62
|
-
if block
|
63
|
-
block.call(sym, *args)
|
64
|
-
else
|
65
|
-
if args.size == 1
|
66
|
-
ann(sym, name=>args.first)
|
67
|
-
else
|
68
|
-
ann(sym, name=>args)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
@_pending_annotations = []
|
73
|
-
super if defined?(super)
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
# Copyright (c) 2005,2011 Thomas Sawyer
|
data/lib/anise/attribute.rb
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
module Anise
|
2
|
-
#require 'facets/inheritor' # removed dependency
|
3
|
-
require 'anise/annotation'
|
4
|
-
require 'anise/module'
|
5
|
-
|
6
|
-
# = Annotated Attributes
|
7
|
-
#
|
8
|
-
# This framework modifies the major attr_* methods to allow easy
|
9
|
-
# addition of annotations. It modifies the built in attribute methods
|
10
|
-
# (attr, attr_reader, attr_writer and attr_accessor), to allow
|
11
|
-
# annotations to be added to them directly rather than requiring
|
12
|
-
# a separate #ann statement.
|
13
|
-
#
|
14
|
-
# require 'anise/attribute'
|
15
|
-
#
|
16
|
-
# class X
|
17
|
-
# include Anise::Attribute
|
18
|
-
#
|
19
|
-
# attr :a, :valid => lambda{ |x| x.is_a?(Integer) }
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# See annotation.rb for more information.
|
23
|
-
#
|
24
|
-
# NOTE: This library was designed to be backward compatible with
|
25
|
-
# the standard versions of the same methods.
|
26
|
-
#
|
27
|
-
module Attribute
|
28
|
-
|
29
|
-
#
|
30
|
-
def self.included(base)
|
31
|
-
base.send(:include, Annotation) #.append_features(base)
|
32
|
-
base.extend Aid
|
33
|
-
|
34
|
-
#inheritor :instance_attributes, [], :|
|
35
|
-
base_class = (class << base; self; end)
|
36
|
-
base_class.attribute_methods.each do |attr_method|
|
37
|
-
annotatable_attribute_method(base_class, attr_method)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
#
|
42
|
-
def self.annotatable_attribute_method(base, attr_method_name)
|
43
|
-
base.module_eval do
|
44
|
-
define_method(attr_method_name) do |*args|
|
45
|
-
args.flatten!
|
46
|
-
|
47
|
-
harg={}; while args.last.is_a?(Hash)
|
48
|
-
harg.update(args.pop)
|
49
|
-
end
|
50
|
-
|
51
|
-
raise ArgumentError if args.empty? and harg.empty?
|
52
|
-
|
53
|
-
if args.empty? # hash mode
|
54
|
-
harg.each { |a,h| __send__(attr_method_name,a,h) }
|
55
|
-
else
|
56
|
-
klass = harg[:class] = args.pop if args.last.is_a?(Class)
|
57
|
-
|
58
|
-
#attr_method.call(*args)
|
59
|
-
super(*args)
|
60
|
-
|
61
|
-
args.each{|a| ann(a.to_sym,harg)}
|
62
|
-
|
63
|
-
instance_attributes!.concat(args) #merge!
|
64
|
-
|
65
|
-
# Use this callback to customize for your needs.
|
66
|
-
if respond_to?(:attr_callback)
|
67
|
-
attr_callback(self, args, harg)
|
68
|
-
end
|
69
|
-
|
70
|
-
# return the names of the attributes created
|
71
|
-
return args
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# Anise::Attributes Doman Language.
|
78
|
-
module Aid
|
79
|
-
|
80
|
-
# Instance attributes, including inherited attributes.
|
81
|
-
def instance_attributes
|
82
|
-
a = []
|
83
|
-
ancestors.each do |anc|
|
84
|
-
next unless anc < Attribute
|
85
|
-
if x = anc.instance_attributes!
|
86
|
-
a |= x
|
87
|
-
end
|
88
|
-
end
|
89
|
-
return a
|
90
|
-
end
|
91
|
-
|
92
|
-
# Local instance attributes.
|
93
|
-
def instance_attributes!
|
94
|
-
@instance_attributes ||= []
|
95
|
-
end
|
96
|
-
|
97
|
-
# Return list of attributes that have a :class annotation.
|
98
|
-
#
|
99
|
-
# class MyClass
|
100
|
-
# attr_accessor :test
|
101
|
-
# attr_accessor :name, String, :doc => 'Hello'
|
102
|
-
# attr_accessor :age, Fixnum
|
103
|
-
# end
|
104
|
-
#
|
105
|
-
# MyClass.instance_attributes # => [:test, :name, :age, :body]
|
106
|
-
# MyClass.classified_attributes # => [:name, :age]
|
107
|
-
#
|
108
|
-
def classified_attributes
|
109
|
-
instance_attributes.find_all do |a|
|
110
|
-
self.ann(a, :class)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# This define a simple adjustment to #attr to allow it to handle the boolean argument and
|
115
|
-
# to be able to accept attributes. It's backward compatible and is not needed for Ruby 1.9
|
116
|
-
# which gets rid of the secondary argument.
|
117
|
-
#
|
118
|
-
def attr(*args)
|
119
|
-
args.flatten!
|
120
|
-
case args.last
|
121
|
-
when TrueClass
|
122
|
-
args.pop
|
123
|
-
attr_accessor(*args)
|
124
|
-
when FalseClass, NilClass
|
125
|
-
args.pop
|
126
|
-
attr_reader(*args)
|
127
|
-
else
|
128
|
-
attr_reader(*args)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
end
|
135
|
-
|
136
|
-
end
|
137
|
-
|
138
|
-
# Copyright (c) 2005, 2008 Thomas Sawyer
|
data/qed/01_annotations.qed
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
= Creating and Reading Annotations
|
2
|
-
|
3
|
-
Load the primary annotations library.
|
4
|
-
|
5
|
-
require 'anise/annotation'
|
6
|
-
|
7
|
-
Given a example class X we can apply annotations to it using the #ann method.
|
8
|
-
|
9
|
-
class X
|
10
|
-
include Anise::Annotation
|
11
|
-
|
12
|
-
ann :x1, :a=>1
|
13
|
-
ann :x1, :b=>2
|
14
|
-
end
|
15
|
-
|
16
|
-
We can then use #ann to lookup the set annotations.
|
17
|
-
|
18
|
-
X.ann(:x1,:a).should == 1
|
19
|
-
|
20
|
-
The #ann method is a public interface, so we can define annotation externally as well.
|
21
|
-
|
22
|
-
X.ann :x1, :a => 2
|
23
|
-
X.ann(:x1, :a).should == 2
|
24
|
-
|
25
|
-
QED.
|
26
|
-
|
@@ -1,60 +0,0 @@
|
|
1
|
-
= Annotation Added Callback
|
2
|
-
|
3
|
-
Load the annotations, which supports callbacks out of the box.
|
4
|
-
|
5
|
-
require 'anise/annotation'
|
6
|
-
|
7
|
-
Given a sample class X, we can use a standard callback method #annotation_added().
|
8
|
-
|
9
|
-
class X
|
10
|
-
include Anise::Annotation
|
11
|
-
|
12
|
-
class << self
|
13
|
-
attr :last_callback
|
14
|
-
|
15
|
-
def annotation_added(name)
|
16
|
-
@last_callback = [name, ann(name)]
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
Now if we add an annotation, we will see the callback catches it.
|
22
|
-
|
23
|
-
X.ann :x1, :a=>1
|
24
|
-
X.last_callback.should == [:x1, {:a => 1}]
|
25
|
-
|
26
|
-
We'll do it again to be sure.
|
27
|
-
|
28
|
-
X.ann :x1, :b=>2
|
29
|
-
X.last_callback.should == [:x1, {:a => 1, :b => 2}]
|
30
|
-
|
31
|
-
== Using Callbacks for Attribute Defaults
|
32
|
-
|
33
|
-
class ::Module
|
34
|
-
def annotation_added(key)
|
35
|
-
base = self
|
36
|
-
if value = ann(key, :default)
|
37
|
-
define_method(key) do
|
38
|
-
instance_variable_set("@#{key}", value) unless instance_variable_defined?("@#{key}")
|
39
|
-
base.module_eval{ attr key }
|
40
|
-
instance_variable_get("@#{key}")
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
Try it out.
|
47
|
-
|
48
|
-
class Y
|
49
|
-
include Anise::Annotation
|
50
|
-
|
51
|
-
attr :a
|
52
|
-
ann :a, :default => 10
|
53
|
-
end
|
54
|
-
|
55
|
-
x = Y.new
|
56
|
-
x.a.should == 10
|
57
|
-
x.a.should == 10
|
58
|
-
|
59
|
-
QED.
|
60
|
-
|