insano_image_resizer 0.3.1 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+
2
+ require 'insano_image_resizer/loggable'
3
+ require 'insano_image_resizer/configurable'
4
+ require 'insano_image_resizer/shell'
5
+ require 'insano_image_resizer/processor'
@@ -0,0 +1,206 @@
1
+ module InsanoImageResizer
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
@@ -0,0 +1,28 @@
1
+ require 'logger'
2
+
3
+ module InsanoImageResizer
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
@@ -0,0 +1,199 @@
1
+ require 'yaml'
2
+ require 'shellwords'
3
+
4
+ module InsanoImageResizer
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
+
@@ -0,0 +1,48 @@
1
+ require 'shellwords'
2
+
3
+ module InsanoImageResizer
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
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.1
4
+ version: 0.3.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -103,18 +103,13 @@ extra_rdoc_files:
103
103
  - LICENSE
104
104
  - README.md
105
105
  files:
106
- - .rspec
107
- - .rvmrc
108
- - Gemfile
109
- - Gemfile.lock
106
+ - lib/insano_image_resizer.rb
107
+ - lib/insano_image_resizer/configurable.rb
108
+ - lib/insano_image_resizer/loggable.rb
109
+ - lib/insano_image_resizer/processor.rb
110
+ - lib/insano_image_resizer/shell.rb
110
111
  - LICENSE
111
112
  - README.md
112
- - Rakefile
113
- - VERSION
114
- - samples/explanation.png
115
- - samples/test.jpg
116
- - samples/test.png
117
- - spec/spec_helper.rb
118
113
  homepage: http://github.com/populr/insano_image_resizer
119
114
  licenses:
