dragonfly 0.9.5 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of dragonfly might be problematic. Click here for more details.

Files changed (39) hide show
  1. data/Gemfile +1 -2
  2. data/History.md +29 -0
  3. data/README.md +1 -1
  4. data/VERSION +1 -1
  5. data/dragonfly.gemspec +15 -14
  6. data/extra_docs/DataStorage.md +5 -2
  7. data/extra_docs/Rails3.md +6 -4
  8. data/features/steps/rails_steps.rb +18 -6
  9. data/features/support/setup.rb +10 -1
  10. data/fixtures/rails/files/features/step_definitions/image_steps.rb +1 -1
  11. data/lib/dragonfly.rb +4 -0
  12. data/lib/dragonfly/analysis/file_command_analyser.rb +2 -1
  13. data/lib/dragonfly/data_storage/couch_data_store.rb +1 -1
  14. data/lib/dragonfly/data_storage/mongo_data_store.rb +4 -2
  15. data/lib/dragonfly/data_storage/s3data_store.rb +5 -1
  16. data/lib/dragonfly/image_magick/generator.rb +2 -2
  17. data/lib/dragonfly/image_magick/processor.rb +2 -2
  18. data/lib/dragonfly/image_magick/utils.rb +3 -25
  19. data/lib/dragonfly/rails/images.rb +17 -12
  20. data/lib/dragonfly/railtie.rb +1 -1
  21. data/lib/dragonfly/shell.rb +44 -0
  22. data/lib/dragonfly/temp_object.rb +13 -6
  23. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +1 -0
  24. data/spec/dragonfly/cookie_monster_spec.rb +4 -2
  25. data/spec/dragonfly/data_storage/file_data_store_spec.rb +1 -1
  26. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +23 -8
  27. data/spec/dragonfly/image_magick/generator_spec.rb +0 -5
  28. data/spec/dragonfly/job_spec.rb +5 -1
  29. data/spec/dragonfly/shell_spec.rb +34 -0
  30. data/spec/dragonfly/temp_object_spec.rb +44 -12
  31. data/spec/functional/image_magick_app_spec.rb +1 -1
  32. data/spec/functional/remote_on_the_fly_spec.rb +6 -6
  33. data/spec/functional/shell_commands_spec.rb +23 -0
  34. data/spec/functional/to_response_spec.rb +1 -1
  35. data/spec/spec_helper.rb +6 -3
  36. data/spec/support/image_matchers.rb +1 -0
  37. data/tmp/.gitignore +2 -0
  38. metadata +71 -85
  39. data/spec/dragonfly/image_magick/utils_spec.rb +0 -18
data/Gemfile CHANGED
@@ -13,8 +13,7 @@ group :development, :test, :cucumber do
13
13
  gem 'mongo'
14
14
  gem 'couchrest', '~> 1.0'
15
15
  gem 'rack-cache'
16
- gem 'rails', '3.0.9', :require => nil
17
- gem 'rake', '0.8.7'
16
+ gem 'rails', '~>3.1.0', :require => nil
18
17
  gem 'rspec', '~> 2.5'
19
18
  gem 'webmock'
20
19
  gem 'yard'
