dtas 0.0.0 → 0.1.I

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Documentation/dtas-console.txt +15 -0
  4. data/Documentation/dtas-player_protocol.txt +5 -3
  5. data/Documentation/dtas-sourceedit.txt +14 -7
  6. data/GIT-VERSION-GEN +3 -3
  7. data/INSTALL +3 -3
  8. data/README +6 -5
  9. data/Rakefile +16 -7
  10. data/bin/dtas-console +48 -3
  11. data/bin/dtas-cueedit +1 -1
  12. data/bin/dtas-sinkedit +12 -28
  13. data/bin/dtas-sourceedit +15 -30
  14. data/lib/dtas.rb +1 -1
  15. data/lib/dtas/command.rb +0 -5
  16. data/lib/dtas/compat_onenine.rb +2 -2
  17. data/lib/dtas/disclaimer.rb +4 -3
  18. data/lib/dtas/edit_client.rb +48 -0
  19. data/lib/dtas/format.rb +2 -9
  20. data/lib/dtas/player.rb +64 -28
  21. data/lib/dtas/player/client_handler.rb +39 -20
  22. data/lib/dtas/process.rb +16 -15
  23. data/lib/dtas/replaygain.rb +19 -3
  24. data/lib/dtas/sink.rb +1 -2
  25. data/lib/dtas/source.rb +1 -141
  26. data/lib/dtas/source/av.rb +29 -0
  27. data/lib/dtas/source/av_ff_common.rb +127 -0
  28. data/lib/dtas/source/{command.rb → cmd.rb} +1 -1
  29. data/lib/dtas/source/ff.rb +30 -0
  30. data/lib/dtas/source/file.rb +94 -0
  31. data/lib/dtas/source/{mp3.rb → mp3gain.rb} +1 -1
  32. data/lib/dtas/source/sox.rb +114 -0
  33. data/lib/dtas/unix_client.rb +1 -9
  34. data/test/player_integration.rb +5 -17
  35. data/test/test_format.rb +10 -14
  36. data/test/test_format_change.rb +4 -8
  37. data/test/test_player_integration.rb +50 -62
  38. data/test/test_process.rb +33 -0
  39. data/test/test_rg_integration.rb +45 -35
  40. data/test/test_sink_pipe_size.rb +20 -0
  41. data/test/test_sink_tee_integration.rb +2 -4
  42. data/test/{test_source.rb → test_source_av.rb} +16 -16
  43. data/test/test_source_sox.rb +115 -0
  44. metadata +23 -12
  45. data/.rsync_doc +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a1e4e92e52c071f8b53fbf9d05c32a276ec9a83
4
- data.tar.gz: d5ab2c9d24f71ae9bd61e2c1b3b4b0ef4fb379ca
3
+ metadata.gz: e1f118957182ff09d25d1da7f0ae4fda9c867e21
4
+ data.tar.gz: a42b9ef9d642cd13bb32a24cf2571a315acb8842
5
5
  SHA512:
6
- metadata.gz: ec862253ef20e2ff0ca445d3795dad21cf1c800312f44443bbce66ea5ff5c51ebc43273db8d51141974b432b76b3a34316140e250b43a179e6d6f5bdcdfdf030
7
- data.tar.gz: ff364dc679cb1e7ea780316588d3af8f098ffe31b132eab165dae446059827f14ee8fda195d725bd2814049116ebe590662bcbb443cf8521fe69070ad1658179
6
+ metadata.gz: 2becf89b867d7183a554c00eed34cf1a8a5f468b051173e072894072b3711fb4c5df40cb2d551c7bf07501fe55e82ac2a3922f93a286ab889fb7cdb853c78974
7
+ data.tar.gz: 6898c1eb14d91ea620f306abec3620c42258ec3d964d40fdd382ec74590cc81b955012047dd8748d88989b9e62a25de3d35f71c175a0ae7985f9aeb084c084ee
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  *.log
8
8
  /man
9
9
  *.gem
10
+ *.gz
@@ -29,6 +29,9 @@ Key bindings are inspired partially by mplayer(1)
29
29
  - SPACE - toggle play/pause state of the playback
30
30
  - 'p'/'P' - decrease/increase timer resolution
31
31
  - BACKSPACE - seek to the beginning of the track
