dragonfly 0.6.2 → 0.7.0

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 (157) hide show
  1. data/.gitignore +2 -0
  2. data/.specopts +2 -0
  3. data/.yardopts +11 -5
  4. data/Gemfile +22 -0
  5. data/Gemfile.rails.2.3.5 +13 -0
  6. data/History.md +49 -0
  7. data/README.md +18 -28
  8. data/Rakefile +24 -36
  9. data/VERSION +1 -1
  10. data/config.ru +4 -1
  11. data/dragonfly.gemspec +85 -99
  12. data/extra_docs/Analysers.md +66 -30
  13. data/extra_docs/Caching.md +22 -0
  14. data/extra_docs/Configuration.md +116 -0
  15. data/extra_docs/DataStorage.md +114 -14
  16. data/extra_docs/Encoding.md +62 -37
  17. data/extra_docs/GeneralUsage.md +118 -0
  18. data/extra_docs/Generators.md +92 -0
  19. data/extra_docs/Heroku.md +51 -0
  20. data/extra_docs/Index.md +8 -9
  21. data/extra_docs/MimeTypes.md +18 -17
  22. data/extra_docs/Models.md +251 -0
  23. data/extra_docs/Processing.md +94 -70
  24. data/extra_docs/Rack.md +53 -0
  25. data/extra_docs/Rails2.md +44 -0
  26. data/extra_docs/Rails3.md +51 -0
  27. data/extra_docs/Sinatra.md +21 -0
  28. data/extra_docs/URLs.md +114 -0
  29. data/features/images.feature +6 -7
  30. data/features/no_processing.feature +0 -6
  31. data/features/rails_2.3.5.feature +1 -1
  32. data/features/rails_3.0.0.rc.feature +8 -0
  33. data/features/steps/dragonfly_steps.rb +14 -12
  34. data/features/steps/rails_steps.rb +20 -9
  35. data/features/support/env.rb +10 -11
  36. data/fixtures/files/app/views/albums/new.html.erb +4 -4
  37. data/fixtures/files/app/views/albums/show.html.erb +1 -1
  38. data/fixtures/files/features/manage_album_images.feature +1 -1
  39. data/fixtures/files/features/step_definitions/{album_steps.rb → image_steps.rb} +4 -3
  40. data/fixtures/files/features/support/paths.rb +2 -0
  41. data/fixtures/files/features/text_images.feature +7 -0
  42. data/fixtures/rails_3.0.0.rc/template.rb +21 -0
  43. data/irbrc.rb +2 -1
  44. data/lib/dragonfly.rb +4 -16
  45. data/lib/dragonfly/{active_record_extensions.rb → active_model_extensions.rb} +1 -1
  46. data/lib/dragonfly/active_model_extensions/attachment.rb +146 -0
  47. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/class_methods.rb +5 -6
  48. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/instance_methods.rb +1 -1
  49. data/lib/dragonfly/{active_record_extensions → active_model_extensions}/validations.rb +5 -9
  50. data/lib/dragonfly/analyser.rb +59 -0
  51. data/lib/dragonfly/analysis/file_command_analyser.rb +1 -1
  52. data/lib/dragonfly/analysis/r_magick_analyser.rb +46 -31
  53. data/lib/dragonfly/app.rb +138 -173
  54. data/lib/dragonfly/config/heroku.rb +19 -0
  55. data/lib/dragonfly/config/r_magick.rb +37 -0
  56. data/lib/dragonfly/config/{rails_defaults.rb → rails.rb} +6 -7
  57. data/lib/dragonfly/configurable.rb +30 -27
  58. data/lib/dragonfly/core_ext/object.rb +1 -1
  59. data/lib/dragonfly/data_storage/file_data_store.rb +59 -26
  60. data/lib/dragonfly/data_storage/mongo_data_store.rb +65 -0
  61. data/lib/dragonfly/data_storage/s3data_store.rb +31 -12
  62. data/lib/dragonfly/encoder.rb +13 -0
  63. data/lib/dragonfly/encoding/r_magick_encoder.rb +10 -19
  64. data/lib/dragonfly/endpoint.rb +43 -0
  65. data/lib/dragonfly/function_manager.rb +65 -0
  66. data/lib/dragonfly/{processing/r_magick_text_processor.rb → generation/r_magick_generator.rb} +25 -11
  67. data/lib/dragonfly/generator.rb +9 -0
  68. data/lib/dragonfly/job.rb +290 -0
  69. data/lib/dragonfly/job_builder.rb +39 -0
  70. data/lib/dragonfly/job_definitions.rb +26 -0
  71. data/lib/dragonfly/job_endpoint.rb +17 -0
  72. data/lib/dragonfly/loggable.rb +28 -0
  73. data/lib/dragonfly/middleware.rb +21 -14
  74. data/lib/dragonfly/processing/r_magick_processor.rb +71 -48
  75. data/lib/dragonfly/processor.rb +9 -0
  76. data/lib/dragonfly/r_magick_utils.rb +24 -0
  77. data/lib/dragonfly/rails/images.rb +10 -7
  78. data/lib/dragonfly/routed_endpoint.rb +42 -0
  79. data/lib/dragonfly/serializer.rb +32 -0
  80. data/lib/dragonfly/simple_cache.rb +23 -0
  81. data/lib/dragonfly/simple_endpoint.rb +64 -0
  82. data/lib/dragonfly/temp_object.rb +77 -45
  83. data/spec/argument_matchers.rb +7 -17
  84. data/spec/dragonfly/active_model_extensions/active_model_setup.rb +97 -0
  85. data/spec/dragonfly/active_model_extensions/active_record_setup.rb +85 -0
  86. data/spec/dragonfly/{active_record_extensions → active_model_extensions}/model_spec.rb +282 -244
  87. data/spec/dragonfly/active_model_extensions/spec_helper.rb +11 -0
  88. data/spec/dragonfly/analyser_spec.rb +123 -0
  89. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +2 -2
  90. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +10 -1
  91. data/spec/dragonfly/app_spec.rb +175 -69
  92. data/spec/dragonfly/configurable_spec.rb +14 -0
  93. data/spec/dragonfly/data_storage/data_store_spec.rb +36 -9
  94. data/spec/dragonfly/data_storage/file_data_store_spec.rb +61 -38
  95. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +18 -0
  96. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +34 -39
  97. data/spec/dragonfly/deprecation_spec.rb +20 -0
  98. data/spec/dragonfly/function_manager_spec.rb +154 -0
  99. data/spec/dragonfly/generation/r_magick_generator_spec.rb +119 -0
  100. data/spec/dragonfly/job_builder_spec.rb +37 -0
  101. data/spec/dragonfly/job_definitions_spec.rb +35 -0
  102. data/spec/dragonfly/job_endpoint_spec.rb +66 -0
  103. data/spec/dragonfly/job_spec.rb +605 -0
  104. data/spec/dragonfly/loggable_spec.rb +80 -0
  105. data/spec/dragonfly/middleware_spec.rb +37 -17
  106. data/spec/dragonfly/processing/r_magick_processor_spec.rb +182 -166
  107. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  108. data/spec/dragonfly/serializer_spec.rb +61 -0
  109. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  110. data/spec/dragonfly/simple_endpoint_spec.rb +78 -0
  111. data/spec/dragonfly/temp_object_spec.rb +154 -119
  112. data/spec/simple_matchers.rb +22 -0
  113. data/spec/spec_helper.rb +28 -4
  114. data/yard/templates/default/layout/html/layout.erb +18 -11
  115. metadata +89 -190
  116. data/config.rb +0 -5
  117. data/extra_docs/ActiveRecord.md +0 -196
  118. data/extra_docs/ExampleUseCases.md +0 -189
  119. data/extra_docs/GettingStarted.md +0 -114
  120. data/extra_docs/Shortcuts.md +0 -118
  121. data/extra_docs/UsingWithRails.md +0 -81
  122. data/features/rails_3.0.0.beta3.feature +0 -7
  123. data/fixtures/rails_3.0.0.beta3/template.rb +0 -16
  124. data/lib/dragonfly/active_record_extensions/attachment.rb +0 -170
  125. data/lib/dragonfly/analyser_list.rb +0 -9
  126. data/lib/dragonfly/analysis/base.rb +0 -10
  127. data/lib/dragonfly/belongs_to_app.rb +0 -24
  128. data/lib/dragonfly/config/heroku_rails_images.rb +0 -23
  129. data/lib/dragonfly/config/r_magick_images.rb +0 -69
  130. data/lib/dragonfly/config/r_magick_text.rb +0 -25
  131. data/lib/dragonfly/config/rails_images.rb +0 -13
  132. data/lib/dragonfly/data_storage/base.rb +0 -21
  133. data/lib/dragonfly/data_storage/base64_data_store.rb +0 -23
  134. data/lib/dragonfly/data_storage/transparent_data_store.rb +0 -21
  135. data/lib/dragonfly/delegatable.rb +0 -14
  136. data/lib/dragonfly/delegator.rb +0 -62
  137. data/lib/dragonfly/encoder_list.rb +0 -9
  138. data/lib/dragonfly/encoding/base.rb +0 -14
  139. data/lib/dragonfly/encoding/transparent_encoder.rb +0 -14
  140. data/lib/dragonfly/extended_temp_object.rb +0 -120
  141. data/lib/dragonfly/parameters.rb +0 -163
  142. data/lib/dragonfly/processing/base.rb +0 -10
  143. data/lib/dragonfly/processor_list.rb +0 -9
  144. data/lib/dragonfly/url_handler.rb +0 -147
  145. data/spec/dragonfly/active_record_extensions/attachment_spec.rb +0 -8
  146. data/spec/dragonfly/active_record_extensions/migration.rb +0 -42
  147. data/spec/dragonfly/active_record_extensions/models.rb +0 -6
  148. data/spec/dragonfly/active_record_extensions/spec_helper.rb +0 -24
  149. data/spec/dragonfly/belongs_to_app_spec.rb +0 -55
  150. data/spec/dragonfly/delegatable_spec.rb +0 -32
  151. data/spec/dragonfly/delegator_spec.rb +0 -145
  152. data/spec/dragonfly/extended_temp_object_spec.rb +0 -71
  153. data/spec/dragonfly/parameters_spec.rb +0 -298
  154. data/spec/dragonfly/processing/r_magick_text_processor_spec.rb +0 -84
  155. data/spec/dragonfly/url_handler_spec.rb +0 -247
  156. data/spec/dragonfly_spec.rb +0 -16
  157. data/spec/ginger_scenarios.rb +0 -13
