rprogram 0.2.3 → 0.3.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.
@@ -0,0 +1,316 @@
1
+ require 'rprogram/exceptions/program_not_found'
2
+ require 'rprogram/rprogram'
3
+
4
+ require 'env/variables'
5
+
6
+ module RProgram
7
+ #
8
+ # @since 0.3.0
9
+ #
10
+ module System
11
+ extend Env::Variables
12
+
13
+ @arch, @platform = RUBY_PLATFORM.split('-',2)
14
+ @platform ||= @arch
15
+
16
+ #
17
+ # Determines the native architecture.
18
+ #
19
+ # @return [String]
20
+ # The native architecture.
21
+ #
22
+ # @example
23
+ # System.arch
24
+ # # => "x86-64"
25
+ #
26
+ # @since 0.3.0
27
+ #
28
+ def System.arch
29
+ @arch
30
+ end
31
+
32
+ #
33
+ # Determines the native platform.
34
+ #
35
+ # @return [String]
36
+ # The native platform.
37
+ #
38
+ # @example
39
+ # System.platform
40
+ # # => "linux"
41
+ #
42
+ def System.platform
43
+ @platform
44
+ end
45
+
46
+ #
47
+ # Determines if the platform is Windows.
48
+ #
49
+ # @return [Boolean]
50
+ # Specifies whether the platform is Windows.
51
+ #
52
+ # @since 0.3.0
53
+ #
54
+ def System.windows?
55
+ if @platform
56
+ @platform.include?('mingw') || @platform.include?('mswin')
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ #
63
+ # Determines if the current Ruby VM is from the 1.8.x family.
64
+ #
65
+ # @return [Boolean]
66
+ # Specifies if the current Ruby VM is from the 1.8.x family.
67
+ #
68
+ # @since 0.3.0
69
+ #
70
+ def System.ruby_1_8?
71
+ RUBY_VERSION[0,4] == '1.8.'
72
+ end
73
+
74
+ #
75
+ # Determines if the current Ruby VM is JRuby.
76
+ #
77
+ # @return [Boolean]
78
+ # Specifies whether the Ruby VM is JRuby.
79
+ #
80
+ # @since 0.3.0
81
+ #
82
+ def System.jruby?
83
+ Object.const_defined?(:RUBY_ENGINE) && \
84
+ Object.const_get(:RUBY_ENGINE) == 'jruby'
85
+ end
86
+
87
+ #
88
+ # Finds the full-path of the program with the matching name.
89
+ #
90
+ # @param [String] name
91
+ # The name of the program to find.
92
+ #
93
+ # @return [Pathname, nil]
94
+ # The full-path of the desired program.
95
+ #
96
+ # @example
97
+ # System.find_program('as')
98
+ # #=> #<Pathname:/usr/bin/as>
99
+ #
100
+ def System.find_program(name)
101
+ # add the `.exe` suffix to the name, if running on Windows
102
+ if windows?
103
+ name = "#{name}.exe"
104
+ end
105
+
106
+ paths.each do |dir|
107
+ full_path = dir.join(name).expand_path
108
+
109
+ return full_path if full_path.file?
110
+ end
111
+
112
+ return nil
113
+ end
114
+
115
+ #
116
+ # Finds the program matching one of the matching names.
117
+ #
118
+ # @param [Array] names
119
+ # The names of the program to use while searching for the program.
120
+ #
121
+ # @return [Pathname, nil]
122
+ # The first full-path for the program.
123
+ #
124
+ # @example
125
+ # System.find_program_by_names("gas","as")
126
+ # # => #<Pathname:/usr/bin/as>
127
+ #
128
+ def System.find_program_by_names(*names)
129
+ names.each do |name|
130
+ if (path = find_program(name))
131
+ return path
132
+ end
133
+ end
134
+
135
+ return nil
136
+ end
137
+
138
+ #
139
+ # Runs a program.
140
+ #
141
+ # @overload run(path,*arguments)
142
+ # Run the program with the given arguments.
143
+ #
144
+ # @param [Pathname, String] path
145
+ # The path of the program to run.
146
+ #
147
+ # @param [Array] arguments
148
+ # Additional arguments to run the program with.
149
+ #
150
+ # @overload run(path,*arguments,options)
151
+ # Run the program with the given arguments and options.
152
+ #
153
+ # @param [Pathname, String] path
154
+ # The path of the program to run.
155
+ #
156
+ # @param [Array] arguments
157
+ # Additional arguments to run the program with.
158
+ #
159
+ # @param [Hash] options
160
+ # Additional options to execute the program with.
161
+ #
162
+ # @option options [Hash{String => String}] :env
163
+ # Environment variables to execute the program with.
164
+ #
165
+ # @option options [String] :popen
166
+ # Specifies to run the program using `IO.popen` with the given
167
+ # IO mode.
168
+ #
169
+ # @return [Boolean]
170
+ # Specifies whether the program exited successfully.
171
+ #
172
+ # @raise [RuntimeError]
173
+ # Passing `:popen`, `:env` or exec options is not supported
174
+ # before Ruby 1.9.1.
175
+ #
176
+ # @see http://rubydoc.info/stdlib/core/1.9.2/Kernel#spawn-instance_method
177
+ # For acceptable options.
178
+ #
179
+ def System.run(*arguments)
180
+ # extra tailing options and ENV variables from arguments
181
+ if arguments.last.kind_of?(Hash)
182
+ options = arguments.pop
183
+ env = (options.delete(:env) || {})
184
+ popen = options.delete(:popen)
185
+ else
186
+ options = {}
187
+ env = {}
188
+ end
189
+
190
+ # all arguments must be Strings
191
+ arguments = arguments.map { |arg| arg.to_s }
192
+
193
+ # print debugging information
194
+ if RProgram.debug
195
+ command = ''
196
+
197
+ env.each do |name,value|
198
+ command << "#{name}=#{value} "
199
+ end
200
+
201
+ command << arguments.join(' ')
202
+ command << " #{options.inspect}" unless options.empty?
203
+
204
+ STDERR.puts ">>> #{command}"
205
+ end
206
+
207
+ # passing ENV variables or exec options is not supported before 1.9.1
208
+ if (!options.empty? && ruby_1_8?)
209
+ raise("cannot pass exec options to Kernel.system in #{RUBY_VERSION}")
210
+ end
211
+
212
+ if popen
213
+ # IO.popen does not accept multiple arguments on Ruby 1.8.x.
214
+ if ruby_1_8?
215
+ raise("cannot use :popen on #{RUBY_VERSION}, please use 1.9.x")
216
+ end
217
+
218
+ # :popen can only be used on Unix, or on Windows with JRuby
219
+ if (windows? && !jruby?)
220
+ raise("cannot use :popen on Windows, unless under JRuby")
221
+ end
222
+ end
223
+
224
+ # re-add ENV variables and exec options
225
+ arguments.unshift(env) unless env.empty?
226
+ arguments.push(options) unless options.empty?
227
+
228
+ if popen
229
+ IO.popen(arguments,popen)
230
+ else
231
+ Kernel.system(*arguments)
232
+ end
233
+ end
234
+
235
+ #
236
+ # The path to the `sudo` program.
237
+ #
238
+ # @return [Pathname, nil]
239
+ # The path to the `sudo` program.
240
+ #
241
+ # @since 0.3.0
242
+ #
243
+ def System.sudo_path
244
+ @sudo ||= find_program('sudo')
245
+ end
246
+
247
+ #
248
+ # Sets the path to the `sudo` program.
249
+ #
250
+ # @param [String, Pathname] path
251
+ # The new path to use.
252
+ #
253
+ # @return [Pathanme]
254
+ # The new path to the `sudo` program.
255
+ #
256
+ # @since 0.3.0
257
+ #
258
+ def System.sudo_path=(path)
259
+ @sudo = Pathname.new(path)
260
+ end
261
+
262
+ #
263
+ # Determines whether `sudo` is available on the system.
264
+ #
265
+ # @return [Boolean]
266
+ # Specifies whether the `sudo` program is installed on the system.
267
+ #
268
+ # @since 0.3.0
269
+ #
270
+ def System.sudo?
271
+ !sudo_path.nil?
272
+ end
273
+
274
+ #
275
+ # Runs a program under sudo.
276
+ #
277
+ # @overload run(path,*arguments)
278
+ # Run the program with the given arguments.
279
+ #
280
+ # @param [Pathname, String] path
281
+ # The path of the program to run.
282
+ #
283
+ # @param [Array] arguments
284
+ # Additional arguments to run the program with.
285
+ #
286
+ # @overload run(path,*arguments,options)
287
+ # Run the program with the given arguments and options.
288
+ #
289
+ # @param [Pathname, String] path
290
+ # The path of the program to run.
291
+ #
292
+ # @param [Array] arguments
293
+ # Additional arguments to run the program with.
294
+ #
295
+ # @param [Hash] options
296
+ # Additional options to execute the program with.
297
+ #
298
+ # @return [Boolean]
299
+ # Specifies whether the program exited successfully.
300
+ #
301
+ # @raise [ProgramNotFound]
302
+ # Indicates that the `sudo` program could not be located.
303
+ #
304
+ # @since 0.1.8
305
+ #
306
+ # @see run
307
+ #
308
+ def System.sudo(*arguments)
309
+ unless sudo?
310
+ raise(ProgramNotFound,'could not find the "sudo" program')
311
+ end
312
+
313
+ return run(sudo_path,*arguments)
314
+ end
315
+ end
316
+ end
data/lib/rprogram/task.rb CHANGED
@@ -1,14 +1,10 @@
1
- require 'rprogram/options'
1
+ require 'rprogram/option'
2
2
  require 'rprogram/option_list'
