jeremyboles-graffic 0.1.0
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/CHANGELOG.rdoc +3 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +28 -0
- data/Rakefile +53 -0
- data/init.rb +5 -0
- data/lib/graffic.rb +276 -0
- data/tasks/graffic_tasks.rake +4 -0
- data/test/graffic_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- data/uninstall.rb +1 -0
- metadata +100 -0
data/CHANGELOG.rdoc
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 [name of plugin creator]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Graffic
|
2
|
+
======
|
3
|
+
|
4
|
+
Graffic is an ActiveRecord class that helps you work with and attach images to other ActiveRecord records.
|
5
|
+
|
6
|
+
|
7
|
+
Example
|
8
|
+
======
|
9
|
+
|
10
|
+
# models/profile_photo.rb
|
11
|
+
class ProfilePhoto < Graffic
|
12
|
+
size :medium, :width => 48, :height => 48, :format => :jpg
|
13
|
+
end
|
14
|
+
|
15
|
+
# models/user.rb
|
16
|
+
class User < ActiveRecord
|
17
|
+
has_one :profile_photo
|
18
|
+
end
|
19
|
+
|
20
|
+
# views/users/_edit.html.erb
|
21
|
+
<% form_for @user do |f| %>
|
22
|
+
<% f.fields_for :profile_photo do |p| %>
|
23
|
+
<%= p.label :file, 'Profile Photo' %>
|
24
|
+
<%= p.field_field :file %>
|
25
|
+
<% end %>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
Copyright (c) 2009 Jeremy Boles, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
desc 'Default: run unit tests.'
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
desc 'Generate RDoc documentation for the Graffic plugin.'
|
10
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
11
|
+
rdoc.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'CHANGELOG.rdoc').include('lib/**/*.rb')
|
12
|
+
|
13
|
+
rdoc.main = "README.rdoc" # page to start on
|
14
|
+
rdoc.title = "Graffic documentation"
|
15
|
+
|
16
|
+
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
17
|
+
rdoc.options << '--inline-source' << '--charset=UTF-8'
|
18
|
+
rdoc.options << '--webcvs=http://github.com/jeremyboles/graffic/tree/master/'
|
19
|
+
end
|
20
|
+
|
21
|
+
desc %{Update ".manifest" with the latest list of project filenames.}
|
22
|
+
task :manifest do
|
23
|
+
list = `git ls-files --full-name --exclude=*.gemspec --exclude=.*`.chomp.split("\n")
|
24
|
+
|
25
|
+
if spec_file = Dir['*.gemspec'].first
|
26
|
+
spec = File.read spec_file
|
27
|
+
spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
|
28
|
+
assignment = $1
|
29
|
+
bunch = $2 ? list.grep(/^test\//) : list
|
30
|
+
'%s%%w(%s)' % [assignment, bunch.join(' ')]
|
31
|
+
end
|
32
|
+
|
33
|
+
File.open(spec_file, 'w') { |f| f << spec }
|
34
|
+
end
|
35
|
+
File.open('.manifest', 'w') { |f| f << list.join("\n") }
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'Test the graffic plugin.'
|
39
|
+
Rake::TestTask.new(:test) do |t|
|
40
|
+
t.libs << 'lib'
|
41
|
+
t.libs << 'test'
|
42
|
+
t.pattern = 'test/**/*_test.rb'
|
43
|
+
t.verbose = true
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Generate documentation for the graffic plugin.'
|
47
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = 'Graffic'
|
50
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
51
|
+
rdoc.rdoc_files.include('README.rdoc')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/init.rb
ADDED
data/lib/graffic.rb
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
# Graffic
|
2
|
+
class Graffic < ActiveRecord::Base
|
3
|
+
attr_writer :file
|
4
|
+
attr_writer :image
|
5
|
+
cattr_writer :bucket_name
|
6
|
+
cattr_writer :queue_name
|
7
|
+
|
8
|
+
belongs_to :resource, :polymorphic => true
|
9
|
+
|
10
|
+
after_create :move
|
11
|
+
after_destroy :delete_s3_file
|
12
|
+
|
13
|
+
# We're using the state machine to keep track of what stage of photo
|
14
|
+
# processing we're at. Here are the states:
|
15
|
+
#
|
16
|
+
# received -> moved -> uploaded -> processed
|
17
|
+
#
|
18
|
+
state_machine :state, :initial => :received do
|
19
|
+
before_transition :to => :moved, :do => :move!
|
20
|
+
before_transition :to => :uploaded, :do => :save_original!
|
21
|
+
before_transition :from => :moved, :do => :upload!
|
22
|
+
before_transition :to => :processed, :do => :process!
|
23
|
+
|
24
|
+
after_transition :from => :moved, :do => :remove_moved_file!
|
25
|
+
after_transition :to => :uploaded, :do => :queue_job!
|
26
|
+
after_transition :to => :processed, :from => :uploaded, :do => :create_sizes!
|
27
|
+
after_transition :to => :processed, :do => :record_dimensions!
|
28
|
+
|
29
|
+
event :move do
|
30
|
+
transition :to => :moved, :from => :received
|
31
|
+
end
|
32
|
+
|
33
|
+
event :upload do
|
34
|
+
transition :to => :uploaded, :from => :moved
|
35
|
+
end
|
36
|
+
|
37
|
+
event :upload_unprocessed do
|
38
|
+
transition :to => :processed, :from => :moved
|
39
|
+
end
|
40
|
+
|
41
|
+
event :process do
|
42
|
+
transition :to => :processed, :from => :uploaded
|
43
|
+
end
|
44
|
+
|
45
|
+
# Based on which state we're at, we'll need to pull the image from a different are
|
46
|
+
state :moved do
|
47
|
+
# If it hasn't been moved, we'll need to pull it from the local file system
|
48
|
+
def image
|
49
|
+
@image ||= Magick::Image.read(tmp_file_path).first
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
state :uploaded, :processed do
|
54
|
+
# If it's been uploaded and procesed, grab it from S3
|
55
|
+
def image
|
56
|
+
@image ||= Magick::Image.from_blob(bucket.get(uploaded_file_path)).first
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
# Returns the bucket for the model
|
63
|
+
def bucket
|
64
|
+
@bucket ||= Graffic::Aws.s3.bucket(bucket_name, true, 'public-read')
|
65
|
+
end
|
66
|
+
|
67
|
+
# Change the bucket name from the default
|
68
|
+
def bucket_name(name = nil)
|
69
|
+
@@bucket_name = name unless name == nil
|
70
|
+
@@bucket_name
|
71
|
+
end
|
72
|
+
|
73
|
+
# Upload all of the files that have been moved. This will only work on
|
74
|
+
# local files.
|
75
|
+
# TODO: Make this work only on file system that the file was used on
|
76
|
+
def handle_moved!
|
77
|
+
image = first(:conditions => { :state => 'moved' })
|
78
|
+
return if image.nil?
|
79
|
+
image.upload
|
80
|
+
end
|
81
|
+
|
82
|
+
# Process all of the the uploaded files that are in the queue
|
83
|
+
def handle_uploaded!
|
84
|
+
message = queue.pop
|
85
|
+
return if message.nil?
|
86
|
+
image = find(message.body)
|
87
|
+
image.process
|
88
|
+
message.body
|
89
|
+
rescue
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
def inherited(child)
|
94
|
+
child.has_one(:original, :class_name => 'Graffic', :as => :resource, :dependent => :destroy, :conditions => { :name => 'original' })
|
95
|
+
super
|
96
|
+
end
|
97
|
+
|
98
|
+
def process(&block)
|
99
|
+
if block_given?
|
100
|
+
@process = block
|
101
|
+
else
|
102
|
+
return @process
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Change the queue name from the default
|
107
|
+
def queue_name(name = nil)
|
108
|
+
@@queue_name = name unless name == nil
|
109
|
+
@@queue_name || 'graffic'
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return the model's queue
|
113
|
+
def queue
|
114
|
+
@queue ||= Graffic::Aws.sqs.queue(queue_name, true)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Create a size of the graphic.
|
118
|
+
def size(name, size={})
|
119
|
+
size[:format] ||= :png
|
120
|
+
size.assert_valid_keys(:width, :height, :format)
|
121
|
+
|
122
|
+
@sizes ||= {}
|
123
|
+
@sizes[name] = size
|
124
|
+
|
125
|
+
has_one(name, :class_name => 'Graffic', :as => :resource, :dependent => :destroy, :conditions => { :name => name.to_s })
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns all of the version names for the mode
|
129
|
+
def sizes
|
130
|
+
@sizes ||= {}
|
131
|
+
end
|
132
|
+
|
133
|
+
# Set the image format
|
134
|
+
def format(format = nil)
|
135
|
+
@format = format unless format.nil?
|
136
|
+
@format ||= :png
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns a size string
|
141
|
+
def size
|
142
|
+
"#{width}x#{height}"
|
143
|
+
end
|
144
|
+
|
145
|
+
# Return the url for displaying the image
|
146
|
+
def url
|
147
|
+
key.public_link
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
# Connivence method for getting the bucket
|
152
|
+
def bucket
|
153
|
+
self.class.bucket
|
154
|
+
end
|
155
|
+
|
156
|
+
def create_sizes!
|
157
|
+
self.class.sizes.each do |name, size|
|
158
|
+
logger.debug("***** Sizing: #{name}")
|
159
|
+
file_name = "#{tmp_file_path}.#{name}.#{image_extension(size[:format])}"
|
160
|
+
|
161
|
+
img = image.crop_resized(size[:width], size[:height])
|
162
|
+
img.write(file_name)
|
163
|
+
|
164
|
+
i = Graffic.create(:file => file_name, :format => size[:format].to_s, :name => name.to_s)
|
165
|
+
update_attribute(name, i)
|
166
|
+
i.upload_unprocessed
|
167
|
+
|
168
|
+
FileUtils.rm(file_name)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Deletes the file from S3
|
173
|
+
def delete_s3_file
|
174
|
+
key.delete
|
175
|
+
end
|
176
|
+
|
177
|
+
# The formate of the image
|
178
|
+
def format
|
179
|
+
attributes['format'] || self.class.format
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns true if the file has versions
|
183
|
+
def has_sizes?
|
184
|
+
!self.class.sizes.empty?
|
185
|
+
end
|
186
|
+
|
187
|
+
def image_extension(atype = nil)
|
188
|
+
(atype || format).to_s
|
189
|
+
end
|
190
|
+
|
191
|
+
# Return the S3 key for the record
|
192
|
+
def key
|
193
|
+
@s3_key ||= bucket.key(uploaded_file_path)
|
194
|
+
end
|
195
|
+
|
196
|
+
# If the file is a Tempfile, we'll need to move to the app's tmp directory so
|
197
|
+
# we can insure that it is retrained until we can upload it
|
198
|
+
# If its a S3 Key, we'll write that file's date to our tmp directory
|
199
|
+
def move!
|
200
|
+
if @file.is_a?(Tempfile)
|
201
|
+
FileUtils.mv(@file.path, tmp_file_path)
|
202
|
+
elsif @file.is_a?(String)
|
203
|
+
FileUtils.cp(@file, tmp_file_path)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Process the image
|
208
|
+
def process!
|
209
|
+
unless self.class.process.nil?
|
210
|
+
@image = self.class.process.call(image)
|
211
|
+
raise 'You need to return an image' unless @image.is_a?(Magick::Image)
|
212
|
+
upload!
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Connivence method for getting the queue
|
217
|
+
def queue
|
218
|
+
self.class.queue
|
219
|
+
end
|
220
|
+
|
221
|
+
# Add a job to the queue
|
222
|
+
def queue_job!
|
223
|
+
logger.debug("***** Graffic(#{self.id})#queue_job!")
|
224
|
+
queue.push(self.id)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Save the image's width and height to the database
|
228
|
+
def record_dimensions!
|
229
|
+
logger.debug("***** Graffic(#{self.id})#record_dimensions!")
|
230
|
+
self.update_attributes(:height => image.rows, :width => image.columns)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Remove the temp file in the app's temp director
|
234
|
+
def remove_moved_file!
|
235
|
+
logger.debug("***** Graffic(#{self.id})#remove_moved_file!")
|
236
|
+
FileUtils.rm(tmp_file_path) if File.exists?(tmp_file_path)
|
237
|
+
end
|
238
|
+
|
239
|
+
# Returns a RMagick constant for the type of image
|
240
|
+
def rmagick_type(atype = nil)
|
241
|
+
return case (atype || format).to_sym
|
242
|
+
when :gif then Magick::LZWCompression
|
243
|
+
when :jpg then Magick::JPEGCompression
|
244
|
+
when :png then Magick::ZipCompression
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Uploads an untouched original
|
249
|
+
def save_original!
|
250
|
+
logger.debug("***** Graffic(#{self.id})#save_original!")
|
251
|
+
if respond_to?(:original)
|
252
|
+
i = Graffic.new(:file => tmp_file_path, :name => 'original')
|
253
|
+
update_attribute(:original, i)
|
254
|
+
i.upload_unprocessed
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Returns the path to the file in the app's tmp directory
|
259
|
+
def tmp_file_path
|
260
|
+
RAILS_ROOT + "/tmp/images/#{id}.tmp"
|
261
|
+
end
|
262
|
+
|
263
|
+
# Upload the file to S3
|
264
|
+
def upload!
|
265
|
+
logger.debug("***** Graffic(#{self.id})#upload!")
|
266
|
+
t = rmagick_type
|
267
|
+
data = image.to_blob { |i| i.compression = t }
|
268
|
+
bucket.put(uploaded_file_path, data, {}, 'public-read')
|
269
|
+
end
|
270
|
+
|
271
|
+
# Return the path on S3 for the file (the key name, essentially)
|
272
|
+
def uploaded_file_path
|
273
|
+
"#{self.class.name.tableize}/#{id}.#{image_extension}"
|
274
|
+
end
|
275
|
+
|
276
|
+
end # Graffic
|
data/test/test_helper.rb
ADDED
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jeremyboles-graffic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Boles
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-16 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: state_machine
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.6.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: right_aws
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.10.0
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rmagick
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.9.1
|
44
|
+
version:
|
45
|
+
description: Graffic is an ActiveRecord class that helps you work with and attach images to other ActiveRecord records.
|
46
|
+
email: jeremy@jeremyboles.com
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README.rdoc
|
53
|
+
- LICENSE
|
54
|
+
- CHANGELOG.rdoc
|
55
|
+
files:
|
56
|
+
- MIT-LICENSE
|
57
|
+
- README
|
58
|
+
- Rakefile
|
59
|
+
- init.rb
|
60
|
+
- install.rb
|
61
|
+
- lib/graffic.rb
|
62
|
+
- tasks/graffic_tasks.rake
|
63
|
+
- test/graffic_test.rb
|
64
|
+
- test/test_helper.rb
|
65
|
+
- uninstall.rb
|
66
|
+
- README.rdoc
|
67
|
+
- LICENSE
|
68
|
+
- CHANGELOG.rdoc
|
69
|
+
has_rdoc: true
|
70
|
+
homepage: http://github.com/jeremyboles/graffic/wikis
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options:
|
73
|
+
- --main
|
74
|
+
- README.rdoc
|
75
|
+
- --inline-source
|
76
|
+
- --charset=UTF-8
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
version:
|
91
|
+
requirements: []
|
92
|
+
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 1.2.0
|
95
|
+
signing_key:
|
96
|
+
specification_version: 2
|
97
|
+
summary: Image asset handing for ActiveRecord and Rails
|
98
|
+
test_files:
|
99
|
+
- test/graffic_test.rb
|
100
|
+
- test/test_helper.rb
|