carrierwave-meta 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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 for it unless uploader has
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
- So, if you are storing width and height in database column and then you decided to store
86
- content type.
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
- Uploader tries to retrieve metadata from a model's column first.
245
+ = A note about testing
89
246
 
90
- = Integrating CarrierWave & JCrop using carrierwave-meta
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. It works now only with RMagick. Add support for other image libraries. It's easy.
256
+ 4. Improve my english skills.
@@ -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
@@ -1,4 +1,5 @@
1
1
  require 'active_support/concern'
2
+ require 'mime/types'
2
3
  require "carrierwave-meta/version"
3
4
  require 'carrierwave-meta/model_delegate_attribute'
4
5
  require 'carrierwave-meta/meta'
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Carrierwave
2
2
  module Meta
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -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(Mime::Types.type_for(file_name).first.to_s)
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(Mime::Types.type_for(file_name).first.to_s)
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
@@ -9,6 +9,8 @@ rescue Bundler::GemNotFound
9
9
  "Did you run `bundle install`?"
10
10
  end
11
11
 
12
+ PROCESSOR = (ENV["PROCESSOR"] || :rmagick).to_sym
13
+
12
14
  require 'mime/types'
13
15
  require 'carrierwave'
14
16
  require 'carrierwave-meta'
@@ -1,5 +1,9 @@
1
1
  class TestUploader < CarrierWave::Uploader::Base
2
- include CarrierWave::RMagick
2
+ if PROCESSOR == :mini_magick
3
+ include CarrierWave::MiniMagick
4
+ else
5
+ include CarrierWave::RMagick
6
+ end
3
7
  include CarrierWave::Meta
4
8
 
5
9
  def store_dir
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: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
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-09-09 00:00:00 Z
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: rspec-rails
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: *id003
79
+ version_requirements: *id004
66
80
  - !ruby/object:Gem::Dependency
67
81
  name: sqlite3-ruby
68
82
  prerelease: false
69
- requirement: &id004 !ruby/object:Gem::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: *id004
93
+ version_requirements: *id005
80
94
  - !ruby/object:Gem::Dependency
81
95
  name: rmagick
82
96
  prerelease: false
83
- requirement: &id005 !ruby/object:Gem::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: *id005
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: &id006 !ruby/object:Gem::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: *id006
135
+ version_requirements: *id008
108
136
  description: ""
109
137
  email:
110
138
  - gzigzigzi@gmail.com