rack_grid_thumb 0.0.3

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.txt ADDED
@@ -0,0 +1,49 @@
1
+ Rack::GridThumb
2
+
3
+ Rack::GridThumb is used to dynamically create thumbnails when in front of rack_grid.
4
+ You should run Rack::GridThumb behind a cache such as Varnish or Rack::Cache
5
+
6
+ Installation
7
+
8
+ # gem install rack_grid_thumb
9
+
10
+ Usage Example with Sinatra.
11
+
12
+ # app.rb
13
+ require 'rack_grid'
14
+ require 'rack_grid_thumb'
15
+
16
+ configure do
17
+ use Rack::GridThumb, :prefix => 'grid'
18
+ use Rack::Grid, :prefix => 'grid'
19
+ end
20
+
21
+ # view.erb
22
+ <img src="/grid/4ba69fde8c8f369a6e000003_50x50.jpg" alt="My Image" />
23
+
24
+ Usage
25
+
26
+ /#{prefix}/#{uid}_50x50.jpg # => Crop and resize to 50x50
27
+ /#{prefix}/#{uid}_50x50-nw.jpg # => Crop and resize with northwest gravity
28
+ /#{prefix}/#{uid}_50x.jpg # => Resize to a width of 50, preserving AR
29
+ /#{prefix}/#{uid}_x50.jpg # => Resize to a height of 50, preserving AR
30
+
31
+
32
+ To prevent pesky end-users and bots from flooding your application with
33
+ render requests you can set up Rack::Thumb to check for an SHA-1 signature
34
+ that is unique to every url. Using this option, only thumbnails requested
35
+ by your templates will be valid. Example:
36
+
37
+ use Rack::Thumb, {
38
+ :secret => "My secret",
39
+ :keylength => "16" # => Only use 16 digits of the SHA-1 key
40
+ }
41
+
42
+ You can then use your +secret+ to generate secure links in your templates:
43
+
44
+ /#{prefix}/#{uid}_50x100-sw-a267c193a7eff046.jpg # => Successful
45
+ /#{prefix}/#{uid}_120x250-a267c193a7eff046.jpg # => Returns a bad request error
46
+
47
+
48
+ Inspired by:
49
+ https://github.com/akdubya/rack-thumb
@@ -0,0 +1,189 @@
1
+ require 'rack'
2
+ require 'mapel'
3
+ require 'digest/sha1'
4
+ require 'tempfile'
5
+
6
+ module Rack
7
+
8
+ class GridThumb
9
+ RE_TH_BASE = /_([0-9]+x|x[0-9]+|[0-9]+x[0-9]+)(-(?:nw|n|ne|w|c|e|sw|s|se))?/
10
+ RE_TH_EXT = /(\.(?:jpg|jpeg|png|gif))/i
11
+ TH_GRAV = {
12
+ '-nw' => :northwest,
13
+ '-n' => :north,
14
+ '-ne' => :northeast,
15
+ '-w' => :west,
16
+ '-c' => :center,
17
+ '-e' => :east,
18
+ '-sw' => :southwest,
19
+ '-s' => :south,
20
+ '-se' => :southeast
21
+ }
22
+
23
+ def initialize(app, options={})
24
+ @app = app
25
+ @keylen = options[:keylength]
26
+ @secret = options[:secret]
27
+ @route = generate_route(options[:prefix])
28
+ end
29
+
30
+ # Generates route given a prefixes.
31
+ def generate_route(prefix = nil)
32
+ if @keylen
33
+ /^(\/#{prefix}\/\w+).*#{RE_TH_BASE}-([0-9a-f]{#{@keylen}})#{RE_TH_EXT}$/
34
+ else
35
+ /^(\/#{prefix}\/\w+).*#{RE_TH_BASE}#{RE_TH_EXT}$/
36
+ end
37
+ end
38
+
39
+ def call(env)
40
+ dup._call(env)
41
+ end
42
+
43
+ def _call(env)
44
+ response = catch(:halt) do
45
+ throw :halt unless %w{GET HEAD}.include? env["REQUEST_METHOD"]
46
+ @env = env
47
+ @path = env["PATH_INFO"]
48
+ if match = @path.match(@route)
49
+ @source, dim, grav = extract_meta(match)
50
+ @image = get_source_image
51
+ @thumb = render_thumbnail(dim, grav) unless head?
52
+ serve
53
+ end
54
+ nil
55
+ end
56
+
57
+ response || @app.call(env)
58
+ end
59
+
60
+ # Extracts filename and options from the path.
61
+ def extract_meta(match)
62
+ result = if @keylen
63
+ extract_signed_meta(match)
64
+ else
65
+ extract_unsigned_meta(match)
66
+ end
67
+
68
+ throw :halt unless result
69
+ result
70
+ end
71
+
72
+ # Extracts filename and options from a signed path.
73
+ def extract_signed_meta(match)
74
+ base, dim, grav, sig, ext = match.captures
75
+ digest = Digest::SHA1.hexdigest("#{base}_#{dim}#{grav}#{ext}#{@secret}")[0..@keylen-1]
76
+ throw(:halt, bad_request) unless sig && (sig == digest)
77
+ [base + ext, dim, grav]
78
+ end
79
+
80
+ # Extracts filename and options from an unsigned path.
81
+ def extract_unsigned_meta(match)
82
+ base, dim, grav, ext = match.captures
83
+ [base + ext, dim, grav]
84
+ end
85
+
86
+ # Fetch the source image from the downstream app, returning the downstream
87
+ # app's response if it is not a success.
88
+ def get_source_image
89
+ status, headers, body = @app.call(@env.merge(
90
+ "PATH_INFO" => @source
91
+ ))
92
+ unless (status >= 200 && status < 300) &&
93
+ (headers["Content-Type"].split("/").first == "image")
94
+ throw :halt, [status, headers, body]
95
+ end
96
+
97
+ @source_headers = headers
98
+
99
+ if !head?
100
+ if body.respond_to?(:path)
101
+ ::File.open(body.path, 'rb')
102
+ elsif body.respond_to?(:each)
103
+ data = ''
104
+ body.each { |part| data << part.to_s }
105
+ Tempfile.new(::File.basename(@path)).tap do |f|
106
+ f.binmode
107
+ f.write(data)
108
+ f.close
109
+ end
110
+ end
111
+ else
112
+ nil
113
+ end
114
+ end
115
+
116
+ # Renders a thumbnail from the source image. Returns a Tempfile.
117
+ def render_thumbnail(dim, grav)
118
+ gravity = grav ? TH_GRAV[grav] : :center
119
+ width, height = parse_dimensions(dim)
120
+ origin_width, origin_height = Mapel.info(@image.path)[:dimensions]
121
+ width = [width, origin_width].min if width
122
+ height = [height, origin_height].min if height
123
+ output = create_tempfile
124
+ cmd = Mapel(@image.path).gravity(gravity)
125
+ if width && height
126
+ cmd.resize!(width, height)
127
+ else
128
+ cmd.resize(width, height, 0, 0, '>')
129
+ end
130
+ cmd.to(output.path).run
131
+ output
132
+ end
133
+
134
+ # Serves the thumbnail. If this is a HEAD request we strip the body as well
135
+ # as the content length because the render was never run.
136
+ def serve
137
+ response = if head?
138
+ @source_headers.delete("Content-Length")
139
+ [200, @source_headers, []]
140
+ else
141
+ [200, @source_headers.merge("Content-Length" => ::File.size(@thumb.path).to_s), self]
142
+ end
143
+
144
+ throw :halt, response
145
+ end
146
+
147
+ # Parses the rendering options; returns false if rendering options are invalid
148
+ def parse_dimensions(meta)
149
+ dimensions = meta.split('x').map do |dim|
150
+ if dim.empty?
151
+ nil
152
+ elsif dim[0].to_i == 0
153
+ throw :halt, bad_request
154
+ else
155
+ dim.to_i
156
+ end
157
+ end
158
+ dimensions.any? ? dimensions : throw(:halt, bad_request)
159
+ end
160
+
161
+ # Creates a new tempfile
162
+ def create_tempfile
163
+ Tempfile.new(::File.basename(@path)).tap { |f| f.close }
164
+ end
165
+
166
+ def bad_request
167
+ body = "Bad thumbnail parameters in #{@path}\n"
168
+ [400, {"Content-Type" => "text/plain",
169
+ "Content-Length" => body.size.to_s},
170
+ [body]]
171
+ end
172
+
173
+ def head?
174
+ @env["REQUEST_METHOD"] == "HEAD"
175
+ end
176
+
177
+ def each
178
+ ::File.open(@thumb.path, "rb") { |file|
179
+ while part = file.read(8192)
180
+ yield part
181
+ end
182
+ }
183
+ end
184
+
185
+ def to_path
186
+ @thumb.path
187
+ end
188
+ end
189
+ end
File without changes
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack_grid_thumb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dusty Doris
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-07-08 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mapel
16
+ requirement: &2157443320 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2157443320
25
+ description: Auto-create thumbnails when used with rack_grid
26
+ email: github@dusty.name
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files:
30
+ - README.txt
31
+ files:
32
+ - README.txt
33
+ - lib/rack_grid_thumb.rb
34
+ - test/test_rack_grid_thumb.rb
35
+ homepage: http://github.com/dusty/rack_grid_thumb
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project: none
55
+ rubygems_version: 1.8.5
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: Auto-create thumbnails when used with rack_grid
59
+ test_files: []