couch_photo 0.0.7 → 1.0.0.beta.1

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.markdown CHANGED
@@ -4,15 +4,74 @@ A mixin for auto-generating image variations on Image documents with CouchDB.
4
4
 
5
5
  ##Installation
6
6
 
7
+ ```sh
7
8
  $ gem install couch_photo
8
-
9
+ ```
9
10
  ## Documentation
10
11
 
11
- Browse the documentation on rdoc.info: http://rdoc.info/github/moonmaster9000/couch_photo
12
+ Browse the documentation on rdoc.info: http://rdoc.info/gems/couch_photo/frames
12
13
 
13
14
  ##How does it work?
14
15
 
15
- Just "include CouchPhoto" in your "CouchRest::Model::Base" classes and define some variations.
16
+ Just "include CouchPhoto" in your "CouchRest::Model::Base" classes and you can start creating image documents and autogenerating variations with ease.
17
+
18
+ ### The original image
19
+
20
+ Each image document is centered around the concept of the "original" image. This is the original, unpolluted image that forms the core of the document.
21
+
22
+ You can use the `load_original_from_file` method on an instance of your `Image` class to create an image document with an original image from your filesystem:
23
+
24
+ ```ruby
25
+ class Image < CouchRest::Model::Base
26
+ include CouchPhoto
27
+ end
28
+
29
+ @image = Image.new
30
+ @image.load_original_from_file "/path/to/my_file.jpg"
31
+ @image.save
32
+ ```
33
+
34
+ Here, we've created a new image instance, then added an "original" image to it via the `load_original_from_file` method by specifiying a file on the disk. Upon `save`, this file is read from disk and stored in CouchDB. We could now access our original image thusly:
35
+
36
+ ```ruby
37
+ @image.original.original_filename # ==> "my_file.jpg"
38
+ @image.original.path # ==> "/your_image_database/8383830jlkfdjskalfjdirewio/variations/original.jpg"
39
+ @image.original.url # ==> "http://your_couch_server/your_image_database/8383830jlkfdjskalfjdirewio/variations/original.jpg"
40
+ @image.original.extension # ==> "jpg"
41
+ @image.original.mimetype # ==> "image/jpg"
42
+ @image.original.data # ==> BINARY BLOB
43
+ @image.original.width # ==> 720
44
+ @image.original.height # ==> 480
45
+ ```
46
+
47
+ Suppose the original image isn't on file, but simply in memory (perhaps this was a form upload). You can use the `load_original` method to pass a raw binary blob via the `:data` parameter:
48
+
49
+ ```ruby
50
+ @image.load_original :filename => "my_file.jpg", :data => File.read("/path/to/my_file.jpg")
51
+ ```
52
+
53
+
54
+ ### The `override_id!` method
55
+
56
+ Instead of letting CouchDB generate a UUID for your image documents, you'll likely prefer that the basename of the "original" become the ID of your document.
57
+
58
+ You can accomplish this by simply calling the `override_id!` method inside your class definition:
59
+
60
+ ```ruby
61
+ class Image < CouchRest::Model::Base
62
+ include CouchPhoto
63
+ override_id!
64
+ end
65
+ ```
66
+
67
+ Now, if you create a new image document with an original, you'll have a much more human readable path to the original image on your CouchDB server:
68
+
69
+ ```ruby
70
+ @image = Image.new
71
+ @image.load_original_from_file "/path/to/my_file.jpg"
72
+ @image.save
73
+ @image.original.url #==> http://your_couch_server/your_image_database/my_file.jpg/variations/original.jpg
74
+ ```
16
75
 
17
76
  ###Simple Variations
18
77
 
@@ -20,77 +79,64 @@ Let's imagine we want to auto-generate small, medium, and large variations of ou
20
79
 
21
80
  First, define an Image document and set the versions on it:
22
81
 
23
- class Image < CouchRest::Model::Base
24
- use_database IMAGE_DB
25
- include CouchPhoto
82
+ ```ruby
83
+ class Image < CouchRest::Model::Base
84
+ use_database IMAGE_DB
85
+ include CouchPhoto
26
86
 
27
- override_id! # the id of the document will be the basename of the original image file
87
+ override_id! # the id of the document will be the basename of the original image file
28
88
 
29
- variations do
30
- small "20x20"
31
- medium "100x100"
32
- large "500x500"
33
- end
34
- end
89
+ variations do
90
+ small "20x20"
91
+ medium "100x100"
92
+ large "500x500"
93
+ end
94
+ end
95
+ ```
35
96
 
36
97
  Next, create an instance of the image document and set the "original" image on it.
37
98
 
38
- i = Image.new
39
- i.original = "avatar.jpg"
40
- i.save
41
-
42
- By calling the original method with a string, CouchPhoto will attempt to open a file with the same name and use that as the original image. If you don't have
43
- an actual file (e.g., maybe this was a form upload), then simply pass a blob as the second parameter. For example,
99
+ ```ruby
100
+ i = Image.new
101
+ i.load_original_from_file "/path/to/avatar.jpg"
102
+ i.save
103
+ ```
44
104
 
45
- blob = File.read "avatar.jpg"
46
- i.original = ["avatar.jpg", blob]
47
-
48
- Now, if you look in your image document in CouchDB, you'd see the following attachments:
105
+ Now, if you look in your image document in CouchDB, you'll see the following attachments:
49
106
 
50
107
  http://your_couch_server/your_image_database/avatar.jpg/original.jpg
51
108
  http://your_couch_server/your_image_database/avatar.jpg/variations/small.jpg
52
109
  http://your_couch_server/your_image_database/avatar.jpg/variations/medium.jpg
53
110
  http://your_couch_server/your_image_database/avatar.jpg/variations/large.jpg
54
111
 
55
- Notice that, because we called the `override_id!` method, `CouchPhoto` set the ID of your image document to the basename of your original image.
56
112
 
57
113
  ###Complex Variations
58
114
 
59
115
  The previous variations were all simple image resizings. What if we wanted to do something a little more complex? Like grayscale the original image? Or create a watermark? No prob!
60
116
 
61
- class Image < CouchRest::Model::Base
62
- use_database IMAGE_DB
63
- include CouchPhoto
117
+ ```ruby
118
+ class Image < CouchRest::Model::Base
119
+ use_database IMAGE_DB
120
+ include CouchPhoto
64
121
 
65
- override_id! # the id of the document will be the basename of the original image file
122
+ override_id! # the id of the document will be the basename of the original image file
66
123
 
67
- variations do
68
- grayscale do |original_image|
69
- original_image.monochrome
70
- end
124
+ variations do
125
+ grayscale do |original_image|
126
+ original_image.monochrome
127
+ end
71
128
 
72
- watermark do |original_image|
73
- original_image.composite(MiniMagick::Image.open("watermark.png", "jpg") do |c|
74
- c.gravity "center"
75
- end
76
- end
129
+ watermark do |original_image|
130
+ original_image.composite(MiniMagick::Image.open("watermark.png", "jpg") do |c|
131
+ c.gravity "center"
77
132
  end
78
133
  end
134
+ end
135
+ end
136
+ ```
79
137
 
80
138
  The `original_image` variable in the blocks is simply the MiniMagick::Image instance of your original image.
81
139
 
82
- ### Accessing the Original Image
83
-
84
- How do you access the the original image? Let me count the ways:
85
-
86
- @image.original.original_filename # ==> "avatar.jpg"
87
- @image.original.filename # ==> "original.jpg"
88
- @image.original.path # ==> "/your_image_database/avatar.jpg/original.jpg"
89
- @image.original.url # ==> "http://your_couch_server/your_image_database/avatar.jpg/original.jpg"
90
- @image.original.basename # ==> "original.jpg"
91
- @image.original.filetype # ==> "jpg"
92
- @image.original.mimetype # ==> "image/jpg"
93
- @image.original.data # ==> BINARY BLOB
94
140
 
95
141
  ### Accessing Variations
96
142
 
@@ -98,84 +144,136 @@ So, now that you've got some variations, how do you access them? Simple!
98
144
 
99
145
  Let's go back to our original image example:
100
146
 
101
- class Image < CouchRest::Model::Base
102
- use_database IMAGE_DB
103
- include CouchPhoto
147
+ ```ruby
148
+ class Image < CouchRest::Model::Base
149
+ include CouchPhoto
104
150
 
105
- override_id! # the id of the document will be the basename of the original image file
151
+ override_id!
106
152
 
107
- variations do
108
- small "20x20"
109
- medium "100x100"
110
- large "500x500"
111
- end
112
- end
153
+ variations do
154
+ small "20x20"
155
+ medium "100x100"
156
+ large "500x500"
157
+ end
158
+ end
159
+
160
+ i = Image.new
161
+ i.load_original_from_file "/path/to/couchdb.jpg"
162
+ i.save
163
+ ```
113
164
 