@@ -0,0 +1,119 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Dragonfly::Generation::RMagickGenerator do
4
+
5
+ describe "plasma" do
6
+ before(:each) do
7
+ @generator = Dragonfly::Generation::RMagickGenerator.new
8
+ end
9
+
10
+ describe "generating an image with the given dimensions" do
11
+ before(:each) do
12
+ @image, @extra = @generator.plasma(23,12)
13
+ end
14
+ it {@image.should have_width(23)}
15
+ it {@image.should have_height(12)}
16
+ it {@image.should have_format('png')}
17
+ it {@extra.should == {:format => :png, :name => 'plasma.png'}}
18
+ end
19
+
20
+ describe "specifying the format" do
21
+ before(:each) do
22
+ @image, @extra = @generator.plasma(23, 12, :gif)
23
+ end
24
+ it {@image.should have_format('gif')}
25
+ it {@extra.should == {:format => :gif, :name => 'plasma.gif'}}
26
+ end
27
+ end
28
+
29
+ describe "text" do
30
+ before(:each) do
31
+ @generator = Dragonfly::Generation::RMagickGenerator.new
32
+ @text = "mmm"
33
+ end
34
+
35
+ describe "creating a text image" do
36
+ before(:each) do
37
+ @image, @extra = @generator.text(@text, :font_size => 12)
38
+ end
39
+ it {@image.should have_width(20..40)} # approximate
40
+ it {@image.should have_height(10..20)}
41
+ it {@image.should have_format('png')}
42
+ it {@extra.should == {:format => :png, :name => 'text.png'}}
43
+ end
44
+
45
+ describe "specifying the format" do
46
+ before(:each) do
47
+ @image, @extra = @generator.text(@text, :format => :gif)
48
+ end
49
+ it {@image.should have_format('gif')}
50
+ it {@extra.should == {:format => :gif, :name => 'text.gif'}}
51
+ end
52
+
53
+ # it "should ignore percent characters used by rmagick"
54
+
55
+ describe "padding" do
56
+ before(:each) do
57
+ no_padding_text, extra = @generator.text(@text, :font_size => 12)
58
+ @width = image_properties(no_padding_text)[:width].to_i
59
+ @height = image_properties(no_padding_text)[:height].to_i
60
+ end
61
+ it "1 number shortcut" do
62
+ image, extra = @generator.text(@text, :padding => '10')
63
+ image.should have_width(@width + 20)
64
+ image.should have_height(@height + 20)
65
+ end
66
+ it "2 numbers shortcut" do
67
+ image, extra = @generator.text(@text, :padding => '10 5')
68
+ image.should have_width(@width + 10)
69
+ image.should have_height(@height + 20)
70
+ end
71
+ it "3 numbers shortcut" do
72
+ image, extra = @generator.text(@text, :padding => '10 5 8')
73
+ image.should have_width(@width + 10)
74
+ image.should have_height(@height + 18)
75
+ end
76
+ it "4 numbers shortcut" do
77
+ image, extra = @generator.text(@text, :padding => '1 2 3 4')
78
+ image.should have_width(@width + 6)
79
+ image.should have_height(@height + 4)
80
+ end
81
+ it "should override the general padding declaration with the specific one (e.g. 'padding-left')" do
82
+ image, extra = @generator.text(@text, :padding => '10', 'padding-left' => 9)
83
+ image.should have_width(@width + 19)
84
+ image.should have_height(@height + 20)
85
+ end
86
+ it "should ignore 'px' suffixes" do
87
+ image, extra = @generator.text(@text, :padding => '1px 2px 3px 4px')
88
+ image.should have_width(@width + 6)
89
+ image.should have_height(@height + 4)
90
+ end
91
+ it "bad padding string" do
92
+ lambda{
93
+ @generator.text(@text, :padding => '1 2 3 4 5')
94
+ }.should raise_error(ArgumentError)
95
+ end
96
+ end
97
+ end
98
+
99
+ describe Dragonfly::Generation::RMagickGenerator::HashWithCssStyleKeys do
100
+ before(:each) do
101
+ @hash = Dragonfly::Generation::RMagickGenerator::HashWithCssStyleKeys[
102
+ :font_style => 'normal',
103
+ :'font-weight' => 'bold',
104
+ 'font_colour' => 'white',
105
+ 'font-size' => 23,
106
+ :hello => 'there'
107
+ ]
108
+ end
109
+ describe "accessing using underscore symbol style" do
110
+ it{ @hash[:font_style].should == 'normal' }
111
+ it{ @hash[:font_weight].should == 'bold' }
112
+ it{ @hash[:font_colour].should == 'white' }
113
+ it{ @hash[:font_size].should == 23 }
114
+ it{ @hash[:hello].should == 'there' }
115
+ it{ @hash[:non_existent_key].should be_nil }
116
+ end
117
+ end
118
+
119
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Dragonfly::JobBuilder do
4
+
5
+ describe "a multi-step job" do
6
+
7
+ before(:each) do
8
+ @job_builder = Dragonfly::JobBuilder.new do |size, format|
9
+ process :thumb, size
10
+ encode format unless format.nil?
11
+ end
12
+ end
13
+
14
+ it "should correctly call job steps" do
15
+ job = mock
16
+ job.should_receive(:process).with(:thumb, '30x30#').and_return(job2=mock)
17
+ job2.should_receive(:encode).with(:jpg).and_return(job3=mock)
18
+ @job_builder.build(job, '30x30#', :jpg).should == job3
19
+ end
20
+
21
+ it "should work consistently with bang methods" do
22
+ job = mock
23
+ job.should_receive(:process!).with(:thumb, '30x30#').and_return(job)
24
+ job.should_receive(:encode!).with(:jpg).and_return(job)
25
+ @job_builder.build!(job, '30x30#', :jpg).should == job
26
+ end
27
+
28
+ it "should yield nil if the arg isn't passed in" do
29
+ job = mock
30
+ job.should_receive(:process).with(:thumb, '30x30#').and_return(job2=mock)
31
+ job2.should_not_receive(:encode)
32
+ @job_builder.build(job, '30x30#').should == job2
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Dragonfly::JobDefinitions do
4
+
5
+ describe "defining jobs" do
6
+
7
+ before(:each) do
8
+ @job_definitions = Dragonfly::JobDefinitions.new
9
+ @object = Object.new
10
+ @object.extend @job_definitions
11
+ end
12
+
13
+ describe "a simple job" do
14
+
15
+ before(:each) do
16
+ @job_definitions.add :thumb do |size|
17
+ process :thumb, size
18
+ end
19
+ end
20
+
21
+ it "correctly call job steps" do
22
+ @object.should_receive(:process).with(:thumb, '30x30#').and_return(job=mock)
23
+ @object.thumb('30x30#').should == job
24
+ end
25
+
26
+ it "should correctly call job steps when bang is given" do
27
+ @object.should_receive(:process!).with(:thumb, '30x30#').and_return(@object)
28
+ @object.thumb!('30x30#').should == @object
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ ## General tests for Endpoint module go here as it's a pretty simple wrapper around that
4
+
5
+ describe Dragonfly::JobEndpoint do
6
+
7
+ def make_request(job, opts={})
8
+ endpoint = Dragonfly::JobEndpoint.new(job)
9
+ Rack::MockRequest.new(endpoint).get('', opts)
10
+ end
11
+
12
+ describe "errors" do
13
+
14
+ before(:each) do
15
+ @app = test_app
16
+ @app.datastore.stub!(:retrieve).with('egg').and_return(["GUNGLE", {:name => 'gung.txt'}])
17
+ @job = Dragonfly::Job.new(@app).fetch('egg')
18
+ end
19
+
20
+ it "should return a correct response if successful" do
21
+ response = make_request(@job)
22
+ response.status.should == 200
23
+ response['ETag'].should =~ /^"\w+"$/
24
+ response['Cache-Control'].should == "public, max-age=31536000"
25
+ response['Content-Type'].should == 'text/plain'
26
+ response['Content-Length'].should == '6'
27
+ response.body.should == 'GUNGLE'
28
+ end
29
+
30
+ it "should return 404 if the datastore raises data not found" do
31
+ @job.should_receive(:apply).and_raise(Dragonfly::DataStorage::DataNotFound)
32
+ response = make_request(@job)
33
+ response.status.should == 404
34
+ end
35
+
36
+ describe "ETag" do
37
+ it "should return an ETag" do
38
+ response = make_request(@job)
39
+ response.headers['ETag'].should =~ /^"\w+"$/
40
+ end
41
+
42
+ [
43
+ "dingle",
44
+ "dingle, eggheads",
45
+ '"dingle", "eggheads"',
46
+ '*'
47
+ ].each do |header|
48
+ it "should return a 304 if the correct ETag is specified in HTTP_IF_NONE_MATCH header e.g. #{header}" do
49
+ @job.should_receive(:unique_signature).at_least(:once).and_return('dingle')
50
+ response = make_request(@job, 'HTTP_IF_NONE_MATCH' => header)
51
+ response.status.should == 304
52
+ response['ETag'].should == '"dingle"'
53
+ response['Cache-Control'].should == "public, max-age=31536000"
54
+ response.body.should be_empty
55
+ end
56
+ end
57
+
58
+ it "should not have applied any steps if the correct ETag is specified in HTTP_IF_NONE_MATCH header" do
59
+ response = make_request(@job, 'HTTP_IF_NONE_MATCH' => @job.serialize)
60
+ @job.applied_steps.should be_empty
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,605 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ # Matchers
4
+ def match_steps(steps)
5
+ simple_matcher("match steps #{steps.inspect}") do |given|
6
+ given.map{|step| step.class } == steps
7
+ end
8
+ end
9
+
10
+ describe Dragonfly::Job do
11
+
12
+ describe "Step types" do
13
+
14
+ {
15
+ Dragonfly::Job::Fetch => :fetch,
16
+ Dragonfly::Job::Process => :process,
17
+ Dragonfly::Job::Encode => :encode,
18
+ Dragonfly::Job::Generate => :generate,
19
+ Dragonfly::Job::FetchFile => :fetch_file
20
+ }.each do |klass, step_name|
21
+ it "should return the correct step name for #{klass}" do
22
+ klass.step_name.should == step_name
23
+ end
24
+ end
25
+
26
+ {
27
+ Dragonfly::Job::Fetch => :f,
28
+ Dragonfly::Job::Process => :p,
29
+ Dragonfly::Job::Encode => :e,
30
+ Dragonfly::Job::Generate => :g,
31
+ Dragonfly::Job::FetchFile => :ff
32
+ }.each do |klass, abbreviation|
33
+ it "should return the correct abbreviation for #{klass}" do
34
+ klass.abbreviation.should == abbreviation
35
+ end
36
+ end
37
+
38
+ describe "step_names" do
39
+ it "should return the available step names" do
40
+ Dragonfly::Job.step_names.should == [:fetch, :process, :encode, :generate, :fetch_file]
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ describe "without temp_object" do
47
+
48
+ before(:each) do
49
+ @app = mock_app
50
+ @job = Dragonfly::Job.new(@app)
51
+ end
52
+
53
+ it "should allow initializing with content" do
54
+ job = Dragonfly::Job.new(@app, Dragonfly::TempObject.new('eggheads'))
55
+ job.temp_object.data.should == 'eggheads'
56
+ end
57
+
58
+ describe "fetch" do
59
+ before(:each) do
60
+ @job.fetch!('some_uid')
61
+ end
62
+
63
+ it { @job.steps.should match_steps([Dragonfly::Job::Fetch]) }
64
+
65
+ it "should retrieve from the app's datastore when applied" do
66
+ @app.datastore.should_receive(:retrieve).with('some_uid').and_return('HELLO')
67
+ @job.apply
68
+ @job.temp_object.data.should == 'HELLO'
69
+ end
70
+
71
+ it "should set extra data if returned from the datastore" do
72
+ @app.datastore.should_receive(:retrieve).with('some_uid').and_return(['HELLO', {:name => 'test.txt', :meta => {1=>2}}])
73
+ @job.apply
74
+ @job.temp_object.data.should == 'HELLO'
75
+ @job.temp_object.name.should == 'test.txt'
76
+ @job.temp_object.meta.should == {1 => 2}
77
+ end
78
+ end
79
+
80
+ describe "process" do
81
+ it "should raise an error when applying" do
82
+ @job.process!(:resize, '20x30')
83
+ lambda{
84
+ @job.apply
85
+ }.should raise_error(Dragonfly::Job::NothingToProcess)
86
+ end
87
+ end
88
+
89
+ describe "encode" do
90
+ it "should raise an error when applying" do
91
+ @job.encode!(:gif)
92
+ lambda{
93
+ @job.apply
94
+ }.should raise_error(Dragonfly::Job::NothingToEncode)
95
+ end
96
+ end
97
+
98
+ describe "analyse" do
99
+ it "should raise an error" do
100
+ lambda{
101
+ @job.analyse(:width)
102
+ }.should raise_error(Dragonfly::Job::NothingToAnalyse)
103
+ end
104
+ end
105
+
106
+ describe "generate" do
107
+ before(:each) do
108
+ @job.generate!(:plasma, 20, 30)
109
+ end
110
+
111
+ it { @job.steps.should match_steps([Dragonfly::Job::Generate]) }
112
+
113
+ it "should use the generator when applied" do
114
+ @app.generator.should_receive(:generate).with(:plasma, 20, 30).and_return('hi')
115
+ @job.apply.data.should == 'hi'
116
+ end
117
+
118
+ it "should save extra data if the generator returns it" do
119
+ @app.generator.should_receive(:generate).with(:plasma, 20, 30).and_return(['hi', {:name => 'plasma.png', :format => :png, :meta => {:a => :b}}])
120
+ @job.apply
121
+ @job.temp_object.data.should == 'hi'
122
+ @job.temp_object.name.should == 'plasma.png'
123
+ @job.temp_object.format.should == :png
124
+ @job.temp_object.meta.should == {:a => :b}
125
+ end
126
+ end
127
+
128
+ describe "fetch_file" do
129
+ before(:each) do
130
+ @job.fetch_file!(File.dirname(__FILE__) + '/../../samples/egg.png')
131
+ end
132
+
133
+ it { @job.steps.should match_steps([Dragonfly::Job::FetchFile]) }
134
+
135
+ it "should fetch the specified file when applied" do
136
+ @job.apply
137
+ @job.temp_object.size.should == 62664
138
+ end
139
+
140
+ end
141
+
142
+ end
143
+
144
+ describe "with temp_object already there" do
145
+
146
+ before(:each) do
147
+ @app = mock_app
148
+ @temp_object = Dragonfly::TempObject.new('HELLO', :name => 'hello.txt', :meta => {:a => :b}, :format => :txt)
149
+ @job = Dragonfly::Job.new(@app)
150
+ @job.temp_object = @temp_object
151
+ end
152
+
153
+ describe "apply" do
154
+ it "should return itself" do
155
+ @job.apply.should == @job
156
+ end
157
+ end
158
+
159
+ describe "process" do
160
+ before(:each) do
161
+ @job.process!(:resize, '20x30')
162
+ end
163
+
164
+ it { @job.steps.should match_steps([Dragonfly::Job::Process]) }
165
+
166
+ it "should use the processor when applied" do
167
+ @app.processor.should_receive(:process).with(@temp_object, :resize, '20x30').and_return('hi')
168
+ @job.apply.data.should == 'hi'
169
+ end
170
+
171
+ it "should maintain the temp object attributes" do
172
+ @app.processor.should_receive(:process).with(@temp_object, :resize, '20x30').and_return('hi')
173
+ temp_object = @job.apply.temp_object
174
+ temp_object.data.should == 'hi'
175
+ temp_object.name.should == 'hello.txt'
176
+ temp_object.meta.should == {:a => :b}
177
+ temp_object.format.should == :txt
178
+ end
179
+ end
180
+
181
+ describe "encode" do
182
+ before(:each) do
183
+ @job.encode!(:gif, :bitrate => 'mumma')
184
+ end
185
+
186
+ it { @job.steps.should match_steps([Dragonfly::Job::Encode]) }
187
+
188
+ it "should use the encoder when applied" do
189
+ @app.encoder.should_receive(:encode).with(@temp_object, :gif, :bitrate => 'mumma').and_return('alo')
190
+ @job.apply.data.should == 'alo'
191
+ end
192
+
193
+ it "should maintain the temp object attributes (except format)" do
194
+ @app.encoder.should_receive(:encode).with(@temp_object, :gif, :bitrate => 'mumma').and_return('alo')
195
+ temp_object = @job.apply.temp_object
196
+ temp_object.data.should == 'alo'
197
+ temp_object.name.should == 'hello.txt'
198
+ temp_object.meta.should == {:a => :b}
199
+ end
200
+
201
+ it "should update the format" do
202
+ @app.encoder.should_receive(:encode).with(@temp_object, :gif, :bitrate => 'mumma').and_return('alo')
203
+ @job.apply.temp_object.format.should == :gif
204
+ end
205
+ end
206
+ end
207
+
208
+ describe "analysis" do
209
+ before(:each) do
210
+ @app = test_app
211
+ @job = Dragonfly::Job.new(@app, Dragonfly::TempObject.new('HELLO'))
212
+ @app.analyser.add(:num_letters){|temp_object, letter| temp_object.data.count(letter) }
213
+ end
214
+ it "should return correctly when calling analyse" do
215
+ @job.analyse(:num_letters, 'L').should == 2
216
+ end
217
+ it "should have mixed in the analyser method" do
218
+ @job.num_letters('L').should == 2
219
+ end
220
+ it "should return nil from analyse if calling any old method" do
221
+ @job.analyse(:robin_van_persie).should be_nil
222
+ end
223
+ it "should not allow calling any old method" do
224
+ lambda{
225
+ @job.robin_van_persie
226
+ }.should raise_error(NoMethodError)
227
+ end
228
+ it "should work correctly with chained jobs, applying before analysing" do
229
+ @app.processor.add(:double){|temp_object| temp_object.data * 2 }
230
+ @job.process(:double).num_letters('L').should == 4
231
+ end
232
+ end
233
+
234
+ describe "adding jobs" do
235
+
236
+ before(:each) do
237
+ @app = mock_app
238
+ end
239
+
240
+ it "should raise an error if the app is different" do
241
+ job1 = Dragonfly::Job.new(@app)
242
+ job2 = Dragonfly::Job.new(mock_app)
243
+ lambda {
244
+ job1 + job2
245
+ }.should raise_error(Dragonfly::Job::AppDoesNotMatch)
246
+ end
247
+
248
+ describe "both belonging to the same app" do
249
+ before(:each) do
250
+ @job1 = Dragonfly::Job.new(@app, Dragonfly::TempObject.new('hello'))
251
+ @job1.process! :resize
252
+ @job2 = Dragonfly::Job.new(@app, Dragonfly::TempObject.new('hola'))
253
+ @job2.encode! :png
254
+ end
255
+
256
+ it "should concatenate jobs" do
257
+ job3 = @job1 + @job2
258
+ job3.steps.should match_steps([
259
+ Dragonfly::Job::Process,
260
+ Dragonfly::Job::Encode
261
+ ])
262
+ end
263
+
264
+ it "should raise an error if the second job has applied steps" do
265
+ @job2.apply
266
+ lambda {
267
+ @job1 + @job2
268
+ }.should raise_error(Dragonfly::Job::JobAlreadyApplied)
269
+ end
270
+
271
+ it "should not raise an error if the first job has applied steps" do
272
+ @job1.apply
273
+ lambda {
274
+ @job1 + @job2
275
+ }.should_not raise_error
276
+ end
277
+
278
+ it "should have the first job's temp_object" do
279
+ (@job1 + @job2).temp_object.data.should == 'hello'
280
+ end
281
+
282
+ it "should have the correct applied steps" do
283
+ @job1.apply
284
+ (@job1 + @job2).applied_steps.should match_steps([
285
+ Dragonfly::Job::Process
286
+ ])
287
+ end
288
+
289
+ it "should have the correct pending steps" do
290
+ @job1.apply
291
+ (@job1 + @job2).pending_steps.should match_steps([
292
+ Dragonfly::Job::Encode
293
+ ])
294
+ end
295
+ end
296
+
297
+ end
298
+
299
+ describe "defining extra steps after applying" do
300
+ before(:each) do
301
+ @app = mock_app
302
+ @job = Dragonfly::Job.new(@app)
303
+ @job.temp_object = Dragonfly::TempObject.new("hello")
304
+ @job.process! :resize
305
+ @job.apply
306
+ @job.encode! :micky
307
+ end
308
+ it "should not call apply on already applied steps" do
309
+ @job.steps[0].should_not_receive(:apply)
310
+ @job.apply
311
+ end
312
+ it "should call apply on not yet applied steps" do
313
+ @job.steps[1].should_receive(:apply)
314
+ @job.apply
315
+ end
316
+ it "should return all steps" do
317
+ @job.steps.should match_steps([
318
+ Dragonfly::Job::Process,
319
+ Dragonfly::Job::Encode
320
+ ])
321
+ end
322
+ it "should return applied steps" do
323
+ @job.applied_steps.should match_steps([
324
+ Dragonfly::Job::Process
325
+ ])
326
+ end
327
+ it "should return the pending steps" do
328
+ @job.pending_steps.should match_steps([
329
+ Dragonfly::Job::Encode
330
+ ])
331
+ end
332
+ it "should not call apply on any steps when already applied" do
333
+ @job.apply
334
+ @job.steps[0].should_not_receive(:apply)
335
+ @job.steps[1].should_not_receive(:apply)
336
+ @job.apply
337
+ end
338
+ end
339
+
340
+ describe "chaining" do
341
+
342
+ before(:each) do
343
+ @app = mock_app
344
+ @job = Dragonfly::Job.new(@app)
345
+ end
346
+
347
+ it "should return itself if bang is used" do
348
+ @job.fetch!('some_uid').should == @job
349
+ end
350
+
351
+ it "should return a new job if bang is not used" do
352
+ @job.fetch('some_uid').should_not == @job
353
+ end
354
+
355
+ describe "when a chained job is defined" do
356
+ before(:each) do
357
+ @job.fetch!('some_uid')
358
+ @job2 = @job.process(:resize, '30x30')
359
+ end
360
+
361
+ it "should return the correct steps for the original job" do
362
+ @job.applied_steps.should match_steps([
363
+ ])
364
+ @job.pending_steps.should match_steps([
365
+ Dragonfly::Job::Fetch
366
+ ])
367
+ end
368
+
369
+ it "should return the correct data for the original job" do
370
+ @job.data.should == 'SOME_DATA'
371
+ end
372
+
373
+ it "should return the correct steps for the new job" do
374
+ @job2.applied_steps.should match_steps([
375
+ ])
376
+ @job2.pending_steps.should match_steps([
377
+ Dragonfly::Job::Fetch,
378
+ Dragonfly::Job::Process
379
+ ])
380
+ end
381
+
382
+ it "should return the correct data for the new job" do
383
+ @job2.data.should == 'SOME_PROCESSED_DATA'
384
+ end
385
+
386
+ it "should not affect the other one when one is applied" do
387
+ @job.apply
388
+ @job.applied_steps.should match_steps([
389
+ Dragonfly::Job::Fetch
390
+ ])
391
+ @job.pending_steps.should match_steps([
392
+ ])
393
+ @job.temp_object.data.should == 'SOME_DATA'
394
+ @job2.applied_steps.should match_steps([
395
+ ])
396
+ @job2.pending_steps.should match_steps([
397
+ Dragonfly::Job::Fetch,
398
+ Dragonfly::Job::Process
399
+ ])
400
+ @job2.temp_object.should be_nil
401
+ end
402
+ end
403
+
404
+ end
405
+
406
+ describe "to_a" do
407
+ before(:each) do
408
+ @app = mock_app
409
+ end
410
+ it "should represent all the steps in array form" do
411
+ job = Dragonfly::Job.new(@app)
412
+ job.fetch! 'some_uid'
413
+ job.generate! :plasma # you wouldn't really call this after fetch but still works
414
+ job.process! :resize, '30x40'
415
+ job.encode! :gif, :bitrate => 20
416
+ job.to_a.should == [
417
+ [:f, 'some_uid'],
418
+ [:g, :plasma],
419
+ [:p, :resize, '30x40'],
420
+ [:e, :gif, {:bitrate => 20}]
421
+ ]
422
+ end
423
+ end
424
+
425
+ describe "from_a" do
426
+
427
+ before(:each) do
428
+ @app = test_app
429
+ end
430
+
431
+ describe "a well-defined array" do
432
+ before(:each) do
433
+ @job = Dragonfly::Job.from_a([
434
+ [:f, 'some_uid'],
435
+ [:g, :plasma],
436
+ [:p, :resize, '30x40'],
437
+ [:e, :gif, {:bitrate => 20}]
438
+ ], @app)
439
+ end
440
+ it "should have the correct step types" do
441
+ @job.steps.should match_steps([
442
+ Dragonfly::Job::Fetch,
443
+ Dragonfly::Job::Generate,
444
+ Dragonfly::Job::Process,
445
+ Dragonfly::Job::Encode
446
+ ])
447
+ end
448
+ it "should have the correct args" do
449
+ @job.steps[0].args.should == ['some_uid']
450
+ @job.steps[1].args.should == [:plasma]
451
+ @job.steps[2].args.should == [:resize, '30x40']
452
+ @job.steps[3].args.should == [:gif, {:bitrate => 20}]
453
+ end
454
+ it "should have no applied steps" do
455
+ @job.applied_steps.should be_empty
456
+ end
457
+ it "should have all steps pending" do
458
+ @job.steps.should == @job.pending_steps
459
+ end
460
+ end
461
+
462
+ [
463
+ :f,
464
+ [:f],
465
+ [[]],
466
+ [[:egg]]
467
+ ].each do |object|
468
+ it "should raise an error if the object passed in is #{object.inspect}" do
469
+ lambda {
470
+ Dragonfly::Job.from_a(object, @app)
471
+ }.should raise_error(Dragonfly::Job::InvalidArray)
472
+ end
473
+ end
474
+
475
+ it "should initialize an empty job if the array is empty" do
476
+ job = Dragonfly::Job.from_a([], @app)
477
+ job.steps.should be_empty
478
+ end
479
+ end
480
+
481
+ describe "from_path" do
482
+ before(:each) do
483
+ @app = test_app
484
+ @serialized = @app.fetch('eggs').serialize
485
+ end
486
+ it "should work with a simple path" do
487
+ Dragonfly::Job.from_path("/#{@serialized}", @app).should be_a(Dragonfly::Job)
488
+ end
489
+ it "should work with no slash" do
490
+ Dragonfly::Job.from_path(@serialized, @app).should be_a(Dragonfly::Job)
491
+ end
492
+ it "should ignore the app's url_path_prefix" do
493
+ @app.url_path_prefix = '/images/yo'
494
+ Dragonfly::Job.from_path("/images/yo/#{@serialized}", @app).should be_a(Dragonfly::Job)
495
+ end
496
+ it "should not work with an incorrect url_path_prefix" do
497
+ @app.url_path_prefix = '/images/yo'
498
+ lambda{
499
+ Dragonfly::Job.from_path("/images/#{@serialized}", @app).should be_a(Dragonfly::Job)
500
+ }.should raise_error(Dragonfly::Serializer::BadString)
501
+ end
502
+ end
503
+
504
+ describe "serialization" do
505
+ before(:each) do
506
+ @app = test_app
507
+ @job = Dragonfly::Job.new(@app).fetch('mumma').process(:resize, '1x50')
508
+ end
509
+ it "should serialize itself" do
510
+ @job.serialize.should =~ /^\w{1,}$/
511
+ end
512
+ it "should deserialize to the same as the original" do
513
+ new_job = Dragonfly::Job.deserialize(@job.serialize, @app)
514
+ new_job.to_a.should == @job.to_a
515
+ end
516
+ end
517
+
518
+ describe "to_app" do
519
+ before(:each) do
520
+ @app = test_app
521
+ @job = Dragonfly::Job.new(@app)
522
+ end
523
+ it "should return an endpoint" do
524
+ endpoint = @job.to_app
525
+ endpoint.should be_a(Dragonfly::JobEndpoint)
526
+ endpoint.job.should == @job
527
+ end
528
+ end
529
+
530
+ describe "url" do
531
+ before(:each) do
532
+ @app = mock_app(:url_for => 'hello')
533
+ @job = Dragonfly::Job.new(@app)
534
+ end
535
+ it "should return a url" do
536
+ @job.generate!(:plasma)
537
+ @job.url.should == 'hello'
538
+ end
539
+ it "should return nil if there are no steps" do
540
+ @job.url.should be_nil
541
+ end
542
+ end
543
+
544
+ describe "to_fetched_job" do
545
+ it "should maintain the same temp_object and be already applied" do
546
+ app = mock_app
547
+ job = Dragonfly::Job.new(app, Dragonfly::TempObject.new("HELLO"))
548
+ new_job = job.to_fetched_job('some_uid')
549
+ new_job.temp_object.data.should == 'HELLO'
550
+ new_job.to_a.should == [
551
+ [:f, 'some_uid']
552
+ ]
553
+ new_job.pending_steps.should be_empty
554
+ end
555
+ end
556
+
557
+ describe "format" do
558
+ before(:each) do
559
+ @app = test_app
560
+ end
561
+ it "should default to nil" do
562
+ job = @app.new_job("HELLO")
563
+ job.format.should be_nil
564
+ end
565
+ it "should use the temp_object format if it exists" do
566
+ job = @app.new_job("HELLO", :format => :txt)
567
+ job.format.should == :txt
568
+ end
569
+ it "should use the analyser format if it exists" do
570
+ @app.analyser.add :format do |temp_object|
571
+ :egg
572
+ end
573
+ job = @app.new_job("HELLO")
574
+ job.format.should == :egg
575
+ end
576
+ it "should prefer the temp_object format if both exist" do
577
+ @app.analyser.add :format do |temp_object|
578
+ :egg
579
+ end
580
+ job = @app.new_job("HELLO", :format => :txt)
581
+ job.format.should == :txt
582
+ end
583
+ end
584
+
585
+ describe "validate_sha!" do
586
+ before(:each) do
587
+ @app = test_app
588
+ @job = @app.fetch('eggs')
589
+ end
590
+ it "should raise an error if nothing is given" do
591
+ lambda{
592
+ @job.validate_sha!(nil)
593
+ }.should raise_error(Dragonfly::Job::NoSHAGiven)
594
+ end
595
+ it "should raise an error if the wrong SHA is given" do
596
+ lambda{
597
+ @job.validate_sha!('asdf')
598
+ }.should raise_error(Dragonfly::Job::IncorrectSHA)
599
+ end
600
+ it "should return self if ok" do
601
+ @job.validate_sha!(@job.sha).should == @job
602
+ end
603
+ end
604
+
605
+ end