rabarber 5.2.3 → 5.2.5
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 +13 -1
- data/README.md +89 -78
- data/lib/rabarber/controllers/concerns/authorization.rb +1 -1
- data/lib/rabarber/version.rb +1 -1
- data/rabarber.gemspec +6 -4
- metadata +9 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23cc4a1c2e65a3b450653b65df49633601756ada11844173b0c6314c41067ca9
|
|
4
|
+
data.tar.gz: 95153a3172c58bf636cb4d17981ba70336a292368f6bac26c2b6a107c4fcbaaa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 180407341e8e4e700fff624ab12de40d9aa66ed4e132e8ddc92c0c9cb4c010309194e7b37bc825a9a92a0e9baf64022a165788055ccba6b41e60aab8d040fc20
|
|
7
|
+
data.tar.gz: 882d08be5cab1db66d0056ef5e0ace724d2d8ce633cdf75af808d28980faeadbdf9e56c4362e3b7197a89b0de80925f77b2dafb12c5f6e44b1f21cb43be0730d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## v5.2.5
|
|
2
|
+
|
|
3
|
+
### Misc:
|
|
4
|
+
|
|
5
|
+
- Added support for Ruby 4
|
|
6
|
+
|
|
7
|
+
## v5.2.4
|
|
8
|
+
|
|
9
|
+
### Misc:
|
|
10
|
+
|
|
11
|
+
- Added support for Rails 8.1
|
|
12
|
+
|
|
1
13
|
## v5.2.3
|
|
2
14
|
|
|
3
15
|
### Bugs:
|
|
@@ -85,7 +97,7 @@ To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/e
|
|
|
85
97
|
|
|
86
98
|
- Optimized various parts of the code and database queries for improved performance
|
|
87
99
|
- Streamlined the authorization process by requiring the user to be authenticated before access is verified
|
|
88
|
-
- Rabarber now skips roles with missing instance context
|
|
100
|
+
- Rabarber now skips roles with missing instance context; missing class context still raises errors
|
|
89
101
|
|
|
90
102
|
## v4.1.4
|
|
91
103
|
|
data/README.md
CHANGED
|
@@ -5,15 +5,13 @@
|
|
|
5
5
|
[](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
Rabarber is a role-based authorization library for Ruby on Rails
|
|
8
|
+
Rabarber is a role-based authorization library for Ruby on Rails. It provides a set of tools for managing user roles and defining access rules, with support for multi-tenancy through context.
|
|
9
9
|
|
|
10
|
-
**
|
|
10
|
+
**Example of Usage:**
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- Dynamic authorization with conditional logic
|
|
16
|
-
- View helpers for role-based content rendering
|
|
12
|
+
Consider a CRM system where users with different roles have distinct access levels. For instance, the role `accountant` can access invoice data but not marketing information, while the role `analyst` can view marketing data but not detailed financial records. You can define such authorization rules easily with Rabarber.
|
|
13
|
+
|
|
14
|
+
___
|
|
17
15
|
|
|
18
16
|
And this is how your controller might look with Rabarber:
|
|
19
17
|
|
|
@@ -23,25 +21,28 @@ class TicketsController < ApplicationController
|
|
|
23
21
|
|
|
24
22
|
grant_access action: :index, roles: :manager
|
|
25
23
|
def index
|
|
26
|
-
#
|
|
24
|
+
# ...
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
def destroy
|
|
30
|
-
#
|
|
28
|
+
# ...
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
31
|
```
|
|
34
32
|
|
|
33
|
+
This means that `admin` users can access everything in `TicketsController`, while the `manager` role can access only the `index` action.
|
|
34
|
+
|
|
35
35
|
## Table of Contents
|
|
36
36
|
|
|
37
37
|
**Gem Usage:**
|
|
38
38
|
- [Installation](#installation)
|
|
39
39
|
- [Configuration](#configuration)
|
|
40
40
|
- [User Role Methods](#user-role-methods)
|
|
41
|
-
- [Role Management](#role-management)
|
|
42
|
-
- [
|
|
43
|
-
- [Dynamic Rules](#dynamic-rules)
|
|
44
|
-
- [
|
|
41
|
+
- [Direct Role Management](#direct-role-management)
|
|
42
|
+
- [Authorization](#authorization)
|
|
43
|
+
- [Dynamic Authorization Rules](#dynamic-authorization-rules)
|
|
44
|
+
- [When Unauthorized](#when-unauthorized)
|
|
45
|
+
- [Context / Multi-tenancy](#context--multi-tenancy)
|
|
45
46
|
- [View Helpers](#view-helpers)
|
|
46
47
|
|
|
47
48
|
**Community Resources:**
|
|
@@ -64,7 +65,7 @@ Install the gem:
|
|
|
64
65
|
bundle install
|
|
65
66
|
```
|
|
66
67
|
|
|
67
|
-
Generate the migration
|
|
68
|
+
Generate the migration to store roles (replace `users` with your table name if different):
|
|
68
69
|
|
|
69
70
|
```shell
|
|
70
71
|
# For standard integer IDs
|
|
@@ -82,7 +83,7 @@ rails db:migrate
|
|
|
82
83
|
|
|
83
84
|
## Configuration
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
Create an initializer to customize Rabarber's behavior (optional):
|
|
86
87
|
|
|
87
88
|
```rb
|
|
88
89
|
Rabarber.configure do |config|
|
|
@@ -92,7 +93,7 @@ Rabarber.configure do |config|
|
|
|
92
93
|
end
|
|
93
94
|
```
|
|
94
95
|
|
|
95
|
-
Roles are cached by default for performance.
|
|
96
|
+
Roles are cached by default for better performance. Clear the cache manually when needed:
|
|
96
97
|
|
|
97
98
|
```rb
|
|
98
99
|
Rabarber::Cache.clear
|
|
@@ -106,13 +107,13 @@ Your user model is automatically augmented with role management methods:
|
|
|
106
107
|
|
|
107
108
|
```rb
|
|
108
109
|
# Assign roles (creates roles if they don't exist)
|
|
109
|
-
user.assign_roles(:
|
|
110
|
+
user.assign_roles(:admin, :manager)
|
|
110
111
|
|
|
111
|
-
# Assign only existing roles
|
|
112
|
+
# Assign only existing roles (don't create new ones)
|
|
112
113
|
user.assign_roles(:accountant, :manager, create_new: false)
|
|
113
114
|
|
|
114
115
|
# Revoke specific roles
|
|
115
|
-
user.revoke_roles(:
|
|
116
|
+
user.revoke_roles(:admin, :manager)
|
|
116
117
|
|
|
117
118
|
# Revoke all roles
|
|
118
119
|
user.revoke_all_roles
|
|
@@ -136,9 +137,9 @@ user.all_roles
|
|
|
136
137
|
User.with_role(:admin, :manager)
|
|
137
138
|
```
|
|
138
139
|
|
|
139
|
-
## Role Management
|
|
140
|
+
## Direct Role Management
|
|
140
141
|
|
|
141
|
-
|
|
142
|
+
You can also manage roles directly:
|
|
142
143
|
|
|
143
144
|
```rb
|
|
144
145
|
# Create a new role
|
|
@@ -159,41 +160,39 @@ Rabarber.roles
|
|
|
159
160
|
Rabarber.all_roles
|
|
160
161
|
```
|
|
161
162
|
|
|
162
|
-
> **Note:** Some methods have been deprecated in favor of the new API shown above.
|
|
163
|
+
> **Note:** Some methods have been deprecated in favor of the new API shown above. Deprecated methods still work but will be removed in a future major version. See the [changelog](https://github.com/enjaku4/rabarber/blob/main/CHANGELOG.md#v520) for the complete list of deprecated methods and their replacements.
|
|
163
164
|
|
|
164
|
-
##
|
|
165
|
+
## Authorization
|
|
165
166
|
|
|
166
|
-
###
|
|
167
|
+
### Setup
|
|
167
168
|
|
|
168
|
-
Include
|
|
169
|
+
Include `Rabarber::Authorization` module in your controllers and configure protection:
|
|
169
170
|
|
|
170
171
|
```rb
|
|
171
172
|
class ApplicationController < ActionController::Base
|
|
172
173
|
include Rabarber::Authorization
|
|
173
174
|
|
|
174
|
-
with_authorization # Enable authorization for all actions
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
class InvoicesController < ApplicationController
|
|
178
|
-
with_authorization only: [:update, :destroy] # Selective authorization
|
|
175
|
+
with_authorization # Enable authorization check for all actions in all controllers by default
|
|
179
176
|
end
|
|
180
177
|
```
|
|
181
178
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
### Skip Authorization
|
|
185
|
-
|
|
186
|
-
You can also selectively skip authorization:
|
|
179
|
+
You can also enable authorization checks selectively. Both `with_authorization` and `skip_authorization` work exactly the same as Rails' `before_action` and `skip_before_action` methods:
|
|
187
180
|
|
|
188
181
|
```rb
|
|
189
182
|
class TicketsController < ApplicationController
|
|
190
|
-
skip_authorization
|
|
183
|
+
skip_authorization only: [:index, :show] # Skip authorization for specific actions
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
class InvoicesController < ApplicationController
|
|
187
|
+
with_authorization except: [:index] # Enable authorization for all actions except index
|
|
191
188
|
end
|
|
192
189
|
```
|
|
193
190
|
|
|
191
|
+
Authorization requires an authenticated user. Rabarber will raise an error if no user is found via the configured `current_user_method`. Ensure authentication happens before authorization.
|
|
192
|
+
|
|
194
193
|
### Authorization Rules
|
|
195
194
|
|
|
196
|
-
Define
|
|
195
|
+
Define authorization rules using `grant_access`:
|
|
197
196
|
|
|
198
197
|
```rb
|
|
199
198
|
class TicketsController < ApplicationController
|
|
@@ -212,9 +211,7 @@ class TicketsController < ApplicationController
|
|
|
212
211
|
end
|
|
213
212
|
```
|
|
214
213
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
Rules are additive across inheritance chains and for the same actions:
|
|
214
|
+
Authorization rules are additive - they combine across inheritance chains and when defined multiple times for the same action:
|
|
218
215
|
|
|
219
216
|
```rb
|
|
220
217
|
class BaseController < ApplicationController
|
|
@@ -232,9 +229,7 @@ class InvoicesController < BaseController
|
|
|
232
229
|
end
|
|
233
230
|
```
|
|
234
231
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
Omit roles to allow unrestricted access:
|
|
232
|
+
It's possible to omit roles to allow unrestricted access:
|
|
238
233
|
|
|
239
234
|
```rb
|
|
240
235
|
class UnrestrictedController < ApplicationController
|
|
@@ -254,64 +249,78 @@ class MixedController < ApplicationController
|
|
|
254
249
|
end
|
|
255
250
|
```
|
|
256
251
|
|
|
257
|
-
|
|
252
|
+
## Dynamic Authorization Rules
|
|
258
253
|
|
|
259
|
-
|
|
254
|
+
For more complex scenarios, Rabarber supports dynamic authorization rules:
|
|
260
255
|
|
|
261
256
|
```rb
|
|
262
|
-
class
|
|
263
|
-
|
|
257
|
+
class OrdersController < ApplicationController
|
|
258
|
+
grant_access roles: :manager, unless: -> { current_user.suspended? }
|
|
264
259
|
|
|
265
|
-
|
|
260
|
+
grant_access action: :show, roles: :client, if: :user_company_matches_order?
|
|
261
|
+
def show
|
|
262
|
+
# ...
|
|
263
|
+
end
|
|
266
264
|
|
|
267
265
|
private
|
|
268
266
|
|
|
269
|
-
def
|
|
270
|
-
|
|
267
|
+
def user_company_matches_order?
|
|
268
|
+
current_user.company == Order.find(params[:id]).company
|
|
271
269
|
end
|
|
272
270
|
end
|
|
273
271
|
```
|
|
274
272
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
## Dynamic Rules
|
|
273
|
+
You can pass a dynamic rule as an `if` or `unless` argument, which can be a symbol or a proc. Symbols refer to instance methods, and procs are evaluated in the controller at request time.
|
|
278
274
|
|
|
279
|
-
|
|
275
|
+
Dynamic rules can also be used without roles at all, allowing you to define custom logic or even delegate to custom policy objects:
|
|
280
276
|
|
|
281
277
|
```rb
|
|
282
|
-
class
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
# Proc-based conditions
|
|
287
|
-
grant_access action: :show, roles: :client, if: -> { current_user.company == Order.find(params[:id]).company }
|
|
288
|
-
def show
|
|
289
|
-
# Accessible to company managers unless suspended, and to clients if the client's company matches the order's company
|
|
278
|
+
class InvoicesController < ApplicationController
|
|
279
|
+
grant_access action: :update, unless: -> { Date.current.on_weekend? }
|
|
280
|
+
def update
|
|
281
|
+
# ...
|
|
290
282
|
end
|
|
291
283
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
# Accessible to company managers unless suspended, and to users based on custom policy logic
|
|
284
|
+
grant_access action: :destroy, if: :destroy_allowed?
|
|
285
|
+
def destroy
|
|
286
|
+
# ...
|
|
296
287
|
end
|
|
297
288
|
|
|
298
289
|
private
|
|
299
290
|
|
|
300
|
-
def
|
|
301
|
-
current_user.
|
|
291
|
+
def destroy_allowed?
|
|
292
|
+
InvoicePolicy.new(current_user).destroy?(Invoice.find(params[:id]))
|
|
302
293
|
end
|
|
294
|
+
end
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## When Unauthorized
|
|
303
298
|
|
|
304
|
-
|
|
305
|
-
|
|
299
|
+
By default, when unauthorized, Rabarber will redirect back (HTML format) or return 403 (other formats). You can override `when_unauthorized` method to customize unauthorized access behavior:
|
|
300
|
+
|
|
301
|
+
```rb
|
|
302
|
+
class ApplicationController < ActionController::Base
|
|
303
|
+
include Rabarber::Authorization
|
|
304
|
+
|
|
305
|
+
with_authorization
|
|
306
|
+
|
|
307
|
+
private
|
|
308
|
+
|
|
309
|
+
def when_unauthorized
|
|
310
|
+
head :not_found # Custom behavior to hide existence of protected resources
|
|
306
311
|
end
|
|
307
312
|
end
|
|
308
313
|
```
|
|
309
314
|
|
|
310
|
-
|
|
315
|
+
The `when_unauthorized` method can be overridden in any controller to provide controller-specific unauthorized access handling.
|
|
316
|
+
|
|
317
|
+
## Context / Multi-tenancy
|
|
311
318
|
|
|
312
|
-
All Rabarber methods accept a `context` parameter, allowing you to work with roles within specific scopes. By default, context is `nil`, meaning roles are global.
|
|
319
|
+
Rabarber supports multi-tenancy through its context feature. All Rabarber methods accept a `context` parameter, allowing you to work with roles within specific scopes. By default, context is `nil`, meaning roles are global. Context can also be an instance of an `ActiveRecord` model or a class.
|
|
313
320
|
|
|
314
|
-
|
|
321
|
+
For example, in a project management app, you might want users to have different roles in different projects - someone could be an `owner` in one project but just a `member` in another.
|
|
322
|
+
|
|
323
|
+
### Contextual Role Assignment and Queries
|
|
315
324
|
|
|
316
325
|
```rb
|
|
317
326
|
# Assign roles within a specific model instance
|
|
@@ -329,7 +338,7 @@ user.has_role?(:admin, context: Project)
|
|
|
329
338
|
user.revoke_roles(:owner, context: project)
|
|
330
339
|
|
|
331
340
|
# Get user roles
|
|
332
|
-
user.roles(context:
|
|
341
|
+
user.roles(context: project)
|
|
333
342
|
|
|
334
343
|
# Get users with a role
|
|
335
344
|
User.with_role(:member, context: project)
|
|
@@ -353,6 +362,8 @@ Rabarber.roles(context: project)
|
|
|
353
362
|
|
|
354
363
|
### Contextual Authorization
|
|
355
364
|
|
|
365
|
+
In authorization rules, in addition to specifying context explicitly, you can also provide a proc or a symbol (similar to dynamic rules):
|
|
366
|
+
|
|
356
367
|
```rb
|
|
357
368
|
class ProjectsController < ApplicationController
|
|
358
369
|
grant_access roles: :admin, context: Project
|
|
@@ -377,11 +388,11 @@ class ProjectsController < ApplicationController
|
|
|
377
388
|
end
|
|
378
389
|
```
|
|
379
390
|
|
|
380
|
-
### Orphaned
|
|
391
|
+
### Orphaned Contextual Roles
|
|
381
392
|
|
|
382
393
|
When a context object is deleted from your database, its associated roles become orphaned and ignored by Rabarber.
|
|
383
394
|
|
|
384
|
-
To clean up orphaned
|
|
395
|
+
To clean up orphaned roles, use:
|
|
385
396
|
|
|
386
397
|
```rb
|
|
387
398
|
Rabarber.prune
|
|
@@ -389,13 +400,13 @@ Rabarber.prune
|
|
|
389
400
|
|
|
390
401
|
### Context Migrations
|
|
391
402
|
|
|
392
|
-
|
|
403
|
+
When you rename or remove models used as contexts, you need to update Rabarber's stored context data accordingly. Use these irreversible data migrations:
|
|
393
404
|
|
|
394
405
|
```rb
|
|
395
406
|
# Rename a context class (e.g., when you rename your Ticket model to Task)
|
|
396
407
|
migrate_authorization_context!("Ticket", "Task")
|
|
397
408
|
|
|
398
|
-
# Remove
|
|
409
|
+
# Remove context data (e.g., when you delete the Ticket model entirely)
|
|
399
410
|
delete_authorization_context!("Ticket")
|
|
400
411
|
```
|
|
401
412
|
|
data/lib/rabarber/version.rb
CHANGED
data/rabarber.gemspec
CHANGED
|
@@ -6,18 +6,20 @@ Gem::Specification.new do |spec|
|
|
|
6
6
|
spec.name = "rabarber"
|
|
7
7
|
spec.version = Rabarber::VERSION
|
|
8
8
|
spec.authors = ["enjaku4", "trafium"]
|
|
9
|
-
spec.email = ["
|
|
9
|
+
spec.email = ["contact@brownbox.dev"]
|
|
10
10
|
spec.homepage = "https://github.com/enjaku4/rabarber"
|
|
11
11
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
12
12
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
13
13
|
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
14
14
|
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
|
15
15
|
spec.metadata["documentation_uri"] = "#{spec.homepage}/blob/main/README.md"
|
|
16
|
+
spec.metadata["mailing_list_uri"] = "#{spec.homepage}/discussions"
|
|
17
|
+
spec.metadata["funding_uri"] = "https://github.com/sponsors/enjaku4"
|
|
16
18
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
17
19
|
spec.summary = "Simple role-based authorization library for Ruby on Rails"
|
|
18
|
-
spec.description = "
|
|
20
|
+
spec.description = "Simple role-based authorization for Rails applications with multi-tenancy support"
|
|
19
21
|
spec.license = "MIT"
|
|
20
|
-
spec.required_ruby_version = ">= 3.2", "<
|
|
22
|
+
spec.required_ruby_version = ">= 3.2", "< 4.1"
|
|
21
23
|
|
|
22
24
|
spec.files = [
|
|
23
25
|
"rabarber.gemspec", "README.md", "CHANGELOG.md", "LICENSE.txt"
|
|
@@ -27,5 +29,5 @@ Gem::Specification.new do |spec|
|
|
|
27
29
|
|
|
28
30
|
spec.add_dependency "dry-configurable", "~> 1.1"
|
|
29
31
|
spec.add_dependency "dry-types", "~> 1.7"
|
|
30
|
-
spec.add_dependency "rails", ">= 7.1", "< 8.
|
|
32
|
+
spec.add_dependency "rails", ">= 7.1", "< 8.2"
|
|
31
33
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rabarber
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.2.
|
|
4
|
+
version: 5.2.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- enjaku4
|
|
@@ -47,7 +47,7 @@ dependencies:
|
|
|
47
47
|
version: '7.1'
|
|
48
48
|
- - "<"
|
|
49
49
|
- !ruby/object:Gem::Version
|
|
50
|
-
version: '8.
|
|
50
|
+
version: '8.2'
|
|
51
51
|
type: :runtime
|
|
52
52
|
prerelease: false
|
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -57,12 +57,11 @@ dependencies:
|
|
|
57
57
|
version: '7.1'
|
|
58
58
|
- - "<"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '8.
|
|
61
|
-
description:
|
|
62
|
-
|
|
63
|
-
control that separates authorization from business logic
|
|
60
|
+
version: '8.2'
|
|
61
|
+
description: Simple role-based authorization for Rails applications with multi-tenancy
|
|
62
|
+
support
|
|
64
63
|
email:
|
|
65
|
-
-
|
|
64
|
+
- contact@brownbox.dev
|
|
66
65
|
executables: []
|
|
67
66
|
extensions: []
|
|
68
67
|
extra_rdoc_files: []
|
|
@@ -106,6 +105,8 @@ metadata:
|
|
|
106
105
|
changelog_uri: https://github.com/enjaku4/rabarber/blob/main/CHANGELOG.md
|
|
107
106
|
bug_tracker_uri: https://github.com/enjaku4/rabarber/issues
|
|
108
107
|
documentation_uri: https://github.com/enjaku4/rabarber/blob/main/README.md
|
|
108
|
+
mailing_list_uri: https://github.com/enjaku4/rabarber/discussions
|
|
109
|
+
funding_uri: https://github.com/sponsors/enjaku4
|
|
109
110
|
rubygems_mfa_required: 'true'
|
|
110
111
|
rdoc_options: []
|
|
111
112
|
require_paths:
|
|
@@ -117,7 +118,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
117
118
|
version: '3.2'
|
|
118
119
|
- - "<"
|
|
119
120
|
- !ruby/object:Gem::Version
|
|
120
|
-
version: '
|
|
121
|
+
version: '4.1'
|
|
121
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
123
|
requirements:
|
|
123
124
|
- - ">="
|