imageproxy 0.1.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.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source :rubygems
2
+
3
+ gem "rack"
4
+ gem "rake"
5
+ gem "mime-types"
6
+
7
+ group :development, :test do
8
+ gem "heroku"
9
+ gem "shotgun"
10
+ gem "rspec"
11
+ gem "rack-test", :require => "rack/test"
12
+ gem "awesome_print"
13
+ gem "jeweler"
14
+ end
@@ -0,0 +1,48 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ awesome_print (0.3.2)
5
+ configuration (1.2.0)
6
+ diff-lcs (1.1.2)
7
+ git (1.2.5)
8
+ heroku (1.18.3)
9
+ launchy (~> 0.3.2)
10
+ rest-client (>= 1.4.0, < 1.7.0)
11
+ jeweler (1.6.2)
12
+ bundler (~> 1.0)
13
+ git (>= 1.2.5)
14
+ rake
15
+ launchy (0.3.7)
16
+ configuration (>= 0.0.5)
17
+ rake (>= 0.8.1)
18
+ mime-types (1.16)
19
+ rack (1.2.2)
20
+ rack-test (0.5.7)
21
+ rack (>= 1.0)
22
+ rake (0.8.7)
23
+ rest-client (1.6.1)
24
+ mime-types (>= 1.16)
25
+ rspec (2.5.0)
26
+ rspec-core (~> 2.5.0)
27
+ rspec-expectations (~> 2.5.0)
28
+ rspec-mocks (~> 2.5.0)
29
+ rspec-core (2.5.1)
30
+ rspec-expectations (2.5.0)
31
+ diff-lcs (~> 1.1.2)
32
+ rspec-mocks (2.5.0)
33
+ shotgun (0.9)
34
+ rack (>= 1.0)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ awesome_print
41
+ heroku
42
+ jeweler
43
+ mime-types
44
+ rack
45
+ rack-test
46
+ rake
47
+ rspec
48
+ shotgun
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 Erik Hanson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,296 @@
1
+ imageproxy
2
+ ==========
3
+
4
+ A image processing proxy server, written in Ruby as a Rack application.
5
+
6
+ There are many possible uses for this, but one major use is to resize images on-the-fly directly from HTML code, rather than processing the image when it is first uploaded or created. For example, if a user uploads a file to your site and it gets stored in http://example.com/uploads/39c0c11af.png, you could show a 50x50 px version like this:
7
+
8
+ <img src="http://example.com/convert?resize=50x50&source=http%3A%2F%2Fexample.com%2Fuploads%2F39c0c11af.png">
9
+
10
+ If you ever decided to change the size, you wouldn't have to re-encode anything, just change the HTML:
11
+
12
+ <img src="http://example.com/convert?resize=75x75&source=http%3A%2F%2Fexample.com%2Fuploads%2F39c0c11af.png">
13
+
14
+ See http://imageproxy.heroku.com/selftest for some examples (it's running on Heroku's free plan, so it will be a bit slow).
15
+
16
+ Status
17
+ ------
18
+
19
+ This project is pretty new. There are definitely some improvements that can be/need to be made. Suggestions and pull requests are welcome.
20
+
21
+ ### Current Features
22
+
23
+ * display information about an image
24
+ * resize, flip, rotate, change format and change quality of images
25
+ * standard query-parameter based URLs as well as Amazon CloudFront-compatibile URLs
26
+ * tested on Heroku and Amazon EC2
27
+ * use the requester's user agent string
28
+ * signed requests (to stop unauthorized use)
29
+ * obfuscated params
30
+
31
+ ### Future Features
32
+
33
+ Feel free to help out with some of these :)
34
+
35
+ * better documentation
36
+ * X-Sendfile / X-Accel-Redirect header
37
+ * signature generation testing tool
38
+ * try mounting inside a Rails app
39
+ * Rails helper for generating image tags that use imageproxy
40
+ * package as a gem?
41
+ * nice error messages for improper API use
42
+ * performance
43
+
44
+
45
+ PERFORMANCE
46
+ -----------
47
+
48
+ imageproxy doesn't do any sort of caching. That kind of thing is better left up to CDNs (like Amazon CloudFront or VoxCast CDN) or to caching proxies such as Varnish.
49
+
50
+ Also, imageproxy itself isn't nearly as fast as it could be. It's written in an interpreted language, and it shells out to curl and ImageMagick to do its work. Presumably, it would be way faster written in C as an Apache module, but this implementation was quite a bit easier :)
51
+
52
+
53
+ API
54
+ ---
55
+
56
+ There are two major functions: `identify` and `convert`, plus a helpful `selftest` function.
57
+
58
+ ### Identify
59
+
60
+ `identify` spits out a lot of information about the provided image.
61
+
62
+ #### Parameters
63
+
64
+ `source` *(Required)* The URL of the image to identify.
65
+
66
+ `signature` To stop unauthorized use. See the "Signing Requests" section of this document.
67
+
68
+ ### Convert
69
+
70
+ `convert` converts an image.
71
+
72
+ #### Parameters
73
+
74
+ `source` *(Required)* The URL of the image to convert. (Also aliased to `src`.)
75
+
76
+ `resize` The new size of the image, in "WxH" format (e.g., `20x30`).
77
+
78
+ `thumbnail` The new size of the image, in "WxH" format (e.g., `20x30`). Thumbnailing assumes the resulting image will be pretty small and makes some optimizations.
79
+
80
+ `shape` The shape of the image, when `resize`ing or `thumbnail`ing to a different aspect ratio. The value can be `preserve` which will preserve the original aspect ratio, `pad` which will add padding to keep the proper aspect ratio (you can supply a `background` parameter to choose the background color to pad with, or leave blank to pad with transparent color if the image format allows it), and `cut` which will cut the image to fit the new size.
81
+
82
+ `flip` Flip the image. The value can be `horizontal` or `vertical`.
83
+
84
+ `rotate` Rotate the image. The value can be any number. When rotating to a non-right-angle, you can specify the `background` parameter to choose the color for the background.
85
+
86
+ `format` Change the format. Possible formats include `gif`, `jpg`, `png`, `png8`, etc.
87
+
88
+ `quality` Choose the compression quality for formats that support lossy compression. The value can be any number from 0 to 100.
89
+
90
+ `progressive` Choose whether a JPEG image should be a progressive JPEG or not. Possible values are `true` and `false`.
91
+
92
+ `background` Some operations allow for a background color to be provided. The format is hex (e.g., `#ff00ff`) or rgba (e.g., `rgba(20,30,19,0.4)`)
93
+
94
+ `signature` To stop unauthorized use. See the "Signing Requests" section of this document.
95
+
96
+ `allowed_domains` A comma-separated list of second-level domains (e.g., "example.com, example.org") that are valid domains for the `source` parameter. If not specified, then the `source` parameter can reference any domain.
97
+
98
+ `max_size` The maximum dimension allowed for a `resize` or `thumbnail` operation. Specifying `20` would cause a resize of `10x30` to fail because the maximum dimension of `20` is less than the largest requested dimension of `30`.
99
+
100
+ ### Request Format
101
+
102
+ The request must start with `identify` or `convert` (for backwards-compatibility, `process` is a synonym for `convert`).
103
+
104
+ The parameters can be query string parameters, like this:
105
+
106
+ http://example.com/convert?resize=100x100&shape=cut
107
+
108
+ Or, the parameters can be Amazon CloudFront-compatible URLs, like this:
109
+
110
+ http://example.com/convert/resize/100x100/shape/cut
111
+
112
+ You can also mix the parameters if you like. This doesn't make much sense except for the case of the `signature` parameter which must be a query param:
113
+
114
+ http://example.com/convert/resize/100x100?signature=szFGj470w%2ByhJYJfTRryFLF9msA%3D
115
+
116
+ **Important:** Make sure to URL escape all query parameters. When using the CloudFront-compatible URL format, make sure to **double-escape** the source URL:
117
+
118
+ http://example.com/convert?resize=100x100&source=http://www.google.com/images/logos/ps_logo2.png # WRONG - not escaped
119
+ http://example.com/convert?resize=100x100&source=http%3A%2F%2Fwww.google.com%2Fimages%2Flogos%2Fps_logo2.png # RIGHT - escaped
120
+
121
+ http://exampe.com/convert/resize/100x100/source/http://www.google.com/images/logos/ps_logo2.png # WRONG - not escaped
122
+ http://exampe.com/convert/resize/100x100/source/http%3A%2F%2Fwww.google.com%2Fimages%2Flogos%2Fps_logo2.png # WRONG - only escaped once
123
+ http://exampe.com/convert/resize/100x100/source/http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png # RIGHT - escaped and then escaped again
124
+
125
+ ### Signing Requests
126
+
127
+ To require that requests are signed, set the following two environment variables:
128
+
129
+ IMAGEPROXY_SIGNATURE_REQUIRED=true
130
+ IMAGEPROXY_SIGNATURE_SECRET=some secret key
131
+
132
+ Then add a `signature` parameter to your query string or path.
133
+
134
+ The signature is calculated with the following formula, which is the same formula that Amazon Web Services uses:
135
+
136
+ URLSafeBase64( HMAC-SHA1( UTF-8-Encoding-Of( YourSecretKey, StringToSign ) ) );
137
+
138
+ Where YourSecretKey is a secret key that you make up, and StringToSign is the full path of the request, excluding host name and the "signature" parameter, in the same order as in the query. For `http://example.com/convert/resize/100x100?shape=cut&signature=szFGj470w%2ByhJYJfTRryFLF9msA%3D` the StringToSign would be `/convert/resize/100x100?shape=cut`.
139
+
140
+ URL safe base64 is the normal encoding but with replacing the + with - and / with _.
141
+
142
+ Example Ruby code to generate the signature:
143
+
144
+ digest = OpenSSL::Digest::Digest.new("sha1")
145
+ Base64.encode64(OpenSSL::HMAC.digest(digest, your_secret_key, your_query_string)).strip.tr('+/', '-_')
146
+
147
+ ### Obfuscating Requests
148
+
149
+ You may obfuscate your requests by Base64 encoding and then URL encoding your query string or path. The parameter name for this encoded value is `_` if you're using a query string or `-` if you're using a path. Example:
150
+
151
+ http://example.com/convert?src=http://example.com/dog.jpg&resize=10x10
152
+ http://example.com/convert?_=c3JjPWh0dHA6Ly9leGFtcGxlLmNvbS9kb2cuanBnJnJlc2l6ZT0xMHgxMA%3D%3D
153
+ http://example.com/convert/-/c3JjPWh0dHA6Ly9leGFtcGxlLmNvbS9kb2cuanBnJnJlc2l6ZT0xMHgxMA%3D%3D
154
+
155
+ ### Example requests
156
+
157
+ CloudFront-compatible URLs:
158
+
159
+ http://example.com/convert/resize/100x100/source/http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
160
+ http://example.com/identify/source/http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
161
+
162
+ Regular query string URLs:
163
+
164
+ http://example.com/convert?resize=100x100&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
165
+ http://example.com/identify?source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
166
+
167
+ Resize:
168
+
169
+ http://example.com/convert?resize=100x100&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
170
+
171
+ Resize with padding:
172
+
173
+ http://example.com/convert?resize=100x100&shape=pad&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
174
+ http://example.com/convert?resize=100x100&shape=pad&background=%23ff00ff&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
175
+
176
+ Resize with cutting:
177
+
178
+ http://example.com/convert?resize=100x100&shape=cut&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
179
+
180
+ Flipping:
181
+
182
+ http://example.com/convert?flip=horizontal&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
183
+ http://example.com/convert?flip=vertical&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
184
+
185
+ Rotating:
186
+
187
+ http://example.com/convert?rotate=90&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
188
+ http://example.com/convert?rotate=120&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
189
+ http://example.com/convert?rotate=120&background=%23ff00ff&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
190
+
191
+ Combo:
192
+
193
+ http://example.com/convert?resize=100x100&shape=cut&rotate=45&background=%23ff00ff&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png
194
+
195
+ With signature (signed with secret key "SEEKRET"):
196
+
197
+ http://example.com/convert/resize/100x100/source/http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png?signature=iNoljMh0kALsoxRLzJfr7Wcq%2BnY%3D
198
+ http://example.com/convert?resize=100x100&source=http%253A%252F%252Fwww.google.com%252Fimages%252Flogos%252Fps_logo2.png&signature=KLga1QNdCY8Xu4thsKdbTUjnYAk%3D
199
+
200
+
201
+ ### Selftest
202
+
203
+ You can go to the `/selftest` URL to see everything in action. For example: [http://imageproxy.heroku.com/selftest](http://imageproxy.heroku.com/selftest).
204
+
205
+
206
+ Sample EC2 Installation Recipe
207
+ ------------------------------
208
+
209
+ Create and boot an instance of an AWS Linux AMI using [Amazon's EC2 console](https://console.aws.amazon.com/ec2/). In this example, I used a "micro" instance.
210
+
211
+ ssh into the instance as user `ec2-user`
212
+
213
+ Make a directory for the proxy:
214
+
215
+ sudo mkdir /opt/imageproxy
216
+ sudo chown ec2-user:ec2-user /opt/imageproxy
217
+
218
+ Install the Ruby HTTP stack + ImageMagick:
219
+
220
+ sudo yum -y install make gcc gcc-c++ http rubygems ruby-devel openssl-devel zlib-devel httpd-devel git curl-devel openssl ImageMagick ImageMagick-devel
221
+
222
+ Install passenger:
223
+
224
+ sudo gem install passenger
225
+ sudo passenger-install-apache2-module
226
+
227
+ Update the Apache config as suggested by the passenger installer
228
+
229
+ LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-3.0.5/ext/apache2/mod_passenger.so
230
+ PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-3.0.5
231
+ PassengerRuby /usr/bin/ruby
232
+
233
+ Set up a VirtualHost as suggested by the passenger installer
234
+
235
+ <VirtualHost *:80>
236
+ ServerName ec2-184-72-213-98.compute-1.amazonaws.com
237
+ DocumentRoot /opt/imageproxy/public
238
+ <Directory /opt/imageproxy/public>
239
+ Allow from all
240
+ Options -MultiViews
241
+ </Directory>
242
+ </VirtualHost>
243
+
244
+ Clone the imageproxy code:
245
+
246
+ git clone git://github.com/eahanson/imageproxy.git
247
+
248
+ Install the gems:
249
+
250
+ bundle install
251
+
252
+ Start Apache:
253
+
254
+ sudo /etc/init.d/httpd start
255
+
256
+
257
+ Sample Heroku Installation Recipe
258
+ ------------------------------
259
+
260
+ Clone the imageproxy code:
261
+
262
+ git clone git://github.com/eahanson/imageproxy.git
263
+
264
+ Set up Heroku:
265
+
266
+ http://devcenter.heroku.com/articles/quickstart
267
+
268
+ Deploy:
269
+
270
+ git push heroku master
271
+
272
+
273
+ If You Want To Modify The Code
274
+ ------------------------------
275
+
276
+ Tun run the server locally:
277
+
278
+ rackup
279
+
280
+ Make sure everthing is working:
281
+
282
+ http://localhost:9292/selftest
283
+
284
+ To run the specs
285
+
286
+ rake spec
287
+
288
+ Thanks
289
+ ------
290
+
291
+ Thanks to [David Hall](https://github.com/moonhouse) for code contributions.
292
+
293
+ License
294
+ -------
295
+
296
+ Licensed under the MIT license. See LICENSE.txt.
@@ -0,0 +1,42 @@
1
+ require 'bundler'
2
+ Bundler.require :test
3
+
4
+ desc "Run all specs"
5
+ task :spec do
6
+ system 'rspec --format nested --color spec'
7
+ end
8
+
9
+ task :default => :spec
10
+
11
+ desc "Run the server locally (for development)"
12
+ task :run do
13
+ require 'uri'
14
+ puts <<EOF
15
+
16
+ See examples at:
17
+
18
+ http://localhost:9393/selftest
19
+
20
+ If you set IMAGEPROXY_SIGNATURE_REQUIRED and IMAGEPROXY_SIGNATURE_SECRET
21
+ environment variables, then the requests in the selftest will be
22
+ signed.
23
+
24
+ EOF
25
+ system 'shotgun'
26
+ end
27
+
28
+
29
+ require 'jeweler'
30
+ Jeweler::Tasks.new do |gem|
31
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
32
+ gem.name = "imageproxy"
33
+ gem.homepage = "http://github.com/eahanson/imageproxy"
34
+ gem.license = "MIT"
35
+ gem.summary = %Q{A image processing proxy server, written in Ruby as a Rack application.}
36
+ gem.description = %Q{A image processing proxy server, written in Ruby as a Rack application. Requires ImageMagick.}
37
+ gem.email = "erik@eahanson.com"
38
+ gem.authors = ["Erik Hanson"]
39
+ # dependencies defined in Gemfile
40
+ end
41
+ Jeweler::RubygemsDotOrgTasks.new
42
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ require 'rack/sendfile'
3
+
4
+ require File.join(File.expand_path(File.dirname(__FILE__)), "imageproxy")
5
+ require File.join(File.expand_path(File.dirname(__FILE__)), "lib", "server")
6
+
7
+ run Rack::Sendfile.new(Server.new)
@@ -0,0 +1,90 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{imageproxy}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Erik Hanson"]
12
+ s.date = %q{2011-06-08}
13
+ s.description = %q{A image processing proxy server, written in Ruby as a Rack application. Requires ImageMagick.}
14
+ s.email = %q{erik@eahanson.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.mdown"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.mdown",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "config.ru",
28
+ "imageproxy.gemspec",
29
+ "imageproxy.rb",
30
+ "lib/command.rb",
31
+ "lib/compare.rb",
32
+ "lib/convert.rb",
33
+ "lib/identify.rb",
34
+ "lib/options.rb",
35
+ "lib/selftest.rb",
36
+ "lib/server.rb",
37
+ "lib/signature.rb",
38
+ "public/background.png",
39
+ "public/sample.png",
40
+ "public/sample_10x20.png",
41
+ "spec/command_spec.rb",
42
+ "spec/convert_spec.rb",
43
+ "spec/options_spec.rb",
44
+ "spec/server_spec.rb",
45
+ "spec/signature_spec.rb",
46
+ "spec/spec_helper.rb"
47
+ ]
48
+ s.homepage = %q{http://github.com/eahanson/imageproxy}
49
+ s.licenses = ["MIT"]
50
+ s.require_paths = ["lib"]
51
+ s.rubygems_version = %q{1.6.2}
52
+ s.summary = %q{A image processing proxy server, written in Ruby as a Rack application.}
53
+
54
+ if s.respond_to? :specification_version then
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
58
+ s.add_runtime_dependency(%q<rack>, [">= 0"])
59
+ s.add_runtime_dependency(%q<rake>, [">= 0"])
60
+ s.add_runtime_dependency(%q<mime-types>, [">= 0"])
61
+ s.add_development_dependency(%q<heroku>, [">= 0"])
62
+ s.add_development_dependency(%q<shotgun>, [">= 0"])
63
+ s.add_development_dependency(%q<rspec>, [">= 0"])
64
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
65
+ s.add_development_dependency(%q<awesome_print>, [">= 0"])
66
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
67
+ else
68
+ s.add_dependency(%q<rack>, [">= 0"])
69
+ s.add_dependency(%q<rake>, [">= 0"])
70
+ s.add_dependency(%q<mime-types>, [">= 0"])
71
+ s.add_dependency(%q<heroku>, [">= 0"])
72
+ s.add_dependency(%q<shotgun>, [">= 0"])
73
+ s.add_dependency(%q<rspec>, [">= 0"])
74
+ s.add_dependency(%q<rack-test>, [">= 0"])
75
+ s.add_dependency(%q<awesome_print>, [">= 0"])
76
+ s.add_dependency(%q<jeweler>, [">= 0"])
77
+ end
78
+ else
79
+ s.add_dependency(%q<rack>, [">= 0"])
80
+ s.add_dependency(%q<rake>, [">= 0"])
81
+ s.add_dependency(%q<mime-types>, [">= 0"])
82
+ s.add_dependency(%q<heroku>, [">= 0"])
83
+ s.add_dependency(%q<shotgun>, [">= 0"])
84
+ s.add_dependency(%q<rspec>, [">= 0"])
85
+ s.add_dependency(%q<rack-test>, [">= 0"])
86
+ s.add_dependency(%q<awesome_print>, [">= 0"])
87
+ s.add_dependency(%q<jeweler>, [">= 0"])
88
+ end
89
+ end
90
+