httpthumbnailer-client 0.1.1 → 1.0.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/Gemfile +10 -7
- data/Gemfile.lock +56 -49
- data/README.md +39 -15
- data/VERSION +1 -1
- data/bin/httpthumbnailer-client +73 -0
- data/httpthumbnailer-client.gemspec +38 -26
- data/lib/httpthumbnailer-client.rb +122 -48
- data/spec/httpthumbnailer-client_spec.rb +9 -138
- data/spec/spec_helper.rb +11 -4
- data/spec/{test-large.jpg → support/test-large.jpg} +0 -0
- data/spec/{test.jpg → support/test.jpg} +0 -0
- data/spec/{test.txt → support/test.txt} +0 -0
- data/spec/thumbnail_spec.rb +66 -0
- data/spec/thumbnails_spec.rb +110 -0
- data/spec/uri_builder_spec.rb +16 -0
- metadata +83 -32
- data/lib/httpthumbnailer-client/multipart_response.rb +0 -45
- data/spec/multipart_response_spec.rb +0 -95
    
        data/Gemfile
    CHANGED
    
    | @@ -1,17 +1,20 @@ | |
| 1 1 | 
             
            source "http://rubygems.org"
         | 
| 2 | 
            +
            ruby "1.9.3"
         | 
| 2 3 |  | 
| 3 | 
            -
            gem "httpclient", ">= 2. | 
| 4 | 
            +
            gem "httpclient", ">= 2.3"
         | 
| 5 | 
            +
            gem "cli", "~> 1.1.0"
         | 
| 6 | 
            +
            gem "multipart-parser", "~> 0.1.1"
         | 
| 4 7 |  | 
| 5 8 | 
             
            # Add dependencies to develop your gem here.
         | 
| 6 9 | 
             
            # Include everything needed to run rake, tests, features, etc.
         | 
| 7 10 | 
             
            group :development do
         | 
| 8 | 
            -
              gem "rspec", "~> 2. | 
| 11 | 
            +
              gem "rspec", "~> 2.13"
         | 
| 12 | 
            +
              gem "rspec-mocks", "~> 2.13"
         | 
| 9 13 | 
             
              gem "cucumber", ">= 0"
         | 
| 10 | 
            -
              gem " | 
| 11 | 
            -
              gem "jeweler", "~> 1.6"
         | 
| 12 | 
            -
              gem "simplecov", ">= 0"
         | 
| 14 | 
            +
              gem "jeweler", "~> 1.8.4"
         | 
| 13 15 | 
             
              gem "rdoc", "~> 3.9"
         | 
| 14 16 | 
             
              gem "daemon", "~> 1"
         | 
| 15 | 
            -
              gem " | 
| 17 | 
            +
              gem "rmagick", "~> 2"
         | 
| 18 | 
            +
              gem "httpthumbnailer", path: '../httpthumbnailer'
         | 
| 19 | 
            +
              gem "unicorn-cuba-base", path: '../unicorn-cuba-base'
         | 
| 16 20 | 
             
            end
         | 
| 17 | 
            -
             | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,75 +1,82 @@ | |
| 1 | 
            +
            PATH
         | 
| 2 | 
            +
              remote: ../httpthumbnailer
         | 
| 3 | 
            +
              specs:
         | 
| 4 | 
            +
                httpthumbnailer (1.0.0)
         | 
| 5 | 
            +
                  rmagick (~> 2)
         | 
| 6 | 
            +
                  unicorn-cuba-base (~> 1.0)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            PATH
         | 
| 9 | 
            +
              remote: ../unicorn-cuba-base
         | 
| 10 | 
            +
              specs:
         | 
| 11 | 
            +
                unicorn-cuba-base (1.0.0)
         | 
| 12 | 
            +
                  cli (~> 1.1.0)
         | 
| 13 | 
            +
                  cuba (~> 3.0)
         | 
| 14 | 
            +
                  facter (~> 1.6.11)
         | 
| 15 | 
            +
                  raindrops (~> 0.11)
         | 
| 16 | 
            +
                  ruby-ip (~> 0.9)
         | 
| 17 | 
            +
                  unicorn (>= 4.6.2)
         | 
| 18 | 
            +
             | 
| 1 19 | 
             
            GEM
         | 
| 2 20 | 
             
              remote: http://rubygems.org/
         | 
| 3 21 | 
             
              specs:
         | 
| 4 22 | 
             
                builder (3.0.0)
         | 
| 5 23 | 
             
                cli (1.1.1)
         | 
| 6 | 
            -
                 | 
| 24 | 
            +
                cuba (3.1.0)
         | 
| 25 | 
            +
                  rack
         | 
| 26 | 
            +
                cucumber (1.1.3)
         | 
| 7 27 | 
             
                  builder (>= 2.1.2)
         | 
