imagery 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.markdown +231 -0
- data/VERSION +1 -1
- data/imagery.gemspec +70 -0
- data/lib/imagery.rb +5 -7
- data/lib/imagery/faking.rb +7 -0
- data/lib/imagery/model.rb +130 -15
- data/lib/imagery/s3.rb +68 -2
- data/lib/imagery/test.rb +17 -0
- data/test/test_imagery.rb +3 -3
- metadata +7 -5
- data/README.rdoc +0 -17
data/.gitignore
CHANGED
data/README.markdown
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
Imagery
|
2
|
+
=======
|
3
|
+
|
4
|
+
(See documentation at [http://labs.sinefunc.com/imagery/doc](http://labs.sinefunc.com/imagery/doc))
|
5
|
+
|
6
|
+
## Image manipulation should be simple. It should be customizable. It should allow for flexibility. Imagery attempts to solve these.
|
7
|
+
|
8
|
+
### Imagery favors:
|
9
|
+
|
10
|
+
1. Simplicity and explicitness over magic DSLs.
|
11
|
+
2. OOP principles such as inheritance and composition.
|
12
|
+
3. Flexibility and extensibility.
|
13
|
+
4. Not being tied to any form of ORM.
|
14
|
+
|
15
|
+
1. Simplicity and Explicitness
|
16
|
+
------------------------------
|
17
|
+
To get started using Imagery you only need ImageMagick, ruby and Imagery of
|
18
|
+
course.
|
19
|
+
|
20
|
+
# on debian based systems
|
21
|
+
sudo apt-get install imagemagick
|
22
|
+
# or maybe using macports
|
23
|
+
sudo port install ImageMagick
|
24
|
+
[sudo] gem install imagery
|
25
|
+
|
26
|
+
Then you may proceed using it.
|
27
|
+
|
28
|
+
require 'rubygems'
|
29
|
+
require 'imagery'
|
30
|
+
|
31
|
+
Photo = Class.new(Struct.new(:id))
|
32
|
+
|
33
|
+
i = Imagery.new(Photo.new(1001))
|
34
|
+
i.root = '/some/path/here'
|
35
|
+
i.sizes = { :thumb => ["48x48^", "48x48"], :large => ["480x320"] }
|
36
|
+
i.save(File.open('/some/path/to/image.jpg'))
|
37
|
+
|
38
|
+
File.exist?('/some/path/here/public/system/photo/1001/thumb.png')
|
39
|
+
# => true
|
40
|
+
|
41
|
+
File.exist?('/some/path/here/public/system/photo/1001/large.png')
|
42
|
+
# => true
|
43
|
+
|
44
|
+
File.exist?('/some/path/here/public/system/photo/1001/original.png')
|
45
|
+
# => true
|
46
|
+
|
47
|
+
# the defaut is to use the .id and the name of the class passed,
|
48
|
+
# but you can specify a different scheme.
|
49
|
+
|
50
|
+
i = Imagery.new(Photo.new(1001), `uuidgen`.strip, "photos")
|
51
|
+
i.root = '/some/path/here'
|
52
|
+
i.file == '/some/path/here/public/system/photos/1c2030a6-6bfa-11df-8997-67a71f1f84c7/original.png'
|
53
|
+
# => true
|
54
|
+
|
55
|
+
2. OOP Principles (that we already know)
|
56
|
+
----------------------------------------
|
57
|
+
|
58
|
+
### Ohm example (See [http://ohm.keyvalue.org](http://ohm.keyvalue.org))
|
59
|
+
|
60
|
+
# Imagery will use ROOT_DIR if its available
|
61
|
+
ROOT_DIR = "/u/apps/site/current"
|
62
|
+
|
63
|
+
class User < Ohm::Model
|
64
|
+
include Ohm::Callbacks
|
65
|
+
|
66
|
+
after :save, :write_avatar
|
67
|
+
|
68
|
+
def avatar=(fp)
|
69
|
+
@avatar_fp = fp
|
70
|
+
end
|
71
|
+
|
72
|
+
def avatar
|
73
|
+
@avatar ||=
|
74
|
+
Imagery.new(self).tap do |i|
|
75
|
+
i.sizes = { :thumb => ["48x48^", "48x48"], :medium => ["120x120"] }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
def write_avatar
|
81
|
+
avatar.save(@avatar_fp[:tempfile]) if @avatar_fp
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Since we're using composition, we can customize the dimensions on an
|
86
|
+
# instance level.
|
87
|
+
class Collage < Ohm::Model
|
88
|
+
attribute :width
|
89
|
+
attribute :height
|
90
|
+
|
91
|
+
def photo
|
92
|
+
@photo ||=
|
93
|
+
Imagery.new(self).tap do |i|
|
94
|
+
i.sizes = { :thumb => ["%sx%s" % [width, height]] }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# For cases where we want to use S3 for some and normal filesystem for others
|
100
|
+
class S3Photo < Imagery::Model
|
101
|
+
include Imagery::S3
|
102
|
+
self.s3_bucket = 'my-bucket'
|
103
|
+
end
|
104
|
+
|
105
|
+
# then maybe some other files are using cloudfront
|
106
|
+
class CloudfrontPhoto < Imagery::Model
|
107
|
+
include Imagery::S3
|
108
|
+
self.s3_bucket = 'my-bucket'
|
109
|
+
self.s3_distribution_domain = 'assets.site.com'
|
110
|
+
end
|
111
|
+
|
112
|
+
3. Flexibility and Extensibility
|
113
|
+
--------------------------------
|
114
|
+
### Existing plugins: Faking and S3
|
115
|
+
|
116
|
+
#### Imagery::S3
|
117
|
+
|
118
|
+
As was shown in some examples above you can easily do S3 integration.
|
119
|
+
The access credentials are assumed to be stored in
|
120
|
+
|
121
|
+
ENV["AMAZON_ACCESS_KEY_ID"]
|
122
|
+
ENV["AMAZON_SECRET_ACCESS_KEY"]
|
123
|
+
|
124
|
+
you can do this by setting it on your .bash_profile / .bashrc or just
|
125
|
+
manually setting them somewhere in your appication
|
126
|
+
|
127
|
+
ENV["AMAZON_ACCESS_KEY_ID"] = '_access_key_id_'
|
128
|
+
ENV["AMAZON_SECRET_ACCESS_KEY"] = '_secret_access_key_'
|
129
|
+
|
130
|
+
Now you can just start using it:
|
131
|
+
|
132
|
+
Photo = Class.new(Struct.new(:id))
|
133
|
+
|
134
|
+
class Imagery::Model
|
135
|
+
include Imagery::S3
|
136
|
+
self.s3_bucket = 'my-bucket'
|
137
|
+
end
|
138
|
+
|
139
|
+
i = Imagery.new(Photo.new(1001))
|
140
|
+
i.root = '/tmp'
|
141
|
+
i.save(File.open('/some/path/to/image.jpg'))
|
142
|
+
|
143
|
+
#### Imagery::Faking
|
144
|
+
|
145
|
+
When doing testing, you definitely don't want to run image
|
146
|
+
resizing everytime. Enter Faking.
|
147
|
+
|
148
|
+
# in your test_helper / spec_helper
|
149
|
+
Imagery::Model.send :include, Imagery::Faking
|
150
|
+
Imagery::Model.mode = :fake
|
151
|
+
|
152
|
+
# but what if we want to run it for real on a case to case basis?
|
153
|
+
# sure we can!
|
154
|
+
Imagery::Model.real {
|
155
|
+
# do some imagery testing here
|
156
|
+
}
|
157
|
+
|
158
|
+
#### Imagery::Test
|
159
|
+
|
160
|
+
There is a module you can include in your test context to automate the pattern
|
161
|
+
of testing / faking on an opt-in basis.
|
162
|
+
|
163
|
+
# in your test_helper / spec_helper
|
164
|
+
class Test::Unit::TestCase
|
165
|
+
include Imagery::Test
|
166
|
+
end
|
167
|
+
|
168
|
+
# now when you do some testing... (User assumes the user example above)
|
169
|
+
imagery do |is_real|
|
170
|
+
user = User.new(:avatar => { tempfile: File.open('avatar.jpg') })
|
171
|
+
user.save
|
172
|
+
|
173
|
+
if is_real
|
174
|
+
assert File.exist?(user.avatar.file)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
Running your test suite:
|
179
|
+
|
180
|
+
REAL_IMAGERY=true rake test
|
181
|
+
|
182
|
+
It's off by default though, so you don't have to do anything to make sure
|
183
|
+
Imagery doesn't run.
|
184
|
+
|
185
|
+
### Extending Imagery
|
186
|
+
By making use of standard Ruby idioms, we can easily do lots with it.
|
187
|
+
Exensibility is addressed via Ruby modules for example:
|
188
|
+
|
189
|
+
module Imagery
|
190
|
+
module MogileStore
|
191
|
+
def self.included(base)
|
192
|
+
class << base
|
193
|
+
attr_accessor :mogile_config
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def save(io)
|
198
|
+
if super
|
199
|
+
# do some mogie FS stuff here
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def delete
|
204
|
+
super
|
205
|
+
# remove the mogile stuff here
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Now just include the module however, whenever you want
|
211
|
+
module Imagery
|
212
|
+
class Model
|
213
|
+
include Imagery::MogileStore
|
214
|
+
self.mogile_config = { :foo => :bar }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
### Note on Patches/Pull Requests
|
220
|
+
|
221
|
+
* Fork the project.
|
222
|
+
* Make your feature addition or bug fix.
|
223
|
+
* Add tests for it. This is important so I don't break it in a
|
224
|
+
future version unintentionally.
|
225
|
+
* Commit, do not mess with rakefile, version, or history.
|
226
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
227
|
+
* Send me a pull request. Bonus points for topic branches.
|
228
|
+
|
229
|
+
### Copyright
|
230
|
+
|
231
|
+
Copyright (c) 2010 Cyril David. See LICENSE for details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/imagery.gemspec
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{imagery}
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Cyril David"]
|
12
|
+
s.date = %q{2010-05-31}
|
13
|
+
s.description = %q{Uses ImageMagick directly underneath. Nuff said.}
|
14
|
+
s.email = %q{cyx.ucron@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.markdown",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"imagery.gemspec",
|
27
|
+
"lib/imagery.rb",
|
28
|
+
"lib/imagery/faking.rb",
|
29
|
+
"lib/imagery/model.rb",
|
30
|
+
"lib/imagery/s3.rb",
|
31
|
+
"lib/imagery/test.rb",
|
32
|
+
"test/fixtures/lake.jpg",
|
33
|
+
"test/helper.rb",
|
34
|
+
"test/test_imagery.rb",
|
35
|
+
"test/test_with_s3.rb"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/sinefunc/imagery}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.6}
|
41
|
+
s.summary = %q{Image resizing without all the bloat}
|
42
|
+
s.test_files = [
|
43
|
+
"test/helper.rb",
|
44
|
+
"test/test_imagery.rb",
|
45
|
+
"test/test_with_s3.rb"
|
46
|
+
]
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<escape>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<contest>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<aws-s3>, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<escape>, [">= 0"])
|
59
|
+
s.add_dependency(%q<contest>, [">= 0"])
|
60
|
+
s.add_dependency(%q<aws-s3>, [">= 0"])
|
61
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
62
|
+
end
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<escape>, [">= 0"])
|
65
|
+
s.add_dependency(%q<contest>, [">= 0"])
|
66
|
+
s.add_dependency(%q<aws-s3>, [">= 0"])
|
67
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
data/lib/imagery.rb
CHANGED
@@ -2,19 +2,17 @@ require 'escape'
|
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
4
|
module Imagery
|
5
|
-
VERSION = "0.0.
|
5
|
+
VERSION = "0.0.2"
|
6
6
|
|
7
7
|
autoload :Model, "imagery/model"
|
8
8
|
autoload :Faking, "imagery/faking"
|
9
9
|
autoload :S3, "imagery/s3"
|
10
|
-
|
10
|
+
autoload :Test, "imagery/test"
|
11
|
+
|
12
|
+
# Syntactic sugar for Imagery::Model::new
|
13
|
+
# @see Imagery::Model#initialize for details
|
11
14
|
def new(*args, &blk)
|
12
15
|
Model.new(*args, &blk)
|
13
16
|
end
|
14
17
|
module_function :new
|
15
|
-
|
16
|
-
def faked(&blk)
|
17
|
-
Model.faked(&blk)
|
18
|
-
end
|
19
|
-
module_function :faked
|
20
18
|
end
|
data/lib/imagery/faking.rb
CHANGED
data/lib/imagery/model.rb
CHANGED
@@ -1,41 +1,146 @@
|
|
1
1
|
module Imagery
|
2
2
|
class Model
|
3
3
|
UnknownSize = Class.new(StandardError)
|
4
|
+
|
5
|
+
@@directory = 'public/system'
|
6
|
+
@@default = { :original => ["1920x1200>"] }
|
4
7
|
|
8
|
+
# This is typically the database ID, you may also use something
|
9
|
+
# like a GUID for it, the sky's the limit.
|
5
10
|
attr :key
|
11
|
+
|
12
|
+
# Used as a grouping scheme, if you name this as `photos` for example,
|
13
|
+
# you will have public/system/photos as the path.
|
6
14
|
attr :namespace
|
15
|
+
|
16
|
+
# Returns a single key => value pair hash, defaulting to @@default.
|
7
17
|
attr :default
|
18
|
+
|
19
|
+
# Returns the directory used which defaults to public/system.
|
8
20
|
attr :directory
|
21
|
+
|
22
|
+
# Defaulting to :original, the value used here will be the default
|
23
|
+
# size used when calling Imagery::Model#url and Imagery::Model#file.
|
24
|
+
attr_accessor :default_size
|
9
25
|
|
10
|
-
|
26
|
+
# Allows you to define the root of your application, all other paths
|
27
|
+
# are determined relative to this.
|
28
|
+
attr_writer :root
|
29
|
+
|
30
|
+
# Allows you to define the different sizes you want as a hash of
|
31
|
+
# :style => [geometry] pairs.
|
32
|
+
attr_writer :sizes
|
11
33
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
34
|
+
# @param [#id] model any ruby object
|
35
|
+
# @param [#to_s] key typically the database ID of a model. But you may also
|
36
|
+
# use anything here just as long as its unique across the
|
37
|
+
# namespace.
|
38
|
+
# @param [String] namespace used as a grouping mechanism for your images.
|
39
|
+
def initialize(model, key = model.id, namespace = namespace_for(model.class))
|
40
|
+
@key = key.to_s
|
41
|
+
@namespace = namespace
|
42
|
+
@default = @@default
|
43
|
+
@directory = @@directory
|
44
|
+
@sizes = {}
|
45
|
+
@default_size = :original
|
18
46
|
end
|
19
47
|
|
48
|
+
# Returns all the sizes defined including the default size.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# Photo = Class.new(Struct.new(:id))
|
52
|
+
# i = Imagery::Model.new(Photo.new(1001))
|
53
|
+
# i.sizes == { :original => ["1920x1280"] }
|
54
|
+
# # => true
|
55
|
+
#
|
56
|
+
# i.sizes = { :thumb => ["48x48"] }
|
57
|
+
# i.sizes == { :thumb => ["48x48"], :original => ["1920x1280"] }
|
58
|
+
# # => true
|
59
|
+
#
|
60
|
+
# @return [Hash] size => [dimension] pairs.
|
20
61
|
def sizes
|
21
|
-
@sizes.merge(
|
62
|
+
@sizes.merge(default)
|
22
63
|
end
|
23
64
|
|
24
|
-
|
65
|
+
# Gives the absolute file for a specified size.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
#
|
69
|
+
# Photo = Class.new(Struct.new(:id))
|
70
|
+
# i = Imagery.new(Photo.new(1001))
|
71
|
+
# i.root = '/tmp'
|
72
|
+
# i.file(:original) == '/tmp/public/system/photo/1001/original.png
|
73
|
+
# # => true
|
74
|
+
#
|
75
|
+
# i.file(:thumb)
|
76
|
+
# # raise Imagery::Model::UnknownSize
|
77
|
+
#
|
78
|
+
# i.sizes = { :thumb => ["100x100"] }
|
79
|
+
# i.file(:thumb) == '/tmp/public/system/photo/1001/thumb.png
|
80
|
+
# # => true
|
81
|
+
#
|
82
|
+
# @param [Symbol] size the size specific filename.
|
83
|
+
# @raise [UnknownSize] if the size is not found in
|
84
|
+
# Imagery::Model#sizes.
|
85
|
+
# @return [String] the absolute path of the size specific filename e.g.
|
86
|
+
# /u/apps/reddit/current/public/system/photo/1/thumb.png
|
87
|
+
# where photo is the namespace and 1 is the key.
|
88
|
+
def file(size = self.default_size)
|
25
89
|
raise UnknownSize, "#{ size } is not defined" unless sizes.has_key?(size)
|
26
90
|
|
27
91
|
root_path(directory, namespace, key, filename(size))
|
28
92
|
end
|
29
93
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
94
|
+
# Gives the absolute URI path for use in a web context.
|
95
|
+
#
|
96
|
+
# Photo = Class.new(Struct.new(:id))
|
97
|
+
# i = Imagery.new(Photo.new(1001))
|
98
|
+
# i.url(:original) == '/system/photo/1001/original.png
|
99
|
+
# # => true
|
100
|
+
#
|
101
|
+
# i.file(:thumb)
|
102
|
+
# # raise Imagery::Model::UnknownSize
|
103
|
+
#
|
104
|
+
# i.sizes = { :thumb => ["100x100"] }
|
105
|
+
# i.file(:thumb) == '/system/photo/1001/thumb.png
|
106
|
+
# # => true
|
107
|
+
#
|
108
|
+
# @param [Symbol] size the size specific url.
|
109
|
+
# @raise [UnknownSize] if the size is not found in
|
110
|
+
# Imagery::Model#sizes.
|
111
|
+
# @return [String] the absolute URI path of the size specific url e.g.
|
112
|
+
# /system/photo/1/thumb.png
|
113
|
+
# where photo is the namespace and 1 is the key.
|
114
|
+
def url(size = self.default_size)
|
35
115
|
file(size).split('public').last
|
36
116
|
end
|
37
117
|
|
118
|
+
# This module is basically here so that plugins like Imagery::S3
|
119
|
+
# can override #save and #delete and call super.
|
38
120
|
module Persistence
|
121
|
+
# Writes the data in `io` and resizes them according to the different
|
122
|
+
# geometry strings.
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
#
|
126
|
+
# Photo = Class.new(Struct.new(:id))
|
127
|
+
# i = Imagery.new(Photo.new(1001))
|
128
|
+
# i.root = '/tmp'
|
129
|
+
# i.size = { :thumb => ["48x48"] }
|
130
|
+
# i.save(File.open('/path/to/file.jpg'))
|
131
|
+
#
|
132
|
+
# File.exist?("/tmp/public/system/photo/1001/thumb.png")
|
133
|
+
# # => true
|
134
|
+
#
|
135
|
+
# File.exist?("/tmp/public/system/photo/1001/original.png")
|
136
|
+
# # => true
|
137
|
+
#
|
138
|
+
# @param [#read] io any object responding to read. Typically
|
139
|
+
# a Rack filehandle is passed here from a
|
140
|
+
# controller in rails or a Sinatra handler.
|
141
|
+
# You may also use File.open to provide a proper
|
142
|
+
# IO handle.
|
143
|
+
# @return [true] returns when all other file operations are done.
|
39
144
|
def save(io)
|
40
145
|
FileUtils.mkdir_p(File.dirname(tmp))
|
41
146
|
File.open(tmp, "wb") { |target| target.write(io.read) }
|
@@ -44,7 +149,9 @@ module Imagery
|
|
44
149
|
|
45
150
|
return true
|
46
151
|
end
|
47
|
-
|
152
|
+
|
153
|
+
# Deletes all of the files related to this Imagery::Model instance.
|
154
|
+
# @return [true] when successfully deleted.
|
48
155
|
def delete
|
49
156
|
FileUtils.rm_rf File.dirname(file)
|
50
157
|
return true
|
@@ -53,6 +160,14 @@ module Imagery
|
|
53
160
|
include Persistence
|
54
161
|
|
55
162
|
private
|
163
|
+
def tmp
|
164
|
+
root_path(directory, namespace, key, 'tmp')
|
165
|
+
end
|
166
|
+
|
167
|
+
def namespace_for(klass)
|
168
|
+
klass.name.split('::').last.downcase
|
169
|
+
end
|
170
|
+
|
56
171
|
def filename(size)
|
57
172
|
"%s.png" % size
|
58
173
|
end
|
data/lib/imagery/s3.rb
CHANGED
@@ -9,8 +9,45 @@ module Imagery
|
|
9
9
|
end
|
10
10
|
|
11
11
|
S3_HOST = "http://s3.amazonaws.com"
|
12
|
-
|
13
|
-
|
12
|
+
|
13
|
+
# Returns a url publicly accessible. If a distribution domain is set,
|
14
|
+
# then the url will be based on that.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
#
|
18
|
+
# class Imagery::Model
|
19
|
+
# include Imagery::S3
|
20
|
+
#
|
21
|
+
# self.s3_bucket = 'bucket-name'
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Photo = Class.new(Struct.new(:id))
|
25
|
+
# i = Imagery.new(Photo.new(1001))
|
26
|
+
#
|
27
|
+
# i.url == 'http://s3.amazonaws.com/bucket-name/photo/1001/original.png'
|
28
|
+
# # => true
|
29
|
+
#
|
30
|
+
# Imagery::Model.s3_distribution_domain = 'assets.site.com'
|
31
|
+
# i.url == 'http://assets.site.com/photo/1001/original.png'
|
32
|
+
# # => true
|
33
|
+
#
|
34
|
+
# # You may also subclass this of course since it's just a ruby object
|
35
|
+
# # and configure them differently as needed.
|
36
|
+
#
|
37
|
+
# class CloudFront < Imagery::Model
|
38
|
+
# include Imagery::S3
|
39
|
+
# self.s3_bucket = 'cloudfront'
|
40
|
+
# self.s3_distribution_domain = 'assets.site.com'
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# class RegularS3 < Imagery::Model
|
44
|
+
# include Imagery::S3
|
45
|
+
# self.s3_bucket = 'cloudfront'
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @param [Symbol] size the preferred size you want for the url.
|
49
|
+
# @return [String] the size specific url.
|
50
|
+
def url(size = self.default_size)
|
14
51
|
if domain = self.class.s3_distribution_domain
|
15
52
|
[domain, namespace, key, filename(size)].join('/')
|
16
53
|
else
|
@@ -18,6 +55,8 @@ module Imagery
|
|
18
55
|
end
|
19
56
|
end
|
20
57
|
|
58
|
+
# In addition to saving the files and resizing them locally, uploads all
|
59
|
+
# the different sizes to amazon S3.
|
21
60
|
def save(io)
|
22
61
|
if super
|
23
62
|
s3_object_keys.each do |key, size|
|
@@ -31,6 +70,8 @@ module Imagery
|
|
31
70
|
end
|
32
71
|
end
|
33
72
|
|
73
|
+
# Deletes all the files related to this Imagery instance and also
|
74
|
+
# all the S3 keys.
|
34
75
|
def delete
|
35
76
|
super
|
36
77
|
s3_object_keys.each do |key, size|
|
@@ -46,11 +87,36 @@ module Imagery
|
|
46
87
|
end
|
47
88
|
|
48
89
|
module Gateway
|
90
|
+
# A wrapper for AWS::S3::S3Object.store. Basically auto-connects
|
91
|
+
# using the environment vars.
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
#
|
95
|
+
# AWS::S3::Base.connected?
|
96
|
+
# # => false
|
97
|
+
#
|
98
|
+
# Imagery::S3::Gateway.store(
|
99
|
+
# 'avatar', File.open('avatar.jpg'), 'bucket'
|
100
|
+
# )
|
101
|
+
# AWS::S3::Base.connected?
|
102
|
+
# # => true
|
103
|
+
#
|
49
104
|
def store(*args)
|
50
105
|
execute(:store, *args)
|
51
106
|
end
|
52
107
|
module_function :store
|
53
108
|
|
109
|
+
# A wrapper for AWS::S3::S3Object.delete. Basically auto-connects
|
110
|
+
# using the environment vars.
|
111
|
+
#
|
112
|
+
# @example
|
113
|
+
#
|
114
|
+
# AWS::S3::Base.connected?
|
115
|
+
# # => false
|
116
|
+
#
|
117
|
+
# Imagery::S3::Gateway.delete('avatar', 'bucket')
|
118
|
+
# AWS::S3::Base.connected?
|
119
|
+
# # => true
|
54
120
|
def delete(*args)
|
55
121
|
execute(:delete, *args)
|
56
122
|
end
|
data/lib/imagery/test.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Imagery
|
2
|
+
module Test
|
3
|
+
def self.included(base)
|
4
|
+
Imagery::Model.send :include, Imagery::Faking
|
5
|
+
Imagery::Model.mode = :fake
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
def imagery
|
10
|
+
if ENV["REAL_IMAGERY"]
|
11
|
+
Imagery::Model.real { yield true }
|
12
|
+
else
|
13
|
+
Imagery::Model.faked { yield false }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/test/test_imagery.rb
CHANGED
@@ -19,14 +19,14 @@ class TestImagery < Test::Unit::TestCase
|
|
19
19
|
test "root_path when no ROOT_DIR" do
|
20
20
|
imagery = Imagery.new(Photo.new(1001))
|
21
21
|
imagery.root = '/'
|
22
|
-
assert_equal "/public/system/photo/1001/tmp", imagery.tmp
|
22
|
+
assert_equal "/public/system/photo/1001/tmp", imagery.send(:tmp)
|
23
23
|
end
|
24
24
|
|
25
25
|
test "root_path when ROOT_DIR defined on imagery" do
|
26
26
|
Imagery::ROOT_DIR = '/root/dir'
|
27
27
|
|
28
28
|
imagery = Imagery.new(Photo.new(1001))
|
29
|
-
assert_equal "/root/dir/public/system/photo/1001/tmp", imagery.tmp
|
29
|
+
assert_equal "/root/dir/public/system/photo/1001/tmp", imagery.send(:tmp)
|
30
30
|
|
31
31
|
Imagery.send :remove_const, :ROOT_DIR
|
32
32
|
end
|
@@ -35,7 +35,7 @@ class TestImagery < Test::Unit::TestCase
|
|
35
35
|
Object::ROOT_DIR = '/root/dir'
|
36
36
|
|
37
37
|
imagery = Imagery.new(Photo.new(1001))
|
38
|
-
assert_equal "/root/dir/public/system/photo/1001/tmp", imagery.tmp
|
38
|
+
assert_equal "/root/dir/public/system/photo/1001/tmp", imagery.send(:tmp)
|
39
39
|
|
40
40
|
Object.send :remove_const, :ROOT_DIR
|
41
41
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Cyril David
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-31 00:00:00 +08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -73,18 +73,20 @@ extensions: []
|
|
73
73
|
|
74
74
|
extra_rdoc_files:
|
75
75
|
- LICENSE
|
76
|
-
- README.
|
76
|
+
- README.markdown
|
77
77
|
files:
|
78
78
|
- .document
|
79
79
|
- .gitignore
|
80
80
|
- LICENSE
|
81
|
-
- README.
|
81
|
+
- README.markdown
|
82
82
|
- Rakefile
|
83
83
|
- VERSION
|
84
|
+
- imagery.gemspec
|
84
85
|
- lib/imagery.rb
|
85
86
|
- lib/imagery/faking.rb
|
86
87
|
- lib/imagery/model.rb
|
87
88
|
- lib/imagery/s3.rb
|
89
|
+
- lib/imagery/test.rb
|
88
90
|
- test/fixtures/lake.jpg
|
89
91
|
- test/helper.rb
|
90
92
|
- test/test_imagery.rb
|
data/README.rdoc
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
= imagery
|
2
|
-
|
3
|
-
Description goes here.
|
4
|
-
|
5
|
-
== Note on Patches/Pull Requests
|
6
|
-
|
7
|
-
* Fork the project.
|
8
|
-
* Make your feature addition or bug fix.
|
9
|
-
* Add tests for it. This is important so I don't break it in a
|
10
|
-
future version unintentionally.
|
11
|
-
* Commit, do not mess with rakefile, version, or history.
|
12
|
-
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
-
* Send me a pull request. Bonus points for topic branches.
|
14
|
-
|
15
|
-
== Copyright
|
16
|
-
|
17
|
-
Copyright (c) 2010 Cyril David. See LICENSE for details.
|