filigree 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,7 +20,7 @@ require 'filigree/string'
20
20
  module Filigree
21
21
  module Configuration
22
22
  include ClassMethodsModule
23
-
23
+
24
24
  #############
25
25
  # Constants #
26
26
  #############
@@ -28,10 +28,10 @@ module Filigree
28
28
  ####################
29
29
  # Instance Methods #
30
30
  ####################
31
-
31
+
32
32
  # @return [Array<String>] Remaining strings that weren't used in configuration
33
33
  attr_accessor :rest
34
-
34
+
35
35
  # Dump the state of the Configuration object. This will dump the
36
36
  # state, encoded in YAML, to different destinations depending on the
37
37
  # io parameter.
@@ -54,25 +54,25 @@ module Filigree
54
54
  # @return [void]
55
55
  def dump(io = nil, *fields)
56
56
  require 'yaml'
57
-
57
+
58
58
  vals =
59
59
  if fields.empty? then self.class.options_long.keys else fields end.inject(Hash.new) do |hash, field|
60
60
  hash.tap { hash[field.to_s] = self.send(field) }
61
61
  end
62
-
62
+
63
63
  case io
64
64
  when nil
65
65
  YAML.dump vals
66
-
66
+
67
67
  when String
68
68
  File.open(io, 'w') { |file| YAML.dump vals, file }
69
-
69
+
70
70
  when IO
71
71
  YAML.dump vals, io
72
72
  end
73
73
  end
74
74
  alias :serialize :dump
75
-
75
+
76
76
  # Configures the object based on the overloaded parameter.
77
77
  #
78
78
  # @overload initialize(args)
@@ -89,28 +89,28 @@ module Filigree
89
89
  # @return [void]
90
90
  def initialize(overloaded = ARGV.clone)
91
91
  set_opts = Array.new
92
-
92
+
93
93
  case overloaded
94
94
  when Array
95
95
  handle_array_options(overloaded, set_opts)
96
-
96
+
97
97
  when String, IO
98
98
  handle_serialized_options(overloaded, set_opts)
99
99
  end
100
-
100
+
101
101
  (self.class.options_long.keys - set_opts).each do |opt_name|
102
102
  default = self.class.options_long[opt_name].default
103
103
  default = self.instance_exec(&default) if default.is_a? Proc
104
-
104
+
105
105
  self.send("#{opt_name}=", default)
106
106
  end
107
-
107
+
108
108
  # Check to make sure all the required options are set.
109
109
  self.class.required_options.each do |option|
110
110
  raise ArgumentError, "Option #{option} not set." if self.send(option).nil?
111
111
  end
112
112
  end
113
-
113
+
114
114
  # Find the appropriate option object given a string.
115
115
  #
116
116
  # @param [String] str Search string
@@ -119,49 +119,49 @@ module Filigree
119
119
  def find_option(str)
120
120
  if str[0,2] == '--'
121
121
  self.class.options_long[str[2..-1]]
122
-
122
+
123
123
  elsif str[0,1] == '-'
124
124
  self.class.options_short[str[1..-1]]
125
125
  end
126
126
  end
127
-
127
+
128
128
  # Configure the object from an array of strings.
129
129
  #
130
130
  # @param [Array<String>] argv String options
131
131
  # @param [Array<String>] set_opts List of names of options already added
132
132
  #
133
- # @return [void]
133
+ # @return [void]
134
134
  def handle_array_options(argv, set_opts)
135
135
  while str = argv.shift
136
-
136
+
137
137
  break if str == '--'
138
-
138
+
139
139
  if option = find_option(str)
140
140
  args = argv.shift(option.arity == -1 ? argv.index { |str| str[0,1] == '-' } : option.arity)
141
-
141
+
142
142
  case option.handler
143
143
  when Array
144
144
  tmp = args.zip(option.handler).map { |arg, sym| arg.send sym }
145
145
  self.send("#{option.long}=", (option.arity == 1 and tmp.length == 1) ? tmp.first : tmp)
146
-
146
+
147
147
  when Proc
148
148
  self.send("#{option.long}=", self.instance_exec(*args, &option.handler))
