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 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