dragonfly 0.1.6 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dragonfly might be problematic. Click here for more details.
- data/.yardopts +4 -0
- data/{README.markdown → README.md} +12 -24
- data/Rakefile +6 -6
- data/VERSION +1 -1
- data/config.rb +1 -3
- data/docs.watchr +1 -1
- data/dragonfly-rails.gemspec +2 -5
- data/dragonfly.gemspec +32 -12
- data/extra_docs/ActiveRecord.md +195 -0
- data/extra_docs/Analysers.md +63 -0
- data/extra_docs/DataStorage.md +33 -0
- data/extra_docs/Encoding.md +58 -0
- data/extra_docs/GettingStarted.md +114 -0
- data/extra_docs/Processing.md +58 -0
- data/extra_docs/Shortcuts.md +118 -0
- data/extra_docs/UsingWithRails.md +104 -0
- data/features/{dragonfly.feature → images.feature} +14 -4
- data/features/no_processing.feature +20 -0
- data/features/steps/dragonfly_steps.rb +29 -8
- data/features/support/env.rb +20 -8
- data/generators/dragonfly_app/USAGE +0 -1
- data/generators/dragonfly_app/dragonfly_app_generator.rb +1 -13
- data/generators/dragonfly_app/templates/metal_file.erb +1 -1
- data/lib/dragonfly/active_record_extensions.rb +1 -0
- data/lib/dragonfly/active_record_extensions/attachment.rb +52 -6
- data/lib/dragonfly/active_record_extensions/validations.rb +26 -6
- data/lib/dragonfly/analysis/base.rb +6 -3
- data/lib/dragonfly/analysis/r_magick_analyser.rb +0 -6
- data/lib/dragonfly/app.rb +53 -35
- data/lib/dragonfly/configurable.rb +1 -1
- data/lib/dragonfly/data_storage/file_data_store.rb +8 -8
- data/lib/dragonfly/delegatable.rb +14 -0
- data/lib/dragonfly/delegator.rb +50 -0
- data/lib/dragonfly/encoding/base.rb +7 -7
- data/lib/dragonfly/encoding/r_magick_encoder.rb +3 -0
- data/lib/dragonfly/encoding/transparent_encoder.rb +1 -1
- data/lib/dragonfly/extended_temp_object.rb +13 -7
- data/lib/dragonfly/parameters.rb +17 -11
- data/lib/dragonfly/processing/base.rb +9 -0
- data/lib/dragonfly/processing/r_magick_processor.rb +15 -1
- data/lib/dragonfly/r_magick_configuration.rb +12 -8
- data/lib/dragonfly/rails/images.rb +1 -1
- data/lib/dragonfly/temp_object.rb +14 -2
- data/lib/dragonfly/url_handler.rb +5 -6
- data/samples/sample.docx +0 -0
- data/spec/dragonfly/active_record_extensions/model_spec.rb +175 -84
- data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +0 -8
- data/spec/dragonfly/app_spec.rb +3 -3
- data/spec/dragonfly/configurable_spec.rb +1 -1
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +55 -40
- data/spec/dragonfly/delegatable_spec.rb +32 -0
- data/spec/dragonfly/delegator_spec.rb +133 -0
- data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +28 -0
- data/spec/dragonfly/extended_temp_object_spec.rb +5 -5
- data/spec/dragonfly/parameters_spec.rb +22 -32
- data/spec/dragonfly/processing/rmagick_processor_spec.rb +1 -2
- data/spec/dragonfly/temp_object_spec.rb +51 -0
- data/spec/dragonfly/url_handler_spec.rb +10 -15
- data/yard/handlers/configurable_attr_handler.rb +38 -0
- data/yard/setup.rb +9 -0
- data/yard/templates/default/fulldoc/html/css/common.css +27 -0
- data/yard/templates/default/module/html/configuration_summary.erb +31 -0
- data/yard/templates/default/module/setup.rb +17 -0
- metadata +31 -12
- data/features/support/image_helpers.rb +0 -9
- data/generators/dragonfly_app/templates/custom_processing.erb +0 -13
- data/lib/dragonfly/analysis/analyser.rb +0 -45
- data/lib/dragonfly/processing/processor.rb +0 -14
- data/spec/dragonfly/analysis/analyser_spec.rb +0 -85
@@ -0,0 +1,104 @@
|
|
1
|
+
Using With Rails
|
2
|
+
================
|
3
|
+
|
4
|
+
There are two main ways to use Dragonfly with Rails - as a {Dragonfly::Middleware middleware},
|
5
|
+
and as a Rails Metal.
|
6
|
+
|
7
|
+
Using as Middleware
|
8
|
+
-------------------
|
9
|
+
In environment.rb:
|
10
|
+
|
11
|
+
config.gem 'dragonfly-rails', :lib => 'dragonfly/rails/images'
|
12
|
+
config.middleware.use 'Dragonfly::MiddlewareWithCache', :images
|
13
|
+
|
14
|
+
The gem dragonfly-rails does nothing more than tie together the gem dependencies dragonfly,
|
15
|
+
rack-cache and rmagick.
|
16
|
+
|
17
|
+
The required file 'dragonfly/rails/images.rb' initializes a dragonfly app, configures it to use rmagick processing, encoding, etc.,
|
18
|
+
and registers the app so that you can use ActiveRecord accessors.
|
19
|
+
|
20
|
+
For reference, the contents are as follows:
|
21
|
+
|
22
|
+
require 'dragonfly'
|
23
|
+
|
24
|
+
### The dragonfly app ###
|
25
|
+
|
26
|
+
app = Dragonfly::App[:images]
|
27
|
+
app.configure_with(Dragonfly::RMagickConfiguration)
|
28
|
+
app.configure do |c|
|
29
|
+
c.log = RAILS_DEFAULT_LOGGER
|
30
|
+
c.datastore.configure do |d|
|
31
|
+
d.root_path = "#{Rails.root}/public/system/dragonfly/#{Rails.env}"
|
32
|
+
end
|
33
|
+
c.url_handler.configure do |u|
|
34
|
+
u.protect_from_dos_attacks = false
|
35
|
+
u.path_prefix = '/media'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
### Extend active record ###
|
40
|
+
ActiveRecord::Base.extend Dragonfly::ActiveRecordExtensions
|
41
|
+
ActiveRecord::Base.register_dragonfly_app(:image, app)
|
42
|
+
|
43
|
+
The second line configures rails to use a {Dragonfly::MiddlewareWithCache middleware} which uses the named app (named `:images`), and puts
|
44
|
+
{http://tomayko.com/src/rack-cache/ Rack::Cache} in front of it for performance.
|
45
|
+
You can pass extra arguments to this line which will go directly to configuring Rack::Cache (see its docs for how to configure it).
|
46
|
+
The default configuration for Rack::Cache is
|
47
|
+
|
48
|
+
{
|
49
|
+
:verbose => true,
|
50
|
+
:metastore => 'file:/var/cache/rack/meta',
|
51
|
+
:entitystore => 'file:/var/cache/rack/body'
|
52
|
+
}
|
53
|
+
|
54
|
+
To see what you can do with the active record accessors, see {file:ActiveRecord}.
|
55
|
+
|
56
|
+
Using as a Rails Metal
|
57
|
+
----------------------
|
58
|
+
The easiest way of setting up as a rails metal is using the supplied generator.
|
59
|
+
(NB I've had a couple of problems with the generator with plural/singular names with early versions of metal in Rails 2.3 -
|
60
|
+
this should be resolvable by making sure the metal name matches its filename).
|
61
|
+
|
62
|
+
./script/generate dragonfly_app images
|
63
|
+
|
64
|
+
The argument 'images' could be anything - it is an arbitrary app name.
|
65
|
+
|
66
|
+
This does two things:
|
67
|
+
|
68
|
+
1. Creates and configures an app as a rails metal
|
69
|
+
2. Registers the app for use with ActiveRecord - see {file:ActiveRecord}
|
70
|
+
|
71
|
+
For reference, the contents of the metal file is given below. You could do something similar yourself without the generator.
|
72
|
+
|
73
|
+
# Allow the metal piece to run in isolation
|
74
|
+
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
|
75
|
+
require 'dragonfly'
|
76
|
+
|
77
|
+
# Configuration of the Dragonfly App
|
78
|
+
Dragonfly::App[:images].configure_with(Dragonfly::RMagickConfiguration)
|
79
|
+
Dragonfly::App[:images].configure do |c|
|
80
|
+
c.log = RAILS_DEFAULT_LOGGER
|
81
|
+
c.datastore.configure do |d|
|
82
|
+
d.root_path = "#{Rails.root}/public/system/dragonfly/#{Rails.env}"
|
83
|
+
end
|
84
|
+
c.url_handler.configure do |u|
|
85
|
+
u.secret = '29205f3e01648d0966bd5b119cd53347390a9ba9'
|
86
|
+
u.path_prefix = '/images'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# The metal, for running the app
|
91
|
+
app = Dragonfly::App[:images]
|
92
|
+
Images = Rack::Builder.new do
|
93
|
+
|
94
|
+
# UNCOMMENT ME!!!
|
95
|
+
# ... if you want to use super-dooper middleware 'rack-cache'
|
96
|
+
# require 'rack/cache'
|
97
|
+
# use Rack::Cache,
|
98
|
+
# :verbose => true,
|
99
|
+
# :metastore => 'file:/var/cache/rack/meta',
|
100
|
+
# :entitystore => 'file:/var/cache/rack/body'
|
101
|
+
|
102
|
+
run app
|
103
|
+
|
104
|
+
end
|
@@ -3,10 +3,11 @@ Feature: champion uses dragonfly to process images
|
|
3
3
|
A user uses dragonfly
|
4
4
|
|
5
5
|
Background:
|
6
|
+
Given we are using the app for images
|
6
7
|
Given a stored image "beach.png" with dimensions 200x100
|
7
8
|
|
8
9
|
Scenario: Go to url for original
|
9
|
-
When I go to the url for
|
10
|
+
When I go to the url for "beach.png", with format 'png'
|
10
11
|
Then the response should be OK
|
11
12
|
And the response should have mime-type 'image/png'
|
12
13
|
And the image should have width '200'
|
@@ -14,7 +15,7 @@ Feature: champion uses dragonfly to process images
|
|
14
15
|
And the image should have format 'png'
|
15
16
|
|
16
17
|
Scenario: Go to url for changed format version
|
17
|
-
When I go to the url for
|
18
|
+
When I go to the url for "beach.png", with format 'gif'
|
18
19
|
Then the response should be OK
|
19
20
|
And the response should have mime-type 'image/gif'
|
20
21
|
And the image should have width '200'
|
@@ -22,7 +23,7 @@ Feature: champion uses dragonfly to process images
|
|
22
23
|
And the image should have format 'gif'
|
23
24
|
|
24
25
|
Scenario: Go to url for soft resized version
|
25
|
-
When I go to the url for
|
26
|
+
When I go to the url for "beach.png", with format 'png' and resize geometry '100x150'
|
26
27
|
Then the response should be OK
|
27
28
|
And the response should have mime-type 'image/png'
|
28
29
|
And the image should have width '100'
|
@@ -30,9 +31,18 @@ Feature: champion uses dragonfly to process images
|
|
30
31
|
And the image should have format 'png'
|
31
32
|
|
32
33
|
Scenario: Go to url for hard resized version
|
33
|
-
When I go to the url for
|
34
|
+
When I go to the url for "beach.png", with format 'png' and resize geometry '100x150!'
|
34
35
|
Then the response should be OK
|
35
36
|
And the response should have mime-type 'image/png'
|
36
37
|
And the image should have width '100'
|
37
38
|
And the image should have height '150'
|
38
39
|
And the image should have format 'png'
|
40
|
+
|
41
|
+
Scenario: use a parameters shortcut
|
42
|
+
# Note that this scenario makes use of the configured default format
|
43
|
+
When I go to the url for "beach.png", with shortcut '100x150!'
|
44
|
+
Then the response should be OK
|
45
|
+
And the response should have mime-type 'image/jpeg'
|
46
|
+
And the image should have width '100'
|
47
|
+
And the image should have height '150'
|
48
|
+
And the image should have format 'jpeg'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Feature: winner uses dragonfly to serve different kinds of files
|
2
|
+
In order to be a winner
|
3
|
+
As a potential loser
|
4
|
+
I want to be a winner
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given we are using the app for files
|
8
|
+
|
9
|
+
Scenario: Go to url for original, without extension
|
10
|
+
Given a stored file "sample.docx"
|
11
|
+
When I go to the url for "sample.docx"
|
12
|
+
Then the response should be OK
|
13
|
+
And the response should have mime-type 'application/zip'
|
14
|
+
And the response should have the same content as the file "sample.docx"
|
15
|
+
|
16
|
+
Scenario: Go to url for original, with extension
|
17
|
+
Given a stored file "sample.docx"
|
18
|
+
When I go to the url for "sample.docx", with format 'docx'
|
19
|
+
Then the response should be OK
|
20
|
+
And the response should have mime-type 'application/zip'
|
@@ -1,28 +1,45 @@
|
|
1
|
+
Given /^we are using the app for (\w+)$/ do |app_name|
|
2
|
+
$app = Dragonfly::App[app_name.to_sym]
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^a stored file "(.+?)"$/ do |name|
|
6
|
+
file = File.new(File.dirname(__FILE__) + "/../../samples/#{name}")
|
7
|
+
uid = $app.store(file)
|
8
|
+
TEMP_FILES[name] = uid
|
9
|
+
end
|
10
|
+
|
1
11
|
Given /^a stored image "(.+?)" with dimensions (\d+)x(\d+)$/ do |name, width, height|
|
2
12
|
tempfile = Tempfile.new(name)
|
3
13
|
`convert -resize #{width}x#{height}! #{SAMPLE_IMAGE_PATH} #{tempfile.path}`
|
4
|
-
|
5
|
-
|
6
|
-
|
14
|
+
uid = $app.store(tempfile)
|
15
|
+
TEMP_FILES[name] = uid
|
16
|
+
end
|
17
|
+
|
18
|
+
When /^I go to the url for "(.+?)"$/ do |name|
|
19
|
+
make_request name
|
7
20
|
end
|
8
21
|
|
9
|
-
When /^I go to the url for
|
10
|
-
|
22
|
+
When /^I go to the url for "(.+?)", with format '([^']+?)'$/ do |name, ext|
|
23
|
+
make_request name, :format => ext
|
11
24
|
end
|
12
25
|
|
13
|
-
When /^I go to the url for
|
14
|
-
|
26
|
+
When /^I go to the url for "(.+?)", with format '(.+?)' and resize geometry '(.+?)'$/ do |name, ext, geometry|
|
27
|
+
make_request(name,
|
15
28
|
:format => ext,
|
16
29
|
:processing_method => :resize,
|
17
30
|
:processing_options => {:geometry => geometry}
|
18
31
|
)
|
19
32
|
end
|
20
33
|
|
34
|
+
When /^I go to the url for "(.+?)", with shortcut '([^']+?)'$/ do |name, arg1|
|
35
|
+
make_request name, arg1
|
36
|
+
end
|
37
|
+
|
21
38
|
Then "the response should be OK" do
|
22
39
|
@response.status.should == 200
|
23
40
|
end
|
24
41
|
|
25
|
-
Then
|
42
|
+
Then /the response should have mime-type '(.+?)'/ do |mime_type|
|
26
43
|
@response.headers['Content-Type'].should == mime_type
|
27
44
|
end
|
28
45
|
|
@@ -37,3 +54,7 @@ end
|
|
37
54
|
Then "the image should have format '(.+?)'" do |format|
|
38
55
|
@response.body.should have_format(format)
|
39
56
|
end
|
57
|
+
|
58
|
+
Then /^the response should have the same content as the file "([^\"]*)"$/ do |name|
|
59
|
+
@response.body.should == $app.fetch(TEMP_FILES[name]).data
|
60
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -3,23 +3,35 @@ require 'dragonfly'
|
|
3
3
|
require 'spec/expectations'
|
4
4
|
require 'test/unit/assertions'
|
5
5
|
require 'ruby-debug'
|
6
|
-
require File.dirname(__FILE__) + '/image_helpers.rb'
|
7
6
|
require File.dirname(__FILE__) + '/../../spec/image_matchers.rb'
|
8
7
|
|
9
8
|
# A hash of <name for reference> => <dragonfly uid> pairs
|
10
|
-
|
9
|
+
TEMP_FILES = {}
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
Dragonfly::App[:images].configure_with(Dragonfly::RMagickConfiguration)
|
12
|
+
Dragonfly::App[:files].configure do |c|
|
13
|
+
c.register_analyser(Dragonfly::Analysis::FileCommandAnalyser)
|
14
|
+
c.register_encoder(Dragonfly::Encoding::TransparentEncoder)
|
15
|
+
end
|
14
16
|
|
15
17
|
SAMPLE_IMAGE_PATH = File.dirname(__FILE__)+'/../../samples/beach.png'
|
16
18
|
|
17
19
|
Before do
|
18
20
|
# Remove temporary images
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
TEMP_FILES.each do |name, uid|
|
22
|
+
$app.datastore.destroy(uid)
|
23
|
+
TEMP_FILES.delete(name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module MyHelpers
|
28
|
+
|
29
|
+
def make_request(name, *args)
|
30
|
+
request = Rack::MockRequest.new($app)
|
31
|
+
url = $app.url_for(TEMP_FILES[name], *args)
|
32
|
+
@response = request.get(url)
|
22
33
|
end
|
34
|
+
|
23
35
|
end
|
24
36
|
|
25
|
-
World(
|
37
|
+
World(MyHelpers)
|
@@ -7,7 +7,6 @@ class DragonflyAppGenerator < Rails::Generator::NamedBase
|
|
7
7
|
metal_name = plural_name.camelize
|
8
8
|
path_prefix = plural_name
|
9
9
|
single_name = singular_name.singularize
|
10
|
-
custom_processor_name = "Custom#{single_name.camelize}Processing"
|
11
10
|
|
12
11
|
record do |m|
|
13
12
|
# The initializer
|
@@ -33,18 +32,7 @@ class DragonflyAppGenerator < Rails::Generator::NamedBase
|
|
33
32
|
:app_name => app_name,
|
34
33
|
:metal_name => metal_name,
|
35
34
|
:path_prefix => path_prefix,
|
36
|
-
:random_secret => Digest::SHA1.hexdigest(Time.now.to_s)
|
37
|
-
:custom_processor_name => custom_processor_name
|
38
|
-
}
|
39
|
-
)
|
40
|
-
|
41
|
-
# The custom processor
|
42
|
-
m.template(
|
43
|
-
'custom_processing.erb',
|
44
|
-
"lib/#{custom_processor_name.underscore}.rb",
|
45
|
-
:assigns => {
|
46
|
-
:module_name => custom_processor_name,
|
47
|
-
:temp_object_name => single_name
|
35
|
+
:random_secret => Digest::SHA1.hexdigest(Time.now.to_s)
|
48
36
|
}
|
49
37
|
)
|
50
38
|
|
@@ -9,7 +9,7 @@ Dragonfly::App[:<%= app_name %>].configure do |c|
|
|
9
9
|
c.datastore.configure do |d|
|
10
10
|
d.root_path = "#{Rails.root}/public/system/dragonfly/#{Rails.env}"
|
11
11
|
end
|
12
|
-
c.url_handler do |u|
|
12
|
+
c.url_handler.configure do |u|
|
13
13
|
u.secret = '<%= random_secret %>'
|
14
14
|
u.path_prefix = '/<%= path_prefix %>'
|
15
15
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Dragonfly
|
2
4
|
module ActiveRecordExtensions
|
3
5
|
|
@@ -5,6 +7,10 @@ module Dragonfly
|
|
5
7
|
|
6
8
|
class Attachment
|
7
9
|
|
10
|
+
extend Forwardable
|
11
|
+
def_delegators :temp_object,
|
12
|
+
:data, :to_file, :file, :tempfile, :path, :process, :process!, :encode, :encode!, :transform, :transform!
|
13
|
+
|
8
14
|
def initialize(app, parent_model, attribute_name)
|
9
15
|
@app, @parent_model, @attribute_name = app, parent_model, attribute_name
|
10
16
|
end
|
@@ -38,10 +44,6 @@ module Dragonfly
|
|
38
44
|
end
|
39
45
|
end
|
40
46
|
|
41
|
-
def size
|
42
|
-
temp_object.size
|
43
|
-
end
|
44
|
-
|
45
47
|
def temp_object
|
46
48
|
if @temp_object
|
47
49
|
@temp_object
|
@@ -60,6 +62,30 @@ module Dragonfly
|
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
65
|
+
def methods(*args)
|
66
|
+
(super + methods_to_delegate_to_temp_object).uniq
|
67
|
+
end
|
68
|
+
|
69
|
+
def public_methods(*args)
|
70
|
+
(super + methods_to_delegate_to_temp_object).uniq
|
71
|
+
end
|
72
|
+
|
73
|
+
def respond_to?(method)
|
74
|
+
super || methods_to_delegate_to_temp_object.include?(method.to_s)
|
75
|
+
end
|
76
|
+
|
77
|
+
def ext
|
78
|
+
has_magic_attribute_for?(:ext) ? magic_attribute_for(:ext) : temp_object.ext
|
79
|
+
end
|
80
|
+
|
81
|
+
def name
|
82
|
+
has_magic_attribute_for?(:name) ? magic_attribute_for(:name) : temp_object.name
|
83
|
+
end
|
84
|
+
|
85
|
+
def size
|
86
|
+
has_magic_attribute_for?(:size) ? magic_attribute_for(:size) : temp_object.size
|
87
|
+
end
|
88
|
+
|
63
89
|
private
|
64
90
|
|
65
91
|
def been_assigned?
|
@@ -91,13 +117,17 @@ module Dragonfly
|
|
91
117
|
attr_writer :temp_object
|
92
118
|
|
93
119
|
def analyser
|
94
|
-
app.
|
120
|
+
app.analysers
|
121
|
+
end
|
122
|
+
|
123
|
+
def methods_to_delegate_to_temp_object
|
124
|
+
analyser.callable_methods
|
95
125
|
end
|
96
126
|
|
97
127
|
def magic_attributes
|
98
128
|
parent_model.class.column_names.select { |name|
|
99
129
|
name =~ /^#{attribute_name}_(.+)$/ &&
|
100
|
-
(
|
130
|
+
(methods_to_delegate_to_temp_object.include?($1) || %w(size ext name).include?($1))
|
101
131
|
}
|
102
132
|
end
|
103
133
|
|
@@ -112,6 +142,22 @@ module Dragonfly
|
|
112
142
|
magic_attributes.each{|attribute| parent_model.send("#{attribute}=", nil) }
|
113
143
|
end
|
114
144
|
|
145
|
+
def has_magic_attribute_for?(property)
|
146
|
+
magic_attributes.include?("#{attribute_name}_#{property}")
|
147
|
+
end
|
148
|
+
|
149
|
+
def magic_attribute_for(property)
|
150
|
+
parent_model.send("#{attribute_name}_#{property}")
|
151
|
+
end
|
152
|
+
|
153
|
+
def method_missing(meth, *args, &block)
|
154
|
+
if methods_to_delegate_to_temp_object.include?(meth.to_s)
|
155
|
+
has_magic_attribute_for?(meth) ? magic_attribute_for(meth) : temp_object.send(meth, *args, &block)
|
156
|
+
else
|
157
|
+
super
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
115
161
|
end
|
116
162
|
end
|
117
163
|
end
|
@@ -2,16 +2,36 @@ module Dragonfly
|
|
2
2
|
module ActiveRecordExtensions
|
3
3
|
module Validations
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
private
|
6
|
+
|
7
|
+
def validates_property(property_name, opts)
|
8
|
+
attrs = opts[:of] or raise ArgumentError, "you need to provide the attribute which has the property, using :of => <attribute_name>"
|
9
|
+
attrs = [attrs].flatten #(make sure it's an array)
|
10
|
+
|
11
|
+
raise ArgumentError, "you must provide either :in => [<value1>, <value2>..] or :as => <value>" unless opts[:in] || opts[:as]
|
12
|
+
allowed_values = opts[:in] || [opts[:as]]
|
13
|
+
|
14
|
+
args = attrs + [opts]
|
9
15
|
validates_each(*args) do |record, attr, attachment|
|
10
16
|
if attachment
|
11
|
-
|
12
|
-
record.errors.add attr, "
|
17
|
+
property = attachment.send(property_name)
|
18
|
+
record.errors.add attr, "#{property_name.to_s.humanize.downcase} is incorrect. It needs to be #{expected_values_string(allowed_values)}," +
|
19
|
+
" but was '#{property}'" unless allowed_values.include?(property)
|
13
20
|
end
|
14
21
|
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def validates_mime_type_of(attribute, opts)
|
26
|
+
validates_property :mime_type, opts.merge(:of => attribute)
|
27
|
+
end
|
28
|
+
|
29
|
+
def expected_values_string(allowed_values)
|
30
|
+
if allowed_values.is_a?(Range)
|
31
|
+
"between #{allowed_values.first} and #{allowed_values.last}"
|
32
|
+
else
|
33
|
+
allowed_values.length > 1 ? "one of '#{allowed_values.join('\', \'')}'" : "'#{allowed_values.first.to_s}'"
|
34
|
+
end
|
15
35
|
end
|
16
36
|
|
17
37
|
end
|