filigree 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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