magickly 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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