Class: Rack::GridThumb
- Inherits:
-
Object
- Object
- Rack::GridThumb
- Defined in:
- lib/rack_grid_thumb.rb
Constant Summary collapse
- RE_TH_BASE =
/_([0-9]+x|x[0-9]+|[0-9]+x[0-9]+)(-(?:nw|n|ne|w|c|e|sw|s|se))?/
- RE_TH_EXT =
/(\.(?:jpg|jpeg|png|gif))/i
- TH_GRAV =
{ '-nw' => :northwest, '-n' => :north, '-ne' => :northeast, '-w' => :west, '-c' => :center, '-e' => :east, '-sw' => :southwest, '-s' => :south, '-se' => :southeast }
Instance Method Summary collapse
- #_call(env) ⇒ Object
- #bad_request ⇒ Object
- #call(env) ⇒ Object
-
#create_tempfile ⇒ Object
Creates a new tempfile.
- #each ⇒ Object
-
#extract_meta(match) ⇒ Object
Extracts filename and options from the path.
-
#extract_signed_meta(match) ⇒ Object
Extracts filename and options from a signed path.
-
#extract_unsigned_meta(match) ⇒ Object
Extracts filename and options from an unsigned path.
-
#generate_route(prefix = nil) ⇒ Object
Generates route given a prefixes.
-
#get_source_image ⇒ Object
Fetch the source image from the downstream app, returning the downstream app’s response if it is not a success.
- #head? ⇒ Boolean
-
#initialize(app, options = {}) ⇒ GridThumb
constructor
A new instance of GridThumb.
-
#parse_dimensions(meta) ⇒ Object
Parses the rendering options; returns false if rendering options are invalid.
-
#render_thumbnail(dim, grav) ⇒ Object
Renders a thumbnail from the source image.
-
#serve ⇒ Object
Serves the thumbnail.
- #to_path ⇒ Object
Constructor Details
#initialize(app, options = {}) ⇒ GridThumb
Returns a new instance of GridThumb.
23 24 25 26 27 28 |
# File 'lib/rack_grid_thumb.rb', line 23 def initialize(app, ={}) @app = app @keylen = [:keylength] @secret = [:secret] @route = generate_route([:prefix]) end |
Instance Method Details
#_call(env) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/rack_grid_thumb.rb', line 43 def _call(env) response = catch(:halt) do throw :halt unless %w{GET HEAD}.include? env["REQUEST_METHOD"] @env = env @path = env["PATH_INFO"] if match = @path.match(@route) @source, dim, grav = (match) @image = get_source_image @thumb = render_thumbnail(dim, grav) unless head? serve end nil end response || @app.call(env) end |
#bad_request ⇒ Object
166 167 168 169 170 171 |
# File 'lib/rack_grid_thumb.rb', line 166 def bad_request body = "Bad thumbnail parameters in #{@path}\n" [400, {"Content-Type" => "text/plain", "Content-Length" => body.size.to_s}, [body]] end |
#call(env) ⇒ Object
39 40 41 |
# File 'lib/rack_grid_thumb.rb', line 39 def call(env) dup._call(env) end |
#create_tempfile ⇒ Object
Creates a new tempfile
162 163 164 |
# File 'lib/rack_grid_thumb.rb', line 162 def create_tempfile Tempfile.new(::File.basename(@path)).tap { |f| f.close } end |
#each ⇒ Object
177 178 179 180 181 182 183 |
# File 'lib/rack_grid_thumb.rb', line 177 def each ::File.open(@thumb.path, "rb") { |file| while part = file.read(8192) yield part end } end |
#extract_meta(match) ⇒ Object
Extracts filename and options from the path.
61 62 63 64 65 66 67 68 69 70 |
# File 'lib/rack_grid_thumb.rb', line 61 def (match) result = if @keylen (match) else (match) end throw :halt unless result result end |
#extract_signed_meta(match) ⇒ Object
Extracts filename and options from a signed path.
73 74 75 76 77 78 |
# File 'lib/rack_grid_thumb.rb', line 73 def (match) base, dim, grav, sig, ext = match.captures digest = Digest::SHA1.hexdigest("#{base}_#{dim}#{grav}#{ext}#{@secret}")[0..@keylen-1] throw(:halt, bad_request) unless sig && (sig == digest) [base + ext, dim, grav] end |
#extract_unsigned_meta(match) ⇒ Object
Extracts filename and options from an unsigned path.
81 82 83 84 |
# File 'lib/rack_grid_thumb.rb', line 81 def (match) base, dim, grav, ext = match.captures [base + ext, dim, grav] end |
#generate_route(prefix = nil) ⇒ Object
Generates route given a prefixes.
31 32 33 34 35 36 37 |
# File 'lib/rack_grid_thumb.rb', line 31 def generate_route(prefix = nil) if @keylen /^(\/#{prefix}\/\w+).*#{RE_TH_BASE}-([0-9a-f]{#{@keylen}})#{RE_TH_EXT}$/ else /^(\/#{prefix}\/\w+).*#{RE_TH_BASE}#{RE_TH_EXT}$/ end end |
#get_source_image ⇒ Object
Fetch the source image from the downstream app, returning the downstream app’s response if it is not a success.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/rack_grid_thumb.rb', line 88 def get_source_image status, headers, body = @app.call(@env.merge( "PATH_INFO" => @source )) unless (status >= 200 && status < 300) && (headers["Content-Type"].split("/").first == "image") throw :halt, [status, headers, body] end @source_headers = headers if !head? if body.respond_to?(:path) ::File.open(body.path, 'rb') elsif body.respond_to?(:each) data = '' body.each { |part| data << part.to_s } Tempfile.new(::File.basename(@path)).tap do |f| f.binmode f.write(data) f.close end end else nil end end |
#head? ⇒ Boolean
173 174 175 |
# File 'lib/rack_grid_thumb.rb', line 173 def head? @env["REQUEST_METHOD"] == "HEAD" end |
#parse_dimensions(meta) ⇒ Object
Parses the rendering options; returns false if rendering options are invalid
148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/rack_grid_thumb.rb', line 148 def parse_dimensions() dimensions = .split('x').map do |dim| if dim.empty? nil elsif dim[0].to_i == 0 throw :halt, bad_request else dim.to_i end end dimensions.any? ? dimensions : throw(:halt, bad_request) end |
#render_thumbnail(dim, grav) ⇒ Object
Renders a thumbnail from the source image. Returns a Tempfile.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/rack_grid_thumb.rb', line 117 def render_thumbnail(dim, grav) gravity = grav ? TH_GRAV[grav] : :center width, height = parse_dimensions(dim) origin_width, origin_height = Mapel.info(@image.path)[:dimensions] width = [width, origin_width].min if width height = [height, origin_height].min if height output = create_tempfile cmd = Mapel(@image.path).gravity(gravity) if width && height cmd.resize!(width, height) else cmd.resize(width, height, 0, 0, '>') end cmd.to(output.path).run output end |
#serve ⇒ Object
Serves the thumbnail. If this is a HEAD request we strip the body as well as the content length because the render was never run.
136 137 138 139 140 141 142 143 144 145 |
# File 'lib/rack_grid_thumb.rb', line 136 def serve response = if head? @source_headers.delete("Content-Length") [200, @source_headers, []] else [200, @source_headers.merge("Content-Length" => ::File.size(@thumb.path).to_s), self] end throw :halt, response end |
#to_path ⇒ Object
185 186 187 |
# File 'lib/rack_grid_thumb.rb', line 185 def to_path @thumb.path end |