image_vise 0.4.0 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 15bac8714b52ca8cc0a2b57037ffb1db1ac85216
4
- data.tar.gz: 1ff26e91dcea4e222f98caf695d92096822c2df1
2
+ SHA256:
3
+ metadata.gz: b65aacfbdbada4bb3d52c9883955ff2204ee84aa66bc82d05fc81320259ac900
4
+ data.tar.gz: d8576cfba12f8793a75dc36f8a5c2c206f7650f402d048432f763de82ddeb0b0
5
5
  SHA512:
6
- metadata.gz: 262b64839c5aceb9230ef958629535248a60a4f62ca4ef8cc7d923419ebdaec7491fb5b8978795212b81d8231d9d0ea3575972fe72a4eaf7ebd7b866b506387f
7
- data.tar.gz: 7ef5cf55c0893907abb72cede01ddfc6a0cc824d5ebf0c0112df2789c1a0453ee03182311766372e10dd5db9d69885c89c6b30c28798b067802873c859b6e0f8
6
+ metadata.gz: 767f28b82f290cfefdf023f83a6794901744d585eff9f0f2af92a6c408718b4663162156270d51f0ea7def4de2e14339a0bc0183b80ba62d4d16f11b69359e11
7
+ data.tar.gz: bb8534e292d19e51015b24586d0db5c472da2b02c3463180c35f42d8cff1d2d39aca875d5446255ea5b67b1bf59f849dda08065792438324a6ed3c9a753d9549
data/.travis.yml CHANGED
@@ -1,12 +1,13 @@
1
1
  rvm:
2
- - 2.2
3
- - 2.3.0
4
- - 2.4.1
2
+ - 2.2.10
3
+ - 2.3.7
4
+ - 2.4.4
5
+ - 2.5.1
5
6
  sudo: false
6
7
  cache: bundler
7
8
 
8
9
  env:
9
10
  global:
10
11
  - SKIP_INTERACTIVE=yes
11
-
12
+ before_install: gem install bundler
12
13
  script: bundle exec rspec
@@ -1,7 +1,7 @@
1
1
  # Anywhere in your app code
2
2
  module ImageViseAppsignal
3
3
  ImageVise::RenderEngine.prepend self
4
-
4
+ ImageVise::Measurometer.drivers << Appsignal # to obtain ImageVise instrumentation
5
5
  def setup_error_handling(rack_env)
6
6
  txn = Appsignal::Transaction.current
7
7
  txn.set_action('%s#%s' % [self.class, 'call'])
data/image_vise.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  else
22
22
  raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
23
  end
24
-
24
+
25
25
  spec.files = `git ls-files -z`.split("\x0")
26
26
  spec.bindir = "exe"
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -14,9 +14,12 @@ class ImageVise::ImageRequest < Ks.strict(:src_url, :pipeline)
14
14
 
15
15
  # Check the signature before decoding JSON (since we will be creating symbols)
16
16
  unless valid_signature?(base64_encoded_params, given_signature, secrets)
17
+ ImageVise::Measurometer.increment_counter('image_vise.params.invalid_signatures', 1)
17
18
  raise SignatureError, "Invalid or missing signature"
18
19
  end
19
20
 
21
+ ImageVise::Measurometer.increment_counter('image_vise.params.valid_signatures', 1)
22
+
20
23
  # Decode the JSON - only AFTER the signature has been validated,
21
24
  # so we can use symbol keys
22
25
  decoded_json = Base64.decode64(base64_encoded_params)
@@ -46,7 +46,10 @@ class ImageVise::Pipeline
46
46
 
47
47
  def apply!(magick_image, image_metadata)
48
48
  @ops.each do |operator|
49
- apply_operator_passing_metadata(magick_image, operator, image_metadata)
49
+ operator_short_classname = operator.class.to_s.split('::').pop
50
+ ImageVise::Measurometer.instrument('image_vise.op.%s' % operator_short_classname) do
51
+ apply_operator_passing_metadata(magick_image, operator, image_metadata)
52
+ end
50
53
  end
51
54
  end
52
55
 
@@ -283,7 +283,10 @@
283
283
  render_file_type = source_file_type
284
284
 
285
285
  # Load the first frame of the animated GIF _or_ the blended compatibility layer from Photoshop
286
- image_list = Magick::Image.read(source_file_path)
286
+ image_list = ImageVise::Measurometer.instrument('image_vise.load_pixbuf') do
287
+ Magick::Image.read(source_file_path)
288
+ end
289
+
287
290
  magick_image = image_list.first # Picks up the "precomp" PSD layer in compatibility mode, or the first frame of a GIF
288
291
 
289
292
  # If any operators want to stash some data for downstream use we use this Hash
@@ -297,7 +300,9 @@
297
300
  # it so that we get a KeyError if some operator has deleted it without providing a replacement.
298
301
  # If no operators touched the writer we are going to use the automatic format selection
