qcmd 0.1.8 → 0.1.9

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