114
- i = Image.new
115
- i.original = "avatar.jpg"
116
- i.save
165
+ The `variations` method on our model is a hash of all the variations; the keys are the variation names, and the values are a variation object:
117
166
 
118
167
  We can access our variations in one of three ways:
119
168
  - the `variations` method, which will allow you to iterate over the variation_name/variation pairs via the `each` method
120
169
  - the `variation.<variation_name>` method, which returns a specific variation
121
- - the `variation("<variation_name>")`, which also returns a specific variation
170
+ - the `variation[:<variation_name>]`, which also returns a specific variation
122
171
 
123
172
  For example:
124
-
125
- i.variations.each do |variation_name, variation_image|
126
- puts variation_name
127
- puts variation.name
128
- puts variation.filename
129
- # etc...
130
- end
131
173
 
132
- i.variation.small
133
- i.variation "large"
174
+ ```ruby
175
+ # iterate over all variations
176
+ i.variations.each do |variation_name, variation_image|
177
+ puts variation_name
178
+ puts variation.filename
179
+ # etc...
180
+ end
181
+
182
+ # access the "small" variation
183
+ i.variations.small
184
+
185
+ # access the "large" variation
186
+ i.variations["large"]
187
+ #or...
188
+ i.variations[:large]
189
+ ```
134
190
 
135
191
  Once you've got a variation, you can call several methods on it:
136
192
 
137
- i.variation.small.name # ==> "small"
138
- i.variation.small.path # ==> "http://your_couch_server/your_image_database/avatar.jpg/variations/small.jpg"
139
- i.variation.small.filename # ==> "variations/small.jpg"
140
- i.variation.small.basename # ==> "small.jpg"
141
- i.variation.small.filetype # ==> "jpg"
142
- i.variation.small.mimetype # ==> "image/jpg"
143
- i.variation.small.data # ==> BINARY BLOB
193
+ ```ruby
194
+ i.variations[:small].name # ==> "small"
195
+ i.variations[:small].path # ==> "/your_image_database/avatar.jpg/variations/small.jpg"
196
+ i.variations[:small].url # ==> "http://your.couch.server/your_image_database/avatar.jpg/variations/small.jpg"
197
+ i.variations[:small].width # ==> 50
198
+ i.variations[:small].height # ==> 50
199
+ i.variations[:small].basename # ==> "small.jpg"
200
+ i.variations[:small].filetype # ==> "jpg"
201
+ i.variations[:small].mimetype # ==> "image/jpg"
202
+ i.variations[:small].data # ==> BINARY BLOB
203
+ ```
144
204
 
145
205
  ## Creating Custom Variations
146
206
 
147
- If you'd like to add a variation to your image document that's not predefined / autogenerated, you can use the add_variation method:
207
+ If you'd like to add a variation to your image document that's not predefined / autogenerated, you can use the `load_custom_variation` and `load_custom_variation_from_file` methods:
148
208
 
149
- i = Image.new
150
- i.original = "somefile.jpg"
151
- i.add_variation "variation_name.jpg", :file => "filename.jpg"
152
- i.add_variation "variation/name.jpg", :blob => File.read("filename.jpg")
209
+ ```ruby
210
+ i = Image.new
211
+ i.load_original_from_file "/path/to/somefile.jpg"
212
+ i.load_custom_variation_from_file "/path/to/small_watermark.jpg"
213
+ i.load_custom_variation :filename => "large_greyscale.jpg", :data => File.read("/path/to/some_image.jpg")
214
+ i.save
215
+ ```
153
216
 
154
- These images would be accessible via the following urls :
217
+ These images would be accessible via the following urls:
155
218
 
156
- http://your_couch_server/your_image_database/somefile.jpg/original.jpg
157
- http://your_couch_server/your_image_database/somefile.jpg/variations/variation_name.jpg
158
- http://your_couch_server/your_image_database/somefile.jpg/variations/variation/name.jpg
219
+
220
+ http://your_couch_server/your_image_database/somefile.jpg/variations/original.jpg
221
+ http://your_couch_server/your_image_database/somefile.jpg/variations/small_watermark.jpg
222
+ http://your_couch_server/your_image_database/somefile.jpg/variations/large_watermark.jpg
159
223
 
224
+
160
225
  ## XMP Metadata
161
- Need to access XMP Metadata? It's as simple as adding xmp_metadata! into your document definition.
162
226
 
163
- class Image < CouchRest::Model::Base
164
- use_database IMAGE_DB
165
- include CouchPhoto
227
+ Need to access the XMP Metadata on your original? It's as simple as adding `extract_xmp_metadata!` into your document definition.
166
228
 
