rmagick_tidy 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +4 -5
- data/README.ja.md +84 -5
- data/README.md +89 -7
- data/lib/rmagick_tidy/configuration.rb +5 -0
- data/lib/rmagick_tidy/hook.rb +6 -1
- data/lib/rmagick_tidy/tracker.rb +21 -1
- data/lib/rmagick_tidy/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d63fdf8da8e0b7479b85be728d7e00e3e7821a042fbaacb730d6c6a3d783d337
|
|
4
|
+
data.tar.gz: 52d88eedea15175cd1b35fd8046f4127ccc73cc3b98511bf7c5afcc02a258ded
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2087aa6871347fede9c6fa3022dad2aeb425a0b2e46cb181071fe623156ae1c0fb21e1ec0152ee8d84663b9e74695cd0e9097a219ce2194b3cceacec4df172ce
|
|
7
|
+
data.tar.gz: 44a0ea74486054b2b9ee69052540c2a6018a707d864ac21dab7ae7a8656e3c278886244553690f445938f6c55509e5e936d4f6e50f513ced5c62d75dc9633d86
|
data/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Release notes for versions after 0.1.0 are published on
|
|
4
|
+
[GitHub Releases](https://github.com/jksy/rmagick_tidy/releases), generated
|
|
5
|
+
from merged pull requests. This file is kept for the history below.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [Unreleased]
|
|
7
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
9
8
|
|
|
10
9
|
## [0.1.0] - 2026-05-21
|
|
11
10
|
|
data/README.ja.md
CHANGED
|
@@ -95,6 +95,64 @@ end
|
|
|
95
95
|
|
|
96
96
|
`to_blob` は `String` を返すのでスコープの解放対象になりません。Image オブジェクトのみがクリーンアップされます。
|
|
97
97
|
|
|
98
|
+
## Rails 以外・ジョブで使う
|
|
99
|
+
|
|
100
|
+
gem 自体が組み込むのは Rails のコントローラだけです。それ以外の場所では、処理の単位を自分で `RmagickTidy.scope` で包んでください。数行で書けます。アプリ側に置いておけば、各ライブラリのバージョンアップにも自分のペースで合わせられます。
|
|
101
|
+
|
|
102
|
+
### Rack ミドルウェア(Sinatra / Hanami / 素の Rack など)
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
class RmagickTidyMiddleware
|
|
106
|
+
def initialize(app)
|
|
107
|
+
@app = app
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def call(env)
|
|
111
|
+
RmagickTidy.scope { @app.call(env) }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
use RmagickTidyMiddleware
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
スコープはアプリから戻った時点で終わります。サーバがボディを送るより前です。`String` の `Array` や Rails・Sinatra の通常のレスポンスのように、中身ができあがっているボディなら問題ありません。streaming body や `Enumerator` のように送信中に画像を作ったり使ったりする場合は、その画像処理をボディの中に書き、ボディの中で `RmagickTidy.scope` を張ってください。アプリ側で作ってボディが後から使う画像は、その時点で解放済みです。
|
|
119
|
+
|
|
120
|
+
### ActiveJob
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
class ApplicationJob < ActiveJob::Base
|
|
124
|
+
around_perform do |_job, block|
|
|
125
|
+
RmagickTidy.scope do
|
|
126
|
+
block.call
|
|
127
|
+
nil
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`nil` を返すのは、`perform` の戻り値を keep しないためです。`perform` の最後が `img.write(path)` だと戻り値は `img` 自身になり、そのままだと GC まで残ってしまいます。
|
|
134
|
+
|
|
135
|
+
### Sidekiq
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
class RmagickTidySidekiqMiddleware
|
|
139
|
+
def call(_job, _payload, _queue)
|
|
140
|
+
RmagickTidy.scope do
|
|
141
|
+
yield
|
|
142
|
+
nil
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
Sidekiq.configure_server do |config|
|
|
148
|
+
config.server_middleware do |chain|
|
|
149
|
+
chain.add RmagickTidySidekiqMiddleware
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Sidekiq 上で動かす ActiveJob のジョブでは、上の `around_perform` とこのミドルウェアのどちらか一方で足ります。両方入れてもスコープが入れ子になるだけです。
|
|
155
|
+
|
|
98
156
|
## strict モード
|
|
99
157
|
|
|
100
158
|
開発・テスト環境で「スコープ外で作られた Image」を検知したいときに使います。
|
|
@@ -106,23 +164,44 @@ end
|
|
|
106
164
|
```
|
|
107
165
|
|
|
108
166
|
- `:off` — 何もしない(本番デフォルト)
|
|
109
|
-
- `:warn` — `warn`
|
|
167
|
+
- `:warn` — `warn` で標準エラー出力に通知(`logger` 設定時はそちらへ出力)
|
|
110
168
|
- `:raise` — `RmagickTidy::OutOfScopeError` を発生
|
|
111
169
|
|
|
112
|
-
|
|
170
|
+
`:warn` の出力先を標準エラー出力以外にしたい場合は、`warn(message)` に応答するオブジェクト(例: `Rails.logger`)を `logger` に設定します。`logger` が `nil`(デフォルト)のときは従来通り `Kernel#warn` にフォールバックします。Railtie は自動では設定しないため、Rails アプリでは initializer で明示的に指定してください。
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
RmagickTidy.configure do |c|
|
|
174
|
+
c.strict_mode = :warn
|
|
175
|
+
c.logger = Rails.logger
|
|
176
|
+
end
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
> **Configuration はスレッドセーフではありません。** `strict_mode`・`logger`(および将来追加されるオプション)は **起動時に 1 回だけ**設定してください(例: Rails の initializer)。ワーカースレッドが `Magick::Image` を使い始めたあとに別スレッドから書き換える挙動は未定義です。
|
|
180
|
+
|
|
181
|
+
## メモリ使用量のベンチマーク
|
|
182
|
+
|
|
183
|
+
[`benchmark/memory.rb`](benchmark/memory.rb) は、生成した画像の read / resize をループで繰り返し、`RmagickTidy.scope` なし・ありの 2 パターンでプロセスの RSS を記録します。
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
bundle exec ruby benchmark/memory.rb
|
|
187
|
+
ITERATIONS=100 SIZE=4000 bundle exec ruby benchmark/memory.rb
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
デフォルト設定(50 回、3000x3000 の画像。Linux 上の Ruby 3.4 / RMagick 7.2 / ImageMagick 7.1 Q16-HDRI)では、scope なしで RSS が約 340 MB まで増え、scope ありでは約 57 MB で安定しました。具体的な数値は環境や ImageMagick のビルドによって変わります。
|
|
113
191
|
|
|
114
192
|
## 仕組み
|
|
115
193
|
|
|
116
194
|
- `Magick::Image` / `Magick::ImageList` に対し、`Module#prepend` で全ての public instance method(および `new`, `read`, `from_blob`, ...のクラスメソッド)をラップ
|
|
117
195
|
- **戻り値の型をチェック**して `Magick::Image` であれば現在のスコープに登録(ホワイトリストを持たないので RMagick のバージョン差異を吸収)
|
|
118
|
-
- 戻り値が `self` と `equal?` なら bang
|
|
196
|
+
- 戻り値が `self` と `equal?` なら bang メソッドとみなして登録しない。同様に、Image を登録するのは初めて見たときだけ(`ObjectSpace::WeakMap` で同一性を記録)。`ImageList#[]`・`#first`・`#pop`・`#delete` が返す要素や、浅いコピーである `ImageList#dup` の中身は再登録しないので、スコープ外で作ったリストを操作しても元の画像は破棄されない
|
|
197
|
+
- `dup` / `clone` もラップする。`Magick::Image#dup` / `#clone` は新しい画像を確保するので、他のメソッドと同様に登録される
|
|
119
198
|
- スコープスタックは `Thread.current` 配下なのでマルチスレッド環境でも安全
|
|
120
199
|
- 二重 `destroy!` は `destroyed?` 判定 + rescue で防止
|
|
121
200
|
|
|
122
201
|
## 対応バージョン
|
|
123
202
|
|
|
124
|
-
- Ruby 3.
|
|
125
|
-
- RMagick
|
|
203
|
+
- Ruby 3.3 以上
|
|
204
|
+
- RMagick 5.x 〜 7.x(CI では 6.x / 7.x でテスト)
|
|
126
205
|
|
|
127
206
|
## ライセンス
|
|
128
207
|
|
data/README.md
CHANGED
|
@@ -95,6 +95,64 @@ end
|
|
|
95
95
|
|
|
96
96
|
`to_blob` returns a `String`, so it is never treated as a cleanup target — only the image objects are released.
|
|
97
97
|
|
|
98
|
+
## Other frameworks and job queues
|
|
99
|
+
|
|
100
|
+
The gem itself only hooks into Rails controllers. Anywhere else, wrap the unit of work in `RmagickTidy.scope` yourself. It only takes a few lines, and keeping them in your app means you control them when those libraries change.
|
|
101
|
+
|
|
102
|
+
### Rack middleware (Sinatra, Hanami, plain Rack, ...)
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
class RmagickTidyMiddleware
|
|
106
|
+
def initialize(app)
|
|
107
|
+
@app = app
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def call(env)
|
|
111
|
+
RmagickTidy.scope { @app.call(env) }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
use RmagickTidyMiddleware
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The scope ends when the app returns, before the server sends the body. That is fine for bodies whose content is already built (an `Array` of strings, a Rails or Sinatra response). If a streaming body or an `Enumerator` creates or uses images while it is being sent, do that image work inside the body, in its own `RmagickTidy.scope`; images created by the app and used later by the body are already released by then.
|
|
119
|
+
|
|
120
|
+
### ActiveJob
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
class ApplicationJob < ActiveJob::Base
|
|
124
|
+
around_perform do |_job, block|
|
|
125
|
+
RmagickTidy.scope do
|
|
126
|
+
block.call
|
|
127
|
+
nil
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The `nil` stops the scope from keeping what `perform` returns. A `perform` that ends with `img.write(path)` returns `img` itself, which would otherwise stay alive until GC.
|
|
134
|
+
|
|
135
|
+
### Sidekiq
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
class RmagickTidySidekiqMiddleware
|
|
139
|
+
def call(_job, _payload, _queue)
|
|
140
|
+
RmagickTidy.scope do
|
|
141
|
+
yield
|
|
142
|
+
nil
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
Sidekiq.configure_server do |config|
|
|
148
|
+
config.server_middleware do |chain|
|
|
149
|
+
chain.add RmagickTidySidekiqMiddleware
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For ActiveJob jobs running on Sidekiq, either the `around_perform` above or this middleware is enough; using both just nests the scopes.
|
|
155
|
+
|
|
98
156
|
## Strict mode
|
|
99
157
|
|
|
100
158
|
Use strict mode in development or test environments to catch images created outside of a scope.
|
|
@@ -106,26 +164,50 @@ end
|
|
|
106
164
|
```
|
|
107
165
|
|
|
108
166
|
- `:off` — do nothing (the production default)
|
|
109
|
-
- `:warn` — print a warning to stderr
|
|
167
|
+
- `:warn` — print a warning to stderr (or to `logger`, if set)
|
|
110
168
|
- `:raise` — raise `RmagickTidy::OutOfScopeError`
|
|
111
169
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
170
|
+
To send `:warn` messages to a logger instead of stderr, set `logger` to any
|
|
171
|
+
object that responds to `warn(message)` (e.g. `Rails.logger`). When `logger`
|
|
172
|
+
is `nil` (the default), the warning falls back to `Kernel#warn`. The Railtie
|
|
173
|
+
does not set it for you, so Rails apps opt in from an initializer:
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
RmagickTidy.configure do |c|
|
|
177
|
+
c.strict_mode = :warn
|
|
178
|
+
c.logger = Rails.logger
|
|
179
|
+
end
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
> **Configuration is not thread-safe.** Set `strict_mode` and `logger` (and
|
|
183
|
+
> any future options) **once at boot** — for example from a Rails initializer —
|
|
184
|
+
> before any worker thread starts using `Magick::Image`. Reading and writing the value
|
|
115
185
|
> from multiple threads concurrently is undefined.
|
|
116
186
|
|
|
187
|
+
## Memory benchmark
|
|
188
|
+
|
|
189
|
+
[`benchmark/memory.rb`](benchmark/memory.rb) reads and resizes a generated image in a loop, once without `RmagickTidy.scope` and once with it, and prints the process RSS as it goes:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
bundle exec ruby benchmark/memory.rb
|
|
193
|
+
ITERATIONS=100 SIZE=4000 bundle exec ruby benchmark/memory.rb
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
With the defaults (50 iterations, 3000x3000 source; Ruby 3.4, RMagick 7.2, ImageMagick 7.1 Q16-HDRI on Linux), RSS climbed to about 340 MB without the scope and stayed at about 57 MB with it. Exact numbers depend on your platform and ImageMagick build.
|
|
197
|
+
|
|
117
198
|
## How it works
|
|
118
199
|
|
|
119
200
|
- `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
201
|
- 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.
|
|
202
|
+
- If the return value is `equal?` to `self`, it is treated as a bang method and not registered. Likewise, an image is registered only the first time it is seen (tracked by identity in an `ObjectSpace::WeakMap`): images handed back from a list, such as by `ImageList#[]`, `#first`, `#pop`, `#delete`, or the shallow copy from `ImageList#dup`, are not registered again, so working with a list created outside the scope does not destroy its images.
|
|
203
|
+
- `dup` / `clone` are wrapped too: `Magick::Image#dup` and `#clone` allocate a new image, which is tracked like any other.
|
|
122
204
|
- The scope stack lives in `Thread.current`, so it is safe to use under multi-threaded servers such as Puma.
|
|
123
205
|
- Calling `destroy!` twice is guarded against with a `destroyed?` check and a defensive `rescue`.
|
|
124
206
|
|
|
125
207
|
## Compatibility
|
|
126
208
|
|
|
127
|
-
- Ruby 3.
|
|
128
|
-
- RMagick
|
|
209
|
+
- Ruby 3.3 or newer
|
|
210
|
+
- RMagick 5.x through 7.x (CI tests against 6.x and 7.x)
|
|
129
211
|
|
|
130
212
|
## License
|
|
131
213
|
|
|
@@ -3,8 +3,13 @@ module RmagickTidy
|
|
|
3
3
|
# :off (default), :warn, :raise
|
|
4
4
|
attr_accessor :strict_mode
|
|
5
5
|
|
|
6
|
+
# Receives strict_mode :warn messages via #warn (e.g. Rails.logger).
|
|
7
|
+
# nil (default) falls back to Kernel#warn, i.e. stderr.
|
|
8
|
+
attr_accessor :logger
|
|
9
|
+
|
|
6
10
|
def initialize
|
|
7
11
|
@strict_mode = :off
|
|
12
|
+
@logger = nil
|
|
8
13
|
end
|
|
9
14
|
end
|
|
10
15
|
|
data/lib/rmagick_tidy/hook.rb
CHANGED
|
@@ -8,6 +8,11 @@ module RmagickTidy
|
|
|
8
8
|
tainted? untrusted? object_id class itself
|
|
9
9
|
].freeze
|
|
10
10
|
|
|
11
|
+
# Copy methods inherited from Kernel, so public_instance_methods(false)
|
|
12
|
+
# misses them. Image#dup / #clone allocate a new C image; ImageList's
|
|
13
|
+
# share the receiver's images, which Tracker.track has already seen.
|
|
14
|
+
COPY_METHODS = %i[dup clone].freeze
|
|
15
|
+
|
|
11
16
|
# Class-level methods on Magick::Image that produce new images.
|
|
12
17
|
CLASS_METHODS = %i[new read ping from_blob read_inline capture constitute combine].freeze
|
|
13
18
|
|
|
@@ -32,7 +37,7 @@ module RmagickTidy
|
|
|
32
37
|
mod = Module.new do
|
|
33
38
|
def self.inspect = "#<RmagickTidy::InstanceHook>"
|
|
34
39
|
end
|
|
35
|
-
method_names = klass.public_instance_methods(false) - SKIP_INSTANCE_METHODS
|
|
40
|
+
method_names = (klass.public_instance_methods(false) | COPY_METHODS) - SKIP_INSTANCE_METHODS
|
|
36
41
|
method_names.each do |name|
|
|
37
42
|
next if name.to_s.end_with?("=")
|
|
38
43
|
|
data/lib/rmagick_tidy/tracker.rb
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
module RmagickTidy
|
|
2
2
|
module Tracker
|
|
3
|
+
# Every Image that has passed through track, held weakly and compared by
|
|
4
|
+
# identity (RMagick maps one Ruby object to one C image). An image seen
|
|
5
|
+
# before is not a new allocation, whichever method hands it back:
|
|
6
|
+
# ImageList#dup, #[], #first share the list's images, and #pop, #shift,
|
|
7
|
+
# #delete return ones taken out of it. Registering those would destroy
|
|
8
|
+
# images that belong to an outer scope or to no scope at all.
|
|
9
|
+
SEEN = ObjectSpace::WeakMap.new
|
|
10
|
+
|
|
3
11
|
module_function
|
|
4
12
|
|
|
5
13
|
def track(result, receiver = nil)
|
|
6
14
|
return result if receiver && result.equal?(receiver)
|
|
7
15
|
|
|
8
16
|
each_image(result) do |img|
|
|
17
|
+
next if SEEN.key?(img)
|
|
18
|
+
|
|
19
|
+
SEEN[img] = true
|
|
9
20
|
register(img)
|
|
10
21
|
end
|
|
11
22
|
result
|
|
@@ -50,7 +61,16 @@ module RmagickTidy
|
|
|
50
61
|
raise OutOfScopeError,
|
|
51
62
|
"Magick::Image #{img.inspect} was created outside of RmagickTidy.scope"
|
|
52
63
|
when :warn
|
|
53
|
-
|
|
64
|
+
report("[rmagick_tidy] Magick::Image created outside of RmagickTidy.scope (#{img.class})")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def report(message)
|
|
69
|
+
logger = RmagickTidy.configuration.logger
|
|
70
|
+
if logger
|
|
71
|
+
logger.warn(message)
|
|
72
|
+
else
|
|
73
|
+
warn message
|
|
54
74
|
end
|
|
55
75
|
end
|
|
56
76
|
end
|
data/lib/rmagick_tidy/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rmagick_tidy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Junichiro Kasuya
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-11-06 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rmagick
|
|
@@ -77,14 +77,14 @@ dependencies:
|
|
|
77
77
|
requirements:
|
|
78
78
|
- - "~>"
|
|
79
79
|
- !ruby/object:Gem::Version
|
|
80
|
-
version: '0
|
|
80
|
+
version: '1.0'
|
|
81
81
|
type: :development
|
|
82
82
|
prerelease: false
|
|
83
83
|
version_requirements: !ruby/object:Gem::Requirement
|
|
84
84
|
requirements:
|
|
85
85
|
- - "~>"
|
|
86
86
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: '0
|
|
87
|
+
version: '1.0'
|
|
88
88
|
- !ruby/object:Gem::Dependency
|
|
89
89
|
name: simplecov-lcov
|
|
90
90
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -125,7 +125,7 @@ licenses:
|
|
|
125
125
|
metadata:
|
|
126
126
|
homepage_uri: https://github.com/jksy/rmagick_tidy
|
|
127
127
|
source_code_uri: https://github.com/jksy/rmagick_tidy/tree/main
|
|
128
|
-
changelog_uri: https://github.com/jksy/rmagick_tidy/
|
|
128
|
+
changelog_uri: https://github.com/jksy/rmagick_tidy/releases
|
|
129
129
|
bug_tracker_uri: https://github.com/jksy/rmagick_tidy/issues
|
|
130
130
|
rubygems_mfa_required: 'true'
|
|
131
131
|
rdoc_options: []
|
|
@@ -135,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
135
135
|
requirements:
|
|
136
136
|
- - ">="
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: 3.
|
|
138
|
+
version: 3.3.0
|
|
139
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
140
|
requirements:
|
|
141
141
|
- - ">="
|