| 8 | 
            -
                  diff-lcs (>= 1.1. | 
| 9 | 
            -
                  gherkin (~> 2. | 
| 28 | 
            +
                  diff-lcs (>= 1.1.2)
         | 
| 29 | 
            +
                  gherkin (~> 2.6.7)
         | 
| 10 30 | 
             
                  json (>= 1.4.6)
         | 
| 31 | 
            +
                  term-ansicolor (>= 1.0.6)
         | 
| 11 32 | 
             
                daemon (1.1.0)
         | 
| 12 | 
            -
                daemons (1.0.10)
         | 
| 13 33 | 
             
                diff-lcs (1.1.3)
         | 
| 14 | 
            -
                 | 
| 15 | 
            -
                gherkin (2. | 
| 34 | 
            +
                facter (1.6.18)
         | 
| 35 | 
            +
                gherkin (2.6.7)
         | 
| 16 36 | 
             
                  json (>= 1.4.6)
         | 
| 17 37 | 
             
                git (1.2.5)
         | 
| 18 | 
            -
                 | 
| 19 | 
            -
                httpclient (2.2.7)
         | 
| 20 | 
            -
                httpthumbnailer (0.3.1)
         | 
| 21 | 
            -
                  cli (~> 1.1.0)
         | 
| 22 | 
            -
                  haml (~> 3)
         | 
| 23 | 
            -
                  mongrel (>= 1.2.0.pre2)
         | 
| 24 | 
            -
                  rmagick (~> 2)
         | 
| 25 | 
            -
                  ruby-ip (~> 0.9)
         | 
| 26 | 
            -
                  sinatra (>= 1.2.6)
         | 
| 38 | 
            +
                httpclient (2.3.3)
         | 
| 27 39 | 
             
                jeweler (1.8.4)
         | 
| 28 40 | 
             
                  bundler (~> 1.0)
         | 
| 29 41 | 
             
                  git (>= 1.2.5)
         | 
| 30 42 | 
             
                  rake
         | 
| 31 43 | 
             
                  rdoc
         | 
| 32 | 
            -
                json (1. | 
| 33 | 
            -
                 | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
                 | 
| 37 | 
            -
                 | 
| 38 | 
            -
                 | 
| 39 | 
            -
                  rack
         | 
| 40 | 
            -
                rake (0.9.2.2)
         | 
| 41 | 
            -
                rdoc (3.12)
         | 
| 42 | 
            -
                  json (~> 1.4)
         | 
| 44 | 
            +
                json (1.6.1)
         | 
| 45 | 
            +
                kgio (2.8.0)
         | 
| 46 | 
            +
                multipart-parser (0.1.1)
         | 
| 47 | 
            +
                rack (1.5.2)
         | 
| 48 | 
            +
                raindrops (0.11.0)
         | 
| 49 | 
            +
                rake (10.0.4)
         | 
| 50 | 
            +
                rdoc (3.9.4)
         | 
| 43 51 | 
             
                rmagick (2.13.1)
         | 
| 44 | 
            -
                rspec (2. | 
| 45 | 
            -
                  rspec-core (~> 2. | 
| 46 | 
            -
                  rspec-expectations (~> 2. | 
| 47 | 
            -
                  rspec-mocks (~> 2. | 
| 48 | 
            -
                rspec-core (2. | 
| 49 | 
            -
                rspec-expectations (2. | 
| 50 | 
            -
                  diff-lcs ( | 
| 51 | 
            -
                rspec-mocks (2. | 
| 52 | 
            +
                rspec (2.13.0)
         | 
| 53 | 
            +
                  rspec-core (~> 2.13.0)
         | 
| 54 | 
            +
                  rspec-expectations (~> 2.13.0)
         | 
| 55 | 
            +
                  rspec-mocks (~> 2.13.0)
         | 
| 56 | 
            +
                rspec-core (2.13.1)
         | 
| 57 | 
            +
                rspec-expectations (2.13.0)
         | 
| 58 | 
            +
                  diff-lcs (>= 1.1.3, < 2.0)
         | 
| 59 | 
            +
                rspec-mocks (2.13.1)
         | 
| 52 60 | 
             
                ruby-ip (0.9.1)
         | 
| 53 | 
            -
                 | 
| 54 | 
            -
             | 
| 55 | 
            -
                   | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
                  rack (~> 1.3, >= 1.3.6)
         | 
| 59 | 
            -
                  rack-protection (~> 1.2)
         | 
| 60 | 
            -
                  tilt (~> 1.3, >= 1.3.3)
         | 
| 61 | 
            -
                tilt (1.3.3)
         | 
| 61 | 
            +
                term-ansicolor (1.0.7)
         | 
| 62 | 
            +
                unicorn (4.6.3)
         | 
| 63 | 
            +
                  kgio (~> 2.6)
         | 
| 64 | 
            +
                  rack
         | 
| 65 | 
            +
                  raindrops (~> 0.7)
         | 
| 62 66 |  | 
| 63 67 | 
             
            PLATFORMS
         | 
| 64 68 | 
             
              ruby
         | 
| 65 69 |  | 
| 66 70 | 
             
            DEPENDENCIES
         | 
| 67 | 
            -
               | 
| 71 | 
            +
              cli (~> 1.1.0)
         | 
| 68 72 | 
             
              cucumber
         | 
| 69 73 | 
             
              daemon (~> 1)
         | 
| 70 | 
            -
              httpclient (>= 2. | 
| 71 | 
            -
              httpthumbnailer | 
| 72 | 
            -
              jeweler (~> 1. | 
| 74 | 
            +
              httpclient (>= 2.3)
         | 
| 75 | 
            +
              httpthumbnailer!
         | 
| 76 | 
            +
              jeweler (~> 1.8.4)
         | 
| 77 | 
            +
              multipart-parser (~> 0.1.1)
         | 
| 73 78 | 
             
              rdoc (~> 3.9)
         | 
| 74 | 
            -
               | 
| 75 | 
            -
               | 
| 79 | 
            +
              rmagick (~> 2)
         | 
| 80 | 
            +
              rspec (~> 2.13)
         | 
| 81 | 
            +
              rspec-mocks (~> 2.13)
         | 
| 82 | 
            +
              unicorn-cuba-base!
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,44 +1,68 @@ | |
| 1 | 
            -
            #  | 
| 1 | 
            +
            # HTTP Thubnailer Client
         | 
| 2 2 |  | 
| 3 3 | 
             
            Ruby client to [httpthumbnailer](http://github.com/jpastuszek/httpthumbnailer) image scaling and conversion HTTP API server.
         | 
| 4 4 |  | 
| 5 5 | 
             
            ## Installing
         | 
| 6 6 |  | 
| 7 | 
            -
             | 
| 7 | 
            +
            ```bash
         | 
| 8 | 
            +
            gem install httpthumbnailer-client
         | 
| 9 | 
            +
            ```
         | 
| 8 10 |  | 
| 9 11 | 
             
            ## Usage
         | 
| 10 12 |  | 
| 11 | 
            -
             | 
| 13 | 
            +
            ### Ruby API
         | 
| 12 14 |  | 
| 13 15 | 
             
            ```ruby
         | 
| 14 16 | 
             
            require 'httpthumbnailer-client'
         | 
| 15 17 |  | 
| 16 | 
            -
            # read  | 
| 18 | 
            +
            # read original image data (may be any format supported by ImageMagick/GraphicsMagick installation on the server)
         | 
| 17 19 | 
             
            data = File.read('image_file.jpg')
         | 
| 18 20 |  | 
| 19 | 
            -
            #  | 
| 21 | 
            +
            # with API server listening on localhost port 3100
         | 
| 22 | 
            +
            # see the API server documentation for available operations, formats and options
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            # generate single thumbnail from image data (single thumbnail API)
         | 
| 25 | 
            +
            thumbnail = HTTPThumbnailerClient.new('http://localhost:3100').thumbnail(data, 'crop', 60, 30, 'jpeg')
         | 
| 26 | 
            +
            thumbnail.mime_type 	# => 'image/jpeg'
         | 
| 27 | 
            +
            thumbnail.data 			# => 60x30 thumbnail JPEG data String
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            # generate set of thumbnails from image data (multipart API)
         | 
| 20 30 | 
             
            thumbnails = HTTPThumbnailerClient.new('http://localhost:3100').thumbnail(data) do
         | 
| 21 | 
            -
            	 | 
| 22 | 
            -
            	thumbnail 'crop',  | 
| 23 | 
            -
            	thumbnail ' | 
| 24 | 
            -
            	thumbnail 'pad', 4, 4, 'PNG'
         | 
| 31 | 
            +
            	thumbnail 'crop', 60, 30, 'jpeg' 
         | 
| 32 | 
            +
            	thumbnail 'crop', 80, 80, 'png'
         | 
| 33 | 
            +
            	thumbnail 'pad', 40, 40, 'png'
         | 
| 25 34 | 
             
            end
         | 
| 26 35 |  | 
| 27 36 | 
             
            thumbnails[0].mime_type 	# => 'image/jpeg'
         | 
| 28 | 
            -
            thumbnails[0].data 			# =>  | 
| 37 | 
            +
            thumbnails[0].data 			# => 60x30 thumbnail JPEG data String
         | 
| 29 38 |  | 
| 30 39 | 
             
            thumbnails[1].mime_type 	# => 'image/png'
         | 
| 31 | 
            -
            thumbnails[1].data 			# =>  | 
| 40 | 
            +
            thumbnails[1].data 			# => 80x80 thumbnail PNG data String
         | 
| 32 41 |  | 
| 33 42 | 
             
            thumbnails[2].mime_type		# => 'image/png'
         | 
| 34 | 
            -
            thumbnails[2].data			# =>  | 
| 43 | 
            +
            thumbnails[2].data			# => 40x40 thumbnail PNG data String
         | 
| 35 44 |  | 
| 36 45 | 
             
            thumbnails.input_mime_type	# => 'image/jpeg' - detected input image format by API server (content based)
         | 
| 37 46 | 
             
            ```
         | 
| 38 47 |  | 
| 39 | 
            -
            For more details see  | 
| 48 | 
            +
            For more details see RSpec for [single thumbnail API](http://github.com/jpastuszek/httpthumbnailer-client/blob/master/spec/thumbnail_spec.rb) and [multipart API](http://github.com/jpastuszek/httpthumbnailer-client/blob/master/spec/thumbnails_spec.rb).
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            ### CLI tool
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            This gem provides `httpthumbnailer-client` command line tool that can be used to thumbnail images via [httpthumbnailer](http://github.com/jpastuszek/httpthumbnailer).
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            ```bash
         | 
| 55 | 
            +
            # start thumbnailing server (to stop: kill `cat httpthumbnailer.pid`)
         | 
| 56 | 
            +
            httpthumbnailer
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            # thumbnail to standard output
         | 
| 59 | 
            +
            cat ~/Pictures/compute.jpg | httpthumbnailer-client -t crop,100,200,png > thumbnail.png
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            # generate multiple thumbnails
         | 
| 62 | 
            +
            cat ~/Pictures/compute.jpg | httpthumbnailer-client -t crop,100,200,jpeg,quality:100 -t pad,200,200,png thumbnail1.jpg thumbnail2.png
         | 
| 63 | 
            +
            ```
         | 
| 40 64 |  | 
| 41 | 
            -
            ## Contributing to  | 
| 65 | 
            +
            ## Contributing to HTTP Thubnailer Client
         | 
| 42 66 |  | 
| 43 67 | 
             
            * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
         | 
| 44 68 | 
             
            * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
         | 
| @@ -50,6 +74,6 @@ For more details see [RSpec tests](http://github.com/jpastuszek/httpthumbnailer- | |
| 50 74 |  | 
| 51 75 | 
             
            ## Copyright
         | 
| 52 76 |  | 
| 53 | 
            -
            Copyright (c)  | 
| 77 | 
            +
            Copyright (c) 2013 Jakub Pastuszek. See LICENSE.txt for
         | 
| 54 78 | 
             
            further details.
         | 
| 55 79 |  | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0. | 
| 1 | 
            +
            1.0.0
         | 
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            require 'cli'
         | 
| 3 | 
            +
            require 'pathname'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 6 | 
            +
            require 'httpthumbnailer-client'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            def parse_spec(spec)
         | 
| 9 | 
            +
            	method, width, height, format, *options = *spec.split(',')
         | 
| 10 | 
            +
            	fail "missing thumbnail specification argument in: #{spec}" unless method and width and height and format
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            	opts = {}
         | 
| 13 | 
            +
            	options.each do |option|
         | 
| 14 | 
            +
            	key, value = option.split(':')
         | 
| 15 | 
            +
            		fail "missing option key or value in specifiaction: #{spec}" unless key and value
         | 
| 16 | 
            +
            	opts[key] = value
         | 
| 17 | 
            +
            	end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            	[method, width, height, format, opts]
         | 
| 20 | 
            +
            end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            settings = CLI.new do
         | 
| 23 | 
            +
            	description 'Client to httpthumbnailer image scaling and conversion HTTP API server'
         | 
| 24 | 
            +
            	stdin :image_data,
         | 
| 25 | 
            +
            		description: 'image to be thumbnailed'
         | 
| 26 | 
            +
            	option :host,
         | 
| 27 | 
            +
            		short: :H,
         | 
| 28 | 
            +
            		description: 'host on which the httpthumbnailer is running',
         | 
| 29 | 
            +
            		default: 'localhost'
         | 
| 30 | 
            +
            	option :port,
         | 
| 31 | 
            +
            		short: :p,
         | 
| 32 | 
            +
            		description: 'TCP port on which the httpthumbnailer is listening',
         | 
| 33 | 
            +
            		cast: Integer,
         | 
| 34 | 
            +
            		default: 3100
         | 
| 35 | 
            +
            	options :thumbnail,
         | 
| 36 | 
            +
            		short: :t,
         | 
| 37 | 
            +
            		description: 'thumbnail specification in format <operation type>,<width>,<height>,<format>[,<option key>:<option value>]* - see httpthumbnailer documenation for more information',
         | 
| 38 | 
            +
            		required: true
         | 
| 39 | 
            +
            	arguments :output_file_name,
         | 
| 40 | 
            +
            		short: :f,
         | 
| 41 | 
            +
            		description: 'file name to store resulting thumbnail; when multiple --thumbail options are used the same number of output file names should be specified; use "-" to write to STDOUT',
         | 
| 42 | 
            +
            		default: '-'
         | 
| 43 | 
            +
            	version (Pathname.new(__FILE__).dirname + '..' + 'VERSION').read
         | 
| 44 | 
            +
            end.parse! do |settings|
         | 
| 45 | 
            +
            	STDERR.puts "Warning: there is more thumbnail specifications given than output file names; some thumbnails will not be saved" if settings.thumbnail.length > settings.output_file_name.length
         | 
| 46 | 
            +
            	fail 'only one thumbnail can be written to STDOUT' if settings.output_file_name.count('-') > 1
         | 
| 47 | 
            +
            	settings.thumbnail = settings.thumbnail.map{|spec| parse_spec(spec)}
         | 
| 48 | 
            +
            end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            thumbnailer = HTTPThumbnailerClient.new("http://#{settings.host}:#{settings.port}")
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            thumbnails = if settings.thumbnail.length == 1
         | 
| 53 | 
            +
            	[thumbnailer.thumbnail(settings.stdin.read, *settings.thumbnail.first)]
         | 
| 54 | 
            +
            else
         | 
| 55 | 
            +
            	thumbnailer.thumbnail(settings.stdin.read) do
         | 
| 56 | 
            +
            		settings.thumbnail.each do |spec|
         | 
| 57 | 
            +
            			thumbnail *spec
         | 
| 58 | 
            +
            		end
         | 
| 59 | 
            +
            	end
         | 
| 60 | 
            +
            end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            thumbnails.zip(settings.output_file_name).select do |thumbnail, out_file| 
         | 
| 63 | 
            +
            	out_file 
         | 
| 64 | 
            +
            end.each do |thumbnail, out_file|
         | 
| 65 | 
            +
            	if out_file == '-'
         | 
| 66 | 
            +
            		STDOUT.write thumbnail.data
         | 
| 67 | 
            +
            	else
         | 
| 68 | 
            +
            		File.open(out_file, 'w') do |file|
         | 
| 69 | 
            +
            			file.write thumbnail.data
         | 
| 70 | 
            +
            		end
         | 
| 71 | 
            +
            	end
         | 
| 72 | 
            +
            end
         | 
| 73 | 
            +
             | 
| @@ -5,13 +5,14 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |s|
         | 
| 7 7 | 
             
              s.name = "httpthumbnailer-client"
         | 
| 8 | 
            -
              s.version = "0. | 
| 8 | 
            +
              s.version = "1.0.0"
         | 
| 9 9 |  | 
| 10 10 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 11 11 | 
             
              s.authors = ["Jakub Pastuszek"]
         | 
| 12 | 
            -
              s.date = " | 
| 12 | 
            +
              s.date = "2013-07-16"
         | 
| 13 13 | 
             
              s.description = "Thumbnails images using httpthumbniler server"
         | 
| 14 14 | 
             
              s.email = "jpastuszek@gmail.com"
         | 
| 15 | 
            +
              s.executables = ["httpthumbnailer-client"]
         | 
| 15 16 | 
             
              s.extra_rdoc_files = [
         | 
| 16 17 | 
             
                "LICENSE.txt",
         | 
| 17 18 | 
             
                "README.md"
         | 
| @@ -25,58 +26,69 @@ Gem::Specification.new do |s| | |
| 25 26 | 
             
                "README.md",
         | 
| 26 27 | 
             
                "Rakefile",
         | 
| 27 28 | 
             
                "VERSION",
         | 
| 29 | 
            +
                "bin/httpthumbnailer-client",
         | 
| 28 30 | 
             
                "features/step_definitions/httpthumbnailer-client_steps.rb",
         | 
| 29 31 | 
             
                "features/support/env.rb",
         | 
| 30 32 | 
             
                "httpthumbnailer-client.gemspec",
         | 
| 31 33 | 
             
                "lib/httpthumbnailer-client.rb",
         | 
| 32 | 
            -
                "lib/httpthumbnailer-client/multipart_response.rb",
         | 
| 33 34 | 
             
                "spec/httpthumbnailer-client_spec.rb",
         | 
| 34 | 
            -
                "spec/multipart_response_spec.rb",
         | 
| 35 35 | 
             
                "spec/spec_helper.rb",
         | 
| 36 | 
            -
                "spec/test-large.jpg",
         | 
| 37 | 
            -
                "spec/test.jpg",
         | 
| 38 | 
            -
                "spec/test.txt"
         | 
| 36 | 
            +
                "spec/support/test-large.jpg",
         | 
| 37 | 
            +
                "spec/support/test.jpg",
         | 
| 38 | 
            +
                "spec/support/test.txt",
         | 
| 39 | 
            +
                "spec/thumbnail_spec.rb",
         | 
| 40 | 
            +
                "spec/thumbnails_spec.rb",
         | 
| 41 | 
            +
                "spec/uri_builder_spec.rb"
         | 
| 39 42 | 
             
              ]
         | 
| 40 43 | 
             
              s.homepage = "http://github.com/jpastuszek/httpthumbnailer-client"
         | 
| 41 44 | 
             
              s.licenses = ["MIT"]
         | 
| 42 45 | 
             
              s.require_paths = ["lib"]
         | 
| 43 | 
            -
              s.rubygems_version = "1.8. | 
| 46 | 
            +
              s.rubygems_version = "1.8.25"
         | 
| 44 47 | 
             
              s.summary = "API client for httpthumbniler server"
         | 
| 45 48 |  | 
| 46 49 | 
             
              if s.respond_to? :specification_version then
         | 
| 47 50 | 
             
                s.specification_version = 3
         | 
| 48 51 |  | 
| 49 52 | 
             
                if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
         | 
| 50 | 
            -
                  s.add_runtime_dependency(%q<httpclient>, [">= 2. | 
| 51 | 
            -
                  s. | 
| 53 | 
            +
                  s.add_runtime_dependency(%q<httpclient>, [">= 2.3"])
         | 
| 54 | 
            +
                  s.add_runtime_dependency(%q<cli>, ["~> 1.1.0"])
         | 
| 55 | 
            +
                  s.add_runtime_dependency(%q<multipart-parser>, ["~> 0.1.1"])
         | 
| 56 | 
            +
                  s.add_development_dependency(%q<rspec>, ["~> 2.13"])
         | 
| 57 | 
            +
                  s.add_development_dependency(%q<rspec-mocks>, ["~> 2.13"])
         | 
| 52 58 | 
             
                  s.add_development_dependency(%q<cucumber>, [">= 0"])
         | 
| 53 | 
            -
                  s.add_development_dependency(%q< | 
| 54 | 
            -
                  s.add_development_dependency(%q<jeweler>, ["~> 1.6"])
         | 
| 55 | 
            -
                  s.add_development_dependency(%q<simplecov>, [">= 0"])
         | 
| 59 | 
            +
                  s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
         | 
| 56 60 | 
             
                  s.add_development_dependency(%q<rdoc>, ["~> 3.9"])
         | 
| 57 61 | 
             
                  s.add_development_dependency(%q<daemon>, ["~> 1"])
         | 
| 58 | 
            -
                  s.add_development_dependency(%q< | 
| 62 | 
            +
                  s.add_development_dependency(%q<rmagick>, ["~> 2"])
         | 
| 63 | 
            +
                  s.add_development_dependency(%q<httpthumbnailer>, [">= 0"])
         | 
| 64 | 
            +
                  s.add_development_dependency(%q<unicorn-cuba-base>, [">= 0"])
         | 
| 59 65 | 
             
                else
         | 
| 60 | 
            -
                  s.add_dependency(%q<httpclient>, [">= 2. | 
| 61 | 
            -
                  s.add_dependency(%q< | 
| 66 | 
            +
                  s.add_dependency(%q<httpclient>, [">= 2.3"])
         | 
| 67 | 
            +
                  s.add_dependency(%q<cli>, ["~> 1.1.0"])
         | 
| 68 | 
            +
                  s.add_dependency(%q<multipart-parser>, ["~> 0.1.1"])
         | 
| 69 | 
            +
                  s.add_dependency(%q<rspec>, ["~> 2.13"])
         | 
| 70 | 
            +
                  s.add_dependency(%q<rspec-mocks>, ["~> 2.13"])
         | 
| 62 71 | 
             
                  s.add_dependency(%q<cucumber>, [">= 0"])
         | 
| 63 | 
            -
                  s.add_dependency(%q< | 
| 64 | 
            -
                  s.add_dependency(%q<jeweler>, ["~> 1.6"])
         | 
| 65 | 
            -
                  s.add_dependency(%q<simplecov>, [">= 0"])
         | 
| 72 | 
            +
                  s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
         | 
| 66 73 | 
             
                  s.add_dependency(%q<rdoc>, ["~> 3.9"])
         | 
| 67 74 | 
             
                  s.add_dependency(%q<daemon>, ["~> 1"])
         | 
| 68 | 
            -
                  s.add_dependency(%q< | 
| 75 | 
            +
                  s.add_dependency(%q<rmagick>, ["~> 2"])
         | 
| 76 | 
            +
                  s.add_dependency(%q<httpthumbnailer>, [">= 0"])
         | 
| 77 | 
            +
                  s.add_dependency(%q<unicorn-cuba-base>, [">= 0"])
         | 
| 69 78 | 
             
                end
         | 
| 70 79 | 
             
              else
         | 
| 71 | 
            -
                s.add_dependency(%q<httpclient>, [">= 2. | 
| 72 | 
            -
                s.add_dependency(%q< | 
| 80 | 
            +
                s.add_dependency(%q<httpclient>, [">= 2.3"])
         | 
| 81 | 
            +
                s.add_dependency(%q<cli>, ["~> 1.1.0"])
         | 
| 82 | 
            +
                s.add_dependency(%q<multipart-parser>, ["~> 0.1.1"])
         | 
| 83 | 
            +
                s.add_dependency(%q<rspec>, ["~> 2.13"])
         | 
| 84 | 
            +
                s.add_dependency(%q<rspec-mocks>, ["~> 2.13"])
         | 
| 73 85 | 
             
                s.add_dependency(%q<cucumber>, [">= 0"])
         | 
| 74 | 
            -
                s.add_dependency(%q< | 
| 75 | 
            -
                s.add_dependency(%q<jeweler>, ["~> 1.6"])
         | 
| 76 | 
            -
                s.add_dependency(%q<simplecov>, [">= 0"])
         | 
| 86 | 
            +
                s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
         | 
| 77 87 | 
             
                s.add_dependency(%q<rdoc>, ["~> 3.9"])
         | 
| 78 88 | 
             
                s.add_dependency(%q<daemon>, ["~> 1"])
         | 
| 79 | 
            -
                s.add_dependency(%q< | 
| 89 | 
            +
                s.add_dependency(%q<rmagick>, ["~> 2"])
         | 
| 90 | 
            +
                s.add_dependency(%q<httpthumbnailer>, [">= 0"])
         | 
| 91 | 
            +
                s.add_dependency(%q<unicorn-cuba-base>, [">= 0"])
         | 
| 80 92 | 
             
              end
         | 
| 81 93 | 
             
            end
         | 
| 82 94 |  | 
| @@ -1,47 +1,98 @@ | |
| 1 | 
            -
            $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 2 1 | 
             
            require 'httpclient'
         | 
| 3 | 
            -
            require ' | 
| 2 | 
            +
            require 'multipart_parser/reader'
         | 
| 4 3 |  | 
| 5 4 | 
             
            class HTTPThumbnailerClient
         | 
| 6 | 
            -
            	 | 
| 7 | 
            -
             | 
| 5 | 
            +
            	HTTPThumbnailerClientError = Class.new(ArgumentError)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            	InvalidThumbnailSpecificationError = Class.new(HTTPThumbnailerClientError)
         | 
| 8 | 
            +
            	ServerResourceNotFoundError = Class.new(HTTPThumbnailerClientError)
         | 
| 9 | 
            +
            	UnsupportedMediaTypeError = Class.new(HTTPThumbnailerClientError)
         | 
| 10 | 
            +
            	ImageTooLargeError = Class.new(HTTPThumbnailerClientError)
         | 
| 11 | 
            +
            	RemoteServerError = Class.new(HTTPThumbnailerClientError)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            	@@errors = {}
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            	module Status
         | 
| 16 | 
            +
            		def status=(st)
         | 
| 17 | 
            +
            			@status = st
         | 
| 18 | 
            +
            		end
         | 
| 8 19 |  | 
| 9 | 
            -
             | 
| 20 | 
            +
            		def status
         | 
| 21 | 
            +
            			@status
         | 
| 22 | 
            +
            		end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            		module Instance
         | 
| 25 | 
            +
            			def status
         | 
| 26 | 
            +
            				defined?(self.class.status) ? self.class.status : 500
         | 
| 27 | 
            +
            			end
         | 
| 28 | 
            +
            		end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            		def self.extended(klass)
         | 
| 31 | 
            +
            			klass.instance_eval do
         | 
| 32 | 
            +
            				include Instance
         | 
| 33 | 
            +
            			end
         | 
| 34 | 
            +
            		end
         | 
| 10 35 | 
             
            	end
         | 
| 11 36 |  | 
| 12 | 
            -
            	 | 
| 37 | 
            +
            	def self.assign_status_code(error, status)
         | 
| 38 | 
            +
            		error.extend Status
         | 
| 39 | 
            +
            		error.status = status
         | 
| 40 | 
            +
            		@@errors[status] = error
         | 
| 41 | 
            +
            		@@errors[error] = status
         | 
| 13 42 | 
             
            	end
         | 
| 14 43 |  | 
| 15 | 
            -
            	 | 
| 44 | 
            +
            	assign_status_code(InvalidThumbnailSpecificationError, 400)
         | 
| 45 | 
            +
            	assign_status_code(ServerResourceNotFoundError, 404)
         | 
| 46 | 
            +
            	assign_status_code(UnsupportedMediaTypeError, 415)
         | 
| 47 | 
            +
            	assign_status_code(ImageTooLargeError, 413)
         | 
| 48 | 
            +
            	assign_status_code(RemoteServerError, 500)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            	def error_for_status(status, body)
         | 
| 51 | 
            +
            		@@errors.fetch(status){|err| RemoteServerError}.new(body)
         | 
| 16 52 | 
             
            	end
         | 
| 17 53 |  | 
| 54 | 
            +
            	UnknownResponseType = Class.new HTTPThumbnailerClientError
         | 
| 55 | 
            +
            	InvalidMultipartResponseError = Class.new HTTPThumbnailerClientError
         | 
| 56 | 
            +
             | 
| 18 57 | 
             
            	class URIBuilder
         | 
| 19 58 | 
             
            		def initialize(service_uri, &block)
         | 
| 20 59 | 
             
            			@specs = []
         | 
| 21 60 | 
             
            			@service_uri = service_uri
         | 
| 22 | 
            -
            			instance_eval &block
         | 
| 61 | 
            +
            			instance_eval &block if block
         | 
| 23 62 | 
             
            		end
         | 
| 24 63 |  | 
| 25 64 | 
             
            		def get
         | 
| 26 65 | 
             
            			"#{@service_uri}/#{@specs.join('/')}"
         | 
| 27 66 | 
             
            		end
         | 
| 28 67 |  | 
| 29 | 
            -
            		def self.thumbnail( | 
| 30 | 
            -
            			self.new('/thumbnail' | 
| 68 | 
            +
            		def self.thumbnail(*spec)
         | 
| 69 | 
            +
            			self.new('/thumbnail').thumbnail(*spec).get
         | 
| 70 | 
            +
            		end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            		def self.thumbnails(&block)
         | 
| 73 | 
            +
            			self.new('/thumbnails', &block).get
         | 
| 31 74 | 
             
            		end
         | 
| 32 75 |  | 
| 33 | 
            -
            		def thumbnail(method, width, height, format = ' | 
| 76 | 
            +
            		def thumbnail(method, width, height, format = 'jpeg', options = {})
         | 
| 77 | 
            +
            			width = width.to_s
         | 
| 78 | 
            +
            			height = height.to_s
         | 
| 79 | 
            +
             | 
| 34 80 | 
             
            			args = []
         | 
| 35 81 | 
             
            			args << method.to_s
         | 
| 36 | 
            -
            			 | 
| 37 | 
            -
            			args <<  | 
| 82 | 
            +
            			width !~ /^([0-9]+|input)$/ and raise InvalidThumbnailSpecificationError.new("bad dimension value: #{width}")
         | 
| 83 | 
            +
            			args << width
         | 
| 84 | 
            +
            			height !~ /^([0-9]+|input)$/ and raise InvalidThumbnailSpecificationError.new("bad dimension value: #{height}")
         | 
| 85 | 
            +
            			args << height
         | 
| 38 86 | 
             
            			args << format.to_s
         | 
| 39 87 |  | 
| 40 88 | 
             
            			options.keys.sort{|a, b| a.to_s <=> b.to_s}.each do |key|
         | 
| 89 | 
            +
            				raise InvalidThumbnailSpecificationError.new("empty option key for value '#{options[key]}'") if key.nil? || key.to_s.empty? 
         | 
| 90 | 
            +
            				raise InvalidThumbnailSpecificationError.new("missing option value for key '#{key}'") if options[key].nil? || options[key].to_s.empty? 
         | 
| 41 91 | 
             
            				args << "#{key}:#{options[key]}"
         | 
| 42 92 | 
             
            			end
         | 
| 43 93 |  | 
| 44 94 | 
             
            			@specs << args.join(',')
         | 
| 95 | 
            +
            			self
         | 
| 45 96 | 
             
            		end
         | 
| 46 97 | 
             
            	end
         | 
| 47 98 |  | 
| @@ -58,64 +109,87 @@ class HTTPThumbnailerClient | |
| 58 109 | 
             
            		attr_accessor :input_mime_type
         | 
| 59 110 | 
             
            	end
         | 
| 60 111 |  | 
| 61 | 
            -
            	class ThumbnailingError
         | 
| 62 | 
            -
            		def initialize(msg)
         | 
| 63 | 
            -
            			@message = msg
         | 
| 64 | 
            -
            			@type = msg.match(/Error: (.*?): /)[1] rescue NoMethodError
         | 
| 65 | 
            -
            		end
         | 
| 66 | 
            -
             | 
| 67 | 
            -
            		attr_reader :type, :message
         | 
| 68 | 
            -
            	end
         | 
| 69 | 
            -
             | 
| 70 112 | 
             
            	def initialize(server_url, options = {})
         | 
| 71 113 | 
             
            		@server_url = server_url
         | 
| 72 114 | 
             
            		@client = HTTPClient.new
         | 
| 115 | 
            +
             | 
| 116 | 
            +
            		# long timeouts for big image data
         | 
| 117 | 
            +
            		@client.send_timeout = options[:send_timeout] || 300
         | 
| 118 | 
            +
            		@client.receive_timeout = options[:receive_timeout] || 300
         | 
| 119 | 
            +
             | 
| 120 | 
            +
            		# don't use keep alive by default since backend won't support it any way unless fronted with nginx or similar
         | 
| 73 121 | 
             
            		@keep_alive = options[:keep_alive] || false
         | 
| 74 122 | 
             
            	end
         | 
| 75 123 |  | 
| 76 | 
            -
            	 | 
| 77 | 
            -
             | 
| 124 | 
            +
            	attr_reader :server_url
         | 
| 125 | 
            +
            	attr_reader :keep_alive
         | 
| 126 | 
            +
             | 
| 127 | 
            +
            	def thumbnail(data, *spec, &block)
         | 
| 128 | 
            +
            		uri = if block
         | 
| 129 | 
            +
            			URIBuilder.thumbnails(&block)
         | 
| 130 | 
            +
            		else
         | 
| 131 | 
            +
            			URIBuilder.thumbnail(*spec)
         | 
| 132 | 
            +
            		end
         | 
| 78 133 |  | 
| 79 134 | 
             
            		response = @client.request('PUT', "#{@server_url}#{uri}", nil, data, {'Content-Type' => 'image/autodetect'})
         | 
| 80 135 | 
             
            		@client.reset_all unless @keep_alive
         | 
| 81 136 |  | 
| 82 137 | 
             
            		content_type = response.header['Content-Type'].last
         | 
| 83 138 |  | 
| 84 | 
            -
            		case content_type
         | 
| 139 | 
            +
            		thumbnails = case content_type
         | 
| 85 140 | 
             
            		when 'text/plain'
         | 
| 86 | 
            -
            			 | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
            			when 413
         | 
| 90 | 
            -
            				raise ImageTooLargeError, response.body.delete("\r")
         | 
| 91 | 
            -
            			else
         | 
| 92 | 
            -
            				raise RemoteServerError, response.body.delete("\r")
         | 
| 93 | 
            -
            			end
         | 
| 141 | 
            +
            			raise error_for_status(response.status, response.body)
         | 
| 142 | 
            +
            		when /^image\//
         | 
| 143 | 
            +
            			Thumbnail.new(content_type, response.body)
         | 
| 94 144 | 
             
            		when /^multipart\/mixed/
         | 
| 95 | 
            -
            			 | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
            				 | 
| 99 | 
            -
            				 | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
            					raise UnknownResponseType, part_content_type
         | 
| 145 | 
            +
            			parts = []
         | 
| 146 | 
            +
            			parser = MultipartParser::Reader.new(MultipartParser::Reader.extract_boundary_value(content_type))
         | 
| 147 | 
            +
            			parser.on_part do |part|
         | 
| 148 | 
            +
            				part_content_type = part.headers['content-type'] or raise InvalidMultipartResponseError, 'missing Content-Type header in multipart part'
         | 
| 149 | 
            +
            				part_status = part.headers['status']
         | 
| 150 | 
            +
            				data = ''
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            				part.on_data do |partial_data|
         | 
| 153 | 
            +
            					data << partial_data
         | 
| 105 154 | 
             
            				end
         | 
| 106 | 
            -
            			end
         | 
| 107 155 |  | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 156 | 
            +
            				part.on_end do
         | 
| 157 | 
            +
            					case part_content_type
         | 
| 158 | 
            +
            					when 'text/plain'
         | 
| 159 | 
            +
            						part_status or raise InvalidMultipartResponseError, 'missing Status header in error part (text/plain)'
         | 
| 160 | 
            +
             | 
| 161 | 
            +
            						begin
         | 
| 162 | 
            +
            							raise error_for_status(part_status.to_i, data.strip)
         | 
| 163 | 
            +
            						rescue HTTPThumbnailerClientError => error # raise for stack trace
         | 
| 164 | 
            +
            							parts << error
         | 
| 165 | 
            +
            						end
         | 
| 166 | 
            +
            					when /^image\//
         | 
| 167 | 
            +
            						parts << Thumbnail.new(part_content_type, data)
         | 
| 168 | 
            +
            					else
         | 
| 169 | 
            +
            						raise UnknownResponseType, part_content_type
         | 
| 170 | 
            +
            					end
         | 
| 171 | 
            +
            				end
         | 
| 111 172 | 
             
            			end
         | 
| 112 173 |  | 
| 113 | 
            -
            			 | 
| 174 | 
            +
            			parser.write response.body
         | 
| 175 | 
            +
            			parser.ended? or raise InvalidMultipartResponseError, 'truncated multipart message'
         | 
| 176 | 
            +
            			parts
         | 
| 114 177 | 
             
            		else
         | 
| 115 178 | 
             
            			raise UnknownResponseType, content_type
         | 
| 116 179 | 
             
            		end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
            		unless response.header['X-Input-Image-Content-Type'].empty?
         | 
| 182 | 
            +
            			thumbnails.extend(ThumbnailsInputMimeTypeMeta)
         | 
| 183 | 
            +
            			thumbnails.input_mime_type = response.header['X-Input-Image-Content-Type'].first
         | 
| 184 | 
            +
            		end
         | 
| 185 | 
            +
             | 
| 186 | 
            +
            		return thumbnails
         | 
| 117 187 | 
             
            	rescue HTTPClient::KeepAliveDisconnected
         | 
| 118 188 | 
             
            		raise RemoteServerError, 'empty response'
         | 
| 119 189 | 
             
            	end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
            	def inspect
         | 
| 192 | 
            +
            		"#<#{self.class.name} server_url=#{server_url.inspect} keep_alive=#{keep_alive.inspect}>"
         | 
| 193 | 
            +
            	end
         | 
| 120 194 | 
             
            end
         | 
| 121 195 |  |