magickly 1.1.3 → 1.2.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 CHANGED
@@ -1,7 +1,4 @@
1
1
  source 'http://rubygems.org'
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem 'activesupport', '>= 2.3.5'
5
2
 
6
3
  gem 'sinatra', '~> 1.2.1', :require => 'sinatra/base'
7
4
  gem 'dragonfly', '~> 0.9.1'
@@ -10,9 +7,8 @@ gem 'addressable', '~> 2.2', :require => 'addressable/uri'
10
7
  gem 'haml', '~> 3.0'
11
8
  gem 'httparty', '~> 0.7.3'
12
9
  gem 'activesupport', '>= 2.0.0', :require => false
10
+ gem 'json', '~> 1.5'
13
11
 
14
- # Add dependencies to develop your gem here.
15
- # Include everything needed to run rake, tests, features, etc.
16
12
  group :development do
17
13
  gem 'jeweler', '~> 1.5'
18
14
  gem 'rcov', '>= 0'
@@ -1,39 +1,40 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activesupport (3.0.7)
4
+ activesupport (3.0.9)
5
5
  addressable (2.2.6)
6
6
  crack (0.1.8)
7
7
  diff-lcs (1.1.2)
8
- dragonfly (0.9.2)
8
+ dragonfly (0.9.4)
9
9
  rack
10
10
  git (1.2.5)
11
- haml (3.1.1)
12
- httparty (0.7.7)
11
+ haml (3.1.2)
12
+ httparty (0.7.8)
13
13
  crack (= 0.1.8)
14
14
  imagesize (0.1.1)
15
- jeweler (1.6.0)
16
- bundler (~> 1.0.0)
15
+ jeweler (1.6.2)
16
+ bundler (~> 1.0)
17
17
  git (>= 1.2.5)
18
18
  rake
19
- newrelic_rpm (3.0.1)
19
+ json (1.5.3)
20
+ newrelic_rpm (3.1.0)
20
21
  rack (1.3.0)
21
22
  rack-test (0.6.0)
22
23
  rack (>= 1.0)
23
- rake (0.9.0)
24
+ rake (0.9.2)
24
25
  rcov (0.9.9)
25
26
  rspec (2.6.0)
26
27
  rspec-core (~> 2.6.0)
27
28
  rspec-expectations (~> 2.6.0)
28
29
  rspec-mocks (~> 2.6.0)
29
- rspec-core (2.6.3)
30
+ rspec-core (2.6.4)
30
31
  rspec-expectations (2.6.0)
31
32
  diff-lcs (~> 1.1.2)
32
33
  rspec-mocks (2.6.0)
33
34
  sinatra (1.2.6)
34
35
  rack (~> 1.1)
35
36
  tilt (< 2.0, >= 1.2.2)
36
- tilt (1.3.1)
37
+ tilt (1.3.2)
37
38
  webmock (1.6.4)
38
39
  addressable (> 2.2.5, ~> 2.2)
39
40
  crack (>= 0.1.7)
@@ -49,6 +50,7 @@ DEPENDENCIES
49
50
  httparty (~> 0.7.3)
50
51
  imagesize (~> 0.1)
51
52
  jeweler (~> 1.5)
53
+ json (~> 1.5)
52
54
  newrelic_rpm
53
55
  rack-test
54
56
  rcov
data/HISTORY.md CHANGED
@@ -1,12 +1,32 @@
1
1
  This gem respects [semantic versioning](http://semver.org/).
2
2
 
3
+ # 1.2.0 (6/28/11)
4
+
5
+ [commits](https://github.com/afeld/magickly/compare/v1.1.3...v1.2.0)
6
+
7
+ * add /analyze and /analyze/:analyzer endpoints
8
+ * fix for negative values for brightness_contrast
9
+ * validation & specs for existing shortcuts
10
+ * added shortcuts:
11
+ * color_palette (analyzer)
12
+ * cross_process
13
+ * glow
14
+ * halftone
15
+ * lomo
16
+ * saturation
17
+ * two_color
18
+
3
19
  # 1.1.3 (5/26/11)
4
20
 
21
+ [commits](https://github.com/afeld/magickly/compare/v1.1.2...v1.1.3)
22
+
5
23
  * make tilt-shift faster
6
24
  * add brightness_contrast shortcut
7
25
 
8
26
  # 1.1.2 (5/16/11)
9
27
 
28
+ [commits](https://github.com/afeld/magickly/compare/v1.1.1...v1.1.2)
29
+
10
30
  * remove rack-cache - this is not the responsibility of the gem
11
31
  * use NewRelic, if configured
12
32
  * ignore params that do not correspond to jobs or processor methods
@@ -15,11 +35,15 @@ This gem respects [semantic versioning](http://semver.org/).
15
35
 
16
36
  # 1.1.1 (4/28/11)
17
37
 
38
+ [commits](https://github.com/afeld/magickly/compare/v1.1.0...v1.1.1)
39
+
18
40
  * cross-compatibility fix for Ruby 1.8/1.9 regarding the checking of available methods from the Processor
19
41
  * Addressable now required properly when included as a gem
20
42
 
21
43
  # 1.1.0 (4/26/11)
22
44
 
45
+ [commits](https://github.com/afeld/magickly/compare/v1.0.0...v1.1.0)
46
+
23
47
  * make underlying Dragonfly application accessible for external configuration
24
48
  * added some example Dragonfly shortcuts
25
49
  * major refactoring: namespacing inside of Magickly module (should be entirely backwards compatible)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # magickly
1
+ # magickly [![Build Status](http://travis-ci.org/afeld/magickly.png)](http://travis-ci.org/afeld/magickly)
2
2
 
3
3
  A service for image manipulation - built as a simple wrapper of Imagemagick which handles caching, c/o the [Dragonfly](http://markevans.github.com/dragonfly/) gem.
4
4
 
@@ -8,7 +8,35 @@ If no query params are provided, a simple sandbox page is displayed. Try it her
8
8
 
9
9
  [magickly.heroku.com](http://magickly.heroku.com)
10
10
 
11
- ## Parameters
11
+ ## Installation
12
+
13
+ Requires Imagemagick >= v6.2.4.
14
+
15
+ $ gem install magickly
16
+
17
+ ## Running the App
18
+
19
+ A few options:
20
+
21
+ ### A. Run the app directly
22
+
23
+ $ magickly
24
+
25
+ The app can be accessed at [http://localhost:4567](http://localhost:4567)
26
+
27
+ ### B. Use as an endpoint in another Rack app
28
+
29
+ As an example, to have magickly accessible at `/magickly` in a Rails app:
30
+
31
+ # Gemfile
32
+ gem 'magickly', '~> 1.1'
33
+
34
+ # config/routes.rb
35
+ match '/magickly', :to => Magickly::App, :anchor => false
36
+
37
+ For more info, see [Rails Routing from the Outside In](http://guides.rubyonrails.org/routing.html#routing-to-rack-applications) or Michael Raidel's [Mount Rails apps in Rails 3](http://inductor.induktiv.at/blog/2010/05/23/mount-rack-apps-in-rails-3/).
38
+
39
+ ## Processing Parameters
12
40
 
13
41
  *See the [Dragonfly documentation](http://markevans.github.com/dragonfly/file.ImageMagick.html) for more details about the permitted* `geometry` *values.*
14
42
 
@@ -18,7 +46,7 @@ The URL of the original image.
18
46
 
19
47
  ### brightness_contrast=*brightness* x *contrast*
20
48
 
21
- *brightness* and *contrast* are percentage change, between -100 and 100. For example, to increase contrast by 20% but leave brightness the same, use `brightness_contrast=0x20`.
49
+ *brightness* and *contrast* are percentage change, between -100 and 100. For example, to increase contrast by 20% but leave brightness unchanged, use `brightness_contrast=0x20`.
22
50
 
23
51
  ![tanned imagemagick logo](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&brightness_contrast=-10x50)
24
52
 
@@ -36,12 +64,28 @@ The URL of the original image.
36
64
 
37
65
  [http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&flop=true](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&flop=true)
38
66
 
67
+ ### glow=*amount*,*softness*
68
+
69
+ where `amount` is a float >= 1.0, and `softness` is an int >= 0.
70
+
71
+ ![glowing imagemagick logo](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&glow=1.2,20)
72
+
73
+ [http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&glow=1.2,20](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&glow=1.2,20)
74
+
39
75
  ### greyscale=true
40
76
 
41
77
  ![flopped imagemagick logo](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&greyscale=true)
42
78
 
43
79
  [http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&greyscale=true](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&greyscale=true)
44
80
 
81
+ ### halftone=*threshold*
82
+
83
+ where *threshold* is a value between 0 and 100.
84
+
85
+ ![halftone imagemagick logo](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&halftone=60)
86
+
87
+ [http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&halftone=60](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&halftone=60)
88
+
45
89
  ### resize=*geometry*
46
90
 
47
91
  ![resized imagemagick logo](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&resize=100x100)
@@ -54,6 +98,14 @@ The URL of the original image.
54
98
 
55
99
  [http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&rotate=45](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&rotate=45)
56
100
 
101
+ ### saturation=*percentage*
102
+
103
+ *percentage* is the percentage of variation: a positive integer. 100 means no change. For example, to increase saturation by 50%, use `saturation=150`.
104
+
105
+ ![saturated imagemagick logo](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&saturation=150)
106
+
107
+ [http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&saturation=150](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&saturation=150)
108
+
57
109
  ### tilt_shift=true
58
110
 
59
111
  ![tilt-shifted imagemagick logo](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&tilt_shift=true)
@@ -68,9 +120,23 @@ The URL of the original image.
68
120
 
69
121
  (note: the `%23` in the geometry string above is an encoded '`#`', which tells Dragonfly to fill the dimensions and crop)
70
122
 
123
+ ### two_color=true
124
+
125
+ ![two color imagemagick logo](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&two_color=true)
126
+
127
+ [http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&two_color=true](http://magickly.heroku.com/?src=http://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Imagemagick-logo.png/200px-Imagemagick-logo.png&two_color=true)
128
+
129
+ ## Analyzers
130
+
131
+ Magickly v1.2.0 introduces the ability to retrieve image properties via a REST API. For example, to retrieve the number of colors in the photo, visit:
132
+
133
+ [magickly.heroku.com/analyze/number_of_colors?src=http://upload.wikimedia.org/wikipedia/commons/0/0d/Imagemagick-logo.png](http://magickly.heroku.com/analyze/number_of_colors?src=http://upload.wikimedia.org/wikipedia/commons/0/0d/Imagemagick-logo.png)
134
+
135
+ To get the list of available analyzers, visit [magickly.heroku.com/analyze](http://magickly.heroku.com/analyze)
136
+
71
137
  ## Customization
72
138
 
73
- In addition to the available listed above, custom "shortcuts" can be created to perform arbitrary imagemagick operations. For example, to create a shortcut called `resize_with_blur`:
139
+ In addition to the available parameters listed above, custom "shortcuts" can be created to perform arbitrary imagemagick operations. For example, to create a shortcut called `resize_with_blur`:
74
140
 
75
141
  # somewhere in your app configuration, i.e. config/initializers/magickly.rb for a Rails 3 app
76
142
  Magickly.dragonfly.configure do |c|
@@ -83,32 +149,6 @@ which can then be used with the query string `?src=...&resize_with_blur=200x`.
83
149
 
84
150
  See the [Dragonfly documentation](http://markevans.github.com/dragonfly/file.GeneralUsage.html#Shortcuts) for more info on "shortcuts", and the [shortcuts.rb](https://github.com/afeld/magickly/blob/master/lib/shortcuts.rb) file for examples.
85
151
 
86
- ## Installation
87
-
88
- $ gem install magickly
89
-
90
- ## Running the App
91
-
92
- A few options:
93
-
94
- ### A. Run the app directly
95
-
96
- $ magickly
97
-
98
- The app can be accessed at [http://localhost:4567](http://localhost:4567)
99
-
100
- ### B. Use as an endpoint in another Rack app
101
-
102
- As an example, to have magickly accessible at `/magickly` in a Rails app:
103
-
104
- # Gemfile
105
- gem 'magickly', '~> 1.1'
106
-
107
- # config/routes.rb
108
- match '/magickly', :to => Magickly::App, :anchor => false
109
-
110
- For more info, see [Rails Routing from the Outside In](http://guides.rubyonrails.org/routing.html#routing-to-rack-applications) or Michael Raidel's [Mount Rails apps in Rails 3](http://inductor.induktiv.at/blog/2010/05/23/mount-rack-apps-in-rails-3/).
111
-
112
152
  ## Contributing to magickly
113
153
 
114
154
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.3
1
+ 1.2.0
@@ -0,0 +1,8 @@
1
+ module Dragonfly
2
+ class App
3
+ # there is a pending pull request to add this method - see https://github.com/markevans/dragonfly/pull/100
4
+ def analyser_methods
5
+ analyser.functions.keys
6
+ end
7
+ end
8
+ end
@@ -14,7 +14,7 @@ module Dragonfly
14
14
  end
15
15
 
16
16
  def retrieve(uid)
17
- response = HTTParty.get uid, :headers => {'cookie' => cookie_str || ''}
17
+ response = HTTParty.get uid, :headers => {'cookie' => cookie_str || ''}, :timeout => 3
18
18
  unless response.ok?
19
19
  #raise Forbidden if response.code == 403
20
20
  raise DataNotFound
Binary file
@@ -4,15 +4,18 @@ require 'active_support/ordered_hash'
4
4
  require 'sinatra/base'
5
5
  require 'addressable/uri'
6
6
  require 'dragonfly'
7
- require File.expand_path(File.join(File.dirname(__FILE__), 'dragonfly', 'data_storage', 'remote_data_store'))
7
+ Dir["#{File.dirname(__FILE__)}/dragonfly/**/*.rb"].each {|file| require file }
8
8
  Dir["#{File.dirname(__FILE__)}/magickly/*.rb"].each {|file| require file }
9
9
 
10
10
 
11
11
  module Magickly
12
- @dragonfly = Dragonfly[:images].configure_with(:imagemagick)
12
+ DEFAULT_PALETTE_COLOR_COUNT = 5
13
+
14
+ @dragonfly = Dragonfly[:magickly].configure_with(:imagemagick)
13
15
  @dragonfly.configure do |c|
14
16
  c.datastore = Dragonfly::DataStorage::RemoteDataStore.new
15
17
  c.log = Logger.new($stdout)
18
+ c.log_commands = true
16
19
  end
17
20
 
18
21
  class << self
@@ -1,6 +1,7 @@
1
1
  module Magickly
2
2
  class App < Sinatra::Base
3
3
  RESERVED_PARAMS = ['src']
4
+ DEMO_IMAGE = 'http://upload.wikimedia.org/wikipedia/commons/0/0d/Imagemagick-logo.png'
4
5
 
5
6
  enable :logging
6
7
  set :root, File.join(File.dirname(__FILE__), '..')
@@ -29,21 +30,52 @@ module Magickly
29
30
 
30
31
  if src
31
32
  # process image
32
-
33
- uri = Addressable::URI.parse(src)
34
- uri.site ||= Addressable::URI.parse(request.url).site
35
-
36
- image = Magickly.process_src(uri.to_s, @options)
33
+ url = uri_to_url(src)
34
+ image = Magickly.process_src(url, @options)
37
35
  image.to_response(env)
38
36
  else
39
37
  # display demo page
40
38
 
41
39
  # get combined list of jobs and processor methods
42
40
  @methods = ( Magickly.dragonfly.job_methods | Magickly.dragonfly.processor_methods )
41
+ @methods.sort!{|m1, m2| m1.to_s <=> m2.to_s }
43
42
  haml :index
44
43
  end
45
44
  end
46
45
 
46
+ get '/analyze' do
47
+ @analyzers = Magickly.dragonfly.analyser_methods
48
+ @analyzers.sort!{|m1, m2| m1.to_s <=> m2.to_s }
49
+ haml :analyzers
50
+ end
51
+
52
+ get '/analyze/:name' do |method|
53
+ src = params[:src]
54
+ if src
55
+ url = uri_to_url(src)
56
+ image = Magickly.process_src(url, @options)
57
+
58
+ begin
59
+ output = image.send(method.to_sym)
60
+ rescue NoMethodError
61
+ method = method.to_s + '?'
62
+ output = image.send(method.to_sym)
63
+ end
64
+
65
+ output.is_a?(String) ? output : output.to_json
66
+ else
67
+ status 400
68
+ "Please provide an image URL with the src parameter."
69
+ end
70
+ end
71
+
72
+
73
+ def uri_to_url(uri)
74
+ url = Addressable::URI.parse(uri)
75
+ url.site ||= Addressable::URI.parse(request.url).site
76
+ url.to_s
77
+ end
78
+
47
79
  # start the server if ruby file executed directly
48
80
  run! if __FILE__ == $0
49
81
  end
@@ -1,20 +1,91 @@
1
1
  Magickly.dragonfly.configure do |c|
2
2
  c.job :brightness_contrast do |val|
3
+ raise ArgumentError, "argument must be of the format '<int>[%]x<int>[%]'" unless val =~ /^-?\d+%?(x-?\d+%?)?$/
3
4
  process :convert, "-brightness-contrast #{val}"
4
5
  end
5
6
 
7
+ c.job :saturation do |percentage|
8
+ raise ArgumentError, "percentage must be a positive integer" unless percentage =~ /^\d+$/
9
+ process :convert, "-modulate 100,#{percentage}"
10
+ end
11
+
6
12
  c.job :resize_with_blur do |size|
7
13
  process :convert, "-filter Gaussian -resize #{size}"
8
14
  end
9
15
 
10
16
  c.job :tilt_shift do |coefficients|
11
- if coefficients == 'true'
12
- # use default polynomial coefficients
13
- coefficients = '4,-4,1'
14
- end
17
+ coefficients = '4,-4,1' if coefficients == 'true'
18
+ raise ArgumentError, "coefficients must be of the format '<decimal>,<decimal>,<decimal>'" unless coefficients =~ /^(-?\d+(\.\d+)?,){2}-?\d+(\.\d+)?$/
15
19
 
16
20
  # note: can be made faster by decreasing sigma passed to option:compose:args
17
21
  action = "\\( +clone -sparse-color Barycentric '0,0 black 0,%h white' -function polynomial #{coefficients} \\) -compose Blur -set option:compose:args 8 -composite"
18
22
  process :convert, action
19
23
  end
24
+
25
+ # thanks to http://www.melissaevans.com/tutorials/pop-art-inspired-by-lichtenstein
26
+ c.job :halftone do |threshold|
27
+ threshold = 50 if threshold == 'true'
28
+ process :convert, "-white-threshold #{threshold.to_i}% -gaussian-blur 2 -ordered-dither 8x1"
29
+ end
30
+
31
+ # thanks to http://www.photoshopsupport.com/tutorials/or/cross-processing.html
32
+ c.job :cross_process do
33
+ process :convert, "-channel Red -sigmoidal-contrast 6,50% -channel Blue -level 25%\\! -channel Green -sigmoidal-contrast 5,45% \\( +clone +matte -fill yellow -colorize 4% \\) -compose overlay -composite"
34
+ end
35
+
36
+ ## thanks to https://github.com/soveran/lomo
37
+ c.job :lomo do |modulate_params|
38
+ if modulate_params == 'true'
39
+ modulate_params = '100,150'
40
+ elsif modulate_params =~ /^\d+,\d+$/
41
+ # valid params
42
+ else
43
+ raise ArgumentError, "modulate_params must be of the format '<int>,<int>'"
44
+ end
45
+
46
+ lomo_mask = File.join(File.dirname(__FILE__), 'images', 'lomo_mask.png')
47
+ process :convert, "\\( +clone -unsharp 1 -contrast -contrast -modulate #{modulate_params} \\( #{lomo_mask} -resize #{@job.width}x#{@job.height}\\! \\) -compose overlay -composite \\) -compose multiply -composite"
48
+ end
49
+
50
+
51
+ ## thanks to Fred Weinhaus (http://www.fmwconcepts.com/imagemagick) for the following: ##
52
+
53
+ c.job :glow do |args|
54
+ if args == 'true'
55
+ amount = 1.2
56
+ softening = 20
57
+ elsif args =~ /^(\d+\.\d+?),(\d+)$/ && $1.to_f >= 1.0 && $2.to_i >= 0
58
+ amount = $1
59
+ softening = $2
60
+ else
61
+ raise ArgumentError, "args must be of the form <amount(float)>,<softening(int)>"
62
+ end
63
+
64
+ process :convert, "\\( +clone -evaluate multiply #{amount} -blur 0x#{softening} \\) -compose plus -composite"
65
+ end
66
+
67
+ c.job :two_color do
68
+ process :convert, "-background black -flatten +matte +dither -colors 2 -colorspace gray -contrast-stretch 0"
69
+ end
70
+
71
+ #########################################################################################
72
+
73
+
74
+ c.job :color_palette_swatch do |count|
75
+ count = Magickly::DEFAULT_PALETTE_COLOR_COUNT if count == 'true'
76
+
77
+ process :convert, "-resize 600x600 -colors #{count} -unique-colors -scale 10000%"
78
+ encode :gif
79
+ end
80
+
81
+ c.analyser.add :color_palette do |temp_object, num_colors|
82
+ num_colors = num_colors.blank? ? Magickly::DEFAULT_PALETTE_COLOR_COUNT : num_colors.to_i
83
+ output = `convert #{temp_object.path} -resize 600x600 -colors #{num_colors} -format %c -depth 8 histogram:info:-`
84
+
85
+ palette = []
86
+ output.scan(/\s+(\d+):[^\n]+#([0-9A-Fa-f]{6})/) do |count, hex|
87
+ palette << { :count => count.to_i, :hex => hex }
88
+ end
89
+ palette
90
+ end
20
91
  end
@@ -0,0 +1,3 @@
1
+ - @analyzers.each do |analyzer|
2
+ %a{:href => "/analyze/#{analyzer.to_s.chomp('?')}?src=#{Magickly::App::DEMO_IMAGE}"}= analyzer
3
+ %br
@@ -40,7 +40,7 @@
40
40
  %br
41
41
  %br
42
42
  %label Image URL:
43
- %input#src-img{:size => 80, :value => "http://upload.wikimedia.org/wikipedia/commons/0/0d/Imagemagick-logo.png"}
43
+ %input#src-img{:size => 80, :value => Magickly::App::DEMO_IMAGE}
44
44
  %br
45
45
  %label Params:
46
46
  %input#params{:size => 80, :value => "resize=200x"}
@@ -5,15 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{magickly}
8
- s.version = "1.1.3"
8
+ s.version = "1.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Aidan Feldman"]
12
- s.date = %q{2011-05-26}
13
- s.default_executable = %q{magickly}
11
+ s.authors = [%q{Aidan Feldman}]
12
+ s.date = %q{2011-06-29}
14
13
  s.description = %q{A service for image manipulation - built as an extensible wrapper of Imagemagick which handles caching, c/o the Dragonfly gem.}
15
14
  s.email = %q{aidan.feldman@gmail.com}
16
- s.executables = ["magickly"]
15
+ s.executables = [%q{magickly}]
17
16
  s.extra_rdoc_files = [
18
17
  "LICENSE.txt",
19
18
  "README.md"
@@ -31,23 +30,27 @@ Gem::Specification.new do |s|
31
30
  "VERSION",
32
31
  "bin/magickly",
33
32
  "config.ru",
33
+ "lib/dragonfly/app.rb",
34
34
  "lib/dragonfly/data_storage/remote_data_store.rb",
35
+ "lib/images/lomo_mask.png",
35
36
  "lib/magickly.rb",
36
37
  "lib/magickly/app.rb",
37
38
  "lib/public/imagemagick.png",
38
39
  "lib/shortcuts.rb",
40
+ "lib/views/analyzers.haml",
39
41
  "lib/views/index.haml",
40
42
  "magickly.gemspec",
41
43
  "spec/requests/magickly_app_spec.rb",
42
44
  "spec/spec_helper.rb",
43
45
  "spec/support/imagemagick.png",
44
46
  "spec/unit/magickly_spec.rb",
45
- "spec/unit/remote_data_store_spec.rb"
47
+ "spec/unit/remote_data_store_spec.rb",
48
+ "spec/unit/shortcut_spec.rb"
46
49
  ]
47
50
  s.homepage = %q{http://github.com/afeld/magickly}
48
- s.licenses = ["MIT"]
49
- s.require_paths = ["lib"]
50
- s.rubygems_version = %q{1.6.2}
51
+ s.licenses = [%q{MIT}]
52
+ s.require_paths = [%q{lib}]
53
+ s.rubygems_version = %q{1.8.5}
51
54
  s.summary = %q{image manipulation as a (plugin-able) service}
52
55
 
53
56
  if s.respond_to? :specification_version then
@@ -60,6 +63,7 @@ Gem::Specification.new do |s|
60
63
  s.add_runtime_dependency(%q<haml>, ["~> 3.0"])
61
64
  s.add_runtime_dependency(%q<httparty>, ["~> 0.7.3"])
62
65
  s.add_runtime_dependency(%q<activesupport>, [">= 2.0.0"])
66
+ s.add_runtime_dependency(%q<json>, ["~> 1.5"])
63
67
  s.add_development_dependency(%q<jeweler>, ["~> 1.5"])
64
68
  s.add_development_dependency(%q<rcov>, [">= 0"])
65
69
  s.add_development_dependency(%q<rack-test>, [">= 0"])
@@ -73,6 +77,7 @@ Gem::Specification.new do |s|
73
77
  s.add_dependency(%q<haml>, ["~> 3.0"])
74
78
  s.add_dependency(%q<httparty>, ["~> 0.7.3"])
75
79
  s.add_dependency(%q<activesupport>, [">= 2.0.0"])
80
+ s.add_dependency(%q<json>, ["~> 1.5"])
76
81
  s.add_dependency(%q<jeweler>, ["~> 1.5"])
77
82
  s.add_dependency(%q<rcov>, [">= 0"])
78
83
  s.add_dependency(%q<rack-test>, [">= 0"])
@@ -87,6 +92,7 @@ Gem::Specification.new do |s|
87
92
  s.add_dependency(%q<haml>, ["~> 3.0"])
88
93
  s.add_dependency(%q<httparty>, ["~> 0.7.3"])
89
94
  s.add_dependency(%q<activesupport>, [">= 2.0.0"])
95
+ s.add_dependency(%q<json>, ["~> 1.5"])
90
96
  s.add_dependency(%q<jeweler>, ["~> 1.5"])
91
97
  s.add_dependency(%q<rcov>, [">= 0"])
92
98
  s.add_dependency(%q<rack-test>, [">= 0"])
@@ -1,4 +1,5 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
+ require 'json'
2
3
 
3
4
  describe Magickly::App do
4
5
  include Rack::Test::Methods
@@ -7,14 +8,14 @@ describe Magickly::App do
7
8
  Magickly::App
8
9
  end
9
10
 
11
+ def setup_image(host='http://www.foo.com')
12
+ @image_filename = 'imagemagick.png'
13
+ @image_url = "#{host}/#{@image_filename}"
14
+ @image_path = File.join(File.dirname(__FILE__), '..', 'support', @image_filename)
15
+ stub_request(:get, @image_url).to_return(:body => File.new(@image_path))
16
+ end
17
+
10
18
  describe "GET /" do
11
- def setup_image(host='http://www.foo.com')
12
- @image_filename = 'imagemagick.png'
13
- @image_url = "#{host}/#{@image_filename}"
14
- @image_path = File.join(File.dirname(__FILE__), '..', 'support', @image_filename)
15
- stub_request(:get, @image_url).to_return(:body => File.new(@image_path))
16
- end
17
-
18
19
  it "should display the demo page for no params" do
19
20
  get '/'
20
21
  last_response.should be_ok
@@ -100,6 +101,42 @@ describe Magickly::App do
100
101
  ImageSize.new(last_response.body).get_width.should eq width
101
102
  end
102
103
  end
104
+
105
+ describe "GET /analyze" do
106
+ it "retrieves the mime_type of an image" do
107
+ setup_image
108
+
109
+ get "/analyze/mime_type?src=#{@image_url}"
110
+
111
+ a_request(:get, @image_url).should have_been_made.once
112
+ last_response.should be_ok
113
+ last_response.body.should eq 'image/png'
114
+ end
115
+
116
+ it "retrieves the color palette of an image" do
117
+ setup_image
118
+
119
+ get "/analyze/color_palette?src=#{@image_url}"
120
+
121
+ a_request(:get, @image_url).should have_been_made.once
122
+ last_response.should be_ok
123
+ last_response.body.should_not be_empty
124
+ json = JSON.parse(last_response.body)
125
+ json.should be_an Array
126
+ json.size.should eq 5
127
+ end
128
+
129
+ it "should handle analyzer methods where the question mark is missing" do
130
+ Magickly.dragonfly.analyser_methods.map{|m| m.to_s }.should include 'landscape?'
131
+ setup_image
132
+
133
+ get "/analyze/landscape?src=#{@image_url}"
134
+
135
+ a_request(:get, @image_url).should have_been_made.once
136
+ last_response.should be_ok
137
+ last_response.body.should =~ /false/
138
+ end
139
+ end
103
140
  end
104
141
 
105
142
  describe MagicklyApp do
@@ -8,7 +8,7 @@ require 'image_size'
8
8
 
9
9
  # Requires supporting files with custom matchers and macros, etc,
10
10
  # in ./support/ and its subdirectories.
11
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
+ # Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
12
12
 
13
13
  set :environment, :test
14
14
 
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Dragonfly shortcuts" do
4
+ before do
5
+ @image_path = File.join(File.dirname(__FILE__), '..', 'support', 'imagemagick.png')
6
+ @image = Magickly.dragonfly.fetch_file(@image_path)
7
+ end
8
+
9
+ describe :brightness_contrast do
10
+ it "should throw an error for no arguments" do
11
+ expect { @image.brightness_contrast }.to raise_error
12
+ end
13
+
14
+ it "should throw an error for a empty string" do
15
+ expect { @image.brightness_contrast('') }.to raise_error(ArgumentError)
16
+ end
17
+
18
+ it "should succeed for a valid argument" do
19
+ @image.brightness_contrast('10x10')
20
+ end
21
+
22
+ it "should succeed for negative values" do
23
+ @image.brightness_contrast('-80x-80')
24
+ end
25
+ end
26
+
27
+ describe :color_palette do
28
+ it "should return an array" do
29
+ palette = @image.color_palette
30
+ palette.should be_an Array
31
+ end
32
+
33
+ it "should have the default number of colors" do
34
+ palette = @image.color_palette
35
+ palette.length.should eq Magickly::DEFAULT_PALETTE_COLOR_COUNT
36
+ end
37
+
38
+ it "should have the specified number of colors" do
39
+ num_colors = 4
40
+ num_colors.should_not eq Magickly::DEFAULT_PALETTE_COLOR_COUNT
41
+
42
+ palette = @image.color_palette(num_colors)
43
+ palette.length.should eq num_colors
44
+ end
45
+ end
46
+
47
+ describe :tilt_shift do
48
+ it "should throw an error for no coefficient" do
49
+ expect { @image.tilt_shift }.to raise_error
50
+ end
51
+
52
+ it "should throw an error for a empty string" do
53
+ expect { @image.tilt_shift('') }.to raise_error(ArgumentError)
54
+ end
55
+
56
+ it "should succeed for a valid coefficient" do
57
+ @image.tilt_shift('2,-1,0.5')
58
+ end
59
+ end
60
+ end
metadata CHANGED
@@ -1,8 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magickly
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 31
4
5
  prerelease:
5
- version: 1.1.3
6
+ segments:
7
+ - 1
8
+ - 2
9
+ - 0
10
+ version: 1.2.0
6
11
  platform: ruby
7
12
  authors:
8
13
  - Aidan Feldman
@@ -10,141 +15,205 @@ autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
17
 
13
- date: 2011-05-26 00:00:00 -04:00
14
- default_executable: magickly
18
+ date: 2011-06-29 00:00:00 Z
15
19
  dependencies:
16
20
  - !ruby/object:Gem::Dependency
17
- name: sinatra
18
- requirement: &id001 !ruby/object:Gem::Requirement
21
+ version_requirements: &id001 !ruby/object:Gem::Requirement
19
22
  none: false
20
23
  requirements:
21
24
  - - ~>
22
25
  - !ruby/object:Gem::Version
26
+ hash: 29
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 1
23
31
  version: 1.2.1
24
32
  type: :runtime
33
+ requirement: *id001
25
34
  prerelease: false
26
- version_requirements: *id001
35
+ name: sinatra
27
36
  - !ruby/object:Gem::Dependency
28
- name: dragonfly
29
- requirement: &id002 !ruby/object:Gem::Requirement
37
+ version_requirements: &id002 !ruby/object:Gem::Requirement
30
38
  none: false
31
39
  requirements:
32
40
  - - ~>
33
41
  - !ruby/object:Gem::Version
42
+ hash: 57
43
+ segments:
44
+ - 0
45
+ - 9
46
+ - 1
34
47
  version: 0.9.1
35
48
  type: :runtime
49
+ requirement: *id002
36
50
  prerelease: false
37
- version_requirements: *id002
51
+ name: dragonfly
38
52
  - !ruby/object:Gem::Dependency
39
- name: addressable
40
- requirement: &id003 !ruby/object:Gem::Requirement
53
+ version_requirements: &id003 !ruby/object:Gem::Requirement
41
54
  none: false
42
55
  requirements:
43
56
  - - ~>
44
57
  - !ruby/object:Gem::Version
58
+ hash: 7
59
+ segments:
60
+ - 2
61
+ - 2
45
62
  version: "2.2"
46
63
  type: :runtime
64
+ requirement: *id003
47
65
  prerelease: false
48
- version_requirements: *id003
66
+ name: addressable
49
67
  - !ruby/object:Gem::Dependency
50
- name: haml
51
- requirement: &id004 !ruby/object:Gem::Requirement
68
+ version_requirements: &id004 !ruby/object:Gem::Requirement
52
69
  none: false
53
70
  requirements:
54
71
  - - ~>
55
72
  - !ruby/object:Gem::Version
73
+ hash: 7
74
+ segments:
75
+ - 3
76
+ - 0
56
77
  version: "3.0"
57
78
  type: :runtime
79
+ requirement: *id004
58
80
  prerelease: false
59
- version_requirements: *id004
81
+ name: haml
60
82
  - !ruby/object:Gem::Dependency
61
- name: httparty
62
- requirement: &id005 !ruby/object:Gem::Requirement
83
+ version_requirements: &id005 !ruby/object:Gem::Requirement
63
84
  none: false
64
85
  requirements:
65
86
  - - ~>
66
87
  - !ruby/object:Gem::Version
88
+ hash: 5
89
+ segments:
90
+ - 0
91
+ - 7
92
+ - 3
67
93
  version: 0.7.3
68
94
  type: :runtime
95
+ requirement: *id005
69
96
  prerelease: false
70
- version_requirements: *id005
97
+ name: httparty
71
98
  - !ruby/object:Gem::Dependency
72
- name: activesupport
73
- requirement: &id006 !ruby/object:Gem::Requirement
99
+ version_requirements: &id006 !ruby/object:Gem::Requirement
74
100
  none: false
75
101
  requirements:
76
102
  - - ">="
77
103
  - !ruby/object:Gem::Version
104
+ hash: 15
105
+ segments:
106
+ - 2
107
+ - 0
108
+ - 0
78
109
  version: 2.0.0
79
110
  type: :runtime
111
+ requirement: *id006
80
112
  prerelease: false
81
- version_requirements: *id006
113
+ name: activesupport
82
114
  - !ruby/object:Gem::Dependency
83
- name: jeweler
84
- requirement: &id007 !ruby/object:Gem::Requirement
115
+ version_requirements: &id007 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ~>
119
+ - !ruby/object:Gem::Version
120
+ hash: 5
121
+ segments:
122
+ - 1
123
+ - 5
124
+ version: "1.5"
125
+ type: :runtime
126
+ requirement: *id007
127
+ prerelease: false
128
+ name: json
129
+ - !ruby/object:Gem::Dependency
130
+ version_requirements: &id008 !ruby/object:Gem::Requirement
85
131
  none: false
86
132
  requirements:
87
133
  - - ~>
88
134
  - !ruby/object:Gem::Version
135
+ hash: 5
136
+ segments:
137
+ - 1
138
+ - 5
89
139
  version: "1.5"
90
140
  type: :development
141
+ requirement: *id008
91
142
  prerelease: false
92
- version_requirements: *id007
143
+ name: jeweler
93
144
  - !ruby/object:Gem::Dependency
94
- name: rcov
95
- requirement: &id008 !ruby/object:Gem::Requirement
145
+ version_requirements: &id009 !ruby/object:Gem::Requirement
96
146
  none: false
97
147
  requirements:
98
148
  - - ">="
99
149
  - !ruby/object:Gem::Version
150
+ hash: 3
151
+ segments:
152
+ - 0
100
153
  version: "0"
101
154
  type: :development
155
+ requirement: *id009
102
156
  prerelease: false
103
- version_requirements: *id008
157
+ name: rcov
104
158
  - !ruby/object:Gem::Dependency
105
- name: rack-test
106
- requirement: &id009 !ruby/object:Gem::Requirement
159
+ version_requirements: &id010 !ruby/object:Gem::Requirement
107
160
  none: false
108
161
  requirements:
109
162
  - - ">="
110
163
  - !ruby/object:Gem::Version
164
+ hash: 3
165
+ segments:
166
+ - 0
111
167
  version: "0"
112
168
  type: :development
169
+ requirement: *id010
113
170
  prerelease: false
114
- version_requirements: *id009
171
+ name: rack-test
115
172
  - !ruby/object:Gem::Dependency
116
- name: rspec
117
- requirement: &id010 !ruby/object:Gem::Requirement
173
+ version_requirements: &id011 !ruby/object:Gem::Requirement
118
174
  none: false
119
175
  requirements:
120
176
  - - ~>
121
177
  - !ruby/object:Gem::Version
178
+ hash: 11
179
+ segments:
180
+ - 2
181
+ - 4
122
182
  version: "2.4"
123
183
  type: :development
184
+ requirement: *id011
124
185
  prerelease: false
125
- version_requirements: *id010
186
+ name: rspec
126
187
  - !ruby/object:Gem::Dependency
127
- name: webmock
128
- requirement: &id011 !ruby/object:Gem::Requirement
188
+ version_requirements: &id012 !ruby/object:Gem::Requirement
129
189
  none: false
130
190
  requirements:
131
191
  - - ~>
132
192
  - !ruby/object:Gem::Version
193
+ hash: 3
194
+ segments:
195
+ - 1
196
+ - 6
133
197
  version: "1.6"
134
198
  type: :development
199
+ requirement: *id012
135
200
  prerelease: false
136
- version_requirements: *id011
201
+ name: webmock
137
202
  - !ruby/object:Gem::Dependency
138
- name: imagesize
139
- requirement: &id012 !ruby/object:Gem::Requirement
203
+ version_requirements: &id013 !ruby/object:Gem::Requirement
140
204
  none: false
141
205
  requirements:
142
206
  - - ~>
143
207
  - !ruby/object:Gem::Version
208
+ hash: 9
209
+ segments:
210
+ - 0
211
+ - 1
144
212
  version: "0.1"
145
213
  type: :development
214
+ requirement: *id013
146
215
  prerelease: false
147
- version_requirements: *id012
216
+ name: imagesize
148
217
  description: A service for image manipulation - built as an extensible wrapper of Imagemagick which handles caching, c/o the Dragonfly gem.
149
218
  email: aidan.feldman@gmail.com
150
219
  executables:
@@ -167,11 +236,14 @@ files:
167
236
  - VERSION
168
237
  - bin/magickly
169
238
  - config.ru
239
+ - lib/dragonfly/app.rb
170
240
  - lib/dragonfly/data_storage/remote_data_store.rb
241
+ - lib/images/lomo_mask.png
171
242
  - lib/magickly.rb
172
243
  - lib/magickly/app.rb
173
244
  - lib/public/imagemagick.png
174
245
  - lib/shortcuts.rb
246
+ - lib/views/analyzers.haml
175
247
  - lib/views/index.haml
176
248
  - magickly.gemspec
177
249
  - spec/requests/magickly_app_spec.rb
@@ -179,7 +251,7 @@ files:
179
251
  - spec/support/imagemagick.png
180
252
  - spec/unit/magickly_spec.rb
181
253
  - spec/unit/remote_data_store_spec.rb
182
- has_rdoc: true
254
+ - spec/unit/shortcut_spec.rb
183
255
  homepage: http://github.com/afeld/magickly
184
256
  licenses:
185
257
  - MIT
@@ -193,7 +265,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
193
265
  requirements:
194
266
  - - ">="
195
267
  - !ruby/object:Gem::Version
196
- hash: 3187711069815381468
268
+ hash: 3
197
269
  segments:
198
270
  - 0
199
271
  version: "0"
@@ -202,11 +274,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
274
  requirements:
203
275
  - - ">="
204
276
  - !ruby/object:Gem::Version
277
+ hash: 3
278
+ segments:
279
+ - 0
205
280
  version: "0"
206
281
  requirements: []
207
282
 
208
283
  rubyforge_project:
209
- rubygems_version: 1.6.2
284
+ rubygems_version: 1.8.5
210
285
  signing_key:
211
286
  specification_version: 3
212
287
  summary: image manipulation as a (plugin-able) service