httpimagestore 1.5.0 → 1.6.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/.idea/httpimagestore.iml +1 -1
- data/Gemfile.lock +4 -4
- data/README.md +12 -0
- data/VERSION +1 -1
- data/httpimagestore.gemspec +2 -2
- data/lib/httpimagestore/configuration/file.rb +9 -8
- data/lib/httpimagestore/configuration/output.rb +48 -17
- data/spec/configuration_output_spec.rb +120 -0
- metadata +3 -3
data/.idea/httpimagestore.iml
CHANGED
@@ -62,7 +62,7 @@
|
|
62
62
|
<orderEntry type="library" scope="PROVIDED" name="ruby-ip (v0.9.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
63
63
|
<orderEntry type="library" scope="PROVIDED" name="sdl4r (v0.9.11, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
64
64
|
<orderEntry type="library" scope="PROVIDED" name="unicorn (v4.6.3, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
65
|
-
<orderEntry type="library" scope="PROVIDED" name="unicorn-cuba-base (v1.1.
|
65
|
+
<orderEntry type="library" scope="PROVIDED" name="unicorn-cuba-base (v1.1.2, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
66
66
|
<orderEntry type="library" scope="PROVIDED" name="uuidtools (v2.1.4, RVM: ruby-1.9.3-p429) [gem]" level="application" />
|
67
67
|
</component>
|
68
68
|
</module>
|
data/Gemfile.lock
CHANGED
@@ -39,14 +39,14 @@ GEM
|
|
39
39
|
nokogiri (~> 1.5.2)
|
40
40
|
oauth2
|
41
41
|
hashie (2.0.5)
|
42
|
-
highline (1.6.
|
42
|
+
highline (1.6.20)
|
43
43
|
httpauth (0.2.0)
|
44
44
|
httpclient (2.3.4.1)
|
45
45
|
httpthumbnailer-client (1.1.1)
|
46
46
|
cli (~> 1.3)
|
47
47
|
httpclient (>= 2.3)
|
48
48
|
multipart-parser (~> 0.1.1)
|
49
|
-
jeweler (1.8.
|
49
|
+
jeweler (1.8.8)
|
50
50
|
builder
|
51
51
|
bundler (~> 1.0)
|
52
52
|
git (>= 1.2.5)
|
@@ -55,13 +55,13 @@ GEM
|
|
55
55
|
nokogiri (= 1.5.10)
|
56
56
|
rake
|
57
57
|
rdoc
|
58
|
-
json (1.8.
|
58
|
+
json (1.8.1)
|
59
59
|
jwt (0.1.8)
|
60
60
|
multi_json (>= 1.5)
|
61
61
|
kgio (2.8.1)
|
62
62
|
mime-types (1.25)
|
63
63
|
msgpack (0.5.5)
|
64
|
-
multi_json (1.8.
|
64
|
+
multi_json (1.8.2)
|
65
65
|
multi_test (0.0.2)
|
66
66
|
multi_xml (0.5.5)
|
67
67
|
multipart-parser (0.1.1)
|
data/README.md
CHANGED
@@ -17,6 +17,10 @@ It is using [HTTP Thumbnailer](https://github.com/jpastuszek/httpthumbnailer) as
|
|
17
17
|
|
18
18
|
## Changelog
|
19
19
|
|
20
|
+
### 1.6.0
|
21
|
+
* `output_store_path` and `output_store_url` `path` argument support
|
22
|
+
* encoding resulting file storage URL
|
23
|
+
|
20
24
|
### 1.5.0
|
21
25
|
* `output_ok` and `output_text` support
|
22
26
|
* named captures regexp matcher support
|
@@ -447,6 +451,10 @@ Arguments:
|
|
447
451
|
|
448
452
|
1. image names - names of images to output storage path for in order
|
449
453
|
|
454
|
+
Options:
|
455
|
+
|
456
|
+
* `path` - name of predefined path that will be used to generate output path; `#{path}` variable and all derivative will be replaced with given image storage path; if not specified the actual storage path will be provided
|
457
|
+
|
450
458
|
Example:
|
451
459
|
|
452
460
|
```sdl
|
@@ -498,6 +506,10 @@ Arguments:
|
|
498
506
|
|
499
507
|
1. image names - names of images
|
500
508
|
|
509
|
+
Options:
|
510
|
+
|
511
|
+
* `path` - name of predefined path that will be used to generate output URL path part; `#{path}` variable and all derivative will be replaced with given image storage path; if not specified the original URL will be provided
|
512
|
+
|
501
513
|
Example:
|
502
514
|
|
503
515
|
```sdl
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.6.0
|
data/httpimagestore.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "httpimagestore"
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "1.6.0"
|
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"]
|
12
|
-
s.date = "2013-
|
12
|
+
s.date = "2013-11-04"
|
13
13
|
s.description = "Thumbnails images using httpthumbnailer and stored data on HTTP server (S3)"
|
14
14
|
s.email = "jpastuszek@gmail.com"
|
15
15
|
s.executables = ["httpimagestore"]
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'httpimagestore/configuration/path'
|
2
2
|
require 'httpimagestore/configuration/handler'
|
3
3
|
require 'pathname'
|
4
|
+
require 'uri'
|
4
5
|
|
5
6
|
module Configuration
|
6
7
|
class FileStorageOutsideOfRootDirError < ConfigurationError
|
@@ -18,7 +19,7 @@ module Configuration
|
|
18
19
|
class FileSourceStoreBase < SourceStoreBase
|
19
20
|
extend Stats
|
20
21
|
def_stats(
|
21
|
-
:total_file_store,
|
22
|
+
:total_file_store,
|
22
23
|
:total_file_store_bytes,
|
23
24
|
:total_file_source,
|
24
25
|
:total_file_source_bytes
|
@@ -31,11 +32,11 @@ module Configuration
|
|
31
32
|
matcher = InclusionMatcher.new(image_name, if_image_name_on)
|
32
33
|
|
33
34
|
self.new(
|
34
|
-
configuration.global,
|
35
|
-
image_name,
|
35
|
+
configuration.global,
|
36
|
+
image_name,
|
36
37
|
matcher,
|
37
|
-
root_dir,
|
38
|
-
path_spec
|
38
|
+
root_dir,
|
39
|
+
path_spec
|
39
40
|
)
|
40
41
|
end
|
41
42
|
|
@@ -80,7 +81,7 @@ module Configuration
|
|
80
81
|
FileSourceStoreBase.stats.incr_total_file_source_bytes(data.bytesize)
|
81
82
|
|
82
83
|
image = Image.new(data)
|
83
|
-
image.source_url = "file://#{rendered_path}"
|
84
|
+
image.source_url = "file://#{URI.encode(rendered_path.to_s)}"
|
84
85
|
image
|
85
86
|
rescue Errno::ENOENT
|
86
87
|
raise NoSuchFileError.new(image_name, rendered_path)
|
@@ -89,7 +90,7 @@ module Configuration
|
|
89
90
|
end
|
90
91
|
end
|
91
92
|
Handler::register_node_parser FileSource
|
92
|
-
|
93
|
+
|
93
94
|
class FileStore < FileSourceStoreBase
|
94
95
|
include ClassLogging
|
95
96
|
|
@@ -105,7 +106,7 @@ module Configuration
|
|
105
106
|
get_named_image_for_storage(request_state) do |image_name, image, rendered_path|
|
106
107
|
storage_path = storage_path(rendered_path)
|
107
108
|
|
108
|
-
image.store_url = "file://#{rendered_path.to_s}"
|
109
|
+
image.store_url = "file://#{URI.encode(rendered_path.to_s)}"
|
109
110
|
|
110
111
|
log.info "storing '#{image_name}' in file '#{storage_path}' (#{image.data.length} bytes)"
|
111
112
|
storage_path.open('wb'){|io| io.write image.data}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'httpimagestore/configuration/handler'
|
2
2
|
require 'httpimagestore/ruby_string_template'
|
3
|
+
require 'uri'
|
3
4
|
|
4
5
|
module Configuration
|
5
6
|
class StorePathNotSetForImage < ConfigurationError
|
@@ -62,29 +63,59 @@ module Configuration
|
|
62
63
|
Handler::register_node_parser OutputText
|
63
64
|
|
64
65
|
class OutputMultiBase
|
65
|
-
class
|
66
|
+
class OutputSpec
|
66
67
|
include ConditionalInclusion
|
68
|
+
attr_reader :image_name
|
69
|
+
attr_reader :path_spec
|
67
70
|
|
68
|
-
def initialize(
|
69
|
-
|
71
|
+
def initialize(global, image_name, path_spec, matcher)
|
72
|
+
@global = global
|
73
|
+
@image_name = image_name
|
74
|
+
@path_spec = path_spec
|
70
75
|
inclusion_matcher matcher
|
71
76
|
end
|
77
|
+
|
78
|
+
def store_path(request_state)
|
79
|
+
store_path = request_state.images[@image_name].store_path or raise StorePathNotSetForImage.new(@image_name)
|
80
|
+
return store_path unless @path_spec
|
81
|
+
rendered_path(store_path, request_state)
|
82
|
+
end
|
83
|
+
|
84
|
+
def store_url(request_state)
|
85
|
+
store_url = request_state.images[@image_name].store_url or raise StoreURLNotSetForImage.new(@image_name)
|
86
|
+
return store_url unless @path_spec
|
87
|
+
uri = URI(store_url)
|
88
|
+
uri.path = '/' + URI.encode(rendered_path(URI.decode(uri.path), request_state))
|
89
|
+
uri
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def rendered_path(store_path, request_state)
|
95
|
+
path = @global.paths[@path_spec]
|
96
|
+
locals = {
|
97
|
+
path: store_path,
|
98
|
+
image_name: @image_name
|
99
|
+
}
|
100
|
+
Pathname.new(path.render(request_state.with_locals(locals))).cleanpath.to_s
|
101
|
+
end
|
72
102
|
end
|
73
103
|
|
74
104
|
def self.parse(configuration, node)
|
75
105
|
nodes = node.values.empty? ? node.children : [node]
|
76
|
-
|
106
|
+
output_specs = nodes.map do |node|
|
77
107
|
image_name = node.grab_values('image name').first
|
78
|
-
|
79
|
-
|
108
|
+
path_spec, if_image_name_on = *node.grab_attributes('path', 'if-image-name-on')
|
109
|
+
matcher = InclusionMatcher.new(image_name, if_image_name_on)
|
110
|
+
OutputSpec.new(configuration.global, image_name, path_spec, matcher)
|
80
111
|
end
|
81
112
|
|
82
113
|
configuration.output and raise StatementCollisionError.new(node, 'output')
|
83
|
-
configuration.output = self.new(
|
114
|
+
configuration.output = self.new(output_specs)
|
84
115
|
end
|
85
116
|
|
86
|
-
def initialize(
|
87
|
-
@
|
117
|
+
def initialize(output_specs)
|
118
|
+
@output_specs = output_specs
|
88
119
|
end
|
89
120
|
end
|
90
121
|
Handler::register_node_parser OutputOK
|
@@ -133,10 +164,10 @@ module Configuration
|
|
133
164
|
end
|
134
165
|
|
135
166
|
def realize(request_state)
|
136
|
-
paths = @
|
137
|
-
|
138
|
-
end.map do |
|
139
|
-
|
167
|
+
paths = @output_specs.select do |output_spec|
|
168
|
+
output_spec.included?(request_state)
|
169
|
+
end.map do |output_spec|
|
170
|
+
output_spec.store_path(request_state)
|
140
171
|
end
|
141
172
|
|
142
173
|
request_state.output do
|
@@ -152,10 +183,10 @@ module Configuration
|
|
152
183
|
end
|
153
184
|
|
154
185
|
def realize(request_state)
|
155
|
-
urls = @
|
156
|
-
|
157
|
-
end.map do |
|
158
|
-
|
186
|
+
urls = @output_specs.select do |output_spec|
|
187
|
+
output_spec.included?(request_state)
|
188
|
+
end.map do |output_spec|
|
189
|
+
output_spec.store_url(request_state)
|
159
190
|
end
|
160
191
|
|
161
192
|
request_state.output do
|
@@ -171,13 +171,27 @@ describe Configuration do
|
|
171
171
|
Pathname.new("/tmp/test.out2")
|
172
172
|
end
|
173
173
|
|
174
|
+
let :test_file do
|
175
|
+
Pathname.new('/tmp/abc/test.out')
|
176
|
+
end
|
177
|
+
|
178
|
+
let :space_test_file do
|
179
|
+
Pathname.new('/tmp/abc/t e s t.out')
|
180
|
+
end
|
181
|
+
|
174
182
|
before :each do
|
183
|
+
test_file.dirname.mkdir
|
184
|
+
test_file.open('w'){|io| io.write('abc')}
|
185
|
+
space_test_file.open('w'){|io| io.write('abc')}
|
175
186
|
in_file.open('w'){|io| io.write('abc')}
|
176
187
|
out_file.unlink if out_file.exist?
|
177
188
|
out2_file.unlink if out2_file.exist?
|
178
189
|
end
|
179
190
|
|
180
191
|
after :each do
|
192
|
+
test_file.exist? and test_file.unlink
|
193
|
+
space_test_file.exist? and space_test_file.unlink
|
194
|
+
test_file.dirname.exist? and test_file.dirname.rmdir
|
181
195
|
out_file.unlink if out_file.exist?
|
182
196
|
out2_file.unlink if out2_file.exist?
|
183
197
|
in_file.unlink
|
@@ -284,6 +298,62 @@ describe Configuration do
|
|
284
298
|
end
|
285
299
|
end
|
286
300
|
|
301
|
+
describe 'custom formatting' do
|
302
|
+
it 'should provide formatted file store path' do
|
303
|
+
subject = Configuration.read(<<-'EOF')
|
304
|
+
path "out" "abc/test.out"
|
305
|
+
path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
|
306
|
+
|
307
|
+
post "single" {
|
308
|
+
store_file "input" root="/tmp" path="out"
|
309
|
+
|
310
|
+
output_store_path "input" path="formatted"
|
311
|
+
}
|
312
|
+
EOF
|
313
|
+
|
314
|
+
subject.handlers[0].sources[0].realize(state)
|
315
|
+
subject.handlers[0].stores[0].realize(state)
|
316
|
+
subject.handlers[0].output.realize(state)
|
317
|
+
|
318
|
+
env.instance_eval &state.output_callback
|
319
|
+
env.res['Content-Type'].should == 'text/plain'
|
320
|
+
env.res.data.should == "hello/abc/world/test-xyz.out\r\n"
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'should provide formatted file store path for each path' do
|
324
|
+
subject = Configuration.read(<<-'EOF')
|
325
|
+
path "in" "test.in"
|
326
|
+
path "out" "abc/test.out"
|
327
|
+
path "out2" "test.out2"
|
328
|
+
|
329
|
+
path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
|
330
|
+
path "formatted2" "#{image_digest}.#{extension}"
|
331
|
+
|
332
|
+
post "single" {
|
333
|
+
source_file "original" root="/tmp" path="in"
|
334
|
+
|
335
|
+
store_file "input" root="/tmp" path="out"
|
336
|
+
store_file "original" root="/tmp" path="out2"
|
337
|
+
|
338
|
+
output_store_path {
|
339
|
+
"input" path="formatted"
|
340
|
+
"original" path="formatted2"
|
341
|
+
}
|
342
|
+
}
|
343
|
+
EOF
|
344
|
+
|
345
|
+
subject.handlers[0].sources[0].realize(state)
|
346
|
+
subject.handlers[0].sources[1].realize(state)
|
347
|
+
subject.handlers[0].stores[0].realize(state)
|
348
|
+
subject.handlers[0].stores[1].realize(state)
|
349
|
+
subject.handlers[0].output.realize(state)
|
350
|
+
|
351
|
+
env.instance_eval &state.output_callback
|
352
|
+
env.res['Content-Type'].should == 'text/plain'
|
353
|
+
env.res.data.should == "hello/abc/world/test-xyz.out\r\nba7816bf8f01cfea.out2\r\n"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
287
357
|
describe 'error handling' do
|
288
358
|
it 'should raise StorePathNotSetForImage for output of not stored image' do
|
289
359
|
subject = Configuration.read(<<-EOF)
|
@@ -402,6 +472,56 @@ describe Configuration do
|
|
402
472
|
end
|
403
473
|
end
|
404
474
|
|
475
|
+
describe 'custom formatting' do
|
476
|
+
it 'should provide formatted file store URL' do
|
477
|
+
subject = Configuration.read(<<-'EOF')
|
478
|
+
path "out" "abc/test.out"
|
479
|
+
|
480
|
+
path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
|
481
|
+
|
482
|
+
post "single" {
|
483
|
+
store_file "input" root="/tmp" path="out"
|
484
|
+
|
485
|
+
output_store_url "input" path="formatted"
|
486
|
+
}
|
487
|
+
EOF
|
488
|
+
|
489
|
+
subject.handlers[0].sources[0].realize(state)
|
490
|
+
subject.handlers[0].stores[0].realize(state)
|
491
|
+
subject.handlers[0].output.realize(state)
|
492
|
+
|
493
|
+
env.instance_eval &state.output_callback
|
494
|
+
env.res['Content-Type'].should == 'text/uri-list'
|
495
|
+
env.res.data.should == "file://abc/hello/world/test-xyz.out\r\n"
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
describe 'URL encoding' do
|
500
|
+
it 'should provide properly encoded file store URL' do
|
501
|
+
subject = Configuration.read(<<-'EOF')
|
502
|
+
path "out" "abc/t e s t.out"
|
503
|
+
path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
|
504
|
+
|
505
|
+
post "single" {
|
506
|
+
store_file "input" root="/tmp" path="out"
|
507
|
+
|
508
|
+
output_store_url {
|
509
|
+
"input"
|
510
|
+
"input" path="formatted"
|
511
|
+
}
|
512
|
+
}
|
513
|
+
EOF
|
514
|
+
|
515
|
+
subject.handlers[0].sources[0].realize(state)
|
516
|
+
subject.handlers[0].stores[0].realize(state)
|
517
|
+
subject.handlers[0].output.realize(state)
|
518
|
+
|
519
|
+
env.instance_eval &state.output_callback
|
520
|
+
env.res['Content-Type'].should == 'text/uri-list'
|
521
|
+
env.res.data.should == "file://abc/t%20e%20s%20t.out\r\nfile://abc/hello/world/t%20e%20s%20t-xyz.out\r\n"
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
405
525
|
describe 'error handling' do
|
406
526
|
it 'should raise StoreURLNotSetForImage for output of not stored image' do
|
407
527
|
subject = Configuration.read(<<-EOF)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpimagestore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: unicorn-cuba-base
|
@@ -324,7 +324,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
324
324
|
version: '0'
|
325
325
|
segments:
|
326
326
|
- 0
|
327
|
-
hash:
|
327
|
+
hash: 3462476370179380761
|
328
328
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
329
329
|
none: false
|
330
330
|
requirements:
|