jeremyboles-graffic 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,21 +11,26 @@ require 'graffic/view_helpers'
11
11
  # A Graffic record progresses through four states: received, moved, uploaded and processed.
12
12
  # Graffic is designed in a way to let slow operating states out of the request cycle, if desired.
13
13
  #
14
+ # TODO: Figure out how to make this read-only
15
+ #
14
16
  class Graffic < ActiveRecord::Base
17
+ after_create :move
15
18
  after_destroy :delete_s3_file
16
19
 
17
- attr_writer :file, :processor
18
-
19
20
  before_validation_on_create :set_initial_state
20
21
 
21
22
  belongs_to :resource, :polymorphic => true
22
23
 
23
- class_inheritable_accessor :bucket_name, :processor
24
+ class_inheritable_accessor :bucket_name
24
25
  class_inheritable_accessor :format, :default => 'png'
25
26
  class_inheritable_accessor :process_queue_name, :default => 'graffic_process'
26
27
  class_inheritable_accessor :upload_queue_name, :default => 'graffic_upload'
28
+ class_inheritable_accessor :should_process_versions, :use_queue, :default => true
27
29
  class_inheritable_accessor :tmp_dir, :default => RAILS_ROOT + '/tmp/graffics'
28
- class_inheritable_hash :versions
30
+ class_inheritable_accessor :processors, :default => []
31
+ class_inheritable_accessor :versions, :default => {}
32
+
33
+ attr_writer :file, :processors
29
34
 
30
35
  validate_on_create :file_was_given
31
36
 
@@ -35,6 +40,7 @@ class Graffic < ActiveRecord::Base
35
40
  @bucket ||= Graffic::Aws.s3.bucket(bucket_name, true, 'public-read')
36
41
  end
37
42
 
43
+ # Create the tmp dir to store files until we upload them
38
44
  def create_tmp_dir
39
45
  FileUtils.mkdir(tmp_dir) unless File.exists?(tmp_dir)
40
46
  end
@@ -45,7 +51,7 @@ class Graffic < ActiveRecord::Base
45
51
  data = YAML.load(message.to_s)
46
52
  begin
47
53
  record = find(data[:id])
48
- record.process!
54
+ record.process
49
55
  rescue ActiveRecord::RecordNotFound
50
56
  return 'Not found'
51
57
  ensure
@@ -55,13 +61,14 @@ class Graffic < ActiveRecord::Base
55
61
  end
56
62
 
57
63
  # Handles the first message in the upload queue
64
+ # TODO: Figure out a better way to handle messages when records aren't there
58
65
  def handle_top_in_upload_queue!
59
66
  if message = upload_queue.receive
60
67
  data = YAML.load(message.to_s)
61
68
  return if data[:hostname] != `hostname`.strip
62
69
  begin
63
70
  record = find(data[:id])
64
- record.upload!
71
+ record.upload
65
72
  rescue ActiveRecord::RecordNotFound
66
73
  return 'Not found'
67
74
  ensure
@@ -70,10 +77,13 @@ class Graffic < ActiveRecord::Base
70
77
  end
71
78
  end
72
79
 
80
+ # DSL method for processing images
73
81
  def process(&block)
74
- self.processor = block if block_given?
82
+ self.processors ||= []
83
+ self.processors << block if block_given?
75
84
  end
76
85
 
86
+ # When we create a new type of Graffic, make sure we save the original
77
87
  def inherited(subclass)
78
88
  subclass.has_one(:original, :class_name => 'Graffic', :as => :resource, :dependent => :destroy, :conditions => { :name => 'original' })
79
89
  super
@@ -84,12 +94,10 @@ class Graffic < ActiveRecord::Base
84
94
  @process_queue ||= Graffic::Aws.sqs.queue(process_queue_name, true)
85
95
  end
86
96
 
97
+ # DSL method for making thumbnails
87
98
  def size(name, size = {})
88
99
  size.assert_valid_keys(:width, :height)
89
- version(name) do |img|
90
- img = img.first if img.respond_to?(:first)
91
- img.crop_resized(size[:width], size[:height])
92
- end
100
+ version(name) { |img| img.crop_resized(size[:width], size[:height]) }
93
101
  end
94
102
 
95
103
  # The queue for uploading images