120
115
  - MIT
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --color
data/.rvmrc DELETED
@@ -1 +0,0 @@
1
- rvm use 1.9.3@insano_image_resizer
data/Gemfile DELETED
@@ -1,9 +0,0 @@
1
- source :rubygems
2
-
3
- group :development, :test do
4
- gem 'rspec'
5
- gem 'jeweler'
6
- gem 'pry'
7
- gem 'pry-nav'
8
- gem 'pry-stack_explorer'
9
- end
data/Gemfile.lock DELETED
@@ -1,45 +0,0 @@
1
- GEM
2
- remote: http://rubygems.org/
3
- specs:
4
- binding_of_caller (0.6.7)
5
- coderay (1.0.6)
6
- diff-lcs (1.1.3)
7
- git (1.2.5)
8
- jeweler (1.8.3)
9
- bundler (~> 1.0)
10
- git (>= 1.2.5)
11
- rake
12
- rdoc
13
- json (1.6.6)
14
- method_source (0.7.1)
15
- pry (0.9.8.4)
16
- coderay (~> 1.0.5)
17
- method_source (~> 0.7.1)
18
- slop (>= 2.4.4, < 3)
19
- pry-nav (0.2.0)
20
- pry (~> 0.9.8.1)
21
- pry-stack_explorer (0.4.1)
22
- binding_of_caller (~> 0.6.2)
23
- pry (~> 0.9.8.2)
24
- rake (0.9.2.2)
25
- rdoc (3.12)
26
- json (~> 1.4)
27
- rspec (2.9.0)
28
- rspec-core (~> 2.9.0)
29
- rspec-expectations (~> 2.9.0)
30
- rspec-mocks (~> 2.9.0)
31
- rspec-core (2.9.0)
32
- rspec-expectations (2.9.1)
33
- diff-lcs (~> 1.1.3)
34
- rspec-mocks (2.9.0)
35
- slop (2.4.4)
36
-
37
- PLATFORMS
38
- ruby
39
-
40
- DEPENDENCIES
41
- jeweler
42
- pry
43
- pry-nav
44
- pry-stack_explorer
45
- rspec
data/Rakefile DELETED
@@ -1,56 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'bundler'
5
- begin
6
- Bundler.setup(:default, :development)
7
- rescue Bundler::BundlerError => e
8
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
10
- exit e.status_code
11
- end
12
- require 'rake'
13
-
14
- require 'jeweler'
15
- Jeweler::Tasks.new do |gem|
16
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
- gem.name = "insano_image_resizer"
18
- gem.homepage = "http://github.com/populr/insano_image_resizer"
19
- gem.license = "MIT"
20
- gem.summary = %Q{An image resizing gem that calls through to VIPS on the command line.}
21
- gem.description = %Q{An image resizing gem that generates smaller versions of requested
22
- images. Calls through to VIPS on the command line to perform processing,
23
- and automatically handles cropping and scaling the requested image, taking
24
- a point of interest into account if requested.}
25
- gem.email = "ben@populr.me"
26
- gem.authors = ["Ben Gotow"]
27
- # dependencies defined in Gemfile
28
- gem.files.exclude 'spec'
29
- gem.files.exclude 'samples'
30
- gem.files.exclude 'tmp'
31
- gem.files.exclude 'demo.rb'
32
- end
33
- Jeweler::RubygemsDotOrgTasks.new
34
-
35
- require 'rspec/core'
36
- require 'rspec/core/rake_task'
37
- RSpec::Core::RakeTask.new(:spec) do |spec|
38
- spec.pattern = FileList['spec/**/*_spec.rb']
39
- end
40
-
41
- RSpec::Core::RakeTask.new(:rcov) do |spec|
42
- spec.pattern = 'spec/**/*_spec.rb'
43
- spec.rcov = true
44
- end
45
-
46
- task :default => :spec
47
-
48
- require 'rdoc/task'
49
- Rake::RDocTask.new do |rdoc|
50
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
51
-
52
- rdoc.rdoc_dir = 'rdoc'
53
- rdoc.title = "image_resizer #{version}"
54
- rdoc.rdoc_files.include('README*')
55
- rdoc.rdoc_files.include('lib/**/*.rb')
56
- end
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.3.1
Binary file
data/samples/test.jpg DELETED
Binary file
data/samples/test.png DELETED
Binary file
data/spec/spec_helper.rb DELETED
@@ -1,61 +0,0 @@
1
- require "rubygems"
2
- require "bundler"
3
- Bundler.setup(:default, :test)
4
-
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
- $LOAD_PATH.unshift(File.dirname(__FILE__))
7
- require 'rspec'
8
- require 'insano_image_resizer'
9
- require 'fileutils'
10
- require 'pry'
11
- require 'pry-nav'
12
- require 'pry-stack_explorer'
13
-
14
- # Requires supporting files with custom matchers and macros, etc,
15
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
16
-
17
- SAMPLES_DIR = Pathname.new(File.expand_path(File.dirname(__FILE__) + '/../samples')) unless defined?(SAMPLES_DIR)
18
-
19
- def todo
20
- raise "TODO"
21
- end
22
-
23
-
24
- # require 'logger'
25
- # LOG_FILE = 'tmp/test.log' unless defined?(LOG_FILE)
26
- # FileUtils.rm_rf(LOG_FILE)
27
- # def mock_app(extra_stubs={})
28
- # mock('app', {
29
- # :datastore => mock('datastore', :store => 'some_uid', :retrieve => ["SOME_DATA", {}], :destroy => nil),
30
- # :processor => mock('processor', :process => "SOME_PROCESSED_DATA"),
31
- # :encoder => mock('encoder', :encode => "SOME_ENCODED_DATA"),
32
- # :analyzer => mock('analyzer', :analyse => "some_result", :analysis_methods => Module.new),
33
- # :generator => mock('generator', :generate => "SOME_GENERATED_DATA"),
34
- # :log => Logger.new(LOG_FILE),
35
- # :cache_duration => 10000,
36
- # :job_definitions => Module.new
37
- # }.merge(extra_stubs)
38
- # )
39
- # end
40
-
41
- # def test_app
42
- # time = Time.now
43
- # app = ImageResizer::App.send(:new, "test_#{time.sec}_#{time.usec}".to_sym)
44
- # app.log = Logger.new(LOG_FILE)
45
- # app.datastore.root_path = 'tmp/file_data_store_test'
46
- # app
47
- # end
48
-
49
- def suppressing_stderr
50
- original_stderr = $stderr.dup
51
- tempfile = Tempfile.new('stderr')
52
- $stderr.reopen(tempfile) rescue
53
- yield
54
- ensure
55
- tempfile.close!
56
- $stderr.reopen(original_stderr)
57
- end
58
-
59
- def running_on_windows?
60
- ENV['OS'] && ENV['OS'].downcase == 'windows_nt'
61
- end