pura-image 0.2.5 → 0.4.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
2
  SHA256:
3
- metadata.gz: 5349acbd820a01a5adce41d49add96c50a8e91adc2555629e62268c0876531a5
4
- data.tar.gz: 293af000448458dbb3554cf203b0eeccc8432516a768116cb7dab56a3a9a3472
3
+ metadata.gz: 6e595cde6db52041e2cfd43fe678c8f42d2f02396db44d6d1f72a77db96f15d7
4
+ data.tar.gz: 320aafe82456416eb38b45b53dae2f452118a6dd0bcbc1ef30ff13661f735125
5
5
  SHA512:
6
- metadata.gz: 06aca37f6a3f060428b1f61dc62ceb39b073b71b7b5f8c3337d451bdc1aa0e95bd3c5b6ffb2a4f9cd610ec73f6e51bf55cadbaa61b1f7a016a705173d2f97bfd
7
- data.tar.gz: 1864fdfa00abf7767c15c755a4d89211639695448192a0cee09b487029ebefd8096b4fb95d00d9c88de776c44f24ba900060a918c0f18344717b489c6b8594df
6
+ metadata.gz: ca3bd535aca6ace92343ec86132c78ca08a87f073b5e5a266a40522684dfb7d54297c25543f82a6216c5692cc1ca46af6ea535c08376dd7927fb86fc0b0fa611
7
+ data.tar.gz: bcf34c74223f1b4e6d3f5cfceab9ec4143152adf086bdb30ada1fb9d0a6b096efe7e111b6e10affd477b4c6cdfcd43cf009f8e881887278b000465a85f63b499
data/Gemfile CHANGED
@@ -3,3 +3,4 @@ source "https://rubygems.org"
3
3
  gemspec
4
4
 
5
5
  gem "rubocop", require: false
6
+ gem "erb", require: false # RuboCop configuration templates on Ruby distributions without default ERB
data/README.md CHANGED
@@ -1,115 +1,84 @@
1
1
  # pura-image
2
2
 
3
- Pure Ruby image processing library with **zero C extension dependencies**. Bundles all **pura-*** format gems and provides an `ImageProcessing` adapter for Rails Active Storage.
3
+ Image processing in Ruby, without installing libvips or ImageMagick. Bundles the **pura-*** codec gems and provides a pipeline API and Rails Active Storage integration.
4
4
 
5
- ## Why this exists (in 30 seconds)
6
-
7
- Stop installing system libraries just to resize an image in Rails.
5
+ ## Installation
8
6
 
9
- ```diff
7
+ ```ruby
10
8
  # Gemfile
11
- gem "image_processing"
12
- + gem "pura-image"
13
- ```
9
+ gem "pura-image"
14
10
 
15
- ```ruby
16
11
  # config/application.rb
17
12
  config.active_storage.variant_processor = :pura
