rmagick_tidy 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8f147dcd4088210662a53b4a66911e721b5b5a506ff4b1bead65db5a94ac873b
4
+ data.tar.gz: c80d6229d5ef1e1272097f3d30b939629c04ddb29bed8f185c2a01dc4f7f4d05
5
+ SHA512:
6
+ metadata.gz: e60b3a6a5812a68936e71e73f073ce9f1d1f57032df67ff05c99c5279acba8fc7f935f8435c263388f95a31ac784ba09caa7e10d63954810f155ef3fadf5f592
7
+ data.tar.gz: 63f5780ab884f412049b3fc7290e3f0a050ce8859a66fd87952417156e978ec6f0623efbadb2b98d88711289158e03130efecce5adedea1aa92351bda61b3d73
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-05-21
11
+
12
+ ### Added
13
+ - Initial release: `RmagickTidy.scope` block that tracks every `Magick::Image` / `Magick::ImageList` created inside it and calls `destroy!` on them when the block exits.
14
+ - `LICENSE` file (MIT).
15
+ - Gem metadata (`source_code_uri`, `changelog_uri`, `bug_tracker_uri`, `rubygems_mfa_required`).
16
+ - RuboCop and SimpleCov for style and coverage; both run in CI.
17
+ - Codecov integration: CI emits `coverage/lcov.info` (via `simplecov-lcov`) and uploads it through `codecov/codecov-action@v4`. README displays the live coverage badge.
18
+ - RSpec coverage for nested `Hash`/`Array` return values, `ImageList` iteration, missing `destroyed?`, `nil` / empty returns, and `destroy!` exception paths.
19
+ - README note about `Configuration` thread-safety expectations.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Junichiro Kasuya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.ja.md ADDED
@@ -0,0 +1,129 @@
1
+ # rmagick_tidy
2
+
3
+ [![CI](https://github.com/jksy/rmagick_tidy/actions/workflows/ci.yml/badge.svg)](https://github.com/jksy/rmagick_tidy/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/jksy/rmagick_tidy/branch/main/graph/badge.svg)](https://codecov.io/gh/jksy/rmagick_tidy)
5
+
6
+ `rmagick_tidy` は RMagick (`Magick::Image`) のメモリ管理を**スコープベース**で自動化する Ruby gem です。
7
+
8
+ RMagick が確保するメモリの実体は ImageMagick の C レイヤー側にあります。Ruby の GC は Ruby が確保したメモリしか把握しないため、ImageMagick 側の使用量を過小評価し、GC が適時に発火しません。Ruby ラッパが回収されるタイミングで C 側のメモリも最終的には解放されますが、それまではプロセスの RSS が膨らみ続けます。これを避けるため、`ensure + destroy!` を各所に手書きして即時解放しているプロジェクトが多いのが現状です。本 gem はその定型処理を 1 ブロックに集約します。
9
+
10
+ English: [README.md](README.md)
11
+
12
+ ## インストール
13
+
14
+ ```ruby
15
+ # Gemfile
16
+ gem "rmagick_tidy"
17
+ ```
18
+
19
+ ```ruby
20
+ require "rmagick_tidy"
21
+ ```
22
+
23
+ `require` した時点で `Magick::Image` / `Magick::ImageList` のフックがインストールされます。
24
+
25
+ ## 基本的な使い方
26
+
27
+ ```ruby
28
+ RmagickTidy.scope do
29
+ img = Magick::Image.read("input.jpg").first
30
+ resized = img.resize(800, 600)
31
+ resized.write("output.jpg")
32
+ end
33
+ # ブロックを抜けたタイミングで img, resized が destroy! 済み
34
+ ```
35
+
36
+ ### ブロックの戻り値は解放されない(keep)
37
+
38
+ 呼び出し元に画像を返したい場合は、その画像をブロックの戻り値にします。
39
+
40
+ ```ruby
41
+ result = RmagickTidy.scope do
42
+ img = Magick::Image.read("input.jpg").first
43
+ img.resize(800, 600) # ← この戻り値だけは keep
44
+ end
45
+ # result は生きている。元の img は destroy! 済み
46
+
47
+ result.write("out.jpg")
48
+ result.destroy!
49
+ ```
50
+
51
+ 戻り値は `Magick::Image` 単体だけでなく、`Array<Image>`、`Hash` の値、`Magick::ImageList` 内の要素も再帰的に keep されます。
52
+
53
+ ### ネスト
54
+
55
+ ```ruby
56
+ RmagickTidy.scope do # 外側
57
+ outer = Magick::Image.read("a.jpg").first
58
+ RmagickTidy.scope do # 内側
59
+ inner = outer.resize(100, 100)
60
+ # inner はここで destroy!
61
+ end
62
+ # outer はまだ生きている
63
+ end
64
+ ```
65
+
66
+ ### 例外時も解放される
67
+
68
+ ```ruby
69
+ RmagickTidy.scope do
70
+ img = Magick::Image.read("x.jpg").first
71
+ raise "boom"
72
+ end
73
+ # img は destroy! されてから例外が再送出される
74
+ ```
75
+
76
+ ### bang メソッドは二重登録されない
77
+
78
+ `resize!` などの bang メソッドは `self` を返すため、`equal?` で判定して再登録しません。
79
+
80
+ ## Rails で使う
81
+
82
+ `require "rmagick_tidy"` を済ませると Railtie が `ActionController::Base` に `within_rmagick_tidy_scope` を mixin します。
83
+
84
+ ```ruby
85
+ class ImagesController < ApplicationController
86
+ around_action :within_rmagick_tidy_scope
87
+
88
+ def show
89
+ img = Magick::Image.read(@source).first
90
+ @blob = img.resize(800, 600).to_blob { |info| info.format = "JPEG" }
91
+ send_data @blob, type: "image/jpeg"
92
+ end
93
+ end
94
+ ```
95
+
96
+ `to_blob` は `String` を返すのでスコープの解放対象になりません。Image オブジェクトのみがクリーンアップされます。
97
+
98
+ ## strict モード
99
+
100
+ 開発・テスト環境で「スコープ外で作られた Image」を検知したいときに使います。
101
+
102
+ ```ruby
103
+ RmagickTidy.configure do |c|
104
+ c.strict_mode = :warn # or :raise / :off (default)
105
+ end
106
+ ```
107
+
108
+ - `:off` — 何もしない(本番デフォルト)
109
+ - `:warn` — `warn` で標準エラー出力に通知
110
+ - `:raise` — `RmagickTidy::OutOfScopeError` を発生
111
+
112
+ > **Configuration はスレッドセーフではありません。** `strict_mode`(および将来追加されるオプション)は **起動時に 1 回だけ**設定してください(例: Rails の initializer)。ワーカースレッドが `Magick::Image` を使い始めたあとに別スレッドから書き換える挙動は未定義です。
113
+
114
+ ## 仕組み
115
+
116
+ - `Magick::Image` / `Magick::ImageList` に対し、`Module#prepend` で全ての public instance method(および `new`, `read`, `from_blob`, ...のクラスメソッド)をラップ
117
+ - **戻り値の型をチェック**して `Magick::Image` であれば現在のスコープに登録(ホワイトリストを持たないので RMagick のバージョン差異を吸収)
118
+ - 戻り値が `self` と `equal?` なら bang メソッドとみなして登録しない
119
+ - スコープスタックは `Thread.current` 配下なのでマルチスレッド環境でも安全
120
+ - 二重 `destroy!` は `destroyed?` 判定 + rescue で防止
121
+
122
+ ## 対応バージョン
123
+
124
+ - Ruby 3.2 以上
125
+ - RMagick 2.x 〜 6.x(戻り値チェック方式のため幅広く動作)
126
+
127
+ ## ライセンス
128
+
129
+ MIT
data/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # rmagick_tidy
2
+
3
+ [![CI](https://github.com/jksy/rmagick_tidy/actions/workflows/ci.yml/badge.svg)](https://github.com/jksy/rmagick_tidy/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/jksy/rmagick_tidy/branch/main/graph/badge.svg)](https://codecov.io/gh/jksy/rmagick_tidy)
5
+
6
+ `rmagick_tidy` is a Ruby gem that automates **scope-based memory management** for RMagick (`Magick::Image`).
7
+
8
+ The bulk of the memory an `Magick::Image` represents is allocated by ImageMagick in C, outside Ruby's heap. Ruby's GC only accounts for memory it allocated itself, so it underestimates the real footprint and fires far less often than the actual memory pressure warrants. The C-side memory is eventually freed when the Ruby wrapper is collected, but until then the process's RSS keeps climbing. To avoid that, many projects scatter `ensure` / `destroy!` pairs through their code to release the memory immediately. This gem replaces that boilerplate with a single block.
9
+
10
+ 日本語版: [README.ja.md](README.ja.md)
11
+
12
+ ## Installation
13
+
14
+ ```ruby
15
+ # Gemfile
16
+ gem "rmagick_tidy"
17
+ ```
18
+
19
+ ```ruby
20
+ require "rmagick_tidy"
21
+ ```
22
+
23
+ Hooks for `Magick::Image` and `Magick::ImageList` are installed when the gem is required.
24
+
25
+ ## Basic usage
26
+
27
+ ```ruby
28
+ RmagickTidy.scope do
29
+ img = Magick::Image.read("input.jpg").first
30
+ resized = img.resize(800, 600)
31
+ resized.write("output.jpg")
32
+ end
33
+ # Both img and resized have been released by the time the block exits.
34
+ ```
35
+
36
+ ### The block return value is preserved (keep set)
37
+
38
+ When you need to return an image to the caller, make it the block's return value.
39
+
40
+ ```ruby
41
+ result = RmagickTidy.scope do
42
+ img = Magick::Image.read("input.jpg").first
43
+ img.resize(800, 600) # returned from the block, so it is kept
44
+ end
45
+ # result is still alive; the original img has been released.
46
+
47
+ result.write("out.jpg")
48
+ result.destroy!
49
+ ```
50
+
51
+ The keep set walks the return value recursively: a single `Magick::Image`, an `Array<Image>`, the values of a `Hash`, and the elements of a `Magick::ImageList` are all preserved.
52
+
53
+ ### Nesting
54
+
55
+ ```ruby
56
+ RmagickTidy.scope do # outer scope
57
+ outer = Magick::Image.read("a.jpg").first
58
+ RmagickTidy.scope do # inner scope
59
+ inner = outer.resize(100, 100)
60
+ # inner is released here.
61
+ end
62
+ # outer is still alive.
63
+ end
64
+ ```
65
+
66
+ ### Cleanup runs on exceptions too
67
+
68
+ ```ruby
69
+ RmagickTidy.scope do
70
+ img = Magick::Image.read("x.jpg").first
71
+ raise "boom"
72
+ end
73
+ # img is released before the exception propagates.
74
+ ```
75
+
76
+ ### Bang methods are not registered twice
77
+
78
+ Bang methods such as `resize!` return `self`. `rmagick_tidy` detects this with an `equal?` check and skips re-registration, so the same image is never released twice.
79
+
80
+ ## Rails integration
81
+
82
+ After `require "rmagick_tidy"`, a Railtie mixes `within_rmagick_tidy_scope` into `ActionController::Base`.
83
+
84
+ ```ruby
85
+ class ImagesController < ApplicationController
86
+ around_action :within_rmagick_tidy_scope
87
+
88
+ def show
89
+ img = Magick::Image.read(@source).first
90
+ @blob = img.resize(800, 600).to_blob { |info| info.format = "JPEG" }
91
+ send_data @blob, type: "image/jpeg"
92
+ end
93
+ end
94
+ ```
95
+
96
+ `to_blob` returns a `String`, so it is never treated as a cleanup target — only the image objects are released.
97
+
98
+ ## Strict mode
99
+
100
+ Use strict mode in development or test environments to catch images created outside of a scope.
101
+
102
+ ```ruby
103
+ RmagickTidy.configure do |c|
104
+ c.strict_mode = :warn # or :raise / :off (default)
105
+ end
106
+ ```
107
+
108
+ - `:off` — do nothing (the production default)
109
+ - `:warn` — print a warning to stderr
110
+ - `:raise` — raise `RmagickTidy::OutOfScopeError`
111
+
112
+ > **Configuration is not thread-safe.** Set `strict_mode` (and any future
113
+ > options) **once at boot** — for example from a Rails initializer — before any
114
+ > worker thread starts using `Magick::Image`. Reading and writing the value
115
+ > from multiple threads concurrently is undefined.
116
+
117
+ ## How it works
118
+
119
+ - `Magick::Image` and `Magick::ImageList` are hooked with `Module#prepend`, wrapping every public instance method as well as class methods such as `new`, `read`, and `from_blob`.
120
+ - Each wrapped method **inspects its return value**: if it is a `Magick::Image`, the image is registered with the current scope. Because the gem keeps no method whitelist, it works across a wide range of RMagick versions.
121
+ - If the return value is `equal?` to `self`, it is treated as a bang method and not registered.
122
+ - The scope stack lives in `Thread.current`, so it is safe to use under multi-threaded servers such as Puma.
123
+ - Calling `destroy!` twice is guarded against with a `destroyed?` check and a defensive `rescue`.
124
+
125
+ ## Compatibility
126
+
127
+ - Ruby 3.2 or newer
128
+ - RMagick 2.x through 6.x (the return-value approach keeps the gem broadly compatible)
129
+
130
+ ## License
131
+
132
+ MIT
@@ -0,0 +1,12 @@
1
+ module RmagickTidy
2
+ class Configuration
3
+ # :off (default), :warn, :raise
4
+ attr_accessor :strict_mode
5
+
6
+ def initialize
7
+ @strict_mode = :off
8
+ end
9
+ end
10
+
11
+ class OutOfScopeError < StandardError; end
12
+ end
@@ -0,0 +1,68 @@
1
+ module RmagickTidy
2
+ module Hook
3
+ # Methods we never want to wrap. Most of them either return self, return
4
+ # primitives, or are introspection / lifecycle helpers where wrapping would
5
+ # be wasteful or risk recursion.
6
+ SKIP_INSTANCE_METHODS = %i[
7
+ destroy! destroyed? inspect to_s == eql? hash freeze frozen?
8
+ tainted? untrusted? object_id class itself
9
+ ].freeze
10
+
11
+ # Class-level methods on Magick::Image that produce new images.
12
+ CLASS_METHODS = %i[new read ping from_blob read_inline capture constitute combine].freeze
13
+
14
+ module_function
15
+
16
+ def install!
17
+ return if @installed
18
+ return unless defined?(::Magick::Image)
19
+
20
+ install_instance_hook(::Magick::Image)
21
+ install_class_hook(::Magick::Image)
22
+
23
+ if defined?(::Magick::ImageList)
24
+ install_instance_hook(::Magick::ImageList)
25
+ install_class_hook(::Magick::ImageList)
26
+ end
27
+
28
+ @installed = true
29
+ end
30
+
31
+ def install_instance_hook(klass)
32
+ mod = Module.new do
33
+ def self.inspect = "#<RmagickTidy::InstanceHook>"
34
+ end
35
+ method_names = klass.public_instance_methods(false) - SKIP_INSTANCE_METHODS
36
+ method_names.each do |name|
37
+ next if name.to_s.end_with?("=")
38
+
39
+ define_wrapper(mod, name)
40
+ end
41
+ klass.prepend(mod)
42
+ end
43
+
44
+ def install_class_hook(klass)
45
+ mod = Module.new do
46
+ def self.inspect = "#<RmagickTidy::ClassHook>"
47
+ end
48
+ CLASS_METHODS.each do |name|
49
+ next unless klass.respond_to?(name)
50
+
51
+ define_wrapper(mod, name)
52
+ end
53
+ klass.singleton_class.prepend(mod)
54
+ end
55
+
56
+ def define_wrapper(mod, name)
57
+ mod.send(:define_method, name) do |*args, **kwargs, &block|
58
+ result = if kwargs.empty?
59
+ super(*args, &block)
60
+ else
61
+ super(*args, **kwargs, &block)
62
+ end
63
+ ::RmagickTidy::Tracker.track(result, self)
64
+ result
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,18 @@
1
+ module RmagickTidy
2
+ module ControllerHelper
3
+ # Use as `around_action :within_rmagick_tidy_scope`.
4
+ def within_rmagick_tidy_scope(&)
5
+ RmagickTidy.scope(&)
6
+ end
7
+ end
8
+
9
+ if defined?(::Rails::Railtie)
10
+ class Railtie < ::Rails::Railtie
11
+ initializer "rmagick_tidy.controller" do
12
+ ActiveSupport.on_load(:action_controller) do
13
+ include ::RmagickTidy::ControllerHelper
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,73 @@
1
+ module RmagickTidy
2
+ class Scope
3
+ attr_reader :images, :keeps
4
+
5
+ def initialize
6
+ @images = []
7
+ @keeps = {}.compare_by_identity
8
+ end
9
+
10
+ def register(image)
11
+ @images << image
12
+ end
13
+
14
+ def keep(image)
15
+ @keeps[image] = true
16
+ end
17
+
18
+ def keep?(image)
19
+ @keeps.key?(image)
20
+ end
21
+ end
22
+
23
+ module Registry
24
+ STACK_KEY = :rmagick_tidy_stack
25
+
26
+ module_function
27
+
28
+ def stack
29
+ Thread.current[STACK_KEY] ||= []
30
+ end
31
+
32
+ def current
33
+ stack.last
34
+ end
35
+
36
+ def in_scope?
37
+ !stack.empty?
38
+ end
39
+
40
+ def push
41
+ stack.push(Scope.new)
42
+ end
43
+
44
+ def pop_and_destroy
45
+ scope = stack.pop
46
+ return unless scope
47
+
48
+ seen = {}.compare_by_identity
49
+ scope.images.each do |img|
50
+ next if seen[img]
51
+
52
+ seen[img] = true
53
+ next if scope.keep?(img)
54
+
55
+ destroy_safely(img)
56
+ end
57
+ nil
58
+ end
59
+
60
+ def destroy_safely(img)
61
+ return unless img
62
+ return if img.respond_to?(:destroyed?) && img.destroyed?
63
+
64
+ img.destroy!
65
+ rescue ::Magick::ImageMagickError
66
+ # Swallow Magick-side destroy errors (e.g. DestroyedImageError on races);
67
+ # the goal is to free what we can without raising from `ensure`. Other
68
+ # exceptions (NoMethodError, ArgumentError, etc.) are intentionally not
69
+ # caught here so unrelated bugs surface early.
70
+ nil
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,57 @@
1
+ module RmagickTidy
2
+ module Tracker
3
+ module_function
4
+
5
+ def track(result, receiver = nil)
6
+ return result if receiver && result.equal?(receiver)
7
+
8
+ each_image(result) do |img|
9
+ register(img)
10
+ end
11
+ result
12
+ end
13
+
14
+ def keep(value)
15
+ scope = Registry.current
16
+ return unless scope
17
+
18
+ each_image(value) do |img|
19
+ scope.keep(img)
20
+ end
21
+ end
22
+
23
+ def each_image(value, &block)
24
+ case value
25
+ when nil
26
+ # nothing
27
+ when ->(v) { defined?(Magick::Image) && v.is_a?(Magick::Image) }
28
+ yield value
29
+ when ->(v) { defined?(Magick::ImageList) && v.is_a?(Magick::ImageList) }
30
+ value.each { |i| yield i if defined?(Magick::Image) && i.is_a?(Magick::Image) }
31
+ when Array
32
+ value.each { |v| each_image(v, &block) }
33
+ when Hash
34
+ value.each_value { |v| each_image(v, &block) }
35
+ end
36
+ end
37
+
38
+ def register(img)
39
+ scope = Registry.current
40
+ if scope
41
+ scope.register(img)
42
+ else
43
+ handle_out_of_scope(img)
44
+ end
45
+ end
46
+
47
+ def handle_out_of_scope(img)
48
+ case RmagickTidy.configuration.strict_mode
49
+ when :raise
50
+ raise OutOfScopeError,
51
+ "Magick::Image #{img.inspect} was created outside of RmagickTidy.scope"
52
+ when :warn
53
+ warn "[rmagick_tidy] Magick::Image created outside of RmagickTidy.scope (#{img.class})"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,3 @@
1
+ module RmagickTidy
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,32 @@
1
+ require "rmagick"
2
+
3
+ require "rmagick_tidy/version"
4
+ require "rmagick_tidy/configuration"
5
+ require "rmagick_tidy/registry"
6
+ require "rmagick_tidy/tracker"
7
+ require "rmagick_tidy/hook"
8
+
9
+ module RmagickTidy
10
+ class << self
11
+ def configuration
12
+ @configuration ||= Configuration.new
13
+ end
14
+
15
+ def configure
16
+ yield configuration
17
+ end
18
+
19
+ def scope
20
+ Registry.push
21
+ result = yield
22
+ Tracker.keep(result)
23
+ result
24
+ ensure
25
+ Registry.pop_and_destroy
26
+ end
27
+ end
28
+ end
29
+
30
+ RmagickTidy::Hook.install!
31
+
32
+ require "rmagick_tidy/railtie" if defined?(Rails::Railtie)
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rmagick_tidy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Junichiro Kasuya
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-05-21 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rmagick
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '5.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '8'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '5.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '8'
32
+ - !ruby/object:Gem::Dependency
33
+ name: rake
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '13.0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '13.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '3.12'
53
+ type: :development
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '3.12'
60
+ - !ruby/object:Gem::Dependency
61
+ name: rubocop
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.60'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.60'
74
+ - !ruby/object:Gem::Dependency
75
+ name: simplecov
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0.22'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.22'
88
+ - !ruby/object:Gem::Dependency
89
+ name: simplecov-lcov
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '0.8'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '0.8'
102
+ description: rmagick_tidy provides a scope block that automatically tracks every Magick::Image
103
+ / Magick::ImageList produced inside it and calls destroy! on them when the block
104
+ exits, so callers no longer need to litter their code with ensure + destroy! pairs.
105
+ email:
106
+ - junichiro.kasuya@gmail.com
107
+ executables: []
108
+ extensions: []
109
+ extra_rdoc_files: []
110
+ files:
111
+ - CHANGELOG.md
112
+ - LICENSE
113
+ - README.ja.md
114
+ - README.md
115
+ - lib/rmagick_tidy.rb
116
+ - lib/rmagick_tidy/configuration.rb
117
+ - lib/rmagick_tidy/hook.rb
118
+ - lib/rmagick_tidy/railtie.rb
119
+ - lib/rmagick_tidy/registry.rb
120
+ - lib/rmagick_tidy/tracker.rb
121
+ - lib/rmagick_tidy/version.rb
122
+ homepage: https://github.com/jksy/rmagick_tidy
123
+ licenses:
124
+ - MIT
125
+ metadata:
126
+ homepage_uri: https://github.com/jksy/rmagick_tidy
127
+ source_code_uri: https://github.com/jksy/rmagick_tidy/tree/main
128
+ changelog_uri: https://github.com/jksy/rmagick_tidy/blob/main/CHANGELOG.md
129
+ bug_tracker_uri: https://github.com/jksy/rmagick_tidy/issues
130
+ rubygems_mfa_required: 'true'
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 3.2.0
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubygems_version: 3.6.2
146
+ specification_version: 4
147
+ summary: Automatic scope-based memory management for RMagick (Magick::Image).
148
+ test_files: []