carrierwave-meta 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +173 -15
- data/carrierwave-meta.gemspec +2 -0
- data/lib/carrierwave-meta.rb +1 -0
- data/lib/carrierwave-meta/meta.rb +8 -1
- data/lib/carrierwave-meta/version.rb +1 -1
- data/spec/modules/meta_spec.rb +4 -7
- data/spec/spec_helper.rb +2 -0
- data/spec/support/test_uploader.rb +5 -1
- metadata +40 -12
data/README.rdoc
CHANGED
@@ -11,14 +11,6 @@ Add the following line to your Gemfile:
|
|
11
11
|
include CarrierWave::RMagick
|
12
12
|
include CarrierWave::Meta
|
13
13
|
|
14
|
-
def store_dir
|
15
|
-
"tmp/store"
|
16
|
-
end
|
17
|
-
|
18
|
-
def cache_dir
|
19
|
-
"tmp/cache"
|
20
|
-
end
|
21
|
-
|
22
14
|
storage :file
|
23
15
|
|
24
16
|
process :store_meta
|
@@ -46,6 +38,9 @@ Add the following line to your Gemfile:
|
|
46
38
|
|
47
39
|
= Saving values to database
|
48
40
|
|
41
|
+
Simply create database columns to hold metadata in your model's table. Currently gem supports width, height,
|
42
|
+
image_size ([width, height]), content_type and file_size fields. Versions are supported too.
|
43
|
+
|
49
44
|
class TestModel
|
50
45
|
attr_accessor :image_width
|
51
46
|
attr_accessor :image_height
|
@@ -74,25 +69,188 @@ When columns are available in the model instance, metadata is stored in that col
|
|
74
69
|
|
75
70
|
= Behind the scenes
|
76
71
|
|
77
|
-
After the file is retrieved from store metadata is recalculated
|
78
|
-
attached model instance.
|
72
|
+
After the file is retrieved from store or cache metadata is recalculated unless uploader has attached
|
73
|
+
model instance. If uploader has attached model instance values are read from that instance.
|
79
74
|
|
80
75
|
uploader = TestUploader.new
|
81
76
|
uploader.retrieve_from_store!('test.jpg')
|
82
77
|
|
83
78
|
uploader.version.width # 200
|
84
79
|
|
85
|
-
|
86
|
-
|
80
|
+
= model_delegate_attribute
|
81
|
+
|
82
|
+
Used to synchronize data between uploader and mounted model instance. Model's instance is used like value cache.
|
83
|
+
|
84
|
+
class DelegateTestModel
|
85
|
+
attr_accessor :processed
|
86
|
+
attr_accessor :a_processed
|
87
|
+
attr_accessor :a_b_processed
|
88
|
+
end
|
89
|
+
|
90
|
+
class DelegateTestUploader < CarrierWave::Uploader::Base
|
91
|
+
model_delegate_attribute :uploaded
|
92
|
+
|
93
|
+
set_processed
|
94
|
+
|
95
|
+
version :a do
|
96
|
+
set_processed
|
97
|
+
version :b do
|
98
|
+
set_processed
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_processed
|
103
|
+
self.processed = true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
model = DelegateTestModel.new
|
108
|
+
uploader = DelegateTestUploader.new(model, :image)
|
109
|
+
file = File.open('test.jpg')
|
110
|
+
|
111
|
+
uploader.store!(file)
|
112
|
+
|
113
|
+
model.processed # true
|
114
|
+
model.a_processed # true
|
115
|
+
model.a_b_processed # true
|
116
|
+
|
117
|
+
model.a_processed = false
|
118
|
+
|
119
|
+
uploader.processed # true
|
120
|
+
uploader.a_processed # false
|
121
|
+
uploader.a_b_processed # true
|
122
|
+
|
123
|
+
When model is mounted to uploader:
|
124
|
+
|
125
|
+
1. If attribute is assigned inside uploader then corresponding property in model is also assigned.
|
126
|
+
2. If attribute is retrieved from uploader instance first checks that value is defined in model and returns it. Otherwise returns uploader's instance variable.
|
127
|
+
3. If file is deleted, value becomes nil.
|
128
|
+
|
129
|
+
Otherwise acts as regular uploader's instance variables.
|
130
|
+
|
131
|
+
This is very useful for:
|
132
|
+
|
133
|
+
= Integrating CarrierWave with JCrop
|
134
|
+
|
135
|
+
Let implement the behavior like at this demo: http://deepliquid.com/projects/Jcrop/demos.php?demo=thumbnail
|
136
|
+
|
137
|
+
The uploader:
|
138
|
+
|
139
|
+
class CropUploader < SobakaUploader
|
140
|
+
include CarrierWave::Meta
|
141
|
+
|
142
|
+
# Crop source is a source image converted from original which could be bigger than source area (left image in the example).
|
143
|
+
version :crop_source do
|
144
|
+
process :resize_to_fit => [300, 300]
|
145
|
+
process :store_meta
|
146
|
+
|
147
|
+
# This is the cropped version of parent image. Let crop to 50x50 square.
|
148
|
+
version :crop do
|
149
|
+
process :crop_to => [50, 50]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Defines crop area dimensions.
|
154
|
+
# This should be assigned before #store! and #cache! called and should be saved in the model's instance.
|
155
|
+
# Otherwise cropped image would be lost after #recreate_versions! is called.
|
156
|
+
# If crop area dimensions are'nt assigned, uploader calculates crop area dimensions inside the
|
157
|
+
# parent image and creates the default image.
|
158
|
+
model_delegate_attribute :x
|
159
|
+
model_delegate_attribute :y
|
160
|
+
model_delegate_attribute :w
|
161
|
+
model_delegate_attribute :h
|
162
|
+
|
163
|
+
# Crop processor
|
164
|
+
def crop_to(width, height)
|
165
|
+
# Checks that crop area is defined and crop should be done.
|
166
|
+
if ((crop_args[0] == crop_args[2]) || (crop_args[1] == crop_args[3]))
|
167
|
+
# If not creates default image and saves it's dimensions.
|
168
|
+
resize_to_fill_and_save_dimensions(width, height)
|
169
|
+
else
|
170
|
+
args = crop_args + [width, height]
|
171
|
+
crop_and_resize(*args)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def crop_and_resize(x, y, width, height, new_width, new_height)
|
176
|
+
manipulate! do |img|
|
177
|
+
cropped_img = img.crop(x, y, width, height)
|
178
|
+
new_img = cropped_img.resize_to_fill(new_width, new_height)
|
179
|
+
destroy_image(cropped_img)
|
180
|
+
destroy_image(img)
|
181
|
+
new_img
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Creates the default crop image.
|
186
|
+
# Here the original crop area dimensions are restored and assigned to the model's instance.
|
187
|
+
def resize_to_fill_and_save_dimensions(new_width, new_height)
|
188
|
+
manipulate! do |img|
|
189
|
+
width, height = img.columns, img.rows
|
190
|
+
new_img = img.resize_to_fill(new_width, new_height)
|
191
|
+
destroy_image(img)
|
192
|
+
|
193
|
+
w_ratio = width.to_f / new_width.to_f
|
194
|
+
h_ratio = height.to_f / new_height.to_f
|
195
|
+
|
196
|
+
ratio = [w_ratio, h_ratio].min
|
197
|
+
|
198
|
+
self.w = ratio * new_width
|
199
|
+
self.h = ratio * new_height
|
200
|
+
self.x = (width - self.w) / 2
|
201
|
+
self.y = (height - self.h) / 2
|
202
|
+
|
203
|
+
new_img
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
def crop_args
|
209
|
+
%w(x y w h).map { |accessor| send(accessor).to_i }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Post should have :crop_source_version_x, :crop_source_version_y, :crop_source_version_h, :crop_source_version_w columns
|
214
|
+
class Post < ActiveRecord::Base
|
215
|
+
mount_uploader CropUploader, :image
|
216
|
+
end
|
217
|
+
|
218
|
+
# Let's upload an image
|
219
|
+
post = Post.new
|
220
|
+
post.image = params[:image] # Let the uploaded file is 800x600 JPEG
|
221
|
+
post.save!
|
222
|
+
|
223
|
+
post.image.crop_source.width # 300
|
224
|
+
post.image.crop_source.height # 200
|
225
|
+
post.image.crop_source.crop.width # 50
|
226
|
+
post.image.crop_source.crop.height # 50
|
227
|
+
|
228
|
+
# Default crop area coordinates within the limits of big image dimensions: square at the center of an image
|
229
|
+
post.image.crop_source.crop.x # 50
|
230
|
+
post.image.crop_source.crop.y # 50
|
231
|
+
post.image.crop_source.crop.w # 200
|
232
|
+
post.image.crop_source.crop.h # 200
|
233
|
+
|
234
|
+
# Let user change the crop area with JCrop script. Pass new crop area parameters to the model.
|
235
|
+
post.crop_source_crop_x = 100
|
236
|
+
post.crop_source_crop_y = 100
|
237
|
+
post.crop_source_crop_w = 100
|
238
|
+
post.crop_source_crop_h = 100
|
239
|
+
|
240
|
+
post.save! # Crop image is reprocessed
|
241
|
+
|
242
|
+
post.image.crop_source.crop.width # 50
|
243
|
+
post.image.crop_source.crop.height # 50
|
87
244
|
|
88
|
-
|
245
|
+
= A note about testing
|
89
246
|
|
90
|
-
|
247
|
+
fschwahn added support for mini-magick. To run tests with mini-magick do:
|
91
248
|
|
249
|
+
bundle exec rake spec PROCESSOR=mini_magick
|
92
250
|
|
93
251
|
= TODO
|
94
252
|
|
95
253
|
1. I do not know how it would work with S3 and other remote storages. Should be tested.
|
96
254
|
2. Write integration guide for JCrop.
|
97
255
|
3. A notice about content-type.
|
98
|
-
4.
|
256
|
+
4. Improve my english skills.
|
data/carrierwave-meta.gemspec
CHANGED
@@ -20,8 +20,10 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_dependency(%q<carrierwave>, [">= 0.5.7"])
|
22
22
|
s.add_dependency(%q<activesupport>, [">= 3.0"])
|
23
|
+
s.add_dependency(%q<mime-types>)
|
23
24
|
s.add_development_dependency(%q<rspec-rails>, ">= 2.6")
|
24
25
|
s.add_development_dependency(%q<sqlite3-ruby>)
|
25
26
|
s.add_development_dependency(%q<rmagick>)
|
27
|
+
s.add_development_dependency(%q<mini_magick>)
|
26
28
|
s.add_development_dependency(%q<mime-types>)
|
27
29
|
end
|
data/lib/carrierwave-meta.rb
CHANGED
@@ -37,6 +37,10 @@ module CarrierWave
|
|
37
37
|
set_content_type(true)
|
38
38
|
end
|
39
39
|
|
40
|
+
def image_size_s
|
41
|
+
image_size.join('x')
|
42
|
+
end
|
43
|
+
|
40
44
|
private
|
41
45
|
def call_store_meta(file = nil)
|
42
46
|
# Re-retrieve metadata for a file only if model is not present.
|
@@ -47,9 +51,12 @@ module CarrierWave
|
|
47
51
|
[].tap do |size|
|
48
52
|
if self.file.content_type =~ /image/
|
49
53
|
manipulate! do |img|
|
50
|
-
if img.is_a?(::Magick::Image)
|
54
|
+
if defined?(::Magick::Image) && img.is_a?(::Magick::Image)
|
51
55
|
size << img.columns
|
52
56
|
size << img.rows
|
57
|
+
elsif defined?(::MiniMagick::Image) && img.is_a?(::MiniMagick::Image)
|
58
|
+
size << img["width"]
|
59
|
+
size << img["height"]
|
53
60
|
else
|
54
61
|
raise "Only RMagick is supported yet. Fork and update it."
|
55
62
|
end
|
data/spec/modules/meta_spec.rb
CHANGED
@@ -13,14 +13,16 @@ describe TestUploader do
|
|
13
13
|
uploader.width.should eq(ORIGINAL_SIZE[0])
|
14
14
|
uploader.height.should eq(ORIGINAL_SIZE[1])
|
15
15
|
uploader.image_size.should eq(ORIGINAL_SIZE)
|
16
|
-
uploader.content_type.should eq(
|
16
|
+
uploader.content_type.should eq(MIME::Types.type_for(file_name).first.to_s)
|
17
17
|
uploader.file_size.should_not be_blank
|
18
|
+
uploader.image_size_s.should eq(ORIGINAL_SIZE.join('x'))
|
18
19
|
|
19
20
|
uploader.version.width.should eq(VERSION_SIZE[0])
|
20
21
|
uploader.version.height.should eq(VERSION_SIZE[1])
|
21
22
|
uploader.version.image_size.should eq(VERSION_SIZE)
|
22
|
-
uploader.version.content_type.should eq(
|
23
|
+
uploader.version.content_type.should eq(MIME::Types.type_for(file_name).first.to_s)
|
23
24
|
uploader.version.file_size.should_not be_blank
|
25
|
+
uploader.version.image_size_s.should eq(VERSION_SIZE.join('x'))
|
24
26
|
end
|
25
27
|
|
26
28
|
def obj_values_are_correct(obj)
|
@@ -37,11 +39,6 @@ describe TestUploader do
|
|
37
39
|
obj.image_version_file_size.should_not be_blank
|
38
40
|
end
|
39
41
|
|
40
|
-
def uploader_values_are_correct(obj)
|
41
|
-
obj.width.should eq(ORIGINAL_SIZE[0])
|
42
|
-
obj.height.should eq(ORIGINAL_SIZE[1])
|
43
|
-
end
|
44
|
-
|
45
42
|
context "detached uploader" do
|
46
43
|
it "stores metadata after cache!" do
|
47
44
|
uploader = TestUploader.new
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: carrierwave-meta
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Victor Sokolov
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-10-14 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: carrierwave
|
@@ -49,9 +49,23 @@ dependencies:
|
|
49
49
|
type: :runtime
|
50
50
|
version_requirements: *id002
|
51
51
|
- !ruby/object:Gem::Dependency
|
52
|
-
name:
|
52
|
+
name: mime-types
|
53
53
|
prerelease: false
|
54
54
|
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: rspec-rails
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
55
69
|
none: false
|
56
70
|
requirements:
|
57
71
|
- - ">="
|
@@ -62,11 +76,11 @@ dependencies:
|
|
62
76
|
- 6
|
63
77
|
version: "2.6"
|
64
78
|
type: :development
|
65
|
-
version_requirements: *
|
79
|
+
version_requirements: *id004
|
66
80
|
- !ruby/object:Gem::Dependency
|
67
81
|
name: sqlite3-ruby
|
68
82
|
prerelease: false
|
69
|
-
requirement: &
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
70
84
|
none: false
|
71
85
|
requirements:
|
72
86
|
- - ">="
|
@@ -76,11 +90,11 @@ dependencies:
|
|
76
90
|
- 0
|
77
91
|
version: "0"
|
78
92
|
type: :development
|
79
|
-
version_requirements: *
|
93
|
+
version_requirements: *id005
|
80
94
|
- !ruby/object:Gem::Dependency
|
81
95
|
name: rmagick
|
82
96
|
prerelease: false
|
83
|
-
requirement: &
|
97
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
84
98
|
none: false
|
85
99
|
requirements:
|
86
100
|
- - ">="
|
@@ -90,11 +104,25 @@ dependencies:
|
|
90
104
|
- 0
|
91
105
|
version: "0"
|
92
106
|
type: :development
|
93
|
-
version_requirements: *
|
107
|
+
version_requirements: *id006
|
108
|
+
- !ruby/object:Gem::Dependency
|
109
|
+
name: mini_magick
|
110
|
+
prerelease: false
|
111
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
hash: 3
|
117
|
+
segments:
|
118
|
+
- 0
|
119
|
+
version: "0"
|
120
|
+
type: :development
|
121
|
+
version_requirements: *id007
|
94
122
|
- !ruby/object:Gem::Dependency
|
95
123
|
name: mime-types
|
96
124
|
prerelease: false
|
97
|
-
requirement: &
|
125
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
98
126
|
none: false
|
99
127
|
requirements:
|
100
128
|
- - ">="
|
@@ -104,7 +132,7 @@ dependencies:
|
|
104
132
|
- 0
|
105
133
|
version: "0"
|
106
134
|
type: :development
|
107
|
-
version_requirements: *
|
135
|
+
version_requirements: *id008
|
108
136
|
description: ""
|
109
137
|
email:
|
110
138
|
- gzigzigzi@gmail.com
|