filigree 0.1.2
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 +7 -0
- data/AUTHORS +1 -0
- data/LICENSE +12 -0
- data/README.md +336 -0
- data/Rakefile +101 -0
- data/lib/filigree/abstract_class.rb +90 -0
- data/lib/filigree/application.rb +107 -0
- data/lib/filigree/array.rb +35 -0
- data/lib/filigree/boolean.rb +40 -0
- data/lib/filigree/class.rb +48 -0
- data/lib/filigree/class_methods_module.rb +40 -0
- data/lib/filigree/commands.rb +261 -0
- data/lib/filigree/configuration.rb +411 -0
- data/lib/filigree/match.rb +499 -0
- data/lib/filigree/object.rb +40 -0
- data/lib/filigree/request_file.rb +33 -0
- data/lib/filigree/string.rb +52 -0
- data/lib/filigree/types.rb +159 -0
- data/lib/filigree/version.rb +8 -0
- data/lib/filigree/visitor.rb +195 -0
- data/lib/filigree.rb +27 -0
- data/test/tc_abstract_class.rb +74 -0
- data/test/tc_application.rb +53 -0
- data/test/tc_array.rb +28 -0
- data/test/tc_boolean.rb +38 -0
- data/test/tc_class.rb +45 -0
- data/test/tc_class_methods_module.rb +71 -0
- data/test/tc_commands.rb +78 -0
- data/test/tc_configuration.rb +173 -0
- data/test/tc_match.rb +307 -0
- data/test/tc_object.rb +43 -0
- data/test/tc_string.rb +36 -0
- data/test/tc_types.rb +116 -0
- data/test/tc_visitor.rb +236 -0
- data/test/ts_filigree.rb +33 -0
- metadata +247 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Filigree
|
3
|
+
# Date: 2013/05/04
|
4
|
+
# Description: Class extensions for dealing with integers and booleans.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
# Standard Library
|
11
|
+
|
12
|
+
# Filigree
|
13
|
+
|
14
|
+
#######################
|
15
|
+
# Classes and Modules #
|
16
|
+
#######################
|
17
|
+
|
18
|
+
# Extra boolean support for the Integer class.
|
19
|
+
class Integer
|
20
|
+
# @return [Boolean] This Integer as a Boolean value.
|
21
|
+
def to_bool
|
22
|
+
self != 0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Extra boolean support for the TrueClass class.
|
27
|
+
class TrueClass
|
28
|
+
# @return [1]
|
29
|
+
def to_i
|
30
|
+
1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Extra boolean support for the FalseClass class.
|
35
|
+
class FalseClass
|
36
|
+
# @return [0]
|
37
|
+
def to_i
|
38
|
+
0
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Filigree
|
3
|
+
# Date: 2013/05/04
|
4
|
+
# Description: Class extensions for the Class class.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
# Standard Library
|
11
|
+
|
12
|
+
# Filigree
|
13
|
+
require 'filigree/types'
|
14
|
+
|
15
|
+
#######################
|
16
|
+
# Classes and Modules #
|
17
|
+
#######################
|
18
|
+
|
19
|
+
class Class
|
20
|
+
# Checks for module inclusion.
|
21
|
+
#
|
22
|
+
# @param [Module] mod Module to check the inclusion of.
|
23
|
+
#
|
24
|
+
# @return [Boolean] If the module was included
|
25
|
+
def includes_module?(mod)
|
26
|
+
self.included_modules.include?(mod)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [String] Name of class without the namespace.
|
30
|
+
def short_name
|
31
|
+
self.name.split('::').last
|
32
|
+
end
|
33
|
+
|
34
|
+
# Checks to see if a Class object is a subclass of the given class.
|
35
|
+
#
|
36
|
+
# @param [Class] klass Class we are checking if this is a subclass of.
|
37
|
+
#
|
38
|
+
# @return [Boolean] If self is a subclass of klass
|
39
|
+
def subclass_of?(klass)
|
40
|
+
check_type(klass, Class, 'klass')
|
41
|
+
|
42
|
+
if (superklass = self.superclass)
|
43
|
+
superklass == klass or superklass.subclass_of?(klass)
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Filigree
|
3
|
+
# Date: 2013/05/15
|
4
|
+
# Description: A module to automatically extend classes with an inner module.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
# Standard Library
|
11
|
+
|
12
|
+
# Filigree
|
13
|
+
|
14
|
+
##########
|
15
|
+
# Errors #
|
16
|
+
##########
|
17
|
+
|
18
|
+
###########
|
19
|
+
# Methods #
|
20
|
+
###########
|
21
|
+
|
22
|
+
#######################
|
23
|
+
# Classes and Modules #
|
24
|
+
#######################
|
25
|
+
|
26
|
+
module Filigree
|
27
|
+
# Including this in a module will cause any class that includes the client
|
28
|
+
# module to also extend itself with the <client module>::ClassMethods module.
|
29
|
+
# If this module is not defined a NameError will be thrown when the client
|
30
|
+
# module is included.
|
31
|
+
module Filigree::ClassMethodsModule
|
32
|
+
def self.included(mod)
|
33
|
+
mod.instance_exec do
|
34
|
+
def included(mod)
|
35
|
+
mod.extend self::ClassMethods
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Filigree
|
3
|
+
# Date: 2013/05/14
|
4
|
+
# Description: Easy application configuration.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
# Standard Library
|
11
|
+
|
12
|
+
# Filigree
|
13
|
+
require 'filigree/array'
|
14
|
+
require 'filigree/class_methods_module'
|
15
|
+
require 'filigree/configuration'
|
16
|
+
|
17
|
+
##########
|
18
|
+
# Errors #
|
19
|
+
##########
|
20
|
+
|
21
|
+
class CommandNotFoundError < RuntimeError
|
22
|
+
def initialize(line)
|
23
|
+
super "No command found for '#{line}'"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#######################
|
28
|
+
# Classes and Modules #
|
29
|
+
#######################
|
30
|
+
|
31
|
+
module Filigree
|
32
|
+
module Commands
|
33
|
+
include ClassMethodsModule
|
34
|
+
|
35
|
+
####################
|
36
|
+
# Instance Methods #
|
37
|
+
####################
|
38
|
+
|
39
|
+
# This will find the appropriate command and execute it.
|
40
|
+
#
|
41
|
+
# @param [String] line String containing the command to be processed and its arguments
|
42
|
+
#
|
43
|
+
# @return [Object] Result of invoking the command's block
|
44
|
+
def call(line)
|
45
|
+
namespace, rest = self.class.get_namespace(line.split)
|
46
|
+
|
47
|
+
if namespace == self.class.commands
|
48
|
+
raise CommandNotFoundError, line
|
49
|
+
end
|
50
|
+
|
51
|
+
command = namespace[:nil]
|
52
|
+
|
53
|
+
action =
|
54
|
+
if command.config
|
55
|
+
conf_obj = command.config.new(rest)
|
56
|
+
rest = conf_obj.rest
|
57
|
+
|
58
|
+
-> (*args) { conf_obj.instance_exec(*args, &command.action) }
|
59
|
+
else
|
60
|
+
command.action
|
61
|
+
end
|
62
|
+
|
63
|
+
if command.action.arity < 0 or command.action.arity == rest.length
|
64
|
+
self.instance_exec(*rest, &action)
|
65
|
+
else
|
66
|
+
raise ArgumentError, "Wrong number of arguments for command: #{command.name}."
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#################
|
71
|
+
# Class Methods #
|
72
|
+
#################
|
73
|
+
|
74
|
+
module ClassMethods
|
75
|
+
# @return [Hash<String, Hash>]
|
76
|
+
attr_accessor :commands
|
77
|
+
|
78
|
+
# @return [Array<Command>]
|
79
|
+
attr_accessor :command_list
|
80
|
+
|
81
|
+
# Add a command to the necessary internal data structures.
|
82
|
+
#
|
83
|
+
# @param [Command] command_obj Command to add
|
84
|
+
#
|
85
|
+
# @return [void]
|
86
|
+
def add_command(command_obj)
|
87
|
+
@command_list << command_obj
|
88
|
+
namespace = reify_namespace(command_obj.name.split.map(:to_sym))
|
89
|
+
namespace[:nil] = command_obj
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add a new command to the class. All command code is executed
|
93
|
+
# in the context of the Commands object.
|
94
|
+
#
|
95
|
+
# @param [String] str Name of the command
|
96
|
+
# @param [Proc] block Code to be executed when the command is run
|
97
|
+
#
|
98
|
+
# @return [void]
|
99
|
+
def command(str, &block)
|
100
|
+
add_command Command.new(str, @help_string, @param_docs, @config, block)
|
101
|
+
|
102
|
+
@help_string = ''
|
103
|
+
@param_docs = Array.new
|
104
|
+
@config = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# This will generate an anonymous {Configuration} class for this
|
108
|
+
# command. After a string resolves to the next command defined
|
109
|
+
# the remainder of the command line will be passed to an
|
110
|
+
# instance of this Configuration class. Any remaining text is
|
111
|
+
# then provided to the command as usual.
|
112
|
+
#
|
113
|
+
# The variables defined in the configuration class are available
|
114
|
+
# in the command's block.
|
115
|
+
#
|
116
|
+
# @param [Proc] block Body of the {Configuration} class
|
117
|
+
#
|
118
|
+
# @return [void]
|
119
|
+
def config(&block)
|
120
|
+
@config = Class.new { include Filigree::Configuration }
|
121
|
+
@config.instance_exec &block
|
122
|
+
end
|
123
|
+
|
124
|
+
# Attaches the provided help string to the command that is
|
125
|
+
# defined next.
|
126
|
+
#
|
127
|
+
# @param [String] str Help string for the next command
|
128
|
+
#
|
129
|
+
# @return [void]
|
130
|
+
def help(str)
|
131
|
+
@help_string = str
|
132
|
+
end
|
133
|
+
|
134
|
+
# Install the instance class variables in the including class.
|
135
|
+
#
|
136
|
+
# @return [void]
|
137
|
+
def install_icvars
|
138
|
+
@commands = Hash.new
|
139
|
+
@command_list = Array.new
|
140
|
+
@config = nil
|
141
|
+
@help_string = ''
|
142
|
+
@param_docs = Array.new
|
143
|
+
end
|
144
|
+
|
145
|
+
# Given a root namespace, find the namespace indicated by the
|
146
|
+
# provided tokens.
|
147
|
+
#
|
148
|
+
# @param [Array<String>] tokens String tokens specifying the namespace
|
149
|
+
# @param [Hash<Symbol, Hash>] root Root namespace
|
150
|
+
#
|
151
|
+
# @return [Array<(Hash<Symbol, Hash>, Array<String>)>]
|
152
|
+
# The requested namespace and the remainder of the tokens.
|
153
|
+
def get_namespace(tokens, root: @commands)
|
154
|
+
if tokens.empty?
|
155
|
+
[root, tokens]
|
156
|
+
else
|
157
|
+
curr_token = tokens.first.to_sym
|
158
|
+
|
159
|
+
if ns = root[curr_token]
|
160
|
+
tokens.shift
|
161
|
+
get_namespace(tokens, root: ns)
|
162
|
+
else
|
163
|
+
[root, tokens]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Add a description for a command's parameter.
|
169
|
+
#
|
170
|
+
# @param [String] name Name of the parameter
|
171
|
+
# @param [String] description Description of the parameter.
|
172
|
+
#
|
173
|
+
# @return [void]
|
174
|
+
def param(name, description)
|
175
|
+
@param_docs << [name, description]
|
176
|
+
end
|
177
|
+
|
178
|
+
# Find or create the namespace specified by tokens.
|
179
|
+
#
|
180
|
+
# @param [Array<String>] tokens Tokens specifying the namespace.
|
181
|
+
# @param [Hash<Symbol, Hash>] root Root namespace
|
182
|
+
#
|
183
|
+
# @return [Array<(Hash<Symbol, Hash>, Array<String>)>]
|
184
|
+
# The requested namespace and the remainder of the tokens.
|
185
|
+
def reify_namespace(tokens, root: @commands)
|
186
|
+
if tokens.empty?
|
187
|
+
root
|
188
|
+
else
|
189
|
+
curr_token = tokens.shift
|
190
|
+
|
191
|
+
ns = root[curr_token]
|
192
|
+
ns = root[curr_token] = Hash.new if ns.nil?
|
193
|
+
|
194
|
+
reify_namespace(tokens, root: ns)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
#############
|
199
|
+
# Callbacks #
|
200
|
+
#############
|
201
|
+
|
202
|
+
def self.extended(klass)
|
203
|
+
klass.install_icvars
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
#################
|
208
|
+
# Inner Classes #
|
209
|
+
#################
|
210
|
+
|
211
|
+
# The POD representing a command.
|
212
|
+
Command = Struct.new(:name, :help, :param_help, :config, :action)
|
213
|
+
|
214
|
+
########################
|
215
|
+
# Pre-defined Commands #
|
216
|
+
########################
|
217
|
+
|
218
|
+
# The default help command. This can be added to your class via
|
219
|
+
# add_command.
|
220
|
+
HELP_COMMAND = Command.new('help', 'Prints this help message.', [], nil, Proc.new do
|
221
|
+
puts 'Usage: <command> [options] <args>'
|
222
|
+
puts
|
223
|
+
puts 'Commands:'
|
224
|
+
|
225
|
+
comm_list = self.class.command_list
|
226
|
+
|
227
|
+
sorted_comm_list = comm_list.sort { |a, b| a.name <=> b.name }
|
228
|
+
max_length = comm_list.map(:name).inject(0) { |max, str| max <= str.length ? str.length : max }
|
229
|
+
|
230
|
+
|
231
|
+
sorted_comm_list.each do |comm|
|
232
|
+
printf " % #{max_length}s", comm.name
|
233
|
+
|
234
|
+
if comm.config
|
235
|
+
print ' [options]'
|
236
|
+
end
|
237
|
+
|
238
|
+
puts comm.param_help.inject('') { |str, pair| str << " <#{pair.first}>" }
|
239
|
+
|
240
|
+
if comm.config
|
241
|
+
options = comm.config.options_long.values.sort { |a, b| a.long <=> b.long }
|
242
|
+
puts Filigree::Configuration::Option.to_s(options, max_length + 4)
|
243
|
+
end
|
244
|
+
|
245
|
+
puts
|
246
|
+
|
247
|
+
if !comm.param_help.empty?
|
248
|
+
max_param_len = comm.param_help.inject(0) do |max, pair|
|
249
|
+
param_len = pair.first.to_s.length
|
250
|
+
max <= param_len ? param_len : max
|
251
|
+
end
|
252
|
+
|
253
|
+
segment_indent = max_param_len + 8
|
254
|
+
comm.param_help.each do |name, help|
|
255
|
+
printf " %-#{max_param_len}s - %s\n", name, help.segment(segment_indent)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end)
|
260
|
+
end
|
261
|
+
end
|