filigree 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f712ddbd29650262ea478bff2b9742c840929dee
4
- data.tar.gz: 130669091a1246adac6601a9ec76848ac3772fc7
3
+ metadata.gz: 3c9911e71c1aab6f164a4f49af71b888f9f17f63
4
+ data.tar.gz: dda61f8f3d3181d3787d364cf54df9a716fa9b5a
5
5
  SHA512:
6
- metadata.gz: 896a8a4972016b0166c616c7115f598e5e43cd621451765e5435f39809818cc066fb751963531db5138030ade28096a1af08866067fb2fd8750bbda0c20e2af7
7
- data.tar.gz: b008d5b0221615918e45a6a40bffa1619f19626aea7150b82a46b6ad3a8be8b7d185d80a659ed6ec9cafc79f75006f6961ac073a9c0b945755fd2f965658de25
6
+ metadata.gz: be44708b88b56c8e6d654a11e5a343319dcf14283154fdcd7acd7b33c6c125c9211b6e8e5bfa44c37c8beb51f34ccb36fbce8440251e5c0728143ccad95c0c68
7
+ data.tar.gz: 1591d525b1bbfde2709a5180ab7fd51d13a0e507036c19f73a89824798ac7e2091099fa39b9240b8939cec8dad2ccd9e41cc470fe0c4365da6f4cd4457b1663c
data/README.md CHANGED
@@ -68,12 +68,12 @@ The most basic pattern is the literal. Here, the object or objects being matche
68
68
  with(_) { :other }
69
69
  end
70
70
  end
71
-
71
+
72
72
  foo(1) # => :one
73
73
  foo(42) # => :other
74
74
  ```
75
75
 
76
- You may also match against variables. This can sometimes conflict with the next kind of pattern, which is a binding pattern. Here, the pattern will match any object, and then make the object it matched available to the *with block* via an attribute reader. This is accomplished using the method_missing callback, so if there is a variable or function with that name you might accidentally compare against a variable or returned value. To bind to a name that is already in scope you can use the {Filigree::MatchEnvironment#Bind} method. In addition, class and destructuring pattern results (see bellow) can be bound to a variable by using the {Filigree::BasicPattern#as} method.
76
+ You may also match against variables. This can sometimes conflict with the next kind of pattern, which is a binding pattern. Here, the pattern will match any object, and then make the object it matched available to the *with block* via an attribute reader. This is accomplished using the method_missing callback, so if there is a variable or function with that name you might accidentally compare against a variable or returned value. To bind to a name that is already in scope you can use either the {Filigree::MatchEnvironment#Bind} method or the `!` Symbol method. In addition, class and destructuring pattern results (see bellow) can be bound to a variable by using the {Filigree::BasicPattern#as} method.
77
77
 
78
78
  ```Ruby
79
79
  var = 42
@@ -93,6 +93,8 @@ x = 3
93
93
  # Returns 42
94
94
  match 42 do
95
95
  with(Bind(:x)) { x }
96
+ # Equivalent to the line above.
97
+ with(!:x) { x }
96
98
  with(42) { :hoopy }
97
99
  end
98
100
  ```
@@ -163,7 +165,7 @@ Filigree's implementation of the visitor pattern is built on the pattern matchin
163
165
  class Binary < Struct.new(:x, :y)
164
166
  extend Filigree::Destructurable
165
167
  include Filigree::Visitor
166
-
168
+
167
169
  def destructure(_)
168
170
  [x, y]
169
171
  end
@@ -174,11 +176,11 @@ class Mul < Binary; end
174
176
 
175
177
  class MathVisitor
176
178
  include Filigree::Visitor
177
-
179
+
178
180
  on(Add.(x, y)) do
179
181
  x + y
180
182
  end
181
-
183
+
182
184
  on(Mul.(x, y)) do
183
185
  x * y
184
186
  end
@@ -236,26 +238,26 @@ Configuration Handling
236
238
  ```Ruby
237
239
  class MyConfig
238
240
  include Filigree::Configuration
239
-
241
+
240
242
  add_option Filigree::Configuration::HELP_OPTION
241
-
243
+
242
244
  help 'Sets the target'
243
245
  required
244
246
  string_option 'target', 't'
245
-
247
+
246
248
  help 'Set the port for the target'
247
249
  default 1025
248
250
  option 'port', 'p', conversions: [:to_i]
249
-
251
+
250
252
  help 'Set credentials'
251
253
  default ['user', 'password']
252
254
  option 'credentials', 'c', conversions: [:to_s, :to_s]
253
-
255
+
254
256
  help 'Be verbose'
255
257
  bool_option 'verbose', 'v'
