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
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)
|