18
13
  ```
19
14
 
20
- ```diff
21
- # Dockerfile
22
- - RUN apt-get install -y libvips-dev imagemagick
23
- ```
15
+ The adapter does not require `image_processing`, `ruby-vips`, or `mini_magick`. Remove `image_processing` from your Gemfile if nothing else uses it. Requiring pura-image does not change your processor setting; select `:pura` explicitly.
24
16
 
25
- That's it. No `brew install vips`. No `apt install imagemagick`. No C compiler. Works on macOS, Linux, Windows, Docker, CI, ruby.wasm anywhere Ruby runs.
17
+ The codecs are written in Ruby. PNG compression uses Ruby's standard `zlib` library, which must be available in your Ruby runtime. No additional image library or image-specific native extension needs to be installed.
26
18
 
27
- - **Drop-in** for `ImageProcessing::Vips` / `ImageProcessing::MiniMagick`
28
- - ✅ **7 formats** — JPEG, PNG, BMP, GIF, TIFF, ICO/CUR, WebP
29
- - ✅ **Some operations are faster than C** (no process-spawn overhead — see [Benchmark](#benchmark))
30
- - ✅ **Runs on ruby.wasm, JRuby, TruffleRuby**
19
+ ## Supported formats and limitations
31
20
 
32
- ## Supported Formats
21
+ All decoded images currently use **8-bit RGB** pixels. Alpha, color profiles, EXIF metadata, and animation are not preserved. This matters for transparent logos and photographs whose orientation is stored in EXIF; pura-image does not yet auto-orient them.
33
22
 
34
- | Format | Decode | Encode | Gem |
35
- |--------|--------|--------|-----|
36
- | JPEG | | | [pura-jpeg](https://github.com/komagata/pura-jpeg) |
37
- | PNG | | | [pura-png](https://github.com/komagata/pura-png) |
38
- | BMP | | | [pura-bmp](https://github.com/komagata/pura-bmp) |
39
- | GIF | | | [pura-gif](https://github.com/komagata/pura-gif) |
40
- | TIFF | | | [pura-tiff](https://github.com/komagata/pura-tiff) |
41
- | ICO/CUR | | | [pura-ico](https://github.com/komagata/pura-ico) |
42
- | WebP | | | [pura-webp](https://github.com/komagata/pura-webp) |
23
+ | Format | Decode | Encode | Limitations |
24
+ |--------|--------|--------|-------------|
25
+ | JPEG | Baseline | Baseline | No progressive JPEG or EXIF orientation correction |
26
+ | PNG | Non-interlaced | RGB | No Adam7; alpha/tRNS discarded; 16-bit input reduced to 8-bit |
27
+ | BMP | Supported by pura-bmp | 24-bit RGB | Alpha is not preserved |
28
+ | GIF | First image | Single image | Animation is not preserved; transparent pixels are flattened |
29
+ | TIFF | 8-bit strips: uncompressed, LZW, PackBits | Uncompressed | First IFD only; alpha is discarded |
30
+ | ICO | BMP/PNG entries | PNG entries | Alpha is not preserved |
31
+ | CUR | Read through pura-ico | Not supported | Saving `.cur` raises `ArgumentError` |
32
+ | WebP | VP8 lossy | VP8L lossless | No VP8L/VP8X decode; **encoded WebP cannot be read back by pura-webp yet** |
43
33
 
44
- Format auto-detection by magic bytes no extension guessing needed for decode.
45
-
46
- ## Installation
47
-
48
- ```bash
49
- gem install pura-image
50
- ```
34
+ See each [pura-* codec repository](https://github.com/komagata?tab=repositories&q=pura-) for its detailed restrictions. WebP also lacks the VP8 loop filter and multi-partition frame support. Format detection uses magic bytes; output format comes from the destination extension.
51
35
 
52
36
  ## Usage
53
37
 
54
38
  ```ruby
55
39
  require "pura-image"
56
40
 
57
- # Load any format (auto-detected from magic bytes)
58
41
  image = Pura::Image.load("photo.jpg")
59
- image = Pura::Image.load("logo.png")
60
- image.width #=> 800
61
- image.height #=> 600
62
-
63
- # Save to any format (detected from extension)
64
- Pura::Image.save(image, "output.png")
65
-
66
- # Convert between formats
67
- Pura::Image.convert("input.bmp", "output.jpg", quality: 85)
68
- Pura::Image.convert("photo.tiff", "photo.png")
42
+ thumb = image.resize_to_limit(800, 600).rotate(90).grayscale
43
+ Pura::Image.save(thumb, "thumb.png", compression: 6)
44
+ Pura::Image.convert("photo.bmp", "photo.jpg", quality: 85)
69
45
  ```
70
46
 
71
- ## Image Operations
47
+ Operations return a new image:
72
48
 
73
- All operations from the `image_processing` gem are supported:
49
+ | Operation | Behavior |
50
+ |-----------|----------|
51
+ | `resize_to_limit(w, h)` | Fit without enlarging |
52
+ | `resize_to_fit(w, h)` | Fit, allowing enlargement |
53
+ | `resize_to_fill(w, h)` | Fill, then center-crop |
54
+ | `resize_to_cover(w, h)` | Cover without cropping |
55
+ | `resize_and_pad(w, h, background: [0, 0, 0])` | Fit and center on an RGB background |
56
+ | `crop(x, y, w, h)` | Crop a positive integer region entirely inside the image |
57
+ | `rotate(degrees)` | Multiples of 90 degrees only |
58
+ | `grayscale` | Convert RGB to equal channels |
59
+ | `strip` | Return a copy; the current image model already discards metadata |
74
60
 
75
- ```ruby
76
- image = Pura::Image.load("photo.jpg")
61
+ `resize_to_limit` and `resize_to_fit` accept one `nil` dimension to leave that side unbounded. Supply at least one positive integer dimension. For example, `resize_to_fit(400, nil)` resizes to width 400 while preserving aspect ratio.
77
62
 