32
+ - 9/0 - decrease/increase ReplayGain preamp
33
+ - 'f'/'F' - decrease/increase ReplayGain fallback_gain value
34
+ - 'r'/'R' - cycle forward/backwards through ReplayGain modes
32
35
  - Ctrl-C - exit dtas-console
33
36
 
34
37
  # ENVIRONMENT
@@ -36,6 +39,18 @@ Key bindings are inspired partially by mplayer(1)
36
39
  DTAS_PLAYER_SOCK - the path to the dtas-player control socket.
37
40
  This defaults to ~/.dtas/player.sock
38
41
 
42
+ # CAVEATS
43
+
44
+ Increasing timer resolution increases the number of CPU wakeups and
45
+ power consumption. This defeats the efforts of dtas-player
46
+ configurations which use large buffers (especially in the sink) to
47
+ reduce wakeups and power consumption.
48
+
49
+ In rare cases (or poorly-configured systems), increased wakeups from
50
+ dtas-console will also increase contention with the sound card,
51
+ negatively affecting audio quality even if CPU utilization is not
52
+ a problem.
53
+
39
54
  # SEE ALSO
40
55
 
41
56
  dtas-player(1), dtas-player_protocol(7), dtas-ctl(1), dtas-sinkedit(1),
@@ -8,7 +8,7 @@ dtas-player_protocol - protocol for controling dtas-player
8
8
  # DESCRIPTION
9
9
 
10
10
  This gives a specification of the dtas-player protocol over a local Unix
11
- SOCK_SEQPACKET or SOCK_DGRAM socket. The dtas-player protocol should NOT be
11
+ SOCK_SEQPACKET socket. The dtas-player protocol should NOT be
12
12
  considered stable at this point and compatibility will break.
13
13
 
