qcmd 0.1.8 → 0.1.9

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.
@@ -0,0 +1,18 @@
1
+ ### 0.1.8 -> 0.1.9
2
+
3
+ * add "alias" command to help
4
+ * add "new" command to create new cues in QLab
5
+ * add "select" command to allow selection of a given cue (by number)
6
+ * `select 2; go` would select and start the QLab workspace at the given cue
7
+ * allow IPv4 machine addresses
8
+
9
+ Internals:
10
+ * set default log level to `:info`
11
+ * separate machine connection and workspace loading
12
+ * simplify `send_workspace_command` method, clean up `send_command`
13
+ * add list of cue types for "new" command
14
+ * unify simple reply handling in CLI
15
+ * fix `-c "COMMAND"` command line option
16
+ * various small bug fixes
17
+
18
+
data/bin/qcmd CHANGED
@@ -3,6 +3,8 @@
3
3
  require 'qcmd'
4
4
  require 'trollop'
5
5
 
6
+ # require 'profile'
7
+
6
8
  VERSION_STRING = "qcmd #{ Qcmd::VERSION } (c) 2012 Figure 53, Baltimore, MD."
7
9
 
8
10
  opts = Trollop::options do
@@ -15,6 +17,8 @@ opts = Trollop::options do
15
17
  opt :command, "Execute a single command and exit", :type => :string
16
18
  end
17
19
 
20
+ Qcmd.log_level = :info
21
+
18
22
  if opts[:verbose]
19
23
  Qcmd.log_level = :debug
20
24
  end
@@ -27,21 +31,32 @@ end
27
31
  # browse local network and check for qlab + qlab workspaces
28
32
 
29
33
  Qcmd::History.load
34
+ Qcmd::Network.init
30
35
 
31
36
  if !opts[:machine_given]
32
37
  Qcmd.ascii_qlab
33
38
  Qcmd.print
34
39
  Qcmd.print Qcmd.centered_text(VERSION_STRING)
35
-
36
40
  Qcmd::Network.browse_and_display opts
37
41
  else
38
- Qcmd::Network.browse
42
+ if Qcmd::Network::IPV4_MATCHER =~ opts[:machine]
43
+ Qcmd.debug "[bin/qcmd] given machine name matches IP address pattern"
44
+ elsif opts[:machine] == 'localhost'
45
+ opts[:machine] = '127.0.0.1'
46
+ else
47
+ Qcmd.debug "[bin/qcmd] browsing network #{ opts[:machine] }"
48
+ Qcmd::Network.browse
49
+ end
39
50
  end
40
51
 
41
52
  if opts[:command_given] && !(opts[:machine_given] && opts[:workspace_given])
