httpimagestore 0.2.0 → 0.2.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/Gemfile +1 -1
- data/Gemfile.lock +8 -8
- data/VERSION +1 -1
- data/bin/httpimagestore +25 -55
- data/features/httpimagestore.feature +33 -43
- data/httpimagestore.gemspec +7 -6
- data/lib/httpimagestore/image_path.rb +54 -0
- data/lib/httpimagestore/s3_service.rb +25 -0
- data/spec/image_path_spec.rb +72 -0
- metadata +9 -8
- data/lib/httpimagestore/pathname.rb +0 -33
- data/spec/pathname_spec.rb +0 -17
data/Gemfile
CHANGED
@@ -11,7 +11,7 @@ gem "mime-types", "~> 1.17.2"
|
|
11
11
|
# Add dependencies to develop your gem here.
|
12
12
|
# Include everything needed to run rake, tests, features, etc.
|
13
13
|
group :development do
|
14
|
-
gem "rspec", "~> 2.
|
14
|
+
gem "rspec", "~> 2.8.0"
|
15
15
|
gem "cucumber", ">= 0"
|
16
16
|
gem "bundler", "~> 1.0.0"
|
17
17
|
gem "jeweler", "~> 1.6.4"
|
data/Gemfile.lock
CHANGED
@@ -56,14 +56,14 @@ GEM
|
|
56
56
|
rdoc (3.11)
|
57
57
|
json (~> 1.4)
|
58
58
|
rmagick (2.13.1)
|
59
|
-
rspec (2.
|
60
|
-
rspec-core (~> 2.
|
61
|
-
rspec-expectations (~> 2.
|
62
|
-
rspec-mocks (~> 2.
|
63
|
-
rspec-core (2.
|
64
|
-
rspec-expectations (2.
|
59
|
+
rspec (2.8.0)
|
60
|
+
rspec-core (~> 2.8.0)
|
61
|
+
rspec-expectations (~> 2.8.0)
|
62
|
+
rspec-mocks (~> 2.8.0)
|
63
|
+
rspec-core (2.8.0)
|
64
|
+
rspec-expectations (2.8.0)
|
65
65
|
diff-lcs (~> 1.1.2)
|
66
|
-
rspec-mocks (2.
|
66
|
+
rspec-mocks (2.8.0)
|
67
67
|
ruby-ip (0.9.0)
|
68
68
|
s3 (0.3.9)
|
69
69
|
proxies (~> 0.2.0)
|
@@ -90,7 +90,7 @@ DEPENDENCIES
|
|
90
90
|
prawn (= 0.8.4)
|
91
91
|
rcov
|
92
92
|
rdoc (~> 3.9)
|
93
|
-
rspec (~> 2.
|
93
|
+
rspec (~> 2.8.0)
|
94
94
|
ruby-ip (~> 0.9)
|
95
95
|
s3 (~> 0.3)
|
96
96
|
sinatra (>= 1.2.6)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/bin/httpimagestore
CHANGED
@@ -17,13 +17,13 @@ options = CLI.new do
|
|
17
17
|
end.parse!
|
18
18
|
|
19
19
|
require 'sinatra/base'
|
20
|
-
require 's3'
|
21
20
|
require 'httpthumbnailer-client'
|
22
21
|
require 'digest/sha2'
|
23
22
|
|
24
23
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
25
24
|
require 'httpimagestore/configuration'
|
26
|
-
require 'httpimagestore/
|
25
|
+
require 'httpimagestore/image_path'
|
26
|
+
require 'httpimagestore/s3_service'
|
27
27
|
|
28
28
|
sinatra = Sinatra.new
|
29
29
|
|
@@ -43,33 +43,19 @@ sinatra.set :uri_encode, (not options.no_uri_encode)
|
|
43
43
|
|
44
44
|
Configuration.from_file(options.config).put(sinatra)
|
45
45
|
|
46
|
-
class
|
47
|
-
def initialize(
|
48
|
-
@
|
49
|
-
|
50
|
-
|
51
|
-
@s3 = S3::Service.new(:access_key_id => key_id, :secret_access_key => key_secret)
|
52
|
-
|
53
|
-
@logger.info "Getting bucket: #{bucket}"
|
54
|
-
@bucket = @s3.buckets.find(bucket) or fail "no buckte '#{bucket}' found"
|
46
|
+
class ThumbnailingError < RuntimeError
|
47
|
+
def initialize(thumbnail_class, remote_error)
|
48
|
+
@remote_error = remote_error
|
49
|
+
super "Thumbnailing for class '#{thumbnail_class.name}' failed: #{remote_error.message}"
|
55
50
|
end
|
56
51
|
|
57
|
-
|
58
|
-
@logger.info "Putting image in bucket '#{@bucket.name}': #{image_path}"
|
59
|
-
|
60
|
-
file = @bucket.objects.build(image_path)
|
61
|
-
file.content_type = content_type
|
62
|
-
file.content = data
|
63
|
-
file.save
|
64
|
-
|
65
|
-
"http://#{@bucket.name}.s3.amazonaws.com/#{image_path}"
|
66
|
-
end
|
52
|
+
attr_reader :remote_error
|
67
53
|
end
|
68
54
|
|
69
55
|
sinatra.before do
|
70
56
|
logger.level = Logger::DEBUG if settings.logging and settings.debug
|
71
57
|
# singletons
|
72
|
-
$s3 ||=
|
58
|
+
$s3 ||= S3Service.new(settings.s3_key_id, settings.s3_key_secret, settings.s3_bucket, :logger => logger)
|
73
59
|
$thumbnailer ||= HTTPThumbnailerClient.new(settings.thumbnailer_url)
|
74
60
|
end
|
75
61
|
|
@@ -87,54 +73,42 @@ sinatra.helpers do
|
|
87
73
|
def digest(data)
|
88
74
|
Digest::SHA2.new.update(data).to_s[0,16]
|
89
75
|
end
|
90
|
-
|
91
|
-
class BadRequestError < RuntimeError
|
92
|
-
end
|
93
|
-
|
94
|
-
class ThumbnailingError < RuntimeError
|
95
|
-
def initialize(thumbnail_class, remote_error)
|
96
|
-
@remote_error = remote_error
|
97
|
-
super "Thumbnailing for class '#{thumbnail_class.name}' failed: #{remote_error.message}"
|
98
|
-
end
|
99
|
-
|
100
|
-
attr_reader :remote_error
|
101
|
-
end
|
102
76
|
end
|
103
77
|
|
104
78
|
sinatra.get '/' do
|
105
79
|
"hello"
|
106
80
|
end
|
107
81
|
|
108
|
-
sinatra.put %r{/thumbnail/([^/]*)/?(.*)} do |
|
109
|
-
|
110
|
-
image_path = Pathname.new(image_path).extend(Pathname::AutoPath)
|
111
|
-
else
|
112
|
-
image_path = Pathname.new(image_path).extend(Pathname::CustomPath)
|
113
|
-
end
|
82
|
+
sinatra.put %r{/thumbnail/([^/]*)/?(.*)} do |thumbnail_classes, image_path|
|
83
|
+
thumbnail_classes = thumbnail_classes.split(',').map{|thumbnail_class| settings.thumbnail_classes[thumbnail_class]}
|
114
84
|
|
115
|
-
classes = classes.split(',').map{|tc| settings.thumbnail_classes[tc]}
|
116
85
|
image = request.body.read
|
86
|
+
image_hash = digest(image)
|
87
|
+
|
88
|
+
unless image_path.empty?
|
89
|
+
image_path = ImagePath::Custom.new(image_hash, image_path)
|
90
|
+
else
|
91
|
+
image_path = ImagePath::Auto.new(image_hash)
|
92
|
+
end
|
117
93
|
|
118
|
-
|
119
|
-
|
120
|
-
thumbnail
|
94
|
+
thumbnails = $thumbnailer.thumbnail(image) do
|
95
|
+
thumbnail_classes.each do |thumbnail_class|
|
96
|
+
thumbnail thumbnail_class.method, thumbnail_class.width, thumbnail_class.height, thumbnail_class.format, thumbnail_class.options
|
121
97
|
end
|
122
98
|
end
|
123
99
|
|
124
100
|
# check for errors
|
125
|
-
|
126
|
-
raise ThumbnailingError.new(thumbnail_class,
|
101
|
+
thumbnails.zip(thumbnail_classes).each do |thumbnail, thumbnail_class|
|
102
|
+
raise ThumbnailingError.new(thumbnail_class, thumbnail) if thumbnail.kind_of? HTTPThumbnailerClient::ThumbnailingError
|
127
103
|
end
|
128
104
|
|
129
105
|
urls = []
|
130
106
|
|
131
|
-
image_hash = digest(image)
|
132
|
-
|
133
107
|
# store all images
|
134
|
-
urls << $s3.put_image(image_path.original_image(
|
108
|
+
urls << $s3.put_image(image_path.original_image(thumbnails.input_mime_type), thumbnails.input_mime_type, image)
|
135
109
|
|
136
|
-
|
137
|
-
urls << $s3.put_image(image_path.thumbnail_image(
|
110
|
+
thumbnails.zip(thumbnail_classes).each do |thumbnail, thumbnail_class|
|
111
|
+
urls << $s3.put_image(image_path.thumbnail_image(thumbnail.mime_type, thumbnail_class.name), thumbnail.mime_type, thumbnail.data)
|
138
112
|
end
|
139
113
|
|
140
114
|
status 200
|
@@ -162,10 +136,6 @@ sinatra.error ThumbnailingError do
|
|
162
136
|
end
|
163
137
|
end
|
164
138
|
|
165
|
-
sinatra.error BadRequestError do
|
166
|
-
halt_exception(400, env['sinatra.error'])
|
167
|
-
end
|
168
|
-
|
169
139
|
sinatra.error Configuration::ThumbnailClassDoesNotExistError do
|
170
140
|
halt_exception(404, env['sinatra.error'])
|
171
141
|
end
|
@@ -12,7 +12,7 @@ Feature: Storing of original image and specified classes of its thumbnails on S3
|
|
12
12
|
|
13
13
|
thumbnail_class 'small', 'crop', 128, 128
|
14
14
|
thumbnail_class 'tiny', 'crop', 32, 32
|
15
|
-
thumbnail_class '
|
15
|
+
thumbnail_class 'tiny_png', 'crop', 32, 32, 'PNG'
|
16
16
|
thumbnail_class 'bad', 'crop', 0, 0
|
17
17
|
thumbnail_class 'superlarge', 'crop', 16000, 16000
|
18
18
|
thumbnail_class 'large_png', 'crop', 7000, 7000, 'PNG'
|
@@ -24,84 +24,74 @@ Feature: Storing of original image and specified classes of its thumbnails on S3
|
|
24
24
|
Scenario: Putting original and its thumbnails to S3 bucket
|
25
25
|
Given there is no 4006450256177f4a.jpg file in S3 bucket
|
26
26
|
And there is no 4006450256177f4a/small.jpg file in S3 bucket
|
27
|
-
And there is no 4006450256177f4a/
|
27
|
+
And there is no 4006450256177f4a/tiny_png.png file in S3 bucket
|
28
28
|
Given test.jpg file content as request body
|
29
|
-
When I do PUT request http://localhost:3000/thumbnail/small,
|
29
|
+
When I do PUT request http://localhost:3000/thumbnail/small,tiny_png
|
30
30
|
Then response status will be 200
|
31
31
|
And response content type will be text/uri-list
|
32
32
|
And response body will be CRLF ended lines
|
33
33
|
"""
|
34
34
|
http://issthumbtest.s3.amazonaws.com/4006450256177f4a.jpg
|
35
35
|
http://issthumbtest.s3.amazonaws.com/4006450256177f4a/small.jpg
|
36
|
-
http://issthumbtest.s3.amazonaws.com/4006450256177f4a/
|
37
|
-
"""
|
38
|
-
|
39
|
-
And http://issthumbtest.s3.amazonaws.com/4006450256177f4a
|
40
|
-
|
36
|
+
http://issthumbtest.s3.amazonaws.com/4006450256177f4a/tiny_png.png
|
37
|
+
"""
|
38
|
+
Then http://issthumbtest.s3.amazonaws.com/4006450256177f4a.jpg will contain JPEG image of size 509x719
|
39
|
+
And http://issthumbtest.s3.amazonaws.com/4006450256177f4a.jpg content type will be image/jpeg
|
40
|
+
Then http://issthumbtest.s3.amazonaws.com/4006450256177f4a/small.jpg will contain JPEG image of size 128x128
|
41
|
+
And http://issthumbtest.s3.amazonaws.com/4006450256177f4a/small.jpg content type will be image/jpeg
|
42
|
+
Then http://issthumbtest.s3.amazonaws.com/4006450256177f4a/tiny_png.png will contain PNG image of size 32x32
|
43
|
+
And http://issthumbtest.s3.amazonaws.com/4006450256177f4a/tiny_png.png content type will be image/png
|
41
44
|
|
42
45
|
Scenario: Putting original and its thumbnails to S3 bucket under custom path
|
43
46
|
Given there is no test/image/4006450256177f4a/test.jpg file in S3 bucket
|
44
47
|
And there is no test/image/4006450256177f4a/test-small.jpg file in S3 bucket
|
45
|
-
And there is no test/image/4006450256177f4a/test-
|
48
|
+
And there is no test/image/4006450256177f4a/test-tiny_png.png file in S3 bucket
|
46
49
|
Given test.jpg file content as request body
|
47
|
-
When I do PUT request http://localhost:3000/thumbnail/small,
|
50
|
+
When I do PUT request http://localhost:3000/thumbnail/small,tiny_png/test/image/test
|
48
51
|
Then response status will be 200
|
49
52
|
And response content type will be text/uri-list
|
50
53
|
And response body will be CRLF ended lines
|
51
54
|
"""
|
52
55
|
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg
|
53
56
|
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg
|
54
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-
|
55
|
-
"""
|
56
|
-
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg will contain JPEG image of size 509x719
|
57
|
-
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg will contain JPEG image of size 128x128
|
58
|
-
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny.jpg will contain JPEG image of size 32x32
|
59
|
-
|
60
|
-
Scenario: Putting original and its thumbnails to S3 bucket under custom path with UTF characters (encoded) in the path and name
|
61
|
-
Given there is no test/图像/4006450256177f4a/测试.jpg file in S3 bucket
|
62
|
-
And there is no test/图像/4006450256177f4a/测试-small.jpg file in S3 bucket
|
63
|
-
Given test.jpg file content as request body
|
64
|
-
When I do PUT request http://localhost:3000/thumbnail/small/test/图像/测试.jpg
|
65
|
-
Then response status will be 200
|
66
|
-
And response content type will be text/uri-list
|
67
|
-
And response body will be CRLF ended lines
|
68
|
-
"""
|
69
|
-
http://issthumbtest.s3.amazonaws.com/test/%E5%9B%BE%E5%83%8F/4006450256177f4a/%E6%B5%8B%E8%AF%95.jpg
|
70
|
-
http://issthumbtest.s3.amazonaws.com/test/%E5%9B%BE%E5%83%8F/4006450256177f4a/%E6%B5%8B%E8%AF%95-small.jpg
|
57
|
+
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png
|
71
58
|
"""
|
72
|
-
|
73
|
-
And http://issthumbtest.s3.amazonaws.com/test
|
59
|
+
Then http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg will contain JPEG image of size 509x719
|
60
|
+
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg content type will be image/jpeg
|
61
|
+
Then http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg will contain JPEG image of size 128x128
|
62
|
+
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg content type will be image/jpeg
|
63
|
+
Then http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png will contain PNG image of size 32x32
|
64
|
+
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png content type will be image/png
|
74
65
|
|
75
|
-
Scenario:
|
66
|
+
Scenario: Custom path name can contain file name extension that may be used as failback to content based detection
|
76
67
|
Given there is no test/image/4006450256177f4a/test.jpg file in S3 bucket
|
68
|
+
And there is no test/image/4006450256177f4a/test-tiny_png.jpg file in S3 bucket
|
77
69
|
Given test.jpg file content as request body
|
78
|
-
|
79
|
-
When I do PUT request http://localhost:3000/thumbnail/tiny/test/image/test.jpg
|
70
|
+
When I do PUT request http://localhost:3000/thumbnail/tiny_png/test/image/test.jpg
|
80
71
|
Then response status will be 200
|
81
72
|
And response content type will be text/uri-list
|
82
73
|
And response body will be CRLF ended lines
|
83
74
|
"""
|
84
75
|
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg
|
85
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-
|
76
|
+
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png
|
86
77
|
"""
|
87
78
|
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg content type will be image/jpeg
|
79
|
+
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png content type will be image/png
|
88
80
|
|
89
|
-
Scenario:
|
90
|
-
Given there is no test
|
91
|
-
And there is no test
|
92
|
-
And there is no test/image/4006450256177f4a/test-png.png file in S3 bucket
|
81
|
+
Scenario: Custom path name encoding when UTF-8 characters can be used
|
82
|
+
Given there is no test/图像/4006450256177f4a/测试.jpg file in S3 bucket
|
83
|
+
And there is no test/图像/4006450256177f4a/测试-small.jpg file in S3 bucket
|
93
84
|
Given test.jpg file content as request body
|
94
|
-
When I do PUT request http://localhost:3000/thumbnail/
|
85
|
+
When I do PUT request http://localhost:3000/thumbnail/small/test/图像/测试
|
95
86
|
Then response status will be 200
|
96
87
|
And response content type will be text/uri-list
|
97
88
|
And response body will be CRLF ended lines
|
98
89
|
"""
|
99
|
-
http://issthumbtest.s3.amazonaws.com/test/
|
100
|
-
http://issthumbtest.s3.amazonaws.com/test/
|
101
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-png.png
|
90
|
+
http://issthumbtest.s3.amazonaws.com/test/%E5%9B%BE%E5%83%8F/4006450256177f4a/%E6%B5%8B%E8%AF%95.jpg
|
91
|
+
http://issthumbtest.s3.amazonaws.com/test/%E5%9B%BE%E5%83%8F/4006450256177f4a/%E6%B5%8B%E8%AF%95-small.jpg
|
102
92
|
"""
|
103
|
-
And http://issthumbtest.s3.amazonaws.com/test
|
104
|
-
And http://issthumbtest.s3.amazonaws.com/test
|
93
|
+
And http://issthumbtest.s3.amazonaws.com/test/图像/4006450256177f4a/测试.jpg will contain JPEG image of size 509x719
|
94
|
+
And http://issthumbtest.s3.amazonaws.com/test/图像/4006450256177f4a/测试-small.jpg will contain JPEG image of size 128x128
|
105
95
|
|
106
96
|
Scenario: Reporting of missing resource
|
107
97
|
When I do GET request http://localhost:3000/blah
|
data/httpimagestore.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "httpimagestore"
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jakub Pastuszek"]
|
@@ -35,10 +35,11 @@ Gem::Specification.new do |s|
|
|
35
35
|
"features/support/test.txt",
|
36
36
|
"httpimagestore.gemspec",
|
37
37
|
"lib/httpimagestore/configuration.rb",
|
38
|
-
"lib/httpimagestore/
|
38
|
+
"lib/httpimagestore/image_path.rb",
|
39
|
+
"lib/httpimagestore/s3_service.rb",
|
39
40
|
"lib/httpimagestore/thumbnail_class.rb",
|
40
41
|
"spec/configuration_spec.rb",
|
41
|
-
"spec/
|
42
|
+
"spec/image_path_spec.rb",
|
42
43
|
"spec/spec_helper.rb",
|
43
44
|
"spec/test.cfg"
|
44
45
|
]
|
@@ -59,7 +60,7 @@ Gem::Specification.new do |s|
|
|
59
60
|
s.add_runtime_dependency(%q<ruby-ip>, ["~> 0.9"])
|
60
61
|
s.add_runtime_dependency(%q<cli>, ["~> 1.1.0"])
|
61
62
|
s.add_runtime_dependency(%q<mime-types>, ["~> 1.17.2"])
|
62
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.
|
63
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
|
63
64
|
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
64
65
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
65
66
|
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
@@ -76,7 +77,7 @@ Gem::Specification.new do |s|
|
|
76
77
|
s.add_dependency(%q<ruby-ip>, ["~> 0.9"])
|
77
78
|
s.add_dependency(%q<cli>, ["~> 1.1.0"])
|
78
79
|
s.add_dependency(%q<mime-types>, ["~> 1.17.2"])
|
79
|
-
s.add_dependency(%q<rspec>, ["~> 2.
|
80
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
80
81
|
s.add_dependency(%q<cucumber>, [">= 0"])
|
81
82
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
82
83
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
@@ -94,7 +95,7 @@ Gem::Specification.new do |s|
|
|
94
95
|
s.add_dependency(%q<ruby-ip>, ["~> 0.9"])
|
95
96
|
s.add_dependency(%q<cli>, ["~> 1.1.0"])
|
96
97
|
s.add_dependency(%q<mime-types>, ["~> 1.17.2"])
|
97
|
-
s.add_dependency(%q<rspec>, ["~> 2.
|
98
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
98
99
|
s.add_dependency(%q<cucumber>, [">= 0"])
|
99
100
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
100
101
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'mime/types'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
class ImagePath
|
5
|
+
class CouldNotDetermineFileExtensionError < ArgumentError
|
6
|
+
def initialize(mime_type)
|
7
|
+
super "could not determine file extension for mime type: #{mime_type}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(id)
|
12
|
+
@id = id.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def mime_extension(mime_type)
|
18
|
+
mime = MIME::Types[mime_type].first or raise CouldNotDetermineFileExtensionError, mime_type
|
19
|
+
'.' + (mime.extensions.select{|e| e.length == 3}.first or mime.extensions.first)
|
20
|
+
end
|
21
|
+
|
22
|
+
class Auto < ImagePath
|
23
|
+
def original_image(mime_type)
|
24
|
+
"#{@id}#{mime_extension(mime_type)}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def thumbnail_image(mime_type, thumbnail_class)
|
28
|
+
"#{@id}/#{thumbnail_class}#{mime_extension(mime_type)}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Custom < ImagePath
|
33
|
+
def initialize(id, path)
|
34
|
+
super(id)
|
35
|
+
@path = Pathname.new(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def original_image(mime_type)
|
39
|
+
extension = begin
|
40
|
+
mime_extension(mime_type)
|
41
|
+
rescue CouldNotDetermineFileExtensionError
|
42
|
+
raise if @path.extname.empty?
|
43
|
+
@path.extname
|
44
|
+
end
|
45
|
+
|
46
|
+
(@path.dirname + @id + "#{@path.basename(@path.extname)}#{extension}").to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def thumbnail_image(mime_type, thumbnail_class)
|
50
|
+
(@path.dirname + @id + "#{@path.basename(@path.extname)}-#{thumbnail_class}#{mime_extension(mime_type)}").to_s
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 's3'
|
2
|
+
|
3
|
+
class S3Service
|
4
|
+
def initialize(key_id, key_secret, bucket, options = {})
|
5
|
+
@options = options
|
6
|
+
@logger = (options[:logger] or Logger.new('/dev/null'))
|
7
|
+
|
8
|
+
@s3 = S3::Service.new(:access_key_id => key_id, :secret_access_key => key_secret)
|
9
|
+
|
10
|
+
@logger.info "Getting bucket: #{bucket}"
|
11
|
+
@bucket = @s3.buckets.find(bucket) or fail "no buckte '#{bucket}' found"
|
12
|
+
end
|
13
|
+
|
14
|
+
def put_image(image_path, content_type, data)
|
15
|
+
@logger.info "Putting image in bucket '#{@bucket.name}': #{image_path}"
|
16
|
+
|
17
|
+
file = @bucket.objects.build(image_path)
|
18
|
+
file.content_type = content_type
|
19
|
+
file.content = data
|
20
|
+
file.save
|
21
|
+
|
22
|
+
"http://#{@bucket.name}.s3.amazonaws.com/#{image_path}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'httpimagestore/image_path'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
shared_examples "extension handling" do |image_path|
|
6
|
+
describe "#original_image" do
|
7
|
+
it "determines extension based on mime type" do
|
8
|
+
Pathname.new(image_path.original_image("image/jpeg")).extname.should == ".jpg"
|
9
|
+
Pathname.new(image_path.original_image("image/tiff")).extname.should == ".tif"
|
10
|
+
Pathname.new(image_path.original_image("image/png")).extname.should == ".png"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should fail if provided extension from mime type could not be determined" do
|
14
|
+
lambda {
|
15
|
+
image_path.original_image("image/xyz")
|
16
|
+
}.should raise_error ImagePath::CouldNotDetermineFileExtensionError, "could not determine file extension for mime type: image/xyz"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#thumbnail_image" do
|
21
|
+
it "determines extension based on mime type" do
|
22
|
+
Pathname.new(image_path.thumbnail_image("image/jpeg", "small")).extname.should == ".jpg"
|
23
|
+
Pathname.new(image_path.thumbnail_image("image/tiff", "small")).extname.should == ".tif"
|
24
|
+
Pathname.new(image_path.thumbnail_image("image/png", "small")).extname.should == ".png"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should fail if provided extension from mime type could not be determined" do
|
28
|
+
lambda {
|
29
|
+
image_path.thumbnail_image("image/xyz", "small")
|
30
|
+
}.should raise_error ImagePath::CouldNotDetermineFileExtensionError, "could not determine file extension for mime type: image/xyz"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ImagePath do
|
36
|
+
describe ImagePath::Auto do
|
37
|
+
describe "#original_image" do
|
38
|
+
it "returns path in format <id>.<ext>" do
|
39
|
+
ImagePath::Auto.new(123).original_image("image/jpeg").should == "123.jpg"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#thumbnail_image" do
|
44
|
+
it "returns path in format <id>/<class>.<ext>" do
|
45
|
+
ImagePath::Auto.new(123).thumbnail_image("image/jpeg", "small").should == "123/small.jpg"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
include_examples "extension handling", ImagePath::Auto.new(123)
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ImagePath::Custom do
|
53
|
+
describe "#original_image" do
|
54
|
+
it "returns path in format abc/<id>/xyz.<ext>" do
|
55
|
+
ImagePath::Custom.new(123, "test/file/path.jpg").original_image("image/jpeg").should == "test/file/123/path.jpg"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should fail back to provided extension if extension from mime type could not be determined" do
|
59
|
+
Pathname.new(ImagePath::Custom.new(123, "test/file/path.abc").original_image("image/xyz")).extname.should == ".abc"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#thumbnail_image" do
|
64
|
+
it "returns path in format abc/<id>/xyz-<class>.<ext>" do
|
65
|
+
ImagePath::Custom.new(123, "test/file/path.jpg").thumbnail_image("image/jpeg", "small").should == "test/file/123/path-small.jpg"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
include_examples "extension handling", ImagePath::Custom.new(123, "test/file/path")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpimagestore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 1
|
10
|
+
version: 0.2.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jakub Pastuszek
|
@@ -134,12 +134,12 @@ dependencies:
|
|
134
134
|
requirements:
|
135
135
|
- - ~>
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
hash:
|
137
|
+
hash: 47
|
138
138
|
segments:
|
139
139
|
- 2
|
140
|
-
-
|
140
|
+
- 8
|
141
141
|
- 0
|
142
|
-
version: 2.
|
142
|
+
version: 2.8.0
|
143
143
|
prerelease: false
|
144
144
|
name: rspec
|
145
145
|
version_requirements: *id008
|
@@ -291,10 +291,11 @@ files:
|
|
291
291
|
- features/support/test.txt
|
292
292
|
- httpimagestore.gemspec
|
293
293
|
- lib/httpimagestore/configuration.rb
|
294
|
-
- lib/httpimagestore/
|
294
|
+
- lib/httpimagestore/image_path.rb
|
295
|
+
- lib/httpimagestore/s3_service.rb
|
295
296
|
- lib/httpimagestore/thumbnail_class.rb
|
296
297
|
- spec/configuration_spec.rb
|
297
|
-
- spec/
|
298
|
+
- spec/image_path_spec.rb
|
298
299
|
- spec/spec_helper.rb
|
299
300
|
- spec/test.cfg
|
300
301
|
homepage: http://github.com/jpastuszek/httpimagestore
|
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'mime/types'
|
2
|
-
|
3
|
-
class Pathname
|
4
|
-
def mime_extension(mime_type)
|
5
|
-
mime = MIME::Types[mime_type].first or return extname
|
6
|
-
'.' + (mime.extensions.select{|e| e.length == 3}.first or mime.extensions.first)
|
7
|
-
end
|
8
|
-
|
9
|
-
module AutoPath
|
10
|
-
# <id>.<ext>
|
11
|
-
def original_image(id, mime_type)
|
12
|
-
"#{id.to_s}#{mime_extension(mime_type)}"
|
13
|
-
end
|
14
|
-
|
15
|
-
# <id>/<class>.<ext>
|
16
|
-
def thumbnail_image(id, mime_type, thumbnail_class)
|
17
|
-
"#{id.to_s}/#{thumbnail_class}#{mime_extension(mime_type)}"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
module CustomPath
|
22
|
-
# abc/xyz.jpg => abc/<id>/xyz.jpg
|
23
|
-
def original_image(id, mime_type)
|
24
|
-
dirname + id.to_s + basename
|
25
|
-
end
|
26
|
-
|
27
|
-
# abc/xyz.jpg => abc/<id>/xyz-<class>.jpg
|
28
|
-
def thumbnail_image(id, mime_type, thumbnail_class)
|
29
|
-
dirname + id.to_s + "#{basename(extname)}-#{thumbnail_class}#{mime_extension(mime_type)}"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
data/spec/pathname_spec.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
require 'httpimagestore/pathname'
|
3
|
-
|
4
|
-
describe Pathname do
|
5
|
-
describe "#original_image" do
|
6
|
-
it "returns path with tid" do
|
7
|
-
Pathname.new("test/file/path.jpg").original_image(123).to_s.should == "test/file/123/path.jpg"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "#thumbnail_image" do
|
12
|
-
it "returns path with tid and thumbnail class name in file name" do
|
13
|
-
Pathname.new("test/file/path.jpg").thumbnail_image(123, 'small').to_s.should == "test/file/123/path-small.jpg"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|