167
- xmp_metadata!
168
- override_id! # the id of the document will be the basename of the original image file
229
+ ```ruby
230
+ class Image < CouchRest::Model::Base
231
+ use_database IMAGE_DB
232
+ include CouchPhoto
169
233
 
170
- variations do
171
- small "20x20"
172
- medium "100x100"
173
- large "500x500"
174
- end
234
+ extract_xmp_metadata!
235
+ override_id! # the id of the document will be the basename of the original image file
236
+
237
+ variations do
238
+ small "20x20"
239
+ medium "100x100"
240
+ large "500x500"
241
+ end
242
+ end
243
+
244
+ i = Image.new
245
+ i.load_original_from_file "avatar.jpg"
246
+ i.save
247
+ ```
248
+
249
+ Now you can access your image's XMP metadata as a hash by calling `i.xmp_metadata`.
250
+
251
+
252
+ ## Adding extra properties to your variations
253
+
254
+ Perhaps you'd like to add some extra properties that you can save for each of your image variations, like "alt" text, or captions. Simply use the `metadata` hash on your variations.
255
+
256
+ For example, given the following definition:
257
+
258
+ ```ruby
259
+ class Image < CouchRest::Model::Base
260
+ use_database IMAGE_DB
261
+ include CouchPhoto
262
+
263
+ variations do
264
+ thumbnail "20x20"
265
+ grayscale do |original_image|
266
+ original_image.monochrome
175
267
  end
176
-
177
- i = Image.new
178
- i.original = "avatar.jpg"
179
- i.save
180
-
181
- Now you can access your image's XMP metadata by calling i.metadata.
268
+ end
269
+ end
270
+ ```
271
+
272
+ You could now set any metadata properties you desire on your variations:
273
+
274
+ ```ruby
275
+ @image = Image.first
276
+ @image.variations[:small].metadata["alt"] = "alt text"
277
+ @image.variations[:grayscale].metadata["caption"] = "Grayscale version of: #{@image.original.url}"
278
+ @image.save
279
+ ```
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.7
1
+ 1.0.0.beta.1
data/couch_photo.gemspec CHANGED
@@ -16,4 +16,5 @@ Gem::Specification.new do |s|
16
16
  s.add_dependency "couchrest", "1.0.1"
17
17
  s.add_dependency "mini_magick"
18
18
  s.add_dependency "couchrest_model", "~> 1.0.0"
19
+ s.add_dependency "activesupport", "~> 3.0.0"
19
20
  end
data/lib/couch_photo.rb CHANGED
@@ -1,5 +1,10 @@
1
1
  require 'mini_magick'
2
2
  require 'couchrest_model'
3
- require 'couch_photo/variations'
4
- require 'couch_photo/attachment'
3
+ require 'couch_photo/variation_metadata'
4
+ require 'couch_photo/custom_variation'
5
5
  require 'couch_photo/couch_photo'
6
+ require 'couch_photo/fake_file'
7
+ require 'couch_photo/original'
8
+ require 'couch_photo/variation_definition'
9
+ require 'couch_photo/variation_config'
10
+ require 'couch_photo/variation'
@@ -1,119 +1,114 @@
1
1
  module CouchPhoto
2
2
  def self.included(base)
3
- base.extend ClassMethods
4
3
  base.property :original_filename
4
+ base.property :variation_metadata, Hash, :default => {}
5
+ base.extend ClassMethods
6
+ base.before_create :generate_variations
5
7
  end
6
8
 
7
- def update_or_create_attachment(attachment)
8
- if self.has_attachment? attachment[:name]
9
- self.update_attachment attachment
10
- else
11
- self.create_attachment attachment
9
+ module ClassMethods
10
+ def override_id!
11
+ self.before_create :set_id_to_original_filename
12
+ end
13
+
14
+ def extract_xmp_metadata!
15
+ @extract_xmp_metadata = true
16
+ self.property :xmp_metadata, Hash, :default => nil
17
+ end
18
+
19
+ def variations(&block)
20
+ @variation_config ||= CouchPhoto::VariationConfig.new
21
+ @variation_config.instance_eval &block if block
22
+ @variation_config
23
+ end
24
+
25
+ def extract_xmp_metadata?
26
+ @extract_xmp_metadata
12
27
  end
13
28
  end
14
29
 
15
- def variations
16
- @variations = Variations.new self
30
+ def load_original_from_file(filepath)
31
+ original.create_attachment File.basename(filepath), File.read(filepath)
17
32
  end
18
33
 