299
302
  writer = metadata.fetch(:writer, ImageVise::AutoWriter.new)
300
- writer.write_image!(magick_image, metadata, render_to_path)
303
+ ImageVise::Measurometer.instrument('image_vise.write_image') do
304
+ writer.write_image!(magick_image, metadata, render_to_path)
305
+ end
301
306
 
302
307
  # Another metadata element is the expire_after, which we default to an app-wide setting
303
308
  metadata.fetch(:expire_after_seconds, ImageVise.cache_lifetime_seconds)
@@ -1,3 +1,3 @@
1
1
  class ImageVise
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
data/lib/image_vise.rb CHANGED
@@ -9,8 +9,10 @@ require 'rack'
9
9
 
10
10
  class ImageVise
11
11
  require_relative 'image_vise/version'
12
+
12
13
  S_MUTEX = Mutex.new
13
14
  private_constant :S_MUTEX
15
+
14
16
  # The default cache liftime is 30 days, and will be used if no custom lifetime is set.
15
17
  DEFAULT_CACHE_LIFETIME = 2_592_000
16
18
 
@@ -158,7 +160,9 @@ class ImageVise
158
160
  return unless maybe_image
159
161
  return unless maybe_image.respond_to?(:destroy!)
160
162
  return if maybe_image.destroyed?
161
- maybe_image.destroy!
163
+ ImageVise::Measurometer.instrument('image_vise.image_destroy_dealloc') do
164
+ maybe_image.destroy!
165
+ end
162
166
  end
163
167
 
164
168
  # Used as a shorthand to force-dealloc Tempfiles in an ensure() blocks. Since
@@ -166,8 +170,10 @@ class ImageVise
166
170
  # in scope but not yet set to an image) we take the possibility of nils into account.
167
171
  def self.close_and_unlink(maybe_tempfile)
168
172
  return unless maybe_tempfile
169
- maybe_tempfile.close unless maybe_tempfile.closed?
170
- maybe_tempfile.unlink
173
+ ImageVise::Measurometer.instrument('image_vise.tempfile_unlink') do
174
+ maybe_tempfile.close unless maybe_tempfile.closed?
175
+ maybe_tempfile.unlink
176
+ end
171
177
  end
172
178
  end
173
179
 
