command-set 0.8.0
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.
- data/doc/README +2 -0
- data/doc/Specifications +219 -0
- data/doc/argumentDSL +36 -0
- data/lib/command-set/arguments.rb +547 -0
- data/lib/command-set/batch-interpreter.rb +0 -0
- data/lib/command-set/command-set.rb +282 -0
- data/lib/command-set/command.rb +456 -0
- data/lib/command-set/dsl.rb +526 -0
- data/lib/command-set/interpreter.rb +196 -0
- data/lib/command-set/og.rb +615 -0
- data/lib/command-set/quick-interpreter.rb +91 -0
- data/lib/command-set/result-list.rb +300 -0
- data/lib/command-set/results.rb +754 -0
- data/lib/command-set/standard-commands.rb +243 -0
- data/lib/command-set/subject.rb +91 -0
- data/lib/command-set/text-interpreter.rb +171 -0
- data/lib/command-set.rb +3 -0
- metadata +70 -0
@@ -0,0 +1,615 @@
|
|
1
|
+
require 'command-set/command'
|
2
|
+
require 'command-set/arguments'
|
3
|
+
require 'facet/kernel/constant'
|
4
|
+
|
5
|
+
module Command
|
6
|
+
class OgArgument < Argument
|
7
|
+
def initialize(name, klass, find_by="name", options={})
|
8
|
+
super(name)
|
9
|
+
@klass = klass
|
10
|
+
@key = find_by
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def complete(prefix, subject)
|
15
|
+
entities = @klass.find(@options.merge({:condition => ["#{@key} like ?", prefix + "%" ]}))
|
16
|
+
return entities.map{|entity| entity.__send__(@key)}
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate(term, subject)
|
20
|
+
found = @klass.find(@options.merge({:condition => ["#{@key} = ?", term]}))
|
21
|
+
if found.empty?
|
22
|
+
#THINK! Is it a good idea to create missing items in the Argument?
|
23
|
+
#Should the Command's action have to do that?
|
24
|
+
if(@options[:accept_missing])
|
25
|
+
return true
|
26
|
+
elsif(@options[:create_missing])
|
27
|
+
new_attributes = @options[:default_values] || {}
|
28
|
+
new_attributes.merge!( @key => term )
|
29
|
+
@klass.create_with(new_attributes)
|
30
|
+
return true
|
31
|
+
else
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse(subject, term)
|
39
|
+
found = @klass.find(@options.merge({:condition => ["#{@key} = ?", term]}))
|
40
|
+
raise RuntimeError, "Couldn't find item on parse!" if found.empty?
|
41
|
+
found[0]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module OgCommands
|
46
|
+
class OgModeCommand < Command
|
47
|
+
subject_methods :interpreter, :current_state
|
48
|
+
|
49
|
+
doesnt_undo
|
50
|
+
|
51
|
+
action do
|
52
|
+
subject.current_state << current
|
53
|
+
subject.interpreter.push_mode(my_mode)
|
54
|
+
end
|
55
|
+
|
56
|
+
class << self
|
57
|
+
def switch_to(model_class, find_by, og_options={})
|
58
|
+
og_options.merge!(:create_missing => true)
|
59
|
+
argument OgArgument.new(:current, model_class, find_by, og_options)
|
60
|
+
|
61
|
+
define_method(:found_current) do
|
62
|
+
current.__send__(find_by)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def mode(command_set)
|
67
|
+
define_method(:my_mode) do
|
68
|
+
command_set.set_prompt(/$/, "#{found_current} : ")
|
69
|
+
return command_set
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class HasOneCommand < Command
|
76
|
+
subject_methods :current_state
|
77
|
+
|
78
|
+
action do
|
79
|
+
if value.nil?
|
80
|
+
dont_undo
|
81
|
+
current_value = get_value
|
82
|
+
puts get_label(current_value)
|
83
|
+
else
|
84
|
+
@old_value = get_value
|
85
|
+
set_value value
|
86
|
+
entity.save
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
undo do
|
91
|
+
set_value @old_value
|
92
|
+
entity.save
|
93
|
+
end
|
94
|
+
|
95
|
+
def entity
|
96
|
+
subject.current_state.last
|
97
|
+
end
|
98
|
+
|
99
|
+
class << self
|
100
|
+
def optional_value(target_class, select_by)
|
101
|
+
optional_argument OgArgument.new(:value, target_class, select_by)
|
102
|
+
end
|
103
|
+
|
104
|
+
def has_one(field)
|
105
|
+
define_method(:get_value) do
|
106
|
+
entity.__send__(field)
|
107
|
+
end
|
108
|
+
|
109
|
+
define_method(:set_value) do |value|
|
110
|
+
entity.__send__("#{field}=", value)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def identified_by(name, &block)
|
115
|
+
if block.nil?
|
116
|
+
define_method(:get_label) do |value|
|
117
|
+
return "" unless value.respond_to?(name.intern)
|
118
|
+
value.__send__(name.intern).to_s
|
119
|
+
end
|
120
|
+
else
|
121
|
+
define_method(:get_label) do |value|
|
122
|
+
return "" unless value.respond_to?(name.intern)
|
123
|
+
block[value.__send__(name.intern)]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class HasManyListCommand < Command
|
131
|
+
optional_argument :search, "A substring of the name of the thing you're looking for"
|
132
|
+
|
133
|
+
doesnt_undo
|
134
|
+
|
135
|
+
subject_methods :current_state
|
136
|
+
|
137
|
+
action do
|
138
|
+
options = {}
|
139
|
+
unless search.nil?
|
140
|
+
options.update({:condition =>
|
141
|
+
["#{list_attribute} like ?", "%#{search_term}%"]})
|
142
|
+
end
|
143
|
+
|
144
|
+
object_list(options).each do |item|
|
145
|
+
puts item
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def entity
|
150
|
+
subject.current_state.last
|
151
|
+
end
|
152
|
+
|
153
|
+
def object_list(options={})
|
154
|
+
objects=entity.__send__("find_#{target_name}", options)
|
155
|
+
return objects.map do |object|
|
156
|
+
object.__send__(list_as)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class << self
|
161
|
+
def has_many(target_name)
|
162
|
+
define_method(:target_name) do
|
163
|
+
target_name
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def listed_as(list_as)
|
168
|
+
define_method(:list_as) do
|
169
|
+
list_as
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class HasManyEditCommand < Command
|
176
|
+
def add(item)
|
177
|
+
owner.__send__("add_#{kind}", item)
|
178
|
+
end
|
179
|
+
|
180
|
+
def remove(item)
|
181
|
+
owner.__send__("remove_#{kind}", item)
|
182
|
+
end
|
183
|
+
|
184
|
+
subject_methods :current_state
|
185
|
+
|
186
|
+
def owner
|
187
|
+
subject.current_state.last
|
188
|
+
end
|
189
|
+
|
190
|
+
class << self
|
191
|
+
def no_more_add_or_remove
|
192
|
+
raise NoMethodError, "Can't add_to and remove_from with same command!"
|
193
|
+
end
|
194
|
+
private :no_more_add_or_remove
|
195
|
+
|
196
|
+
def defined_sense
|
197
|
+
class << self
|
198
|
+
alias_method :add_to, :no_more_add_or_remove
|
199
|
+
alias_method :remove_from, :no_more_add_or_remove
|
200
|
+
def defined_sense
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def find_a(target_class, find_by)
|
206
|
+
argument OgArgument.new(:item, target_class, find_by)
|
207
|
+
end
|
208
|
+
|
209
|
+
def remove_from(kind)
|
210
|
+
define_method(:kind) do
|
211
|
+
return kind.to_s.singular
|
212
|
+
end
|
213
|
+
|
214
|
+
action do
|
215
|
+
remove(item)
|
216
|
+
end
|
217
|
+
|
218
|
+
undo do
|
219
|
+
add(item)
|
220
|
+
end
|
221
|
+
defined_sense
|
222
|
+
end
|
223
|
+
|
224
|
+
def add_to(kind)
|
225
|
+
define_method(:kind) do
|
226
|
+
return kind.to_s.singular
|
227
|
+
end
|
228
|
+
|
229
|
+
action do
|
230
|
+
add(item)
|
231
|
+
end
|
232
|
+
|
233
|
+
undo do
|
234
|
+
remove(item)
|
235
|
+
end
|
236
|
+
defined_sense
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
class DisplayCommand < Command
|
243
|
+
subject_methods :current_state
|
244
|
+
|
245
|
+
doesnt_undo
|
246
|
+
|
247
|
+
action do
|
248
|
+
pairs = []
|
249
|
+
simple_fields.each_pair do |field, value|
|
250
|
+
pairs << [field.to_s, value]
|
251
|
+
end
|
252
|
+
single_relations.each_pair do |field, value|
|
253
|
+
pairs << [field.to_s, value]
|
254
|
+
end
|
255
|
+
many_relations.each_pair do |field, number|
|
256
|
+
pairs << [field.to_s, "#{number} items"]
|
257
|
+
end
|
258
|
+
field_width = pairs.map{|f,v| f.length}.max + 1
|
259
|
+
pairs.each do |field, value|
|
260
|
+
puts "#{field.rjust(field_width)}: #{value}"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def simple_fields
|
265
|
+
{}
|
266
|
+
end
|
267
|
+
|
268
|
+
def single_relations
|
269
|
+
{}
|
270
|
+
end
|
271
|
+
|
272
|
+
def many_relations
|
273
|
+
{}
|
274
|
+
end
|
275
|
+
|
276
|
+
def entity
|
277
|
+
subject.current_state.last
|
278
|
+
end
|
279
|
+
|
280
|
+
class << self
|
281
|
+
def simple_fields(list)
|
282
|
+
define_method(:simple_fields) do
|
283
|
+
fields = {}
|
284
|
+
list.each do |name, field_name|
|
285
|
+
fields[name] = entity.__send__(field_name)
|
286
|
+
end
|
287
|
+
return fields
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def single_relations(list)
|
292
|
+
define_method(:single_relations) do
|
293
|
+
fields = {}
|
294
|
+
list.each do |name, field_name, called|
|
295
|
+
target = entity.__send__(field_name)
|
296
|
+
if target.nil?
|
297
|
+
fields[name] = ""
|
298
|
+
else
|
299
|
+
fields[name] = target.__send__(called)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
return fields
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def many_relations(list)
|
307
|
+
define_method(:many_relations) do
|
308
|
+
fields = {}
|
309
|
+
list.each do |name, field_name|
|
310
|
+
fields[name] = entity.__send__("count_#{field_name}")
|
311
|
+
end
|
312
|
+
return fields
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
class PropertyCommand < Command
|
319
|
+
subject_methods :current_state
|
320
|
+
|
321
|
+
action do
|
322
|
+
if value.nil?
|
323
|
+
dont_undo
|
324
|
+
puts get_value
|
325
|
+
else
|
326
|
+
@old_value = get_value
|
327
|
+
set_value value
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
undo do
|
332
|
+
set_value @old_value
|
333
|
+
end
|
334
|
+
|
335
|
+
def entity
|
336
|
+
subject.current_state.last
|
337
|
+
end
|
338
|
+
|
339
|
+
def value
|
340
|
+
nil
|
341
|
+
end
|
342
|
+
|
343
|
+
class << self
|
344
|
+
def field_name(field)
|
345
|
+
define_method(:get_value) do
|
346
|
+
entity.__send__(field)
|
347
|
+
end
|
348
|
+
|
349
|
+
define_method(:set_value) do |value|
|
350
|
+
entity.__send__("#{field}=", value)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def editable
|
355
|
+
optional_argument :value, "The new value"
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
class << self
|
361
|
+
def command_set(config)
|
362
|
+
set = CommandSet.new("og_commands")
|
363
|
+
def set.entity_modes
|
364
|
+
return @entity_modes||={}
|
365
|
+
end
|
366
|
+
normalize_config(config)
|
367
|
+
entity_commands_from_config(set, config)
|
368
|
+
list_command_from_config(set, config)
|
369
|
+
return set
|
370
|
+
end
|
371
|
+
|
372
|
+
# This is a messy method that ensures that the configuration hash for the
|
373
|
+
# CommandSet is well constructed. It should catch errors and do coversions,
|
374
|
+
# set defaults, etc. to set up the creation of the CommandSet. It's source
|
375
|
+
# may be instructive for creating or editing config hashes.
|
376
|
+
def normalize_config(config)
|
377
|
+
entity_count = 1
|
378
|
+
required_names = ["class"]
|
379
|
+
config.each do |entity_config|
|
380
|
+
required_names.each do |name|
|
381
|
+
if entity_config[name].nil?
|
382
|
+
raise RuntimeError, "Entity #{entity_config.pretty_inspect} missing #{name}!"
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
begin
|
387
|
+
real_class = constant(entity_config["class"])
|
388
|
+
entity_config["real_class"] = real_class
|
389
|
+
rescue NameError
|
390
|
+
raise RuntimeError, "Class: #{entity_config["class"]} unrecognized. Missing require?"
|
391
|
+
end
|
392
|
+
|
393
|
+
entity_config["select_by"] = "primary_key" if entity_config["select_by"].nil?
|
394
|
+
|
395
|
+
unless entity_config["edit_command"].nil?
|
396
|
+
if entity_config["edit_config"].nil?
|
397
|
+
raise RuntimeError, "Class: #{item["class"]} has an edit_command but no edit_config!"
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
if entity_config["listable?"]
|
402
|
+
if entity_config["list_as"].nil?
|
403
|
+
if not entity_config["plural_name"].nil?
|
404
|
+
entity_config["list_as"] = entity_config["plural_name"]
|
405
|
+
elsif not entity_config["edit_command"].nil?
|
406
|
+
entity_config["list_as"] = entity_config["edit_command"] + "s"
|
407
|
+
else
|
408
|
+
raise RuntimeError, "Class: #{entity_config["class"]} marked listable, " +
|
409
|
+
"but without a list name!"
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
entity_count += 1
|
414
|
+
end
|
415
|
+
|
416
|
+
#fields pass - so that relations can reuse entity defs
|
417
|
+
config.each do |entity_config|
|
418
|
+
fields_config = entity_config["edit_config"]
|
419
|
+
next if fields_config.nil?
|
420
|
+
|
421
|
+
fields_config["simple_fields"]||=[]
|
422
|
+
fields_config["simple_fields"].each do |field_config|
|
423
|
+
if field_config["field_name"].nil?
|
424
|
+
raise RuntimeError, "Class: #{entity_config["class"]} field missing field_name!"
|
425
|
+
end
|
426
|
+
|
427
|
+
field_config["name"]||=field_config["field_name"]
|
428
|
+
end
|
429
|
+
|
430
|
+
fields_config["single_relations"]||=[]
|
431
|
+
fields_config["single_relations"].each do |relation_config|
|
432
|
+
if relation_config["field_name"].nil?
|
433
|
+
raise RuntimeError, "Class: #{entity_config["class"]} relation " +
|
434
|
+
"missing field_name!"
|
435
|
+
end
|
436
|
+
|
437
|
+
relation_config["name"]||=relation_config["field_name"]
|
438
|
+
if relation_config["target"].nil?
|
439
|
+
raise RuntimeError, "Class: #{entity_config["class"]} relation " +
|
440
|
+
"#{relation_config["name"]} has no target!"
|
441
|
+
end
|
442
|
+
|
443
|
+
relation_config["target"]["select_by"]||="name"
|
444
|
+
end
|
445
|
+
|
446
|
+
fields_config["many_relations"]||=[]
|
447
|
+
fields_config["many_relations"].each do |relation_config|
|
448
|
+
if relation_config["field_name"].nil?
|
449
|
+
raise RuntimeError, "Class: #{entity_config["class"]} field missing field_name!"
|
450
|
+
end
|
451
|
+
|
452
|
+
relation_config["name"]||=relation_config["field_name"]
|
453
|
+
|
454
|
+
if relation_config["target"].nil?
|
455
|
+
raise RuntimeError, "Class: #{entity_config["class"]} relation " +
|
456
|
+
"#{relation_config["name"]} has no target!"
|
457
|
+
end
|
458
|
+
|
459
|
+
relation_config["target"]["select_by"]||="name"
|
460
|
+
unless Class === relation_config["target"]["real_class"]
|
461
|
+
if relation_config["target"]["class"].nil?
|
462
|
+
raise RuntimeError,
|
463
|
+
"Class: #{entity_config["class"]} relation " +
|
464
|
+
"#{relation_config["name"]} target has no class!"
|
465
|
+
else
|
466
|
+
relation_config["target"]["real_class"] =
|
467
|
+
constant(relation_config["target"]["real_class"])
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
|
475
|
+
def entity_commands_from_config(set, config)
|
476
|
+
config.each do |entity_config|
|
477
|
+
next if entity_config["edit_command"].nil?
|
478
|
+
|
479
|
+
my_mode = entity_mode(entity_config["edit_config"])
|
480
|
+
set.entity_modes[entity_config["class"]]=my_mode
|
481
|
+
|
482
|
+
set.command(OgModeCommand, entity_config["edit_command"]) do
|
483
|
+
switch_to entity_config["real_class"], entity_config["select_by"]
|
484
|
+
mode my_mode
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
def list_command_from_config(set, config)
|
490
|
+
lists = {}
|
491
|
+
config.find_all{|item| item["listable?"] }.each do |item|
|
492
|
+
entry = item["list_as"]
|
493
|
+
entry_class = item["real_class"]
|
494
|
+
find_by = item["select_by"]
|
495
|
+
|
496
|
+
lists[entry] = [entry_class, find_by]
|
497
|
+
end
|
498
|
+
|
499
|
+
set.command "list" do
|
500
|
+
argument :what, lists.keys
|
501
|
+
|
502
|
+
optional_argument :search, "A substring of the name of the thing you're looking for"
|
503
|
+
|
504
|
+
doesnt_undo
|
505
|
+
|
506
|
+
define_method(:make_list) do |klass, list_attribute, search_term|
|
507
|
+
objects = []
|
508
|
+
if search_term.nil?
|
509
|
+
objects = klass.find
|
510
|
+
else
|
511
|
+
objects = klass.find(:condition => ["#{list_attribute} like ?",
|
512
|
+
"%#{search_term}%"])
|
513
|
+
end
|
514
|
+
|
515
|
+
objects.map do |object|
|
516
|
+
object.__send__(list_attribute)
|
517
|
+
end.each do |item|
|
518
|
+
puts item
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
action do
|
523
|
+
list_me = lists[what]
|
524
|
+
raise CommandException if list_me.nil?
|
525
|
+
make_list(*(list_me + [search]))
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
|
531
|
+
def entity_mode(fields_config)
|
532
|
+
mode = CommandSet.define_commands do
|
533
|
+
include_commands StandardCommands::Quit
|
534
|
+
|
535
|
+
command "exit" do
|
536
|
+
subject_methods :interpreter, :current_state
|
537
|
+
|
538
|
+
doesnt_undo
|
539
|
+
action do
|
540
|
+
entity = subject.current_state.pop
|
541
|
+
entity.save
|
542
|
+
subject.interpreter.pop_mode
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
command "delete!" do
|
547
|
+
subject_methods :interpreter, :current_state
|
548
|
+
|
549
|
+
action do
|
550
|
+
entity = subject.current_state.pop
|
551
|
+
entity.delete
|
552
|
+
subject.interpreter.pop_mode
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
command DisplayCommand, "display" do
|
557
|
+
simple_fields fields_config["simple_fields"].map{|f| [f["name"], f["field_name"]]}
|
558
|
+
singles = fields_config["single_relations"].map do |f|
|
559
|
+
[ f["name"], f["field_name"], f["target"]["select_by"] ]
|
560
|
+
end
|
561
|
+
single_relations singles
|
562
|
+
many_relations fields_config["many_relations"].map{|f| [f["name"], f["field_name"]]}
|
563
|
+
end
|
564
|
+
|
565
|
+
fields_config["simple_fields"].each do |property|
|
566
|
+
command PropertyCommand, property["name"] do
|
567
|
+
field_name property["field_name"]
|
568
|
+
if property["edit?"]
|
569
|
+
editable
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
fields_config["single_relations"].each do |relation|
|
575
|
+
command HasOneCommand, relation["name"] do
|
576
|
+
optional_value relation["target"]["real_class"], relation["target"]["select_by"]
|
577
|
+
has_one relation["field_name"]
|
578
|
+
identified_by relation["target"]["select_by"]
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
fields_config["many_relations"].each do |relation_config|
|
583
|
+
unless relation_config["list?"] or relation_config["edit?"]
|
584
|
+
next
|
585
|
+
end
|
586
|
+
sub_command relation_config["name"] do
|
587
|
+
if relation_config["list?"]
|
588
|
+
command HasManyListCommand, :list do
|
589
|
+
has_many relation_config["field_name"]
|
590
|
+
listed_as relation_config["target"]["select_by"]
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
if relation_config["edit?"]
|
595
|
+
command HasManyEditCommand, :add do
|
596
|
+
find_a relation_config["target"]["real_class"],
|
597
|
+
relation_config["target"]["select_by"]
|
598
|
+
add_to relation_config["field_name"]
|
599
|
+
end
|
600
|
+
|
601
|
+
command HasManyEditCommand, :remove do
|
602
|
+
find_a relation_config["target"]["real_class"],
|
603
|
+
relation_config["target"]["select_by"]
|
604
|
+
remove_from relation_config["field_name"]
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
610
|
+
return mode
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'command-set/interpreter'
|
2
|
+
|
3
|
+
module Command
|
4
|
+
#A looser Subject, used by QuickInterpreter for testing purposes.
|
5
|
+
#Normally, a Subject doesn't allow reads from it's fields - you have to
|
6
|
+
#explicitly call get_image. This class allows reads, which makes testing
|
7
|
+
#easier.
|
8
|
+
class PermissiveSubject < Subject
|
9
|
+
protected
|
10
|
+
def add_accessor(name)
|
11
|
+
super
|
12
|
+
(class << self; self; end).instance_eval do
|
13
|
+
define_method(name) do
|
14
|
+
return instance_variable_get("@#{name}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
#This class exists mostly to make spec and unit test writing easier.
|
22
|
+
#Because Commands need so much care and feeding on the back end, it can be
|
23
|
+
#troublesome to write specs on the directly. QuickInterpreter is designed
|
24
|
+
#for programmatic access, and easy setup.
|
25
|
+
#
|
26
|
+
#Example usage:
|
27
|
+
#
|
28
|
+
# @interpreter = QuickInterpreter::define_interpreter do #unique to QI
|
29
|
+
# command :test do
|
30
|
+
# subject.a_field << "one"
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# @subject = @interpreter.subject_template
|
35
|
+
# @subject.a_field = []
|
36
|
+
# @interpreter.subject = @subject
|
37
|
+
#
|
38
|
+
# @interpreter.process_input(:test, "args")
|
39
|
+
# @subject.a_field.length # => 1; note that normally you need to
|
40
|
+
# #+get_image+ to access fields of the subject
|
41
|
+
#
|
42
|
+
class QuickInterpreter < BaseInterpreter
|
43
|
+
class << self
|
44
|
+
#You can use this method to create a new interpreter with a command
|
45
|
+
#set. The block is passed to a new CommandSet, so you can use
|
46
|
+
#DSL::CommandSetDefinition there.
|
47
|
+
def define_interpreter(&block)
|
48
|
+
interpreter = new
|
49
|
+
interpreter.command_set = CommandSet::define_commands(&block)
|
50
|
+
return interpreter
|
51
|
+
end
|
52
|
+
|
53
|
+
alias define_commands define_interpreter
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize
|
57
|
+
@formatter_factory = proc {Results::Formatter.new(::Command::raw_stdout)}
|
58
|
+
super
|
59
|
+
end
|
60
|
+
|
61
|
+
#Saves the block passed to create formatters with. Cleaner a singleton
|
62
|
+
#get_formatter definition.
|
63
|
+
def make_formatter(&block)
|
64
|
+
@formatter_factory = proc &block
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_formatter #:nodoc:
|
68
|
+
return @formatter_factory.call
|
69
|
+
end
|
70
|
+
|
71
|
+
#Accepts it's input as multiple arguments for convenience
|
72
|
+
def process_input(*words)
|
73
|
+
super(words)
|
74
|
+
end
|
75
|
+
|
76
|
+
#Always returns "yes" so that undo warnings can be ignored.
|
77
|
+
def prompt_user(message)
|
78
|
+
"yes"
|
79
|
+
end
|
80
|
+
|
81
|
+
#Passes the arguments to process_input directly to CommandSetup
|
82
|
+
def cook_input(words)
|
83
|
+
CommandSetup.new(words)
|
84
|
+
end
|
85
|
+
|
86
|
+
#See PermissiveSubject
|
87
|
+
def get_subject
|
88
|
+
return PermissiveSubject.new
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|