19
- def original
20
- raise "You do not have an original attachment" unless original_attachment_name
21
- @original ||= Variation.new self, original_attachment_name
34
+ def load_original(options)
35
+ raise "You must provide a :filename parameter" unless options[:filename]
36
+ raise "You must provide a :data parameter" unless options[:data]
37
+
38
+ original.create_attachment options[:filename], options[:data]
22
39
  end
23
40
 
24
- def original_attachment_name
25
- @original_attachment_name ||= self["_attachments"].keys.select {|a| a.match /original\.[^\.]+/}.first
41
+ def load_custom_variation_from_file(filepath)
42
+ variation_name = File.basename filepath
43
+ variations[variation_name] = CouchPhoto::CustomVariation.new self, variation_name
44
+ variations[variation_name].create_attachment File.read(filepath)
26
45
  end
27
46
 
28
- def variation(variation_name=nil)
29
- variation_name ? variations.send(variation_name) : variations
47
+ def load_custom_variation(variation_name, blob)
48
+ variations[variation_name] = CouchPhoto::CustomVariation.new self, variation_name
49
+ variations[variation_name].create_attachment blob
30
50
  end
31
51
 
32
- def original=(*args)#filepath, blob=nil
33
- if args[0].kind_of?(Array)
34
- filepath, blob = args[0].first, args[0].last
35
- else
36
- filepath = args[0]
37
- blob = nil
38
- end
39
- image_format = filetype(filepath)
40
- filename = File.basename filepath
41
- basename = File.basename(filepath, "." + "#{image_format}")
42
-
43
- if self.class.xmp_metadata?
44
- if blob
45
- File.open("/tmp/#{filename}", 'w') { |f| f.write blob } if !blob.nil?
46
- `convert /tmp/#{filename} /tmp/#{basename}.xmp`
47
- else
48
- `convert #{filepath} /tmp/#{basename}.xmp`
49
- end
50
- self.metadata = Hash.from_xml File.read("/tmp/#{basename}.xmp")
51
- File.delete("/tmp/#{basename}.xmp")
52
- end
52
+ def original
53
+ @original ||= CouchPhoto::Original.new self
54
+ end
53
55
 
54
- self["_id"] = filename if self.class.override_id?
55
- self.original_filename = filename
56
- blob ||= File.read filepath
57
- attachment_name = "original.#{image_format}"
58
- attachment = {:name => attachment_name, :file => Attachment.new(blob, attachment_name)}
59
- update_or_create_attachment attachment
60
- self.class.variation_definitions.run_variations(blob).each do |variation_name, variation_blob|
61
- update_or_create_attachment :name => "variations/#{variation_name}.#{image_format}", :file => Attachment.new(variation_blob, attachment_name)
62
- end
56
+ def variations
57
+ @variations ||= load_variations
63
58
  end
64
-
65
- def add_variation(variation_name, opt={})
66
- if opt[:file]
67
- blob = File.read("#{opt[:file]}")
68
- elsif opt[:blob]
69
- blob = opt[:blob]
59
+
60
+ private
61
+ def load_variations
62
+ all_variations = {}
63
+
64
+ # setup defined variations
65
+ defined_variation_names.each do |defined_variation_name|
66
+ all_variations[defined_variation_name.to_sym] = CouchPhoto::Variation.new self, defined_variation_name
67
+ end
68
+
69
+ # setup custom variations
70
+ custom_variation_names.each do |custom_variation_name|
71
+ all_variations[custom_variation_name] = CouchPhoto::CustomVariation.new self, custom_variation_name
70
72
  end
71
- update_or_create_attachment :name => "variations/#{variation_name}", :file => Attachment.new(blob, variation_name)
72
- variations.add_variation variation_name
73
+
74
+ all_variations
73
75
  end
74
76
 
75
- def filetype(filename)
76
- filename.match(/^.*\.([^\.]*)$/)[1]
77
+ def defined_variation_names
78
+ self.class.variations.variation_definitions.keys.map(&:to_s)
77
79
  end
78
80
 
79
- module ClassMethods
80
- def variations(&block)
81
- raise "You must pass a block to the `variations' method." unless block
82
- variation_definitions.instance_eval(&block)
83
- end
84
-
85
- def override_id!
86
- @override_id = true
87
- end
88
-
89
- def xmp_metadata!
90
- @xmp_metadata = true
91
- self.property :metadata, Hash
92
- end
93
-
94
- def xmp_metadata?
95
- @xmp_metadata
96
- end
97
-
98
- def override_id?
99
- @override_id
81
+ def custom_variation_names
82
+ all_variation_names.select do |variation_name|
83
+ !defined_variation_names.include?(strip_extension(variation_name))
100
84
  end
