insano_image_resizer 0.3.0 → 0.3.1

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/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