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 +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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c9911e71c1aab6f164a4f49af71b888f9f17f63
|
4
|
+
data.tar.gz: dda61f8f3d3181d3787d364cf54df9a716fa9b5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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. {
|
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]
|
data/lib/filigree/application.rb
CHANGED
@@ -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
|
|
data/lib/filigree/class.rb
CHANGED
@@ -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
|
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
|
data/lib/filigree/commands.rb
CHANGED
@@ -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)
|