85
+ end
101
86
 
102
- def variation_definitions
103
- @variation_definitions ||= VariationDefinitions.new
87
+ def all_variation_names
88
+ if self["_attachments"]
89
+ self["_attachments"].keys.
90
+ select {|attachment_name| attachment_name.match /variations\/.*/}.
91
+ map {|attachment_name| attachment_name.gsub(/^variations\/(.*)$/) { $1 }}
92
+ else
93
+ []
104
94
  end
105
95
  end
106
-
107
- module_function
108
- def variation_short_name(variation_path)
109
- variation_path.gsub(/(?:variations\/)?(.+)\.[^\.]+/) {$1}
96
+
97
+ def strip_extension(filename)
98
+ filename.gsub(/\.[^.]*/, '')
110
99
  end
111
-
112
- def variation_short_name_from_path(variation_path)
113
- variation_path.gsub(/(?:variations\/)?(.+)/) {$1}
100
+
101
+ def set_id_to_original_filename
102
+ if self.original_filename.blank?
103
+ self.errors.add :original, "must be set before create"
104
+ else
105
+ self.id = self.original_filename
106
+ end
114
107
  end
115
-
116
- def variation_file_extension(variation_path)
117
- variation_path.gsub(/(?:variations\/)?.+\.([^\.]+)/) {$1}
108
+
109
+ def generate_variations
110
+ self.class.variations.variation_definitions.each do |variation_name, variation_definition|
111
+ variation_definition.generate_variation self
112
+ end
118
113
  end
119
114
  end
@@ -0,0 +1,20 @@
1
+ module CouchPhoto
2
+ class CustomVariation
3
+ include VariationMetadata
4
+
5
+ def initialize(document, variation_name)
6
+ @document = document
7
+ @variation_name = variation_name
8
+ end
9
+
10
+ def create_attachment(blob)
11
+ @data = blob
12
+ @document.create_attachment :name => attachment_name, :file => FakeFile.new(@data)
13
+ end
14
+
15
+ private
16
+ def attachment_name
17
+ "variations/#{@variation_name}"
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  module CouchPhoto
2
- class Attachment #:nodoc:
2
+ class FakeFile
3
3
  attr_reader :read, :path
4
4
  def initialize(data, path=nil)
5
5
  @read = data
