filigree 0.3.0 → 0.3.1
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.
- checksums.yaml +4 -4
- data/README.md +20 -18
- data/lib/filigree/abstract_class.rb +7 -7
- data/lib/filigree/application.rb +12 -12
- data/lib/filigree/class.rb +3 -3
- data/lib/filigree/class_methods_module.rb +5 -1
- data/lib/filigree/commands.rb +40 -40
- data/lib/filigree/configuration.rb +72 -70
- data/lib/filigree/match.rb +82 -71
- data/lib/filigree/string.rb +8 -8
- data/lib/filigree/types.rb +10 -10
- data/lib/filigree/version.rb +1 -1
- data/lib/filigree/visitor.rb +80 -45
- data/test/tc_abstract_class.rb +16 -16
- data/test/tc_application.rb +7 -7
- data/test/tc_boolean.rb +4 -4
- data/test/tc_class.rb +9 -9
- data/test/tc_class_methods_module.rb +69 -11
- data/test/tc_commands.rb +12 -12
- data/test/tc_configuration.rb +43 -43
- data/test/tc_match.rb +72 -58
- data/test/tc_object.rb +7 -7
- data/test/tc_string.rb +3 -3
- data/test/tc_types.rb +29 -29
- data/test/tc_visitor.rb +108 -58
- metadata +54 -54
data/lib/filigree/string.rb
CHANGED
@@ -24,29 +24,29 @@ class String
|
|
24
24
|
# @param [Fixnum] max_length Maximum length per line
|
25
25
|
def segment(indent, max_length = 80)
|
26
26
|
lines = Array.new
|
27
|
-
|
27
|
+
|
28
28
|
words = self.split(/\s/)
|
29
29
|
line = words.shift
|
30
|
-
|
30
|
+
|
31
31
|
line_length = indent + line.length
|
32
|
-
|
32
|
+
|
33
33
|
words.each do |word|
|
34
34
|
new_length = line_length + 1 + word.length
|
35
|
-
|
35
|
+
|
36
36
|
if new_length < max_length
|
37
37
|
line += " #{word}"
|
38
38
|
line_length = new_length
|
39
|
-
|
39
|
+
|
40
40
|
else
|
41
41
|
lines << line
|
42
|
-
|
42
|
+
|
43
43
|
line = word
|
44
44
|
line_length = indent + word.length
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
lines << line unless line.empty?
|
49
|
-
|
49
|
+
|
50
50
|
lines.join("\n" + (' ' * indent))
|
51
51
|
end
|
52
52
|
end
|
data/lib/filigree/types.rb
CHANGED
@@ -29,7 +29,7 @@ require 'filigree/class_methods_module'
|
|
29
29
|
# @return [Object] The object passed as parameter obj
|
30
30
|
def check_type(obj, type, blame = nil, nillable = false, strict = false)
|
31
31
|
type_ok = (obj.nil? && nillable) || (strict ? obj.instance_of?(type) : obj.is_a?(type))
|
32
|
-
|
32
|
+
|
33
33
|
if type_ok
|
34
34
|
obj
|
35
35
|
else
|
@@ -57,7 +57,7 @@ end
|
|
57
57
|
def check_array_type(array, type, blame = nil, nillable = false, strict = false)
|
58
58
|
array.each do |obj|
|
59
59
|
type_ok = (obj.nil? && nillable) || (strict ? obj.instance_of?(type) : obj.is_a?(type))
|
60
|
-
|
60
|
+
|
61
61
|
if not type_ok
|
62
62
|
if blame
|
63
63
|
raise TypeError, "Parameter #{blame} must contain instances of the #{type.name} class."
|
@@ -77,7 +77,7 @@ module Filigree
|
|
77
77
|
# variables. This also provides a default constructor.
|
78
78
|
module TypedClass
|
79
79
|
include ClassMethodsModule
|
80
|
-
|
80
|
+
|
81
81
|
# Set each of the typed instance variables to its corresponding
|
82
82
|
# value.
|
83
83
|
#
|
@@ -89,7 +89,7 @@ module Filigree
|
|
89
89
|
self.send("#{name}=", val)
|
90
90
|
end
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
module ClassMethods
|
94
94
|
# Define the default constructor for the including class.
|
95
95
|
#
|
@@ -103,7 +103,7 @@ module Filigree
|
|
103
103
|
if self.class.typed_ivars.length != vals.length
|
104
104
|
raise ArgumentError, "#{self.class.typed_ivars.length} arguments expected, #{vals.length} given."
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
self.set_typed_ivars(vals)
|
108
108
|
end
|
109
109
|
else
|
@@ -113,7 +113,7 @@ module Filigree
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
# Define a new typed accessor.
|
118
118
|
#
|
119
119
|
# @param [Symbol] name Name of the accessor
|
@@ -129,7 +129,7 @@ module Filigree
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
private :define_typed_accessor
|
132
|
-
|
132
|
+
|
133
133
|
# Define a typed instance variable.
|
134
134
|
#
|
135
135
|
# @param [Symbol] name Name of the accessor
|
@@ -140,14 +140,14 @@ module Filigree
|
|
140
140
|
# @return [void]
|
141
141
|
def typed_ivar(name, type, nillable = false, strict = false)
|
142
142
|
typed_ivars << name
|
143
|
-
|
143
|
+
|
144
144
|
define_typed_accessor(name, nillable, strict, *
|
145
145
|
type.is_a?(Array) ? [type.first, method(:check_array_type)] : [type, method(:check_type)]
|
146
146
|
)
|
147
|
-
|
147
|
+
|
148
148
|
attr_reader name
|
149
149
|
end
|
150
|
-
|
150
|
+
|
151
151
|
# Return the typed instance variables for an object.
|
152
152
|
#
|
153
153
|
# @return [Array<Symbol>] Array of defined typed instance variables
|
data/lib/filigree/version.rb
CHANGED
data/lib/filigree/visitor.rb
CHANGED
@@ -20,13 +20,13 @@ require 'filigree/match'
|
|
20
20
|
module Filigree
|
21
21
|
# An implementation of the Visitor pattern.
|
22
22
|
module Visitor
|
23
|
-
|
23
|
+
|
24
24
|
include ClassMethodsModule
|
25
|
-
|
25
|
+
|
26
26
|
####################
|
27
27
|
# Instance Methods #
|
28
28
|
####################
|
29
|
-
|
29
|
+
|
30
30
|
# Find the correct pattern and execute its block on the provided
|
31
31
|
# objects.
|
32
32
|
#
|
@@ -34,41 +34,57 @@ module Filigree
|
|
34
34
|
#
|
35
35
|
# @return [Object] Result of calling the matched pattern's block
|
36
36
|
#
|
37
|
-
# @raise [MatchError] Raised when no matching pattern is found
|
37
|
+
# @raise [MatchError] Raised when no matching pattern is found and
|
38
|
+
# strict matching is enabled.
|
38
39
|
def visit(*objects)
|
40
|
+
# FIXME: A dirty hack. Find a better place to initialize this.
|
41
|
+
@match_bindings ||= Array.new
|
42
|
+
|
43
|
+
@match_bindings.push OpenStruct.new
|
44
|
+
|
39
45
|
self.class.patterns.each do |pattern|
|
40
|
-
|
41
|
-
|
42
|
-
|
46
|
+
|
47
|
+
# FIXME: Make these take their arguments in the same order
|
48
|
+
if pattern.match?(objects, self)
|
49
|
+
result = pattern.(self, objects)
|
50
|
+
@match_bindings.pop
|
51
|
+
return result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@match_bindings.pop
|
56
|
+
|
57
|
+
if self.class.strict_match?
|
58
|
+
# If we didn't find anything we raise a MatchError.
|
59
|
+
raise MatchError
|
60
|
+
else
|
61
|
+
nil
|
43
62
|
end
|
44
|
-
|
45
|
-
# If we didn't find anything we raise a MatchError.
|
46
|
-
raise MatchError
|
47
63
|
end
|
48
|
-
|
64
|
+
|
49
65
|
#############
|
50
66
|
# Callbacks #
|
51
67
|
#############
|
52
|
-
|
68
|
+
|
53
69
|
# This is used to get and set binding names
|
54
70
|
def method_missing(name, *args)
|
55
|
-
if args.empty? and @match_bindings.respond_to?(name)
|
56
|
-
@match_bindings.send(name)
|
71
|
+
if args.empty? and @match_bindings.last.respond_to?(name)
|
72
|
+
@match_bindings.last.send(name)
|
57
73
|
elsif name.to_s[-1] == '=' and args.length == 1
|
58
|
-
@match_bindings.send(name, *args)
|
74
|
+
@match_bindings.last.send(name, *args)
|
59
75
|
else
|
60
76
|
super(name, *args)
|
61
77
|
end
|
62
78
|
end
|
63
|
-
|
79
|
+
|
64
80
|
#################
|
65
81
|
# Class Methods #
|
66
82
|
#################
|
67
|
-
|
83
|
+
|
68
84
|
module ClassMethods
|
69
|
-
|
85
|
+
|
70
86
|
attr_reader :patterns
|
71
|
-
|
87
|
+
|
72
88
|
# Force a name binding.
|
73
89
|
#
|
74
90
|
# @param [Symbol] name Name to bind to
|
@@ -77,7 +93,7 @@ module Filigree
|
|
77
93
|
def Bind(name)
|
78
94
|
BindingPattern.new(name)
|
79
95
|
end
|
80
|
-
|
96
|
+
|
81
97
|
# Force a literal comparison.
|
82
98
|
#
|
83
99
|
# @param [Object] obj Object to be comapred against
|
@@ -86,7 +102,7 @@ module Filigree
|
|
86
102
|
def Literal(obj)
|
87
103
|
LiteralPattern.new(obj)
|
88
104
|
end
|
89
|
-
|
105
|
+
|
90
106
|
# Inserts a new pattern in the appropriate place in the patterns
|
91
107
|
# list.
|
92
108
|
#
|
@@ -100,10 +116,10 @@ module Filigree
|
|
100
116
|
return
|
101
117
|
end
|
102
118
|
end
|
103
|
-
|
119
|
+
|
104
120
|
@patterns << new_pat
|
105
121
|
end
|
106
|
-
|
122
|
+
|
107
123
|
# A callback used to pass patterns declared in a parent class to
|
108
124
|
# a subclass.
|
109
125
|
#
|
@@ -113,15 +129,16 @@ module Filigree
|
|
113
129
|
def inherited(klass)
|
114
130
|
klass.install_icvars(@patterns.clone)
|
115
131
|
end
|
116
|
-
|
132
|
+
|
117
133
|
# Install the instance class variables in the including class.
|
118
134
|
#
|
119
135
|
# @return [void]
|
120
136
|
def install_icvars(inherited_patterns = Array.new)
|
121
|
-
@patterns
|
122
|
-
@deferred
|
137
|
+
@patterns = inherited_patterns
|
138
|
+
@deferred = Array.new
|
139
|
+
@strict_match = false
|
123
140
|
end
|
124
|
-
|
141
|
+
|
125
142
|
# Define a pattern for this visitor.
|
126
143
|
#
|
127
144
|
# @see match Pattern matching description
|
@@ -131,24 +148,42 @@ module Filigree
|
|
131
148
|
#
|
132
149
|
# @return [void]
|
133
150
|
def on(*pattern, &block)
|
134
|
-
guard = if pattern.last.is_a?(Proc) then pattern.pop end
|
135
|
-
|
151
|
+
guard = if pattern.last.is_a?(Proc) then pattern.pop end
|
152
|
+
|
136
153
|
pattern = Filigree::wrap_pattern_elements(pattern)
|
137
154
|
add_pattern (mp = OuterPattern.new(pattern, guard, block))
|
138
|
-
|
155
|
+
|
139
156
|
if block
|
140
157
|
@deferred.each { |pattern| pattern.block = block }
|
141
158
|
@deferred.clear
|
142
|
-
|
159
|
+
|
143
160
|
else
|
144
161
|
@deferred << mp
|
145
162
|
end
|
146
163
|
end
|
147
|
-
|
164
|
+
|
165
|
+
# Tell the visitor that it must raise an exception if no match is
|
166
|
+
# found.
|
167
|
+
#
|
168
|
+
# @param [Boolean] bool Raise an exception or not.
|
169
|
+
#
|
170
|
+
# @return [void]
|
171
|
+
def strict_match(bool)
|
172
|
+
@strict_match = bool
|
173
|
+
end
|
174
|
+
|
175
|
+
# Accessor for the strict match member.
|
176
|
+
#
|
177
|
+
# @return [Boolean] The value of the class's @strict_match
|
178
|
+
# instance variable.
|
179
|
+
def strict_match?
|
180
|
+
@strict_match
|
181
|
+
end
|
182
|
+
|
148
183
|
#############
|
149
184
|
# Callbacks #
|
150
185
|
#############
|
151
|
-
|
186
|
+
|
152
187
|
# Used to generate wildcard and binding patterns.
|
153
188
|
def method_missing(name, *args)
|
154
189
|
if args.empty?
|
@@ -157,19 +192,19 @@ module Filigree
|
|
157
192
|
super(name, *args)
|
158
193
|
end
|
159
194
|
end
|
160
|
-
|
195
|
+
|
161
196
|
def self.extended(klass)
|
162
197
|
klass.install_icvars
|
163
198
|
end
|
164
199
|
end
|
165
200
|
end
|
166
|
-
|
201
|
+
|
167
202
|
# This class can be used to call multiple visitors on an object at once.
|
168
203
|
# This could potentialy reduce the number of times data structures are
|
169
204
|
# traversed.
|
170
205
|
class TourGuide
|
171
206
|
attr_reader :visitors
|
172
|
-
|
207
|
+
|
173
208
|
# Call each visitor on the specified objects.
|
174
209
|
#
|
175
210
|
# @param [Object] objects Objects to be visited
|
@@ -178,7 +213,7 @@ module Filigree
|
|
178
213
|
def visit(*objects)
|
179
214
|
@visitors.each { |visitor| visitor.visit(*objects) }
|
180
215
|
end
|
181
|
-
|
216
|
+
|
182
217
|
# Construct a tour guide for a list of visitors.
|
183
218
|
#
|
184
219
|
# @param [Visitor] visitors List of visitors
|
@@ -186,12 +221,12 @@ module Filigree
|
|
186
221
|
@visitors = visitors
|
187
222
|
end
|
188
223
|
end
|
189
|
-
|
224
|
+
|
190
225
|
# This module provides a default implementation of three common traversal
|
191
226
|
# patterns: pre-order, post-order, and in-order (level-order). The
|
192
227
|
# including class must implement the `children` function.
|
193
228
|
module Visitable
|
194
|
-
|
229
|
+
|
195
230
|
# Visit this object with the provided visitor in pre-, post-, or
|
196
231
|
# in-order traversal.
|
197
232
|
#
|
@@ -203,18 +238,18 @@ module Filigree
|
|
203
238
|
case method
|
204
239
|
when :preorder
|
205
240
|
visitor.visit(self)
|
206
|
-
children.compact.each { |child| child.visit(visitor, :preorder) }
|
207
|
-
|
241
|
+
children.flatten.compact.each { |child| child.visit(visitor, :preorder) }
|
242
|
+
|
208
243
|
when :inorder
|
209
244
|
nodes = [self]
|
210
|
-
|
245
|
+
|
211
246
|
while node = nodes.shift
|
212
|
-
nodes += node.children.compact
|
247
|
+
nodes += node.children.flatten.compact
|
213
248
|
visitor.visit(node)
|
214
249
|
end
|
215
|
-
|
250
|
+
|
216
251
|
when :postorder
|
217
|
-
children.compact.each { |child| child.visit(visitor, :postorder) }
|
252
|
+
children.flatten.compact.each { |child| child.visit(visitor, :postorder) }
|
218
253
|
visitor.visit(self)
|
219
254
|
end
|
220
255
|
end
|
data/test/tc_abstract_class.rb
CHANGED
@@ -20,54 +20,54 @@ require 'filigree/abstract_class'
|
|
20
20
|
class AbstractClassTester < Minitest::Test
|
21
21
|
class Foo
|
22
22
|
extend Filigree::AbstractClass
|
23
|
-
|
23
|
+
|
24
24
|
abstract_method :foo
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
class Bar < Foo; end
|
28
|
-
|
28
|
+
|
29
29
|
class Bam < Foo
|
30
30
|
def foo
|
31
31
|
true
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
class Baf < Foo
|
36
36
|
extend Filigree::AbstractClass
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
class Zap < Baf; end
|
40
|
-
|
40
|
+
|
41
41
|
def setup
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def test_abstract_method
|
45
45
|
assert_raises(AbstractMethodError) { Bar.new.foo }
|
46
|
-
|
46
|
+
|
47
47
|
Bam.new.foo
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
def test_instantiate_abstract_class
|
51
51
|
assert_raises(AbstractClassError) { Foo.new }
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
def test_instantiate_subclass
|
55
|
-
Bar.new
|
55
|
+
Bar.new
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
def test_multi_level_abstract_hierarchy
|
59
59
|
assert_raises(AbstractClassError) { Baf.new }
|
60
|
-
|
60
|
+
|
61
61
|
Zap.new
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
def test_multiple_hierarchies
|
65
65
|
baf = Class.new { extend Filigree::AbstractClass }
|
66
66
|
baz = Class.new(baf)
|
67
|
-
|
67
|
+
|
68
68
|
assert_raises(AbstractClassError) { Foo.new }
|
69
69
|
assert_raises(AbstractClassError) { baf.new }
|
70
|
-
|
70
|
+
|
71
71
|
Bar.new
|
72
72
|
baz.new
|
73
73
|
end
|