149
149
  end
150
-
150
+
151
151
  set_opts << option.long
152
152
  end
153
153
  end
154
-
154
+
155
155
  # Save the rest of the command line for later.
156
156
  self.rest = argv
157
157
  end
158
-
158
+
159
159
  # Configure the object from a serialization source.
160
160
  #
161
161
  # @param [String, IO] overloaded Serialization source
162
162
  # @param [Array<String>] set_opts List of names of options already added
163
163
  #
164
- # @return [void]
164
+ # @return [void]
165
165
  def handle_serialized_options(overloaded, set_opts)
166
166
  options =
167
167
  if overloaded.is_a? String
@@ -173,33 +173,35 @@ module Filigree
173
173
  else
174
174
  YAML.load overloaded
175
175
  end
176
-
176
+
177
177
  options.each do |option, val|
178
178
  set_opts << option
179
179
  self.send "#{option}=", val
180
180
  end
181
181
  end
182
-
182
+
183
183
  #################
184
184
  # Class Methods #
185
185
  #################
186
-
186
+
187
187
  module ClassMethods
188
188
  # @return [Hash<String, Option>] Hash of options with long names used as keys
189
189
  attr_reader :options_long
190
190
  # @return [Hash<String, Option>] hash of options with short name used as keys
191
191
  attr_reader :options_short
192
-
192
+
193
193
  # Add an option to the necessary data structures.
194
194
  #
195
195
  # @param [Option] opt Option to add
196
196
  #
197
197
  # @return [void]
198
198
  def add_option(opt)
199
+ attr_accessor opt.long
200
+
199
201
  @options_long[opt.long] = opt
200
202
  @options_short[opt.short] = opt unless opt.short.nil?
201
203
  end
202
-
204
+
203
205
  # Define an automatic configuration variable.
204
206
  #
205
207
  # @param [Symbol] name Name of the configuration variable
@@ -209,7 +211,7 @@ module Filigree
209
211
  def auto(name, &block)
210
212
  define_method(name, &block)
211
213
  end
212
-
214
+
213
215
  # Define a boolean option. The variable will be set to true if
214
216
  # the flag is seen and be false otherwise.
215
217
  #
@@ -221,7 +223,7 @@ module Filigree
221
223
  @next_default = false
222
224
  option(long, short) { true }
223
225
  end
224
-
226
+
225
227
  # Sets the default value for the next command. If a block is
226
228
  # provided it will be used. If not, the val parameter will be.
227
229
  #
@@ -232,7 +234,7 @@ module Filigree
232
234
  def default(val = nil, &block)
233
235
  @next_default = block ? block : val
234
236
  end
235
-
237
+
236
238
  # Sets the help string for the next command.
237
239
  #
238
240
  # @param [String] str Command help string
@@ -241,20 +243,20 @@ module Filigree
241
243
  def help(str)
242
244
  @help_string = str
243
245
  end
244
-
246
+
245
247
  # Install the instance class variables in the including class.
246
248
  #
247
249
  # @return [void]
248
250
  def install_icvars
249
- @help_string = ''
250
- @next_default = nil
251
- @next_required = false
252
- @options_long = Hash.new
253
- @options_short = Hash.new
254
- @required = Array.new
255
- @usage = ''
251
+ @help_string = ''
252
+ @next_default = nil
253
+ @next_required = false
254
+ @options_long = Hash.new
255
+ @options_short = Hash.new
256
+ @required = Array.new
257
+ @usage = ''
256
258
  end
257
-
259
+
258
260
  # Define a new option.
259
261
  #
260
262
  # @param [String] long Long option name
@@ -264,23 +266,23 @@ module Filigree
264
266
  #
265
267
  # @return [void]
266
268
  def option(long, short = nil, conversions: nil, &block)
267
-
269
+
268
270
  attr_accessor long.to_sym
269
-
271
+
270
272
  long = long.to_s
271
273
  short = short.to_s if short
272
-
274
+
273
275
  add_option Option.new(long, short, @help_string, @next_default,
274
- conversions.nil? ? block : conversions)
275
-
276
+ conversions.nil? ? block : conversions)
277
+
276
278
  @required << long.to_sym if @next_required