@@ -97,12 +105,11 @@ class Graffic < ActiveRecord::Base
97
105
  @upload_queue ||= Graffic::Aws.sqs.queue(upload_queue_name, true)
98
106
  end
99
107
 
108
+ # DSL method for declaring a version of an image
100
109
  def version(name, &block)
101
- self.versions ||= {}
102
- if block_given?
103
- self.versions[name] = block || nil
104
- has_one(name, :class_name => 'Graffic', :as => :resource, :dependent => :destroy, :conditions => { :name => name.to_s })
105
- end
110
+ self.versions[name] ||= []
111
+ self.versions[name] << block if block_given?
112
+ has_one(name, :class_name => 'Graffic', :as => :resource, :dependent => :destroy, :conditions => { :name => name.to_s })
106
113
  end
107
114
  end
108
115
 
@@ -111,6 +118,7 @@ class Graffic < ActiveRecord::Base
111
118
  attributes['format'] || self.class.format
112
119
  end
113
120
 
121
+ # Return an RMagick image, based on the state of image
114
122
  def image
115
123
  @image ||= case self.state
116
124
  when 'moved': Magick::Image.read(tmp_file_path).first
@@ -119,70 +127,51 @@ class Graffic < ActiveRecord::Base
119
127
  end
120
128
 
121
129
  # Move the file to the temporary directory
122
- def move!
123
- move_without_queue!
124
- queue_for_upload
125
- end
126
-
127
- # Move the file to the temporary directory
128
- def move_without_queue!
130
+ def move
131
+ logger.debug("***** Moving? #{self.state}")
132
+ return unless self.state == 'received'
129
133
  logger.debug("***** Graffic[#{self.id}](#{self.name})#move!")
130
- if @file.is_a?(Tempfile)
131
- @file.write(tmp_file_path)
132
- change_state('moved')
133
- elsif @file.is_a?(String)
134
- FileUtils.cp(@file, tmp_file_path)
135
- change_state('moved')
136
- elsif @file.is_a?(Magick::Image)
134
+ logger.debug("***** File type: #{@file.class.name}")
135
+ if @file.is_a?(Tempfile) # Uploaded File
136
+ FileUtils.cp(@file.path, tmp_file_path)
137
+ logger.debug("***** Moving file: #{@file.path}, #{tmp_file_path}")
138
+ elsif @file.is_a?(String) # String representing a file's location
139
+ FileUtils.cp(@file.strip, tmp_file_path)
140
+ logger.debug("***** Moving file: #{@file.strip}, #{tmp_file_path}")
141
+ elsif @file.is_a?(Magick::Image) # An actually RMagick file
137
142
  @image = @file
138
- upload_without_queue!
143
+ self.use_queue = false
139
144
  change_state('uploaded')
145
+ process
146
+ return
147
+ else
148
+ raise "Don't know how to handle file type #{@file.class.name}"
140
149
  end
150
+
151
+ change_state('moved')
152
+ use_queue? ? queue_for_upload : upload
141
153
  end
142
154
 
143
- # Process the image
144
- def process!
145
- process_without_verions!
146
- process_versions
155
+ attr_accessor :should_process_versions
156
+ def should_process_versions?
157
+ should_process_versions.nil? ? self.class.should_process_versions : should_process_versions
147
158
  end
148
159
 
149
- # Process the image without the versions
150
- def process_without_verions!
160
+ # Process the image
161
+ def process
162
+ return unless self.state == 'uploaded'
151
163
  logger.debug("***** Graffic[#{self.id}](#{self.name})#process!")
152
164
  run_processors
153
165
  record_image_dimensions_and_format
154
166
  upload_image
155
167
  change_state('processed')
168
+
169
+ process_versions if should_process_versions?
156
170
  end
157
-
171
+
158
172
  # Returns the processor for the instance
159
- def processor
160
- @processor || self.class.processor
161
- end
162
-
163
- # Move the file if it saved successfully
164
- def save_and_move
165
- move! if status = save
166
- status
167
- end
168
-
169
- # Save the file and process it immediately. Does to use queues.
170
- def save_and_process
171
- if status = save
172
- move_without_queue!
173
- upload_without_queue!
174
- process!
175
- end
176
- status
177
- end
178
-
179
- def save_and_process_without_versions
180
- if status = save
181
- move_without_queue!
182
- upload_without_queue!
183
- process_without_verions!
184
- end
185
- status
173
+ def processors
174
+ @processors || self.class.processors
186
175
  end
