filigree 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -4,5 +4,5 @@
4
4
  # Description: This file specifies the version number of Filigree.
5
5
 
6
6
  module Filigree
7
- VERSION = '0.3.0'
7
+ VERSION = '0.3.1'
8
8
  end
@@ -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
- @match_bindings = OpenStruct.new
41
-
42
- return pattern.(self, objects) if pattern.match?(objects, self)
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 = inherited_patterns
122
- @deferred = Array.new
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
@@ -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