data/History.md CHANGED
@@ -1,3 +1,32 @@
1
+ 0.9.8 (2011-09-08)
2
+ ==================
3
+ Fixes
4
+ -----
5
+ - Regenerated gemspec again with ruby 1.8.7 - didn't seem to be fixed
6
+
7
+ 0.9.7 (2011-09-08)
8
+ ==================
9
+ Fixes
10
+ -----
11
+ - Regenerated gemspec to overcome annoying yaml issue (http://blog.rubygems.org/2011/08/31/shaving-the-yaml-yacc.html)
12
+
13
+ 0.9.6 (2011-09-06)
14
+ ==================
15
+ Features
16
+ --------
17
+ - Allow setting `content_type` when storing in Mongo GridFS
18
+
19
+ Changes
20
+ -------
21
+ - Tests use Rails 3.1
22
+
23
+ Fixes
24
+ -----
25
+ - Moved from fog's deprecated `get_object_url` to `get_object_https_url`
26
+ - Allow initializing a TempObject with Rack::Test::UploadedFile
27
+ - Tests working in Windows (except feature that uses FileCommandAnalyser)
28
+ - Better shell quoting
29
+
1
30
  0.9.5 (2011-07-27)
2
31
  ==================
3
32
  Features
data/README.md CHANGED
@@ -12,7 +12,7 @@ For the lazy Rails user...
12
12
  **Gemfile**:
13
13
 
14
14
  gem 'rack-cache', :require => 'rack/cache'
15
- gem 'dragonfly', '~>0.9.4'
15
+ gem 'dragonfly', '~>0.9.8'
16
16
 
17
17
  **Initializer** (e.g. config/initializers/dragonfly.rb):
18
18
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.5
1
+ 0.9.8
data/dragonfly.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{dragonfly}
8
- s.version = "0.9.5"
8
+ s.version = "0.9.8"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Mark Evans"]
12
- s.date = %q{2011-08-02}
11
+ s.authors = [%q{Mark Evans}]
12
+ s.date = %q{2011-09-08}
13
13
  s.description = %q{Dragonfly is a framework that enables on-the-fly processing for any content type.
14
14
  It is especially suited to image handling. Its uses range from image thumbnails to standard attachments to on-demand text generation.}
15
15
  s.email = %q{mark@new-bamboo.co.uk}
@@ -123,6 +123,7 @@ Gem::Specification.new do |s|
123
123
  "lib/dragonfly/routed_endpoint.rb",
124
124
  "lib/dragonfly/serializer.rb",
125
125
  "lib/dragonfly/server.rb",
126
+ "lib/dragonfly/shell.rb",
126
127
  "lib/dragonfly/simple_cache.rb",
127
128
  "lib/dragonfly/temp_object.rb",
128
129
  "lib/dragonfly/url_mapper.rb",
@@ -154,7 +155,6 @@ Gem::Specification.new do |s|
154
155
  "spec/dragonfly/image_magick/encoder_spec.rb",
155
156
  "spec/dragonfly/image_magick/generator_spec.rb",
156
157
  "spec/dragonfly/image_magick/processor_spec.rb",
157
- "spec/dragonfly/image_magick/utils_spec.rb",
158
158
  "spec/dragonfly/job_builder_spec.rb",
159
159
  "spec/dragonfly/job_definitions_spec.rb",
160
160
  "spec/dragonfly/job_endpoint_spec.rb",
@@ -164,6 +164,7 @@ Gem::Specification.new do |s|
164
164
  "spec/dragonfly/routed_endpoint_spec.rb",
165
165
  "spec/dragonfly/serializer_spec.rb",
166
166
  "spec/dragonfly/server_spec.rb",
167
+ "spec/dragonfly/shell_spec.rb",
167
168
  "spec/dragonfly/simple_cache_spec.rb",
168
169
  "spec/dragonfly/temp_object_spec.rb",
169
170
  "spec/dragonfly/url_mapper_spec.rb",
@@ -171,12 +172,14 @@ Gem::Specification.new do |s|
171
172
  "spec/functional/image_magick_app_spec.rb",
172
173
  "spec/functional/model_urls_spec.rb",
173
174
  "spec/functional/remote_on_the_fly_spec.rb",
175
+ "spec/functional/shell_commands_spec.rb",
174
176
  "spec/functional/to_response_spec.rb",
175
177
  "spec/spec_helper.rb",
176
178
  "spec/support/argument_matchers.rb",
177
179
  "spec/support/image_matchers.rb",
178
180
  "spec/support/simple_matchers.rb",
179
181
  "spec/test_imagemagick.ru",
182
+ "tmp/.gitignore",
180
183
  "yard/handlers/configurable_attr_handler.rb",
181
184
  "yard/setup.rb",
182
185
  "yard/templates/default/fulldoc/html/css/common.css",
@@ -185,9 +188,9 @@ Gem::Specification.new do |s|
185
188
  "yard/templates/default/module/setup.rb"
186
189
  ]