14
14
  Inspiration is taken from MPRIS and MPRIS 2.0 specifications (e.g.
@@ -33,6 +33,7 @@ dtas-sinkedit(1), and dtas-enq(1) also implement this protocol.
33
33
  - ENVVALUE - must be a suitable environment variable (setenv(3))
34
34
  - COMMAND, this may be quoted string passed to sh -c "",
35
35
  variable/argument expansion will be performed by the shell
36
+ - SOURCENAME - "sox" or "av", more backends may be supported in the future
36
37
  - FILENAME - an expanded pathname relative to / is recommended since
37
38
  dtas-player and the client may run in different directories
38
39
 
@@ -167,14 +168,15 @@ For little-endian machines, $ECAFMT defaults to: -fs32_le,2,44100
167
168
  + nonblock=BOOLEAN - drop audio data to avoid holding back other sinks
168
169
  + pipe_size=UNSIGNED - set the size of the pipe for the sink (Linux-only)
169
170
 
170
- * source cat - dump the current source command and env in YAML
171
+ * source SOURCENAME cat - dump the current source command and env in YAML
171
172
 
172
- * source ed SOURCEARGS - edit the source (decoder) command and environment.
173
+ * source ed SOURCENAME SOURCEARGS - edit the source parameters.
173
174
  This changes here will immediately restart the source process.
174
175
  See the code for dtas-sourceedit(1) for an example of using this.
175
176
  + command=COMMAND - change the command-line used to decode audio
176
177
  + env.ENVNAME=ENVVALUE - set ENVNAME to ENVVALUE for the source process
177
178
  + env#ENVNAME - unset ENVNAME in the source process (only)
179
+ + tryorder=INTEGER - lower values are tried first
178
180
 
179
181
  * watch - adds the client to the passive watch list for notifications.
180
182
  It is recommended clients issue no further commands and open
@@ -3,23 +3,30 @@
3
3
 
4
4
  # NAME
5
5
 
6
- dtas-sourceedit - edit the command and environment of the decoder
6
+ dtas-sourceedit - edit parameters of a source decoder
7
7
 
8
8
  # SYNOPSYS
9
9
 
10
- dtas-sourceedit
10
+ dtas-sourceedit {sox | av | ff}
11
11
 
12
12
  # DESCRIPTION
13
13
 
14
- dtas-sourceedit spawns an editor to allow editing of a sink as a YAML file.
15
- See dtas-player_protocol(7) for details on SINKARGS.
14
+ dtas-sourceedit spawns an editor to allow editing of a source as a YAML file.
15
+ See dtas-player_protocol(7) for details on SOURCEARGS.
16
16
 
17
17
  # EXAMPLES
18
18
 
19
- Invoking dtas-sourceedit will spawn your favorite text editor on a
20
- given SINKNAME:
19
+ Invoking dtas-sourceedit will spawn your favorite text editor on "sox":
21
20
 
22
- $ dtas-sourceedit
21
+ $ dtas-sourceedit sox
22
+
23
+ To change the way dtas-player calls avconv (part of libav):
24
+
25
+ $ dtas-sourceedit av
26
+
27
+ To change the way dtas-player calls ffmpeg (lightly-tested):
28
+
29
+ $ dtas-sourceedit ff
23
30
 
24
31
  # ENVIRONMENT
25
32
 
@@ -3,7 +3,7 @@
3
3
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
4
  CONSTANT = "DTAS::VERSION"
5
5
  RVF = "lib/dtas/version.rb"
6
- DEF_VER = "v0.0.0"
6
+ DEF_VER = "v0.1.I"
7
7
  vn = DEF_VER
8
8
 
9
9
  # First see if there is a version file (included in release tarballs),
@@ -22,9 +22,9 @@ if File.exist?(".git")
22
22
  end
23
23
 
24
24
  vn = vn.sub!(/\Av/, "")
25
- new_ruby_version = "#{CONSTANT} = '#{vn}'\n"
25
+ new_ruby_version = "#{CONSTANT} = '#{vn}' # :nodoc:\n"
26
26
  cur_ruby_version = File.read(RVF) rescue nil
27
27
  if new_ruby_version != cur_ruby_version
28
- File.open(RVF, "w") { |fp| fp.write("# :enddoc:\n#{new_ruby_version}") }
28
+ File.open(RVF, "w") { |fp| fp.write(new_ruby_version) }
29
29
  end
30
30
  puts vn if $0 == __FILE__
data/INSTALL CHANGED
@@ -40,10 +40,10 @@ Debian users can install sox, mp3gain, and flac dependencies easily:
40
40
 
41
41
  Grab the latest tarball from our HTTP site:
42
42
 
43
- http://dtas.80x24.org/2013/dtas-0.0.0.tar.gz
43
+ http://dtas.80x24.org/2013/dtas-0.1.I.tar.gz
44
44
 
45
- $ tar zxvf dtas-0.0.0.tar.gz
46
- $ cd dtas-0.0.0
45
+ $ tar zxvf dtas-0.1.I.tar.gz
46
+ $ cd dtas-0.1.I
47
47
  $ ruby setup.rb
48
48
 
49
49
  GNU/Linux users may optionally install "io_splice" and
data/README CHANGED
@@ -40,17 +40,18 @@ Users of dtas-player will also be interested in the following scripts:
40
40
  * dtas-xdelay - alternative sink for dtas-player
41
41
 
42
42
  All scripts have some documentation in the Documentation/ directory or
43
- manpages distributed with the gem. dtas exposes no public APIs outside
44
- of command-line and YAML text. dtas is aimed at users familiar with the
45
- *nix command-line and editing text files. Familiarity with the Ruby
46
- programming language is absolutely NOT required.
43
+ manpages distributed with the gem. Documentation is also available on
44
+ http://dtas.80x24.org/$COMMAND.txt in plain-text form.
45
+
46
+ dtas exposes no public APIs outside of command-line and YAML text. dtas is
47
+ aimed at users familiar with the *nix command-line and editing text files.
48
+ Familiarity with the Ruby programming language is absolutely NOT required.
47
49
 
48
50
  Coming:
49
51
 
50
52
  * MPRIS/MPRIS 2.0 bridge for partial dtas-player control
51
53
  * tracklist support in dtas-player (maybe?)
52
54
  * whatever command-line tools come to mind...
53
- * native ffmpeg/avconv/gst support in dtas-player
54
55
  * better error handling, many bugfixes, etc...
55
56
  * better documentation
56
57
 
data/Rakefile CHANGED
@@ -59,9 +59,20 @@ h = Hoe.spec('dtas') do |p|
59
59
  end
60
60
 
61
61
  task :rsync_docs do
62
- dest = "80x24.org:/srv/dtas/"
63
- system("rsync --chmod=Fugo=r --files-from=.rsync_doc -av ./ #{dest}")
64
- system("rsync --chmod=Fugo=r -av ./Documentation/*.txt #{dest}")
62
+ dest = ENV["RSYNC_DEST"] || "80x24.org:/srv/dtas/"
63
+ top = %w(INSTALL NEWS README COPYING)
64
+ files = []
65
+ Dir['Documentation/*.txt'].to_a.concat(top).each do |txt|
66
+ gz = "#{txt}.gz"
67
+ tmp = "#{gz}.#$$"
68
+ sh("gzip -9 < #{txt} > #{tmp}")
69
+ st = File.stat(txt)
70
+ File.utime(st.atime, st.mtime, tmp) # make nginx gzip_static happy
71
+ File.rename(tmp, gz)
72
+ files << txt
73
+ files << gz
74
+ end
75
+ sh("rsync --chmod=Fugo=r -av #{files.join(' ')} #{dest}")
65
76
  end
66
77
 
67
78
  task :coverage do
@@ -81,8 +92,7 @@ task tarball: "pkg/#{base}" do
81
92
  Dir.chdir("pkg") do
82
93
  tgz = "#{base}.tar.gz"
83
94
  tmp = "#{tmp}.#$$"
84
- cmd = "tar cf - #{base} | gzip -9 > #{tmp}"
85
- system(cmd) or abort "#{cmd}: #$?"
95
+ sh "tar cf - #{base} | gzip -9 > #{tmp}"
86
96
  File.rename(tmp, tgz)
87
97
  end
88
98
  end
@@ -90,8 +100,7 @@ end
90
100
  task dist: [ :tarball, :package ] do
91
101
  Dir.chdir("pkg") do
92
102
  %w(dtas-linux dtas-mpris).each do |gem|
93
- cmd = "gem build ../#{gem}.gemspec"
94
- system(cmd) or abort "#{cmd}: #$?"
103
+ sh "gem build ../#{gem}.gemspec"
95
104
  end
96
105
  end
97
106
  end
@@ -5,6 +5,7 @@
5
5
  #
6
6
  # Note: no idea what I'm doing, especially w.r.t. curses
7
7
  require 'dtas/unix_client'
8
+ require 'dtas/rg_state'
8
9
  require 'curses'
9
10
  require 'yaml'
10
11
 
@@ -14,6 +15,14 @@ c = DTAS::UNIXClient.new
14
15
  cur = YAML.load(c.req('current'))
15
16
  readable = [ w, $stdin ]
16
17
 
18
+ # current rg mode
19
+ rg_mode = DTAS::RGState::RG_MODE.keys.unshift("off")
20
+ if (rg = cur["rg"]) && (rg = rg["mode"])
21
+ rg_mode_i = rg_mode.index(cur["rg"]["mode"])
22
+ else
23
+ rg_mode_i = 0
24
+ end
25
+
17
26
  def update_tfmt(prec)
18
27
  prec == 0 ? '%H:%M:%S' : "%H:%M:%S.%#{prec}N"
19
28
  end
@@ -69,6 +78,24 @@ def show_events(lineno, screen, events)
69
78
  end
70
79
  end
71
80
 
81
+ def fmt_to_s(f)
82
+ r = [ f['rate'], f['channels'], f['type'], f['bits'] ]
83
+ r.compact!
84
+ r.join(',')
85
+ end
86
+
87
+ def rg_string(rg, current)
88
+ rv = "rg mode=#{rg['mode']||'off'}"
89
+ defaults = DTAS::RGState::RG_DEFAULT
90
+ # don't show things that are too rare
91
+ %w(preamp fallback_gain).each do |param|
92
+ val = rg[param] || defaults[param]
93
+ rv << " #{param}=#{val}"
94
+ end
95
+ env = current && current["env"] and rv << " / RGFX='#{env['RGFX']}'"
96
+ rv
97
+ end
98
+
72
99
  begin
73
100
  Curses.init_screen
74
101
  Curses.nonl
@@ -79,6 +106,7 @@ begin
79
106
  screen.keypad(true)
80
107
  loop do
81
108
  lineno = -1
109
+ pfmt = cur['format']
82
110
  if current = cur['current']
83
111
  Curses.setpos(lineno += 1, 0)
84
112
  Curses.clrtoeol
@@ -89,13 +117,16 @@ begin
89
117
  rate = current_format['rate'].to_f
90
118
  elapsed += nr / rate
91
119
  total = " [#{Time.at(current['samples'] / rate).strftime(tfmt)}]"
120
+ fmt = "(#{fmt_to_s(current_format)} > #{fmt_to_s(pfmt)})"
92
121
  else
93
122
  total = ""
123
+ fmt = fmt_to_s(pfmt)
124
+ fmt = "(#{fmt} > #{fmt})"
94
125
  end
95
126
 
96
127
  Curses.setpos(lineno += 1, 0)
97
128
  Curses.clrtoeol
98
- Curses.addstr("#{Time.at(elapsed).strftime(tfmt)}#{total}")
129
+ Curses.addstr("#{Time.at(elapsed).strftime(tfmt)}#{total} #{fmt}")
99
130
  else
100
131
  Curses.setpos(lineno += 1, 0)
101
132
  Curses.clrtoeol
@@ -104,6 +135,11 @@ begin
104
135
  Curses.clrtoeol
105
136
  end
106
137
 
138
+ rgs = rg_string(cur["rg"] || {}, current)
139
+ Curses.setpos(lineno += 1, 0)
140
+ Curses.clrtoeol
141
+ Curses.addstr(rgs)
142
+
107
143
  show_events(lineno, screen, events)
108
144
 
109
145
  Curses.refresh # draw and wait
@@ -119,8 +155,8 @@ begin
119
155
  when $stdin
120
156
  # keybindings taken from mplayer / vi
121
157
  case key = Curses.getch
122
- when "j" then c.req_ok("seek +5")
123
- when "k" then c.req_ok("seek -5")
158
+ when "j" then c.req_ok("seek -5")
159
+ when "k" then c.req_ok("seek +5")
124
160
  when Curses::KEY_DOWN then c.req_ok("seek -60")
125
161
  when Curses::KEY_UP then c.req_ok("seek +60")
126
162
  when Curses::KEY_LEFT then c.req_ok("seek -10")
@@ -129,8 +165,17 @@ begin
129
165
  # yes, some of us have long audio files
130
166
  when Curses::KEY_PPAGE then c.req_ok("seek +600")
131
167
  when Curses::KEY_NPAGE then c.req_ok("seek -600")
168
+ when "9" then c.req_ok("rg preamp-=1")
169
+ when "0" then c.req_ok("rg preamp+=1")
170
+ when "F" then c.req_ok("rg fallback_gain+=1")
171
+ when "f" then c.req_ok("rg fallback_gain-=1")
132
172
  when " "
133
173
  c.req("play_pause")
174
+ when "r" # cycle through replaygain modes
175
+ rg_mode_i >= 1 and c.req_ok("rg mode=#{rg_mode[rg_mode_i -= 1]}")
176
+ when "R"
177
+ rg_mode_i < (rg_mode.size - 1) and
178
+ c.req_ok("rg mode=#{rg_mode[rg_mode_i += 1]}")
134
179
  when "p" # lower precision of time display
135
180
  if prec_nr >= 1
136
181
  prec_nr -= 1
@@ -5,7 +5,7 @@
5
5
  require 'tempfile'
6
6
  require 'shellwords'
7
7
  usage = "Usage: #$0 FILENAME"
8
- editor = ENV["VISUAL"] || ENV["EDITOR"]
8
+ editor = ENV["VISUAL"] || ENV["EDITOR"] || "vi"
9
9
  ARGV.size > 0 or abort usage
10
10
 
11
11
  def err_msg(cmd, status)
@@ -2,20 +2,16 @@
2
2
  # -*- encoding: binary -*-
3
3
  # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
4
4
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
5
- require 'dtas/unix_client'
6
- require 'dtas/disclaimer'
7
- require 'tempfile'
8
- require 'yaml'
9
- editor = ENV["VISUAL"] || ENV["EDITOR"]
10
- c = DTAS::UNIXClient.new
11
- usage = "#$0 SINKNAME"
5
+ require 'dtas/edit_client'
6
+ include DTAS::EditClient
7
+ c = client_socket
8
+ sinks = c.req('sink ls') || "(unknown)"
9
+ usage = "Usage: #{DTAS_PROGNAME} SINKNAME\n" \
10
+ "available SINKNAME values: #{sinks}"
12
11
  ARGV.size == 1 or abort usage
13
12
  name = ARGV[0]
14
13
 
15
- tmp = Tempfile.new(%w(dtas-sinkedit .yml))
16
- tmp.sync = true
17
- tmp.binmode
18
-
14
+ tmp = tmpyaml
19
15
  buf = c.req(%W(sink cat #{name}))
20
16
  abort(buf) if buf =~ /\AERR/
21
17
  orig = YAML.load(buf)
@@ -27,32 +23,20 @@ tmp.rewind
27
23
  sink = YAML.load(tmp.read)
28
24
 
29
25
  cmd = %W(sink ed #{name})
30
- if env = sink["env"]
31
- env.each do |k,v|
32
- cmd << (v.nil? ? "env##{k}" : "env.#{k}=#{v}")
33
- end
34
- end
35
-
36
- # remove deleted env
37
- if orig_env = orig["env"]
38
- env ||= {}
39
- deleted_keys = orig_env.keys - env.keys
40
- deleted_keys.each { |k| cmd << "env##{k}" }
41
- end
26
+ update_cmd_env(cmd, orig, sink)
42
27
 
28
+ # both of these default to false
43
29
  %w(nonblock active).each do |field|
44
- if sink.key?(field)
45
- cmd << "#{field}=#{sink[field] ? 'true' : 'false'}"
46
- end
30
+ cmd << "#{field}=#{sink[field] ? 'true' : 'false'}"
47
31
  end
48
32
 
49
33
  %w(prio pipe_size).each do |field|
50
34
  value = sink[field] and cmd << "#{field}=#{value}"
51
35
  end
52
36
 
37
+ # nil OK
53
38
  %w(command).each do |field|
54
- value = sink[field]
55
- cmd << "#{field}=#{value}"
39
+ cmd << "#{field}=#{sink[field]}"
56
40
  end
57
41
 
58
42
  c.req_ok(cmd)
@@ -2,21 +2,17 @@
2
2
  # -*- encoding: binary -*-
3
3
  # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
4
4
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
5
- require 'tempfile'
6
- require 'yaml'
7
- require 'dtas/unix_client'
8
- require 'dtas/disclaimer'
9
- editor = ENV["VISUAL"] || ENV["EDITOR"]
10
- c = DTAS::UNIXClient.new
11
- usage = $0
12
- ARGV.size == 0 or abort usage
13
- name = ARGV[0]
5
+ require 'dtas/edit_client'
6
+ include DTAS::EditClient
7
+ c = client_socket
8
+ sources = c.req('source ls') || "(unknown)"
9
+ usage = "Usage: #{DTAS_PROGNAME} SOURCENAME\n" \
10
+ "available SOURCENAME values: #{sources}"
11
+ ARGV.size <= 1 or abort usage
12
+ name = ARGV[0] || "sox"
14
13
 
15
- tmp = Tempfile.new(%w(dtas-sourceedit .yml))
16
- tmp.sync = true
17
- tmp.binmode
18
-
19
- buf = c.req(%W(source cat))
14
+ tmp = tmpyaml
15
+ buf = c.req(%W(source cat #{name}))
20
16
  abort(buf) if buf =~ /\AERR/
21
17
  orig = YAML.load(buf)
22
18
 
@@ -26,23 +22,12 @@ system(cmd) or abort "#{cmd} failed: #$?"
26
22
  tmp.rewind
27
23
  source = YAML.load(tmp.read)
28
24
 
29
- cmd = %W(source ed)
30
- if env = source["env"]
31
- env.each do |k,v|
32
- cmd << (v.nil? ? "env##{k}" : "env.#{k}=#{v}")
33
- end
34
- end
35
-
36
- # remove deleted env
37
- if orig_env = orig["env"]
38
- env ||= {}
39
- deleted_keys = orig_env.keys - env.keys
40
- deleted_keys.each { |k| cmd << "env##{k}" }
41
- end
25
+ cmd = %W(source ed #{name})
26
+ update_cmd_env(cmd, orig, source)
42
27
 
43
- %w(command).each do |field|
44
- value = source[field]
45
- cmd << "#{field}=#{value}"
28
+ # nil OK
29
+ %w(tryorder command).each do |field|
30
+ cmd << "#{field}=#{source[field]}"
46
31
  end
47
32
 
48
33
  c.req_ok(cmd)