256
-
258
+
257
259
  auto 'next_port' { self.port + 1 }
258
-
260
+
259
261
  help 'load data from file'
260
262
  option 'file', 'f' do |f|
261
263
  process_file f
@@ -282,18 +284,18 @@ Now that we can parse configuration options, how about we handle commands?
282
284
  ```Ruby
283
285
  class MyCommands
284
286
  include Filigree::Commands
285
-
287
+
286
288
  help 'Adds two numbers together'
287
289
  param 'x', 'The first number to add'
288
290
  param 'y', 'The second number to add'
289
291
  command 'add' do |x, y|
290
292
  x.to_i + y.to_i
291
293
  end
292
-
294
+
293
295
  help 'Say hello from the command handler'
294
296
  config do
295
297
  default 'world'
296
- string_option 'subject', 's'
298
+ string_option 'subject', 's'
297
299
  end
298
300
  command 'hello' do
299
301
  "hello #{subject}"
@@ -312,7 +314,7 @@ Type Checking
312
314
 
313
315
  Filigree provides two ways to perform basic type checking at run time:
314
316
 
315
- 1. {check_type} and {check_array_type}
317
+ 1. {check\_type} and {check\_array\_type}
316
318
  2. {Filigree::TypedClass}
317
319
 
318
320
  The first option will simply check the type of an object or an array of objects. Optionally, you can assign blame to a named variable, allow the value to be nil, or perform strict checking. Strict checking uses the `instance_of?` method while non-strict checking uses `is_a?`.
@@ -322,10 +324,10 @@ The second option works like so:
322
324
  ```Ruby
323
325
  class Foo
324
326
  include Filigree::TypedClass
325
-
327
+
326
328
  typed_ivar :bar, Integer
327
329
  typed_ivar :baz, String
328
-
330
+
329
331
  default_constructor
330
332
  end
331
333
 
@@ -36,11 +36,11 @@ end
36
36
  module Filigree
37
37
  # A module the implements the abstract class and abstract method patterns.
38
38
  module AbstractClass
39
-
39
+
40
40
  ####################
41
41
  # Instance Methods #
42
42
  ####################
43
-
43
+
44
44
  # Declares a method with the given name. If it is called it will raise
45
45
  # an AbstractMethodError.
46
46
  #
@@ -49,19 +49,19 @@ module Filigree
49
49
  # @return [void]
50
50
  def abstract_method(name)
51
51
  abstract_class_name = @abstract_class.name
52
-
52
+
53
53
  define_method name do
54
54
  raise AbstractMethodError.new name, abstract_class_name
55
55
  end
56
56
  end
57
-
57
+
58
58
  # Install instance class variables in the extended class.
59
59
  #
60
60
  # @return [void]
61
61
  def install_icvars
62
62
  @abstract_class = self
63
63
  end
64
-
64
+
65
65
  # Raise an AbstractClassError if someone attempts to instantiate an
66
66
  # abstract class.
67
67
  #
@@ -75,11 +75,11 @@ module Filigree
75
75
  super
76
76
  end
77
77
  end
78
-
78
+
79
79
  #############
80
80
  # Callbacks #
81
81
  #############
82
-
82
+
83
83
  # Tell the extended class to install its instance class variables.
84
84
  #
85
85
  # @return [void]
@@ -30,11 +30,11 @@ module Filigree
30
30
  # the basic framework for larger desktop and command line applications.
31
31
  module Application
32
32
  include ClassMethodsModule
33
-
33
+
34
34
  #############
35
35
  # Constants #
36
36
  #############
