couch_photo 0.0.7 → 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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