78
- # Resize
79
- image.resize_to_limit(800, 600) # downsize only, keep aspect ratio
80
- image.resize_to_fit(400, 400) # resize to fit, keep aspect ratio
81
- image.resize_to_fill(400, 400) # fill exact size, center crop excess
82
- image.resize_and_pad(400, 400) # fit within bounds, pad with black
83
- image.resize_to_cover(400, 400) # cover bounds, no crop
84
-
85
- # Transform
86
- image.crop(10, 10, 200, 200) # crop region
87
- image.rotate(90) # rotate 90/180/270 degrees
88
- image.grayscale # convert to grayscale
89
-
90
- # Chain operations
91
- result = Pura::Image.load("photo.jpg")
92
- .resize_to_limit(800, 600)
93
- .rotate(90)
94
- .grayscale
95
-
96
- Pura::Image.save(result, "thumb.jpg", quality: 80)
97
- ```
98
-
99
- ## Rails Active Storage Integration
63
+ These are a subset of the operations available in Vips/ImageMagick. Unsupported keyword options, such as `gravity:`, raise `ArgumentError`; they are not silently ignored. Saver options are passed to the selected codec: JPEG supports `quality:` and `subsampling:`, PNG supports `compression:`, and GIF supports `max_colors:`.
100
64
 
101
- Drop-in replacement for libvips/ImageMagick:
65
+ ## Pipeline API
102
66
 
103
67
  ```ruby
104
- # Gemfile
105
- gem "image_processing"
106
- gem "pura-image"
68
+ require "image_processing/pura"
107
69
 
108
- # config/application.rb
109
- config.active_storage.variant_processor = :pura
70
+ pipeline = ImageProcessing::Pura.source("photo.jpg")
71
+ pipeline.resize_to_limit(400, nil).convert("png").call(destination: "thumb.png")
72
+ pipeline.resize_to_limit(100, 100).call(destination: "small.jpg")
73
+
74
+ ImageProcessing::Pura.valid_image?("photo.jpg") # true when this backend can decode it
110
75
  ```
111
76
 
112
- Models and views stay exactly the same:
77
+ The chainable API is provided by [pura-processing](https://github.com/komagata/pura-processing), adapted from `image_processing` under the MIT license. Only `loader(page: 0)` is supported; other pages and unknown loader options are rejected.
78
+
79
+ ## Rails Active Storage
80
+
81
+ With the processor configuration above, use ordinary variants:
113
82
 
114
83
  ```ruby
115
84
  class User < ApplicationRecord
@@ -123,63 +92,19 @@ end
123
92
  <%= image_tag user.avatar.variant(:thumb) %>
