Skip to content
Browse files

Namespace everything under Imagery module and lib/imagery

  • Loading branch information...
1 parent 013c543 commit bddd1395892bdfad940832eada8b8d5e75709a67 @josh josh committed with Dec 17, 2009
Showing with 483 additions and 471 deletions.
  1. +11 −9 config.ru
  2. +0 −33 image_server.rb
  3. +0 −68 lib/image.rb
  4. +0 −47 lib/image_variant_generator.rb
  5. +12 −0 lib/imagery.rb
  6. +69 −0 lib/imagery/image.rb
  7. +47 −0 lib/imagery/image_variant_generator.rb
  8. 0 lib/{ → imagery}/logger_ext.rb
  9. +41 −0 lib/imagery/middleware/accel_redirect.rb
  10. +21 −0 lib/imagery/middleware/cache_purge.rb
  11. +17 −0 lib/imagery/middleware/favicon_filter.rb
  12. +32 −0 lib/imagery/middleware/logged_request.rb
  13. +24 −0 lib/imagery/middleware/remote_proxy.rb
  14. +13 −0 lib/imagery/middleware/server_name.rb
  15. +32 −0 lib/imagery/send_file.rb
  16. +22 −0 lib/imagery/server.rb
  17. +55 −0 lib/imagery/svg_generator.rb
  18. +23 −0 lib/imagery/transformations.rb
  19. +35 −0 lib/imagery/transformations/borders.rb
  20. +8 −8 lib/{ → imagery}/transformations/sizes.rb
  21. +8 −9 lib/{ → imagery}/transformations/transform.rb
  22. 0 lib/{ → imagery}/vendor/SyslogLogger-1.4.0/History.txt
  23. 0 lib/{ → imagery}/vendor/SyslogLogger-1.4.0/Manifest.txt
  24. 0 lib/{ → imagery}/vendor/SyslogLogger-1.4.0/README.txt
  25. 0 lib/{ → imagery}/vendor/SyslogLogger-1.4.0/Rakefile
  26. 0 lib/{ → imagery}/vendor/SyslogLogger-1.4.0/lib/syslog_logger.rb
  27. 0 lib/{ → imagery}/vendor/SyslogLogger-1.4.0/test/test_syslog_logger.rb
  28. +0 −40 lib/middleware/accel_redirect.rb
  29. +0 −20 lib/middleware/cache_purge.rb
  30. +0 −16 lib/middleware/favicon_filter.rb
  31. +0 −31 lib/middleware/logged_request.rb
  32. +0 −23 lib/middleware/remote_proxy.rb
  33. +0 −12 lib/middleware/server_name.rb
  34. +0 −31 lib/send_file.rb
  35. +0 −56 lib/svg_generator.rb
  36. +0 −21 lib/transformations.rb
  37. +0 −35 lib/transformations/borders.rb
  38. +2 −1 test/helper.rb
  39. +6 −6 test/test_image_variant_generator.rb
  40. +1 −1 test/test_remote_proxy.rb
  41. +4 −4 test/test_svg_generation.rb