277
-
279
+
278
280
  # Reset state between option declarations.
279
- @help_string = ''
280
- @next_default = nil
281
+ @help_string = ''
282
+ @next_default = nil
281
283
  @next_required = false
282
284
  end
283
-
285
+
284
286
  # Mark some options as required. If no names are provided then
285
287
  # the next option to be defined is required; if names are
286
288
  # provided they are all marked as required.
@@ -295,12 +297,12 @@ module Filigree
295
297
  @required += names
296
298
  end
297
299
  end
298
-
300
+
299
301
  # @return [Array<Symbol>] Options that need to be marked as required
300
302
  def required_options
301
303
  @required
302
304
  end
303
-
305
+
304
306
  # Define an option that takes a single string argument.
305
307
  #
306
308
  # @param [String] long Long option name
@@ -310,7 +312,7 @@ module Filigree
310
312
  def string_option(long, short = nil)
311
313
  option(long, short) { |str| str }
312
314
  end
313
-
315
+
314
316
  # Add's a usage string to the entire configuration object. If
315
317
  # no string is provided the current usage string is returned.
316
318
  #
@@ -318,22 +320,22 @@ module Filigree
318
320
  #
319
321
  # @return [String] Current or new usage string
320
322
  def usage(str = nil)
321
- if str then @usage = str else @usage end
323
+ if str then @usage = str else @usage end
322
324
  end
323
-
325
+
324
326
  #############
325
327
  # Callbacks #
326
328
  #############
327
-
329
+
328
330
  def self.extended(klass)
329
331
  klass.install_icvars
330
332
  end
331
333
  end
332
-
334
+
333
335
  #################
334
336
  # Inner Classes #
335
337
  #################
336
-
338
+
337
339
  # This class represents an option that can appear in the
338
340
  # configuration.
339
341
  class Option < Struct.new(:long, :short, :help, :default, :handler)
@@ -342,11 +344,11 @@ module Filigree
342
344
  # @return [Fixnum] Number of arguments the option takes
343
345
  def arity
344
346
  case self.handler
345
- when Array then self.handler.length
346
- when Proc then self.handler.arity
347
+ when Array then self.handler.length
348
+ when Proc then self.handler.arity
347
349
  end
348
350
  end
349
-
351
+
350
352
  # Print the option information out as a string.
351
353
  #
352
354
  # Layout:
@@ -362,14 +364,14 @@ module Filigree
362
364
  def to_s(max_long, max_short, indent = 0)
363
365
  segment_indent = indent + max_long + max_short + 8
364
366
  segmented_help = self.help.segment(segment_indent)
365
-
367
+
366
368
  if self.short
367
369
  sprintf "#{' ' * indent}%-#{max_long + 3}s %-#{max_short + 1}s - %s", "--#{self.long},", '-' + self.short, segmented_help
368
370
  else
369
371
  sprintf "#{' ' * indent}%-#{max_long + max_short + 5}s - %s", '--' + self.long, segmented_help
370
372
  end
371
373
  end
372
-
374
+
373
375
  # Helper method used to print out information on a set of options.
374
376
  #
375
377
  # @param [Array<Option>] options Options to be printed
@@ -378,29 +380,29 @@ module Filigree
378
380
  # @return [String]
379
381
  def self.to_s(options, indent = 0)
380
382
  lines = []
381
-
383
+
382
384
  max_long = options.lazy.map { |opt| opt.long.length }.max
383
385
  max_short = options.lazy.map(&:short).reject { |opt| opt.nil? }.map(&:length).max
384
-
386
+
385
387
  options.each do |opt|
386
388
  lines << opt.to_s(max_long, max_short, indent)
387
389
  end
388
-
390
+
389
391
  lines.join("\n")
390
392
  end
391
393
  end
392
-
394
+
393
395
  #######################
394
396
  # Pre-defined Options #
395
397
  #######################
396
-
398
+
397
399
  # The default help option. This can be added to your class via
398
400
  # add_option.
