sys_cmd 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 307b77667d06748740cea3b10c0b0112475dac60
4
- data.tar.gz: d584ecfcfb2cbbdce499adede7c68bf59014cd24
3
+ metadata.gz: 1699ccea4cb7b0bc8248c247383683ba89dfa43a
4
+ data.tar.gz: eeb6ffc30f8bfc9a9cec6ad680adcd0c7b33fed8
5
5
  SHA512:
6
- metadata.gz: 24290cf375b93fb4414d85f6534535285fd528b11be30f37fa5662226382df7eaae663006ce6c01133dff20fd1e1ab13c4878ec0df1ab04079e1af41e0a961fe
7
- data.tar.gz: 85b2d3089a50c50e30d25bd70b9a0ea3ce19c0661d8d486a8d2d10759a988eecd224ca4d1a4f8cb15c4c5b0c699389ad9bea53b7aaa630e7891dcef8b4e05dfc
6
+ metadata.gz: 316a8757c455ecd7ae0cb136a5b444e70c33fcee0263277dd287421d1360bef5e4922f31fc29877f34b92d9a2c197838f68dd45ffe6b6be3eab462ec29f360c1
7
+ data.tar.gz: 480aaa5ec37aad47601d8890be1a164e1eda342d688bb88c0a7c70d8d1d86c23a3d0272b66c1e512f6e761608dc386975e30de216ac6956511d0f970a77009f8
data/.travis.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.1
3
+ - 2.2.2
4
+ - 2.1.6
5
+ - 1.9.3
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # SysCmd
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/sys_cmd.svg)](http://badge.fury.io/rb/sys_cmd)
4
+ [![Build Status](https://travis-ci.org/jgoizueta/sys_cmd.svg)](https://travis-ci.org/jgoizueta/sys_cmd)
5
+
3
6
  SysCmd is a DSL to define commands to be executed by the system.
4
7
 
5
8
  The command arguments will we escaped properly for bash or
@@ -25,19 +28,82 @@ Or install it yourself as:
25
28
 
26
29
  ## Usage
27
30
 
28
- Example:
31
+ A command can be defined with a simple DSL (passing a block that defines
32
+ the command arguments to the SysCmd.command method):
29
33
 
30
34
  cmd = SysCmd.command 'ffmpeg' do
31
35
  option '-i', file: 'input video file.mkv'
32
36
  option '-vcodec', 'mjpeg'
33
37
  file 'output.mkv'
34
38
  end
39
+
40
+ The block is executed with +instance_eval+ inside the command definition
41
+ (an instance of SysCmd::Definicion), so +self+ and instance variables refer
42
+ to the definition. If this is not desirable an argument can be passed to
43
+ the block with the +Definition+ object:
44
+
45
+ cmd = SysCmd.command 'ffmpeg' do |cmd|
46
+ cmd.option '-i', file: 'input video file.mkv'
47
+ cmd.option '-vcodec', 'mjpeg'
48
+ cmd.file 'output.mkv'
49
+ end
50
+
51
+ The command can be converted to a String which represents it with
52
+ arguments quoted for the target OS/shell (here we assume a UN*X system)
53
+
35
54
  puts cmd.to_s # ffmpeg -i input\ video\ file.mkv -vcodec mjpeg output.mkv
55
+
56
+ A command can be generated for a system different from the current host
57
+ by passing the +:os+ option:
58
+
59
+ wcmd = SysCmd.command 'ffmpeg', os: :windows do |cmd|
60
+ cmd.option '-i', file: 'input video file.mkv'
61
+ cmd.option '-vcodec', 'mjpeg'
62
+ cmd.file 'output.mkv'
63
+ end
64
+ puts cmd.to_s # ffmpeg -i "input video file.mkv" -vcodec mjpeg "output.mkv"
65
+
66
+ Currently only +:windows+ (for CMD.EXE syntax) and +:unix+ (for bash syntax) are
67
+ accepted for the +:os+ parameter. +:unix+ represent any UN*X-like system
68
+ (including linux, OSX, etc.)
69
+
70
+ A Command can also be executed:
71
+
36
72
  cmd.run
37
73
  if cmd.success?
38
74
  puts cmd.output
39
75
  end
40
76
 
77
+ By default execution is done by launching a shell to interpret the command.
78
+ Unquoted arguments will be interpreted by the shell in that case:
79
+
80
+ cmd = SysCmd.command 'echo' do
81
+ argument '$BASH'
82
+ end
83
+ cmd.run
84
+ puts cmd.output # /bin/bash
85
+
86
+ Shell execution can be avoided by passing the +:direct+ option with value
87
+ +true+ to the +run+ method. In that case the command is executed directly,
88
+ and no shell interpretation takes place, so:
89
+
90
+ cmd.run direct: true
91
+ puts cmd.output # $BASH
92
+
93
+ If the command options include
94
+ an option with the name of the command being defined it is used to
95
+ replace the command name. This can be handy to pass user configuration
96
+ to define the location/name of commands in a particular system:
97
+
98
+ options = {
99
+ curl: "/usr/local/bin/curl"
100
+ }
101
+ cmd = SysCmd.command 'curl', options do
102
+ file 'http://jsonip.com'
103
+ end
104
+ puts cmd.to_s # /usr/local/bin/curl http://jsonip.com
105
+
106
+
41
107
  ## Development
42
108
 
43
109
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
data/lib/sys_cmd.rb CHANGED
@@ -26,7 +26,7 @@ module SysCmd
26
26
  @last_arg = :command
27
27
  end
28
28
 
29
- attr_reader :command
29
+ attr_reader :command, :shell
30
30
 
31
31
  def to_s
32
32
  command
@@ -124,14 +124,13 @@ module SysCmd
124
124
  @last_arg = :file
125
125
  end
126
126
 
127
- # Add the value of an option.
127
+ # Add the value of an option (or a quoted argument)
128
128
  #
129
129
  # option '-x'
130
130
  # join_value 123 # -x 123
131
131
  #
132
132
  def value(value, options = {})
133
133
  return unless @shell.applicable?(options)
134
- raise "An option is required for value" unless @last_arg == :option
135
134
  @command << ' ' << @shell.escape_filename(value.to_s)
136
135
  @last_arg = :value
137
136
  end
@@ -162,10 +161,12 @@ module SysCmd
162
161
  @last_arg = :value
163
162
  end
164
163
 
165
- # Add a generic argument to the command.
164
+ # Add an unquoted argument to the command.
165
+ # This is not useful for commands executed directly, since the arguments
166
+ # are note interpreted by a shell in that case.
166
167
  def argument(value, options = {})
167
168
  return unless @shell.applicable?(options)
168
- @command << ' ' << @shell.escape_value(value)
169
+ @command << ' ' << value.to_s
169
170
  @last_arg = :argument
170
171
  end
171
172
 
@@ -173,8 +174,14 @@ module SysCmd
173
174
 
174
175
  # An executable system command
175
176
  class Command
176
- def initialize(command)
177
- @command = command
177
+ def initialize(command, options = {})
178
+ if command.respond_to?(:shell)
179
+ @command = command.command
180
+ @shell = command.shell
181
+ else
182
+ @command = command
183
+ @shell = Shell.new(options)
184
+ end
178
185
  @output = nil
179
186
  @status = nil
180
187
  @error_output = nil
@@ -185,6 +192,17 @@ module SysCmd
185
192
 
186
193
  # Execute the command.
187
194
  #
195
+ # By default the command is executed by a shell. In this case,
196
+ # unquoted arguments are interpreted by the shell, e.g.
197
+ #
198
+ # SysCmd.command('echo $BASH').run # /bin/bash
199
+ #
200
+ # When the +:direct+ option is set to true, no shell is used and
201
+ # the command is directly executed; in this case unquoted arguments
202
+ # are not interpreted:
203
+ #
204
+ # SysCmd.command('echo $BASH').run # $BASH
205
+ #
188
206
  # The exit status of the command is retained in the +status+ attribute
189
207
  # (and its numeric value in the +status_value+ attribute).
190
208
  #
@@ -213,14 +231,19 @@ module SysCmd
213
231
  #
214
232
  def run(options = {})
215
233
  @output = @status = @error_output = @error = nil
234
+ if options[:direct]
235
+ command = @shell.split(@command)
236
+ else
237
+ command = [@command]
238
+ end
216
239
  begin
217
240
  case options[:error_output]
218
241
  when :mix # mix stderr with stdout
219
- @output, @status = Open3.capture2e(@command)
242
+ @output, @status = Open3.capture2e(*command)
220
243
  when :separate
221
- @output, @error_output, @status = Open3.capture3(@command)
244
+ @output, @error_output, @status = Open3.capture3(*command)
222
245
  else # :console (do not capture stderr output)
223
- @output, @status = Open3.capture2(@command)
246
+ @output, @status = Open3.capture2(*command)
224
247
  end
225
248
  rescue => error
226
249
  @error = error.dup
@@ -265,8 +288,14 @@ module SysCmd
265
288
  #
266
289
  def self.command(command, options = {}, &block)
267
290
  definition = Definition.new(command, options)
268
- definition.instance_eval &block if block
269
- Command.new definition.command
291
+ if block
292
+ if block.arity == 1
293
+ block.call definition
294
+ else
295
+ definition.instance_eval &block
296
+ end
297
+ end
298
+ Command.new definition
270
299
  end
271
300
 
272
301
  # Build and run a command
@@ -305,6 +334,26 @@ module SysCmd
305
334
  end
306
335
  end
307
336
 
337
+ def self.split(text, options = {})
338
+ case os_type(options)
339
+ when :windows
340
+ words = []
341
+ field = ''
342
+ line.scan(/\G\s*(?>([^\s\^\'\"]+)|'([^\']*)'|"((?:[^\"\^]|\\.)*)"|(\^.?)|(\S))(\s|\z)?/m) do
343
+ |word, sq, dq, esc, garbage, sep|
344
+ raise ArgumentError, "Unmatched double quote: #{line.inspect}" if garbage
345
+ field << (word || sq || (dq || esc).gsub(/\^(.)/, '\\1'))
346
+ if sep
347
+ words << field
348
+ field = ''
349
+ end
350
+ end
351
+ words
352
+ else
353
+ Shellwords.shellsplit(text)
354
+ end
355
+ end
356
+
308
357
  def self.line_separator(options = {})
309
358
  case os_type(options)
310
359
  when :windows
@@ -335,6 +384,10 @@ module SysCmd
335
384
  SysCmd.escape(text, os: @type)
336
385
  end
337
386
 
387
+ def split(text)
388
+ SysCmd.split(text, os: @type)
389
+ end
390
+
338
391
  def escape_filename(name)
339
392
  escape name
340
393
  end
@@ -1,3 +1,3 @@
1
1
  module SysCmd
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/sys_cmd.gemspec CHANGED
@@ -24,4 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 1.9"
25
25
  spec.add_development_dependency "rake", "~> 10.0"
26
26
  spec.add_development_dependency "minitest", "~> 5.4"
27
+
28
+ spec.required_ruby_version = '>= 1.9.3'
27
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sys_cmd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javier Goizueta
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-04-26 00:00:00.000000000 Z
11
+ date: 2015-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: os
@@ -96,7 +96,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
96
  requirements:
97
97
  - - ">="
98
98
  - !ruby/object:Gem::Version
99
- version: '0'
99
+ version: 1.9.3
100
100
  required_rubygems_version: !ruby/object:Gem::Requirement
101
101
  requirements:
102
102
  - - ">="
@@ -104,8 +104,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  version: '0'
105
105
  requirements: []
106
106
  rubyforge_project:
107
- rubygems_version: 2.4.6
107
+ rubygems_version: 2.2.2
108
108
  signing_key:
109
109
  specification_version: 4
110
110
  summary: Execute shell commands.
111
111
  test_files: []
112
+ has_rdoc: