insano_image_resizer 0.3.1 → 0.3.3

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.
@@ -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