187
190
  s.homepage = %q{http://github.com/markevans/dragonfly}
188
- s.licenses = ["MIT"]
189
- s.require_paths = ["lib"]
190
- s.rubygems_version = %q{1.5.2}
191
+ s.licenses = [%q{MIT}]
192
+ s.require_paths = [%q{lib}]
193
+ s.rubygems_version = %q{1.8.6}
191
194
  s.summary = %q{Ideal gem for handling attachments in Rails, Sinatra and Rack applications.}
192
195
  s.test_files = [
193
196
  "spec/dragonfly/active_model_extensions/model_spec.rb",
@@ -212,7 +215,6 @@ Gem::Specification.new do |s|
212
215
  "spec/dragonfly/image_magick/encoder_spec.rb",
213
216
  "spec/dragonfly/image_magick/generator_spec.rb",
214
217
  "spec/dragonfly/image_magick/processor_spec.rb",
215
- "spec/dragonfly/image_magick/utils_spec.rb",
216
218
  "spec/dragonfly/job_builder_spec.rb",
217
219
  "spec/dragonfly/job_definitions_spec.rb",
218
220
  "spec/dragonfly/job_endpoint_spec.rb",
@@ -222,6 +224,7 @@ Gem::Specification.new do |s|
222
224
  "spec/dragonfly/routed_endpoint_spec.rb",
223
225
  "spec/dragonfly/serializer_spec.rb",
224
226
  "spec/dragonfly/server_spec.rb",
227
+ "spec/dragonfly/shell_spec.rb",
225
228
  "spec/dragonfly/simple_cache_spec.rb",
226
229
  "spec/dragonfly/temp_object_spec.rb",
227
230
  "spec/dragonfly/url_mapper_spec.rb",
@@ -229,6 +232,7 @@ Gem::Specification.new do |s|
229
232
  "spec/functional/image_magick_app_spec.rb",
230
233
  "spec/functional/model_urls_spec.rb",
231
234
  "spec/functional/remote_on_the_fly_spec.rb",
235
+ "spec/functional/shell_commands_spec.rb",
232
236
  "spec/functional/to_response_spec.rb",
233
237
  "spec/spec_helper.rb",
234
238
  "spec/support/argument_matchers.rb",
@@ -250,8 +254,7 @@ Gem::Specification.new do |s|
250
254
  s.add_development_dependency(%q<mongo>, [">= 0"])
251
255
  s.add_development_dependency(%q<couchrest>, ["~> 1.0"])
252
256
  s.add_development_dependency(%q<rack-cache>, [">= 0"])
253
- s.add_development_dependency(%q<rails>, ["= 3.0.9"])
254
- s.add_development_dependency(%q<rake>, ["= 0.8.7"])
257
+ s.add_development_dependency(%q<rails>, ["~> 3.1.0"])
255
258
  s.add_development_dependency(%q<rspec>, ["~> 2.5"])
256
259
  s.add_development_dependency(%q<webmock>, [">= 0"])
257
260
  s.add_development_dependency(%q<yard>, [">= 0"])
@@ -269,8 +272,7 @@ Gem::Specification.new do |s|
269
272
  s.add_dependency(%q<mongo>, [">= 0"])
270
273
  s.add_dependency(%q<couchrest>, ["~> 1.0"])
271
274
  s.add_dependency(%q<rack-cache>, [">= 0"])
272
- s.add_dependency(%q<rails>, ["= 3.0.9"])
273
- s.add_dependency(%q<rake>, ["= 0.8.7"])
275
+ s.add_dependency(%q<rails>, ["~> 3.1.0"])
274
276
  s.add_dependency(%q<rspec>, ["~> 2.5"])
275
277
  s.add_dependency(%q<webmock>, [">= 0"])
276
278
  s.add_dependency(%q<yard>, [">= 0"])
@@ -289,8 +291,7 @@ Gem::Specification.new do |s|
289
291
  s.add_dependency(%q<mongo>, [">= 0"])
290
292
  s.add_dependency(%q<couchrest>, ["~> 1.0"])
291
293
  s.add_dependency(%q<rack-cache>, [">= 0"])
292
- s.add_dependency(%q<rails>, ["= 3.0.9"])
293
- s.add_dependency(%q<rake>, ["= 0.8.7"])
294
+ s.add_dependency(%q<rails>, ["~> 3.1.0"])
294
295
  s.add_dependency(%q<rspec>, ["~> 2.5"])
295
296
  s.add_dependency(%q<webmock>, [">= 0"])
296
297
  s.add_dependency(%q<yard>, [">= 0"])
@@ -132,6 +132,8 @@ You can also pass any options to `MongoDataStore.new` as an options hash.
132
132
 
133
133
  You can't serve directly from the mongo datastore.
134
134
 
135
+ You can optionally pass in a `:content_type` option to `store` to tell it the content's MIME type.
136
+
135
137
  Couch datastore
136
138
  ---------------
137
139
  To configure with the {Dragonfly::DataStorage::CouchDataStore CouchDataStore}:
@@ -150,8 +152,9 @@ To configure:
150
152
 
151
153
  You can also pass these options to `CouchDataStore.new` as an options hash.
152
154
 
153
- You can serve directly from the couch datastore. You can optionally pass in a `:mime_type` option to `store`
154
- to tell it what to use for its 'Content-Type' header.
155
+ You can serve directly from the couch datastore.
156
+
157
+ You can optionally pass in a `:content_type` option to `store` to tell it what to use for its 'Content-Type' header.
155
158
 
156
159
  Custom datastore
157
160
  ----------------
data/extra_docs/Rails3.md CHANGED
@@ -23,17 +23,19 @@ config/initializers/dragonfly.rb:
23
23
 
24
24
  application.rb:
25
25
 
26
- config.middleware.insert 0, 'Dragonfly::Middleware', :images
27
- config.middleware.insert_before 'Dragonfly::Middleware', 'Rack::Cache', {
26
+ config.middleware.insert 0, 'Rack::Cache', {
28
27
  :verbose => true,
29
28
  :metastore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"),
30
29
  :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
31
- }
30
+ } # unless Rails.env.production? ## uncomment this 'unless' in Rails 3.1,
31
+ ## because it already inserts Rack::Cache in production
32
+
33
+ config.middleware.insert_after 'Rack::Cache', 'Dragonfly::Middleware', :images
32
34
 
33
35
  Gemfile
34
36
  -------
35
37
 
36
- gem 'dragonfly', '~>0.9.4'
38
+ gem 'dragonfly', '~>0.9.8'
37
39
  gem 'rack-cache', :require => 'rack/cache'
38
40
 
39
41
  Capistrano
@@ -1,3 +1,5 @@
1
+ require 'fileutils'
2
+
1
3
  RAILS_APP_NAME = 'tmp_app'
2
4
  FIXTURES_PATH = ROOT_PATH + "/fixtures"
3
5
 
@@ -12,17 +14,27 @@ end
12
14
  ##############################################################################
13
15
 
14
16
  Given "a Rails application set up for using dragonfly" do
15
- raise "Problem setting up Rails app" unless `
16
- cd #{fixture_path} &&
17
- rm -rf #{RAILS_APP_NAME} &&
18
- bundle exec rails new #{RAILS_APP_NAME} -m template.rb`
17
+ ok = nil
18
+ FileUtils.cd fixture_path do
19
+ FileUtils.rm_rf RAILS_APP_NAME
20
+ ok = `bundle exec rails new #{RAILS_APP_NAME} -m template.rb`
21
+ end
22
+ raise "Problem setting up Rails app" unless ok
19
23
  end
20
24
 
21
25
  Then /^the (.+) cucumber features in my Rails app should pass$/ do |filename|
22
26
  puts "\n*** RUNNING FEATURES IN THE RAILS APP... ***\n"
23
27
  path = File.join(fixture_path, RAILS_APP_NAME)
24
- `cd #{path} && RAILS_ENV=cucumber rake db:migrate`
25
- features_passed = system "cd #{path} && cucumber features/#{filename}.feature"
28
+ FileUtils.cd path do
29
+ env = ENV['RAILS_ENV']
30
+ ENV['RAILS_ENV'] = 'cucumber'
31
+ `rake db:migrate`
32
+ ENV['RAILS_ENV'] = env
33
+ end
34
+ features_passed = nil
35
+ FileUtils.cd path do
36
+ features_passed = system "cucumber features/#{filename}.feature"
37
+ end
26
38
  puts "\n*** FINISHED RUNNING FEATURES IN THE RAILS APP ***\n"
27
39
  raise "Features failed" unless features_passed
28
40
  end
@@ -5,8 +5,17 @@ require ROOT_PATH + '/spec/support/image_matchers.rb'
5
5
  # A hash of <name for reference> => <dragonfly uid> pairs
6
6
  TEMP_FILES = {}
7
7
 
8
- Dragonfly[:images].configure_with(:imagemagick)
8
+ root_path = ROOT_PATH + '/tmp/dragonfly_cukes'
9
+ logger = Logger.new(ROOT_PATH + '/tmp/dragonfly_cukes.log')
10
+
11
+ Dragonfly[:images].configure_with(:imagemagick).configure do |c|
12
+ c.datastore.root_path = root_path
13
+ c.log = logger
14
+ end
15
+
9
16
  Dragonfly[:files].configure do |c|
17
+ c.datastore.root_path = root_path
18
+ c.log = logger
10
19
  c.analyser.register(Dragonfly::Analysis::FileCommandAnalyser)
11
20
  end
12
21
 
@@ -16,7 +16,7 @@ end
16
16
 
17
17
  Then /^I should see a (.+) image of size (.+)$/ do |format, size|
18
18
  tempfile = Tempfile.new('wicked')
19
- page.source.force_encoding('UTF-8').encode! if page.source.respond_to?(:force_encoding) # For some reason need this on Ruby 1.9.2
19
+ tempfile.binmode
20
20
  tempfile.write page.source
21
21
  tempfile.close
22
22
  output = `identify #{tempfile.path}`.split(' ')
data/lib/dragonfly.rb CHANGED
@@ -52,5 +52,9 @@ module Dragonfly
52
52
  App.register_configuration(:rails){ Config::Rails }
53
53
  App.register_configuration(:heroku){ Config::Heroku }
54
54
 
55
+ def running_on_windows?
56
+ ENV['OS'] && ENV['OS'].downcase == 'windows_nt'
57
+ end
58
+
55
59
  end
56
60
  end
@@ -3,6 +3,7 @@ module Dragonfly
3
3
 
4
4
  class FileCommandAnalyser
5
5
 
6
+ include Shell
6
7
  include Configurable
7
8
 
8
9
  configurable_attr :file_command, "file"
@@ -11,7 +12,7 @@ module Dragonfly
11
12
 
12
13
  def mime_type(temp_object)
13
14
  content_type = if use_filesystem
14
- `#{file_command} -b --mime '#{temp_object.path}'`
15
+ `#{file_command} -b --mime #{quote temp_object.path}`
15
16
  else
16
17
  IO.popen("#{file_command} -b --mime -", 'r+') do |io|
17
18
  if num_bytes_to_check
@@ -29,7 +29,7 @@ module Dragonfly
29
29
  temp_object.file do |f|
30
30
  doc = CouchRest::Document.new(:meta => marshal_encode(meta))
31
31
  response = db.save_doc(doc)
32
- doc.put_attachment(name, f, {:content_type => content_type})
32
+ doc.put_attachment(name, f.dup, :content_type => content_type)
33
33
  form_uid(response['id'], name)
34
34
  end
35
35
  rescue RuntimeError => e
@@ -31,8 +31,10 @@ module Dragonfly
31
31
 
32
32
  def store(temp_object, opts={})
33
33
  ensure_authenticated!
34
+ content_type = opts[:content_type] || opts[:mime_type] || 'application/octet-stream'
34
35
  temp_object.file do |f|
35
- mongo_id = grid.put(f, :metadata => marshal_encode(opts[:meta] || {}))
36
+ mongo_id = grid.put(f, :content_type => content_type,
37
+ :metadata => marshal_encode(opts[:meta] || {}))
36
38
  mongo_id.to_s
37
39
  end
38
40
  end
@@ -76,7 +78,7 @@ module Dragonfly
76
78
  @authenticated ||= db.authenticate(username, password)
77
79
  end
78
80
  end
79
-
81
+
80
82
  def bson_id(uid)
81
83
  OBJECT_ID.from_string(uid)
82
84
  end
@@ -69,7 +69,11 @@ module Dragonfly
69
69
 
70
70
  def url_for(uid, opts={})
71
71
  if opts && opts[:expires]
72
- storage.get_object_url(bucket_name, uid, opts[:expires])
72
+ if storage.respond_to?(:get_object_https_url) # fog's get_object_url is deprecated (aug 2011)
73
+ storage.get_object_https_url(bucket_name, uid, opts[:expires])
74
+ else
75
+ storage.get_object_url(bucket_name, uid, opts[:expires])
76
+ end
73
77
  else
74
78
  "http://#{bucket_name}.s3.amazonaws.com/#{uid}"
75
79
  end
@@ -42,7 +42,7 @@ module Dragonfly
42
42
  def plain(width, height, colour, opts={})
43
43
  format = opts[:format] || 'png'
44
44
  [
45
- convert(nil, "-size #{width}x#{height} \"xc:#{colour}\"", format),
45
+ convert(nil, "-size #{width}x#{height} xc:#{colour}", format),
46
46
  {:format => format.to_sym, :name => "plain.#{format}"}
47
47
  ]
48
48
  end
@@ -96,7 +96,7 @@ module Dragonfly
96
96
  args.push("-size #{width}x#{height}")
97
97
  args.push("xc:#{background}")
98
98
  args.push("-annotate 0x0+#{padding_left}+#{padding_top} #{escaped_string}")
99
- run "#{convert_command} #{args.join(' ')} #{tempfile.path}"
99
+ run convert_command, "#{args.join(' ')} #{quote tempfile.path}"
100
100
  end
101
101
 
102
102
  [
@@ -23,7 +23,7 @@ module Dragonfly
23
23
  include Utils
24
24
 
25
25
  def resize(temp_object, geometry)
26
- convert(temp_object, "-resize \"#{geometry}\"")
26
+ convert(temp_object, "-resize #{geometry}")
27
27
  end
28
28
 
29
29
  def crop(temp_object, opts={})
@@ -69,7 +69,7 @@ module Dragonfly
69
69
  end
70
70
 
71
71
  def rotate(temp_object, amount, opts={})
72
- convert(temp_object, "-rotate \"#{amount}#{opts[:qualifier]}\"")
72
+ convert(temp_object, "-rotate #{amount}#{opts[:qualifier]}")
73
73
  end
74
74
 
75
75
  def strip(temp_object)
@@ -4,20 +4,17 @@ module Dragonfly
4
4
  module ImageMagick
5
5
  module Utils
6
6
 
7
- # Exceptions
8
- class ShellCommandFailed < RuntimeError; end
9
-
7
+ include Shell
10
8
  include Loggable
11
9
  include Configurable
12
10
  configurable_attr :convert_command, "convert"
13
11
  configurable_attr :identify_command, "identify"
14
- configurable_attr :log_commands, false
15
12
 
16
13
  private
17
14
 
18
15
  def convert(temp_object=nil, args='', format=nil)
19
16
  tempfile = new_tempfile(format)
20
- run "#{convert_command} #{'"'+temp_object.path+'"' if temp_object} #{args} #{tempfile.path}"
17
+ run convert_command, %(#{quote(temp_object.path) if temp_object} #{args} #{quote(tempfile.path)})
21
18
  tempfile
22
19
  end
23
20
 
@@ -35,7 +32,7 @@ module Dragonfly
35
32
  end
36
33
 
37
34
  def raw_identify(temp_object, args='')
38
- run "#{identify_command} #{args} \"#{temp_object.path}\""
35
+ run identify_command, "#{args} #{quote(temp_object.path)}"
39
36
  end
40
37
 
41
38
  def new_tempfile(ext=nil)
@@ -45,25 +42,6 @@ module Dragonfly
45
42
  tempfile
46
43
  end
47
44
 
48
- def run(command)
49
- log.debug("Running command: #{command}") if log_commands
50
- begin
51
- result = `#{command}`
52
- rescue Errno::ENOENT
53
- raise_shell_command_failed(command)
54
- end
55
- if $?.exitstatus == 1
56
- throw :unable_to_handle
57
- elsif !$?.success?
58
- raise_shell_command_failed(command)
59
- end
60
- result
61
- end
62
-
63
- def raise_shell_command_failed(command)
64
- raise ShellCommandFailed, "Command failed (#{command}) with exit status #{$?.exitstatus}"
65
- end
66
-
67
45
  end
68
46
  end
69
47
  end