@@ -0,0 +1,32 @@
1
+ module CouchPhoto
2
+ class Original
3
+ include VariationMetadata
4
+
5
+ def initialize(document)
6
+ @document = document
7
+ end
8
+
9
+ def create_attachment(filename, data)
10
+ extension = File.extname filename
11
+ @data = data
12
+ @attachment_name = "variations/original#{extension}"
13
+ @document.create_attachment :name => @attachment_name, :file => FakeFile.new(@data)
14
+ @document.original_filename = File.basename(filename)
15
+ @document.xmp_metadata = xmp_metadata if @document.class.extract_xmp_metadata?
16
+ end
17
+
18
+ def original_filename
19
+ if exists?
20
+ @document.original_filename
21
+ end
22
+ end
23
+
24
+ private
25
+ def attachment_name
26
+ @attachment_name ||=
27
+ @document["_attachments"].keys.select do |attachment_name|
28
+ attachment_name.match /variations\/original\.[^.]*/
29
+ end.first
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,10 @@
1
+ module CouchPhoto
2
+ class Variation
3
+ include VariationMetadata
4
+
5
+ def initialize(document, variation_name)
6
+ @document = document
7
+ @variation_name = variation_name
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ module CouchPhoto
2
+ class VariationConfig
3
+ attr_reader :variation_definitions
4
+
5
+ def initialize
6
+ @variation_definitions = {}
7
+ end
8
+
9
+ def method_missing(variation_name, *args, &block)
10
+ variation_name = variation_name.to_sym
11
+ resize_definition = args.first
12
+ @variation_definitions[variation_name.to_sym] = CouchPhoto::VariationDefinition.new(
13
+ variation_name,
14
+ resize_definition,
15
+ &block
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ module CouchPhoto
2
+ class VariationDefinition
3
+ def initialize(variation_name, resize_definition=nil, &custom_definition)
4
+ @variation_name = variation_name
5
+ @resize_definition = resize_definition
6
+ @custom_definition = custom_definition
7
+ end
8
+
9
+ def generate_variation(document)
10
+ attachment_name = "variations/#{@variation_name}.#{document.original.extension}"
11
+ variation = manipulate document.original.data
12
+
13
+ if document.has_attachment?(attachment_name)
14
+ document.update_attachment :name => attachment_name, :file => FakeFile.new(variation)
15
+ else
16
+ document.create_attachment :name => attachment_name, :file => FakeFile.new(variation)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def manipulate(image_blob)
23
+ mini_magick_image = MiniMagick::Image.read(image_blob)
24
+
25
+ if @resize_definition
26
+ mini_magick_image.resize @resize_definition
27
+ elsif @custom_definition
28
+ @custom_definition.call mini_magick_image
29
+ end
30
+
31
+ mini_magick_image.to_blob
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,70 @@
1
+ module CouchPhoto
2
+ module VariationMetadata
3
+ def path
4
+ if exists?
5
+ "/#{@document.database.name}/#{@document.id}/#{attachment_name}"
6
+ end
7
+ end
8
+
9
+ def extension
10
+ if exists?
11
+ @extension ||= File.extname(attachment_name).gsub(/\./, '')
12
+ end
13
+ end
14
+
15
+ def mime_type
16
+ if exists?
17
+ @mime_type ||= @document["_attachments"][attachment_name]["content_type"]
18
+ end
19
+ end
20
+
21
+ def data
22
+ if exists?
23
+ @data ||= @document.read_attachment attachment_name
24
+ end
25
+ end
26
+
27
+ def width
28
+ mini_magick[:width]
29
+ end
30
+
31
+ def metadata
32
+ @document.variation_metadata[@variation_name.to_s] ||= {}
33
+ end
34
+
35
+ def height
36
+ mini_magick[:height]
37
+ end
38
+
39
+ def xmp_metadata
40
+ tmp_image_uuid = `uuidgen`.strip
41
+ tmp_image_file_name = "/tmp/#{tmp_image_uuid}.#{extension}"
42
+ tmp_xmp_file_name = "/tmp/#{tmp_image_uuid}.xmp"
43
+ mini_magick.write tmp_image_file_name
44
+ `convert #{tmp_image_file_name} #{tmp_xmp_file_name} 2> /dev/null`
45
+ Hash.from_xml File.read(tmp_xmp_file_name)
46
+ end
47
+
48
+ def url
49
+ if exists?
50
+ "#{@document.database.root}/#{@document.id}/#{attachment_name}"
51
+ end
52
+ end
53
+
54
+ private
55
+ def exists?
56
+ !@document["_attachments"][attachment_name].nil?
57
+ end
58
+
59
+ def mini_magick
60
+ if exists?
61
+ @mini_magick ||= MiniMagick::Image.read(self.data)
62
+ end
63
+ end
64
+
65
+ def attachment_name
66
+ "variations/#{@variation_name}.#{@document.original.extension}"
67
+ end
68
+ end
69
+ end
70
+
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couch_photo
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
5
- prerelease:
4
+ hash: 62196353
5
+ prerelease: 6
6
6
  segments:
7
+ - 1
7
8
  - 0
8
9
  - 0
9
- - 7
10
- version: 0.0.7
10
+ - beta
11
+ - 1
12
+ version: 1.0.0.beta.1
11
13
  platform: ruby
12
14
  authors:
13
15
  - Matt Parker
@@ -15,7 +17,7 @@ autorequire:
15
17
  bindir: bin
16
18
  cert_chain: []
17
19
 
18
- date: 2011-09-20 00:00:00 Z
20
+ date: 2011-09-25 00:00:00 Z
19
21
  dependencies:
20
22
  - !ruby/object:Gem::Dependency
21
23
  name: cucumber
@@ -105,6 +107,22 @@ dependencies:
105
107
  version: 1.0.0
106
108
  type: :runtime
107
109
  version_requirements: *id006
110
+ - !ruby/object:Gem::Dependency
111
+ name: activesupport
112
+ prerelease: false
113
+ requirement: &id007 !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ~>
117
+ - !ruby/object:Gem::Version
118
+ hash: 7
119
+ segments:
120
+ - 3
121
+ - 0
122
+ - 0
123
+ version: 3.0.0
124
+ type: :runtime
125
+ version_requirements: *id007
108
126
  description: Manage an image and all of its variations and xmp metadata in a single document.
109
127
  email: moonmaster9000@gmail.com
110
128
  executables: []
@@ -114,9 +132,14 @@ extensions: []
114
132
  extra_rdoc_files: []
115
133
 
116
134
  files:
117
- - lib/couch_photo/attachment.rb
118
135
  - lib/couch_photo/couch_photo.rb
119
- - lib/couch_photo/variations.rb
136
+ - lib/couch_photo/custom_variation.rb
137
+ - lib/couch_photo/fake_file.rb
138
+ - lib/couch_photo/original.rb
139
+ - lib/couch_photo/variation.rb
140
+ - lib/couch_photo/variation_config.rb
141
+ - lib/couch_photo/variation_definition.rb
142
+ - lib/couch_photo/variation_metadata.rb
120
143
  - lib/couch_photo.rb
121
144
  - VERSION
122
145
  - README.markdown
@@ -141,12 +164,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
141
164
  required_rubygems_version: !ruby/object:Gem::Requirement
142
165
  none: false
143
166
  requirements:
144
- - - ">="
167
+ - - ">"
145
168
  - !ruby/object:Gem::Version
146
- hash: 3
169
+ hash: 25
147
170
  segments:
148
- - 0
149
- version: "0"
171
+ - 1
172
+ - 3
173
+ - 1
174
+ version: 1.3.1
150
175
  requirements: []
151
176
 
152
177
  rubyforge_project:
@@ -1,103 +0,0 @@
1
- module CouchPhoto
2
-
3
-
4
- class VariationDefinitions
5
- attr_reader :variations
6
-
7
- def initialize
8
- @variations = {}
9
- end
10
-
11
- def method_missing(method_name, *args, &block)
12
- if block
13
- @variations[method_name.to_sym] = VariationDefinition.new method_name, *args, &block
14
- else
15
- @variations[method_name.to_sym] = VariationDefinition.new method_name, *args
16
- end
17
- end
18
-
19
- def run_variations(blob)
20
- results = {}
21
- @variations.each do |variation_name, variation|
22
- results[variation_name] = variation.create_variation blob
23
- end
24
- results
25
- end
26
- end
27
-
28
- class VariationDefinition
29
- def initialize(name, format=nil, &block)
30
- raise "A variation can not have both a format and a block. Choose one." if format and block
31
- raise "A variation must have either a format (e.g., '20x20>') or a block." if !format and !block
32
- @name = name
33
- @format = format
34
- @block = block
35
- end
36
-
37
- def create_variation(blob)
38
- image = MiniMagick::Image.read(blob)
39
- @format ? image.resize(@format) : @block.call(image)
40
- image.to_blob
41
- end
42
- end
43
-
44
- class Variations
45
- attr_reader :variations, :document
46
-
47
- def initialize(document)
48
- @document = document
49
- @variations = {}
50
- attachments = document["_attachments"] || {}
51
- attachments.keys.select {|name| name.match /variations\//}.each do |variation_name|
52
- if document.class.variation_definitions.variations.keys.include?(CouchPhoto.variation_short_name(variation_name).to_sym)
53
- variation_key = CouchPhoto.variation_short_name(variation_name)
54
- else
55
- variation_key = CouchPhoto.variation_short_name_from_path(variation_name)
56
- end
57
- @variations[variation_key] = Variation.new document, variation_name
58
- end
59
- end
60
-
61
- def each(&block)
62
- @variations.each &block
63
- end
64
-
65
- def count
66
- attachments = self.document["_attachments"] || {}
67
- attachments.keys.count {|name| name.match /variations\//}
68
- end
69
-
70
- def method_missing(method_name, *args, &block)
71
- raise "Unknown variation '#{method_name}'" unless @variations[method_name.to_s]
72
- @variations[method_name.to_s]
73
- end
74
-
75
- def add_variation(variation_name)
76
- @variations[variation_name] = Variation.new document, "variations/#{variation_name}"
77
- end
78
- end
79
-
80
- class Variation
81
- attr_reader :name, :url, :path, :filename, :basename, :filetype, :mimetype
82
-
83
- def initialize(document, attachment_name)
84
- @path = "/" + [document.database.name, document.id, attachment_name].join("/")
85
- @url = [document.database.to_s, document.id, attachment_name].join "/"
86
- @attachment_name = attachment_name
87
- @name = CouchPhoto.variation_short_name attachment_name
88
- @filename = attachment_name
89
- @basename = File.basename attachment_name
90
- @document = document
91
- @filetype = CouchPhoto.variation_file_extension attachment_name
92
- @mimetype = document["_attachments"][attachment_name]["content_type"]
93
- end
94
-
95
- def data
96
- @document.read_attachment @attachment_name
97
- end
98
-
99
- def original_filename
100
- @document.original_filename
101
- end
102
- end
103
- end