scale_down 0.6.1 → 0.7.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/CHANGES CHANGED
@@ -1,3 +1,7 @@
1
+ == 2012-01-26==
2
+ Version 0.7.0
3
+ Prefefined labels for image sizes
4
+
1
5
  == 2012-01-24 ==
2
6
  Version 0.6.1
3
7
  Minor change in CMYK conversion
data/README.md CHANGED
@@ -1,64 +1,98 @@
1
1
  [![Build Status](https://secure.travis-ci.org/jweir/ScaleDown.png)](http://travis-ci.org/jweir/ScaleDown)
2
2
 
3
+ The image scaling server used by [Fame Driver](http://famedriver.com).
4
+ It supports dynamic sizes, image cropping and color conversion (CMYK to RGB).
5
+ It only supports images on the same machine (this is not a distributed scaler).
6
+
3
7
  ScaleDown
4
8
  ==========
9
+ So you want to scale an image?
10
+ ------------------------------
5
11
 
6
- An on demand image scaling server.
12
+ You have an image on `example.com`. It has a public path of `/images/john/picture.png`.
13
+ You want it scaled to fit in a 400x400 pixel box. ScaleDown is running on the subdomain `images`.
7
14
 
8
- Images are scaled based upon their URL. An HMAC signature is used to prevent unauthorized scaling of images.
15
+ http://server/images/john/scaled/400x400/picture.png?HMAC_SIGNATURE
9
16
 
10
- Supports cropping images and converts CMYK to RGB.
17
+ The scaled file is saved in a public path identical to the request. It will be statically served on the next request.
11
18
 
12
- URL Schema
13
- ==========
19
+ Geometry, Labels and Cropping
20
+ ==============================
14
21
 
15
- Your file on `example.com` has a public path of `/images/john/picture.png`. You want it scaled to fit in a 400x400 pixel box. **ScaleDown** is running on the subdomain `images`.
22
+ There are two methods of requesting a scaled image: label or geometry.
16
23
 
17
- The URL to your image would be:
24
+ Labels
25
+ ------
18
26
 
19
- http://images.example.com/images/john/scaled/400x400/picture.png?HMAC_SIGNATURE
27
+ A label is a predefined box the scaled image will fit within.
28
+ Labels are defined in the `config.ru` file.
20
29
 
21
- The scaled file is saved in a public path identical to the request. Once an image is scaled your webserve can statically server the file.
30
+ ```sh
31
+ # A label called thumbnail has been defined as "100x100"
32
+ # The below url will scale picture.png to fit into a 100 x 100 box
33
+ http://server/images/john/scaled/thumbnail/picture.png
34
+ ^^^^^^
35
+ ```
22
36
 
23
- The schema is
37
+ ```ruby
38
+ # in your config.ru file
39
+ config.labels = {
40
+ "thumbnail" => "100x100"
41
+ }
42
+ ```
24
43
 
25
- http://:host/:path_to_file/scaled/:geometry/:filename?:hmac_signature
44
+ Geometry
45
+ --------
26
46
 
27
- :host the address running ScaleDown
28
- :path_to_file the public path of the original file
29
- `scaled` keyword
30
- :geometry width x height and an optional `-crop`
31
- :filename the filename of the original image to scale
32
- :hmac_signature security measure to validate the request
47
+ Geometries allow the client to request an image of any size.
33
48
 
49
+ A geometry defines a box the image must fit within: `WIDTHxHEIGHT`.
50
+
51
+
52
+ ```sh
53
+ # scale to a 300 pixel width box by 900 pixel high box
54
+ http://server/images/john/scaled/300x900/picture.png?HMAC_SIGNATURE
55
+ ^^^^^^^
56
+ ```
34
57
 
35
- Geometry & Cropping
36
- ====================
58
+ Either, but not both, dimensions may use the keyword `auto`. This will scale the image to fit the defined dimension.
37
59
 
38
- To crop an image include the `-crop` option. The image will be scaled and cropped to fit the geometry.
60
+ ```sh
61
+ # scale to a 500 pixel wide box, of any height
62
+ http://server/images/john/scaled/500xauto/picture.png?HMAC_SIGNATURE
63
+ ^^^^
64
+ ```
39
65
 
40
- http://images.example.com/images/scaled/400x300-crop/logo.png?A3SDACEDF
66
+ When using a geometry an HMAC is required (see below).
67
+
68
+ Crop
69
+ ----
41
70
 
42
- Using `auto` as a dimension will fit the image to the other dimension.
71
+ To crop an image include the `-crop` option. The image will be scaled and cropped to fit the geometry. This is a simple crop positioned on the center and top of the image.
43
72
 
44
- For example, to ensure an image is 300 pixels wide set the height to auto
73
+ Both geometry and labels accept the `-crop` option.
45
74
 
46
- http://images.example.com/images/scaled/300xauto/logo.png/?A3SDACEDF
75
+ ```sh
76
+ http://server/images/john/scaled/thumbnail-crop/picture.png
77
+ http://server/images/john/scaled/100x100-crop/picture.png
78
+ ```
47
79
 
48
- There is a very simple `/info` function for getting image dimensions. It just returns a string with the WIDTHxHEIGHT.
80
+ Info
81
+ ----
82
+ There is a very simple `/info` function for getting image dimensions. It just returns a string with the WIDTHxHEIGHT of the original image.
49
83
 
50
- http://images.example.com/images/logo.png/info
84
+ http://server/images/logo.png/info
51
85
 
52
86
 
53
- HMAC
54
- ====
87
+ HMAC & Geometry
88
+ ==============
55
89
 
56
- You don't want someone taking down your server with malicious URLs, so an HMAC signature is used. This ensures that your URL was generated by a trusted source.
90
+ An HMAC signature is required to prevent a DOS attack. This ensures that your URL was generated by a trusted source.
57
91
 
58
92
  HMAC requires a shared key between the application generating the URL and the ScaleDown server.
59
93
 
60
- Sample Ruby URL Generator
61
- -------------------------
94
+ Ruby HMAC URL Generator
95
+ ------------------
62
96
 
63
97
  ```ruby
64
98
  require 'ruby-hmac'
@@ -66,12 +100,12 @@ require 'ruby-hmac'
66
100
  def signed_image_url(absolute_path, filename, geometry)
67
101
  shared_secret = "secret"
68
102
  hmac = HMAC::SHA1.new(shared_secret).update([absolute_path, 'scaled', geometry, filename].join("/")).to_s[0...8]
69
- "http://images.example.com#{[absolute_path, 'scaled', geometry, CGI.escape(filename)].join("/")}?#{hmac}"
103
+ "http://server#{[absolute_path, 'scaled', geometry, CGI.escape(filename)].join("/")}?#{hmac}"
70
104
  end
71
105
  ```
72
106
 
73
- Sample Node.js URL Generator
74
- ----------------------------
107
+ Node.js HMAC URL Generator
108
+ ---------------------
75
109
 
76
110
  ```javascript
77
111
  // Uses the Node.js crypot library
@@ -88,8 +122,23 @@ function signed_image_url(absolute_path, filename, geometry){
88
122
  }
89
123
  ```
90
124
 
91
- PNG, JPG, TIFF and other images supported
92
- ========================================
125
+ URL Schema
126
+ ==========
127
+
128
+ The schema is
129
+
130
+ http://:host/:path_to_file/scaled/:target/:filename?:hmac_signature
131
+
132
+ :host the address running ScaleDown
133
+ :path_to_file the public path of the original file
134
+ `scaled` keyword
135
+ :target a label or width x height. An optional `-crop` can be included with either
136
+ :filename the filename of the original image to scale
137
+ :hmac_signature security measure to validate the request(not required for labels)
138
+
139
+
140
+ Supported Image Formats
141
+ =======================
93
142
 
94
143
  ScaleDown will handle any image that Image Magick can process. But there are some rules:
95
144
 
@@ -101,7 +150,9 @@ Installation & Configuration
101
150
 
102
151
  gem install scale_down
103
152
 
104
- Create a Rackup file (config.ru). See https://github.com/jweir/ScaleDown/tree/master/config_sample.ru more options
153
+ Create a Rackup file (config.ru).
154
+
155
+ See https://github.com/jweir/ScaleDown/tree/master/config.sample.ru for all options
105
156
 
106
157
  ```ruby
107
158
  require 'rubygems'
data/config.sample.ru ADDED
@@ -0,0 +1,38 @@
1
+ require 'scale_down'
2
+
3
+ ScaleDown.tap do |config|
4
+
5
+ # The location of the public directory where the original are located
6
+ # This directory will also be the target for writing scaled images
7
+ # Most likely this will be a symbolic link to a shared directory
8
+ config.public_path = "#{File.expand_path(File.dirname(__FILE__))}/public"
9
+
10
+ # Labels are predefined geometries.
11
+ # They have the advantage of not requiring an HMAC signature in the URL
12
+ # The keys should be strings, not symbols
13
+ config.labels = {
14
+ "thumbnail" => "100x100"
15
+ }
16
+
17
+ # an array of the max width and height an image can be scaled, in pixels.
18
+ config.max_dimensions = [1200,1200]
19
+
20
+ # the max file size allowed for the original file to be scaled, in bytes
21
+ scaledown.max_file_size = 25 * 1_048_576 # 25 Megabytes
22
+
23
+ # This is the shared secret for generating and verifying the HMAC signature.
24
+ # This string would be shared with any application generating URLS for scaled images
25
+ # http://www.random.org/strings/
26
+ config.hmac_key = "secret"
27
+
28
+ # Change the method for generating the HMAC
29
+ config.hmac_method = HMAC::SHA1
30
+
31
+ # How long of an HMAC signature is required
32
+ config.hmac_length = 8
33
+
34
+ # Optional logger
35
+ # config.logger = YOUR_LOGGER
36
+ end
37
+
38
+ run ScaleDown::Controller
@@ -31,6 +31,10 @@ module ScaleDown
31
31
  # +Must be set+
32
32
  attr_accessor :public_folder
33
33
 
34
+ # Labels are predefined gemeotries
35
+ attr_accessor :labels
36
+ ScaleDown.labels = Hash.new
37
+
34
38
  def public_folder=(str)
35
39
  @public_folder = str
36
40
  ScaleDown::Controller.public_folder = str
@@ -25,7 +25,7 @@ class ScaleDown::Controller < Sinatra::Application
25
25
  params = {
26
26
  :hmac => request.env["QUERY_STRING"],
27
27
  :filename => parts.pop,
28
- :geometry => parts.pop,
28
+ :target => parts.pop, # the label or geometry
29
29
  :splat => parts
30
30
  }
31
31
 
@@ -54,7 +54,7 @@ class ScaleDown::Controller < Sinatra::Application
54
54
  ScaleDown::Dispatcher.process \
55
55
  :path => params[:splat].join("/"),
56
56
  :filename => params[:filename],
57
- :geometry => params[:geometry],
57
+ :target => params[:target],
58
58
  :hmac => params[:hmac]
59
59
  end
60
60
  end
@@ -37,11 +37,13 @@ class ScaleDown::Dispatcher
37
37
  end
38
38
 
39
39
  def image_options
40
- dimensions, *options = @params[:geometry].split("-")
41
- width, height = dimensions.split("x")
42
- {:height => height.to_i, :width => width.to_i}.tap do |o|
43
- options.each {|k| o[k.to_sym] = true}
44
- end
40
+ width, height = (target_is_label? ? ScaleDown.labels[target] : target).split("x")
41
+
42
+ {
43
+ :height => height.to_i,
44
+ :width => width.to_i,
45
+ :crop => crop?
46
+ }
45
47
  end
46
48
 
47
49
  def scale
@@ -51,8 +53,24 @@ class ScaleDown::Dispatcher
51
53
  :options => image_options
52
54
  end
53
55
 
56
+ def target
57
+ @dimensions ||= @params[:target].split(/-crop\Z/).first
58
+ end
59
+
60
+ def target_is_label?
61
+ @target_is_label ||= ScaleDown.labels.keys.include?(target)
62
+ end
63
+
64
+ def crop?
65
+ @crop ||= ! @params[:target].match(/-crop\Z/).nil?
66
+ end
67
+
54
68
  def valid_hmac?
55
- ScaleDown.valid_hmac?(@params)
69
+ if target_is_label?
70
+ true
71
+ else
72
+ ScaleDown.valid_hmac?(@params)
73
+ end
56
74
  end
57
75
 
58
76
  def redirect_code
@@ -60,7 +78,7 @@ class ScaleDown::Dispatcher
60
78
  end
61
79
 
62
80
  def redirect_path
63
- ["/"+@params[:path], @params[:geometry], scaled_filename].join("/")
81
+ ["/"+@params[:path], @params[:target], scaled_filename].join("/")
64
82
  end
65
83
 
66
84
  def root_file_exists?
@@ -1,3 +1,3 @@
1
1
  module ScaleDown
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
data/lib/scale_down.rb CHANGED
@@ -19,7 +19,7 @@ module ScaleDown
19
19
 
20
20
 
21
21
  def self.valid_hmac?(params)
22
- str = ["/", params[:path], "/", params[:geometry], "/", params[:filename]].join
22
+ str = ["/", params[:path], "/", params[:target], "/", params[:filename]].join
23
23
  hmac(str) == params[:hmac]
24
24
  end
25
25
 
@@ -9,5 +9,25 @@ class ScaleDown::Test < Test::Unit::TestCase
9
9
  assert_equal "/tmp/directory", ScaleDown::Controller.public_folder
10
10
  end
11
11
  end
12
+
13
+ context "labels" do
14
+ context "by default" do
15
+ should "be an empty hash" do
16
+ end
17
+ end
18
+
19
+ context "when defined" do
20
+ setup do
21
+ ScaleDown.labels = {
22
+ "thumbnail" => "40x40",
23
+ "large" => "600x600"
24
+ }
25
+ end
26
+
27
+ should "return the defined hash" do
28
+ assert_equal "40x40", ScaleDown.labels["thumbnail"]
29
+ end
30
+ end
31
+ end
12
32
  end
13
33
  end
@@ -12,16 +12,16 @@ class ScaleDown::Controller::Test < Test::Unit::TestCase
12
12
  ScaleDown::Dispatcher.expects(:process).with(
13
13
  :path => "user/path/scaled",
14
14
  :filename => "filename.png",
15
- :geometry => "400x300-crop-grayscale",
15
+ :target => "400x300-crop-grayscale",
16
16
  :hmac => "HMAC").
17
- returns ["path","status"]
17
+ returns ["path","status"]
18
18
 
19
- get '/user/path/scaled/400x300-crop-grayscale/filename.png?HMAC'
19
+ get '/user/path/scaled/400x300-crop-grayscale/filename.png?HMAC'
20
20
  end
21
21
  end
22
22
 
23
23
  context "a valid request" do
24
- should "send the file" do
24
+ should "respond with the file" do
25
25
  ScaleDown::Dispatcher.expects(:process).returns ["/image-path", 302]
26
26
  get "/path/geo/filename?hmac"
27
27
 
@@ -14,7 +14,7 @@ class ScaleDown::Dispatcher::Test < Test::Unit::TestCase
14
14
  @params = {
15
15
  :path => "file/path/scaled",
16
16
  :filename => "filename.png",
17
- :geometry => "400x300-crop",
17
+ :target => "400x300-crop",
18
18
  :hmac => hmac[0...8]
19
19
  }
20
20
  end
@@ -87,8 +87,35 @@ class ScaleDown::Dispatcher::Test < Test::Unit::TestCase
87
87
  end
88
88
  end
89
89
 
90
- context "process response" do
90
+ context "target" do
91
+ context "from a label" do
92
+ setup do
93
+ ScaleDown.labels = { "thumbnail" => "40x50" }
94
+ @subject = ScaleDown::Dispatcher.new :target => "thumbnail"
95
+ end
96
+
97
+ should "use the label's width and height" do
98
+ dim = @subject.image_options
99
+ assert_equal 40, dim[:width]
100
+ assert_equal 50, dim[:height]
101
+ assert_equal false, dim[:crop]
102
+ end
103
+
104
+ should "always valid the hmac" do
105
+ assert @subject.valid_hmac?
106
+ end
107
+
108
+ should "work with the cropped flag" do
109
+ @subject = ScaleDown::Dispatcher.new :target => "thumbnail-crop"
110
+ dim = @subject.image_options
111
+ assert_equal 40, dim[:width]
112
+ assert_equal 50, dim[:height]
113
+ assert_equal true, dim[:crop]
114
+ end
115
+ end
116
+ end
91
117
 
118
+ context "process response" do
92
119
  context "for an existing, unscaled image" do
93
120
  setup do
94
121
  File.expects(:exists?).with("/tmp/file/path/filename.png").returns true
@@ -26,7 +26,7 @@ class ScaleDown::Test < Test::Unit::TestCase
26
26
  @params = {
27
27
  :path => "file/path",
28
28
  :filename => "filename.png",
29
- :geometry => "400x300-crop",
29
+ :target => "400x300-crop",
30
30
  :hmac => hmac[0...8]
31
31
  }
32
32
  end
@@ -42,6 +42,7 @@ class ScaleDown::Test < Test::Unit::TestCase
42
42
 
43
43
  context "integration test" do
44
44
  setup do
45
+
45
46
  FileUtils.mkdir_p("/tmp/scale_down/test_images/example_1")
46
47
  FileUtils.cp fixture_path("files/graphic.png"), "/tmp/scale_down/test_images/example_1/graphic.png"
47
48
  FileUtils.mkdir_p("/tmp/scale_down/test_images/example_2")
@@ -58,7 +59,7 @@ class ScaleDown::Test < Test::Unit::TestCase
58
59
  assert_equal "300x500", last_response.body
59
60
  end
60
61
 
61
- should "get an image and scale it" do
62
+ should "get an image with a geometry and scale it" do
62
63
  valid_get '/test_images/example_1/scaled/400x300-cropped/graphic.png'
63
64
  assert_equal 200, last_response.status
64
65
  assert File.exists?("/tmp/scale_down/test_images/example_1/scaled/400x300-cropped/graphic.png")
@@ -75,6 +76,29 @@ class ScaleDown::Test < Test::Unit::TestCase
75
76
  assert_equal 500, last_response.status
76
77
  assert !File.exists?("/tmp/scale_down/test_images/example_2/scaled/400x300-cropped/invalid_jpeg.jpg")
77
78
  end
79
+
80
+ context "using a label" do
81
+ setup do
82
+ ScaleDown.labels = { "very-large" => "600x600" }
83
+ end
84
+
85
+ should "get an image with a label and scale it" do
86
+ get '/test_images/example_1/scaled/very-large/graphic.png'
87
+ assert_equal 200, last_response.status
88
+ assert File.exists?("/tmp/scale_down/test_images/example_1/scaled/very-large/graphic.png")
89
+
90
+ get '/test_images/example_1/scaled/very-large-crop/graphic.png'
91
+ assert_equal 200, last_response.status
92
+ assert File.exists?("/tmp/scale_down/test_images/example_1/scaled/very-large-crop/graphic.png")
93
+ end
94
+
95
+ context "that does not exist" do
96
+ should "return an error" do
97
+ get '/test_images/example_1/scaled/toosmall/graphic.png'
98
+ assert_equal 403, last_response.status
99
+ end
100
+ end
101
+ end
78
102
  end
79
103
  end
80
104
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scale_down
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-24 00:00:00.000000000Z
12
+ date: 2012-01-26 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rmagick
16
- requirement: &2153706660 !ruby/object:Gem::Requirement
16
+ requirement: &2152325300 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2153706660
24
+ version_requirements: *2152325300
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sinatra
27
- requirement: &2153706160 !ruby/object:Gem::Requirement
27
+ requirement: &2152324800 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '1.0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2153706160
35
+ version_requirements: *2152324800
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: ruby-hmac
38
- requirement: &2153705700 !ruby/object:Gem::Requirement
38
+ requirement: &2152324340 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.4.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *2153705700
46
+ version_requirements: *2152324340
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: contest
49
- requirement: &2153705240 !ruby/object:Gem::Requirement
49
+ requirement: &2152323880 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 0.1.2
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2153705240
57
+ version_requirements: *2152323880
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rake
60
- requirement: &2153704780 !ruby/object:Gem::Requirement
60
+ requirement: &2152323420 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 0.9.2.2
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *2153704780
68
+ version_requirements: *2152323420
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mocha
71
- requirement: &2153734500 !ruby/object:Gem::Requirement
71
+ requirement: &2152353140 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - =
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 0.9.8
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *2153734500
79
+ version_requirements: *2152353140
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rack-test
82
- requirement: &2153734040 !ruby/object:Gem::Requirement
82
+ requirement: &2152352680 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - =
@@ -87,7 +87,7 @@ dependencies:
87
87
  version: 0.5.6
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *2153734040
90
+ version_requirements: *2152352680
91
91
  description: ''
92
92
  email:
93
93
  - john@famedriver.com
@@ -102,7 +102,7 @@ files:
102
102
  - README.md
103
103
  - Rakefile
104
104
  - color_profiles/sRGB.icm
105
- - config_sample.ru
105
+ - config.sample.ru
106
106
  - lib/scale_down.rb
107
107
  - lib/scale_down/configuration.rb
108
108
  - lib/scale_down/controller.rb
data/config_sample.ru DELETED
@@ -1,21 +0,0 @@
1
- require 'scale_down'
2
-
3
- ScaleDown.tap do |config|
4
-
5
- # This is the shared secret. Use something strong
6
- # Perhaps a visit to http://www.random.org/strings/?num=10&len=20&digits=on&upperalpha=on&format=html&rnd=new
7
- config.hmac_key = "secret"
8
-
9
- # You can use a different HMAC, see the ruby-hmac gen
10
- config.hmac_method = HMAC::SHA1
11
-
12
- # The length of the HMAC signature to use
13
- config.hmac_length = 8
14
-
15
- # The location of the public directory for serving static files
16
- # This might be redudant since it will always, maybe, maybe not, be the same as root_path
17
- config.public_path = "#{File.expand_path(File.dirname(__FILE__))}/public"
18
-
19
- end
20
-
21
- run ScaleDown::Controller