view_component-contrib 0.1.6 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +187 -21
- data/lib/view_component_contrib/style_variants.rb +184 -0
- data/lib/view_component_contrib/version.rb +1 -1
- data/lib/view_component_contrib.rb +1 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b586c9ad03730cff1cc38721c6904f427c71a73f5777605f17fc0379f60f8dc7
|
4
|
+
data.tar.gz: 7c2ed9aed5c845492a8487fab7ce5e66c639d455d8b4027c383b047ad961fdf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1d2f760734b6ca7f71da538fbba4c04b0ff91b885cc47e09f3943b81a5a097ef62293348dc3ac07e20a4b812e52cf8a4bd8b39b6cb306551dceac4a784c652b
|
7
|
+
data.tar.gz: baaf5f34009ca87228246f260357339849f3d22e988543f36c36e772293bede4f249741fee5749db46d37a78dd8dc94674e13181d618282463a31afd5dba48b7
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,20 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.2.1 (2023-11-16)
|
6
|
+
|
7
|
+
- Fix style variants inhertiance. ([@palkan][])
|
8
|
+
|
9
|
+
## 0.2.0 (2023-11-07)
|
10
|
+
|
11
|
+
- Introduce style variants. ([@palkan][])
|
12
|
+
|
13
|
+
- **Require Ruby 2.7+**. ([@palkan][])
|
14
|
+
|
15
|
+
- Add system tests to generator. ([@palkan][])
|
16
|
+
|
17
|
+
- Drop Webpack-related stuff from the generator. ([@palkan][])
|
18
|
+
|
5
19
|
## 0.1.6 (2023-11-07)
|
6
20
|
|
7
21
|
- Support preview classes named `<component|partial>_preview.rb`. ([@palkan][])
|
data/README.md
CHANGED
@@ -32,7 +32,6 @@ The command above:
|
|
32
32
|
- Configure `view_component` paths.
|
33
33
|
- Adds `ApplicationViewComponent` and `ApplicationViewComponentPreview` classes.
|
34
34
|
- Configures testing framework (RSpec or Minitest).
|
35
|
-
- Adds required JS/CSS configuration.
|
36
35
|
- **Adds a custom generator to create components**.
|
37
36
|
|
38
37
|
The custom generator would allow you to create all the required component files in a single command:
|
@@ -94,6 +93,8 @@ ActiveSupport.on_load(:view_component) do
|
|
94
93
|
end
|
95
94
|
```
|
96
95
|
|
96
|
+
You can still continue using preview clases with the `_preview.rb` suffix, they would work as before.
|
97
|
+
|
97
98
|
#### Reducing previews boilerplate
|
98
99
|
|
99
100
|
In most cases, previews contain only the `default` example and a very simple template (`= render Component.new(**options)`).
|
@@ -180,9 +181,125 @@ end
|
|
180
181
|
|
181
182
|
If you need more control over your template, you can add a custom `preview.html.*` template (which will be used for all examples in this preview), or even create an example-specific `previews/example.html.*` (e.g. `previews/mobile.html.erb`).
|
182
183
|
|
184
|
+
## Style variants
|
185
|
+
|
186
|
+
Since v0.2.0, we provide a custom extentions to manage CSS classes and their combinations—**Style Variants**. This is especially useful for project using CSS frameworks such as TailwindCSS.
|
187
|
+
|
188
|
+
The idea is to define variants schema in the component class and use it to compile the resulting list of CSS classes. (Inspired by [Tailwind Variants](https://www.tailwind-variants.org) and [CVA variants](https://cva.style/docs/getting-started/variants)).
|
189
|
+
|
190
|
+
Consider an example:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
class ButtonComponent < ViewComponent::Base
|
194
|
+
include ViewComponentContrib::StyleVariants
|
195
|
+
|
196
|
+
style do
|
197
|
+
base {
|
198
|
+
%w[
|
199
|
+
font-medium bg-blue-500 text-white rounded-full
|
200
|
+
]
|
201
|
+
}
|
202
|
+
variants {
|
203
|
+
color {
|
204
|
+
primary { %w[bg-blue-500 text-white] }
|
205
|
+
secondary { %w[bg-purple-500 text-white] }
|
206
|
+
}
|
207
|
+
size {
|
208
|
+
sm { "text-sm" }
|
209
|
+
md { "text-base" }
|
210
|
+
lg { "px-4 py-3 text-lg" }
|
211
|
+
}
|
212
|
+
}
|
213
|
+
defaults { {size: :md, color: :primary} }
|
214
|
+
end
|
215
|
+
|
216
|
+
attr_reader :size, :color
|
217
|
+
|
218
|
+
def initialize(size: nil, color: nil)
|
219
|
+
@size = size
|
220
|
+
@color = color
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
Now, in the template, you can use the `#style` method and pass the variants to it:
|
226
|
+
|
227
|
+
```erb
|
228
|
+
<button class="<%= style(size:, color:) %>">Click me</button>
|
229
|
+
```
|
230
|
+
|
231
|
+
Passing `size: :lg` and `color: :secondary` would result in the following HTML:
|
232
|
+
|
233
|
+
```html
|
234
|
+
<button class="font-medium bg-purple-500 text-white rounded-full px-4 py-3 text-lg">Click me</button>
|
235
|
+
```
|
236
|
+
|
237
|
+
**NOTE:** If you pass `nil`, the default value would be used.
|
238
|
+
|
239
|
+
You can define multiple style sets in a single component:
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
class ButtonComponent < ViewComponent::Base
|
243
|
+
include ViewComponentContrib::StyleVariants
|
244
|
+
|
245
|
+
# default component styles
|
246
|
+
style do
|
247
|
+
# ...
|
248
|
+
end
|
249
|
+
|
250
|
+
style :image do
|
251
|
+
variants {
|
252
|
+
orient {
|
253
|
+
portrait { "w-32 h-32" }
|
254
|
+
landscape { "w-64 h-32" }
|
255
|
+
}
|
256
|
+
}
|
257
|
+
end
|
258
|
+
end
|
259
|
+
```
|
260
|
+
|
261
|
+
And in the template:
|
262
|
+
|
263
|
+
```erb
|
264
|
+
<div>
|
265
|
+
<button class="<%= style(size:, theme:) %>">Click me</button>
|
266
|
+
<img src="..." class="<%= style(:image, orient: :portrait) %>">
|
267
|
+
</div>
|
268
|
+
```
|
269
|
+
|
270
|
+
Finally, you can inject into the class list compilation process to add your own logic:
|
271
|
+
|
272
|
+
```ruby
|
273
|
+
class ButtonComponent < ViewComponent::Base
|
274
|
+
include ViewComponentContrib::StyleVariants
|
275
|
+
|
276
|
+
# You can provide either a proc or any other callable object
|
277
|
+
style_config.postprocess_with do |classes|
|
278
|
+
# classes is an array of CSS classes
|
279
|
+
TailwindMerge.call(classes).join(" ")
|
280
|
+
end
|
281
|
+
end
|
282
|
+
```
|
283
|
+
|
284
|
+
### Using with TailwindCSS LSP
|
285
|
+
|
286
|
+
To make completions (and other LSP features) work with our DSL, try the following configuration:
|
287
|
+
|
288
|
+
```json
|
289
|
+
"tailwindCSS.includeLanguages": {
|
290
|
+
"erb": "html",
|
291
|
+
"ruby": "html"
|
292
|
+
},
|
293
|
+
"tailwindCSS.experimental.classRegex": [
|
294
|
+
"%w\\[([^\\]]*)\\]"
|
295
|
+
]
|
296
|
+
```
|
297
|
+
|
298
|
+
**NOTE:** It will only work with `%w[ ... ]` word arrays, but you can adjust it to your needs.
|
299
|
+
|
183
300
|
## Organizing assets (JS, CSS)
|
184
301
|
|
185
|
-
**NOTE
|
302
|
+
**NOTE**: This section assumes the usage of Vite or Webpack. See [this discussion](https://github.com/palkan/view_component-contrib/discussions/14) for other options.
|
186
303
|
|
187
304
|
We store JS and CSS files in the same sidecar folder:
|
188
305
|
|
@@ -203,6 +320,18 @@ import "./index.css"
|
|
203
320
|
|
204
321
|
In the root of the `components` folder we have the `index.js` file, which loads all the components:
|
205
322
|
|
323
|
+
- With Vite:
|
324
|
+
|
325
|
+
```js
|
326
|
+
// With Vite
|
327
|
+
import.meta.glob("./**/index.js").forEach((path) => {
|
328
|
+
const mod = await import(path);
|
329
|
+
mod.default();
|
330
|
+
});
|
331
|
+
```
|
332
|
+
|
333
|
+
- With Webpack:
|
334
|
+
|
206
335
|
```js
|
207
336
|
// components/index.js
|
208
337
|
const context = require.context(".", true, /index.js$/)
|
@@ -211,12 +340,11 @@ context.keys().forEach(context);
|
|
211
340
|
|
212
341
|
### Using with StimulusJS
|
213
342
|
|
214
|
-
You can define Stimulus controllers right in the `
|
343
|
+
You can define Stimulus controllers right in the component folder in the `controller.js` file:
|
215
344
|
|
216
345
|
```js
|
217
|
-
import "./index.css"
|
218
346
|
// We reserve Controller for the export name
|
219
|
-
import { Controller as BaseController } from "stimulus";
|
347
|
+
import { Controller as BaseController } from "@hotwired/stimulus";
|
220
348
|
|
221
349
|
export class Controller extends BaseController {
|
222
350
|
connect() {
|
@@ -225,20 +353,60 @@ export class Controller extends BaseController {
|
|
225
353
|
}
|
226
354
|
```
|
227
355
|
|
228
|
-
Then,
|
356
|
+
Then, in your Stimulus entrypoint, you can load and register your component controllers as follows:
|
357
|
+
|
358
|
+
- With Vite:
|
229
359
|
|
230
360
|
```js
|
231
|
-
|
232
|
-
|
361
|
+
import { Application } from "@hotwired/stimulus";
|
362
|
+
|
363
|
+
const application = Application.start();
|
364
|
+
|
365
|
+
// Configure Stimulus development experience
|
366
|
+
application.debug = false;
|
367
|
+
window.Stimulus = application;
|
368
|
+
|
369
|
+
// Generic controllers
|
370
|
+
const genericControllers = import.meta.globEager(
|
371
|
+
"../controllers/**/*_controller.js"
|
372
|
+
);
|
373
|
+
|
374
|
+
for (let path in genericControllers) {
|
375
|
+
let module = genericControllers[path];
|
376
|
+
let name = path
|
377
|
+
.match(/controllers\/(.+)_controller\.js$/)[1]
|
378
|
+
.replaceAll("/", "-")
|
379
|
+
.replaceAll("_", "-");
|
380
|
+
|
381
|
+
application.register(name, module.default);
|
382
|
+
}
|
383
|
+
|
384
|
+
// Controllers from components
|
385
|
+
const controllers = import.meta.globEager(
|
386
|
+
"./../../app/frontend/components/**/controller.js"
|
387
|
+
);
|
388
|
+
|
389
|
+
for (let path in controllers) {
|
390
|
+
let module = controllers[path];
|
391
|
+
let name = path
|
392
|
+
.match(/app\/frontend\/components\/(.+)\/controller\.js$/)[1]
|
393
|
+
.replaceAll("/", "-")
|
394
|
+
.replaceAll("_", "-");
|
395
|
+
application.register(name, module.default);
|
396
|
+
}
|
233
397
|
|
234
|
-
|
398
|
+
export default application;
|
399
|
+
```
|
400
|
+
|
401
|
+
- With Webpack:
|
402
|
+
|
403
|
+
```js
|
235
404
|
import { Application } from "stimulus";
|
236
405
|
export const application = Application.start();
|
237
406
|
|
238
|
-
//
|
239
|
-
import { application } from "../init/stimulus";
|
407
|
+
// ... other controllers
|
240
408
|
|
241
|
-
const context = require.context("
|
409
|
+
const context = require.context("./../../app/frontend/components/", true, /controllers.js$/)
|
242
410
|
context.keys().forEach((path) => {
|
243
411
|
const mod = context(path);
|
244
412
|
|
@@ -248,9 +416,10 @@ context.keys().forEach((path) => {
|
|
248
416
|
// Convert path into a controller identifier:
|
249
417
|
// example/index.js -> example
|
250
418
|
// nav/user_info/index.js -> nav--user-info
|
251
|
-
const identifier = path
|
252
|
-
.
|
253
|
-
.
|
419
|
+
const identifier = path
|
420
|
+
.match(/app\/frontend\/components\/(.+)\/controller\.js$/)[1]
|
421
|
+
.replaceAll("/", "-")
|
422
|
+
.replaceAll("_", "-");
|
254
423
|
|
255
424
|
application.register(identifier, mod.Controller);
|
256
425
|
});
|
@@ -265,6 +434,8 @@ class ApplicationViewComponent
|
|
265
434
|
def identifier
|
266
435
|
@identifier ||= self.class.name.sub("::Component", "").underscore.split("/").join("--")
|
267
436
|
end
|
437
|
+
|
438
|
+
alias_method :controller_name, :identifier
|
268
439
|
end
|
269
440
|
```
|
270
441
|
|
@@ -272,7 +443,7 @@ And now in your template:
|
|
272
443
|
|
273
444
|
```erb
|
274
445
|
<!-- component.html -->
|
275
|
-
<div data-controller="<%=
|
446
|
+
<div data-controller="<%= controller_name %>">
|
276
447
|
</div>
|
277
448
|
```
|
278
449
|
|
@@ -495,11 +666,6 @@ And the template looks like this now:
|
|
495
666
|
|
496
667
|
You can use the `#wrapped` method on any component inherited from `ApplicationViewComponent` to wrap it automatically:
|
497
668
|
|
498
|
-
## ToDo list
|
499
|
-
|
500
|
-
- Better preview tools (w/o JS deps 😉).
|
501
|
-
- Hotwire-related extensions.
|
502
|
-
|
503
669
|
## License
|
504
670
|
|
505
671
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponentContrib
|
4
|
+
# Organize style in variants that can be combined.
|
5
|
+
# Inspired by https://www.tailwind-variants.org and https://cva.style/docs/getting-started/variants
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# class ButtonComponent < ViewComponent::Base
|
10
|
+
# include ViewComponentContrib::StyleVariants
|
11
|
+
#
|
12
|
+
# erb_template <<~ERB
|
13
|
+
# <button class="<%= style(size: 'sm', color: 'secondary') %>">Click me</button>
|
14
|
+
# ERB
|
15
|
+
#
|
16
|
+
# style do
|
17
|
+
# base {
|
18
|
+
# %w(
|
19
|
+
# font-medium bg-blue-500 text-white rounded-full
|
20
|
+
# )
|
21
|
+
# }
|
22
|
+
# variants {
|
23
|
+
# color {
|
24
|
+
# primary { %w(bg-blue-500 text-white) }
|
25
|
+
# secondary { %w(bg-purple-500 text-white) }
|
26
|
+
# }
|
27
|
+
# size {
|
28
|
+
# sm { "text-sm" }
|
29
|
+
# md { "text-base" }
|
30
|
+
# lg { "px-4 py-3 text-lg" }
|
31
|
+
# }
|
32
|
+
# }
|
33
|
+
# defaults { {size: :md, color: :primary} }
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# attr_reader :size, :color
|
37
|
+
#
|
38
|
+
# def initialize(size: :md, color: :primary)
|
39
|
+
# @size = size
|
40
|
+
# @color = color
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
module StyleVariants
|
45
|
+
class VariantBuilder
|
46
|
+
attr_reader :unwrap_blocks
|
47
|
+
|
48
|
+
def initialize(unwrap_blocks = true)
|
49
|
+
@unwrap_blocks = unwrap_blocks
|
50
|
+
@variants = {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def build(&block)
|
54
|
+
instance_eval(&block)
|
55
|
+
@variants
|
56
|
+
end
|
57
|
+
|
58
|
+
def respond_to_missing?(name, include_private = false)
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
def method_missing(name, &block)
|
63
|
+
return super unless block_given?
|
64
|
+
|
65
|
+
@variants[name] = if unwrap_blocks
|
66
|
+
VariantBuilder.new(false).build(&block)
|
67
|
+
else
|
68
|
+
block
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class StyleSet
|
74
|
+
def initialize(&init_block)
|
75
|
+
@base_block = nil
|
76
|
+
@defaults = {}
|
77
|
+
@variants = {}
|
78
|
+
|
79
|
+
instance_eval(&init_block) if init_block
|
80
|
+
end
|
81
|
+
|
82
|
+
def base(&block)
|
83
|
+
@base_block = block
|
84
|
+
end
|
85
|
+
|
86
|
+
def defaults(&block)
|
87
|
+
@defaults = block.call.freeze
|
88
|
+
end
|
89
|
+
|
90
|
+
def variants(&block)
|
91
|
+
@variants = VariantBuilder.new(true).build(&block)
|
92
|
+
end
|
93
|
+
|
94
|
+
def compile(**variants)
|
95
|
+
acc = Array(@base_block&.call || [])
|
96
|
+
|
97
|
+
@defaults.merge(variants.compact).each do |variant, value|
|
98
|
+
variant = @variants.dig(variant, value) || next
|
99
|
+
styles = variant.is_a?(::Proc) ? variant.call : variant
|
100
|
+
acc.concat(Array(styles))
|
101
|
+
end
|
102
|
+
|
103
|
+
acc
|
104
|
+
end
|
105
|
+
|
106
|
+
def dup
|
107
|
+
copy = super
|
108
|
+
copy.instance_variable_set(:@defaults, @defaults.dup)
|
109
|
+
copy.instance_variable_set(:@variants, @variants.dup)
|
110
|
+
copy
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class StyleConfig # :nodoc:
|
115
|
+
DEFAULT_POST_PROCESSOR = ->(compiled) { compiled.join(" ") }
|
116
|
+
|
117
|
+
attr_reader :postprocessor
|
118
|
+
|
119
|
+
def initialize
|
120
|
+
@styles = {}
|
121
|
+
@postprocessor = DEFAULT_POST_PROCESSOR
|
122
|
+
end
|
123
|
+
|
124
|
+
def define(name, &block)
|
125
|
+
styles[name] = StyleSet.new(&block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def compile(name, **variants)
|
129
|
+
styles[name]&.compile(**variants).then do |compiled|
|
130
|
+
next unless compiled
|
131
|
+
|
132
|
+
postprocess(compiled)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Allow defining a custom postprocessor
|
137
|
+
def postprocess_with(callable = nil, &block)
|
138
|
+
@postprocessor = callable || block
|
139
|
+
end
|
140
|
+
|
141
|
+
def dup
|
142
|
+
copy = super
|
143
|
+
copy.instance_variable_set(:@styles, @styles.dup)
|
144
|
+
copy
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
attr_reader :styles
|
150
|
+
|
151
|
+
def postprocess(compiled) = postprocessor.call(compiled)
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.included(base)
|
155
|
+
base.extend ClassMethods
|
156
|
+
end
|
157
|
+
|
158
|
+
module ClassMethods
|
159
|
+
# Returns the name of the default style set based on the class name:
|
160
|
+
# MyComponent::Component => my_component
|
161
|
+
# Namespaced::MyComponent => my_component
|
162
|
+
def default_style_name
|
163
|
+
@default_style_name ||= name.demodulize.sub(/(::Component|Component)$/, "").underscore.presence || "component"
|
164
|
+
end
|
165
|
+
|
166
|
+
def style(name = default_style_name, &block)
|
167
|
+
style_config.define(name.to_sym, &block)
|
168
|
+
end
|
169
|
+
|
170
|
+
def style_config
|
171
|
+
@style_config ||=
|
172
|
+
if superclass.respond_to?(:style_config)
|
173
|
+
superclass.style_config.dup
|
174
|
+
else
|
175
|
+
StyleConfig.new
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def style(name = self.class.default_style_name, **variants)
|
181
|
+
self.class.style_config.compile(name.to_sym, **variants)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -11,6 +11,7 @@ module ViewComponentContrib
|
|
11
11
|
autoload :TranslationHelper, "view_component_contrib/translation_helper"
|
12
12
|
autoload :WrapperComponent, "view_component_contrib/wrapper_component"
|
13
13
|
autoload :WrappedHelper, "view_component_contrib/wrapped_helper"
|
14
|
+
autoload :StyleVariants, "view_component_contrib/style_variants"
|
14
15
|
|
15
16
|
autoload :Base, "view_component_contrib/base"
|
16
17
|
autoload :Preview, "view_component_contrib/preview"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component-contrib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: view_component
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.15.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.15.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,6 +156,7 @@ files:
|
|
156
156
|
- lib/view_component_contrib/preview/default_template.rb
|
157
157
|
- lib/view_component_contrib/preview/sidecarable.rb
|
158
158
|
- lib/view_component_contrib/railtie.rb
|
159
|
+
- lib/view_component_contrib/style_variants.rb
|
159
160
|
- lib/view_component_contrib/translation_helper.rb
|
160
161
|
- lib/view_component_contrib/version.rb
|
161
162
|
- lib/view_component_contrib/wrapped_helper.rb
|
@@ -177,7 +178,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
177
178
|
requirements:
|
178
179
|
- - ">="
|
179
180
|
- !ruby/object:Gem::Version
|
180
|
-
version: '2.
|
181
|
+
version: '2.7'
|
181
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
183
|
requirements:
|
183
184
|
- - ">="
|