42
53
  Qcmd.print_wrapped("if you give a command, you must also give a
43
54
  machine name and a workspace name to connect to")
55
+ elsif opts[:command_given] && opts[:machine_given] && opts[:workspace_given]
56
+ opts[:command_mode] = true
44
57
  end
45
58
 
46
-
59
+ # Profiler__::start_profile
47
60
  Qcmd::CLI.launch opts
61
+ # Profiler__::stop_profile
62
+ # Profiler__::print_profile($stderr)
@@ -33,10 +33,23 @@ module Qcmd
33
33
  class << self
34
34
  include Qcmd::Plaintext
35
35
 
36
- attr_accessor :log_level
37
36
  attr_accessor :debug_mode
38
37
  attr_accessor :context
39
38
 
39
+ LEVELS = %w(debug info warning error none)
40
+
41
+ def log_level
42
+ @log_level ||= :info
43
+ end
44
+
45
+ def log_level=(value)
46
+ if LEVELS.include?(value.to_s)
47
+ @log_level = value
48
+ else
49
+ raise "Invalid log_level value: #{ value.to_s }"
50
+ end
51
+ end
52
+
40
53
  def verbose!
41
54
  self.log_level = :debug
42
55
  end
@@ -73,7 +86,11 @@ module Qcmd
73
86
 
74
87
  Qcmd::Configuration.log.puts "[%s] %s" % [Time.now.strftime('%T'), message]
75
88
 
76
- log(message) if log_level == :debug
89
+ log(:debug, message)
90
+ end
91
+
92
+ def log_level_acheived? level
93
+ LEVELS.index(level.to_s) >= LEVELS.index(log_level.to_s)
77
94
  end
78
95
 
79
96
  def connected?
@@ -87,7 +87,7 @@ module Qcmd
87
87
  end
88
88
 
89
89
  def osc_arguments
90
- code[1..-1]
90
+ stringify code[1..-1]
91
91
  end
92
92
 
93
93
  # the raw command
@@ -100,6 +100,7 @@ module Qcmd
100
100
  def send_message
101
101
  responses = []
102
102
 
103
+ Qcmd.debug "[Action send_message] send #{ osc_message.encode }"
103
104
  Qcmd.context.qlab.send(osc_message) do |response|
104
105
  # puts "response to: #{ osc_message.inspect }"
105
106
  # puts response.inspect
@@ -120,6 +121,20 @@ module Qcmd
120
121
  responses
121
122
  end
122
123
  end
124
+
125
+ def stringify args
126
+ if args.nil?
127
+ nil
128
+ else
129
+ args.map {|arg|
130
+ if arg.is_a?(Symbol)
131
+ arg.to_s
132
+ else
133
+ arg
134
+ end
135
+ }
136
+ end
137
+ end
123
138
  end
124
139
 
125
140
  class CueAction < Action
@@ -133,18 +148,7 @@ module Qcmd
133
148
  end
134
149
 
135
150
  def osc_arguments
136
- args = code[3..-1]
137
- if args.nil?
138
- nil
139
- else
140
- args.map {|arg|
141
- if arg.is_a?(Symbol)
142
- arg.to_s
143
- else
144
- arg
145
- end
146
- }
147
- end
151
+ stringify code[3..-1]
148
152
  end
149
153
 
150
154
  # cue specific fields
@@ -23,22 +23,25 @@ module Qcmd
23
23
  connect_to_machine_by_name(options[:machine])
24
24
  end
25
25
 
26
+ load_workspaces
27
+
26
28
  if options[:workspace_given]
27
- Qcmd.debug "[CLI initialize] autoconnecting to workspace #{ options[:machine] }"
29
+ Qcmd.debug "[CLI initialize] autoconnecting to workspace #{ options[:workspace] }"
28
30
 
29
31
  Qcmd.while_quiet do
30
32
  connect_to_workspace_by_name(options[:workspace], options[:workspace_passcode])
31
33
  end
32
34
 
33
35
  if options[:command_given]
34
- handle_input options[:command]
35
- print %[sent command "#{ options[:command] }"]
36
- exit 0
36
+ handle_input Qcmd::Parser.parse(options[:command])
37
+ return
37
38
  end
38
39
  elsif Qcmd.context.machine.workspaces.size == 1 &&
39
40
  !Qcmd.context.machine.workspaces.first.passcode? &&
40
41
  !Qcmd.context.workspace_connected?
41
- connect_to_workspace_by_index(0, nil)
42
+ if !connect_default_workspace
43
+ Handler.print_workspace_list
44
+ end
42
45
  end
43
46
  end
44
47
 
@@ -79,7 +82,7 @@ module Qcmd
79
82
  if arg.is_a?(Array)
80
83
  replace_args(arg, original_expression)
81
84
  elsif (arg.is_a?(Symbol) || arg.is_a?(String)) && alias_arg_matcher =~ arg.to_s
82
- while alias_arg_matcher =~ arg.to_s
85
+ while alias_arg_matcher =~ arg.to_s
83
86
  arg_idx = $1.to_i
84
87
  arg_val = original_expression[arg_idx]
85
88
 
@@ -141,7 +144,7 @@ module Qcmd
141
144
  ["#{clock} #{prefix.join(' ')}", "> "]
142
145
  end
143
146
 
144
- def connect machine
147
+ def connect_machine machine
145
148
  if machine.nil?
146
149
  print "A valid machine is needed to connect!"
147
150
  return
@@ -157,18 +160,10 @@ module Qcmd
157
160
  # tell QLab to always reply to messages
158
161
  response = Qcmd::Action.evaluate('/alwaysReply 1')
159
162
  if response.nil? || response.empty?
160
- print %[Failed to connect to QLab machine "#{ machine.name }"]
163
+ log(:error, %[Failed to connect to QLab machine "#{ machine.name }"])
161
164
  elsif response.status == 'ok'
162
165
  print %[Connected to machine "#{ machine.name }"]
163
166
  end
164
-
165
- machine.workspaces = Qcmd::Action.evaluate('workspaces').map {|ws| QLab::Workspace.new(ws)}
166
-
167
- if Qcmd.context.machine.workspaces.size == 1 && !Qcmd.context.machine.workspaces.first.passcode?
168
- connect_to_workspace_by_index(0, nil)
169
- else
170
- Handler.print_workspace_list
171
- end
172
167
  end
173
168
 
174
169
  def disconnected_machine_warning
@@ -183,20 +178,29 @@ module Qcmd
183
178
  end
184
179
 
185
180
  def connect_to_machine_by_name machine_name
186
- if machine = Qcmd::Network.find(machine_name)
187
- print "Connecting to machine: #{machine_name}"
188
- connect machine
181
+ machine = nil
182
+
183
+ # machine name can be found or IPv4 address is given
184
+ if Qcmd::Network.find(machine_name)
185
+ machine = Qcmd::Network.find(machine_name)
186
+ elsif Qcmd::Network::IPV4_MATCHER =~ machine_name
187
+ machine = Qcmd::Machine.new(machine_name, machine_name, 53000)
188
+ end
189
+
190
+ if machine.nil?
191
+ log(:warning, 'Sorry, that machine could not be found')
189
192
  else
190
- print 'Sorry, that machine could not be found'
193
+ print "Connecting to machine: #{machine_name}"
194
+ connect_machine machine
191
195
  end
192
196
  end
193
197
 
194
198
  def connect_to_machine_by_index machine_idx
195
199
  if machine = Qcmd::Network.find_by_index(machine_idx)
196
200
  print "Connecting to machine: #{machine.name}"
197
- connect machine
201
+ connect_machine machine
198
202
  else
199
- print 'Sorry, that machine could not be found'
203
+ log(:warning, 'Sorry, that machine could not be found')
200
204
  end
201
205
  end
202
206
 
@@ -208,7 +212,7 @@ module Qcmd
208
212
  print "That workspace isn't on the list."
209
213
  end
210
214
  else
211
- print %[You can't connect to a workspace until you've connected to a machine. ]
215
+ log(:warning, %[You can't connect to a workspace until you've connected to a machine. ])
212
216
  disconnected_machine_warning
213
217
  end
214
218
  end
@@ -218,16 +222,15 @@ module Qcmd
218
222
  if workspace = Qcmd.context.machine.find_workspace(workspace_name)
219
223
  workspace.passcode = passcode
220
224
  print "Connecting to workspace: #{workspace_name}"
221
-
222
225
  use_workspace workspace
223
226
  else
224
- print "That workspace doesn't seem to exist, try one of the following:"
227
+ log(:warning, "That workspace doesn't seem to exist, try one of the following:")
225
228
  Qcmd.context.machine.workspaces.each do |ws|
226
- print %[ "#{ ws.name }"]
229
+ log(:warning, %[ "#{ ws.name }"])
227
230
  end
228
231
  end
229
232
  else
230
- print %[You can't connect to a workspace until you've connected to a machine. ]
233
+ log(:warning, %[You can't connect to a workspace until you've connected to a machine. ])
231
234
  disconnected_machine_warning
232
235
  end
233
236
  end
@@ -248,7 +251,7 @@ module Qcmd
248
251
  reply = Qcmd::Action.evaluate(ws_action_string)
249
252
 
250
253
  if reply == 'badpass'
251
- print 'Failed to connect to workspace, bad passcode or no passcode given.'
254
+ log(:error, 'Failed to connect to workspace, bad passcode or no passcode given.')
252
255
  Qcmd.context.disconnect_workspace
253
256
  elsif reply == 'ok'
254
257
  print %[Connected to "#{Qcmd.context.workspace.name}"]
@@ -284,7 +287,7 @@ module Qcmd
284
287
  begin
285
288
  if /;/ =~ cli_input
286
289
  cli_input.split(';').each do |sub_input|
287
- handle_input Qcmd::Parser.parse(sub_input)
290
+ handle_input Qcmd::Parser.parse(sub_input.strip)
288
291
  end
289
292
  else
290
293
  handle_input Qcmd::Parser.parse(cli_input)
@@ -300,6 +303,8 @@ module Qcmd
300
303
  def handle_input args
301
304
  command = args[0].to_s
302
305
 
306
+ Qcmd.debug "[CLI handle_input] command: #{ command }; args: #{ args.inspect }"
307
+
303
308
  case command
304
309
  when 'exit', 'quit', 'q'
305
310
  print 'exiting...'
@@ -318,6 +323,12 @@ module Qcmd
318
323
  connect_to_machine_by_name machine_ident
319
324
  end
320
325
 
326
+ load_workspaces
327
+
328
+ if !connect_default_workspace
329
+ Handler.print_workspace_list
330
+ end
331
+
321
332
  when 'disconnect'
322
333
  disconnect_what = args[1]
323
334
 
@@ -382,18 +393,12 @@ module Qcmd
382
393
  print_wrapped("no workspace command given. available workspace commands
383
394
  are: #{Qcmd::InputCompleter::ReservedWorkspaceWords.join(', ')}")
384
395
  else
385
- send_workspace_command(workspace_command, *args)
396
+ reply = send_workspace_command(workspace_command, *args)
397
+ handle_simple_reply reply
386
398
  end
387
399
 
388
400
  when 'help'
389
- help_command = args.shift
390
-
391
- if help_command.nil?
392
- # print help according to current context
393
- Qcmd::Commands::Help.print_all_commands
394
- else
395
- # print command specific help
396
- end
401
+ Qcmd::Commands::Help.print_all_commands
397
402
 
398
403
  when 'cues'
399
404
  if !Qcmd.context.workspace_connected?
@@ -441,33 +446,9 @@ module Qcmd
441
446
  cue_action = Qcmd::CueAction.new(args)
442
447
 
443
448
  reply = cue_action.evaluate
449
+ handle_simple_reply reply
444
450
 
445
- if reply.is_a?(QLab::Reply)
446
- if !reply.status.nil?
447
- print reply.status
448
- end
449
- else
450
- render_data reply
451
- end
452
-
453
- # fixate on cue
454
- if Qcmd.context.workspace.has_cues?
455
- _cue = Qcmd.context.workspace.cues.find {|cue|
456
- case cue_action.id_field
457
- when :cue
458
- cue.number.to_s == cue_action.identifier.to_s
459
- when :cue_id
460
- cue.id.to_s == cue_action.identifier.to_s
461
- end
462
- }
463
-
464
- if _cue
465
- Qcmd.context.cue = _cue
466
- Qcmd.context.cue_connected = true
467
-
468
- Qcmd.context.cue.sync
469
- end
470
- end
451
+ fixate_on_cue(cue_action)
471
452
 
472
453
  when 'aliases'
473
454
  print centered_text(" Available Custom Commands ", '-')
@@ -483,6 +464,38 @@ module Qcmd
483
464
  new_alias = add_alias(args[1].to_s, args[2])
484
465
  print %[Added alias for "#{ args[1] }": #{ new_alias }]
485
466
 
467
+ when 'new'
468
+ # create new cue
469
+
470
+ if !(args.size == 2 && QLab::Cue::TYPES.include?(args.last.to_s))
471
+ log(:warning, "That cue type can't be created, try one of the following:")
472
+ log(:warning, joined_wrapped(QLab::Cue::TYPES.join(", ")))
473
+ else
474
+ reply = send_workspace_command(command, *args)
475
+ handle_simple_reply reply
476
+ end
477
+
478
+ when 'select'
479
+
480
+ if args.size == 2
481
+ reply = send_workspace_command "#{ args[0] }/#{ args[1] }"
482
+
483
+ if reply.respond_to?(:status) && reply.status == 'ok'
484
+ # cue exists, get name and fixate
485
+ cue_action = Qcmd::CueAction.new([:cue, args[1], :name])
486
+ reply = cue_action.evaluate
487
+ if reply.is_a?(QLab::Reply)
488
+ # something went wrong
489
+ handle_simple_reply reply
490
+ else
491
+ print "Selected #{args[1]} - #{reply}"
492
+ fixate_on_cue(cue_action)
493
+ end
494
+ end
495
+ else
496
+ log(:warning, "The select command should be in the form `select CUE_NUMBER`.")
497
+ end
498
+
486
499
  else
487
500
  if aliases[command]
488
501
  Qcmd.debug "[CLI handle_input] using alias #{ command }"
@@ -509,7 +522,6 @@ module Qcmd
509
522
  handle_input(new_expression)
510
523
  end
511
524
 
512
-
513
525
  elsif Qcmd.context.cue_connected? && Qcmd::InputCompleter::ReservedCueWords.include?(command)
514
526
  # prepend the given command with a cue address
515
527
  if Qcmd.context.cue.number.nil? || Qcmd.context.cue.number.size == 0
@@ -523,31 +535,18 @@ module Qcmd
523
535
  cue_action = Qcmd::CueAction.new(args)
524
536
 
525
537
  reply = cue_action.evaluate
538
+ handle_simple_reply reply
526
539
 
527
- if reply.is_a?(QLab::Reply)
528
- if !reply.status.nil?
529
- print reply.status
530
- end
531
- else
532
- render_data reply
533
- end
534
-
535
- # send_workspace_command(command, *args)
536
540
  elsif Qcmd.context.workspace_connected? && Qcmd::InputCompleter::ReservedWorkspaceWords.include?(command)
537
- send_workspace_command(command, *args)
541
+ reply = send_workspace_command(command, *args)
542
+ handle_simple_reply reply
538
543
 
539
544
  else
540
545
  # failure modes?
541
546
  if %r[/] =~ command
542
547
  # might be legit OSC command, try sending
543
548
  reply = Qcmd::Action.evaluate(args)
544
- if reply.is_a?(QLab::Reply)
545
- if !reply.status.nil?
546
- print reply.status
547
- end
548
- else
549
- render_data reply
550
- end
549
+ handle_simple_reply reply
551
550
  else
552
551
  if Qcmd.context.cue_connected?
553
552
  # cue is connected, but command isn't a valid cue command
@@ -557,7 +556,9 @@ module Qcmd
557
556
  # workspace is connected, but command isn't a valid workspace command
558
557
  print_wrapped("Unrecognized command: '#{ command }'. Try one of these workspace commands: #{ Qcmd::InputCompleter::ReservedWorkspaceWords.join(', ') }")
559
558
  elsif Qcmd.context.machine_connected?
560
- send_command(command, *args)
559
+ # send a command directly to a machine
560
+ reply = Qcmd::Action.evaluate(args)
561
+ handle_simple_reply reply
561
562
  else
562
563
  print 'you must connect to a machine before sending commands'
563
564
  end
@@ -588,6 +589,16 @@ module Qcmd
588
589
  ### communication actions
589
590
  private
590
591
 
592
+ def handle_simple_reply reply
593
+ if reply.is_a?(QLab::Reply)
594
+ if !reply.status.nil?
595
+ print reply.status
596
+ end
597
+ else
598
+ render_data reply
599
+ end
600
+ end
601
+
591
602
  def render_data data
592
603
  if data.is_a?(Array) || data.is_a?(Hash)
593
604
  begin
@@ -601,42 +612,51 @@ module Qcmd
601
612
  end
602
613
  end
603
614
 
615
+ def fixate_on_cue cue_action
616
+ # fixate on cue
617
+ if Qcmd.context.workspace.has_cues?
618
+ _cue = Qcmd.context.workspace.cues.find {|cue|
619
+ case cue_action.id_field
620
+ when :cue
621
+ cue.number.to_s == cue_action.identifier.to_s
622
+ when :cue_id
623
+ cue.id.to_s == cue_action.identifier.to_s
624
+ end
625
+ }
604
626
 
605
- def send_command command, *args
606
- options = args.extract_options!
627
+ if _cue
628
+ Qcmd.context.cue = _cue
629
+ Qcmd.context.cue_connected = true
607
630
 
608
- Qcmd.debug "[CLI send_command] building command from command, args, options: #{ command.inspect }, #{ args.inspect }, #{ options.inspect }"
631
+ Qcmd.context.cue.sync
632
+ end
633
+ end
634
+ end
609
635
 
610
- # make sure command is valid OSC Address
611
- if %r[^/] =~ command
612
- address = command
636
+ def send_workspace_command _command, *args
637
+ if !Qcmd.context.workspace.nil?
638
+ args[0] = "workspace/#{ Qcmd.context.workspace.id }/#{ _command }"
639
+ Qcmd::Action.evaluate(args)
613
640
  else
614
- address = "/#{ command }"
641
+ log(:warning, "A workspace needs to be connected before a workspace command can be sent.")
615
642
  end
643
+ end
616
644
 
617
- osc_message = OSC::Message.new address, *args
645
+ ## QLab commands
618
646
 
619
- Qcmd.debug "[CLI send_command] sending osc message #{ osc_message.address } #{osc_message.has_arguments? ? 'with' : 'without'} args"
647
+ def load_workspaces
648
+ Qcmd.context.machine.workspaces = Qcmd::Action.evaluate('workspaces').map {|ws| QLab::Workspace.new(ws)}
649
+ end
620
650
 
621
- if block_given?
622
- # use given response handler, pass it response as a QLab Reply
623
- Qcmd.context.qlab.send osc_message do |response|
624
- Qcmd.debug "[CLI send_command] converting OSC::Message to QLab::Reply"
625
- yield QLab::Reply.new(response)
626
- end
651
+ def connect_default_workspace
652
+ if Qcmd.context.machine.workspaces.size == 1 && !Qcmd.context.machine.workspaces.first.passcode?
653
+ connect_to_workspace_by_index(0, nil)
654
+ true
627
655
  else
628
- # rely on default response handler
629
- Qcmd.context.qlab.send(osc_message)
656
+ false
630
657
  end
631
658
  end
632
659
 
633
- def send_workspace_command _command, *args
634
- command = "workspace/#{ Qcmd.context.workspace.id }/#{ _command }"
635
- send_command(command, *args)
636
- end
637
-
638
- ## QLab commands
639
-
640
660
  def load_cues
641
661
  cues = Qcmd::Action.evaluate('/cueLists')
642
662
  Qcmd.context.workspace.cue_lists = cues.map {|cue_list| Qcmd::QLab::CueList.new(cue_list)}
@@ -61,7 +61,7 @@ module Qcmd
61
61
  end
62
62
 
63
63
  Qcmd.print
64
- Qcmd.print_wrapped('Type `use "WORKSPACE_NAME" PASSCODE` to load a workspace. Passcode is required if workspace is [PROTECTED].')
64
+ Qcmd.print_wrapped('Type `use "WORKSPACE NAME" PASSCODE` to load a workspace. Passcode is required if workspace is [PROTECTED].')
65
65
  Qcmd.print
66
66
  end
67
67
  end
@@ -4,14 +4,17 @@ module Qcmd
4
4
  # Browse the LAN and find open and running QLab instances.
5
5
  class Network
6
6
  BROWSE_TIMEOUT = 2
7
+ IPV4_MATCHER = /\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/
7
8
 
8
9
  class << self
9
10
  attr_accessor :machines, :browse_thread
10
11
 
12
+ def init
13
+ self.machines ||= []
14
+ end
15
+
11
16
  # browse can be used alone to populate the machines list
12
17
  def browse
13
- self.machines = []
14
-
15
18
  self.browse_thread = Thread.start do
16
19
  DNSSD.browse! '_qlab._tcp.' do |b|
17
20
  DNSSD.resolve b.name, b.type, b.domain do |r|
@@ -41,7 +44,7 @@ module Qcmd
41
44
  end
42
45
 
43
46
  Qcmd.print
44
- Qcmd.print 'Type `connect MACHINE` to connect to a machine'
47
+ Qcmd.print 'Type `connect "MACHINE NAME"` or `connect IP_ADDRESS` to connect to a machine'
45
48
  Qcmd.print
46
49
  end
47
50
 
@@ -1,16 +1,18 @@
1
1
  module Qcmd
2
2
  module Plaintext
3
- def log message=nil
4
- if message
5
- puts message
6
- else
7
- puts
3
+ def log level, message=nil
4
+ if Qcmd.log_level_acheived?(level)
5
+ if message
6
+ puts message
7
+ else
8
+ puts
9
+ end
8
10
  end
9
11
  end
10
12
 
11
13
  # display output unless absolutely silent
12
14
  def print message=nil
13
- log(message) unless Qcmd.silent?
15
+ log(:info, message) unless Qcmd.silent?
14
16
  end
15
17
 
16
18
  def set_columns value
@@ -86,6 +88,10 @@ module Qcmd
86
88
  word_wrap(line, options)
87
89
  end
88
90
 
91
+ def joined_wrapped *args
92
+ wrapped_text(*args).join("\n")
93
+ end
94
+
89
95
  def print_wrapped line
90
96
  print wrapped_text(line)
91
97
  end
@@ -44,6 +44,10 @@ module Qcmd
44
44
  # \"armed\":true}]
45
45
 
46
46
  class Cue
47
+ TYPES = %w(audio mic video camera fade osc midi midi file timecode group
48
+ start stop pause load reset devamp goto target arm disarm wait
49
+ memo script cuelist)
50
+
47
51
  attr_accessor :data
48
52
 
49
53
  def initialize options={}
@@ -1,3 +1,3 @@
1
1
  module Qcmd
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  # This class contains our logic for parsing
2
- # S-Expressions. They are turned into a
2
+ # S-Expressions. They are turned into a
3
3
  # native Ruby representation like:
4
4
  # [:def, :something [:lambda, [:a], [:do_something]]]
5
5
  class Sexpistol
@@ -26,7 +26,7 @@ class Sexpistol
26
26
  end
27
27
  end
28
28
  end
29
-
29
+
30
30
  # Convert nil, true and false into (), #t and #f for compatability
31
31
  # with Scheme
32
32
  def convert_scheme_literals(data)
@@ -39,7 +39,7 @@ class Sexpistol
39
39
  end
40
40
  end
41
41
  end
42
-
42
+
43
43
  # Convert a set of nested arrays back into an S-Expression
44
44
  def to_sexp(data)
45
45
  data = convert_scheme_literals(data) if(@scheme_compatability)
@@ -47,6 +47,9 @@ class Sexpistol
47
47
  mapped = data.map do |item|
48
48
  if( item.is_a?(Array))
49
49
  to_sexp(item)
50
+ elsif item.is_a?(String)
51
+ # preserve string literal double quoting
52
+ item.inspect
50
53
  else
51
54
  item.to_s
52
55
  end
@@ -56,9 +59,9 @@ class Sexpistol
56
59
  data.to_s
57
60
  end
58
61
  end
59
-
62
+
60
63
  private
61
-
64
+
62
65
  def recursive_map(data, &block)
63
66
  if(data.is_a?(Array))
64
67
  return data.map do |x|
@@ -72,5 +75,5 @@ class Sexpistol
72
75
  block.call(data)
73
76
  end
74
77
  end
75
-
76
- end
78
+
79
+ end
@@ -32,6 +32,11 @@ describe Qcmd::Parser do
32
32
  tokens.should eql([:cue, 10, :name, [:cue, 3, :name]])
33
33
  end
34
34
 
35
+ it "should parse nested commands with string literals" do
36
+ tokens = Qcmd::Parser.parse 'alias cue-rename (cue $1 name "Hello World")'
37
+ tokens.should eql([:alias, :'cue-rename', [:cue, :'$1', :name, 'Hello World']])
38
+ end
39
+
35
40
  it "should parse alias commands" do
36
41
  tokens = Qcmd::Parser.parse 'alias copy-name (cue 10 name (cue 3 name))'
37
42
  tokens.should eql([:alias, :'copy-name', [:cue, 10, :name, [:cue, 3, :name]]])
@@ -56,4 +61,24 @@ describe Qcmd::Parser do
56
61
  tokens = Qcmd::Parser.parse %[cue 1 name "this is (not good)"]
57
62
  tokens.should eql([:cue, 1, :name, 'this is (not good)'])
58
63
  end
64
+
65
+ ## Generating
66
+
67
+ describe "generating expressions" do
68
+ it "should leave string literals intact" do
69
+ expression = Qcmd::Parser.generate([:cue, :'$1', :name, 'Hello World'])
70
+ expression.should eql('(cue $1 name "Hello World")')
71
+ end
72
+
73
+ it "should handle nesting" do
74
+ expression = Qcmd::Parser.generate([:cue, :'$1', :name, [:cue, :'$2', :name]])
75
+ expression.should eql('(cue $1 name (cue $2 name))')
76
+ end
77
+
78
+ it "should handle escaped double quotes" do
79
+ expression = Qcmd::Parser.generate([:go, 'word word', 10, -12.3, 'life "is good" yeah'])
80
+ expression.should eql('(go "word word" 10 -12.3 "life \"is good\" yeah")')
81
+
82
+ end
83
+ end
59
84
  end
@@ -3,14 +3,14 @@ require 'qcmd'
3
3
  describe Qcmd do
4
4
  # tests go here
5
5
  it "should log debug messages when in verbose mode" do
6
- Qcmd.should_receive(:log).with('hello')
6
+ Qcmd.should_receive(:log).with(:debug, 'hello')
7
7
  Qcmd.verbose!
8
8
  Qcmd.log_level.should eql(:debug)
9
9
  Qcmd.debug 'hello'
10
10
  end
11
11
 
12
12
  it 'should not log debug messages when not in verbose mode' do
13
- Qcmd.should_not_receive(:log)
13
+ Kernel.should_not_receive(:puts)
14
14
  Qcmd.quiet!
15
15
  Qcmd.log_level.should eql(:warning)
16
16
  Qcmd.debug 'hello'
@@ -21,7 +21,7 @@ describe Qcmd do
21
21
  Qcmd.log_level.should eql(:debug)
22
22
 
23
23
  Qcmd.while_quiet do
24
- Qcmd.should_not_receive(:log)
24
+ Kernel.should_not_receive(:puts)
25
25
  Qcmd.log_level.should eql(:warning)
26
26
  Qcmd.debug 'hello'
27
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qcmd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-20 00:00:00.000000000 Z
12
+ date: 2013-04-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dnssd
@@ -132,6 +132,7 @@ extensions: []
132
132
  extra_rdoc_files: []
133
133
  files:
134
134
  - .gitignore
135
+ - CHANGELOG.md
135
136
  - Gemfile
136
137
  - LICENSE.txt
137
138
  - README.md