httpimagestore 1.1.0 → 1.2.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/README.md CHANGED
@@ -14,6 +14,20 @@ It is using [HTTP Thumbnailer](https://github.com/jpastuszek/httpthumbnailer) as
14
14
  * storage under custom paths including image hash, content determined extension or used URL path
15
15
  * based on [Unicorn HTTP server](http://unicorn.bogomips.org) with UNIX socket communication support
16
16
 
17
+ ## Changelog
18
+
19
+ ### 1.2.0
20
+
21
+ * matching for query string key and value
22
+ * getting query string key value into variable
23
+ * optional query string matchers with default value
24
+ * default values for optional component matchers
25
+ * S3 storage prefix support
26
+
27
+ ### 1.1.0
28
+
29
+ * passing thumbnailer options via query string parameters
30
+
17
31
  ## Installing
18
32
 
19
33
  HTTP Image Store is released as gem and can be installed from [RubyGems](http://rubygems.org) with:
@@ -113,13 +127,26 @@ Endpoints will be evaluated in order of definition until one is matched.
113
127
 
114
128
  Statement should start with one of the following HTTP verbs in lowercase: `get`, `post`, `put`, `delete`, followed by list of URI component matchers:
115
129
 
116
- * `string` - character string will match whole URI component in that position
117
- * `:symbol` - string beginning with `:` will match any URI component in that position and will store matched component string in variable named as symbol; this variable can then be used to build path, thumbnail parameters or in any other places where variables are expanded
118
- * `:symbol?` - string beginning with `:` and followed with `?` will be optionally matched; request URI may not contain component in this location (usually at the end of URI) to be matched for this endpoint to be evaluated
119
- * `:symbol/regexp/` - in this format symbol will be matched only if `/` surrounded [regular expression](http://rubular.com) matches the URI section
130
+ * `<string>` - `<string>` literally match against URI component in that position for the endpoint to be evaluated
131
+ * `:<symbol>` - match any URI component in that position and store matched component value in variable named `<symbol>`
132
+ * `:<symbol>?[defalut]` - optionally match URI component in that position and store matched component value in variable named `<symbol>`; request URI may not contain component in that position (usually at the end of URI) to be matched for this endpoint to be evaluated; if `[default]` value is specified it will be used when no value was found in the URI, otherwise empty string will be used
133
+ * `:<symbol>/<regexp>/` - match URI component using `/` surrounded [regular expression](http://rubular.com) and store matched component value in variable named `<symbol>`
134
+ * `&<key>=<value>` - match this endpoint when query string contains key `<key>` with value of `<value>`
135
+ * `&:<key>` - match query string parameter of key `<key>` and store it's value in variable named `<key>`
136
+ * `&:<key>?[default]` - optionally match query string parameter of key `<key>`; when `[default]` is specified it will be used as variable value, otherwise empty string will be used
137
+
138
+ Note that any remaining unmatched URI is stored in `path` variable.
139
+ All query string parameters are available as variables named by their key.
140
+ Additionally `query_string_options` variable is build from query string parameters and can be used to specify options to [HTTP Thumbnailer](https://github.com/jpastuszek/httpthumbnailer).
120
141
 
121
- Note that any remaining URI are is stored in `path` variable.
122
- Also any query string parameters are available as variables. Additionally `query_string_options` is build from query string parameters and can be used to specify options to [HTTP Thumbnailer](https://github.com/jpastuszek/httpthumbnailer).
142
+ Note that variables can get overwritten in order of evaluation:
143
+ 1. all query string parameters
144
+ 2. `path` variable value
145
+ 3. all matched URI components and query string parameters in order of specification from left to right
146
+ 4. `query_string_options` variable value
147
+
148
+ Note that URI components are URI decoded after they are matched.
149
+ Query string parameter values are URI decoded before they are matched.
123
150
 
124
151
  Example:
125
152
 
@@ -183,6 +210,7 @@ Options:
183
210
 
184
211
  * `bucket` - name of bucket to source image from
185
212
  * `path` - name of predefined path that will be used to generate key to object to source
213
+ * `prefix` - prefix object key with given prefix value; this does not affect fromat of output URL; prefix will not be included in source path output; default: ``
186
214
 
187
215
  Example:
188
216
 
@@ -288,6 +316,7 @@ Options:
288
316
  * `bucket` - name of bucket to store image in
289
317
  * `path` - name of predefined path that will be used to generate key to store object under
290
318
  * `public` - if set to `true` the image will be readable by everybody; this affects fromat of output URL; default: `false`
319
+ * `prefix` - prefix storeage key with given prefix value; this does not affect fromat of output URL; prefix will not be included in storage path output; default: ``
291
320
 
292
321
  Example:
293
322
 
@@ -602,7 +631,7 @@ On demand API example:
602
631
  $ curl -X PUT 10.1.1.24:3000/v1/original -q --data-binary @Pictures/compute.jpg
603
632
  4006450256177f4a.jpg
604
633
 
605
- # Obtainig fit method 100x1000 thumbnail to /tmp/test.jpg
634
+ # Getting fit operation 100x1000 thumbnail to /tmp/test.jpg
606
635
  $ curl 10.1.1.24:3000/v1/thumbnail/4006450256177f4a.jpg/fit/100/1000 -v -s -o /tmp/test.jpg
607
636
  * About to connect() to 10.1.1.24 port 3000 (#0)
608
637
  * Trying 10.1.1.24... connected
@@ -653,6 +682,85 @@ $ identify /tmp/test.jpg
653
682
  /tmp/test.jpg JPEG 100x100 100x100+0+0 8-bit sRGB 3.31KB 0.000u 0:00.000
654
683
  ```
655
684
 
685
+ ## Facebook like API example
686
+
687
+ Based on [Facebook APIs](https://developers.facebook.com/docs/reference/api/using-pictures/#sizes).
688
+
689
+ ```sdl
690
+ s3 key="AIAITCKMELYWQZPJP7HQ" secret="V37lCu0F48Tv9s7QVqIT/sLf/wwqhNSB4B0Em7Ei" ssl=false
691
+
692
+ path "hash" "#{digest}"
693
+ path "path" "#{path}"
694
+
695
+ put "original" {
696
+ thumbnail "input" "original" operation="limit" width=100 height=100 format="jpeg" quality=95
697
+
698
+ store_s3 "original" bucket="mybucket_v1" path="hash"
699
+
700
+ output_store_path "original"
701
+ }
702
+
703
+ # type selected
704
+ get "&type=square" {
705
+ source_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="path"
706
+
707
+ thumbnail "original" "thumbnail" operation="crop" width="50" height="50" format="input"
708
+
709
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
710
+ }
711
+
712
+ get "&type=small" {
713
+ source_s3 "original" bucket="mybucket_v1" path="path"
714
+
715
+ thumbnail "original" "thumbnail" operation="fit" width="50" height="2000" format="input"
716
+
717
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
718
+ }
719
+
720
+ get "&type=normall" {
721
+ source_s3 "original" bucket="mybucket_v1" path="path"
722
+
723
+ thumbnail "original" "thumbnail" operation="fit" width="100" height="2000" format="input"
724
+
725
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
726
+ }
727
+
728
+ get "&type=large" {
729
+ source_s3 "original" bucket="mybucket_v1" path="path"
730
+
731
+ thumbnail "original" "thumbnail" operation="fit" width="200" height="2000" format="input"
732
+
733
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
734
+ }
735
+
736
+ # crop to specified width and height
737
+ get "&:width" "&:height" {
738
+ source_s3 "original" bucket="mybucket_v1" path="path"
739
+
740
+ thumbnail "original" "thumbnail" operation="crop" width="#{width}" height="#{height}" format="input"
741
+
742
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
743
+ }
744
+
745
+ # fit to width when no height is specified
746
+ get "&:width" "&:height?1080" {
747
+ source_s3 "original" bucket="mybucket_v1" path="path"
748
+
749
+ thumbnail "original" "thumbnail" operation="fit" width="#{width}" height="#{height}" format="input"
750
+
751
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
752
+ }
753
+
754
+ # default to small class
755
+ get {
756
+ source_s3 "original" bucket="bmybucket_v1" path="path"
757
+
758
+ thumbnail "original" "thumbnail" operation="crop" width="50" height="50" format="input"
759
+
760
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
761
+ }
762
+ ```
763
+
656
764
  ## Usage
657
765
 
658
766
  After installation of the gem the `httpimagestore` executable is installed in **PATH**.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.2.0
@@ -3,7 +3,8 @@ Feature: S3 object Cache-Control header settings
3
3
 
4
4
  Background:
5
5
  Given S3 settings in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_S3_TEST_BUCKET environment variables
6
- Given httpimagestore server is running at http://localhost:3000/ with the following configuration
6
+ Given httpthumbnailer server is running at http://localhost:3100/health_check
7
+ Given httpimagestore server is running at http://localhost:3000/health_check with the following configuration
7
8
  """
8
9
  s3 key="@AWS_ACCESS_KEY_ID@" secret="@AWS_SECRET_ACCESS_KEY@" ssl=false
9
10
 
@@ -21,7 +22,6 @@ Feature: S3 object Cache-Control header settings
21
22
  store_s3 "cache" bucket="@AWS_S3_TEST_BUCKET@" public=true path="hash-name" cache-control="public, max-age=31557600, s-maxage=0"
22
23
  }
23
24
  """
24
- Given httpthumbnailer server is running at http://localhost:3100/
25
25
 
26
26
  @cache-control
27
27
  Scenario: Image files get don't get Cache-Control header by default
@@ -4,7 +4,8 @@ Feature: Image list based thumbnailing and S3 storage
4
4
 
5
5
  Background:
6
6
  Given S3 settings in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_S3_TEST_BUCKET environment variables
7
- Given httpimagestore server is running at http://localhost:3000/ with the following configuration
7
+ Given httpthumbnailer server is running at http://localhost:3100/health_check
8
+ Given httpimagestore server is running at http://localhost:3000/health_check with the following configuration
8
9
  """
9
10
  s3 key="@AWS_ACCESS_KEY_ID@" secret="@AWS_SECRET_ACCESS_KEY@" ssl=false
10
11
 
@@ -49,7 +50,6 @@ Feature: Image list based thumbnailing and S3 storage
49
50
  }
50
51
  }
51
52
  """
52
- Given httpthumbnailer server is running at http://localhost:3100/
53
53
 
54
54
  @compatibility @test
55
55
  Scenario: Putting original and its thumbnails to S3 bucket
@@ -4,7 +4,8 @@ Feature: Image list based thumbnailing and S3 storage
4
4
 
5
5
  Background:
6
6
  Given S3 settings in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_S3_TEST_BUCKET environment variables
7
- Given httpimagestore server is running at http://localhost:3000/ with the following configuration
7
+ Given httpthumbnailer server is running at http://localhost:3100/health_check
8
+ Given httpimagestore server is running at http://localhost:3000/health_check with the following configuration
8
9
  """
9
10
  s3 key="@AWS_ACCESS_KEY_ID@" secret="@AWS_SECRET_ACCESS_KEY@" ssl=false
10
11
 
@@ -44,7 +45,6 @@ Feature: Image list based thumbnailing and S3 storage
44
45
  source_file "original" root="/dev" path="zero"
45
46
  }
46
47
  """
47
- Given httpthumbnailer server is running at http://localhost:3100/
48
48
 
49
49
  @error-reporting
50
50
  Scenario: Reporting of missing resource
@@ -0,0 +1,149 @@
1
+ Feature: Store limited original image in S3 and thumbnail on facebook API
2
+ Similar API to described in https://developers.facebook.com/docs/reference/api/using-pictures/#sizes
3
+
4
+ Background:
5
+ Given S3 settings in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_S3_TEST_BUCKET environment variables
6
+ Given httpthumbnailer server is running at http://localhost:3100/health_check
7
+ Given httpimagestore server is running at http://localhost:3000/health_check with the following configuration
8
+ """
9
+ s3 key="@AWS_ACCESS_KEY_ID@" secret="@AWS_SECRET_ACCESS_KEY@" ssl=false
10
+
11
+ path "original-hash" "#{digest}"
12
+ path "path" "#{path}"
13
+
14
+ put "original" {
15
+ thumbnail "input" "original" operation="limit" width=100 height=100 format="jpeg" quality=95
16
+
17
+ store_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="original-hash"
18
+
19
+ output_store_path "original"
20
+ }
21
+
22
+ get "&type=square" {
23
+ source_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="path"
24
+
25
+ thumbnail "original" "thumbnail" operation="crop" width="50" height="50" format="input"
26
+
27
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
28
+ }
29
+
30
+ get "&type=small" {
31
+ source_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="path"
32
+
33
+ thumbnail "original" "thumbnail" operation="fit" width="50" height="2000" format="input"
34
+
35
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
36
+ }
37
+
38
+ get "&type=normall" {
39
+ source_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="path"
40
+
41
+ thumbnail "original" "thumbnail" operation="fit" width="100" height="2000" format="input"
42
+
43
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
44
+ }
45
+
46
+ get "&type=large" {
47
+ source_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="path"
48
+
49
+ thumbnail "original" "thumbnail" operation="fit" width="200" height="2000" format="input"
50
+
51
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
52
+ }
53
+
54
+ get "&:width" "&:height" {
55
+ source_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="path"
56
+
57
+ thumbnail "original" "thumbnail" operation="crop" width="#{width}" height="#{height}" format="input"
58
+
59
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
60
+ }
61
+
62
+ get "&:width" "&:height?1080" {
63
+ source_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="path"
64
+
65
+ thumbnail "original" "thumbnail" operation="fit" width="#{width}" height="#{height}" format="input"
66
+
67
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
68
+ }
69
+
70
+ get {
71
+ source_s3 "original" bucket="@AWS_S3_TEST_BUCKET@" path="path"
72
+
73
+ thumbnail "original" "thumbnail" operation="crop" width="50" height="50" format="input"
74
+
75
+ output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
76
+ }
77
+ """
78
+
79
+ @facebook @type
80
+ Scenario: Putting original to S3 bucket
81
+ Given there is no 4006450256177f4a file in S3 bucket
82
+ Given test.jpg file content as request body
83
+ When I do PUT request http://localhost:3000/original
84
+ Then response status will be 200
85
+ And response content type will be text/plain
86
+ And response body will be CRLF ended lines
87
+ """
88
+ 4006450256177f4a
89
+ """
90
+ Then S3 object 4006450256177f4a will contain JPEG image of size 71x100
91
+ When I do GET request http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a
92
+ Then response status will be 403
93
+
94
+ @facebook @type
95
+ Scenario: Getting square type tumbnail
96
+ Given test.jpg file content is stored in S3 under 4006450256177f4a
97
+ When I do GET request http://localhost:3000/4006450256177f4a?type=square
98
+ Then response status will be 200
99
+ And response content type will be image/jpeg
100
+ Then response body will contain JPEG image of size 50x50
101
+
102
+ @facebook @type
103
+ Scenario: Getting small type tumbnail
104
+ Given test.jpg file content is stored in S3 under 4006450256177f4a
105
+ When I do GET request http://localhost:3000/4006450256177f4a?type=small
106
+ Then response status will be 200
107
+ And response content type will be image/jpeg
108
+ Then response body will contain JPEG image of size 50x71
109
+
110
+ @facebook @type
111
+ Scenario: Getting normall type tumbnail
112
+ Given test.jpg file content is stored in S3 under 4006450256177f4a
113
+ When I do GET request http://localhost:3000/4006450256177f4a?type=normall
114
+ Then response status will be 200
115
+ And response content type will be image/jpeg
116
+ Then response body will contain JPEG image of size 100x141
117
+
118
+ @facebook @type
119
+ Scenario: Getting large type tumbnail
120
+ Given test.jpg file content is stored in S3 under 4006450256177f4a
121
+ When I do GET request http://localhost:3000/4006450256177f4a?type=large
122
+ Then response status will be 200
123
+ And response content type will be image/jpeg
124
+ Then response body will contain JPEG image of size 200x283
125
+
126
+ @facebook @type
127
+ Scenario: Getting square type tumbnail when no type is specified
128
+ Given test.jpg file content is stored in S3 under 4006450256177f4a
129
+ When I do GET request http://localhost:3000/4006450256177f4a
130
+ Then response status will be 200
131
+ And response content type will be image/jpeg
132
+ Then response body will contain JPEG image of size 50x50
133
+
134
+ @facebook @size
135
+ Scenario: Getting custom size tumbnail
136
+ Given test.jpg file content is stored in S3 under 4006450256177f4a
137
+ When I do GET request http://localhost:3000/4006450256177f4a?width=123&height=321
138
+ Then response status will be 200
139
+ And response content type will be image/jpeg
140
+ Then response body will contain JPEG image of size 123x321
141
+
142
+ @facebook @size
143
+ Scenario: Getting custom size tumbnail without height
144
+ Given test.jpg file content is stored in S3 under 4006450256177f4a
145
+ When I do GET request http://localhost:3000/4006450256177f4a?width=123
146
+ Then response status will be 200
147
+ And response content type will be image/jpeg
148
+ Then response body will contain JPEG image of size 123x174
149
+
@@ -5,7 +5,8 @@ Feature: Store limited original image in S3 and thumbnail based on request
5
5
 
6
6
  Background:
7
7
  Given S3 settings in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_S3_TEST_BUCKET environment variables
8
- Given httpimagestore server is running at http://localhost:3000/ with the following configuration
8
+ Given httpthumbnailer server is running at http://localhost:3100/health_check
9
+ Given httpimagestore server is running at http://localhost:3000/health_check with the following configuration
9
10
  """
10
11
  s3 key="@AWS_ACCESS_KEY_ID@" secret="@AWS_SECRET_ACCESS_KEY@" ssl=false
11
12
 
@@ -36,7 +37,6 @@ Feature: Store limited original image in S3 and thumbnail based on request
36
37
  output_image "thumbnail" cache-control="public, max-age=31557600, s-maxage=0"
37
38
  }
38
39
  """
39
- Given httpthumbnailer server is running at http://localhost:3100/
40
40
 
41
41
  @s3-store-and-thumbnail
42
42
  Scenario: Putting original to S3 bucket
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "httpimagestore"
8
- s.version = "1.1.0"
8
+ s.version = "1.2.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-07-24"
12
+ s.date = "2013-07-29"
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"]
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
30
30
  "features/cache-control.feature",
31
31
  "features/compatibility.feature",
32
32
  "features/error-reporting.feature",
33
+ "features/facebook.feature",
33
34
  "features/health-check.feature",
34
35
  "features/s3-store-and-thumbnail.feature",
35
36
  "features/step_definitions/httpimagestore_steps.rb",
@@ -42,7 +42,7 @@ module Configuration
42
42
  @locals[:query_string_options] = query_string.sort.map{|kv| kv.join(':')}.join(',')
43
43
  log.debug "processing request with body length: #{body.bytesize} bytes and locals: #{@locals} "
44
44
 
45
- @locals[:body] = body
45
+ @locals[:_body] = body
46
46
 
47
47
  @images = Images.new(memory_limit)
48
48
  @memory_limit = memory_limit
@@ -81,8 +81,8 @@ module Configuration
81
81
 
82
82
  class InputSource
83
83
  def realize(request_state)
84
- request_state.locals[:body].empty? and raise ZeroBodyLengthError
85
- request_state.images['input'] = Image.new(request_state.locals[:body])
84
+ request_state.locals[:_body].empty? and raise ZeroBodyLengthError
85
+ request_state.images['input'] = Image.new(request_state.locals[:_body])
86
86
  end
87
87
  end
88
88
 
@@ -202,24 +202,46 @@ module Configuration
202
202
  handler_configuration.http_method = node.name
203
203
  handler_configuration.uri_matchers = node.values.map do |matcher|
204
204
  case matcher
205
- when %r{^:[^/]+/.*/$}
206
- name, regexp = *matcher.match(%r{^:([^/]+)/(.*)/$}).captures
205
+ # URI component matchers
206
+ when %r{^:([^/]+)/(.*)/$} # :foobar/.*/
207
+ name = $1
208
+ regexp = $2
207
209
  Matcher.new(name.to_sym) do
208
210
  Regexp.new("(#{regexp})")
209
211
  end
210
- when /^:.+\?$/
211
- name = matcher.sub(/^:(.+)\?$/, '\1').to_sym
212
+ when /^:(.+)\?(.*)$/ # :foo?bar
213
+ name = $1.to_sym
214
+ default = $2
212
215
  Matcher.new(name) do
213
- ->{match(name) || captures.push('')}
216
+ ->{match(name) || captures.push(default)}
214
217
  end
215
- when /^:/
216
- name = matcher.sub(/^:/, '').to_sym
218
+ when /^:(.+)$/ # :foobar
219
+ name = $1.to_sym
217
220
  Matcher.new(name) do
218
221
  name
219
222
  end
220
- else
223
+ # Query string matchers
224
+ when /^\&([^=]+)=(.+)$/# ?foo=bar
225
+ name = $1
226
+ value = $2
221
227
  Matcher.new(nil) do
222
- matcher
228
+ ->{req[name] && req[name] == value}
229
+ end
230
+ when /^\&:(.+)\?(.*)$/# &:foo?bar
231
+ name = $1
232
+ default = $2
233
+ Matcher.new(name.to_sym) do
234
+ ->{captures.push(req[name] || default)}
235
+ end
236
+ when /^\&:(.+)$/# &:foo
237
+ name = $1
238
+ Matcher.new(name.to_sym) do
239
+ ->{req[name] && captures.push(req[name])}
240
+ end
241
+ # String URI component matcher
242
+ else # foobar
243
+ Matcher.new(nil) do
244
+ Regexp.escape(matcher)
223
245
  end
224
246
  end
225
247
  end
@@ -62,7 +62,7 @@ module Configuration
62
62
  Pathname.new(path).extname.delete('.')
63
63
  when :digest
64
64
  return locals[:_digest] if locals.include? :_digest
65
- data = locals[:body] or raise NoMetaValueForPathTemplatePlaceholerError.new(path_name, template, :body, name)
65
+ data = locals[:_body] or raise NoMetaValueForPathTemplatePlaceholerError.new(path_name, template, :body, name)
66
66
  digest = Digest::SHA2.new.update(data).to_s[0,16]
67
67
  # cache digest in request locals
68
68
  locals[:_digest] = digest
@@ -75,9 +75,10 @@ module Configuration
75
75
  node.required_attributes('bucket', 'path')
76
76
  node.valid_attribute_values('public_access', true, false, nil)
77
77
 
78
- bucket, path_spec, cache_control, public_access, if_image_name_on =
79
- *node.grab_attributes('bucket', 'path', 'cache-control', 'public', 'if-image-name-on')
78
+ bucket, path_spec, public_access, cache_control, prefix, if_image_name_on =
79
+ *node.grab_attributes('bucket', 'path', 'public', 'cache-control', 'prefix', 'if-image-name-on')
80
80
  public_access = false if public_access.nil?
81
+ prefix = '' if prefix.nil?
81
82
 
82
83
  self.new(
83
84
  configuration.global,
@@ -86,16 +87,18 @@ module Configuration
86
87
  bucket,
87
88
  path_spec,
88
89
  public_access,
89
- cache_control
90
+ cache_control,
91
+ prefix
90
92
  )
91
93
  end
92
94
 
93
- def initialize(global, image_name, matcher, bucket, path_spec, public_access, cache_control)
95
+ def initialize(global, image_name, matcher, bucket, path_spec, public_access, cache_control, prefix)
94
96
  super global, image_name, matcher
95
97
  @bucket = bucket
96
98
  @path_spec = path_spec
97
99
  @public_access = public_access
98
100
  @cache_control = cache_control
101
+ @prefix = prefix
99
102
  local :bucket, @bucket
100
103
  end
101
104
 
@@ -114,7 +117,7 @@ module Configuration
114
117
  def object(path)
115
118
  begin
116
119
  bucket = client.buckets[@bucket]
117
- yield bucket.objects[path]
120
+ yield bucket.objects[@prefix + path]
118
121
  rescue AWS::S3::Errors::AccessDenied
119
122
  raise S3AccessDenied.new(@bucket, path)
120
123
  rescue AWS::S3::Errors::NoSuchBucket
@@ -18,10 +18,10 @@ describe Configuration do
18
18
  EOF
19
19
 
20
20
  subject.paths['uri'].render(path: 'test/abc.jpg').should == 'test/abc.jpg'
21
- subject.paths['hash'].render(path: 'test/abc.jpg', body: 'hello').should == '2cf24dba5fb0a30e.jpg'
22
- subject.paths['hash-name'].render(path: 'test/abc.jpg', body: 'hello', imagename: 'xbrna').should == '2cf24dba5fb0a30e/xbrna.jpg'
23
- subject.paths['structured'].render(path: 'test/abc.jpg', body: 'hello').should == 'test/2cf24dba5fb0a30e/abc.jpg'
24
- subject.paths['structured-name'].render(path: 'test/abc.jpg', body: 'hello', imagename: 'xbrna').should == 'test/2cf24dba5fb0a30e/abc-xbrna.jpg'
21
+ subject.paths['hash'].render(path: 'test/abc.jpg', _body: 'hello').should == '2cf24dba5fb0a30e.jpg'
22
+ subject.paths['hash-name'].render(path: 'test/abc.jpg', _body: 'hello', imagename: 'xbrna').should == '2cf24dba5fb0a30e/xbrna.jpg'
23
+ subject.paths['structured'].render(path: 'test/abc.jpg', _body: 'hello').should == 'test/2cf24dba5fb0a30e/abc.jpg'
24
+ subject.paths['structured-name'].render(path: 'test/abc.jpg', _body: 'hello', imagename: 'xbrna').should == 'test/2cf24dba5fb0a30e/abc-xbrna.jpg'
25
25
  end
26
26
 
27
27
  describe 'error handling' do
@@ -87,6 +87,7 @@ else
87
87
  s3_client = AWS::S3.new(use_ssl: false)
88
88
  s3_test_bucket = s3_client.buckets[ENV['AWS_S3_TEST_BUCKET']]
89
89
  s3_test_bucket.objects['test.jpg'].write(@test_data, content_type: 'image/jpeg')
90
+ s3_test_bucket.objects['test_prefix/test.jpg'].write(@test_data, content_type: 'image/jpeg')
90
91
  end
91
92
 
92
93
  it 'should source image from S3 using path spec' do
@@ -113,6 +114,34 @@ else
113
114
  status(state.images['original'].source_url).should == 200
114
115
  end
115
116
 
117
+ describe 'storage prefix' do
118
+ subject do
119
+ Configuration.read(<<-EOF)
120
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
121
+ path "hash" "\#{test_image}"
122
+ get {
123
+ source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" prefix="test_prefix/"
124
+ }
125
+ EOF
126
+ end
127
+
128
+ it 'should still provide valid HTTPS URL incliding prefix' do
129
+ subject.handlers[0].image_sources[0].realize(state)
130
+
131
+ state.images['original'].source_url.should start_with "https://"
132
+ state.images['original'].source_url.should include ENV['AWS_S3_TEST_BUCKET']
133
+ state.images['original'].source_url.should include "/test_prefix/test.jpg"
134
+ state.images['original'].source_url.should include ENV['AWS_ACCESS_KEY_ID']
135
+ status(state.images['original'].source_url).should == 200
136
+ end
137
+
138
+ it 'should provide source path without prefix' do
139
+ subject.handlers[0].image_sources[0].realize(state)
140
+
141
+ state.images['original'].source_path.should == "test.jpg"
142
+ end
143
+ end
144
+
116
145
  describe 'non encrypted connection mode' do
117
146
  subject do
118
147
  Configuration.read(<<-EOF)
@@ -292,6 +321,8 @@ else
292
321
  s3_test_bucket = s3_client.buckets[ENV['AWS_S3_TEST_BUCKET']]
293
322
  @test_object = s3_test_bucket.objects['test_out.jpg']
294
323
  @test_object.delete
324
+ test_object = s3_test_bucket.objects['test_prefix/test_out.jpg']
325
+ test_object.delete
295
326
  end
296
327
 
297
328
  before :each do
@@ -323,6 +354,34 @@ else
323
354
  status(state.images['input'].store_url).should == 200
324
355
  end
325
356
 
357
+ describe 'storage prefix' do
358
+ subject do
359
+ Configuration.read(<<-EOF)
360
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
361
+ path "hash" "\#{test_image}"
362
+ post {
363
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" prefix="test_prefix/"
364
+ }
365
+ EOF
366
+ end
367
+
368
+ it 'should still provide valid HTTPS URL incliding prefix' do
369
+ subject.handlers[0].stores[0].realize(state)
370
+
371
+ state.images['input'].store_url.should start_with "https://"
372
+ state.images['input'].store_url.should include ENV['AWS_S3_TEST_BUCKET']
373
+ state.images['input'].store_url.should include "test_prefix/test_out.jpg"
374
+ state.images['input'].store_url.should include ENV['AWS_ACCESS_KEY_ID']
375
+ status(state.images['input'].store_url).should == 200
376
+ end
377
+
378
+ it 'should provide storage path without prefix' do
379
+ subject.handlers[0].stores[0].realize(state)
380
+
381
+ state.images['input'].store_path.should == "test_out.jpg"
382
+ end
383
+ end
384
+
326
385
  describe 'non encrypted connection mode' do
327
386
  subject do
328
387
  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.1.0
4
+ version: 1.2.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-07-24 00:00:00.000000000 Z
12
+ date: 2013-07-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: unicorn-cuba-base
@@ -241,6 +241,7 @@ files:
241
241
  - features/cache-control.feature
242
242
  - features/compatibility.feature
243
243
  - features/error-reporting.feature
244
+ - features/facebook.feature
244
245
  - features/health-check.feature
245
246
  - features/s3-store-and-thumbnail.feature
246
247
  - features/step_definitions/httpimagestore_steps.rb
@@ -291,7 +292,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
291
292
  version: '0'
292
293
  segments:
293
294
  - 0
294
- hash: 1029095012750983706
295
+ hash: -1673888350520322204
295
296
  required_rubygems_version: !ruby/object:Gem::Requirement
296
297
  none: false
297
298
  requirements: