insano_image_resizer 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -19,7 +19,7 @@ Usage
19
19
 
20
20
  In your Gemfile:
21
21
 
22
- gem 'insano_resizer'
22
+ gem 'insano_image_resizer'
23
23
 
24
24
  Example:
25
25
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
data/spec/spec_helper.rb CHANGED
@@ -5,7 +5,7 @@ Bundler.setup(:default, :test)
5
5
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
6
  $LOAD_PATH.unshift(File.dirname(__FILE__))
7
7
  require 'rspec'
8
- require 'image_resizer'
8
+ require 'insano_image_resizer'
9
9
  require 'fileutils'
10
10
  require 'pry'
11
11
  require 'pry-nav'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: insano_image_resizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -111,18 +111,9 @@ files:
111
111
  - README.md
112
112
  - Rakefile
113
113
  - VERSION
114
- - lib/image_resizer.rb
115
- - lib/image_resizer/configurable.rb
116
- - lib/image_resizer/loggable.rb
117
- - lib/image_resizer/processor.rb
118
- - lib/image_resizer/shell.rb
119
114
  - samples/explanation.png
120
115
  - samples/test.jpg
121
116
  - samples/test.png
122
- - spec/image_resizer/configurable_spec.rb
123
- - spec/image_resizer/loggable_spec.rb
124
- - spec/image_resizer/processor_spec.rb
125
- - spec/image_resizer/shell_spec.rb
126
117
  - spec/spec_helper.rb
127
118
  homepage: http://github.com/populr/insano_image_resizer
128
119
  licenses:
data/lib/image_resizer.rb DELETED
@@ -1,5 +0,0 @@
1
-
2
- require 'image_resizer/loggable'
3
- require 'image_resizer/configurable'
4
- require 'image_resizer/shell'
5
- require 'image_resizer/processor'
@@ -1,206 +0,0 @@
1
- module ImageResizer
2
- module Configurable
3
-
4
- # Exceptions
5
- class NotConfigured < RuntimeError; end
6
- class BadConfigAttribute < RuntimeError; end
7
-
8
- def self.included(klass)
9
- klass.class_eval do
10
- include Configurable::InstanceMethods
11
- extend Configurable::ClassMethods
12
- end
13
- end
14
-
15
- class DeferredBlock # Inheriting from Proc causes errors in some versions of Ruby
16
- def initialize(blk)
17
- @blk = blk
18
- end
19
-
20
- def call
21
- @blk.call
22
- end
23
- end
24
-
25
- module InstanceMethods
26
-
27
- def configure(&block)
28
- yield ConfigurationProxy.new(self)
29
- self
30
- end
31
-
32
- def configure_with(config, *args, &block)
33
- config = saved_config_for(config) if config.is_a?(Symbol)
34
- config.apply_configuration(self, *args)
35
- configure(&block) if block
36
- self
37
- end
38
-
39
- def has_config_method?(method_name)
40
- config_methods.include?(method_name.to_sym)
41
- end
42
-
43
- def configuration
44
- @configuration ||= {}
45
- end
46
-
47
- def config_methods
48
- @config_methods ||= self.class.config_methods.dup
49
- end
50
-
51
- def default_configuration
52
- @default_configuration ||= self.class.default_configuration.dup
53
- end
54
-
55
- def set_config_value(key, value)
56
- configuration[key] = value
57
- child_configurables.each{|c| c.set_if_unset(key, value) }
58
- value
59
- end
60
-
61
- def use_as_fallback_config(other_configurable)
62
- other_configurable.add_child_configurable(self)
63
- self.fallback_configurable = other_configurable
64
- end
65
-
66
- protected
67
-
68
- def add_child_configurable(obj)
69
- child_configurables << obj
70
- config_methods.push(*obj.config_methods)
71
- fallback_configurable.config_methods.push(*obj.config_methods) if fallback_configurable
72
- end
73
-
74
- def set_if_unset(key, value)
75
- set_config_value(key, value) unless set_locally?(key)
76
- end
77
-
78
- private
79
-
80
- attr_accessor :fallback_configurable
81
-
82
- def child_configurables
83
- @child_configurables ||= []
84
- end
85
-
86
- def set_locally?(key)
87
- instance_variable_defined?("@#{key}")
88
- end
89
-
90
- def default_value(key)
91
- if default_configuration[key].is_a?(DeferredBlock)
92
- default_configuration[key] = default_configuration[key].call
93
- end
94
- default_configuration[key]
95
- end
96
-
97
- def saved_configs
98
- self.class.saved_configs
99
- end
100
-
101
- def saved_config_for(symbol)
102
- config = saved_configs[symbol]
103
- if config.nil?
104
- raise ArgumentError, "#{symbol.inspect} is not a known configuration - try one of #{saved_configs.keys.join(', ')}"
105
- end
106
- config = config.call if config.respond_to?(:call)
107
- config
108
- end
109
-
110
- end
111
-
112
- module ClassMethods
113
-
114
- def default_configuration
115
- @default_configuration ||= configurable_ancestors.reverse.inject({}) do |default_config, klass|
116
- default_config.merge!(klass.default_configuration)
117
- default_config
118
- end
119
- end
120
-
121
- def config_methods
122
- @config_methods ||= configurable_ancestors.inject([]) do |conf_methods, klass|
123
- conf_methods |= klass.config_methods
124
- conf_methods
125
- end
126
- end
127
-
128
- def nested_configurables
129
- @nested_configurables ||= []
130
- end
131
-
132
- def register_configuration(name, config=nil, &config_in_block)
133
- saved_configs[name] = config_in_block || config
134
- end
135
-
136
- def saved_configs
137
- @saved_configs ||= {}
138
- end
139
-
140
- def configurable_ancestors
141
- @configurable_ancestors ||= ancestors.select{|a| a.included_modules.include?(Configurable) } - [self]
142
- end
143
-
144
- private
145
-
146
- def configurable_attr attribute, default=nil, &blk
147
- default_configuration[attribute] = blk ? DeferredBlock.new(blk) : default
148
-
149
- # Define the reader
150
- define_method(attribute) do
151
- configuration.has_key?(attribute) ? configuration[attribute] : default_value(attribute)
152
- end
153
-
154
- # Define the writer
155
- define_method("#{attribute}=") do |value|
156
- instance_variable_set("@#{attribute}", value)
157
- set_config_value(attribute, value)
158
- end
159
-
160
- configuration_method attribute
161
- configuration_method "#{attribute}="
162
- end
163
-
164
- def configuration_method(*method_names)
165
- config_methods.push(*method_names.map{|n| n.to_sym }).uniq!
166
- end
167
-
168
- def nested_configurable(*method_names)
169
- nested_configurables.push(*method_names)
170
- end
171
-
172
- end
173
-
174
- class ConfigurationProxy
175
-
176
- def initialize(owner)
177
- @owner = owner
178
- end
179
-
180
- def method_missing(method_name, *args, &block)
181
- if owner.has_config_method?(method_name)
182
- attribute = method_name.to_s.tr('=','').to_sym
183
- if method_name.to_s =~ /=$/ && owner.has_config_method?(attribute) # a bit hacky - if it has both getter and setter, assume it's a configurable_attr
184
- owner.set_config_value(attribute, args.first)
185
- else
186
- owner.send(method_name, *args, &block)
187
- end
188
- elsif nested_configurable?(method_name)
189
- owner.send(method_name)
190
- else
191
- raise BadConfigAttribute, "You tried to configure using '#{method_name.inspect}', but the valid config attributes are #{owner.config_methods.map{|a| %('#{a.inspect}') }.sort.join(', ')}"
192
- end
193
- end
194
-
195
- private
196
-
197
- attr_reader :owner
198
-
199
- def nested_configurable?(method)
200
- owner.class.nested_configurables.include?(method.to_sym)
201
- end
202
-
203
- end
204
-
205
- end
206
- end
@@ -1,28 +0,0 @@
1
- require 'logger'
2
-
3
- module ImageResizer
4
- module Loggable
5
-
6
- def log
7
- case @log_object
8
- when nil
9
- @log_object = Logger.new($stdout)
10
- when Proc
11
- @log_object[]
12
- when Logger
13
- @log_object
14
- end
15
- end
16
-
17
- def log=(object)
18
- @log_object = object
19
- end
20
-
21
- attr_reader :log_object
22
-
23
- def use_same_log_as(object)
24
- self.log = proc{ object.log }
25
- end
26
-
27
- end
28
- end
@@ -1,199 +0,0 @@
1
- require 'yaml'
2
- require 'shellwords'
3
-
4
- module ImageResizer
5
- class Processor
6
-
7
- include Configurable
8
- include Shell
9
- include Loggable
10
-
11
- def process(input_path, viewport_size = {}, interest_point = {})
12
- input_properties = fetch_image_properties(input_path)
13
- input_has_alpha = (input_properties["bands"] == 4)
14
-
15
- output_tmp = Tempfile.new(["img", input_has_alpha ? ".png" : ".jpg"])
16
-
17
- transform = calculate_transform(input_path, input_properties, viewport_size, interest_point)
18
- run_transform(input_path, output_tmp.path, transform)
19
-
20
- return output_tmp.path
21
- end
22
-
23
- def fetch_image_properties(input_path)
24
- # read in the image headers to discover the width and height of the image.
25
- # There's actually some extra metadata we ignore here, but this seems to be
26
- # the only way to get width and height from VIPS.
27
- result = run("vips im_printdesc '#{input_path}'")
28
-
29
- # for some reason, the response isn't just YAML. It's one line of text in parenthesis
30
- # followed by YAML. Let's chop off the first line to get to the good stuff.
31
- result = result[(result.index(')') + 2)..-1]
32
- return YAML::load(result)
33
- end
34
-
35
- def calculate_transform(input_path, input_properties, viewport_size, interest_point)
36
-
37
- # Pull out the properties we're interested in
38
- image_size = {w: input_properties["width"].to_f, h: input_properties["height"].to_f}
39
-
40
- # By default, the interest size is 30% of the total image size.
41
- # In the future, this could be a parameter, and you'd pass the # of pixels around
42
- # the POI you are interested in.
43
- if (interest_point[:xf])
44
- interest_point[:x] = image_size[:w] * interest_point[:xf]
45
- end
46
-
47
- if (interest_point[:yf])
48
- interest_point[:y] = image_size[:h] * interest_point[:yf]
49
- end
50
-
51
- if (interest_point[:region] == nil)
52
- interest_point[:region] = 1
53
- end
54
-
55
- if (interest_point[:x] == nil)
56
- interest_point[:x] = image_size[:w] * 0.5
57
- interest_point[:region] = 1
58
- end
59
- if (interest_point[:y] == nil)
60
- interest_point[:y] = image_size[:h] * 0.5
61
- interest_point[:region] = 1
62
- end
63
-
64
- interest_size = {w: image_size[:w] * interest_point[:region], h: image_size[:h] * interest_point[:region]}
65
-
66
- # Has the user specified both the width and the height of the viewport? If they haven't,
67
- # let's go ahead and fill in the missing properties for them so that they get output at
68
- # the original aspect ratio of the image.
69
- if ((viewport_size[:w] == nil) && (viewport_size[:h] == nil))
70
- viewport_size = {w: input_properties["width"], h: input_properties["height"]}
71
-
72
- elsif (viewport_size[:w] == nil)
73
- viewport_size[:w] = viewport_size[:h] * (input_properties["width"] / input_properties["height"])
74
-
75
- elsif (viewport_size[:h] == nil)
76
- viewport_size[:h] = viewport_size[:w] * (input_properties["height"] / input_properties["width"])
77
- end
78
-
79
- # how can we take our current image and fit it into the viewport? Time for
80
- # some fun math! First, let's determine a scale such that the image fits
81
- # within the viewport. There are a few rules we want to apply:
82
- # 1) The image should _always_ fill the viewport.
83
- # 2) The 1/3 of the image around the interest_point should always be visible.
84
- # This means that if we try to cram a massive image into a tiny viewport,
85
- # we won't get a simple scale-to-fill. We'll get a more zoomed-in version
86
- # showing just the 1/3 around the interest_point.
87
-
88
- scale_to_fill = [viewport_size[:w] / image_size[:w], viewport_size[:h] / image_size[:h]].max
89
-
90
- scale_to_interest = [interest_size[:w] / image_size[:w], interest_size[:h] / image_size[:h]].max
91
-
92
- log.debug("POI: ")
93
- log.debug(interest_point)
94
- log.debug("Image size: ")
95
- log.debug(image_size)
96
- log.debug("Requested viewport size: ")
97
- log.debug(viewport_size)
98
- log.debug("scale_to_fill: %f" % scale_to_fill)
99
- log.debug("scale_to_interest: %f" % scale_to_interest)
100
-
101
-
102
- scale_for_best_region = [scale_to_fill, scale_to_interest].max
103
-
104
- # cool! Now, let's figure out what the content offset within the image should be.
105
- # We want to keep the point of interest in view whenever possible. First, let's
106
- # compute an optimal frame around the POI:
107
- best_region = {x: interest_point[:x].to_f - (image_size[:w] * scale_for_best_region) / 2,
108
- y: interest_point[:y].to_f - (image_size[:h] * scale_for_best_region) / 2,
109
- w: image_size[:w] * scale_for_best_region,
110
- h: image_size[:h] * scale_for_best_region}
111
-
112
- # Up to this point, we've been using 'scale_for_best_region' to be the preferred scale of the image.
113
- # So, scale could be 1/3 if we want to show the area around the POI, or 1 if we're fitting a whole image
114
- # in a viewport that is exactly the same aspect ratio.
115
-
116
- # The next step is to compute a scale that should be applied to the image to make this desired section of
117
- # the image fit within the viewport. This is different from the previous scale—if we wanted to fit 1/3 of
118
- # the image in a 100x100 pixel viewport, we computed best_region using that 1/3, and now we need to find
119
- # the scale that will fit it into 100px.
120
- scale = [scale_to_fill, viewport_size[:w] / best_region[:w], viewport_size[:h] / best_region[:h]].max
121
-
122
- # Next, we scale the best_region so that it is in final coordinates. When we perform the affine transform,
123
- # it will SCALE the entire image and then CROP it to a region, so our transform rect needs to be in the
124
- # coordinate space of the SCALED image, not the initial image.
125
- transform = {}
126
- transform[:x] = best_region[:x] * scale
127
- transform[:y] = best_region[:y] * scale
128
- transform[:w] = best_region[:w] * scale
129
- transform[:h] = best_region[:h] * scale
130
- transform[:scale] = scale
131
-
132
- # transform now represents the region we'd like to have in the final image. All of it, or part of it, may
133
- # not actually be within the bounds of the image! We're about to apply some constraints, but first let's
134
- # trim the best_region so that it's the SHAPE of the viewport, not just the SCALE of the viewport. Remember,
135
- # since the region is still centered around the POI, we can just trim equally on either the W or H as necessary.
136
- transform[:x] -= (viewport_size[:w] - transform[:w]) / 2
137
- transform[:y] -= (viewport_size[:h] - transform[:h]) / 2
138
- transform[:w] = viewport_size[:w]
139
- transform[:h] = viewport_size[:h]
140
-
141
- # alright—now our transform most likely extends beyond the bounds of the image
142
- # data. Let's add some constraints that push it within the bounds of the image.
143
- if (transform[:x] + transform[:w] > image_size[:w] * scale)
144
- transform[:x] = image_size[:w] * scale - transform[:w]
145
- end
146
-
147
- if (transform[:y] + transform[:h] > image_size[:h] * scale)
148
- transform[:y] = image_size[:h] * scale - transform[:h]
149
- end
150
-
151
- if (transform[:x] < 0)
152
- transform[:x] = 0.0
153
- end
154
-
155
- if (transform[:y] < 0)
156
- transform[:y] = 0.0
157
- end
158
-
159
- log.debug("The transform properties:")
160
- log.debug(transform)
161
-
162
- return transform
163
- end
164
-
165
- def run_transform(input_path, output_path, transform)
166
- # Call through to VIPS:
167
- # int im_affinei(in, out, interpolate, a, b, c, d, dx, dy, x, y, w, h)
168
- # The first six params are a transformation matrix. A and D are used for X and Y
169
- # scale, the other two are b = Y skew and c = X skew. TX and TY are translations
170
- # but don't seem to be used.
171
- # The last four params define a rect of the source image that is transformed.
172
- log.debug(output_path[-3..-1])
173
-
174
- if ((transform[:scale] < 0.5) && (output_path[-3..-1] == "jpg"))
175
- scale = transform[:scale]
176
- size = [transform[:w], transform[:h]].max
177
-
178
- transform[:scale] = scale * 2
179
- transform[:x] *= 2
180
- transform[:y] *= 2
181
- transform[:w] *= 2
182
- transform[:h] *= 2
183
-
184
-
185
- log.debug("Using two-pass transform")
186
- run("vips im_affinei '#{input_path}' '#{output_path}' bilinear #{transform[:scale]} 0 0 #{transform[:scale]} 0 0 #{transform[:x]} #{transform[:y]} #{transform[:w]} #{transform[:h]}")
187
- run("vipsthumbnail -s #{size} --nosharpen -o '%s_thumb.jpg:65' '#{output_path}'")
188
- FileUtils.mv(output_path[0..-5]+"_thumb.jpg", output_path)
189
-
190
- else
191
- run("vips im_affinei '#{input_path}' '#{output_path}' bilinear #{transform[:scale]} 0 0 #{transform[:scale]} 0 0 #{transform[:x]} #{transform[:y]} #{transform[:w]} #{transform[:h]}")
192
-
193
- end
194
-
195
- return output_path
196
- end
197
- end
198
- end
199
-
@@ -1,48 +0,0 @@
1
- require 'shellwords'
2
-
3
- module ImageResizer
4
- module Shell
5
-
6
- include Configurable
7
- configurable_attr :log_commands, true
8
-
9
- # Exceptions
10
- class CommandFailed < RuntimeError; end
11
-
12
- def run(command, args="")
13
- full_command = "#{command} #{escape_args(args)}"
14
- log.debug("Running command: #{full_command}") if log_commands
15
- begin
16
- result = `#{full_command}`
17
- rescue Errno::ENOENT
18
- raise_shell_command_failed(full_command)
19
- end
20
- if $?.exitstatus == 1
21
- throw :unable_to_handle
22
- elsif !$?.success?
23
- raise_shell_command_failed(full_command)
24
- end
25
- result
26
- end
27
-
28
- def raise_shell_command_failed(command)
29
- raise CommandFailed, "Command failed (#{command}) with exit status #{$?.exitstatus}"
30
- end
31
-
32
- def escape_args(args)
33
- args.shellsplit.map do |arg|
34
- quote arg.gsub(/\\?'/, %q('\\\\''))
35
- end.join(' ')
36
- end
37
-
38
- def quote(string)
39
- q = running_on_windows? ? '"' : "'"
40
- q + string + q
41
- end
42
-
43
- def running_on_windows?
44
- ENV['OS'] && ENV['OS'].downcase == 'windows_nt'
45
- end
46
-
47
- end
48
- end
@@ -1,479 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageResizer::Configurable do
4
-
5
- before(:each) do
6
- class Car
7
- include ImageResizer::Configurable
8
- configurable_attr :colour
9
- configurable_attr :top_speed, 216
10
- def self.other_thing=(thing); end
11
- end
12
- @car = Car.new
13
- end
14
-
15
- describe "setup" do
16
- it "should provide attr_readers for configurable attributes" do
17
- @car.should respond_to(:colour)
18
- end
19
-
20
- it "should provide attr_writers for configurable attributes" do
21
- @car.colour = 'verde'
22
- @car.colour.should == 'verde'
23
- end
24
-
25
- it "should set default values for configurable attributes" do
26
- @car.top_speed.should == 216
27
- end
28
-
29
- it "should set the default as nil if not specified" do
30
- @car.colour.should be_nil
31
- end
32
-
33
- it "should allow setting to nil" do
34
- @car.top_speed = nil
35
- @car.top_speed.should be_nil
36
- end
37
-
38
- it "should allow specifying configurable attrs as strings" do
39
- class Bike
40
- include ImageResizer::Configurable
41
- configurable_attr 'colour', 'rude'
42
- end
43
- Bike.new.colour.should == 'rude'
44
- end
45
- end
46
-
47
- describe "configuring" do
48
- it "should allow you to change values" do
49
- @car.configure do |c|
50
- c.colour = 'red'
51
- end
52
- @car.colour.should == 'red'
53
- end
54
-
55
- it "should not allow you to call other methods on the object via the configuration" do
56
- lambda{
57
- @car.configure do |c|
58
- c.other_thing = 5
59
- end
60
- }.should raise_error(ImageResizer::Configurable::BadConfigAttribute)
61
- end
62
-
63
- it "should return itself" do
64
- @car.configure{|c|}.should == @car
65
- end
66
- end
67
-
68
- describe "getting configuration" do
69
- it "should return the configuration when nothing is set" do
70
- @car.configuration.should == {}
71
- end
72
- it "should return the configuration when something is set" do
73
- @car.top_speed = 10
74
- @car.configuration.should == {:top_speed => 10}
75
- end
76
- end
77
-
78
- describe "multiple objects" do
79
- it "should return the default configuration" do
80
- Car.default_configuration.should == {:colour => nil, :top_speed => 216}
81
- end
82
- it "should allow instances to be configured differently" do
83
- car1 = Car.new
84
- car1.configure{|c| c.colour = 'green'}
85
- car2 = Car.new
86
- car2.configure{|c| c.colour = 'yellow'}
87
- car1.configuration.should == {:colour => 'green'}
88
- car2.configuration.should == {:colour => 'yellow'}
89
- end
90
- end
91
-
92
- describe "lazy attributes" do
93
- before(:each) do
94
- cow = @cow = mock('cow')
95
- class Lazy; end
96
- Lazy.class_eval do
97
- include ImageResizer::Configurable
98
- configurable_attr(:sound){ cow.moo }
99
- end
100
- @lazy = Lazy.new
101
- end
102
- it "should not call the block if the configurable attribute is set to something else" do
103
- @cow.should_not_receive(:moo)
104
- @lazy.configure{|c| c.sound = 'baa' }
105
- @lazy.sound.should == 'baa'
106
- end
107
- it "should call the block if it's not been changed, once it's accessed" do
108
- @cow.should_receive(:moo).and_return('mooo!')
109
- @lazy.sound.should == 'mooo!'
110
- end
111
- it "should not call the block when accessed again" do
112
- @cow.should_receive(:moo).exactly(:once).and_return('mooo!')
113
- @lazy.sound.should == 'mooo!'
114
- @lazy.sound.should == 'mooo!'
115
- end
116
- it "should not call an explicitly passed in proc" do
117
- @lazy.configure{|c| c.sound = lambda{ @cow.fart }}
118
- @lazy.sound.should be_a(Proc)
119
- end
120
- end
121
-
122
- describe "configuration method" do
123
-
124
- before(:each) do
125
- class ClassWithMethod
126
- include ImageResizer::Configurable
127
- def add_thing(thing)
128
- 'poo'
129
- end
130
- def remove_thing(thing)
131
- 'bum'
132
- end
133
- configuration_method :add_thing, :remove_thing
134
- end
135
- @thing = ClassWithMethod.new
136
- end
137
-
138
- it "should allow calling the method through 'configure'" do
139
- @thing.configure do |c|
140
- c.add_thing('duck')
141
- c.remove_thing('dog')
142
- end
143
- end
144
-
145
- end
146
-
147
- describe "nested configurable objects" do
148
-
149
- before(:each) do
150
- class NestedThing
151
- include ImageResizer::Configurable
152
- configurable_attr :age, 29
153
- def some_method(val)
154
- @some_thing = val
155
- end
156
- configuration_method :some_method
157
- attr_reader :some_thing
158
- end
159
-
160
- class Car
161
- def nested_thing
162
- @nested_thing ||= NestedThing.new
163
- end
164
- nested_configurable :nested_thing
165
- end
166
-
167
- @car.configure do |c|
168
- c.nested_thing.configure do |nt|
169
- nt.age = 50
170
- nt.some_method('yo')
171
- end
172
- end
173
- end
174
-
175
- it "should allow configuring nested configurable accessors" do
176
- @car.nested_thing.age.should == 50
177
- end
178
-
179
- it "should allow configuring nested configurable normal methods" do
180
- @car.nested_thing.some_thing.should == 'yo'
181
- end
182
-
183
- it "should not allow configuring directly on the config object" do
184
- expect{
185
- @car.configure do |c|
186
- c.some_method('other')
187
- end
188
- }.to raise_error(ImageResizer::Configurable::BadConfigAttribute)
189
- end
190
- end
191
-
192
- describe "configuring with a saved config" do
193
- before(:each) do
194
- @cool_configuration = Object.new
195
- def @cool_configuration.apply_configuration(car, colour=nil)
196
- car.configure do |c|
197
- c.colour = (colour || 'vermelho')
198
- end
199
- end
200
- end
201
-
202
- it "should allow configuration by a saved config" do
203
- @car.configure_with(@cool_configuration)
204
- @car.colour.should == 'vermelho'
205
- @car.top_speed.should == 216
206
- end
207
-
208
- it "should pass any args through to the saved config" do
209
- @car.configure_with(@cool_configuration, 'preto')
210
- @car.colour.should == 'preto'
211
- end
212
-
213
- it "should yield a block for any extra configuration" do
214
- @car.configure_with(@cool_configuration) do |c|
215
- c.colour = 'branco'
216
- end
217
- @car.colour.should == 'branco'
218
- end
219
-
220
- it "should return itself" do
221
- @car.configure_with(@cool_configuration).should == @car
222
- end
223
-
224
- describe "using a symbol to specify the config" do
225
-
226
- before(:all) do
227
- @rally_config = Object.new
228
- Car.register_configuration(:rally, @rally_config)
229
- @long_journey_config = Object.new
230
- Car.register_configuration(:long_journey){ @long_journey_config }
231
- Car.register_configuration(:some_library){ SomeLibrary }
232
- end
233
-
234
- it "should map the symbol to the correct configuration" do
235
- @rally_config.should_receive(:apply_configuration).with(@car)
236
- @car.configure_with(:rally)
237
- end
238
-
239
- it "should map the symbol to the correct configuration lazily" do
240
- @long_journey_config.should_receive(:apply_configuration).with(@car)
241
- @car.configure_with(:long_journey)
242
- end
243
-
244
- it "should throw an error if an unknown symbol is passed in" do
245
- lambda {
246
- @car.configure_with(:eggs)
247
- }.should raise_error(ArgumentError)
248
- end
249
-
250
- it "should only try to load the library when asked to" do
251
- lambda{
252
- @car.configure_with(:some_library)
253
- }.should raise_error(NameError, /uninitialized constant.*SomeLibrary/)
254
- end
255
- end
256
-
257
- end
258
-
259
- describe "falling back to another config" do
260
- before(:each) do
261
- class Garage
262
- include ImageResizer::Configurable
263
- configurable_attr :top_speed, 100
264
- end
265
- @garage = Garage.new
266
- @car.use_as_fallback_config(@garage)
267
- end
268
-
269
- describe "when nothing set" do
270
- it "should use its default" do
271
- @car.top_speed.should == 216
272
- end
273
- it "shouldn't affect the fallback config object" do
274
- @garage.top_speed.should == 100
275
- end
276
- end
277
-
278
- describe "if set" do
279
- before(:each) do
280
- @car.top_speed = 444
281
- end
282
- it "should work normally" do
283
- @car.top_speed.should == 444
284
- end
285
- it "shouldn't affect the fallback config object" do
286
- @garage.top_speed.should == 100
287
- end
288
- end
289
-
290
- describe "both set" do
291
- before(:each) do
292
- @car.top_speed = 444
293
- @garage.top_speed = 3000
294
- end
295
- it "should prefer its own setting" do
296
- @car.top_speed.should == 444
297
- end
298
- it "shouldn't affect the fallback config object" do
299
- @garage.top_speed.should == 3000
300
- end
301
- end
302
-
303
- describe "the fallback config is set" do
304
- before(:each) do
305
- @garage.top_speed = 3000
306
- end
307
- it "should use the fallback config" do
308
- @car.top_speed.should == 3000
309
- end
310
- it "shouldn't affect the fallback config object" do
311
- @garage.top_speed.should == 3000
312
- end
313
- end
314
-
315
- describe "falling back multiple levels" do
316
- before(:each) do
317
- @klass = Class.new
318
- @klass.class_eval do
319
- include ImageResizer::Configurable
320
- configurable_attr :veg, 'carrot'
321
- end
322
- @a = @klass.new
323
- @b = @klass.new
324
- @b.use_as_fallback_config(@a)
325
- @c = @klass.new
326
- @c.use_as_fallback_config(@b)
327
- end
328
-
329
- it "should be the default if nothing set" do
330
- @c.veg.should == 'carrot'
331
- end
332
-
333
- it "should fall all the way back to the top one if necessary" do
334
- @a.veg = 'turnip'
335
- @c.veg.should == 'turnip'
336
- end
337
-
338
- it "should prefer the closer one over the further away one" do
339
- @b.veg = 'tatty'
340
- @a.veg = 'turnip'
341
- @c.veg.should == 'tatty'
342
- end
343
-
344
- it "should work properly with nils" do
345
- @a.veg = nil
346
- @c.veg = 'broc'
347
- @a.veg.should be_nil
348
- @b.veg.should be_nil
349
- @c.veg.should == 'broc'
350
- end
351
- end
352
-
353
- describe "objects with different methods" do
354
- before(:each) do
355
- class Dad
356
- include ImageResizer::Configurable
357
- end
358
- @dad = Dad.new
359
- class Kid
360
- include ImageResizer::Configurable
361
- configurable_attr :lug, 'default-lug'
362
- end
363
- @kid = Kid.new
364
- @kid.use_as_fallback_config(@dad)
365
- end
366
-
367
- it "should not allow setting on the fallback obj directly" do
368
- lambda{
369
- @dad.lug = 'leg'
370
- }.should raise_error(NoMethodError)
371
- end
372
-
373
- it "should not have the fallback obj respond to the method" do
374
- @dad.should_not respond_to(:lug=)
375
- end
376
-
377
- it "should allow configuring through the fallback object even if it doesn't have that method" do
378
- @dad.configure do |c|
379
- c.lug = 'leg'
380
- end
381
- @kid.lug.should == 'leg'
382
- end
383
-
384
- it "should work when a grandchild config is added later" do
385
- class Grandkid
386
- include ImageResizer::Configurable
387
- configurable_attr :oogie, 'boogie'
388
- end
389
- grandkid = Grandkid.new
390
- grandkid.use_as_fallback_config(@kid)
391
- @dad.configure{|c| c.oogie = 'duggen' }
392
- grandkid.oogie.should == 'duggen'
393
- end
394
-
395
- it "should allow configuring twice through the fallback object" do
396
- @dad.configure{|c| c.lug = 'leg' }
397
- @dad.configure{|c| c.lug = 'blug' }
398
- @kid.lug.should == 'blug'
399
- end
400
- end
401
-
402
- describe "clashing with configurable modules" do
403
- before(:each) do
404
- @mod = mod = Module.new
405
- @mod.module_eval do
406
- include ImageResizer::Configurable
407
- configurable_attr :team, 'spurs'
408
- end
409
- @class = Class.new
410
- @class.class_eval do
411
- include mod
412
- include ImageResizer::Configurable
413
- configurable_attr :tree, 'elm'
414
- end
415
- end
416
-
417
- it "should not override the defaults from the module" do
418
- obj = @class.new
419
- obj.team.should == 'spurs'
420
- end
421
-
422
- it "should still use its own defaults" do
423
- obj = @class.new
424
- obj.tree.should == 'elm'
425
- end
426
-
427
- describe "when the configurable_attr is specified in a subclass that doesn't include Configurable" do
428
- before(:each) do
429
- @subclass = Class.new(@class)
430
- @subclass.class_eval do
431
- configurable_attr :car, 'mazda'
432
- configurable_attr :tree, 'oak'
433
- end
434
- @obj = @subclass.new
435
- end
436
-
437
- it "should still work with default values" do
438
- @obj.car.should == 'mazda'
439
- end
440
-
441
- it "should override the default from the parent" do
442
- @obj.tree.should == 'oak'
443
- end
444
- end
445
-
446
- end
447
-
448
- end
449
-
450
- describe "inheriting configurable_attrs from multiple places" do
451
- before(:each) do
452
- module A
453
- include ImageResizer::Configurable
454
- configurable_attr :a
455
- end
456
- module B
457
- include ImageResizer::Configurable
458
- configurable_attr :b
459
- end
460
- class K
461
- include ImageResizer::Configurable
462
- include A
463
- include B
464
- configurable_attr :c
465
- end
466
- class L < K
467
- end
468
- end
469
-
470
- it "should include configuration from all of its mixins" do
471
- l = L.new
472
- l.configure do |c|
473
- c.a = 'something'
474
- c.b = 'something'
475
- c.c = 'something'
476
- end
477
- end
478
- end
479
- end
@@ -1,80 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class Testoast
4
- include ImageResizer::Loggable
5
- end
6
-
7
- describe ImageResizer::Loggable do
8
-
9
- before(:each) do
10
- @object = Testoast.new
11
- end
12
-
13
- shared_examples_for "common" do
14
- it "should return a log" do
15
- @object.log.should be_a(Logger)
16
- end
17
- it "should cache the log" do
18
- @object.log.should == @object.log
19
- end
20
- end
21
-
22
- describe "without being set" do
23
- it "should return the log object as nil" do
24
- @object.log_object.should be_nil
25
- end
26
- it_should_behave_like 'common'
27
- end
28
-
29
- describe "when set" do
30
- before(:each) do
31
- @log = Logger.new($stdout)
32
- @object.log = @log
33
- end
34
- it "should return the new log" do
35
- @object.log.should == @log
36
- end
37
- it "should return the log object" do
38
- @object.log_object.should == @log
39
- end
40
- it_should_behave_like 'common'
41
- end
42
-
43
- describe "when set as a proc" do
44
- before(:each) do
45
- @log = Logger.new($stdout)
46
- @object.log = proc{ @log }
47
- end
48
- it "should return the new log" do
49
- @object.log.should == @log
50
- end
51
- it "should return the log object" do
52
- @object.log_object.should be_a(Proc)
53
- end
54
- it "should allow for changing logs" do
55
- logs = [@log]
56
- @object.log = proc{ logs[0] }
57
- @object.log.should == @log
58
-
59
- new_log = Logger.new($stdout)
60
- logs[0] = new_log
61
-
62
- @object.log.should == new_log
63
- end
64
- it_should_behave_like 'common'
65
- end
66
-
67
- describe "sharing logs" do
68
- before(:each) do
69
- @log = Logger.new($stdout)
70
- @obj1 = Testoast.new
71
- @obj2 = Testoast.new
72
- end
73
- it "should enable sharing logs" do
74
- @obj1.log = proc{ @log }
75
- @obj2.use_same_log_as(@obj1)
76
- @obj2.log.should == @log
77
- end
78
- end
79
-
80
- end
@@ -1,144 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageResizer::Processor do
4
-
5
- # IMPORTANT: Enable the flag below to print out the transforms that are generated by calculate_transform
6
- # as you run the tests. This is great for updating the expected_results array below if you change the
7
- # function and desire different behavior for the inputs, or add other test viewports of POIs.
8
- actual_printed = false
9
- actual_results = []
10
-
11
- interest_variants_fractional = [{xf: 0, yf: 0}, {xf: 0, yf: 1}, {xf: 1, yf: 1}, {xf: 1, yf: 0}, {xf: 0.5, yf: 0.5}]
12
- interest_variants_absolute = [{x: 0, y: 0}, {x: 0, y: 2500}, {x: 3800, y: 2500}, {x: 3800, y: 0}, {x: 3800.0 * 0.5, y: 2500.0 * 0.5}]
13
- viewport_variants = [{w: 3800, h: 3000},
14
- {w: 5000, h: 5000},
15
- {w: 100, h: 100},
16
- {w: 100, h: 300},
17
- {w: 300, h: 100},
18
- {w: 100},
19
- {h: 100},
20
- {w: 3000},
21
- {h: 3000},
22
- {}]
23
-
24
- expected_abs_index = 0
25
- expected_fraction_index = 0
26
-
27
- expected_results = [{:x=>0.0, :y=>0.0, :w=>3800, :h=>3000, :scale=>1.2},
28
- {:x=>0.0, :y=>0.0, :w=>5000, :h=>5000, :scale=>2.0},
29
- {:x=>0.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
30
- {:x=>0.0, :y=>0.0, :w=>100, :h=>300, :scale=>0.12},
31
- {:x=>0.0, :y=>0.0, :w=>300, :h=>100, :scale=>0.07894736842105263},
32
- {:x=>0.0, :y=>0.0, :w=>100, :h=>0, :scale=>0.02631578947368421},
33
- {:x=>0.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
34
- {:x=>0.0, :y=>0.0, :w=>3000, :h=>0, :scale=>0.7894736842105263},
35
- {:x=>0.0, :y=>0.0, :w=>3000, :h=>3000, :scale=>1.2},
36
- {:x=>0.0, :y=>0.0, :w=>3800, :h=>2500, :scale=>1.0},
37
- {:x=>0.0, :y=>0.0, :w=>3800, :h=>3000, :scale=>1.2},
38
- {:x=>0.0, :y=>0.0, :w=>5000, :h=>5000, :scale=>2.0},
39
- {:x=>0.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
40
- {:x=>0.0, :y=>0.0, :w=>100, :h=>300, :scale=>0.12},
41
- {:x=>0.0, :y=>97.36842105263156, :w=>300, :h=>100, :scale=>0.07894736842105263},
42
- {:x=>0.0, :y=>65.78947368421052, :w=>100, :h=>0, :scale=>0.02631578947368421},
43
- {:x=>0.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
44
- {:x=>0.0, :y=>1973.6842105263158, :w=>3000, :h=>0, :scale=>0.7894736842105263},
45
- {:x=>0.0, :y=>0.0, :w=>3000, :h=>3000, :scale=>1.2},
46
- {:x=>0.0, :y=>0.0, :w=>3800, :h=>2500, :scale=>1.0},
47
- {:x=>760.0, :y=>0.0, :w=>3800, :h=>3000, :scale=>1.2},
48
- {:x=>2600.0, :y=>0.0, :w=>5000, :h=>5000, :scale=>2.0},
49
- {:x=>52.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
50
- {:x=>356.0, :y=>0.0, :w=>100, :h=>300, :scale=>0.12},
51
- {:x=>0.0, :y=>97.36842105263156, :w=>300, :h=>100, :scale=>0.07894736842105263},
52
- {:x=>0.0, :y=>65.78947368421052, :w=>100, :h=>0, :scale=>0.02631578947368421},
53
- {:x=>52.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
54
- {:x=>0.0, :y=>1973.6842105263158, :w=>3000, :h=>0, :scale=>0.7894736842105263},
55
- {:x=>1560.0, :y=>0.0, :w=>3000, :h=>3000, :scale=>1.2},
56
- {:x=>0.0, :y=>0.0, :w=>3800, :h=>2500, :scale=>1.0},
57
- {:x=>760.0, :y=>0.0, :w=>3800, :h=>3000, :scale=>1.2},
58
- {:x=>2600.0, :y=>0.0, :w=>5000, :h=>5000, :scale=>2.0},
59
- {:x=>52.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
60
- {:x=>356.0, :y=>0.0, :w=>100, :h=>300, :scale=>0.12},
61
- {:x=>0.0, :y=>0.0, :w=>300, :h=>100, :scale=>0.07894736842105263},
62
- {:x=>0.0, :y=>0.0, :w=>100, :h=>0, :scale=>0.02631578947368421},
63
- {:x=>52.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
64
- {:x=>0.0, :y=>0.0, :w=>3000, :h=>0, :scale=>0.7894736842105263},
65
- {:x=>1560.0, :y=>0.0, :w=>3000, :h=>3000, :scale=>1.2},
66
- {:x=>0.0, :y=>0.0, :w=>3800, :h=>2500, :scale=>1.0},
67
- {:x=>380.0, :y=>0.0, :w=>3800, :h=>3000, :scale=>1.2},
68
- {:x=>1300.0, :y=>0.0, :w=>5000, :h=>5000, :scale=>2.0},
69
- {:x=>26.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
70
- {:x=>178.0, :y=>0.0, :w=>100, :h=>300, :scale=>0.12},
71
- {:x=>0.0, :y=>48.68421052631578, :w=>300, :h=>100, :scale=>0.07894736842105263},
72
- {:x=>0.0, :y=>32.89473684210526, :w=>100, :h=>0, :scale=>0.02631578947368421},
73
- {:x=>26.0, :y=>0.0, :w=>100, :h=>100, :scale=>0.04},
74
- {:x=>0.0, :y=>986.8421052631579, :w=>3000, :h=>0, :scale=>0.7894736842105263},
75
- {:x=>780.0, :y=>0.0, :w=>3000, :h=>3000, :scale=>1.2},
76
- {:x=>0.0, :y=>0.0, :w=>3800, :h=>2500, :scale=>1.0}]
77
-
78
- before(:each) do
79
- @source_jpg_path = SAMPLES_DIR.join('test.jpg') # 3872, 2592
80
- @source_png_path = SAMPLES_DIR.join('test.png') # 177, 180
81
- @source_jpg_props = {};
82
- @source_jpg_props['width'] = 3800
83
- @source_jpg_props['height'] = 2500
84
- @source_jpg_props['bands'] = 3
85
-
86
-
87
- @processor = ImageResizer::Processor.new
88
- end
89
-
90
- describe "fetch_image_properties" do
91
-
92
- it "should return a hash of properties containing width and height" do
93
- properties = @processor.fetch_image_properties(@source_jpg_path)
94
- properties['width'].should == @source_jpg_props['width']
95
- properties['height'].should == @source_jpg_props['height']
96
- end
97
-
98
- it "should return a hash of properties containing depth (JPG=3)" do
99
- properties = @processor.fetch_image_properties(@source_jpg_path)
100
- properties['bands'].should == 3
101
- end
102
-
103
- it "should return a hash of properties containing depth (PNG=4)" do
104
- properties = @processor.fetch_image_properties(@source_png_path)
105
- properties['bands'].should == 4
106
- end
107
-
108
- describe "calculate_transform" do
109
-
110
- interest_variants_absolute.each do |ip|
111
- viewport_variants.each do |viewport|
112
-
113
- it "should produce the correct transform viewport: #{viewport}, point: #{ip}" do
114
- transform = @processor.calculate_transform(@source_jpg_path, @source_jpg_props, viewport, ip)
115
- transform.should == expected_results[expected_abs_index]
116
- expected_abs_index += 1
117
- end
118
-
119
- end
120
- end
121
-
122
- interest_variants_fractional.each do |ip|
123
- viewport_variants.each do |viewport|
124
-
125
- it "should produce the correct transform viewport: #{viewport}, point: #{ip}" do
126
- transform = @processor.calculate_transform(@source_jpg_path, @source_jpg_props, viewport, ip)
127
-
128
- if (actual_printed == true)
129
- actual_results.push(transform)
130
- if (actual_results.size() == interest_variants_fractional.size() * viewport_variants.size() - 1)
131
- print actual_results
132
- end
133
- end
134
-
135
- transform.should == expected_results[expected_fraction_index]
136
- expected_fraction_index += 1
137
- end
138
-
139
- end
140
- end
141
-
142
- end
143
- end
144
- end
@@ -1,34 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageResizer::Shell do
4
-
5
- include ImageResizer::Shell
6
-
7
- it "should raise an error if the identify command isn't found" do
8
- suppressing_stderr do
9
- lambda{
10
- run "non-existent-command"
11
- }.should raise_error(ImageResizer::Shell::CommandFailed)
12
- end
13
- end
14
-
15
- describe "escaping args" do
16
- {
17
- %q(hello) => %q('hello'),
18
- %q("hello") => %q('hello'),
19
- %q('hello') => %q('hello'),
20
- %q(he\'llo) => %q('he'\''llo'),
21
- %q('he'\''llo') => %q('he'\''llo'),
22
- %q("he'llo") => %q('he'\''llo'),
23
- %q(hel$(lo)) => %q('hel$(lo)'),
24
- %q(hel\$(lo)) => %q('hel$(lo)'),
25
- %q('hel\$(lo)') => %q('hel\$(lo)')
26
- }.each do |args, escaped_args|
27
- it "should escape #{args.inspect} -> #{escaped_args.inspect}" do
28
- pending "not applicable to windows" if running_on_windows?
29
- escape_args(args).should == escaped_args
30
- end
31
- end
32
- end
33
-
34
- end