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
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
|
-
|