View
20 config.ru
@@ -3,7 +3,9 @@
require 'rubygems'
require 'rack/cache'
require 'rack/contrib'
-require 'image_server'
+
+$: << File.join(File.dirname(__FILE__), 'lib')
+require 'imagery'
require 'config/env'
@@ -20,22 +22,22 @@ end
if ENV['NGINX_ACCEL_REDIRECTS']
STDERR.puts 'Using accel redirect (Shopify config).'
- require 'lib/middleware/accel_redirect'
- use AccelRedirect
+ require 'imagery/middleware/accel_redirect'
+ use Imagery::AccelRedirect
else
use Rack::Sendfile
end
use Rack::ShowExceptions
# 1. Forget about stupid favicons
-use FaviconFilter
+use Imagery::FaviconFilter
# 2. Log all other incoming requests
-use LoggedRequest
+use Imagery::LoggedRequest
# 3. Override server name into something non embarrasing
-use ServerName
+use Imagery::ServerName
# 4. Content type needs to be present, default to attachment
use Rack::ContentType, "application/octet-stream"
@@ -46,10 +48,10 @@ use Rack::Cache,
:entitystore => ENV['ENTITY_STORE']
# 6. handle PURGE requests
-use CachePurge
+use Imagery::CachePurge
# 7. See if files already exist on remote host, if so handle them directly
-use RemoteProxy
+use Imagery::RemoteProxy
# 8. Otherwise run the image server and produce the missing images
-run ImageServer.new
+run Imagery::Server.new
View
33 image_server.rb
@@ -1,33 +0,0 @@
-require 'lib/send_file'
-require 'lib/middleware/cache_purge'
-require 'lib/middleware/logged_request'
-require 'lib/middleware/remote_proxy'
-require 'lib/middleware/server_name'
-require 'lib/middleware/favicon_filter'
-require 'lib/logger_ext'
-require 'lib/transformations'
-require 'lib/svg_generator'
-require 'lib/image_variant_generator'
-require 'lib/image'
-
-class ImageServer
- include SendFile
-
- NotFound = [404, {'Content-Type' => 'text/html'}, ['<h1>File not Found</h1>']].freeze
-
- def call(env)
- Logger.current.info 'Attempting to generate missing file...'
-
- [SvgGenerator, ImageVariantGenerator].each do |generator|
- if image = generator.from_url(env['imagery.origin_host'], env['PATH_INFO'] + (env['QUERY_STRING'].empty? ? '' : "?#{env['QUERY_STRING']}"))
-
- return send_file(image)
- end
- end
-
- Logger.current.info 'No generator available'
-
- NotFound
- end
-
-end
View
68 lib/image.rb
@@ -1,68 +0,0 @@
-require 'RMagick'
-require 'fileutils'
-require 'patron'
-
-class Image
- attr_accessor :content
- attr_reader :headers
- attr_reader :status
- attr_reader :server
-
- def initialize(server, path)
- @server = server
- @path = path
- download(path)
- end
-
- def found?
- @status == 200
- end
-
- def basename
- File.basename(@path)
- end
-
- def basename_no_ext
- File.basename(@path, ext)
- end
-
- def ext
- File.extname(@path)
- end
-
- def dirname
- File.dirname(@path)
- end
-
- private
-
- def session
- @@session ||= begin
- sess = Patron::Session.new
- sess.timeout = 10
- sess.headers['User-Agent'] = 'imagery/1.0'
- sess
- end
- end
-
- def download(path_info)
- session.base_url = "http://#{server}"
-
- response = Logger.current.info_with_time "Loading http://#{server}#{path_info}" do
- session.get(path_info)
- end
-
- @path = path_info.split('?')[0]
- @headers = response.headers
- @status = response.status
-
- if found?
- self.content = response.body
- true
- else
- Logger.current.error "Not found"
- false
- end
- end
-end
-
View
47 lib/image_variant_generator.rb
@@ -1,47 +0,0 @@
-require 'RMagick'
-require 'fileutils'
-
-class ImageVariantGenerator
- VARIANT_DELIMITER = '_'
- SupportedImageTypes = ['.gif', '.jpg', '.jpeg', '.png', '.bmp']
-
- attr_accessor :content
- attr_accessor :content_type
-
- def self.variant_parser
- @variant_parser ||= /(.*)\_(#{Transformations.list.join('|')})(#{SupportedImageTypes.join('|')})/i
- end
-
- def self.from_url(server, path)
- return nil unless path =~ variant_parser
-
- remote_path = "#{$1}#{$3}"
-
- file = Image.new(server, remote_path)
- if file.found?
- transform_content(file, $2)
- file
- else
- nil
- end
- end
-
- def initialize(image)
- @image = image
- end
-
- def self.transform_content(image, variant)
- img = Magick::Image.from_blob(image.content).first
- transformation = Transformations[variant]
-
- Logger.current.info_with_time "Transforming image to #{variant}" do
- raise ArgumentError, "#{variant} is not a known transformation. (#{Transformations.list.join(', ')})" if transformation.nil?
- img = transformation.call(img)
- raise ArgumentError, "Creating variant #{variant} for #{path} produced an error. Please return a Magick::Image" if img.nil?
- image.content = img.to_blob
- end
- true
- end
-
-end
-
View
12 lib/imagery.rb
@@ -0,0 +1,12 @@
+require 'imagery/send_file'
+require 'imagery/middleware/cache_purge'
+require 'imagery/middleware/logged_request'
+require 'imagery/middleware/remote_proxy'
+require 'imagery/middleware/server_name'
+require 'imagery/middleware/favicon_filter'
+require 'imagery/logger_ext'
+require 'imagery/transformations'
+require 'imagery/svg_generator'
+require 'imagery/image_variant_generator'
+require 'imagery/image'
+require 'imagery/server'
View
69 lib/imagery/image.rb
@@ -0,0 +1,69 @@
+require 'RMagick'
+require 'fileutils'
+require 'patron'
+
+module Imagery
+ class Image
+ attr_accessor :content
+ attr_reader :headers
+ attr_reader :status
+ attr_reader :server
+
+ def initialize(server, path)
+ @server = server
+ @path = path
+ download(path)
+ end
+
+ def found?
+ @status == 200
+ end
+
+ def basename
+ File.basename(@path)
+ end
+
+ def basename_no_ext
+ File.basename(@path, ext)
+ end
+
+ def ext
+ File.extname(@path)
+ end
+
+ def dirname
+ File.dirname(@path)
+ end
+
+ private
+
+ def session
+ @@session ||= begin
+ sess = Patron::Session.new
+ sess.timeout = 10
+ sess.headers['User-Agent'] = 'imagery/1.0'
+ sess
+ end
+ end
+
+ def download(path_info)
+ session.base_url = "http://#{server}"
+
+ response = Logger.current.info_with_time "Loading http://#{server}#{path_info}" do
+ session.get(path_info)
+ end
+
+ @path = path_info.split('?')[0]
+ @headers = response.headers
+ @status = response.status
+
+ if found?
+ self.content = response.body
+ true
+ else
+ Logger.current.error "Not found"
+ false
+ end
+ end
+ end
+end
View
47 lib/imagery/image_variant_generator.rb
@@ -0,0 +1,47 @@
+require 'RMagick'
+require 'fileutils'
+
+module Imagery
+ class ImageVariantGenerator
+ VARIANT_DELIMITER = '_'
+ SupportedImageTypes = ['.gif', '.jpg', '.jpeg', '.png', '.bmp']
+
+ attr_accessor :content
+ attr_accessor :content_type
+
+ def self.variant_parser
+ @variant_parser ||= /(.*)\_(#{Transformations.list.join('|')})(#{SupportedImageTypes.join('|')})/i
+ end
+
+ def self.from_url(server, path)
+ return nil unless path =~ variant_parser
+
+ remote_path = "#{$1}#{$3}"
+
+ file = Image.new(server, remote_path)
+ if file.found?
+ transform_content(file, $2)
+ file
+ else
+ nil
+ end
+ end
+
+ def initialize(image)
+ @image = image
+ end
+
+ def self.transform_content(image, variant)
+ img = Magick::Image.from_blob(image.content).first
+ transformation = Transformations[variant]
+
+ Logger.current.info_with_time "Transforming image to #{variant}" do
+ raise ArgumentError, "#{variant} is not a known transformation. (#{Transformations.list.join(', ')})" if transformation.nil?
+ img = transformation.call(img)
+ raise ArgumentError, "Creating variant #{variant} for #{path} produced an error. Please return a Magick::Image" if img.nil?
+ image.content = img.to_blob
+ end
+ true
+ end
+ end
+end
View
0 lib/logger_ext.rb → lib/imagery/logger_ext.rb
File renamed without changes.
View
41 lib/imagery/middleware/accel_redirect.rb
@@ -0,0 +1,41 @@
+require 'rack/file'
+
+class File #:nodoc:
+ alias :to_path :path
+end
+
+# To make this work you have to add:
+#
+# location /cache/ {
+ # internal;
+ # alias /mnt/data/cache/rack/body;
+# }
+#
+# to nginx config
+
+
+module Imagery
+ class AccelRedirect
+ F = ::File
+
+ def initialize(app, variation=nil)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, body = @app.call(env)
+ if body.respond_to?(:to_path)
+
+ path = body.to_path
+ url = path.sub(/^#{ENV['CACHE_LOCATION']}/i, '/cache')
+
+ Logger.current.info " => sending #{url} through nginx"
+
+ headers['Content-Length'] = '0'
+ headers['X-Accel-Redirect'] = url
+ body = []
+ end
+ [status, headers, body]
+ end
+ end
+end
View
21 lib/imagery/middleware/cache_purge.rb
@@ -0,0 +1,21 @@
+module Imagery
+ class CachePurge
+ Success = [200, {'Content-Type' => 'text/plain'}, ['OK']]
+
+ def initialize(app)
+ @app = app
+ end
+
+ # PURGE /s/files/1/0001/4168/files/thumbs/pic_thumb.jpg?12428536032 HTTP/1.0
+
+ def call(env)
+ # Rack cache automatically invalidates resource if the verbs are not GET/POST so
+ # we don't actually have to do anything. Simply don't delegate those to the backend
+ if env['REQUEST_METHOD'] == 'PURGE'
+ Success
+ else
+ @app.call(env)
+ end
+ end
+ end
+end
View
17 lib/imagery/middleware/favicon_filter.rb
@@ -0,0 +1,17 @@
+module Imagery
+ class FaviconFilter
+ Empty = [200, {'Content-Type' => 'text/plain'}, ['']].freeze
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ if env['REQUEST_URI'] == '/favicon.ico'
+ return Empty
+ else
+ @app.call(env)
+ end
+ end
+ end
+end
View
32 lib/imagery/middleware/logged_request.rb
@@ -0,0 +1,32 @@
+module Imagery
+ class LoggedRequest
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+
+ request = Rack::Cache::Request.new(env)
+
+ resp = nil
+
+ Logger.current.buffer do
+
+ Logger.current.info "#{request.request_method} #{request.path} [#{request.ip}]"
+
+ secs = Benchmark.realtime do
+ Logger.current.intend do
+ resp = @app.call(env)
+ end
+ end
+
+ Logger.current.info((resp[0] < 399 ? 'Success' : "Error [#{resp[0]}]") + " after %.3fs Cache: %s" % [secs, resp[1]['X-Rack-Cache']])
+ Logger.current.info ''
+
+ end
+
+ resp
+ end
+ end
+end
View
24 lib/imagery/middleware/remote_proxy.rb
@@ -0,0 +1,24 @@
+module Imagery
+ class RemoteProxy
+ include SendFile
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ request = Rack::Request.new(env)
+
+ requested_file = Image.new(env['imagery.origin_host'], env['PATH_INFO'] + (env['QUERY_STRING'].empty? ? '' : "?#{env['QUERY_STRING']}"))
+
+ # If file exists we simply sent it to the client.
+ if requested_file.found?
+ Logger.current.info "Requested file exists upstream."
+
+ send_file(requested_file)
+ else
+ @app.call(env)
+ end
+ end
+ end
+end
View
13 lib/imagery/middleware/server_name.rb
@@ -0,0 +1,13 @@
+module Imagery
+ class ServerName
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ hash = @app.call(env)
+ hash[1]['Server'] = 'Shopify Imagery'
+ hash
+ end
+ end
+end
View
32 lib/imagery/send_file.rb
@@ -0,0 +1,32 @@
+require 'time'
+
+module Imagery
+ module SendFile
+ CopyHeaders = ['Content-Type', 'Cache-Control', 'Last-Modified', 'ETag']
+
+ ContentTypes = {
+ '.gif' => 'image/gif',
+ '.jpg' => 'image/jpeg',
+ '.jpeg' => 'image/jpeg',
+ '.png' => 'image/png',
+ '.bmp' => 'image/x-bitmap',
+ '.svg' => 'image/svg+xml'
+ }
+
+ def send_file(file)
+ headers = {'Content-Length' => file.content.length.to_s}
+
+ if file.respond_to?(:headers)
+ CopyHeaders.each do |key|
+ headers[key] = file.headers[key] if file.headers.has_key?(key)
+ end
+ end
+
+ headers['ETag'] ||= Digest::MD5.hexdigest(file.content)
+ headers['Cache-Control'] ||= 'public, max-age=31557600'
+ headers['Last-Modified'] ||= Time.new.httpdate
+
+ [200, headers, [file.content]]
+ end
+ end
+end
View
22 lib/imagery/server.rb
@@ -0,0 +1,22 @@
+module Imagery
+ class Server
+ include SendFile
+
+ NotFound = [404, {'Content-Type' => 'text/html'}, ['<h1>File not Found</h1>']].freeze
+
+ def call(env)
+ Logger.current.info 'Attempting to generate missing file...'
+
+ [SvgGenerator, ImageVariantGenerator].each do |generator|
+ if image = generator.from_url(env['imagery.origin_host'], env['PATH_INFO'] + (env['QUERY_STRING'].empty? ? '' : "?#{env['QUERY_STRING']}"))
+
+ return send_file(image)
+ end
+ end
+
+ Logger.current.info 'No generator available'
+
+ NotFound
+ end
+ end
+end
View
55 lib/imagery/svg_generator.rb
@@ -0,0 +1,55 @@
+require 'RMagick'
+require 'fileutils'
+require 'net/http'
+
+module Imagery
+ # http://localhost:9292/s/files/1/0001/8392/assets/fish.svg
+ #
+ class SvgGenerator
+ SvgFileTest = /\.svg\.png/i
+
+ def self.from_url(server, path)
+ return nil unless path =~ SvgFileTest
+
+ file = Image.new(server, original_path_for(path) )
+ if file.found?
+ file.headers['Content-Type'] = 'image/png'
+ file.content = Converter.new(file.content).svg_to_png
+ file
+ else
+ nil
+ end
+ end
+
+ def self.original_path_for(path)
+ path.gsub(/\.png/, '')
+ end
+
+ class Converter
+ def initialize(blob)
+ @blob = blob
+ end
+
+ def svg_to_png
+ logger.info "** rasterize svg to png"
+ result = popen("rsvg-convert")
+ raise TransformationError, "Data was not a valid SVG image." unless $? == 0
+ result
+ end
+
+ private
+
+ def logger
+ Logger.current
+ end
+
+ def popen(cmd)
+ IO.popen(cmd, 'r+') do |io|
+ io.write @blob
+ io.close_write
+ io.read
+ end
+ end
+ end
+ end
+end
View
23 lib/imagery/transformations.rb
@@ -0,0 +1,23 @@
+require 'RMagick'
+
+module Imagery
+ module Transformations
+ @transformations = Hash.new
+
+ def self.list
+ @transformations.keys
+ end
+
+ def self.[](name)
+ @transformations[name.to_s]
+ end
+
+ def self.register(name, &block)
+ @transformations[name.to_s] = Proc.new(&block)
+ end
+ end
+end
+
+
+
+Dir[ File.dirname(__FILE__) + '/transformations/*.rb'].each { |f| require f }
View
35 lib/imagery/transformations/borders.rb
@@ -0,0 +1,35 @@
+# Creates a
+
+Imagery::Transformations.register :border do |image|
+ # Add Polaroid border
+ image.border!(5, 5, "white")
+end
+
+Imagery::Transformations.register :shadow do |image|
+ shadow = image.flip
+ shadow = shadow.colorize(1, 1, 1, "#ccc")
+ shadow.background_color = "white"
+ shadow.border!(10, 10, "white")
+ shadow = shadow.blur_image(0, 7)
+
+ x = (shadow.columns - image.columns) / 2
+ y = (shadow.rows - image.rows) / 2
+
+ ## Composite original image on top of shadow and save
+ shadow.composite(image, x, y-3, Magick::OverCompositeOp)
+end
+
+Imagery::Transformations.register :polaroid do |image|
+ image.border!(10, 10, "white")
+
+ shadow = image.colorize(1, 1, 1, "#ccc")
+ shadow.background_color = "white"
+ shadow.border!(10, 10, "white")
+ shadow = shadow.blur_image(0, 7)
+
+ x = (shadow.columns - image.columns) / 2
+ y = (shadow.rows - image.rows) / 2
+
+ ## Composite original image on top of shadow and save
+ shadow.composite(image, x, y-3, Magick::OverCompositeOp)
+end
View
16 lib/transformations/sizes.rb → lib/imagery/transformations/sizes.rb
@@ -1,31 +1,31 @@
-Transformations.register :pico do |image|
+Imagery::Transformations.register :pico do |image|
image.change_geometry("16x16>") { |x, y, image| image.resize!(x,y) }
end
-Transformations.register :icon do |image|
+Imagery::Transformations.register :icon do |image|
image.change_geometry("32x32>") { |x, y, image| image.resize!(x,y) }
end
-Transformations.register :thumb do |image|
+Imagery::Transformations.register :thumb do |image|
image.change_geometry("50x50>") { |x, y, image| image.resize!(x,y) }
end
-Transformations.register :small do |image|
+Imagery::Transformations.register :small do |image|
image.change_geometry("100x100>") { |x, y, image| image.resize!(x,y) }
end
-Transformations.register :compact do |image|
+Imagery::Transformations.register :compact do |image|
image.change_geometry("160x160>") { |x, y, image| image.resize!(x,y) }
end
-Transformations.register :medium do |image|
+Imagery::Transformations.register :medium do |image|
image.change_geometry("240x240>") { |x, y, image| image.resize!(x,y) }
end
-Transformations.register :large do |image|
+Imagery::Transformations.register :large do |image|
image.change_geometry("480x480>") { |x, y, image| image.resize!(x,y) }
end
-Transformations.register :grande do |image|
+Imagery::Transformations.register :grande do |image|
image.change_geometry("600x600>") { |x, y, image| image.resize!(x,y) }
end
View
17 lib/transformations/transform.rb → lib/imagery/transformations/transform.rb
@@ -1,34 +1,33 @@
-Transformations.register :square do |image|
+Imagery::Transformations.register :square do |image|
min = [image.columns, image.rows].min
image.crop_resized(min, min, Magick::CenterGravity)
end
-Transformations.register 'max-square' do |image|
+Imagery::Transformations.register 'max-square' do |image|
max = [image.columns, image.rows].max
image.crop_resized(max, max, Magick::CenterGravity)
end
-Transformations.register 'pico-square' do |image|
+Imagery::Transformations.register 'pico-square' do |image|
image.crop_resized(16,16, Magick::CenterGravity)
end
-Transformations.register 'icon-square' do |image|
+Imagery::Transformations.register 'icon-square' do |image|
image.crop_resized(32,32, Magick::CenterGravity)
end
-Transformations.register 'thumb-square' do |image|
+Imagery::Transformations.register 'thumb-square' do |image|
image.crop_resized(50,50, Magick::CenterGravity)
end
-Transformations.register 'medium-square' do |image|
+Imagery::Transformations.register 'medium-square' do |image|
image.crop_resized(240,240, Magick::CenterGravity)
end
-Transformations.register 'small-square' do |image|
+Imagery::Transformations.register 'small-square' do |image|
image.crop_resized(100,100, Magick::CenterGravity)
end
-Transformations.register 'large-square' do |image|
+Imagery::Transformations.register 'large-square' do |image|
image.crop_resized(480,480, Magick::CenterGravity)
end
-
View
0 lib/vendor/SyslogLogger-1.4.0/History.txt → ...ery/vendor/SyslogLogger-1.4.0/History.txt
File renamed without changes.
View
0 lib/vendor/SyslogLogger-1.4.0/Manifest.txt → ...ry/vendor/SyslogLogger-1.4.0/Manifest.txt
File renamed without changes.
View
0 lib/vendor/SyslogLogger-1.4.0/README.txt → ...gery/vendor/SyslogLogger-1.4.0/README.txt
File renamed without changes.
View
0 lib/vendor/SyslogLogger-1.4.0/Rakefile → ...magery/vendor/SyslogLogger-1.4.0/Rakefile
File renamed without changes.
View
0 ...r/SyslogLogger-1.4.0/lib/syslog_logger.rb → ...r/SyslogLogger-1.4.0/lib/syslog_logger.rb
File renamed without changes.
View
0 ...ogLogger-1.4.0/test/test_syslog_logger.rb → ...ogLogger-1.4.0/test/test_syslog_logger.rb
File renamed without changes.
View
40 lib/middleware/accel_redirect.rb
@@ -1,40 +0,0 @@
-require 'rack/file'
-
-class File #:nodoc:
- alias :to_path :path
-end
-
-# To make this work you have to add:
-#
-# location /cache/ {
- # internal;
- # alias /mnt/data/cache/rack/body;
-# }
-#
-# to nginx config
-
-
-class AccelRedirect
- F = ::File
-
- def initialize(app, variation=nil)
- @app = app
- end
-
- def call(env)
- status, headers, body = @app.call(env)
- if body.respond_to?(:to_path)
-
- path = body.to_path
- url = path.sub(/^#{ENV['CACHE_LOCATION']}/i, '/cache')
-
- Logger.current.info " => sending #{url} through nginx"
-
- headers['Content-Length'] = '0'
- headers['X-Accel-Redirect'] = url
- body = []
- end
- [status, headers, body]
- end
-
-end
View
20 lib/middleware/cache_purge.rb
@@ -1,20 +0,0 @@
-
-class CachePurge
- Success = [200, {'Content-Type' => 'text/plain'}, ['OK']]
-
- def initialize(app)
- @app = app
- end
-
- # PURGE /s/files/1/0001/4168/files/thumbs/pic_thumb.jpg?12428536032 HTTP/1.0
-
- def call(env)
- # Rack cache automatically invalidates resource if the verbs are not GET/POST so
- # we don't actually have to do anything. Simply don't delegate those to the backend
- if env['REQUEST_METHOD'] == 'PURGE'
- Success
- else
- @app.call(env)
- end
- end
-end
View
16 lib/middleware/favicon_filter.rb
@@ -1,16 +0,0 @@
-
-class FaviconFilter
- Empty = [200, {'Content-Type' => 'text/plain'}, ['']].freeze
-
- def initialize(app)
- @app = app
- end
-
- def call(env)
- if env['REQUEST_URI'] == '/favicon.ico'
- return Empty
- else
- @app.call(env)
- end
- end
-end
View
31 lib/middleware/logged_request.rb
@@ -1,31 +0,0 @@
-
-class LoggedRequest
-
- def initialize(app)
- @app = app
- end
-
- def call(env)
-
- request = Rack::Cache::Request.new(env)
-
- resp = nil
-
- Logger.current.buffer do
-
- Logger.current.info "#{request.request_method} #{request.path} [#{request.ip}]"
-
- secs = Benchmark.realtime do
- Logger.current.intend do
- resp = @app.call(env)
- end
- end
-
- Logger.current.info((resp[0] < 399 ? 'Success' : "Error [#{resp[0]}]") + " after %.3fs Cache: %s" % [secs, resp[1]['X-Rack-Cache']])
- Logger.current.info ''
-
- end
-
- resp
- end
-end
View
23 lib/middleware/remote_proxy.rb
@@ -1,23 +0,0 @@
-
-class RemoteProxy
- include SendFile
-
- def initialize(app)
- @app = app
- end
-
- def call(env)
- request = Rack::Request.new(env)
-
- requested_file = Image.new(env['imagery.origin_host'], env['PATH_INFO'] + (env['QUERY_STRING'].empty? ? '' : "?#{env['QUERY_STRING']}"))
-
- # If file exists we simply sent it to the client.
- if requested_file.found?
- Logger.current.info "Requested file exists upstream."
-
- send_file(requested_file)
- else
- @app.call(env)
- end
- end
-end
View
12 lib/middleware/server_name.rb
@@ -1,12 +0,0 @@
-
-class ServerName
- def initialize(app)
- @app = app
- end
-
- def call(env)
- hash = @app.call(env)
- hash[1]['Server'] = 'Shopify Imagery'
- hash
- end
-end
View
31 lib/send_file.rb
@@ -1,31 +0,0 @@
-require 'time'
-
-module SendFile
-
- CopyHeaders = ['Content-Type', 'Cache-Control', 'Last-Modified', 'ETag']
-
- ContentTypes = {
- '.gif' => 'image/gif',
- '.jpg' => 'image/jpeg',
- '.jpeg' => 'image/jpeg',
- '.png' => 'image/png',
- '.bmp' => 'image/x-bitmap',
- '.svg' => 'image/svg+xml'
- }
-
- def send_file(file)
- headers = {'Content-Length' => file.content.length.to_s}
-
- if file.respond_to?(:headers)
- CopyHeaders.each do |key|
- headers[key] = file.headers[key] if file.headers.has_key?(key)
- end
- end
-
- headers['ETag'] ||= Digest::MD5.hexdigest(file.content)
- headers['Cache-Control'] ||= 'public, max-age=31557600'
- headers['Last-Modified'] ||= Time.new.httpdate
-
- [200, headers, [file.content]]
- end
-end
View
56 lib/svg_generator.rb
@@ -1,56 +0,0 @@
-require 'RMagick'
-require 'fileutils'
-require 'net/http'
-
-# http://localhost:9292/s/files/1/0001/8392/assets/fish.svg
-#
-class SvgGenerator
- SvgFileTest = /\.svg\.png/i
-
- def self.from_url(server, path)
- return nil unless path =~ SvgFileTest
-
- file = Image.new(server, original_path_for(path) )
- if file.found?
- file.headers['Content-Type'] = 'image/png'
- file.content = Converter.new(file.content).svg_to_png
- file
- else
- nil
- end
- end
-
- def self.original_path_for(path)
- path.gsub(/\.png/, '')
- end
-
- class Converter
- def initialize(blob)
- @blob = blob
- end
-
- def svg_to_png
- logger.info "** rasterize svg to png"
- result = popen("rsvg-convert")
- raise TransformationError, "Data was not a valid SVG image." unless $? == 0
- result
- end
-
- private
-
- def logger
- Logger.current
- end
-
- def popen(cmd)
- IO.popen(cmd, 'r+') do |io|
- io.write @blob
- io.close_write
- io.read
- end
- end
- end
-
-
-end
-
View
21 lib/transformations.rb
@@ -1,21 +0,0 @@
-require 'RMagick'
-
-module Transformations
- @transformations = Hash.new
-
- def self.list
- @transformations.keys
- end
-
- def self.[](name)
- @transformations[name.to_s]
- end
-
- def self.register(name, &block)
- @transformations[name.to_s] = Proc.new(&block)
- end
-end
-
-
-
-Dir[ File.dirname(__FILE__) + '/transformations/*.rb'].each { |f| require f }
View
35 lib/transformations/borders.rb
@@ -1,35 +0,0 @@
-# Creates a
-
-Transformations.register :border do |image|
- # Add Polaroid border
- image.border!(5, 5, "white")
-end
-
-Transformations.register :shadow do |image|
- shadow = image.flip
- shadow = shadow.colorize(1, 1, 1, "#ccc")
- shadow.background_color = "white"
- shadow.border!(10, 10, "white")
- shadow = shadow.blur_image(0, 7)
-
- x = (shadow.columns - image.columns) / 2
- y = (shadow.rows - image.rows) / 2
-
- ## Composite original image on top of shadow and save
- shadow.composite(image, x, y-3, Magick::OverCompositeOp)
-end
-
-Transformations.register :polaroid do |image|
- image.border!(10, 10, "white")
-
- shadow = image.colorize(1, 1, 1, "#ccc")
- shadow.background_color = "white"
- shadow.border!(10, 10, "white")
- shadow = shadow.blur_image(0, 7)
-
- x = (shadow.columns - image.columns) / 2
- y = (shadow.rows - image.rows) / 2
-
- ## Composite original image on top of shadow and save
- shadow.composite(image, x, y-3, Magick::OverCompositeOp)
-end
View
3 test/helper.rb
@@ -1,4 +1,5 @@
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..')
+$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib')
require 'rubygems'
require 'test/unit'
@@ -7,5 +8,5 @@
require 'mocha'
require 'rack'
-require 'image_server'
+require 'imagery'
require 'config/env'
View
12 test/test_image_variant_generator.rb
@@ -19,18 +19,18 @@ def setup
end
def test_successfull_call
- assert ImageVariantGenerator.from_url('static.shopify.com', '/image_pico.png')
- assert ImageVariantGenerator.from_url('static.shopify.com', '/image_small.png')
+ assert Imagery::ImageVariantGenerator.from_url('static.shopify.com', '/image_pico.png')
+ assert Imagery::ImageVariantGenerator.from_url('static.shopify.com', '/image_small.png')
end
def test_return_nil_on_404
- assert_equal nil, ImageVariantGenerator.from_url('static.shopify.com', '/failed_image_pico.png')
+ assert_equal nil, Imagery::ImageVariantGenerator.from_url('static.shopify.com', '/failed_image_pico.png')
end
def test_wrong_filename
- assert_equal nil, ImageVariantGenerator.from_url('static.shopify.com', '/image.png')
- assert_equal nil, ImageVariantGenerator.from_url('static.shopify.com', '/image_whatever.png')
- assert_equal nil, ImageVariantGenerator.from_url('static.shopify.com', '/image.tga')
+ assert_equal nil, Imagery::ImageVariantGenerator.from_url('static.shopify.com', '/image.png')
+ assert_equal nil, Imagery::ImageVariantGenerator.from_url('static.shopify.com', '/image_whatever.png')
+ assert_equal nil, Imagery::ImageVariantGenerator.from_url('static.shopify.com', '/image.tga')
end
end
View
2 test/test_remote_proxy.rb
@@ -27,7 +27,7 @@ def setup
)
- @app = RemoteProxy.new lambda { StandardResponse }
+ @app = Imagery::RemoteProxy.new lambda { StandardResponse }
end
def test_successfull_call
View
8 test/test_svg_generation.rb
@@ -10,14 +10,14 @@ def setup
def test_successfull_call
Patron::Session.any_instance.expects(:get).with('/image.svg').returns( stub(:headers => {}, :body => File.read( File.dirname(__FILE__) + '/assets/fish.svg'), :status => 200))
- assert SvgGenerator.from_url('static.shopify.com', '/image.svg.png')
+ assert Imagery::SvgGenerator.from_url('static.shopify.com', '/image.svg.png')
end
def test_wrong_filename
- assert_equal nil, SvgGenerator.from_url('static.shopify.com', '/image.svg')
- assert_equal nil, SvgGenerator.from_url('static.shopify.com', '/image.png')
- assert_equal nil, SvgGenerator.from_url('static.shopify.com', '/image.svg.bmp')
+ assert_equal nil, Imagery::SvgGenerator.from_url('static.shopify.com', '/image.svg')
+ assert_equal nil, Imagery::SvgGenerator.from_url('static.shopify.com', '/image.png')
+ assert_equal nil, Imagery::SvgGenerator.from_url('static.shopify.com', '/image.svg.bmp')
end
end

0 comments on commit bddd139

Please sign in to comment.
Something went wrong with that request. Please try again.