@@ -0,0 +1,92 @@
1
+ # Measurometer is 1-1 API compatible with Appsignal,
2
+ # which we use a lot
3
+ class ImageVise::Measurometer
4
+ class << self
5
+ # Permits adding instrumentation drivers. To magically
6
+ # obtain all Appsignal instrumentation, add the Appsignal module
7
+ # as a driver.
8
+ #
9
+ # Measurometer.drivers << Appsignal
10
+ #
11
+ # A driver must be reentrant and thread-safe - it should be possible
12
+ # to have multiple `instrument` calls open from different threads at the
13
+ # same time.
14
+ #
15
+ # The driver must support the same interface as the Measurometer class
16
+ # itself, minus the `drivers` and `instrument_instance_method` methods.
17
+ #
18
+ # @return Array
19
+ def drivers
20
+ @drivers ||= []
21
+ @drivers
22
+ end
23
+
24
+ # Runs a given block within a cascade of `instrument` blocks of all the
25
+ # added drivers.
26
+ #
27
+ # Measurometer.instrument('do_foo') { compute! }
28
+ #
29
+ # unfolds to
30
+ #
31
+ # Appsignal.instrument('do_foo') do
32
+ # Statsd.timing('do_foo') do
33
+ # compute!
34
+ # end
35
+ # end
36
+ #
37
+ # Note that it is _imperative_ that the block return value is preserved
38
+ # by the drivers and passed as the result of the block.
39
+ #
40
+ # @param block_name[String] under which path to push the metric
41
+ # @param blk[#call] the block to instrument
42
+ # @return [Object] the return value of &blk
43
+ def instrument(block_name, &blk)
44
+ return yield unless @drivers && @drivers.any? # The block wrapping business is not free
45
+ @drivers.inject(blk) { |outer_block, driver|
46
+ -> {
47
+ driver.instrument(block_name, &outer_block)
48
+ }
49
+ }.call
50
+ end
51
+
52
+ # Adds a distribution value (sample) under a given path
53
+ #
54
+ # @param value_path[String] under which path to push the metric
55
+ # @param value[Numeric] distribution value
56
+ # @return nil
57
+ def add_distribution_value(value_path, value)
58
+ (@drivers || []).each { |d| d.add_distribution_value(value_path, value) }
59
+ nil
60
+ end
61
+
62
+ # Increment a named counter under a given path
63
+ #
64
+ # @param counter_path[String] under which path to push the metric
65
+ # @param by[Integer] the counter increment to apply
66
+ # @return nil
67
+ def increment_counter(counter_path, by)
68
+ (@drivers || []).each { |d| d.increment_counter(counter_path, by) }
69
+ nil
70
+ end
71
+
72
+ # Wrap an anonymous module around an instance method in the given class to have
73
+ # it instrumented automatically. The name of the measurement will be interpolated as:
74
+ #
75
+ # "#{prefix}.#{rightmost_class_constant_name}.#{instance_method_name}"
76
+ #
77
+ # @param target_class[Class] the class to instrument
78
+ # @param instance_method_name_to_instrument[Symbol] the method name to instrument
79
+ # @param path_prefix[String] under which path to push the instrumented metric
80
+ # @return void
81
+ def instrument_instance_method(target_class, instance_method_name_to_instrument, path_prefix)
82
+ short_class_name = target_class.to_s.split('::').last
83
+ instrumentation_name = [path_prefix, short_class_name, instance_method_name_to_instrument].join('.')
84
+ instrumenter_module = Module.new do
85
+ define_method(instance_method_name_to_instrument) do |*any|
86
+ ::ImageVise::Measurometer.instrument(instrumentation_name) { super(*any) }
87
+ end
88
+ end
89
+ target_class.prepend(instrumenter_module)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe ImageVise::Measurometer do
4
+ RSpec::Matchers.define :include_counter_or_measurement_named do |named|
5
+ match do |actual|
6
+ actual.any? do |e|
7
+ e[0] == named && e[1] > 0
8
+ end
9
+ end
10
+ end
11
+
12
+ it 'instruments a full cycle FormatParser.parse' do
13
+ driver_class = Class.new do
14
+ attr_accessor :timings, :counters, :distributions
15
+ def initialize
16
+ @timings = []
17
+ @distributions = []
18
+ @counters = []
19
+ end
20
+
21
+ def instrument(block_name)
22
+ s = Process.clock_gettime(Process::CLOCK_MONOTONIC)
23
+ yield.tap do
24
+ delta = Process.clock_gettime(Process::CLOCK_MONOTONIC) - s
25
+ @timings << [block_name, delta * 1000]
26
+ end
27
+ end
28
+
29
+ def add_distribution_value(value_path, value)
30
+ @distributions << [value_path, value]
31
+ end
32
+
33
+ def increment_counter(value_path, value)
34
+ @counters << [value_path, value]
35
+ end
36
+ end
37
+
38
+ instrumenter = driver_class.new
39
+ described_class.drivers << instrumenter
40
+
41
+ builder = ImageVise::Pipeline.new
42
+ pipeline = builder.
43
+ auto_orient.
44
+ fit_crop(width: 48, height: 48, gravity: 'c').
45
+ sharpen(radius: 2, sigma: 0.5).
46
+ ellipse_stencil.
47
+ strip_metadata
48
+
49
+ image = Magick::Image.read(test_image_path)[0]
50
+ pipeline.apply! image, {}
51
+
52
+ described_class.drivers.delete(instrumenter)
53
+ expect(described_class.drivers).not_to include(instrumenter)
54
+
55
+ expect(instrumenter.timings).to include_counter_or_measurement_named('image_vise.op.AutoOrient')
56
+ expect(instrumenter.timings).to include_counter_or_measurement_named('image_vise.op.StripMetadata')
57
+ end
58
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_vise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-27 00:00:00.000000000 Z
11
+ date: 2018-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: patron
@@ -215,7 +215,7 @@ files:
215
215
  - SECURITY.md
216
216
  - examples/config.ru
217
217
  - examples/custom_image_operator.rb
218
- - examples/error_handline_appsignal.rb
218
+ - examples/error_handling_appsignal.rb
219
219
  - examples/error_handling_sentry.rb
220
220
  - image_vise.gemspec
221
221
  - lib/image_vise.rb
@@ -240,6 +240,7 @@ files:
240
240
  - lib/image_vise/version.rb
241
241
  - lib/image_vise/writers/auto_writer.rb
242
242
  - lib/image_vise/writers/jpeg_writer.rb
243
+ - lib/measurometer.rb
243
244
  - spec/image_vise/auto_orient_spec.rb
244
245
  - spec/image_vise/background_fill_spec.rb
245
246
  - spec/image_vise/crop_spec.rb
@@ -261,6 +262,7 @@ files:
261
262
  - spec/image_vise/writers/jpeg_writer_spec.rb
262
263
  - spec/image_vise_spec.rb
263
264
  - spec/layers-with-blending.psd
265
+ - spec/measurometer_spec.rb
264
266
  - spec/spec_helper.rb
265
267
  - spec/test_server.rb
266
268
  - spec/waterside_magic_hour.jpg
@@ -290,7 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
290
292
  version: '0'
291
293
  requirements: []
292
294
  rubyforge_project:
293
- rubygems_version: 2.5.2
295
+ rubygems_version: 2.7.3
294
296
  signing_key:
295
297
  specification_version: 4
296
298
  summary: Runtime thumbnailing proxy