3
+ require 'rprogram/non_option'
3
4
 
4
5
  module RProgram
5
6
  class Task
6
7
 
7
- include Options
8
-
9
- # Specifies whether the task will be run under sudo
10
- attr_accessor :sudo
11
-
12
8
  #
13
9
  # Creates a new Task object.
14
10
  #
@@ -30,13 +26,119 @@ module RProgram
30
26
  # end
31
27
  #
32
28
  def initialize(options={},&block)
33
- @sudo = (options.delete(:sudo) || false)
34
29
  @subtasks = {}
35
30
  @options = options
36
31
 
37
32
  block.call(self) if block
38
33
  end
39
34
 
35
+ #
36
+ # @return [Hash]
37
+ # All defined non-options of the class.
38
+ #
39
+ def self.non_options
40
+ @non_options ||= {}
41
+ end
42
+
43
+ #
44
+ # Searches for the non-option with the matching name in the class
45
+ # and it's ancestors.
46
+ #
47
+ # @param [Symbol, String] name
48
+ # The name to search for.
49
+ #
50
+ # @return [true, false]
51
+ # Specifies whether the non-option with the matching name was
52
+ # defined.
53
+ #
54
+ def self.has_non_option?(name)
55
+ name = name.to_sym
56
+
57
+ ancestors.each do |base|
58
+ if base < RProgram::Task
59
+ return true if base.non_options.include?(name)
60
+ end
61
+ end
62
+
63
+ return false
64
+ end
65
+
66
+ #
67
+ # Searches for the non-option with the matching name in the class
68
+ # and it's ancestors.
69
+ #
70
+ # @param [Symbol, String] name
71
+ # The name to search for.
72
+ #
73
+ # @return [NonOption]
74
+ # The non-option with the matching name.
75
+ #
76
+ def self.get_non_option(name)
77
+ name = name.to_sym
78
+
79
+ ancestors.each do |base|
80
+ if base < RProgram::Task
81
+ if base.non_options.has_key?(name)
82
+ return base.non_options[name]
83
+ end
84
+ end
85
+ end
86
+
87
+ return nil
88
+ end
89
+
90
+ #
91
+ # @return [Hash]
92
+ # All defined options for the class.
93
+ #
94
+ def self.options
95
+ @options ||= {}
96
+ end
97
+
98
+ #
99
+ # Searches for the option with the matching name in the class and
100
+ # it's ancestors.
101
+ #
102
+ # @param [Symbol, String] name
103
+ # The name to search for.
104
+ #
105
+ # @return [true, false]
106
+ # Specifies whether the option with the matching name was defined.
107
+ #
108
+ def self.has_option?(name)
109
+ name = name.to_sym
110
+
111
+ ancestors.each do |base|
112
+ if base < RProgram::Task
113
+ return true if base.options.has_key?(name)
114
+ end
115
+ end
116
+
117
+ return false
118
+ end
119
+
120
+ #
121
+ # Searches for the option with the matching name in the class and
122
+ # it's ancestors.
123
+ #
124
+ # @param [Symbol, String] name
125
+ # The name to search for.
126
+ #
127
+ # @return [Option]
128
+ # The option with the matching name.
129
+ #
130
+ def self.get_option(name)
131
+ name = name.to_sym
132
+
133
+ ancestors.each do |base|
134
+ if base < RProgram::Task
135
+ return base.options[name] if base.options.has_key?(name)
136
+ end
137
+ end
138
+
139
+ return nil
140
+ end
141
+
40
142
  #
