httpimagestore 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.1, RVM: ruby-1.9.3-p429) [gem]" level="application" />
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.19)
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.7)
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.0)
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.0)
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.5.0
1
+ 1.6.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "httpimagestore"
8
- s.version = "1.5.0"
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-10-22"
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 ImageName < String
66
+ class OutputSpec
66
67
  include ConditionalInclusion
68
+ attr_reader :image_name
69
+ attr_reader :path_spec
67
70
 
68
- def initialize(name, matcher)
69
- super name
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
- names = nodes.map do |node|
106
+ output_specs = nodes.map do |node|
77
107
  image_name = node.grab_values('image name').first
78
- matcher = InclusionMatcher.new(image_name, node.grab_attributes('if-image-name-on').first)
79
- ImageName.new(image_name, matcher)
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(names)
114
+ configuration.output = self.new(output_specs)
84
115
  end
85
116
 
86
- def initialize(names)
87
- @names = names
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 = @names.select do |name|
137
- name.included?(request_state)
138
- end.map do |name|
139
- request_state.images[name].store_path or raise StorePathNotSetForImage.new(name)
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 = @names.select do |name|
156
- name.included?(request_state)
157
- end.map do |name|
158
- request_state.images[name].store_url or raise StoreURLNotSetForImage.new(name)
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.5.0
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-10-22 00:00:00.000000000 Z
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: 1314462780475505822
327
+ hash: 3462476370179380761
328
328
  required_rubygems_version: !ruby/object:Gem::Requirement
329
329
  none: false
330
330
  requirements: