oahu-dragonfly 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/.yardopts +24 -0
- data/Gemfile +30 -0
- data/History.md +323 -0
- data/LICENSE +20 -0
- data/README.md +88 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/config.ru +14 -0
- data/docs.watchr +1 -0
- data/dragonfly.gemspec +297 -0
- data/extra_docs/Analysers.md +66 -0
- data/extra_docs/Caching.md +23 -0
- data/extra_docs/Configuration.md +124 -0
- data/extra_docs/Couch.md +49 -0
- data/extra_docs/DataStorage.md +153 -0
- data/extra_docs/Encoding.md +67 -0
- data/extra_docs/GeneralUsage.md +121 -0
- data/extra_docs/Generators.md +60 -0
- data/extra_docs/Heroku.md +50 -0
- data/extra_docs/ImageMagick.md +125 -0
- data/extra_docs/Index.md +33 -0
- data/extra_docs/MimeTypes.md +40 -0
- data/extra_docs/Models.md +272 -0
- data/extra_docs/Mongo.md +45 -0
- data/extra_docs/Processing.md +77 -0
- data/extra_docs/Rack.md +52 -0
- data/extra_docs/Rails2.md +57 -0
- data/extra_docs/Rails3.md +62 -0
- data/extra_docs/Sinatra.md +25 -0
- data/extra_docs/URLs.md +169 -0
- data/features/images.feature +47 -0
- data/features/no_processing.feature +14 -0
- data/features/rails_3.0.5.feature +8 -0
- data/features/steps/common_steps.rb +8 -0
- data/features/steps/dragonfly_steps.rb +66 -0
- data/features/steps/rails_steps.rb +28 -0
- data/features/support/env.rb +13 -0
- data/features/support/setup.rb +32 -0
- data/fixtures/rails_3.0.5/files/app/models/album.rb +7 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/show.html.erb +6 -0
- data/fixtures/rails_3.0.5/files/config/initializers/dragonfly.rb +4 -0
- data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/image_steps.rb +25 -0
- data/fixtures/rails_3.0.5/files/features/support/paths.rb +17 -0
- data/fixtures/rails_3.0.5/files/features/text_images.feature +7 -0
- data/fixtures/rails_3.0.5/template.rb +20 -0
- data/irbrc.rb +18 -0
- data/lib/dragonfly.rb +55 -0
- data/lib/dragonfly/active_model_extensions.rb +13 -0
- data/lib/dragonfly/active_model_extensions/attachment.rb +250 -0
- data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +148 -0
- data/lib/dragonfly/active_model_extensions/class_methods.rb +95 -0
- data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
- data/lib/dragonfly/active_model_extensions/validations.rb +41 -0
- data/lib/dragonfly/analyser.rb +58 -0
- data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
- data/lib/dragonfly/analysis/image_magick_analyser.rb +6 -0
- data/lib/dragonfly/app.rb +172 -0
- data/lib/dragonfly/config/heroku.rb +19 -0
- data/lib/dragonfly/config/image_magick.rb +6 -0
- data/lib/dragonfly/config/rails.rb +20 -0
- data/lib/dragonfly/configurable.rb +207 -0
- data/lib/dragonfly/core_ext/array.rb +7 -0
- data/lib/dragonfly/core_ext/hash.rb +7 -0
- data/lib/dragonfly/core_ext/object.rb +12 -0
- data/lib/dragonfly/core_ext/string.rb +9 -0
- data/lib/dragonfly/core_ext/symbol.rb +9 -0
- data/lib/dragonfly/data_storage.rb +9 -0
- data/lib/dragonfly/data_storage/couch_data_store.rb +64 -0
- data/lib/dragonfly/data_storage/file_data_store.rb +141 -0
- data/lib/dragonfly/data_storage/mongo_data_store.rb +86 -0
- data/lib/dragonfly/data_storage/s3data_store.rb +145 -0
- data/lib/dragonfly/encoder.rb +13 -0
- data/lib/dragonfly/encoding/image_magick_encoder.rb +6 -0
- data/lib/dragonfly/function_manager.rb +71 -0
- data/lib/dragonfly/generation/image_magick_generator.rb +6 -0
- data/lib/dragonfly/generator.rb +9 -0
- data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
- data/lib/dragonfly/image_magick/analyser.rb +51 -0
- data/lib/dragonfly/image_magick/config.rb +41 -0
- data/lib/dragonfly/image_magick/encoder.rb +57 -0
- data/lib/dragonfly/image_magick/generator.rb +145 -0
- data/lib/dragonfly/image_magick/processor.rb +99 -0
- data/lib/dragonfly/image_magick/utils.rb +72 -0
- data/lib/dragonfly/image_magick_utils.rb +4 -0
- data/lib/dragonfly/job.rb +451 -0
- data/lib/dragonfly/job_builder.rb +39 -0
- data/lib/dragonfly/job_definitions.rb +26 -0
- data/lib/dragonfly/job_endpoint.rb +15 -0
- data/lib/dragonfly/loggable.rb +28 -0
- data/lib/dragonfly/middleware.rb +20 -0
- data/lib/dragonfly/processing/image_magick_processor.rb +6 -0
- data/lib/dragonfly/processor.rb +9 -0
- data/lib/dragonfly/rails/images.rb +27 -0
- data/lib/dragonfly/response.rb +97 -0
- data/lib/dragonfly/routed_endpoint.rb +40 -0
- data/lib/dragonfly/serializer.rb +32 -0
- data/lib/dragonfly/server.rb +113 -0
- data/lib/dragonfly/simple_cache.rb +23 -0
- data/lib/dragonfly/temp_object.rb +175 -0
- data/lib/dragonfly/url_mapper.rb +78 -0
- data/samples/beach.png +0 -0
- data/samples/egg.png +0 -0
- data/samples/round.gif +0 -0
- data/samples/sample.docx +0 -0
- data/samples/taj.jpg +0 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +1426 -0
- data/spec/dragonfly/active_model_extensions/spec_helper.rb +91 -0
- data/spec/dragonfly/analyser_spec.rb +123 -0
- data/spec/dragonfly/analysis/file_command_analyser_spec.rb +48 -0
- data/spec/dragonfly/app_spec.rb +135 -0
- data/spec/dragonfly/configurable_spec.rb +461 -0
- data/spec/dragonfly/core_ext/array_spec.rb +19 -0
- data/spec/dragonfly/core_ext/hash_spec.rb +19 -0
- data/spec/dragonfly/core_ext/string_spec.rb +17 -0
- data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
- data/spec/dragonfly/data_storage/couch_data_store_spec.rb +76 -0
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +296 -0
- data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +57 -0
- data/spec/dragonfly/data_storage/s3_data_store_spec.rb +258 -0
- data/spec/dragonfly/data_storage/shared_data_store_examples.rb +77 -0
- data/spec/dragonfly/function_manager_spec.rb +154 -0
- data/spec/dragonfly/hash_with_css_style_keys_spec.rb +24 -0
- data/spec/dragonfly/image_magick/analyser_spec.rb +64 -0
- data/spec/dragonfly/image_magick/encoder_spec.rb +41 -0
- data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
- data/spec/dragonfly/image_magick/processor_spec.rb +233 -0
- data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
- data/spec/dragonfly/job_builder_spec.rb +37 -0
- data/spec/dragonfly/job_definitions_spec.rb +35 -0
- data/spec/dragonfly/job_endpoint_spec.rb +173 -0
- data/spec/dragonfly/job_spec.rb +1046 -0
- data/spec/dragonfly/loggable_spec.rb +80 -0
- data/spec/dragonfly/middleware_spec.rb +47 -0
- data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
- data/spec/dragonfly/serializer_spec.rb +61 -0
- data/spec/dragonfly/server_spec.rb +278 -0
- data/spec/dragonfly/simple_cache_spec.rb +27 -0
- data/spec/dragonfly/temp_object_spec.rb +306 -0
- data/spec/dragonfly/url_mapper_spec.rb +126 -0
- data/spec/functional/deprecations_spec.rb +51 -0
- data/spec/functional/image_magick_app_spec.rb +27 -0
- data/spec/functional/model_urls_spec.rb +85 -0
- data/spec/functional/remote_on_the_fly_spec.rb +51 -0
- data/spec/functional/to_response_spec.rb +31 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/argument_matchers.rb +19 -0
- data/spec/support/image_matchers.rb +47 -0
- data/spec/support/simple_matchers.rb +53 -0
- data/yard/handlers/configurable_attr_handler.rb +38 -0
- data/yard/setup.rb +15 -0
- data/yard/templates/default/fulldoc/html/css/common.css +107 -0
- data/yard/templates/default/layout/html/layout.erb +89 -0
- data/yard/templates/default/module/html/configuration_summary.erb +31 -0
- data/yard/templates/default/module/setup.rb +17 -0
- metadata +544 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
# --------------------------------------------------------------- #
|
5
|
+
# MODELS
|
6
|
+
# --------------------------------------------------------------- #
|
7
|
+
class MyModel
|
8
|
+
|
9
|
+
# Callbacks
|
10
|
+
extend ActiveModel::Callbacks
|
11
|
+
define_model_callbacks :save, :destroy
|
12
|
+
|
13
|
+
include ActiveModel::Validations
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def create!(attrs={})
|
17
|
+
new(attrs).save!
|
18
|
+
end
|
19
|
+
|
20
|
+
def find(id)
|
21
|
+
new(instances[id])
|
22
|
+
end
|
23
|
+
|
24
|
+
def instances
|
25
|
+
@instances ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(attrs={})
|
30
|
+
attrs.each do |key, value|
|
31
|
+
send("#{key}=", value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_accessor :id
|
36
|
+
|
37
|
+
def to_hash
|
38
|
+
self.class::ATTRIBUTES.inject({}) do |hash, attr|
|
39
|
+
hash[attr] = send(attr)
|
40
|
+
hash
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def save
|
45
|
+
_run_save_callbacks {
|
46
|
+
self.id ||= rand(1000)
|
47
|
+
self.class.instances[id] = self.to_hash
|
48
|
+
}
|
49
|
+
end
|
50
|
+
def save!
|
51
|
+
save
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def destroy
|
56
|
+
_run_destroy_callbacks {}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Item < MyModel
|
61
|
+
|
62
|
+
ATTRIBUTES = [
|
63
|
+
:title,
|
64
|
+
:preview_image_uid,
|
65
|
+
:preview_image_some_analyser_method,
|
66
|
+
:preview_image_size,
|
67
|
+
:preview_image_name,
|
68
|
+
:preview_image_blah_blah,
|
69
|
+
:other_image_uid,
|
70
|
+
:yet_another_image_uid,
|
71
|
+
:otra_imagen_uid,
|
72
|
+
:trailer_video_uid,
|
73
|
+
:created_at,
|
74
|
+
:updated_at
|
75
|
+
]
|
76
|
+
attr_accessor *ATTRIBUTES
|
77
|
+
end
|
78
|
+
|
79
|
+
class Car < MyModel
|
80
|
+
ATTRIBUTES = [
|
81
|
+
:image_uid,
|
82
|
+
:reliant_image_uid,
|
83
|
+
:type
|
84
|
+
]
|
85
|
+
attr_accessor *ATTRIBUTES
|
86
|
+
end
|
87
|
+
|
88
|
+
class Photo < MyModel
|
89
|
+
ATTRIBUTES = [:image_uid]
|
90
|
+
attr_accessor *ATTRIBUTES
|
91
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dragonfly::Analyser do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@analyser = Dragonfly::Analyser.new
|
7
|
+
@analyser.log = Logger.new(LOG_FILE)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "analysis_methods module" do
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@analyser.add(:num_letters){|temp_object, letter| temp_object.data.count(letter) }
|
14
|
+
@obj = Object.new
|
15
|
+
@obj.extend @analyser.analysis_methods
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return a module" do
|
19
|
+
@analyser.analysis_methods.should be_a(Module)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should provide the object with the analyser method" do
|
23
|
+
@obj.analyser.should == @analyser
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should provide the object with the direct analysis method, provided that analyse method exists" do
|
27
|
+
def @obj.analyse(meth, *args)
|
28
|
+
analyser.analyse Dragonfly::TempObject.new('HELLO'), meth, *args
|
29
|
+
end
|
30
|
+
@obj.num_letters('L').should == 2
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "analyse" do
|
36
|
+
it "should return nil if the function isn't defined" do
|
37
|
+
@analyser.analyse(Dragonfly::TempObject.new("Hello"), :width).should be_nil
|
38
|
+
end
|
39
|
+
it "should return nil if the function can't be handled" do
|
40
|
+
@analyser.add(:width){ throw :unable_to_handle }
|
41
|
+
@analyser.analyse(Dragonfly::TempObject.new("Hello"), :width).should be_nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "analysis_method_names" do
|
46
|
+
it "should return the analysis methods" do
|
47
|
+
@analyser.add(:width){}
|
48
|
+
@analyser.add(:height){}
|
49
|
+
@analyser.analysis_method_names.should == [:width, :height]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "cache" do
|
54
|
+
before(:each) do
|
55
|
+
@temp_object = Dragonfly::TempObject.new('HELLO')
|
56
|
+
end
|
57
|
+
|
58
|
+
def it_should_analyse_using(meth, temp_object, *args)
|
59
|
+
result = mock('result')
|
60
|
+
@analyser.should_receive(:call_last).with(meth, temp_object, *args).exactly(:once).and_return result
|
61
|
+
@analyser.analyse(temp_object, meth, *args).should == result
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should do the analysis the first time" do
|
66
|
+
it_should_analyse_using(:blah, @temp_object, :arg1)
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "when already called" do
|
70
|
+
before(:each) do
|
71
|
+
@result = it_should_analyse_using(:blah, @temp_object, :arg1)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should not do it subsequent times but still return the result" do
|
75
|
+
@analyser.should_not_receive(:call_last)
|
76
|
+
@analyser.analyse(@temp_object, :blah, :arg1).should == @result
|
77
|
+
@analyser.analyse(@temp_object, :blah, :arg1).should == @result
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should not use the cache if the temp_object is different" do
|
81
|
+
temp_object = Dragonfly::TempObject.new('aaa')
|
82
|
+
it_should_analyse_using(:blah, temp_object, :arg1)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not use the cache if the method name is different" do
|
86
|
+
it_should_analyse_using(:egghead, @temp_object, :arg1)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not use the cache if the args are different" do
|
90
|
+
it_should_analyse_using(:blah, @temp_object, :arg2)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should do it again if the cache has been cleared" do
|
94
|
+
@analyser.clear_cache!
|
95
|
+
it_should_analyse_using(:blah, @temp_object, :arg1)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should not use the cache if it has been turned off" do
|
99
|
+
@analyser.enable_cache = false
|
100
|
+
it_should_analyse_using(:blah, @temp_object, :arg1)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "cache size" do
|
106
|
+
it "should not exceed the cache size" do
|
107
|
+
@analyser.cache_size = 2
|
108
|
+
|
109
|
+
res1 = it_should_analyse_using(:blah, @temp_object, :arg1)
|
110
|
+
res2 = it_should_analyse_using(:blah, @temp_object, :arg2)
|
111
|
+
res3 = it_should_analyse_using(:blah, @temp_object, :arg3) # Should kick out first one
|
112
|
+
|
113
|
+
it_should_analyse_using(:blah, @temp_object, :arg1)
|
114
|
+
|
115
|
+
# Third analysis should still be cached
|
116
|
+
@analyser.should_not_receive(:call_last)
|
117
|
+
@analyser.analyse(@temp_object, :blah, :arg3).should == res3
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
png_path = File.dirname(__FILE__) + '/../../../samples/egg.png'
|
4
|
+
|
5
|
+
describe Dragonfly::Analysis::FileCommandAnalyser do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@analyser = Dragonfly::Analysis::FileCommandAnalyser.new
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "mime_type" do
|
12
|
+
|
13
|
+
describe "when using the filesystem" do
|
14
|
+
before(:each) do
|
15
|
+
@analyser.use_filesystem = true
|
16
|
+
@temp_object = Dragonfly::TempObject.new(File.new(png_path))
|
17
|
+
end
|
18
|
+
it "should give the mime-type" do
|
19
|
+
@analyser.mime_type(@temp_object).should == 'image/png'
|
20
|
+
end
|
21
|
+
it "should not have initialized the data string" do
|
22
|
+
@analyser.mime_type(@temp_object)
|
23
|
+
@temp_object.instance_eval{@data}.should be_nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "when not using the filesystem" do
|
28
|
+
before(:each) do
|
29
|
+
@analyser.use_filesystem = false
|
30
|
+
@temp_object = Dragonfly::TempObject.new(File.read(png_path))
|
31
|
+
end
|
32
|
+
it "should give the mime-type" do
|
33
|
+
@analyser.mime_type(@temp_object).should == 'image/png'
|
34
|
+
end
|
35
|
+
it "should not have initialized the file" do
|
36
|
+
@analyser.mime_type(@temp_object)
|
37
|
+
@temp_object.instance_eval{@tempfile}.should be_nil
|
38
|
+
end
|
39
|
+
it "should work properly (without a broken pipe error) for big files of format jpg" do
|
40
|
+
data = Dragonfly::ImageMagick::Generator.new.plasma(1000, 1000, :jpg).first
|
41
|
+
temp_object = Dragonfly::TempObject.new(data)
|
42
|
+
@analyser.mime_type(temp_object).should == "image/jpeg"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
def request(app, path)
|
5
|
+
Rack::MockRequest.new(app).get(path)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Dragonfly::App do
|
9
|
+
|
10
|
+
describe ".instance" do
|
11
|
+
|
12
|
+
it "should create a new instance if it didn't already exist" do
|
13
|
+
app = Dragonfly::App.instance(:images)
|
14
|
+
app.should be_a(Dragonfly::App)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return an existing instance if called by name" do
|
18
|
+
app = Dragonfly::App.instance(:images)
|
19
|
+
Dragonfly::App.instance(:images).should == app
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should also work using square brackets" do
|
23
|
+
Dragonfly[:images].should == Dragonfly::App.instance(:images)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".new" do
|
29
|
+
it "should not be callable" do
|
30
|
+
lambda{
|
31
|
+
Dragonfly::App.new
|
32
|
+
}.should raise_error(NoMethodError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "mime types" do
|
37
|
+
describe "#mime_type_for" do
|
38
|
+
before(:each) do
|
39
|
+
@app = test_app
|
40
|
+
end
|
41
|
+
it "should return the correct mime type for a symbol" do
|
42
|
+
@app.mime_type_for(:png).should == 'image/png'
|
43
|
+
end
|
44
|
+
it "should work for strings" do
|
45
|
+
@app.mime_type_for('png').should == 'image/png'
|
46
|
+
end
|
47
|
+
it "should work with uppercase strings" do
|
48
|
+
@app.mime_type_for('PNG').should == 'image/png'
|
49
|
+
end
|
50
|
+
it "should work with a dot" do
|
51
|
+
@app.mime_type_for('.png').should == 'image/png'
|
52
|
+
end
|
53
|
+
it "should return nil if not known" do
|
54
|
+
@app.mime_type_for(:mark).should be_nil
|
55
|
+
end
|
56
|
+
it "should allow for configuring extra mime types" do
|
57
|
+
@app.register_mime_type 'mark', 'application/mark'
|
58
|
+
@app.mime_type_for(:mark).should == 'application/mark'
|
59
|
+
end
|
60
|
+
it "should override existing mime types when registered" do
|
61
|
+
@app.register_mime_type :png, 'ping/pong'
|
62
|
+
@app.mime_type_for(:png).should == 'ping/pong'
|
63
|
+
end
|
64
|
+
it "should have a per-app mime-type configuration" do
|
65
|
+
other_app = Dragonfly[:other_app]
|
66
|
+
@app.register_mime_type(:mark, 'first/one')
|
67
|
+
other_app.register_mime_type(:mark, 'second/one')
|
68
|
+
@app.mime_type_for(:mark).should == 'first/one'
|
69
|
+
other_app.mime_type_for(:mark).should == 'second/one'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "remote_url_for" do
|
75
|
+
before(:each) do
|
76
|
+
@app = test_app
|
77
|
+
@app.datastore = Object.new
|
78
|
+
end
|
79
|
+
it "should raise an error if the datastore doesn't provide it" do
|
80
|
+
lambda{
|
81
|
+
@app.remote_url_for('some_uid')
|
82
|
+
}.should raise_error(NotImplementedError)
|
83
|
+
end
|
84
|
+
it "should correctly call it if the datastore provides it" do
|
85
|
+
@app.datastore.should_receive(:url_for).with('some_uid', :some => :opts).and_return 'http://egg.head'
|
86
|
+
@app.remote_url_for('some_uid', :some => :opts).should == 'http://egg.head'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#store" do
|
91
|
+
before(:each) do
|
92
|
+
@app = test_app
|
93
|
+
end
|
94
|
+
it "should allow just storing content" do
|
95
|
+
@app.datastore.should_receive(:store).with(a_temp_object_with_data("HELLO"), {})
|
96
|
+
@app.store("HELLO")
|
97
|
+
end
|
98
|
+
it "should allow storing using a TempObject" do
|
99
|
+
temp_object = Dragonfly::TempObject.new("HELLO")
|
100
|
+
@app.datastore.should_receive(:store).with(temp_object, {})
|
101
|
+
@app.store(temp_object)
|
102
|
+
end
|
103
|
+
it "should allow storing with extra stuff" do
|
104
|
+
@app.datastore.should_receive(:store).with(
|
105
|
+
a_temp_object_with_data("HELLO"), :meta => {:egg => :head}, :option => :blarney
|
106
|
+
)
|
107
|
+
@app.store("HELLO", :meta => {:egg => :head}, :option => :blarney)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "url_for" do
|
112
|
+
before(:each) do
|
113
|
+
@app = test_app
|
114
|
+
@job = @app.fetch('eggs')
|
115
|
+
end
|
116
|
+
it "should give the server url by default" do
|
117
|
+
@app.url_for(@job).should =~ %r{^/\w+$}
|
118
|
+
end
|
119
|
+
it "should allow configuring" do
|
120
|
+
@app.configure do |c|
|
121
|
+
c.define_url do |app, job, opts|
|
122
|
+
"doogies"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
@app.url_for(@job).should == 'doogies'
|
126
|
+
end
|
127
|
+
it "should yield the correct dooberries" do
|
128
|
+
@app.define_url do |app, job, opts|
|
129
|
+
[app, job, opts]
|
130
|
+
end
|
131
|
+
@app.url_for(@job, {'chuddies' => 'margate'}).should == [@app, @job, {'chuddies' => 'margate'}]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,461 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dragonfly::Configurable do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
class Car
|
7
|
+
include Dragonfly::Configurable
|
8
|
+
configurable_attr :colour
|
9
|
+
configurable_attr :top_speed, 216
|
10
|
+
def self.other_thing=(thing); end
|
11
|
+
end
|
12
|
+
@car = Car.new
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "setup" do
|
16
|
+
it "should provide attr_readers for configurable attributes" do
|
17
|
+
@car.should respond_to(:colour)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should provide attr_writers for configurable attributes" do
|
21
|
+
@car.colour = 'verde'
|
22
|
+
@car.colour.should == 'verde'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should set default values for configurable attributes" do
|
26
|
+
@car.top_speed.should == 216
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should set the default as nil if not specified" do
|
30
|
+
@car.colour.should be_nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should allow setting to nil" do
|
34
|
+
@car.top_speed = nil
|
35
|
+
@car.top_speed.should be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should allow specifying configurable attrs as strings" do
|
39
|
+
class Bike
|
40
|
+
include Dragonfly::Configurable
|
41
|
+
configurable_attr 'colour', 'rude'
|
42
|
+
end
|
43
|
+
Bike.new.colour.should == 'rude'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "configuring" do
|
48
|
+
it "should allow you to change values" do
|
49
|
+
@car.configure do |c|
|
50
|
+
c.colour = 'red'
|
51
|
+
end
|
52
|
+
@car.colour.should == 'red'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should not allow you to call other methods on the object via the configuration" do
|
56
|
+
lambda{
|
57
|
+
@car.configure do |c|
|
58
|
+
c.other_thing = 5
|
59
|
+
end
|
60
|
+
}.should raise_error(Dragonfly::Configurable::BadConfigAttribute)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should return itself" do
|
64
|
+
@car.configure{|c|}.should == @car
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "getting configuration" do
|
69
|
+
it "should return the configuration when nothing is set" do
|
70
|
+
@car.configuration.should == {}
|
71
|
+
end
|
72
|
+
it "should return the configuration when something is set" do
|
73
|
+
@car.top_speed = 10
|
74
|
+
@car.configuration.should == {:top_speed => 10}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "multiple objects" do
|
79
|
+
it "should return the default configuration" do
|
80
|
+
Car.default_configuration.should == {:colour => nil, :top_speed => 216}
|
81
|
+
end
|
82
|
+
it "should allow instances to be configured differently" do
|
83
|
+
car1 = Car.new
|
84
|
+
car1.configure{|c| c.colour = 'green'}
|
85
|
+
car2 = Car.new
|
86
|
+
car2.configure{|c| c.colour = 'yellow'}
|
87
|
+
car1.configuration.should == {:colour => 'green'}
|
88
|
+
car2.configuration.should == {:colour => 'yellow'}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "lazy attributes" do
|
93
|
+
before(:each) do
|
94
|
+
cow = @cow = mock('cow')
|
95
|
+
class Lazy; end
|
96
|
+
Lazy.class_eval do
|
97
|
+
include Dragonfly::Configurable
|
98
|
+
configurable_attr(:sound){ cow.moo }
|
99
|
+
end
|
100
|
+
@lazy = Lazy.new
|
101
|
+
end
|
102
|
+
it "should not call the block if the configurable attribute is set to something else" do
|
103
|
+
@cow.should_not_receive(:moo)
|
104
|
+
@lazy.configure{|c| c.sound = 'baa' }
|
105
|
+
@lazy.sound.should == 'baa'
|
106
|
+
end
|
107
|
+
it "should call the block if it's not been changed, once it's accessed" do
|
108
|
+
@cow.should_receive(:moo).and_return('mooo!')
|
109
|
+
@lazy.sound.should == 'mooo!'
|
110
|
+
end
|
111
|
+
it "should not call the block when accessed again" do
|
112
|
+
@cow.should_receive(:moo).exactly(:once).and_return('mooo!')
|
113
|
+
@lazy.sound.should == 'mooo!'
|
114
|
+
@lazy.sound.should == 'mooo!'
|
115
|
+
end
|
116
|
+
it "should not call an explicitly passed in proc" do
|
117
|
+
@lazy.configure{|c| c.sound = lambda{ @cow.fart }}
|
118
|
+
@lazy.sound.should be_a(Proc)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "using in the singleton class" do
|
123
|
+
it "should work" do
|
124
|
+
class OneOff
|
125
|
+
class << self
|
126
|
+
include Dragonfly::Configurable
|
127
|
+
configurable_attr :food, 'bread'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
OneOff.food.should == 'bread'
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "configuration method" do
|
135
|
+
|
136
|
+
before(:each) do
|
137
|
+
class ClassWithMethod
|
138
|
+
include Dragonfly::Configurable
|
139
|
+
def add_thing(thing)
|
140
|
+
'poo'
|
141
|
+
end
|
142
|
+
def remove_thing(thing)
|
143
|
+
'bum'
|
144
|
+
end
|
145
|
+
configuration_method :add_thing, :remove_thing
|
146
|
+
end
|
147
|
+
@thing = ClassWithMethod.new
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should allow calling the method through 'configure'" do
|
151
|
+
@thing.configure do |c|
|
152
|
+
c.add_thing('duck')
|
153
|
+
c.remove_thing('dog')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "nested configurable objects" do
|
160
|
+
|
161
|
+
before(:each) do
|
162
|
+
class NestedThing
|
163
|
+
include Dragonfly::Configurable
|
164
|
+
configurable_attr :age, 29
|
165
|
+
def some_method(val)
|
166
|
+
@some_thing = val
|
167
|
+
end
|
168
|
+
configuration_method :some_method
|
169
|
+
attr_reader :some_thing
|
170
|
+
end
|
171
|
+
|
172
|
+
class Car
|
173
|
+
def nested_thing
|
174
|
+
@nested_thing ||= NestedThing.new
|
175
|
+
end
|
176
|
+
nested_configurable :nested_thing
|
177
|
+
end
|
178
|
+
|
179
|
+
@car.configure do |c|
|
180
|
+
c.nested_thing.configure do |nt|
|
181
|
+
nt.age = 50
|
182
|
+
nt.some_method('yo')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should allow configuring nested configurable accessors" do
|
188
|
+
@car.nested_thing.age.should == 50
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should allow configuring nested configurable normal methods" do
|
192
|
+
@car.nested_thing.some_thing.should == 'yo'
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should not allow configuring directly on the config object" do
|
196
|
+
expect{
|
197
|
+
@car.configure do |c|
|
198
|
+
c.some_method('other')
|
199
|
+
end
|
200
|
+
}.to raise_error(Dragonfly::Configurable::BadConfigAttribute)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "configuring with a saved config" do
|
205
|
+
before(:each) do
|
206
|
+
@cool_configuration = Object.new
|
207
|
+
def @cool_configuration.apply_configuration(car, colour=nil)
|
208
|
+
car.configure do |c|
|
209
|
+
c.colour = (colour || 'vermelho')
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should allow configuration by a saved config" do
|
215
|
+
@car.configure_with(@cool_configuration)
|
216
|
+
@car.colour.should == 'vermelho'
|
217
|
+
@car.top_speed.should == 216
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should pass any args through to the saved config" do
|
221
|
+
@car.configure_with(@cool_configuration, 'preto')
|
222
|
+
@car.colour.should == 'preto'
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should yield a block for any extra configuration" do
|
226
|
+
@car.configure_with(@cool_configuration) do |c|
|
227
|
+
c.colour = 'branco'
|
228
|
+
end
|
229
|
+
@car.colour.should == 'branco'
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should return itself" do
|
233
|
+
@car.configure_with(@cool_configuration).should == @car
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "using a symbol to specify the config" do
|
237
|
+
|
238
|
+
before(:all) do
|
239
|
+
@rally_config = Object.new
|
240
|
+
Car.register_configuration(:rally, @rally_config)
|
241
|
+
@long_journey_config = Object.new
|
242
|
+
Car.register_configuration(:long_journey){ @long_journey_config }
|
243
|
+
Car.register_configuration(:some_library){ SomeLibrary }
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should map the symbol to the correct configuration" do
|
247
|
+
@rally_config.should_receive(:apply_configuration).with(@car)
|
248
|
+
@car.configure_with(:rally)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should map the symbol to the correct configuration lazily" do
|
252
|
+
@long_journey_config.should_receive(:apply_configuration).with(@car)
|
253
|
+
@car.configure_with(:long_journey)
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should throw an error if an unknown symbol is passed in" do
|
257
|
+
lambda {
|
258
|
+
@car.configure_with(:eggs)
|
259
|
+
}.should raise_error(ArgumentError)
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should only try to load the library when asked to" do
|
263
|
+
lambda{
|
264
|
+
@car.configure_with(:some_library)
|
265
|
+
}.should raise_error(NameError, /uninitialized constant.*SomeLibrary/)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
describe "falling back to another config" do
|
272
|
+
before(:each) do
|
273
|
+
@garage = Object.new
|
274
|
+
class << @garage
|
275
|
+
include Dragonfly::Configurable
|
276
|
+
configurable_attr :top_speed, 100
|
277
|
+
end
|
278
|
+
@car.use_as_fallback_config(@garage)
|
279
|
+
end
|
280
|
+
|
281
|
+
describe "when nothing set" do
|
282
|
+
it "should use its default" do
|
283
|
+
@car.top_speed.should == 216
|
284
|
+
end
|
285
|
+
it "shouldn't affect the fallback config object" do
|
286
|
+
@garage.top_speed.should == 100
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "if set" do
|
291
|
+
before(:each) do
|
292
|
+
@car.top_speed = 444
|
293
|
+
end
|
294
|
+
it "should work normally" do
|
295
|
+
@car.top_speed.should == 444
|
296
|
+
end
|
297
|
+
it "shouldn't affect the fallback config object" do
|
298
|
+
@garage.top_speed.should == 100
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe "both set" do
|
303
|
+
before(:each) do
|
304
|
+
@car.top_speed = 444
|
305
|
+
@garage.top_speed = 3000
|
306
|
+
end
|
307
|
+
it "should prefer its own setting" do
|
308
|
+
@car.top_speed.should == 444
|
309
|
+
end
|
310
|
+
it "shouldn't affect the fallback config object" do
|
311
|
+
@garage.top_speed.should == 3000
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe "the fallback config is set" do
|
316
|
+
before(:each) do
|
317
|
+
@garage.top_speed = 3000
|
318
|
+
end
|
319
|
+
it "should use the fallback config" do
|
320
|
+
@car.top_speed.should == 3000
|
321
|
+
end
|
322
|
+
it "shouldn't affect the fallback config object" do
|
323
|
+
@garage.top_speed.should == 3000
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
describe "falling back multiple levels" do
|
328
|
+
before(:each) do
|
329
|
+
@klass = Class.new
|
330
|
+
@klass.class_eval do
|
331
|
+
include Dragonfly::Configurable
|
332
|
+
configurable_attr :veg, 'carrot'
|
333
|
+
end
|
334
|
+
@a = @klass.new
|
335
|
+
@b = @klass.new
|
336
|
+
@b.use_as_fallback_config(@a)
|
337
|
+
@c = @klass.new
|
338
|
+
@c.use_as_fallback_config(@b)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should be the default if nothing set" do
|
342
|
+
@c.veg.should == 'carrot'
|
343
|
+
end
|
344
|
+
|
345
|
+
it "should fall all the way back to the top one if necessary" do
|
346
|
+
@a.veg = 'turnip'
|
347
|
+
@c.veg.should == 'turnip'
|
348
|
+
end
|
349
|
+
|
350
|
+
it "should prefer the closer one over the further away one" do
|
351
|
+
@b.veg = 'tatty'
|
352
|
+
@a.veg = 'turnip'
|
353
|
+
@c.veg.should == 'tatty'
|
354
|
+
end
|
355
|
+
|
356
|
+
it "should work properly with nils" do
|
357
|
+
@a.veg = nil
|
358
|
+
@c.veg = 'broc'
|
359
|
+
@a.veg.should be_nil
|
360
|
+
@b.veg.should be_nil
|
361
|
+
@c.veg.should == 'broc'
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
describe "objects with different methods" do
|
366
|
+
before(:each) do
|
367
|
+
@dad = Object.new
|
368
|
+
class << @dad
|
369
|
+
include Dragonfly::Configurable
|
370
|
+
end
|
371
|
+
@kid = Object.new
|
372
|
+
class << @kid
|
373
|
+
include Dragonfly::Configurable
|
374
|
+
configurable_attr :lug, 'default-lug'
|
375
|
+
end
|
376
|
+
@kid.use_as_fallback_config(@dad)
|
377
|
+
end
|
378
|
+
|
379
|
+
it "should not allow setting on the fallback obj directly" do
|
380
|
+
lambda{
|
381
|
+
@dad.lug = 'leg'
|
382
|
+
}.should raise_error(NoMethodError)
|
383
|
+
end
|
384
|
+
|
385
|
+
it "should not have the fallback obj respond to the method" do
|
386
|
+
@dad.should_not respond_to(:lug=)
|
387
|
+
end
|
388
|
+
|
389
|
+
it "should allow configuring through the fallback object even if it doesn't have that method" do
|
390
|
+
@dad.configure do |c|
|
391
|
+
c.lug = 'leg'
|
392
|
+
end
|
393
|
+
@kid.lug.should == 'leg'
|
394
|
+
end
|
395
|
+
|
396
|
+
it "should work when a grandchild config is added later" do
|
397
|
+
grandkid = Object.new
|
398
|
+
class << grandkid
|
399
|
+
include Dragonfly::Configurable
|
400
|
+
configurable_attr :oogie, 'boogie'
|
401
|
+
end
|
402
|
+
grandkid.use_as_fallback_config(@kid)
|
403
|
+
@dad.configure{|c| c.oogie = 'duggen' }
|
404
|
+
grandkid.oogie.should == 'duggen'
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should allow configuring twice through the fallback object" do
|
408
|
+
@dad.configure{|c| c.lug = 'leg' }
|
409
|
+
@dad.configure{|c| c.lug = 'blug' }
|
410
|
+
@kid.lug.should == 'blug'
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
describe "clashing with configurable modules" do
|
415
|
+
before(:each) do
|
416
|
+
@mod = mod = Module.new
|
417
|
+
@mod.module_eval do
|
418
|
+
include Dragonfly::Configurable
|
419
|
+
configurable_attr :team, 'spurs'
|
420
|
+
end
|
421
|
+
@class = Class.new
|
422
|
+
@class.class_eval do
|
423
|
+
include mod
|
424
|
+
include Dragonfly::Configurable
|
425
|
+
configurable_attr :tree, 'elm'
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
it "should not override the defaults from the module" do
|
430
|
+
obj = @class.new
|
431
|
+
obj.team.should == 'spurs'
|
432
|
+
end
|
433
|
+
|
434
|
+
it "should still use its own defaults" do
|
435
|
+
obj = @class.new
|
436
|
+
obj.tree.should == 'elm'
|
437
|
+
end
|
438
|
+
|
439
|
+
describe "when the configurable_attr is specified in a subclass that doesn't include Configurable" do
|
440
|
+
before(:each) do
|
441
|
+
@subclass = Class.new(@class)
|
442
|
+
@subclass.class_eval do
|
443
|
+
configurable_attr :car, 'mazda'
|
444
|
+
configurable_attr :tree, 'oak'
|
445
|
+
end
|
446
|
+
@obj = @subclass.new
|
447
|
+
end
|
448
|
+
|
449
|
+
it "should still work with default values" do
|
450
|
+
@obj.car.should == 'mazda'
|
451
|
+
end
|
452
|
+
|
453
|
+
it "should override the default from the parent" do
|
454
|
+
@obj.tree.should == 'oak'
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
461
|
+
end
|