187
176
 
188
177
  # Returns a size string. Good for RMagick and image_tag :size
@@ -191,18 +180,15 @@ class Graffic < ActiveRecord::Base
191
180
  end
192
181
 
193
182
  # Upload the file
194
- def upload!
195
- upload_without_queue!
196
- queue_for_processing
197
- end
198
-
199
- # Upload the file
200
- def upload_without_queue!
183
+ def upload
184
+ return unless self.state == 'moved'
201
185
  logger.debug("***** Graffic[#{self.id}](#{self.name})#upload!")
202
186
  upload_image
203
187
  save_original
204
188
  remove_tmp_file
189
+
205
190
  change_state('uploaded')
191
+ use_queue? ? queue_for_processing : process
206
192
  end
207
193
 
208
194
  # Return the url for displaying the image
@@ -210,6 +196,11 @@ class Graffic < ActiveRecord::Base
210
196
  self.s3_key.public_link
211
197
  end
212
198
 
199
+ attr_accessor :use_queue
200
+ def use_queue?
201
+ use_queue.nil? ? self.class.use_queue : use_queue
202
+ end
203
+
213
204
  protected
214
205
 
215
206
  # Connivence method for getting the bucket
@@ -243,15 +234,12 @@ protected
243
234
  end
244
235
 
245
236
  def process_versions
246
- unless self.versions.blank?
247
- self.versions.each do |version, processor|
248
- logger.debug("***** Graffic[#{self.id}](#{self.name}): Processing version: #{version}")
249
- g = Graffic.create(:file => self.image, :name => version.to_s)
250
- g.processor = processor unless processor.nil?
251
- g.save_and_process
252
- self.update_attribute(version, g)
253
- end
254
- end
237
+ self.versions.each do |version, processors|
238
+ logger.debug("***** Graffic[#{self.id}](#{self.name}): Processing version: #{version} (#{processors.size} processors)")
239
+ g = Graffic.new(:file => self.image, :name => version.to_s, :use_queue => false)
240
+ g.processors += processors unless processors.nil?
241
+ self.update_attribute(version, g)
242
+ end unless self.versions.blank?
255
243
  end
256
244
 
257
245
  def queue_for_upload
@@ -281,11 +269,17 @@ protected
281
269
  end
282
270
 
283
271
  def run_processors
284
- logger.debug("***** Graffic[#{self.id}](#{self.name}): Running processor")
285
- unless self.processor.blank?
286
- @image = processor.call(image, self)
272
+ logger.debug("***** Graffic[#{self.id}](#{self.name}): Running processor (#{self.processors.try(:size)} processors)")
273
+ self.processors.each do |processor|
274
+ img = self.image
275
+ img = img.first if img.respond_to?(:first)
276
+ @image = case processor.arity # Pass in the record itself, if the block wants it
277
+ when 1: processor.call(img)
278
+ when 2: processor.call(img, self)
279
+ end
287
280
  raise 'You need to return an image' unless @image.is_a?(Magick::Image)
288
- end
281
+ logger.debug("Returned Image Size: #{@image.columns}x#{@image.rows}")
282
+ end unless self.processors.blank?
289
283
  end
290
284
 
291
285
  def s3_key
@@ -295,8 +289,7 @@ protected
295
289
  def save_original
296
290
  if respond_to?(:original)
297
291
  logger.debug("***** Graffic[#{self.id}](#{self.name}): Saving Original")
298
- g = Graffic.new(:file => tmp_file_path, :name => 'original')
299
- g.save_and_process
292
+ g = Graffic.new(:file => tmp_file_path, :name => 'original', :use_queue => false)
300
293
  self.update_attribute(:original, g)
301
294
  end
302
295
  end
@@ -1,6 +1,6 @@
1
1
  module Graffic::ViewHelpers
2
2
  def graffic_tag(graffic, version_or_opts = {}, opts = {})
3
- if graffic
3
+ if graffic && graffic.state.eql?('processed')
4
4
  if version_or_opts.is_a?(Symbol)
5
5
  graffic_tag(graffic.try(version_or_opts), opts)
6
6
  else
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jeremyboles-graffic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Boles