41
143
  # Creates a new Task object, then formats command-line arguments
42
144
  # using the Task object.
@@ -69,15 +171,31 @@ module RProgram
69
171
  end
70
172
 
71
173
  #
72
- # Specifies whether the task will be ran under sudo.
174
+ # @see has_non_option?
175
+ #
176
+ def has_non_option?(name)
177
+ self.class.has_non_option?(name)
178
+ end
179
+
180
+ #
181
+ # @see get_non_option
182
+ #
183
+ def get_non_option(name)
184
+ self.class.get_non_option(name)
185
+ end
186
+
187
+ #
188
+ # @see has_option?
73
189
  #
74
- # @return [Boolean]
75
- # Returns `true` if sudo is enabled, returns `false` otherwise.
190
+ def has_option?(name)
191
+ self.class.has_option?(name)
192
+ end
193
+
76
194
  #
77
- # @since 0.1.8
195
+ # @see get_option
78
196
  #
79
- def sudo?
80
- @sudo == true
197
+ def get_option(name)
198
+ self.class.get_option(name)
81
199
  end
82
200
 
83
201
  #
@@ -166,9 +284,17 @@ module RProgram
166
284
  # options and tailing non-options of the task and it's sub-tasks.
167
285
  #
168
286
  def arguments
169
- leading_non_options + options + tailing_non_options
287
+ tailing_args = tailing_non_options
288
+
289
+ if tailing_args.any? { |arg| arg[0,1] == '-' }
290
+ tailing_args.unshift('--')
291
+ end
292
+
293
+ return leading_non_options + options + tailing_args
170
294
  end
171
295
 
296
+ alias to_a arguments
297
+
172
298
  protected
173
299
 
174
300
  #
@@ -185,20 +311,22 @@ module RProgram
185
311
  #
186
312
  def self.subtask(name,task)
187
313
  name = name.to_s
314
+ file = __FILE__
315
+ line = __LINE__ + 3
188
316
 
189
317
  class_eval %{
190
318
  def #{name}(options={},&block)
191
319
  if @subtasks[#{name.dump}]
192
320
  @subtasks[#{name.dump}].options.merge!(options)
193
321
 
194
- block.call(@subtasks[#{name.dump}]) if block
322
+ yield(@subtasks[#{name.dump}]) if block_given?
195
323
  else
196
324
  @subtasks[#{name.dump}] = #{task}.new(options,&block)
197
325
  end
198
326
 
199
327
  return @subtasks[#{name.dump}]
200
328
  end
201
- }
329
+ }, file, line
202
330
  end
203
331
 
204
332
  #