399
401
  HELP_OPTION = Option.new('help', 'h', 'Prints this help message.', nil, Proc.new do
400
402
  puts "Usage: #{self.class.usage}"
401
403
  puts
402
404
  puts 'Options:'
403
-
405
+
404
406
  options = self.class.options_long.values.sort { |a, b| a.long <=> b.long }
405
407
  puts Option.to_s(options, 2)
406
408
 
@@ -43,7 +43,7 @@ class MatchError < RuntimeError; end
43
43
  # accidentally compare against a variable. To bind to a name that is already
44
44
  # in scope you can use the {Filigree::MatchEnvironment#Bind} method. In
45
45
  # addition, class and destructuring pattern results (see bellow) can be bound
46
- # to a variable by using the {Filigree::BasicPattern#as} method.
46
+ # to a variable by using the {Filigree::BasicPattern#as} method.
47
47
 
48
48
  # If you wish to match string patterns you may use regular expressions. Any
49
49
  # object that isn't a string will fail to match against a regular expression.
@@ -55,7 +55,7 @@ class MatchError < RuntimeError; end
55
55
  # instance of that class. If you wish to compare one regular expression to
56
56
  # another, or one class to another, you can force the comparison using the
57
57
  # {Filigree::MatchEnvironment#Literal} method.
58
- #
58
+ #
59
59
  # Destructuring patterns allow you to match against an instance of a class,
60
60
  # while simultaneously binding values stored inside the object to variables
61
61
  # in the context of the with block. A class that is destructurable must
@@ -131,7 +131,7 @@ class MatchError < RuntimeError; end
131
131
  # @a = a
132
132
  # @b = b
133
133
  # end
134
- #
134
+ #
135
135
  # def destructure(_)
136
136
  # [@a, @b]
137
137
  # end
@@ -156,9 +156,9 @@ class MatchError < RuntimeError; end
156
156
  # @return [Object] Result of evaluating the matched pattern's block
157
157
  def match(*objects, &block)
158
158
  me = Filigree::MatchEnvironment.new
159
-
159
+
160
160
  me.instance_exec &block
161
-
161
+
162
162
  me.find_match(objects)
163
163
  end
164
164
 
@@ -167,11 +167,11 @@ end
167
167
  #######################
168
168
 
169
169
  module Filigree
170
-
170
+
171
171
  ###########
172
172
  # Methods #
173
173
  ###########
174
-
174
+
175
175
  # Wrap non-pattern objects in pattern objects so they can all be treated
176
176
  # in the same way during pattern sorting and matching.
177
177
  #
@@ -188,11 +188,11 @@ module Filigree
188
188
  end
189
189
  end
190
190
  end
191
-
191
+
192
192
  #######################
193
193
  # Modules and Classes #
194
194
  #######################
195
-
195
+
196
196
  # A module indicating that an object may be destructured. The including
197
197
  # class must define the `destructure` instance method, which takes one
198
198
  # argument specifying the number of pattern elements it is being matched
@@ -207,7 +207,7 @@ module Filigree
207
207
  DestructuringPattern.new(self, Filigree::wrap_pattern_elements(pattern))
208
208
  end
209
209
  end
210
-
210
+
211
211
  # Match blocks are evaluated inside an instance of MatchEnvironment.
212
212
  class MatchEnvironment
213
213
  # Force binding to the given name
@@ -218,21 +218,21 @@ module Filigree
218
218
  def Bind(name)
219
219
  BindingPattern.new(name)
220
220
  end
221
-
221
+
222
222
  # Force a literal comparison
223
223
  #
224
224
  # @param [Object] obj Object to test equality with
225
225
  #
226
- # @return [LiteralPattern]
226
+ # @return [LiteralPattern]
227
227
  def Literal(obj)
228
228
  LiteralPattern.new(obj)
229
229
  end
230
-
230
+
231
231
  def initialize
232
232
  @patterns = Array.new
233
233
  @deferred = Array.new
234
234
  end
235
-
235
+
236
236
  # Find a match for the given objects among the defined patterns.
237
237
  #
238
238
  # @param [Array<Object>] objects Objects to be matched
@@ -242,15 +242,15 @@ module Filigree
242
242
  # @raise [MatchError] Raised if no pattern matches the objects
243
243
  def find_match(objects)
244
244
  @patterns.each do |pattern|
245
- env = OpenStruct.new
246
-
245
+ env = OpenStruct.new
246
+
247
247
  return pattern.(env, objects) if pattern.match?(objects, env)
248
248
  end
249
-
249
+
250
250
  # If we didn't find anything we raise a MatchError.
251
251
  raise MatchError
252
252
  end
253
-
253
+
254
254
  # Define a pattern in this match call.
255
255
  #
256
256
  # @see match Documentation on pattern matching
@@ -260,26 +260,26 @@ module Filigree
260
260
  #
261
261
  # @return [void]
262
262
  def with(*pattern, &block)
263
- guard = if pattern.last.is_a?(Proc) then pattern.pop end
264
-
263
+ guard = if pattern.last.is_a?(Proc) then pattern.pop end
264
+
265
265
  pattern = Filigree::wrap_pattern_elements(pattern)
266
-
266
+
267
267
  @patterns << (mp = OuterPattern.new(pattern, guard, block))
268
-
268
+
269
269
  if block
270
270
  @deferred.each { |pattern| pattern.block = block }
271
271
  @deferred.clear
272
-
272
+
273
273
  else
274
274
  @deferred << mp
275
275
  end
276
276
  end
277
277
  alias :w :with
278
-
278
+
279
279
  #############
280
280
  # Callbacks #
281
281
  #############
282
-
282
+
283
283
  # Callback used to generate wildcard and binding patterns
284
284
  def method_missing(name, *args)
285
285
  if args.empty?
@@ -289,12 +289,12 @@ module Filigree
289
289
  end
290
290
  end
291
291
  end
292
-
292
+
293
293
  # This class provides the basis for all match patterns.
294
294
  class BasicPattern
295
295
  extend AbstractClass
296
296
  include Comparable
297
-
297
+
298
298
  # Base implementation of bi-directional comparison for patterns.
299
299
  #
300
300
  # @param [BasicPattern] other Right-hand side of the comparison
@@ -302,9 +302,11 @@ module Filigree
302
302
  # @return [Integer] Value corresponding to less than, equal to, or
303
303
  # greater than the right-hand side pattern.
304
304
  def <=>(other)
305
- self.weight - other.weight
305
+ # This is performed in the non-intuitive order due to
306
+ # higher-priority patterns having lower weights.
307
+ other.weight - self.weight
306
308
  end
307
-
309
+
308
310
  # Wraps this pattern in a {BindingPattern}, causing the object that
309
311
  # this pattern matches to be bound to this name in the with block.
310
312
  #
@@ -313,30 +315,30 @@ module Filigree
313
315
  binding_pattern.tap { |bp| bp.pattern_elem = self }
314
316
  end
315
317
  end
316
-
318
+
317
319
  # A pattern that matches any object
318
320
  class WildcardPattern < BasicPattern
319
321
  include Singleton
320
-
322
+
321
323
  # Return true for any object and don't create any bindings.
322
324
  #
323
325
  # @return [true]
324
326
  def match?(_, _)
325
327
  true
326
328
  end
327
-
329
+
328
330
  def weight
329
331
  4
330
332
  end
331
333
  end
332
-
334
+
333
335
  # An abstract class that matches only a single object to a single pattern.
334
336
  class SingleObjectPattern < BasicPattern
335
337
  extend AbstractClass
336
-
338
+
337
339
  # @return [BasicPattern]
338
340
  attr_reader :pattern_elem
339
-
341
+
340
342
  # Create a new pattern with a single element.
341
343
  #
342
344
  # @param [Object] pattern_elem Object representing the pattern
@@ -344,7 +346,7 @@ module Filigree
344
346
  @pattern_elem = pattern_elem
345
347
  end
346
348
  end
347
-
349
+
348
350
  # A pattern for checking to see if an object is an instance of a given
349
351
  # class.
350
352
  class InstancePattern < SingleObjectPattern
@@ -361,10 +363,10 @@ module Filigree
361
363
  else -1
362
364
  end
363
365
  else
364
- super
366
+ super(other)
365
367
  end
366
368
  end
367
-
369
+
368
370
  # Test the object to see if the object is an instance of the given
369
371
  # class.
370
372
  #
@@ -374,12 +376,12 @@ module Filigree
374
376
  def match?(object, _)
375
377
  object.is_a?(@pattern_elem)
376
378
  end
377
-
379
+
378
380
  def weight
379
381
  3
380
382
  end
381
383
  end
382
-
384
+
383
385
  # A pattern that forces an equality comparison
384
386
  class LiteralPattern < SingleObjectPattern
385
387
  # Test the object for equality to the pattern element.
@@ -390,12 +392,12 @@ module Filigree
390
392
  def match?(object, _)
391
393
  object == @pattern_elem
392
394
  end
393
-
395
+
394
396
  def weight
395
397
  0
396
398
  end
397
399
  end
398
-
400
+
399
401
  # A pattern that tests a string against a regular expression.
400
402
  class RegexpPattern < SingleObjectPattern
401
403
  # Test the object to see if it matches the wrapped regular
@@ -410,18 +412,18 @@ module Filigree
410
412
  env.send("match_data=", md) if match
411
413
  end
412
414
  end
413
-
415
+
414
416
  def weight
415
417
  2
416
418
  end
417
419
  end
418
-
420
+
419
421
  # A pattern that binds a sub-pattern's matching object to a name in the
420
422
  # binding environment.
421
423
  class BindingPattern < SingleObjectPattern
422
-
424
+
423
425
  attr_writer :pattern_elem
424
-
426
+
425
427
  # Create a new binding pattern.
426
428
  #
427
429
  # @param [Symbol] name Name to bind to
@@ -430,12 +432,12 @@ module Filigree
430
432
  @name = name
431
433
  super(pattern_elem)
432
434
  end
433
-
435
+
434
436
  # Overridden method to prevent binding BindingPattern objects.
435
437
  def as(_, _)
436
438
  raise 'Binding a BindingPattern is not allowed.'
437
439
  end
438
-
440
+
439
441
  # Test the object for equality to the pattern element. Binds the
440
442
  # object to the binding pattern's name if it does match.
441
443
  #
@@ -446,26 +448,26 @@ module Filigree
446
448
  def match?(object, env)
447
449
  @pattern_elem.match?(object, env).tap { |match| env.send("#{@name}=", object) if match }
448
450
  end
449
-
451
+
450
452
  def weight
451
453
  @pattern_elem.weight
452
454
  end
453
455
  end
454
-
456
+
455
457
  # An abstract class that matches multiple objects to multiple patterns.
456
458
  class MultipleObjectPattern < BasicPattern
457
459
  extend AbstractClass
458
-
460
+
459
461
  # @return [Array<BasicPattern>]
460
462
  attr_reader :pattern
461
-
463
+
462
464
  # Create a new pattern with multiple elements.
463
465
  #
464
466
  # @param [Array<Object>] pattern Array of pattern elements
465
467
  def initialize(pattern)
466
468
  @pattern = pattern
467
469
  end
468
-
470
+
469
471
  # A wrapper method to sort MultipleObjectPattern objects by their
470
472
  # arity.
471
473
  #
@@ -480,7 +482,7 @@ module Filigree
480
482
  self.pattern.length - other.pattern.length
481
483
  end
482
484
  end
483
-
485
+
484
486
  # Test multiple objects against multiple pattern elements.
485
487
  #
486
488
  # @param [Object] objects Object to test pattern against
@@ -491,20 +493,20 @@ module Filigree
491
493
  @pattern.zip(objects).each do |pattern_elem, object|
492
494
  return false unless pattern_elem.match?(object, env)
493
495
  end
494
-
496
+
495
497
  true
496
-
498
+
497
499
  else
498
500
  (@pattern.length == 1 and @pattern.first == WildcardPattern.instance)
499
501
  end
500
502
  end
501
503
  end
502
-
504
+
503
505
  # The class that contains all of the pattern elements passed to a with clause.
504
506
  class OuterPattern < MultipleObjectPattern
505
507
  attr_writer :block
506
508
  attr_reader :guard
507
-
509
+
508
510
  # Specialized version of the bi-directional comparison operator.
509
511
  #
510
512
  # @param [BasicPattern] other Right-hand side of the comparison
@@ -517,7 +519,7 @@ module Filigree
517
519
  self.pattern.zip(other.pattern).inject(0) do |total, pair|
518
520
  total + (pair.first <=> pair.last)
519
521
  end <=> 0
520
-
522
+
521
523
  if comp_res == 0
522
524
  self.guard ? (other.guard ? 0 : 1) : (other.guard ? -1 : comp_res)
523
525
  else
@@ -525,7 +527,7 @@ module Filigree
525
527
  end
526
528
  end
527
529
  end
528
-
530
+
529
531
  # Create a new outer pattern with the given pattern elements, guard,
530
532
  # and block.
531
533
  #
@@ -537,7 +539,7 @@ module Filigree
537
539
  @guard = guard
538
540
  @block = block
539
541
  end
540
-
542
+
541
543
  # Call the pattern's block, passing the given objects to the block.
542
544
  #
543
545
  # @param [Object] env Environment in which to evaluate the block
@@ -545,7 +547,7 @@ module Filigree
545
547
  def call(env, objects = [])
546
548
  if @block then env.instance_exec(*objects, &@block) else nil end
547
549
  end
548
-
550
+
549
551
  # Test the objects for equality to the pattern elements.
550
552
  #
551
553
  # @param [Object] objects Objects to test pattern elements against
@@ -556,14 +558,14 @@ module Filigree
556
558
  super && (@guard.nil? or env.instance_exec(&@guard))
557
559
  end
558
560
  end
559
-
561
+
560
562
  # A pattern that matches an instance of a class and destructures it so
561
563
  # that the values contained by the object may be matched upon.
562
564
  class DestructuringPattern < MultipleObjectPattern
563
-
565
+
564
566
  # @return [Class]
565
567
  attr_reader :klass
566
-
568
+
567
569
  # Specialized version of the bi-directional comparison operator.
568
570
  #
569
571
  # @param [BasicPattern] other Right-hand side of the comparison
@@ -578,16 +580,16 @@ module Filigree
578
580
  total + (pair.first <=> pair.last)
579
581
  end / self.pattern.length
580
582
  end
581
-
583
+
582
584
  elsif self.klass.subclass_of?(other.klass) then 1
583
585
  else -1
584
586
  end
585
-
587
+
586
588
  else
587
589
  super
588
590
  end
589
591
  end
590
-
592
+
591
593
  # Create a new destructuring pattern.
592
594
  #
593
595
  # @param [Class] klass Class to match instances of. It must be destructurable.
@@ -596,7 +598,7 @@ module Filigree
596
598
  @klass = klass
597
599
  super(pattern)
598
600
  end
599
-
601
+
600
602
  # Test to see if the object is an instance of the appropriate class,
601
603
  # and if so destructure it and test it's values against the
602
604
  # sub-pattern elements.
@@ -608,7 +610,7 @@ module Filigree
608
610
  def match?(object, env)
609
611
  object.is_a?(@klass) and super(object.destructure(@pattern.length), env)
610
612
  end
611
-
613
+
612
614
  def weight
613
615
  1
614
616
  end
@@ -621,7 +623,7 @@ end
621
623
 
622
624
  class Array
623
625
  extend Filigree::Destructurable
624
-
626
+
625
627
  # Destructuring for the array class. If the array is being matched
626
628
  # against two patterns the destructuring of the array will be the first
627
629
  # element and then an array containing the rest of the values. If there
@@ -655,3 +657,12 @@ class Regexp
655
657
  binding_pattern.tap { |bp| bp.pattern_elem = Filigree::RegexpPattern.new(self) }
656
658
  end
657
659
  end
660
+
661
+ class Symbol
662
+ # Turns a symbol into a binding pattern.
663
+ #
664
+ # @return [Filigree::BindingPattern]
665
+ def !
666
+ Filigree::BindingPattern.new(self)
667
+ end
668
+ end