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,23 @@
|
|
1
|
+
module Dragonfly
|
2
|
+
class SimpleCache < Hash
|
3
|
+
|
4
|
+
def initialize(max_size)
|
5
|
+
@max_size = max_size
|
6
|
+
@keys = []
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :max_size
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
if !has_key?(key)
|
13
|
+
@keys << key
|
14
|
+
if size == max_size
|
15
|
+
key_to_purge = @keys.shift
|
16
|
+
delete(key_to_purge)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Dragonfly
|
6
|
+
|
7
|
+
# A TempObject is used for HOLDING DATA.
|
8
|
+
# It's the thing that is passed between the datastore, the processor and the encoder, and is useful
|
9
|
+
# for separating how the data was created and how it is later accessed.
|
10
|
+
#
|
11
|
+
# You can initialize it various ways:
|
12
|
+
#
|
13
|
+
# temp_object = Dragonfly::TempObject.new('this is the content') # with a String
|
14
|
+
# temp_object = Dragonfly::TempObject.new(Pathname.new('path/to/content')) # with a Pathname
|
15
|
+
# temp_object = Dragonfly::TempObject.new(File.new('path/to/content')) # with a File
|
16
|
+
# temp_object = Dragonfly::TempObject.new(some_tempfile) # with a Tempfile
|
17
|
+
# temp_object = Dragonfly::TempObject.new(some_other_temp_object) # with another TempObject
|
18
|
+
#
|
19
|
+
# However, no matter how it was initialized, you can always access the data a number of ways:
|
20
|
+
#
|
21
|
+
# temp_object.data # returns a data string
|
22
|
+
# temp_object.file # returns a file object holding the data
|
23
|
+
# temp_object.path # returns a path for the file
|
24
|
+
#
|
25
|
+
# The data/file are created lazily, something which you may wish to take advantage of.
|
26
|
+
#
|
27
|
+
# For example, if a TempObject is initialized with a file, and temp_object.data is never called, then
|
28
|
+
# the data string will never be loaded into memory.
|
29
|
+
#
|
30
|
+
# Conversely, if the TempObject is initialized with a data string, and neither temp_object.file nor temp_object.path
|
31
|
+
# are ever called, then the filesystem will never be hit.
|
32
|
+
#
|
33
|
+
class TempObject
|
34
|
+
|
35
|
+
# Class configuration
|
36
|
+
class << self
|
37
|
+
|
38
|
+
include Configurable
|
39
|
+
configurable_attr :block_size, 8192
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# Instance Methods
|
44
|
+
|
45
|
+
def initialize(obj)
|
46
|
+
if obj.is_a? TempObject
|
47
|
+
@data = obj.get_data
|
48
|
+
@tempfile = obj.get_tempfile
|
49
|
+
@pathname = obj.get_pathname
|
50
|
+
elsif obj.is_a? String
|
51
|
+
@data = obj
|
52
|
+
elsif obj.is_a? Tempfile
|
53
|
+
@tempfile = obj
|
54
|
+
elsif obj.is_a? File
|
55
|
+
@pathname = Pathname.new(obj.path)
|
56
|
+
@original_filename = @pathname.basename.to_s
|
57
|
+
elsif obj.is_a? Pathname
|
58
|
+
@pathname = obj
|
59
|
+
@original_filename = @pathname.basename.to_s
|
60
|
+
elsif obj.respond_to?(:tempfile)
|
61
|
+
@tempfile = obj.tempfile
|
62
|
+
else
|
63
|
+
raise ArgumentError, "#{self.class.name} must be initialized with a String, a Pathname, a File, a Tempfile, another TempObject, or something that responds to .tempfile"
|
64
|
+
end
|
65
|
+
@tempfile.close if @tempfile
|
66
|
+
@original_filename = obj.original_filename if obj.respond_to?(:original_filename)
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :original_filename
|
70
|
+
|
71
|
+
def data
|
72
|
+
@data ||= file{|f| f.read }
|
73
|
+
end
|
74
|
+
|
75
|
+
def tempfile
|
76
|
+
@tempfile ||= begin
|
77
|
+
case
|
78
|
+
when @data
|
79
|
+
@tempfile = new_tempfile(@data)
|
80
|
+
when @pathname
|
81
|
+
@tempfile = copy_to_tempfile(@pathname.expand_path)
|
82
|
+
end
|
83
|
+
@tempfile
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def file(&block)
|
88
|
+
f = tempfile.open
|
89
|
+
tempfile.binmode
|
90
|
+
if block_given?
|
91
|
+
ret = yield f
|
92
|
+
tempfile.close
|
93
|
+
else
|
94
|
+
ret = f
|
95
|
+
end
|
96
|
+
ret
|
97
|
+
end
|
98
|
+
|
99
|
+
def path
|
100
|
+
@pathname ? @pathname.expand_path.to_s : tempfile.path
|
101
|
+
end
|
102
|
+
|
103
|
+
def size
|
104
|
+
@data ? @data.bytesize : File.size(path)
|
105
|
+
end
|
106
|
+
|
107
|
+
def each(&block)
|
108
|
+
to_io do |io|
|
109
|
+
while part = io.read(block_size)
|
110
|
+
yield part
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_file(path)
|
116
|
+
if @data
|
117
|
+
File.open(path, 'wb'){|f| f.write(@data) }
|
118
|
+
else
|
119
|
+
FileUtils.cp(self.path, path)
|
120
|
+
end
|
121
|
+
File.new(path, 'rb')
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_io(&block)
|
125
|
+
@data ? StringIO.open(@data, 'rb', &block) : file(&block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def inspect
|
129
|
+
content_string = case
|
130
|
+
when @data
|
131
|
+
data_string = size > 20 ? "#{@data[0..20]}..." : @data
|
132
|
+
"data=#{data_string.inspect}"
|
133
|
+
when @pathname then "pathname=#{@pathname.inspect}"
|
134
|
+
when @tempfile then "tempfile=#{@tempfile.inspect}"
|
135
|
+
end
|
136
|
+
to_s.sub(/>$/, " #{content_string} >")
|
137
|
+
end
|
138
|
+
|
139
|
+
protected
|
140
|
+
|
141
|
+
# We don't use normal accessors here because #data etc. do more than just return the instance var
|
142
|
+
def get_data
|
143
|
+
@data
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_pathname
|
147
|
+
@pathname
|
148
|
+
end
|
149
|
+
|
150
|
+
def get_tempfile
|
151
|
+
@tempfile
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def block_size
|
157
|
+
self.class.block_size
|
158
|
+
end
|
159
|
+
|
160
|
+
def copy_to_tempfile(path)
|
161
|
+
tempfile = new_tempfile
|
162
|
+
FileUtils.cp path, tempfile.path
|
163
|
+
tempfile
|
164
|
+
end
|
165
|
+
|
166
|
+
def new_tempfile(content=nil)
|
167
|
+
tempfile = Tempfile.new('dragonfly')
|
168
|
+
tempfile.binmode
|
169
|
+
tempfile.write(content) if content
|
170
|
+
tempfile.close
|
171
|
+
tempfile
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Dragonfly
|
2
|
+
class UrlMapper
|
3
|
+
|
4
|
+
# Exceptions
|
5
|
+
class BadUrlFormat < StandardError; end
|
6
|
+
|
7
|
+
class Segment < Struct.new(:param, :seperator, :pattern)
|
8
|
+
|
9
|
+
def regexp_string
|
10
|
+
@regexp_string ||= "(#{Regexp.escape(seperator)}#{pattern}+?)?"
|
11
|
+
end
|
12
|
+
#
|
13
|
+
# def regexp
|
14
|
+
# @regexp ||= Regexp.new(regexp_string)
|
15
|
+
# end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(url_format, patterns={})
|
20
|
+
@url_format = url_format
|
21
|
+
raise BadUrlFormat, "bad url format #{url_format}" if url_format[/[\w_]:[\w_]/]
|
22
|
+
init_segments(patterns)
|
23
|
+
init_url_regexp
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :url_format, :url_regexp, :segments
|
27
|
+
|
28
|
+
def params_for(path, query=nil)
|
29
|
+
if path and md = path.match(url_regexp)
|
30
|
+
params = Rack::Utils.parse_query(query)
|
31
|
+
params_in_url.each_with_index do |var, i|
|
32
|
+
value = md[i+1][1..-1] if md[i+1]
|
33
|
+
params[var] = value
|
34
|
+
end
|
35
|
+
params
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def params_in_url
|
40
|
+
@params_in_url ||= url_format.scan(/\:[\w_]+/).map{|f| f.tr(':','') }
|
41
|
+
end
|
42
|
+
|
43
|
+
def url_for(params)
|
44
|
+
params = params.dup
|
45
|
+
url = url_format.dup
|
46
|
+
segments.each do |seg|
|
47
|
+
value = params[seg.param]
|
48
|
+
value ? url.sub!(/:[\w_]+/, value) : url.sub!(/.:[\w_]+/, '')
|
49
|
+
params.delete(seg.param)
|
50
|
+
end
|
51
|
+
url << "?#{Rack::Utils.build_query(params)}" if params.any?
|
52
|
+
url
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def init_segments(patterns)
|
58
|
+
@segments = []
|
59
|
+
url_format.scan(/([^\w_]):([\w_]+)/).each do |seperator, param|
|
60
|
+
segments << Segment.new(
|
61
|
+
param,
|
62
|
+
seperator,
|
63
|
+
patterns[param.to_sym] || '[^\/\-\.]'
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def init_url_regexp
|
69
|
+
i = -1
|
70
|
+
regexp_string = url_format.gsub(/[^\w_]:[\w_]+/) do
|
71
|
+
i += 1
|
72
|
+
segments[i].regexp_string
|
73
|
+
end
|
74
|
+
@url_regexp = Regexp.new('^' + regexp_string + '$')
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/samples/beach.png
ADDED
Binary file
|
data/samples/egg.png
ADDED
Binary file
|
data/samples/round.gif
ADDED
Binary file
|
data/samples/sample.docx
ADDED
Binary file
|
data/samples/taj.jpg
ADDED
Binary file
|
@@ -0,0 +1,1426 @@
|
|
1
|
+
require 'dragonfly/active_model_extensions/spec_helper'
|
2
|
+
|
3
|
+
describe Item do
|
4
|
+
|
5
|
+
def set_up_item_class(app=test_app)
|
6
|
+
app.define_macro(MyModel, :image_accessor)
|
7
|
+
Item.class_eval do
|
8
|
+
image_accessor :preview_image
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# See extra setup in models / initializer files
|
13
|
+
|
14
|
+
describe "defining accessors" do
|
15
|
+
|
16
|
+
let(:app1){ Dragonfly[:img] }
|
17
|
+
let(:app2){ Dragonfly[:vid] }
|
18
|
+
|
19
|
+
describe "attachment classes" do
|
20
|
+
before(:each) do
|
21
|
+
app1.define_macro(MyModel, :image_accessor)
|
22
|
+
app2.define_macro(MyModel, :video_accessor)
|
23
|
+
Item.class_eval do
|
24
|
+
image_accessor :preview_image
|
25
|
+
video_accessor :trailer_video
|
26
|
+
end
|
27
|
+
@classes = Item.dragonfly_attachment_classes
|
28
|
+
@class1, @class2 = @classes
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return the attachment classes" do
|
32
|
+
@class1.superclass.should == Dragonfly::ActiveModelExtensions::Attachment
|
33
|
+
@class2.superclass.should == Dragonfly::ActiveModelExtensions::Attachment
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should associate the correct app with each class" do
|
37
|
+
@class1.app.should == app1
|
38
|
+
@class2.app.should == app2
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should associate the correct attribute with each class" do
|
42
|
+
@class1.attribute.should == :preview_image
|
43
|
+
@class2.attribute.should == :trailer_video
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should associate the correct model class with each class" do
|
47
|
+
@class1.model_class.should == Item
|
48
|
+
@class2.model_class.should == Item
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should work for included modules (e.g. Mongoid::Document)" do
|
53
|
+
mongoid_document = Module.new
|
54
|
+
app1.define_macro_on_include(mongoid_document, :dog_accessor)
|
55
|
+
model_class = Class.new do
|
56
|
+
def self.before_save(*args); end
|
57
|
+
def self.before_destroy(*args); end
|
58
|
+
include mongoid_document
|
59
|
+
dog_accessor :doogie
|
60
|
+
end
|
61
|
+
klass = model_class.dragonfly_attachment_classes.first
|
62
|
+
klass.app.should == app1
|
63
|
+
klass.attribute.should == :doogie
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "correctly defined" do
|
69
|
+
|
70
|
+
before(:each) do
|
71
|
+
@app = test_app
|
72
|
+
@app.define_macro(MyModel, :image_accessor)
|
73
|
+
Item.class_eval do
|
74
|
+
image_accessor :preview_image
|
75
|
+
end
|
76
|
+
@item = Item.new
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should provide a reader" do
|
80
|
+
@item.should respond_to(:preview_image)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should provide a writer" do
|
84
|
+
@item.should respond_to(:preview_image=)
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "when there has been nothing assigned" do
|
88
|
+
it "the reader should return nil" do
|
89
|
+
@item.preview_image.should be_nil
|
90
|
+
end
|
91
|
+
it "the uid should be nil" do
|
92
|
+
@item.preview_image_uid.should be_nil
|
93
|
+
end
|
94
|
+
it "should not try to store anything on save" do
|
95
|
+
@app.datastore.should_not_receive(:store)
|
96
|
+
@item.save!
|
97
|
+
end
|
98
|
+
it "should not try to destroy anything on save" do
|
99
|
+
@app.datastore.should_not_receive(:destroy)
|
100
|
+
@item.save!
|
101
|
+
end
|
102
|
+
it "should not try to destroy anything on destroy" do
|
103
|
+
@app.datastore.should_not_receive(:destroy)
|
104
|
+
@item.destroy
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "when the uid is set manually" do
|
109
|
+
before(:each) do
|
110
|
+
@item.preview_image_uid = 'some_known_uid'
|
111
|
+
end
|
112
|
+
it "should not try to retrieve any data" do
|
113
|
+
@app.datastore.should_not_receive(:retrieve)
|
114
|
+
@item.save!
|
115
|
+
end
|
116
|
+
it "should not try to destroy any data" do
|
117
|
+
@app.datastore.should_not_receive(:destroy)
|
118
|
+
@item.save!
|
119
|
+
end
|
120
|
+
it "should not try to store any data" do
|
121
|
+
@app.datastore.should_not_receive(:store)
|
122
|
+
@item.save!
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "when there has been some thing assigned but not saved" do
|
127
|
+
before(:each) do
|
128
|
+
@item.preview_image = "DATASTRING"
|
129
|
+
end
|
130
|
+
it "the reader should return an attachment" do
|
131
|
+
@item.preview_image.should be_a(Dragonfly::ActiveModelExtensions::Attachment)
|
132
|
+
end
|
133
|
+
it "the uid should be nil" do
|
134
|
+
@item.preview_image_uid.should be_nil
|
135
|
+
end
|
136
|
+
it "should store the image when saved" do
|
137
|
+
@app.datastore.should_receive(:store).with(a_temp_object_with_data("DATASTRING"), hash_including)
|
138
|
+
@item.save!
|
139
|
+
end
|
140
|
+
it "should not try to destroy anything on destroy" do
|
141
|
+
@app.datastore.should_not_receive(:destroy)
|
142
|
+
@item.destroy
|
143
|
+
end
|
144
|
+
it "should return nil for the url" do
|
145
|
+
@item.preview_image.url.should be_nil
|
146
|
+
end
|
147
|
+
describe "when the uid is set manually" do
|
148
|
+
before(:each) do
|
149
|
+
@item.preview_image_uid = 'some_known_uid'
|
150
|
+
end
|
151
|
+
it "should not try to retrieve any data" do
|
152
|
+
@app.datastore.should_not_receive(:retrieve)
|
153
|
+
@item.save!
|
154
|
+
end
|
155
|
+
it "should not try to destroy any data" do
|
156
|
+
@app.datastore.should_not_receive(:destroy)
|
157
|
+
@item.save!
|
158
|
+
end
|
159
|
+
it "should not try to store any data" do
|
160
|
+
@app.datastore.should_not_receive(:store)
|
161
|
+
@item.save!
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "when something has been assigned and saved" do
|
168
|
+
|
169
|
+
before(:each) do
|
170
|
+
@item.preview_image = "DATASTRING"
|
171
|
+
@app.datastore.should_receive(:store).with(a_temp_object_with_data("DATASTRING"), hash_including).once.and_return('some_uid')
|
172
|
+
@item.save!
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should have the correct uid" do
|
176
|
+
@item.preview_image_uid.should == 'some_uid'
|
177
|
+
end
|
178
|
+
it "should not try to store anything if saved again" do
|
179
|
+
@app.datastore.should_not_receive(:store)
|
180
|
+
@item.save!
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should not try to destroy anything if saved again" do
|
184
|
+
@app.datastore.should_not_receive(:destroy)
|
185
|
+
@item.save!
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should destroy the data on destroy" do
|
189
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
190
|
+
@item.destroy
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should return the url for the data" do
|
194
|
+
@item.preview_image.url.should =~ %r<^/\w+$>
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should destroy the old data when the uid is set manually" do
|
198
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
199
|
+
@item.preview_image_uid = 'some_known_uid'
|
200
|
+
@item.save!
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "when accessed by a new model object" do
|
204
|
+
before(:each) do
|
205
|
+
@item = Item.find(@item.id)
|
206
|
+
end
|
207
|
+
it "should destroy the data on destroy" do
|
208
|
+
@app.datastore.should_receive(:destroy).with(@item.preview_image_uid)
|
209
|
+
@item.destroy
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "when something new is assigned" do
|
214
|
+
before(:each) do
|
215
|
+
@item.preview_image = "ANEWDATASTRING"
|
216
|
+
@app.datastore.stub!(:store).and_return('some_uid')
|
217
|
+
end
|
218
|
+
it "should set the uid to nil" do
|
219
|
+
@item.preview_image_uid.should be_nil
|
220
|
+
end
|
221
|
+
it "should destroy the old data when saved" do
|
222
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
223
|
+
@item.save!
|
224
|
+
end
|
225
|
+
it "should not try to destroy the old data if saved again" do
|
226
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
227
|
+
@item.save!
|
228
|
+
@app.datastore.should_not_receive(:destroy).with('some_uid')
|
229
|
+
@item.save!
|
230
|
+
end
|
231
|
+
it "should destroy the old data when saved, even if yet another thing is assigned" do
|
232
|
+
@item.preview_image = "YET ANOTHER DATA STRING"
|
233
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
234
|
+
@item.save!
|
235
|
+
end
|
236
|
+
it "should store the new data when saved" do
|
237
|
+
@app.datastore.should_receive(:store).with(a_temp_object_with_data("ANEWDATASTRING"), hash_including)
|
238
|
+
@item.save!
|
239
|
+
end
|
240
|
+
it "should destroy the old data on destroy" do
|
241
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
242
|
+
@item.destroy
|
243
|
+
end
|
244
|
+
it "should destroy the old data on destroy, even if yet another thing is assigned" do
|
245
|
+
@item.preview_image = "YET ANOTHER DATA STRING"
|
246
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
247
|
+
@item.destroy
|
248
|
+
end
|
249
|
+
it "should destroy the old data when the uid has been set manually" do
|
250
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
251
|
+
@item.preview_image_uid = 'some_known_uid'
|
252
|
+
@item.save!
|
253
|
+
end
|
254
|
+
it "should return the new size" do
|
255
|
+
@item.preview_image.size.should == 14
|
256
|
+
end
|
257
|
+
it "should return the new data" do
|
258
|
+
@item.preview_image.data.should == 'ANEWDATASTRING'
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe "when it is set to nil" do
|
263
|
+
before(:each) do
|
264
|
+
@item.preview_image = nil
|
265
|
+
end
|
266
|
+
it "should set the uid to nil" do
|
267
|
+
@item.preview_image_uid.should be_nil
|
268
|
+
end
|
269
|
+
it "should return the attribute as nil" do
|
270
|
+
@item.preview_image.should be_nil
|
271
|
+
end
|
272
|
+
it "should destroy the data on save" do
|
273
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
274
|
+
@item.save!
|
275
|
+
@item.preview_image.should be_nil
|
276
|
+
end
|
277
|
+
it "should destroy the old data on destroy" do
|
278
|
+
@app.datastore.should_receive(:destroy).with('some_uid')
|
279
|
+
@item.destroy
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
describe "when the data can't be found" do
|
284
|
+
it "should log a warning if the data wasn't found on destroy" do
|
285
|
+
@app.datastore.should_receive(:destroy).with('some_uid').and_raise(Dragonfly::DataStorage::DataNotFound)
|
286
|
+
@app.log.should_receive(:warn)
|
287
|
+
@item.destroy
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
describe "other types of assignment" do
|
294
|
+
before(:each) do
|
295
|
+
@app.generator.add :egg do
|
296
|
+
"Gungedin"
|
297
|
+
end
|
298
|
+
@app.processor.add :doogie do |temp_object|
|
299
|
+
temp_object.data.upcase
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
describe "assigning with a job" do
|
304
|
+
before(:each) do
|
305
|
+
@job = @app.generate(:egg)
|
306
|
+
@item.preview_image = @job
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should work" do
|
310
|
+
@item.preview_image.data.should == 'Gungedin'
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should not be affected by subsequent changes to the job" do
|
314
|
+
@job.process!(:doogie)
|
315
|
+
@item.preview_image.data.should == 'Gungedin'
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
describe "assigning with another attachment" do
|
320
|
+
before(:each) do
|
321
|
+
Item.class_eval do
|
322
|
+
image_accessor :other_image
|
323
|
+
end
|
324
|
+
end
|
325
|
+
it "should work like assigning the job" do
|
326
|
+
@item.preview_image = 'eggheads'
|
327
|
+
@item.other_image = @item.preview_image
|
328
|
+
@item.preview_image = 'dogchin'
|
329
|
+
@item.other_image.data.should == 'eggheads'
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
describe "assigning by means of a bang method" do
|
334
|
+
before(:each) do
|
335
|
+
@app.processor.add :double do |temp_object|
|
336
|
+
temp_object.data * 2
|
337
|
+
end
|
338
|
+
@app.encoder.add do |temp_object, format|
|
339
|
+
temp_object.data.downcase + format.to_s
|
340
|
+
end
|
341
|
+
@item.preview_image = "HELLO"
|
342
|
+
end
|
343
|
+
it "should modify as if being assigned again" do
|
344
|
+
@item.preview_image.process!(:double)
|
345
|
+
@item.preview_image.data.should == 'HELLOHELLO'
|
346
|
+
end
|
347
|
+
it "should update the magic attributes" do
|
348
|
+
@item.preview_image.process!(:double)
|
349
|
+
@item.preview_image_size.should == 10
|
350
|
+
end
|
351
|
+
it "should work for encode" do
|
352
|
+
@item.preview_image.encode!(:egg)
|
353
|
+
@item.preview_image.data.should == 'helloegg'
|
354
|
+
end
|
355
|
+
it "should work repeatedly" do
|
356
|
+
@item.preview_image.process!(:double).encode!(:egg)
|
357
|
+
@item.preview_image.data.should == 'hellohelloegg'
|
358
|
+
@item.preview_image_size.should == 13
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
describe "remote_url" do
|
364
|
+
it "should give the remote url if the uid is set" do
|
365
|
+
@item.preview_image_uid = 'some/uid'
|
366
|
+
@app.should_receive(:remote_url_for).with('some/uid', :some => 'param').and_return('http://egg.nog')
|
367
|
+
@item.preview_image.remote_url(:some => 'param').should == 'http://egg.nog'
|
368
|
+
end
|
369
|
+
it "should return nil if the content is not yet saved" do
|
370
|
+
@item.preview_image = "hello"
|
371
|
+
@item.preview_image.remote_url(:some => 'param').should be_nil
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
end
|
376
|
+
|
377
|
+
describe "validations" do
|
378
|
+
|
379
|
+
before(:all) do
|
380
|
+
@app = test_app
|
381
|
+
@app.define_macro(MyModel, :image_accessor)
|
382
|
+
end
|
383
|
+
|
384
|
+
describe "validates_presence_of" do
|
385
|
+
|
386
|
+
before(:all) do
|
387
|
+
Item.class_eval do
|
388
|
+
image_accessor :preview_image
|
389
|
+
validates_presence_of :preview_image
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
it "should be valid if set" do
|
394
|
+
Item.new(:preview_image => "1234567890").should be_valid
|
395
|
+
end
|
396
|
+
|
397
|
+
it "should be invalid if not set" do
|
398
|
+
Item.new.should_not be_valid
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
|
403
|
+
describe "validates_size_of" do
|
404
|
+
|
405
|
+
before(:all) do
|
406
|
+
Item.class_eval do
|
407
|
+
image_accessor :preview_image
|
408
|
+
validates_size_of :preview_image, :within => (6..10)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should be valid if ok" do
|
413
|
+
Item.new(:preview_image => "1234567890").should be_valid
|
414
|
+
end
|
415
|
+
|
416
|
+
it "should be invalid if too small" do
|
417
|
+
Item.new(:preview_image => "12345").should_not be_valid
|
418
|
+
end
|
419
|
+
|
420
|
+
end
|
421
|
+
|
422
|
+
describe "validates_property" do
|
423
|
+
|
424
|
+
before(:each) do
|
425
|
+
@item = Item.new(:preview_image => "1234567890")
|
426
|
+
end
|
427
|
+
|
428
|
+
before(:all) do
|
429
|
+
custom_analyser = Class.new do
|
430
|
+
def mime_type(temp_object)
|
431
|
+
case temp_object.data
|
432
|
+
when "WRONG TYPE" then 'wrong/type'
|
433
|
+
when "OTHER TYPE" then nil
|
434
|
+
else 'how/special'
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
def number_of_Gs(temp_object)
|
439
|
+
temp_object.data.count('G')
|
440
|
+
end
|
441
|
+
end
|
442
|
+
@app.analyser.register(custom_analyser)
|
443
|
+
|
444
|
+
Item.class_eval do
|
445
|
+
validates_property :mime_type, :of => :preview_image, :in => ['how/special', 'how/crazy'], :if => :its_friday
|
446
|
+
validates_property :mime_type, :of => [:other_image, :yet_another_image], :as => 'how/special'
|
447
|
+
validates_property :number_of_Gs, :of => :preview_image, :in => (0..2)
|
448
|
+
validates_property :mime_type, :of => :otra_imagen, :in => ['que/pasa', 'illo/tio'], :message => "tipo de contenido incorrecto. Que chungo tio"
|
449
|
+
|
450
|
+
image_accessor :preview_image
|
451
|
+
image_accessor :other_image
|
452
|
+
image_accessor :yet_another_image
|
453
|
+
image_accessor :otra_imagen
|
454
|
+
|
455
|
+
def its_friday
|
456
|
+
true
|
457
|
+
end
|
458
|
+
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
it "should be valid if nil, if not validated on presence (even with validates_property)" do
|
463
|
+
@item.other_image = nil
|
464
|
+
@item.should be_valid
|
465
|
+
end
|
466
|
+
|
467
|
+
it "should be invalid if the property is nil" do
|
468
|
+
@item.preview_image = "OTHER TYPE"
|
469
|
+
@item.should_not be_valid
|
470
|
+
@item.errors[:preview_image].should == ["mime type is incorrect. It needs to be one of 'how/special', 'how/crazy', but was 'application/octet-stream'"]
|
471
|
+
end
|
472
|
+
|
473
|
+
it "should be invalid if the property is wrong" do
|
474
|
+
@item.preview_image = "WRONG TYPE"
|
475
|
+
@item.should_not be_valid
|
476
|
+
@item.errors[:preview_image].should == ["mime type is incorrect. It needs to be one of 'how/special', 'how/crazy', but was 'wrong/type'"]
|
477
|
+
end
|
478
|
+
|
479
|
+
it "should work for a range" do
|
480
|
+
@item.preview_image = "GOOGLE GUM"
|
481
|
+
@item.should_not be_valid
|
482
|
+
@item.errors[:preview_image].should == ["number of gs is incorrect. It needs to be between 0 and 2, but was '3'"]
|
483
|
+
end
|
484
|
+
|
485
|
+
it "should validate individually" do
|
486
|
+
@item.other_image = "1234567"
|
487
|
+
@item.yet_another_image = "WRONG TYPE"
|
488
|
+
@item.should_not be_valid
|
489
|
+
@item.errors[:other_image].should == []
|
490
|
+
@item.errors[:yet_another_image].should == ["mime type is incorrect. It needs to be 'how/special', but was 'wrong/type'"]
|
491
|
+
end
|
492
|
+
|
493
|
+
it "should include standard extra options like 'if' on mime type validation" do
|
494
|
+
@item.should_receive(:its_friday).and_return(false)
|
495
|
+
@item.preview_image = "WRONG TYPE"
|
496
|
+
@item.should be_valid
|
497
|
+
end
|
498
|
+
|
499
|
+
it "should require either :as or :in as an argument" do
|
500
|
+
lambda{
|
501
|
+
Item.class_eval do
|
502
|
+
validates_property :mime_type, :of => :preview_image
|
503
|
+
end
|
504
|
+
}.should raise_error(ArgumentError)
|
505
|
+
end
|
506
|
+
|
507
|
+
it "should require :of as an argument" do
|
508
|
+
lambda{
|
509
|
+
Item.class_eval do
|
510
|
+
validates_property :mime_type, :as => 'hi/there'
|
511
|
+
end
|
512
|
+
}.should raise_error(ArgumentError)
|
513
|
+
end
|
514
|
+
|
515
|
+
it "should allow for custom messages" do
|
516
|
+
@item.otra_imagen = "WRONG TYPE"
|
517
|
+
@item.should_not be_valid
|
518
|
+
@item.errors[:otra_imagen].should == ["tipo de contenido incorrecto. Que chungo tio"]
|
519
|
+
end
|
520
|
+
|
521
|
+
it "should allow for custom messages including access to the property name and expected/allowed values" do
|
522
|
+
@item.should_receive(:its_friday).and_return(false) # hack to get rid of other validation
|
523
|
+
Item.class_eval do
|
524
|
+
validates_property :mime_type, :of => :preview_image, :as => 'one/thing',
|
525
|
+
:message => proc{|actual, model| "Unlucky #{model.title}! Was #{actual}" }
|
526
|
+
end
|
527
|
+
@item.title = 'scubby'
|
528
|
+
@item.preview_image = "WRONG TYPE"
|
529
|
+
@item.should_not be_valid
|
530
|
+
@item.errors[:preview_image].should == ["Unlucky scubby! Was wrong/type"]
|
531
|
+
end
|
532
|
+
|
533
|
+
end
|
534
|
+
|
535
|
+
end
|
536
|
+
|
537
|
+
describe "extra properties" do
|
538
|
+
|
539
|
+
before(:each) do
|
540
|
+
@app = test_app
|
541
|
+
custom_analyser = Class.new do
|
542
|
+
def some_analyser_method(temp_object)
|
543
|
+
"abc" + temp_object.data[0..0]
|
544
|
+
end
|
545
|
+
def number_of_As(temp_object); temp_object.data.count('A'); end
|
546
|
+
end
|
547
|
+
@app.analyser.register(custom_analyser)
|
548
|
+
@app.define_macro(MyModel, :image_accessor)
|
549
|
+
Item.class_eval do
|
550
|
+
image_accessor :preview_image
|
551
|
+
end
|
552
|
+
@item = Item.new
|
553
|
+
end
|
554
|
+
|
555
|
+
describe "magic attributes" do
|
556
|
+
|
557
|
+
it "should default the magic attribute as nil" do
|
558
|
+
@item.preview_image_some_analyser_method.should be_nil
|
559
|
+
end
|
560
|
+
|
561
|
+
it "should set the magic attribute when assigned" do
|
562
|
+
@item.preview_image = '123'
|
563
|
+
@item.preview_image_some_analyser_method.should == 'abc1'
|
564
|
+
end
|
565
|
+
|
566
|
+
it "should not set non-magic attributes with the same prefix when assigned" do
|
567
|
+
@item.preview_image_blah_blah = 'wassup'
|
568
|
+
@item.preview_image = '123'
|
569
|
+
@item.preview_image_blah_blah.should == 'wassup'
|
570
|
+
end
|
571
|
+
|
572
|
+
it "should update the magic attribute when something else is assigned" do
|
573
|
+
@item.preview_image = '123'
|
574
|
+
@item.preview_image = '456'
|
575
|
+
@item.preview_image_some_analyser_method.should == 'abc4'
|
576
|
+
end
|
577
|
+
|
578
|
+
it "should not reset non-magic attributes with the same prefix when set to nil" do
|
579
|
+
@item.preview_image_blah_blah = 'wassup'
|
580
|
+
@item.preview_image = '123'
|
581
|
+
@item.preview_image = nil
|
582
|
+
@item.preview_image_blah_blah.should == 'wassup'
|
583
|
+
end
|
584
|
+
|
585
|
+
it "should work for size too" do
|
586
|
+
@item.preview_image = '123'
|
587
|
+
@item.preview_image_size.should == 3
|
588
|
+
end
|
589
|
+
|
590
|
+
it "should store the original file name if it exists" do
|
591
|
+
data = 'jasdlkf sadjl'
|
592
|
+
data.stub!(:original_filename).and_return('hello.png')
|
593
|
+
@item.preview_image = data
|
594
|
+
@item.preview_image_name.should == 'hello.png'
|
595
|
+
end
|
596
|
+
|
597
|
+
end
|
598
|
+
|
599
|
+
describe "meta from magic attributes" do
|
600
|
+
|
601
|
+
it "should set the meta for the magic attribute when assigned" do
|
602
|
+
@item.preview_image = '123'
|
603
|
+
@item.preview_image.meta[:some_analyser_method].should == 'abc1'
|
604
|
+
end
|
605
|
+
|
606
|
+
it "should not set meta for non-magic attributes with the same prefix when assigned" do
|
607
|
+
@item.preview_image = '123'
|
608
|
+
@item.preview_image.meta[:blah_blah].should be_nil
|
609
|
+
end
|
610
|
+
|
611
|
+
it "should update the meta for the magic attribute when something else is assigned" do
|
612
|
+
@item.preview_image = '123'
|
613
|
+
@item.preview_image = '456'
|
614
|
+
@item.preview_image.meta[:some_analyser_method].should == 'abc4'
|
615
|
+
end
|
616
|
+
|
617
|
+
it "should include the meta for size too" do
|
618
|
+
@item.preview_image = '123'
|
619
|
+
@item.preview_image.meta[:size].should == 3
|
620
|
+
end
|
621
|
+
|
622
|
+
it "should store the meta for the original file name if it exists" do
|
623
|
+
data = 'jasdlkf sadjl'
|
624
|
+
data.stub!(:original_filename).and_return('hello.png')
|
625
|
+
@item.preview_image = data
|
626
|
+
@item.preview_image.meta[:name].should == 'hello.png'
|
627
|
+
end
|
628
|
+
|
629
|
+
it "should still have the meta after reload" do
|
630
|
+
@item.preview_image = '123'
|
631
|
+
@item.save!
|
632
|
+
item = Item.find(@item.id)
|
633
|
+
item.preview_image.meta[:some_analyser_method].should == 'abc1'
|
634
|
+
end
|
635
|
+
|
636
|
+
end
|
637
|
+
|
638
|
+
describe "delegating methods to the job" do
|
639
|
+
before(:each) do
|
640
|
+
@item.preview_image = "DATASTRING"
|
641
|
+
end
|
642
|
+
it "should have properties from the analyser" do
|
643
|
+
@item.preview_image.number_of_As.should == 2
|
644
|
+
end
|
645
|
+
it "should report that it responds to analyser methods" do
|
646
|
+
@item.preview_image.respond_to?(:number_of_As).should be_true
|
647
|
+
end
|
648
|
+
it "should include analyser methods in methods" do
|
649
|
+
@item.preview_image.methods.include?('number_of_As'.to_method_name).should be_true
|
650
|
+
end
|
651
|
+
it "should include analyser methods in public_methods" do
|
652
|
+
@item.preview_image.public_methods.include?('number_of_As'.to_method_name).should be_true
|
653
|
+
end
|
654
|
+
|
655
|
+
it "should update when something new is assigned" do
|
656
|
+
@item.preview_image = 'ANEWDATASTRING'
|
657
|
+
@item.preview_image.number_of_As.should == 3
|
658
|
+
end
|
659
|
+
|
660
|
+
describe "from a new model object" do
|
661
|
+
before(:each) do
|
662
|
+
@app.datastore.stub!(:store).and_return('my_uid')
|
663
|
+
item = Item.create!(:preview_image => 'DATASTRING')
|
664
|
+
@item = Item.find(item.id)
|
665
|
+
end
|
666
|
+
it "should load the content then delegate the method" do
|
667
|
+
@app.datastore.should_receive(:retrieve).with('my_uid').and_return(['DATASTRING', {}])
|
668
|
+
@item.preview_image.number_of_As.should == 2
|
669
|
+
end
|
670
|
+
it "should use the magic attribute if there is one, and not load the content" do
|
671
|
+
@app.datastore.should_not_receive(:retrieve)
|
672
|
+
@item.should_receive(:preview_image_some_analyser_method).at_least(:once).and_return('result yo')
|
673
|
+
@item.preview_image.some_analyser_method.should == 'result yo'
|
674
|
+
end
|
675
|
+
|
676
|
+
it "should use the magic attribute for size if there is one, and not the job object" do
|
677
|
+
@item.preview_image.send(:job).should_not_receive(:size)
|
678
|
+
@item.should_receive("preview_image_size").and_return(17)
|
679
|
+
@item.preview_image.size.should == 17
|
680
|
+
end
|
681
|
+
|
682
|
+
it "should delegate 'size' to the job object if there is no magic attribute for it" do
|
683
|
+
@item.other_image = 'blahdata'
|
684
|
+
@item.other_image.send(:job).should_receive(:size).and_return 54
|
685
|
+
@item.other_image.size.should == 54
|
686
|
+
end
|
687
|
+
|
688
|
+
end
|
689
|
+
|
690
|
+
it "should not raise an error if a non-existent method is called" do
|
691
|
+
# Just checking method missing works ok
|
692
|
+
lambda{
|
693
|
+
@item.preview_image.eggbert
|
694
|
+
}.should raise_error(NoMethodError)
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
describe "job shortcuts" do
|
699
|
+
before(:each) do
|
700
|
+
@app.job :bacon do
|
701
|
+
process :breakfast
|
702
|
+
end
|
703
|
+
@item = Item.new :preview_image => 'gurg'
|
704
|
+
end
|
705
|
+
it "should add job shortcuts for that app" do
|
706
|
+
job = @item.preview_image.bacon
|
707
|
+
job.steps.first.should be_a(Dragonfly::Job::Process)
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
describe "setting things on the attachment" do
|
712
|
+
|
713
|
+
before(:each) do
|
714
|
+
@item = Item.new
|
715
|
+
end
|
716
|
+
|
717
|
+
describe "name" do
|
718
|
+
before(:each) do
|
719
|
+
@item.preview_image = "Hello"
|
720
|
+
@item.preview_image.name = 'hello.there'
|
721
|
+
end
|
722
|
+
it "should allow for setting the name" do
|
723
|
+
@item.preview_image.name.should == 'hello.there'
|
724
|
+
end
|
725
|
+
it "should update the magic attribute" do
|
726
|
+
@item.preview_image_name.should == 'hello.there'
|
727
|
+
end
|
728
|
+
it "should return the name" do
|
729
|
+
(@item.preview_image.name = 'no.silly').should == 'no.silly'
|
730
|
+
end
|
731
|
+
it "should update the ext too" do
|
732
|
+
@item.preview_image.ext.should == 'there'
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
describe "meta" do
|
737
|
+
before(:each) do
|
738
|
+
@item.preview_image = "Hello all"
|
739
|
+
@item.preview_image.meta = {:slime => 'balls'}
|
740
|
+
end
|
741
|
+
it "should allow for setting the meta" do
|
742
|
+
@item.preview_image.meta.should == {:slime => 'balls'}
|
743
|
+
end
|
744
|
+
it "should allow for updating the meta" do
|
745
|
+
@item.preview_image.meta[:numb] = 'nuts'
|
746
|
+
@item.preview_image.meta.should == {:slime => 'balls', :numb => 'nuts'}
|
747
|
+
end
|
748
|
+
it "should return the meta" do
|
749
|
+
(@item.preview_image.meta = {:doogs => 'boogs'}).should == {:doogs => 'boogs'}
|
750
|
+
end
|
751
|
+
it "should save it correctly" do
|
752
|
+
@item.save!
|
753
|
+
item = Item.find(@item.id)
|
754
|
+
item.preview_image.apply.meta.should include_hash(:slime => 'balls')
|
755
|
+
end
|
756
|
+
it "should include meta info about the model" do
|
757
|
+
@item.save!
|
758
|
+
item = Item.find(@item.id)
|
759
|
+
item.preview_image.meta.should include_hash(:model_class => 'Item', :model_attachment => :preview_image)
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
end
|
764
|
+
|
765
|
+
end
|
766
|
+
|
767
|
+
describe "inheritance" do
|
768
|
+
|
769
|
+
before(:all) do
|
770
|
+
@app = test_app
|
771
|
+
@app2 = test_app
|
772
|
+
@app.define_macro(MyModel, :image_accessor)
|
773
|
+
@app2.define_macro(MyModel, :egg_accessor)
|
774
|
+
Car.class_eval do
|
775
|
+
image_accessor :image
|
776
|
+
end
|
777
|
+
Photo.class_eval do
|
778
|
+
egg_accessor :image
|
779
|
+
end
|
780
|
+
|
781
|
+
@base_class = Car
|
782
|
+
class ReliantRobin < Car; image_accessor :reliant_image; end
|
783
|
+
@subclass = ReliantRobin
|
784
|
+
class ReliantRobinWithModule < Car
|
785
|
+
include Module.new
|
786
|
+
image_accessor :reliant_image
|
787
|
+
end
|
788
|
+
@subclass_with_module = ReliantRobinWithModule
|
789
|
+
@unrelated_class = Photo
|
790
|
+
end
|
791
|
+
|
792
|
+
it "should allow assigning base class accessors" do
|
793
|
+
@base_class.create! :image => 'blah'
|
794
|
+
end
|
795
|
+
it "should not allow assigning subclass accessors in the base class" do
|
796
|
+
@base_class.new.should_not respond_to(:reliant_image=)
|
797
|
+
end
|
798
|
+
it "should allow assigning base class accessors in the subclass" do
|
799
|
+
@subclass.create! :image => 'blah'
|
800
|
+
end
|
801
|
+
it "should allow assigning subclass accessors in the subclass" do
|
802
|
+
@subclass.create! :reliant_image => 'blah'
|
803
|
+
end
|
804
|
+
it "should allow assigning base class accessors in the subclass, even if it has mixins" do
|
805
|
+
@subclass_with_module.create! :image => 'blah'
|
806
|
+
end
|
807
|
+
it "should allow assigning subclass accessors in the subclass, even if it has mixins" do
|
808
|
+
@subclass_with_module.create! :reliant_image => 'blah'
|
809
|
+
end
|
810
|
+
it "should return the correct attachment classes for the base class" do
|
811
|
+
@base_class.dragonfly_attachment_classes.should match_attachment_classes([[Car, :image, @app]])
|
812
|
+
end
|
813
|
+
it "should return the correct attachment classes for the subclass" do
|
814
|
+
@subclass.dragonfly_attachment_classes.should match_attachment_classes([[ReliantRobin, :image, @app], [ReliantRobin, :reliant_image, @app]])
|
815
|
+
end
|
816
|
+
it "should return the correct attachment classes for the subclass with module" do
|
817
|
+
@subclass_with_module.dragonfly_attachment_classes.should match_attachment_classes([[ReliantRobinWithModule, :image, @app], [ReliantRobinWithModule, :reliant_image, @app]])
|
818
|
+
end
|
819
|
+
it "should return the correct attachment classes for a class from a different hierarchy" do
|
820
|
+
@unrelated_class.dragonfly_attachment_classes.should match_attachment_classes([[Photo, :image, @app2]])
|
821
|
+
end
|
822
|
+
end
|
823
|
+
|
824
|
+
describe "setting the url" do
|
825
|
+
before(:each) do
|
826
|
+
set_up_item_class
|
827
|
+
@item = Item.new
|
828
|
+
stub_request(:get, "http://some.url/yo.png").to_return(:body => "aaaaayo")
|
829
|
+
end
|
830
|
+
|
831
|
+
it "should allow setting the url" do
|
832
|
+
@item.preview_image_url = 'http://some.url/yo.png'
|
833
|
+
@item.preview_image.data.should == 'aaaaayo'
|
834
|
+
end
|
835
|
+
it "should return nil always for the reader" do
|
836
|
+
@item.preview_image_url = 'http://some.url/yo.png'
|
837
|
+
@item.preview_image_url.should be_nil
|
838
|
+
end
|
839
|
+
it "should have set the name" do
|
840
|
+
@item.preview_image_url = 'http://some.url/yo.png'
|
841
|
+
@item.preview_image_name.should == 'yo.png'
|
842
|
+
@item.preview_image.meta[:name].should == 'yo.png'
|
843
|
+
end
|
844
|
+
[nil, ""].each do |value|
|
845
|
+
it "should do nothing if set with #{value.inspect}" do
|
846
|
+
@item.preview_image_url = value
|
847
|
+
@item.preview_image.should be_nil
|
848
|
+
end
|
849
|
+
end
|
850
|
+
end
|
851
|
+
|
852
|
+
describe "removing the accessor with e.g. a form" do
|
853
|
+
before(:each) do
|
854
|
+
set_up_item_class
|
855
|
+
@item = Item.new
|
856
|
+
@item.preview_image = "something"
|
857
|
+
end
|
858
|
+
|
859
|
+
[
|
860
|
+
1,
|
861
|
+
"1",
|
862
|
+
true,
|
863
|
+
"true",
|
864
|
+
"blahblah"
|
865
|
+
].each do |value|
|
866
|
+
it "should remove the accessor if passed #{value.inspect}" do
|
867
|
+
@item.remove_preview_image = value
|
868
|
+
@item.preview_image.should be_nil
|
869
|
+
end
|
870
|
+
|
871
|
+
it "should return true when called if set with #{value.inspect}" do
|
872
|
+
@item.remove_preview_image = value
|
873
|
+
@item.remove_preview_image.should be_true
|
874
|
+
end
|
875
|
+
end
|
876
|
+
|
877
|
+
[
|
878
|
+
0,
|
879
|
+
"0",
|
880
|
+
false,
|
881
|
+
"false",
|
882
|
+
"",
|
883
|
+
nil
|
884
|
+
].each do |value|
|
885
|
+
it "should not remove the accessor if passed #{value.inspect}" do
|
886
|
+
@item.remove_preview_image = value
|
887
|
+
@item.preview_image.should_not be_nil
|
888
|
+
end
|
889
|
+
|
890
|
+
it "should return false when called if set with #{value.inspect}" do
|
891
|
+
@item.remove_preview_image = value
|
892
|
+
@item.remove_preview_image.should be_false
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
it "should return false by default for the getter" do
|
897
|
+
@item.remove_preview_image.should be_false
|
898
|
+
end
|
899
|
+
|
900
|
+
end
|
901
|
+
|
902
|
+
describe "callbacks" do
|
903
|
+
|
904
|
+
describe "after_assign" do
|
905
|
+
|
906
|
+
before(:each) do
|
907
|
+
@app = test_app
|
908
|
+
@app.define_macro(MyModel, :image_accessor)
|
909
|
+
end
|
910
|
+
|
911
|
+
describe "as a block" do
|
912
|
+
|
913
|
+
def set_after_assign(*args, &block)
|
914
|
+
Item.class_eval do
|
915
|
+
image_accessor :preview_image do
|
916
|
+
after_assign(*args, &block)
|
917
|
+
end
|
918
|
+
end
|
919
|
+
end
|
920
|
+
|
921
|
+
it "should call it after assign" do
|
922
|
+
x = nil
|
923
|
+
set_after_assign{ x = 3 }
|
924
|
+
Item.new.preview_image = "hello"
|
925
|
+
x.should == 3
|
926
|
+
end
|
927
|
+
|
928
|
+
it "should not call it after unassign" do
|
929
|
+
x = nil
|
930
|
+
set_after_assign{ x = 3 }
|
931
|
+
Item.new.preview_image = nil
|
932
|
+
x.should be_nil
|
933
|
+
end
|
934
|
+
|
935
|
+
it "should yield the attachment" do
|
936
|
+
x = nil
|
937
|
+
set_after_assign{|a| x = a.data }
|
938
|
+
Item.new.preview_image = "discussion"
|
939
|
+
x.should == "discussion"
|
940
|
+
end
|
941
|
+
|
942
|
+
it "should evaluate in the model context" do
|
943
|
+
x = nil
|
944
|
+
set_after_assign{ x = title.upcase }
|
945
|
+
item = Item.new
|
946
|
+
item.title = "big"
|
947
|
+
item.preview_image = "jobs"
|
948
|
+
x.should == "BIG"
|
949
|
+
end
|
950
|
+
|
951
|
+
it "should allow passing a symbol for calling a model method" do
|
952
|
+
set_after_assign :set_title
|
953
|
+
item = Item.new
|
954
|
+
def item.set_title; self.title = 'duggen'; end
|
955
|
+
item.preview_image = "jobs"
|
956
|
+
item.title.should == "duggen"
|
957
|
+
end
|
958
|
+
|
959
|
+
it "should allow passing multiple symbols" do
|
960
|
+
set_after_assign :set_title, :upcase_title
|
961
|
+
item = Item.new
|
962
|
+
def item.set_title; self.title = 'doobie'; end
|
963
|
+
def item.upcase_title; self.title.upcase!; end
|
964
|
+
item.preview_image = "jobs"
|
965
|
+
item.title.should == "DOOBIE"
|
966
|
+
end
|
967
|
+
|
968
|
+
it "should not re-trigger callbacks (causing an infinite loop)" do
|
969
|
+
set_after_assign{|a| self.preview_image = 'dogman' }
|
970
|
+
item = Item.new
|
971
|
+
item.preview_image = "hello"
|
972
|
+
end
|
973
|
+
|
974
|
+
end
|
975
|
+
|
976
|
+
end
|
977
|
+
|
978
|
+
describe "after_unassign" do
|
979
|
+
before(:each) do
|
980
|
+
@app = test_app
|
981
|
+
@app.define_macro(MyModel, :image_accessor)
|
982
|
+
Item.class_eval do
|
983
|
+
image_accessor :preview_image do
|
984
|
+
after_unassign{ self.title = 'unassigned' }
|
985
|
+
end
|
986
|
+
end
|
987
|
+
@item = Item.new :title => 'yo'
|
988
|
+
end
|
989
|
+
|
990
|
+
it "should not call it after assign" do
|
991
|
+
@item.preview_image = 'suggs'
|
992
|
+
@item.title.should == 'yo'
|
993
|
+
end
|
994
|
+
|
995
|
+
it "should call it after unassign" do
|
996
|
+
@item.preview_image = nil
|
997
|
+
@item.title.should == 'unassigned'
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
|
1001
|
+
describe "copy_to" do
|
1002
|
+
before(:each) do
|
1003
|
+
@app = test_app
|
1004
|
+
@app.define_macro(MyModel, :image_accessor)
|
1005
|
+
@app.processor.add(:append) do |temp_object, string|
|
1006
|
+
temp_object.data + string
|
1007
|
+
end
|
1008
|
+
Item.class_eval do
|
1009
|
+
image_accessor :preview_image do
|
1010
|
+
copy_to(:other_image){|a| a.process(:append, title) }
|
1011
|
+
copy_to(:yet_another_image)
|
1012
|
+
end
|
1013
|
+
image_accessor :other_image
|
1014
|
+
image_accessor :yet_another_image
|
1015
|
+
end
|
1016
|
+
@item = Item.new :title => 'yo'
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
it "should copy to the other image when assigned" do
|
1020
|
+
@item.preview_image = 'hello bear'
|
1021
|
+
@item.other_image.data.should == 'hello bearyo'
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
it "should remove the other image when unassigned" do
|
1025
|
+
@item.preview_image = 'hello bear'
|
1026
|
+
@item.preview_image = nil
|
1027
|
+
@item.other_image.should be_nil
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
it "should allow simply copying over without args" do
|
1031
|
+
@item.preview_image = 'hello bear'
|
1032
|
+
@item.yet_another_image.data.should == 'hello bear'
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
describe "storage_opts" do
|
1040
|
+
|
1041
|
+
def set_storage_opts(*args, &block)
|
1042
|
+
Item.class_eval do
|
1043
|
+
image_accessor :preview_image do
|
1044
|
+
storage_opts(*args, &block)
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
before(:each) do
|
1050
|
+
@app = test_app
|
1051
|
+
@app.define_macro(MyModel, :image_accessor)
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
it "should send the specified options to the datastore on store" do
|
1055
|
+
set_storage_opts :egg => 'head'
|
1056
|
+
item = Item.new :preview_image => 'hello'
|
1057
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(:egg => 'head'))
|
1058
|
+
item.save!
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
it "should allow putting in a proc" do
|
1062
|
+
set_storage_opts{ {:egg => 'numb'} }
|
1063
|
+
item = Item.new :preview_image => 'hello'
|
1064
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(:egg => 'numb'))
|
1065
|
+
item.save!
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
it "should yield the attachment and exec in model context" do
|
1069
|
+
set_storage_opts{|a| {:egg => (a.data + title)} }
|
1070
|
+
item = Item.new :title => 'lump', :preview_image => 'hello'
|
1071
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(:egg => 'hellolump'))
|
1072
|
+
item.save!
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
it "should allow giving it a method symbol" do
|
1076
|
+
set_storage_opts :special_ops
|
1077
|
+
item = Item.new :preview_image => 'hello'
|
1078
|
+
def item.special_ops; {:a => 1}; end
|
1079
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(:a => 1))
|
1080
|
+
item.save!
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
it "should allow setting more than once" do
|
1084
|
+
Item.class_eval do
|
1085
|
+
image_accessor :preview_image do
|
1086
|
+
storage_opts{{ :a => title, :b => 'dumple' }}
|
1087
|
+
storage_opts{{ :b => title.upcase, :c => 'digby' }}
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
item = Item.new :title => 'lump', :preview_image => 'hello'
|
1091
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(
|
1092
|
+
:a => 'lump', :b => 'LUMP', :c => 'digby'
|
1093
|
+
))
|
1094
|
+
item.save!
|
1095
|
+
end
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
describe "storage_path, etc." do
|
1099
|
+
|
1100
|
+
def set_storage_path(path=nil, &block)
|
1101
|
+
Item.class_eval do
|
1102
|
+
image_accessor :preview_image do
|
1103
|
+
storage_path(path, &block)
|
1104
|
+
end
|
1105
|
+
def monkey
|
1106
|
+
"mr/#{title}/monkey"
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
before(:each) do
|
1112
|
+
@app = test_app
|
1113
|
+
@app.define_macro(MyModel, :image_accessor)
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
it "should allow setting as a string" do
|
1117
|
+
set_storage_path 'always/the/same'
|
1118
|
+
item = Item.new :preview_image => 'bilbo'
|
1119
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(
|
1120
|
+
:path => 'always/the/same'
|
1121
|
+
))
|
1122
|
+
item.save!
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
it "should allow setting as a symbol" do
|
1126
|
+
set_storage_path :monkey
|
1127
|
+
item = Item.new :title => 'billy'
|
1128
|
+
item.preview_image = 'bilbo'
|
1129
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(
|
1130
|
+
:path => 'mr/billy/monkey'
|
1131
|
+
))
|
1132
|
+
item.save!
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
it "should allow setting as a block" do
|
1136
|
+
set_storage_path{|a| "#{a.data}/megs/#{title}" }
|
1137
|
+
item = Item.new :title => 'billy'
|
1138
|
+
item.preview_image = 'bilbo'
|
1139
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(
|
1140
|
+
:path => 'bilbo/megs/billy'
|
1141
|
+
))
|
1142
|
+
item.save!
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
it "should work for other storage_xxx declarations" do
|
1146
|
+
Item.class_eval do
|
1147
|
+
image_accessor :preview_image do
|
1148
|
+
storage_eggs 23
|
1149
|
+
end
|
1150
|
+
end
|
1151
|
+
item = Item.new :preview_image => 'bilbo'
|
1152
|
+
@app.datastore.should_receive(:store).with(anything, hash_including(
|
1153
|
+
:eggs => 23
|
1154
|
+
))
|
1155
|
+
item.save!
|
1156
|
+
end
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
describe "unknown config method" do
|
1160
|
+
it "should raise an error" do
|
1161
|
+
lambda{
|
1162
|
+
Item.class_eval do
|
1163
|
+
image_accessor :preview_image do
|
1164
|
+
what :now?
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
}.should raise_error(NoMethodError)
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
describe "changed?" do
|
1172
|
+
before(:each) do
|
1173
|
+
set_up_item_class
|
1174
|
+
@item = Item.new
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
it "should be changed when assigned" do
|
1178
|
+
@item.preview_image = 'ggg'
|
1179
|
+
@item.preview_image.should be_changed
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
it "should not be changed when saved" do
|
1183
|
+
@item.preview_image = 'ggg'
|
1184
|
+
@item.save!
|
1185
|
+
@item.preview_image.should_not be_changed
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
it "should not be changed when reloaded" do
|
1189
|
+
@item.preview_image = 'ggg'
|
1190
|
+
@item.save!
|
1191
|
+
item = Item.find(@item.id)
|
1192
|
+
item.preview_image.should_not be_changed
|
1193
|
+
end
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
describe "retain and pending" do
|
1197
|
+
before(:each) do
|
1198
|
+
set_up_item_class(@app=test_app)
|
1199
|
+
@app.analyser.add :some_analyser_method do |temp_object|
|
1200
|
+
temp_object.data.upcase
|
1201
|
+
end
|
1202
|
+
@item = Item.new
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
it "should return nil if there are no changes" do
|
1206
|
+
@item.retained_preview_image.should be_nil
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
it "should return nil if assigned but not saved" do
|
1210
|
+
@item.preview_image = 'hello'
|
1211
|
+
@item.retained_preview_image.should be_nil
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
it "should return nil if assigned and saved" do
|
1215
|
+
@item.preview_image = 'hello'
|
1216
|
+
@item.save!
|
1217
|
+
@item.retained_preview_image.should be_nil
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
it "should return the saved stuff if assigned and retained" do
|
1221
|
+
@item.preview_image = 'hello'
|
1222
|
+
@item.preview_image.name = 'dog.biscuit'
|
1223
|
+
@app.datastore.should_receive(:store).with(a_temp_object_with_data('hello'), anything).and_return('new/uid')
|
1224
|
+
@item.preview_image.retain!
|
1225
|
+
Dragonfly::Serializer.marshal_decode(@item.retained_preview_image).should == {
|
1226
|
+
:uid => 'new/uid',
|
1227
|
+
:some_analyser_method => 'HELLO',
|
1228
|
+
:size => 5,
|
1229
|
+
:name => 'dog.biscuit'
|
1230
|
+
}
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
it "should return nil if assigned, retained and saved" do
|
1234
|
+
@item.preview_image = 'hello'
|
1235
|
+
@item.preview_image.retain!
|
1236
|
+
@item.save!
|
1237
|
+
@item.retained_preview_image.should be_nil
|
1238
|
+
end
|
1239
|
+
|
1240
|
+
it "should return nil if assigned, saved and retained" do
|
1241
|
+
@item.preview_image = 'hello'
|
1242
|
+
@item.save!
|
1243
|
+
@item.preview_image.retain!
|
1244
|
+
@item.retained_preview_image.should be_nil
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
it "should return nil if no changes have been made" do
|
1248
|
+
@item.preview_image = 'hello'
|
1249
|
+
@item.save!
|
1250
|
+
item = Item.find(@item.id)
|
1251
|
+
item.preview_image.retain!
|
1252
|
+
item.retained_preview_image.should be_nil
|
1253
|
+
end
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
describe "assigning from a pending state" do
|
1257
|
+
before(:each) do
|
1258
|
+
set_up_item_class(@app=test_app)
|
1259
|
+
@app.analyser.add :some_analyser_method do |temp_object|
|
1260
|
+
temp_object.data.upcase
|
1261
|
+
end
|
1262
|
+
@pending_string = Dragonfly::Serializer.marshal_encode(
|
1263
|
+
:uid => 'new/uid',
|
1264
|
+
:some_analyser_method => 'HELLO',
|
1265
|
+
:size => 5,
|
1266
|
+
:name => 'dog.biscuit'
|
1267
|
+
)
|
1268
|
+
@item = Item.new
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
it "should update the attributes" do
|
1272
|
+
@item.retained_preview_image = @pending_string
|
1273
|
+
@item.preview_image_uid.should == 'new/uid'
|
1274
|
+
@item.preview_image_some_analyser_method.should == 'HELLO'
|
1275
|
+
@item.preview_image_size.should == 5
|
1276
|
+
@item.preview_image_name.should == 'dog.biscuit'
|
1277
|
+
end
|
1278
|
+
|
1279
|
+
it "should update the attachment meta" do
|
1280
|
+
@item.retained_preview_image = @pending_string
|
1281
|
+
@item.preview_image.meta[:some_analyser_method].should == 'HELLO'
|
1282
|
+
@item.preview_image.meta[:size].should == 5
|
1283
|
+
@item.preview_image.meta[:name].should == 'dog.biscuit'
|
1284
|
+
end
|
1285
|
+
|
1286
|
+
it "should be a normal fetch job" do
|
1287
|
+
@item.retained_preview_image = @pending_string
|
1288
|
+
@app.datastore.should_receive(:retrieve).with('new/uid').and_return(Dragonfly::TempObject.new('retrieved yo'))
|
1289
|
+
@item.preview_image.data.should == 'retrieved yo'
|
1290
|
+
end
|
1291
|
+
|
1292
|
+
it "should give the correct url" do
|
1293
|
+
@item.retained_preview_image = @pending_string
|
1294
|
+
@item.preview_image.url.should =~ %r{^/\w+/dog.biscuit$}
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
it "should raise an error if the pending string contains a non-magic attr method" do
|
1298
|
+
pending_string = Dragonfly::Serializer.marshal_encode(
|
1299
|
+
:uid => 'new/uid',
|
1300
|
+
:some_analyser_method => 'HELLO',
|
1301
|
+
:size => 5,
|
1302
|
+
:name => 'dog.biscuit',
|
1303
|
+
:something => 'else'
|
1304
|
+
)
|
1305
|
+
item = @item
|
1306
|
+
lambda{
|
1307
|
+
item.retained_preview_image = pending_string
|
1308
|
+
}.should raise_error(Dragonfly::ActiveModelExtensions::Attachment::BadAssignmentKey)
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
[nil, "", "asdfsad"].each do |value|
|
1312
|
+
it "should do nothing if assigned with #{value}" do
|
1313
|
+
@item.retained_preview_image = value
|
1314
|
+
@item.preview_image_uid.should be_nil
|
1315
|
+
end
|
1316
|
+
end
|
1317
|
+
|
1318
|
+
it "should return the pending string again" do
|
1319
|
+
@item.retained_preview_image = @pending_string
|
1320
|
+
Dragonfly::Serializer.marshal_decode(@item.retained_preview_image).should ==
|
1321
|
+
Dragonfly::Serializer.marshal_decode(@pending_string)
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
it "should destroy the old one on save" do
|
1325
|
+
@item.preview_image = 'oldone'
|
1326
|
+
@app.datastore.should_receive(:store).with(a_temp_object_with_data('oldone'), anything).and_return('old/uid')
|
1327
|
+
@item.save!
|
1328
|
+
item = Item.find(@item.id)
|
1329
|
+
item.retained_preview_image = @pending_string
|
1330
|
+
@app.datastore.should_receive(:destroy).with('old/uid')
|
1331
|
+
item.save!
|
1332
|
+
end
|
1333
|
+
|
1334
|
+
describe "combinations of assignment" do
|
1335
|
+
before(:each) do
|
1336
|
+
end
|
1337
|
+
it "should destroy the previously retained one if something new is then assigned" do
|
1338
|
+
@item.retained_preview_image = @pending_string
|
1339
|
+
@app.datastore.should_receive(:destroy).with('new/uid')
|
1340
|
+
@item.preview_image = 'yet another new thing'
|
1341
|
+
end
|
1342
|
+
|
1343
|
+
it "should destroy the previously retained one if something new is already assigned" do
|
1344
|
+
@item.preview_image = 'yet another new thing'
|
1345
|
+
@app.datastore.should_receive(:destroy).with('new/uid')
|
1346
|
+
@item.retained_preview_image = @pending_string
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
it "should destroy the previously retained one if nil is then assigned" do
|
1350
|
+
@item.retained_preview_image = @pending_string
|
1351
|
+
@app.datastore.should_receive(:destroy).with('new/uid')
|
1352
|
+
@item.preview_image = nil
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
it "should destroy the previously retained one if nil is already assigned" do
|
1356
|
+
@item.preview_image = nil
|
1357
|
+
@app.datastore.should_receive(:destroy).with('new/uid')
|
1358
|
+
@item.retained_preview_image = @pending_string
|
1359
|
+
end
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
end
|
1363
|
+
|
1364
|
+
describe "retain macro" do
|
1365
|
+
|
1366
|
+
before(:each) do
|
1367
|
+
@app = test_app
|
1368
|
+
@app.define_macro(MyModel, :image_accessor)
|
1369
|
+
Item.class_eval do
|
1370
|
+
image_accessor :preview_image do
|
1371
|
+
retain
|
1372
|
+
end
|
1373
|
+
end
|
1374
|
+
@item = Item.new
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
it "should retain if specified" do
|
1378
|
+
@app.datastore.should_receive(:store).with(a_temp_object_with_data('boo'), anything).and_return('uidyo')
|
1379
|
+
@item.preview_image = 'boo'
|
1380
|
+
@item.retained_preview_image.should =~ /^\w+$/
|
1381
|
+
end
|
1382
|
+
end
|
1383
|
+
|
1384
|
+
describe "format and mime type" do
|
1385
|
+
before(:each) do
|
1386
|
+
@app = test_app
|
1387
|
+
@app.analyser.add :mime_type do |temp_object|
|
1388
|
+
'some/type'
|
1389
|
+
end
|
1390
|
+
set_up_item_class(@app)
|
1391
|
+
@item = Item.new
|
1392
|
+
@content = "doggo"
|
1393
|
+
@content.stub!(:original_filename).and_return('egg.png')
|
1394
|
+
end
|
1395
|
+
it "should trust the file extension with format if configured to" do
|
1396
|
+
@item.preview_image = @content
|
1397
|
+
@item.preview_image.format.should == :png
|
1398
|
+
end
|
1399
|
+
it "should trust the file extension with mime_type if configured to" do
|
1400
|
+
@item.preview_image = @content
|
1401
|
+
@item.preview_image.mime_type.should == 'image/png'
|
1402
|
+
end
|
1403
|
+
it "should not trust the file extension with format if configured not to" do
|
1404
|
+
@app.trust_file_extensions = false
|
1405
|
+
@item.preview_image = @content
|
1406
|
+
@item.preview_image.format.should == nil
|
1407
|
+
end
|
1408
|
+
it "should not trust the file extension with mime_type if configured not to" do
|
1409
|
+
@app.trust_file_extensions = false
|
1410
|
+
@item.preview_image = @content
|
1411
|
+
@item.preview_image.mime_type.should == 'some/type'
|
1412
|
+
end
|
1413
|
+
end
|
1414
|
+
|
1415
|
+
describe "inspect" do
|
1416
|
+
before(:each) do
|
1417
|
+
set_up_item_class
|
1418
|
+
@item = Item.new :preview_image => 'blug'
|
1419
|
+
@item.save!
|
1420
|
+
end
|
1421
|
+
it "should be awesome" do
|
1422
|
+
@item.preview_image.inspect.should =~ %r{^<Dragonfly Attachment uid="[^"]+".*>$}
|
1423
|
+
end
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
end
|