124
93
  ```
125
94
 
126
- No `brew install vips`. No `apt install imagemagick`. Just `gem install` and go.
127
-
128
- ### ImageProcessing::Pura API
129
-
130
- ```ruby
131
- require "image_processing/pura"
132
-
133
- # Same API as ImageProcessing::Vips
134
- processed = ImageProcessing::Pura
135
- .source("photo.jpg")
136
- .resize_to_limit(400, 400)
137
- .convert("png")
138
- .call(destination: "output.png")
139
-
140
- # Pipeline branching
141
- pipeline = ImageProcessing::Pura.source("photo.jpg")
142
- large = pipeline.resize_to_limit(800, 800).call(destination: "large.jpg")
143
- medium = pipeline.resize_to_limit(500, 500).call(destination: "medium.jpg")
144
- small = pipeline.resize_to_limit(300, 300).call(destination: "small.jpg")
145
-
146
- # Validation
147
- ImageProcessing::Pura.valid_image?("photo.jpg") #=> true
148
- ```
149
-
150
- ## Benchmark
151
-
152
- 400×400 image, Ruby 4.0.2 + YJIT vs ffmpeg (C + SIMD).
153
-
154
- ### Decode
95
+ The dedicated transformer handles supported image variants without the `image_processing` gem. This does not provide PDF/video previewers or a metadata analyzer. Image format and operation limitations above also apply to Rails variants.
155
96
 
156
- | Format | pura-* | ffmpeg | vs ffmpeg |
157
- |--------|--------|--------|-----------|
158
- | TIFF | **14 ms** | 59 ms | 🚀 **4× faster** |
159
- | BMP | **39 ms** | 59 ms | 🚀 **1.5× faster** |
160
- | GIF | 77 ms | 65 ms | ~1× (comparable) |
161
- | PNG | 111 ms | 60 ms | 1.9× slower |
162
- | JPEG | 304 ms | 55 ms | 5.5× slower |
97
+ The integration CI exercises a real upload and variant generation on Rails 7.2, 8.0, and 8.1, with eager loading both enabled and disabled, and checks that `:disabled` is left unchanged.
163
98
 
164
- ### Encode
99
+ ## Browser demo
165
100
 
166
- | Format | pura-* | ffmpeg | vs ffmpeg |
167
- |--------|--------|--------|-----------|
168
- | TIFF | **0.8 ms** | 58 ms | 🚀 **73× faster** |
169
- | BMP | **35 ms** | 58 ms | 🚀 **1.7× faster** |
170
- | PNG | **52 ms** | 61 ms | 🚀 **faster** |
171
- | JPEG | 238 ms | 62 ms | 3.8× slower |
172
- | GIF | 377 ms | 59 ms | 6.4× slower |
101
+ The [ruby.wasm demo](https://komagata.github.io/pura-image/) runs transformations in the browser. See [demo/README.md](demo/README.md). Runtime support depends on the Ruby version and available standard libraries; CRuby CI results should not be read as verification of every alternative Ruby implementation.
173
102
 
174
- 5 out of 11 operations are **faster than C** (ffmpeg process-spawn overhead).
103
+ ## Performance
175
104
 
176
- ## Why pure Ruby?
105
+ The earlier 400×400 benchmark compared Ruby 4.0.2 + YJIT to a new ffmpeg process for every operation. Its measurements include ffmpeg process startup and do not establish that the codec is faster than an in-process C library such as libvips.
177
106
 
178
- - **`gem install` and go** no `brew install`, no `apt install`, no C compiler
179
- - **Works everywhere Ruby works** — CRuby, ruby.wasm, mruby, JRuby, TruffleRuby
180
- - **Edge/Wasm ready** — browsers (ruby.wasm), sandboxed environments, no system libraries needed
181
- - **Perfect for dev/CI** — no ImageMagick/libvips setup. `rails new` → image upload → it just works
182
- - **7 formats, 1 interface** — unified API across JPEG, PNG, BMP, GIF, TIFF, ICO, WebP
107
+ For Rails deployment decisions, measure the complete decode–resize–encode pipeline with your own photographs, and compare output quality, file size, latency, and peak memory under the same settings. pura-image prioritizes installation convenience; native codecs are usually the appropriate comparison for throughput-sensitive workloads.
183
108
 
184
109
  ## License
185
110
 
@@ -1,33 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "image_processing"
3
+ require "pura/processing"
4
4
 
5
5
  require_relative "../pura-image"
6
6
 
7
7
  module ImageProcessing
8
8
  module Pura
9
- extend Chainable
9
+ extend ::Pura::Processing::Chainable
10
10
 
11
11
  def self.valid_image?(file)
12
12
  path = file.respond_to?(:path) ? file.path : file.to_s
13
- data = File.binread(path, 8)
14
- format = ::Pura::Image::Processor.detect_format(data)
15
- return false unless format
16
-
17
13
  ::Pura::Image::Processor.load(path)
18
14
  true
19
15
  rescue StandardError
20
16
  false
21
17
  end
22
18
 
23
- class Processor < ImageProcessing::Processor
19
+ class Processor < ::Pura::Processing::Processor
24
20
  accumulator :image, ::Pura::Image::Wrapper
25
21
 
26
22
  def self.supports_resize_on_load?
27
23
  false
28
24
  end
29
25
 
30
- def self.load_image(path_or_image, **_options)
26
+ def self.load_image(path_or_image, page: 0)
27
+ raise ArgumentError, "only the first image (page: 0) is supported" unless page.is_a?(Integer) && page.zero?
28
+
31
29
  if path_or_image.is_a?(::Pura::Image::Wrapper)
32
30
  path_or_image
33
31
  elsif path_or_image.is_a?(String)
@@ -35,7 +33,7 @@ module ImageProcessing
35
33
  elsif path_or_image.respond_to?(:path)
36
34
  ::Pura::Image::Processor.load(path_or_image.path)
37
35
  else
38
- raise ImageProcessing::Error, "unsupported source: #{path_or_image.inspect}"
36
+ raise ::Pura::Processing::Error, "unsupported source: #{path_or_image.inspect}"
39
37
  end
40
38
  end
41
39
 
@@ -43,45 +41,36 @@ module ImageProcessing
43
41
  ::Pura::Image::Processor.save(wrapper, destination.to_s, **options)
44
42
  end
45
43
 
46
- def resize_to_limit(width, height, **_options)
47
- w = image.width
48
- h = image.height
49
-
50
- return image if w <= (width || w) && h <= (height || h)
51
-
52
- width ||= w
53
- height ||= h
54
- image.resize_to_fit(width, height)
44
+ def resize_to_limit(width, height)
45
+ image.resize_to_limit(width, height)
55
46
  end
56
47
 
57
- def resize_to_fit(width, height, **_options)
58
- width ||= image.width
59
- height ||= image.height
48
+ def resize_to_fit(width, height)
60
49
  image.resize_to_fit(width, height)
61
50
  end
62
51
 
63
- def resize_to_fill(width, height, **_options)
52
+ def resize_to_fill(width, height)
64
53
  image.resize_to_fill(width, height)
65
54
  end
66
55
 
67
- def resize_and_pad(width, height, background: nil, **_options)
56
+ def resize_and_pad(width, height, background: nil)
68
57
  bg = background || [0, 0, 0]
69
58
  image.resize_and_pad(width, height, background: bg)
70
59
  end
71
60
 
72
- def resize_to_cover(width, height, **_options)
61
+ def resize_to_cover(width, height)
73
62
  image.resize_to_cover(width, height)
74
63
  end
75
64
 
76
- def crop(left, top, width, height, **_options)
65
+ def crop(left, top, width, height)
77
66
  image.crop(left, top, width, height)
78
67
  end
79
68
 
80
- def rotate(degrees, **_options)
69
+ def rotate(degrees)
81
70
  image.rotate(degrees)
82
71
  end
83
72
 
84
- def colourspace(space, **_options)
73
+ def colourspace(space)
85
74
  if %w[b-w grey16].include?(space.to_s)
86
75
  image.grayscale
87
76
  else
@@ -89,11 +78,11 @@ module ImageProcessing
89
78
  end
90
79
  end
91
80
 
92
- def strip(**_options)
81
+ def strip
93
82
  image.strip
94
83
  end
95
84
 
96
- def convert(format, **_options)
85
+ def convert(format)
97
86
  # Store desired format for save_image
98
87
  @format = format.to_s.delete(".")
99
88
  image
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pura
4
+ module Image
5
+ # Rails versions before variant_transformer= select the backend in Variation.
6
+ module LegacyVariation
7
+ private
8
+
9
+ def transformer
10
+ return super unless ActiveStorage.variant_processor == :pura
11
+
12
+ Pura::Image::Transformer.new(transformations.except(:format))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -34,19 +34,14 @@ module Pura
34
34
  end
35
35
 
36
36
  def resize_to_limit(max_width, max_height)
37
- return dup_image if @width <= max_width && @height <= max_height
37
+ scale = fit_scale(max_width, max_height)
38
+ return dup_image if scale >= 1
38
39
 
39
- scale = [@width.to_f / max_width, @height.to_f / max_height].max
40
- new_w = [(@width / scale).round, 1].max
41
- new_h = [(@height / scale).round, 1].max
42
- resize(new_w, new_h)
40
+ resize_scaled(scale)
43
41
  end
44
42
 
45
43
  def resize_to_fit(max_width, max_height)
46
- scale = [@width.to_f / max_width, @height.to_f / max_height].max
47
- new_w = [(@width / scale).round, 1].max
48
- new_h = [(@height / scale).round, 1].max
49
- resize(new_w, new_h)
44
+ resize_scaled(fit_scale(max_width, max_height))
50
45
  end
51
46
 
52
47
  def resize_to_fill(fill_width, fill_height)
@@ -91,6 +86,22 @@ module Pura
91
86
 
92
87
  private
93
88
 
89
+ def fit_scale(max_width, max_height)
90
+ dimensions = [max_width, max_height].compact
91
+ unless dimensions.any? && dimensions.all? { |value| value.is_a?(Integer) && value.positive? }
92
+ raise ArgumentError, "provide at least one positive integer dimension"
93
+ end
94
+
95
+ scales = []
96
+ scales << (max_width.to_f / @width) if max_width
97
+ scales << (max_height.to_f / @height) if max_height
98
+ scales.min
99
+ end
100
+
101
+ def resize_scaled(scale)
102
+ resize([(@width * scale).round, 1].max, [(@height * scale).round, 1].max)
103
+ end
104
+
94
105
  def dup_image
95
106
  self.class.new(@width, @height, @pixels.dup)
96
107
  end
@@ -22,8 +22,7 @@ module Pura
22
22
  ".gif" => :gif,
23
23
  ".tif" => :tiff, ".tiff" => :tiff,
24
24
  ".webp" => :webp,
25
- ".ico" => :ico,
26
- ".cur" => :ico
25
+ ".ico" => :ico
27
26
  }.freeze
28
27
 
29
28
  class << self
@@ -97,22 +96,22 @@ module Pura
97
96
  def encode_with_format(image, path, format, **options)
98
97
  case format
99
98
  when :jpeg
100
- Pura::Jpeg.encode(image, path, quality: options.fetch(:quality, 85))
99
+ Pura::Jpeg.encode(image, path, **options)
101
100
  when :png
102
101
  png_img = Pura::Png::Image.new(image.width, image.height, image.pixels)
103
- Pura::Png.encode(png_img, path, compression: options.fetch(:compression, 6))
102
+ Pura::Png.encode(png_img, path, **options)
104
103
  when :bmp
105
104
  bmp_img = Pura::Bmp::Image.new(image.width, image.height, image.pixels)
106
- Pura::Bmp.encode(bmp_img, path)
105
+ Pura::Bmp.encode(bmp_img, path, **options)
107
106
  when :gif
108
107
  gif_img = Pura::Gif::Image.new(image.width, image.height, image.pixels)
109
- Pura::Gif.encode(gif_img, path)
108
+ Pura::Gif.encode(gif_img, path, **options)
110
109
  when :tiff
111
110
  tiff_img = Pura::Tiff::Image.new(image.width, image.height, image.pixels)
112
- Pura::Tiff.encode(tiff_img, path)
111
+ Pura::Tiff.encode(tiff_img, path, **options)
113
112
  when :ico
114
113
  ico_img = Pura::Ico::Image.new(image.width, image.height, image.pixels)
115
- Pura::Ico.encode(ico_img, path)
114
+ Pura::Ico.encode(ico_img, path, **options)
116
115
  when :webp
117
116
  raise ArgumentError, "WebP support requires the pura-webp gem" unless defined?(Pura::Webp)
118
117
 
@@ -1,45 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Only define railtie if Rails is available
4
- if defined?(Rails)
5
- require "rails/railtie"
6
-
7
- module Pura
8
- module Image
9
- class Railtie < Rails::Railtie
10
- initializer "pura_image.set_variant_processor", before: "active_storage.configs" do |app|
11
- app.config.active_storage.variant_processor = :pura
12
- end
13
-
14
- initializer "pura_image.patch_active_storage_variation" do
15
- ActiveSupport.on_load(:active_storage_blob) do
16
- require "image_processing/pura"
17
-
18
- variation_patch = Module.new do
19
- private
20
-
21
- def transformer
22
- if ActiveStorage.variant_processor == :pura && ActiveStorage.variant_transformer.nil?
23
- @pura_transformer ||= begin
24
- klass = Class.new(ActiveStorage::Transformers::ImageProcessingTransformer) do
25
- private
26
-
27
- def processor
28
- ImageProcessing::Pura
29
- end
30
- end
31
-
32
- klass.new(transformations.except(:format))
33
- end
34
- else
35
- super
3
+ require "rails/railtie"
4
+
5
+ module Pura
6
+ module Image
7
+ class Railtie < Rails::Railtie
8
+ initializer "pura_image.configure_active_storage", after: "active_storage.configs" do |app|
9
+ app.config.after_initialize do
10
+ if defined?(ActiveStorage) && ActiveStorage.variant_processor == :pura
11
+ require_relative "transformer"
12
+ if ActiveStorage.respond_to?(:variant_transformer=)
13
+ ActiveStorage.variant_transformer = Pura::Image::Transformer
14
+ else
15
+ require_relative "legacy_variation"
16
+ ActiveSupport.on_load(:active_storage_blob) do
17
+ unless ActiveStorage::Variation < Pura::Image::LegacyVariation
18
+ ActiveStorage::Variation.prepend(Pura::Image::LegacyVariation)
36
19
  end
37
20
  end
38
21
  end
39
-
40
- unless ActiveStorage::Variation.ancestors.include?(variation_patch)
41
- ActiveStorage::Variation.prepend(variation_patch)
42
- end
43
22
  end
44
23
  end
45
24
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "image_processing/pura"
4
+ require "active_storage/transformers/transformer"
5
+
6
+ module Pura
7
+ module Image
8
+ # Uses Active Storage's backend-independent lifecycle without image_processing.
9
+ class Transformer < ActiveStorage::Transformers::Transformer
10
+ SUPPORTED_OPERATIONS = %i[
11
+ resize_to_limit resize_to_fit resize_to_fill resize_and_pad resize_to_cover
12
+ crop rotate grayscale colourspace strip
13
+ ].freeze
14
+
15
+ private
16
+
17
+ def process(file, format:)
18
+ operations = transformations.each_with_object([]) do |(name, argument), result|
19
+ raise ArgumentError, "unsupported transformation: #{name}" unless SUPPORTED_OPERATIONS.include?(name.to_sym)
20
+
21
+ result << [name, argument] if argument.present?
22
+ end
23
+
24
+ ImageProcessing::Pura.source(file).convert(format).apply(operations).call
25
+ end
26
+ end
27
+ end
28
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Pura
4
4
  module Image
5
- VERSION = "0.2.5"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
data/pura-image.gemspec CHANGED
@@ -20,14 +20,14 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.files = Dir["lib/**/*.rb"] + ["test/fixtures/test_64x64.jpg"] + ["pura-image.gemspec", "Gemfile", "Rakefile", "README.md", "LICENSE"]
22
22
 
23
- spec.add_dependency "pura-jpeg", "~> 0.1"
24
- spec.add_dependency "pura-png", "~> 0.1"
25
- spec.add_dependency "pura-bmp", "~> 0.1"
26
- spec.add_dependency "pura-gif", "~> 0.1"
27
- spec.add_dependency "pura-tiff", "~> 0.1"
28
- spec.add_dependency "pura-ico", "~> 0.1"
29
- spec.add_dependency "pura-webp", "~> 0.2"
30
- spec.add_dependency "image_processing", "~> 1.2"
23
+ spec.add_dependency "pura-jpeg", "~> 0.1", ">= 0.1.1"
24
+ spec.add_dependency "pura-png", "~> 0.1", ">= 0.1.1"
25
+ spec.add_dependency "pura-bmp", "~> 0.1", ">= 0.1.1"
26
+ spec.add_dependency "pura-gif", "~> 0.1", ">= 0.1.1"
27
+ spec.add_dependency "pura-tiff", "~> 0.1", ">= 0.1.1"
28
+ spec.add_dependency "pura-ico", "~> 0.1", ">= 0.1.1"
29
+ spec.add_dependency "pura-webp", "~> 0.2", ">= 0.2.1"
30
+ spec.add_dependency "pura-processing", "~> 0.1"
31
31
  spec.add_development_dependency "minitest", "~> 5.0"
32
32
  spec.add_development_dependency "rake", "~> 13.0"
33
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pura-image
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - komagata
@@ -16,6 +16,9 @@ dependencies:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
18
  version: '0.1'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.1
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -23,6 +26,9 @@ dependencies:
23
26
  - - "~>"
24
27
  - !ruby/object:Gem::Version
25
28
  version: '0.1'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 0.1.1
26
32
  - !ruby/object:Gem::Dependency
27
33
  name: pura-png
28
34
  requirement: !ruby/object:Gem::Requirement
@@ -30,6 +36,9 @@ dependencies:
30
36
  - - "~>"
31
37
  - !ruby/object:Gem::Version
32
38
  version: '0.1'
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 0.1.1
33
42
  type: :runtime
34
43
  prerelease: false
35
44
  version_requirements: !ruby/object:Gem::Requirement
@@ -37,6 +46,9 @@ dependencies:
37
46
  - - "~>"
38
47
  - !ruby/object:Gem::Version
39
48
  version: '0.1'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 0.1.1
40
52
  - !ruby/object:Gem::Dependency
41
53
  name: pura-bmp
42
54
  requirement: !ruby/object:Gem::Requirement
@@ -44,6 +56,9 @@ dependencies:
44
56
  - - "~>"
45
57
  - !ruby/object:Gem::Version
46
58
  version: '0.1'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.1
47
62
  type: :runtime
48
63
  prerelease: false
49
64
  version_requirements: !ruby/object:Gem::Requirement
@@ -51,6 +66,9 @@ dependencies:
51
66
  - - "~>"
52
67
  - !ruby/object:Gem::Version
53
68
  version: '0.1'
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 0.1.1
54
72
  - !ruby/object:Gem::Dependency
55
73
  name: pura-gif
56
74
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +76,9 @@ dependencies:
58
76
  - - "~>"
59
77
  - !ruby/object:Gem::Version
60
78
  version: '0.1'
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 0.1.1
61
82
  type: :runtime
62
83
  prerelease: false
63
84
  version_requirements: !ruby/object:Gem::Requirement
@@ -65,6 +86,9 @@ dependencies:
65
86
  - - "~>"
66
87
  - !ruby/object:Gem::Version
67
88
  version: '0.1'
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 0.1.1
68
92
  - !ruby/object:Gem::Dependency
69
93
  name: pura-tiff
70
94
  requirement: !ruby/object:Gem::Requirement
@@ -72,6 +96,9 @@ dependencies:
72
96
  - - "~>"
73
97
  - !ruby/object:Gem::Version
74
98
  version: '0.1'
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 0.1.1
75
102
  type: :runtime
76
103
  prerelease: false
77
104
  version_requirements: !ruby/object:Gem::Requirement
@@ -79,6 +106,9 @@ dependencies:
79
106
  - - "~>"
80
107
  - !ruby/object:Gem::Version
81
108
  version: '0.1'
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 0.1.1
82
112
  - !ruby/object:Gem::Dependency
83
113
  name: pura-ico
84
114
  requirement: !ruby/object:Gem::Requirement
@@ -86,6 +116,9 @@ dependencies:
86
116
  - - "~>"
87
117
  - !ruby/object:Gem::Version
88
118
  version: '0.1'
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 0.1.1
89
122
  type: :runtime
90
123
  prerelease: false
91
124
  version_requirements: !ruby/object:Gem::Requirement
@@ -93,6 +126,9 @@ dependencies:
93
126
  - - "~>"
94
127
  - !ruby/object:Gem::Version
95
128
  version: '0.1'
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 0.1.1
96
132
  - !ruby/object:Gem::Dependency
97
133
  name: pura-webp
98
134
  requirement: !ruby/object:Gem::Requirement
@@ -100,6 +136,9 @@ dependencies:
100
136
  - - "~>"
101
137
  - !ruby/object:Gem::Version
102
138
  version: '0.2'
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 0.2.1
103
142
  type: :runtime
104
143
  prerelease: false
105
144
  version_requirements: !ruby/object:Gem::Requirement
@@ -107,20 +146,23 @@ dependencies:
107
146
  - - "~>"
108
147
  - !ruby/object:Gem::Version
109
148
  version: '0.2'
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 0.2.1
110
152
  - !ruby/object:Gem::Dependency
111
- name: image_processing
153
+ name: pura-processing
112
154
  requirement: !ruby/object:Gem::Requirement
113
155
  requirements:
114
156
  - - "~>"
115
157
  - !ruby/object:Gem::Version
116
- version: '1.2'
158
+ version: '0.1'
117
159
  type: :runtime
118
160
  prerelease: false
119
161
  version_requirements: !ruby/object:Gem::Requirement
120
162
  requirements:
121
163
  - - "~>"
122
164
  - !ruby/object:Gem::Version
123
- version: '1.2'
165
+ version: '0.1'
124
166
  - !ruby/object:Gem::Dependency
125
167
  name: minitest
126
168
  requirement: !ruby/object:Gem::Requirement
@@ -163,9 +205,11 @@ files:
163
205
  - Rakefile
164
206
  - lib/image_processing/pura.rb
165
207
  - lib/pura-image.rb
208
+ - lib/pura/image/legacy_variation.rb
166
209
  - lib/pura/image/operations.rb
167
210
  - lib/pura/image/processor.rb
168
211
  - lib/pura/image/railtie.rb
212
+ - lib/pura/image/transformer.rb
169
213
  - lib/pura/image/version.rb
170
214
  - pura-image.gemspec
171
215
  - test/fixtures/test_64x64.jpg