37
-
37
+
38
38
  REQUIRED_METHODS = [
39
39
  :kill,
40
40
  :pause,
@@ -46,29 +46,29 @@ module Filigree
46
46
  ####################
47
47
  # Instance Methods #
48
48
  ####################
49
-
49
+
50
50
  attr_accessor :configuration
51
51
  alias :config :configuration
52
-
52
+
53
53
  def initialize
54
54
  @configuration = self.class::Configuration.new
55
-
55
+
56
56
  # Set up signal handlers.
57
57
  Signal.trap('ABRT') { self.stop }
58
58
  Signal.trap('INT') { self.stop }
59
59
  Signal.trap('QUIT') { self.stop }
60
60
  Signal.trap('TERM') { self.stop }
61
-
61
+
62
62
  Signal.trap('KILL') { self.kill }
63
-
63
+
64
64
  Signal.trap('CONT') { self.resume }
65
65
  Signal.trap('STOP') { self.pause }
66
66
  end
67
-
67
+
68
68
  #################
69
69
  # Class Methods #
70
70
  #################
71
-
71
+
72
72
  module ClassMethods
73
73
  # Check to make sure all of the required methods are defined.
74
74
  #
@@ -82,7 +82,7 @@ module Filigree
82
82
  end
83
83
  end
84
84
  end
85
-
85
+
86
86
  # Create a new instance of this application and run it.
87
87
  #
88
88
  # @return [Object]
@@ -90,11 +90,11 @@ module Filigree
90
90
  self.new.run
91
91
  end
92
92
  end
93
-
93
+
94
94
  #############
95
95
  # Callbacks #
96
96
  #############
97
-
97
+
98
98
  class << self
99
99
  alias :old_included :included
100
100
 
@@ -25,12 +25,12 @@ class Class
25
25
  def includes_module?(mod)
26
26
  self.included_modules.include?(mod)
27
27
  end
28
-
28
+
29
29
  # @return [String] Name of class without the namespace.
30
30
  def short_name
31
31
  self.name.split('::').last
32
32
  end
33
-
33
+
34
34
  # Checks to see if a Class object is a subclass of the given class.
35
35
  #
36
36
  # @param [Class] klass Class we are checking if this is a subclass of.
@@ -38,7 +38,7 @@ class Class
38
38
  # @return [Boolean] If self is a subclass of klass
39
39
  def subclass_of?(klass)
40
40
  check_type(klass, Class, 'klass')
41
-
41
+
42
42
  if (superklass = self.superclass)
43
43
  superklass == klass or superklass.subclass_of?(klass)
44
44
  else
@@ -32,7 +32,11 @@ module Filigree
32
32
  def self.included(mod)
33
33
  mod.instance_exec do
34
34
  def included(mod)
35
- mod.extend self::ClassMethods
35
+ mod.extend(self::ClassMethods) if self.const_defined?(:ClassMethods)
36
+
37
+ if self.method_defined?(:ClassVariables)
38
+ mod.instance_exec(self.method(:ClassVariables))
39
+ end
36
40
  end
37
41
  end
38
42
  end
@@ -30,11 +30,11 @@ end
30
30
  module Filigree
31
31
  module Commands
32
32
  include ClassMethodsModule
33
-
33
+
34
34
  ####################
35
35
  # Instance Methods #
36
36
  ####################
37
-
37
+
38
38
  # This will find the appropriate command and execute it.
39
39
  #
40
40
  # @param [String] line String containing the command to be processed and its arguments
@@ -42,41 +42,41 @@ module Filigree
42
42
  # @return [Object] Result of invoking the command's block
43
43
  def call(line)
44
44
  namespace, rest = self.class.get_namespace(line.split)
45
-
45
+
46
46
  if namespace == self.class.commands
47
47
  raise CommandNotFoundError, line
48
48
  end
49
-
49
+
50
50
  command = namespace[:nil]
51
-
52
- action =
51
+
52
+ action =
53
53
  if command.config
54
54
  conf_obj = command.config.new(rest)
55
55
  rest = conf_obj.rest
56
-
56
+
57
57
  -> (*args) { conf_obj.instance_exec(*args, &command.action) }
58
58
  else
59
59
  command.action
60
60
  end
61
-
61
+
62
62
  if command.action.arity < 0 or command.action.arity == rest.length
63
63
  self.instance_exec(*rest, &action)
64
64
  else
65
65
  raise ArgumentError, "Wrong number of arguments for command: #{command.name}."
66
66
  end
67
67
  end
68
-
68
+
69
69
  #################
70
70
  # Class Methods #
71
71
  #################
72
-
72
+
73
73
  module ClassMethods
74
74
  # @return [Hash<String, Hash>]
75
75
  attr_accessor :commands
76
-
76
+
77
77
  # @return [Array<Command>]
78
78
  attr_accessor :command_list
79
-
79
+
80
80
  # Add a command to the necessary internal data structures.
81
81
  #
82
82
  # @param [Command] command_obj Command to add
@@ -87,7 +87,7 @@ module Filigree
87
87
  namespace = reify_namespace(command_obj.name.split.map(&:to_sym))
88
88
  namespace[:nil] = command_obj
89
89
  end
90
-
90
+
91
91
  # Add a new command to the class. All command code is executed
92
92
  # in the context of the Commands object.
93
93
  #
@@ -97,12 +97,12 @@ module Filigree
97
97
  # @return [void]
98
98
  def command(str, &block)
99
99
  add_command Command.new(str, @help_string, @param_docs, @config, block)
100
-
100
+
101
101
  @help_string = ''
102
102
  @param_docs = Array.new
103
103
  @config = nil
104
104
  end
105
-
105
+
106
106
  # This will generate an anonymous {Configuration} class for this
107
107
  # command. After a string resolves to the next command defined
108
108
  # the remainder of the command line will be passed to an
@@ -119,7 +119,7 @@ module Filigree
119
119
  @config = Class.new { include Filigree::Configuration }
120
120
  @config.instance_exec &block
121
121
  end
122
-
122
+
123
123
  # Attaches the provided help string to the command that is
124
124
  # defined next.
125
125
  #
@@ -129,7 +129,7 @@ module Filigree
129
129
  def help(str)
130
130
  @help_string = str
131
131
  end
132
-
132
+
133
133
  # Install the instance class variables in the including class.
134
134
  #
135
135
  # @return [void]
@@ -140,7 +140,7 @@ module Filigree
140
140
  @help_string = ''
141
141
  @param_docs = Array.new
142
142
  end
143
-
143
+
144
144
  # Given a root namespace, find the namespace indicated by the
145
145
  # provided tokens.
146
146
  #
@@ -154,7 +154,7 @@ module Filigree
154
154
  [root, tokens]
155
155
  else
156
156
  curr_token = tokens.first.to_sym
157
-
157
+
158
158
  if ns = root[curr_token]
159
159
  tokens.shift
160
160
  get_namespace(tokens, root: ns)
@@ -163,17 +163,17 @@ module Filigree
163
163
  end
164
164
  end
165
165
  end
166
-
166
+
167
167
  # Add a description for a command's parameter.
168
168
  #
169
169
  # @param [String] name Name of the parameter
170
170
  # @param [String] description Description of the parameter.
171
171
  #
172
- # @return [void]
172
+ # @return [void]
173
173
  def param(name, description)
174
174
  @param_docs << [name, description]
175
175
  end
176
-
176
+
177
177
  # Find or create the namespace specified by tokens.
178
178
  #
179
179
  # @param [Array<String>] tokens Tokens specifying the namespace.
@@ -186,69 +186,69 @@ module Filigree
186
186
  root
187
187
  else
188
188
  curr_token = tokens.shift
189
-
189
+
190
190
  ns = root[curr_token]
191
191
  ns = root[curr_token] = Hash.new if ns.nil?
192
-
192
+
193
193
  reify_namespace(tokens, root: ns)
194
194
  end
195
195
  end
196
-
196
+
197
197
  #############
198
198
  # Callbacks #
199
199
  #############
200
-
200
+
201
201
  def self.extended(klass)
202
202
  klass.install_icvars
203
203
  end
204
204
  end
205
-
205
+
206
206
  #################
207
207
  # Inner Classes #
208
208
  #################
209
-
209
+
210
210
  # The POD representing a command.
211
211
  Command = Struct.new(:name, :help, :param_help, :config, :action)
212
-
212
+
213
213
  ########################
214
214
  # Pre-defined Commands #
215
215
  ########################
216
-
216
+
217
217
  # The default help command. This can be added to your class via
218
218
  # add_command.
219
219
  HELP_COMMAND = Command.new('help', 'Prints this help message.', [], nil, Proc.new do
220
220
  puts 'Usage: <command> [options] <args>'
221
221
  puts
222
222
  puts 'Commands:'
223
-
223
+
224
224
  comm_list = self.class.command_list
225
-
225
+
226
226
  sorted_comm_list = comm_list.sort { |a, b| a.name <=> b.name }
227
227
  max_length = comm_list.lazy.map { |opt| opt.name.length }.max
228
-
229
-
228
+
229
+
230
230
  sorted_comm_list.each do |comm|
231
231
  printf " % #{max_length}s", comm.name
232
-
232
+
233
233
  if comm.config
234
234
  print ' [options]'
235
235
  end
236
-
236
+
237
237
  puts comm.param_help.inject('') { |str, pair| str << " <#{pair.first}>" }
238
-
238
+
239
239
  if comm.config
240
240
  options = comm.config.options_long.values.sort { |a, b| a.long <=> b.long }
241
241
  puts Filigree::Configuration::Option.to_s(options, max_length + 4)
242
242
  end
243
-
243
+
244
244
  puts
245
-
245
+
246
246
  if !comm.param_help.empty?
247
247
  max_param_len = comm.param_help.inject(0) do |max, pair|
248
248
  param_len = pair.first.to_s.length
249
249
  max <= param_len ? param_len : max
250
250
  end
251
-
251
+
252
252
  segment_indent = max_param_len + 8
253
253
  comm.param_help.each do |name, help|
254
254
  printf " %-#{max_param_len}s - %s\n", name, help.segment(segment_indent)