hubbado-policy 1.0.1

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: 8338406be7ea8412d5c7d81fee557858ac5327d9f84ef048a662b04f381dcb41
4
+ data.tar.gz: 4f58a42ba93883f48735ab220f9ff8047f360b41ea9d0a6e35c8c21c9a78318e
5
+ SHA512:
6
+ metadata.gz: 4e084a506c028722b36741f9d93f0f0cf6385d04c8aedbab34b3053eaaeb804e5734bc93466351f2c4ecfe486ea5763ba6f5ed2700877fbeb445a828eb61e1b2
7
+ data.tar.gz: 48fc0f6b56cd3dc53da3bc79e73fb9ba41bd35795e3e9da0b3e8b9d82164aafa07cb656047debfb8bcd067ca916b5b84e0e3012cb0bc1497d051ff23d870f480
data/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
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.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.1] - 2025-05-20
9
+
10
+ Bump because rubygems does not allow repushing the same version even if it is yanked.
11
+
12
+ ## [1.0.0] - 2025-05-20
13
+
14
+ ### Added
15
+ - Initial release of the hubbado-policy gem
16
+ - `Policy` class with DSL for defining authorization rules
17
+ - `Result` class to represent policy outcomes
18
+ - `Scope` class for filtering collections based on permissions
19
+ - Testing support with `Substitute` module for scopes
20
+ - Rails integration via Railtie
21
+ - Full compatibility with the eventide-project/dependency gem
22
+
23
+ ### Documentation
24
+ - Comprehensive README with usage examples
25
+ - Detailed explanations of all major components
26
+ - Rails integration guide
27
+ - Testing instructions and examples
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hubbado
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.md ADDED
@@ -0,0 +1,404 @@
1
+ # Hubbado Policy
2
+
3
+ A lightweight, flexible policy framework for Ruby applications that helps you implement authorization logic in a consistent way.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'hubbado-policy'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ $ gem install hubbado-policy
23
+ ```
24
+
25
+ ## Overview
26
+
27
+ Hubbado Policy provides three main components:
28
+
29
+ 1. **Policy** - Defines authorization rules
30
+ 2. **Result** - Represents the outcome of policy checks
31
+ 3. **Scope** - Filters collections based on authorization rules
32
+
33
+ ## Policy Objects
34
+
35
+ Policy objects encapsulate authorization logic and determine whether certain actions are permitted for a given user and record combination.
36
+
37
+ ### Basic Usage
38
+
39
+ ```ruby
40
+ class ArticlePolicy < Hubbado::Policy::Base
41
+ define_policy :view do
42
+ return permitted if user.admin?
43
+
44
+ if record.published?
45
+ permitted
46
+ else
47
+ denied(:not_published)
48
+ end
49
+ end
50
+
51
+ define_policy :edit do
52
+ return permitted if user.admin?
53
+
54
+ if record.author == user
55
+ permitted
56
+ else
57
+ denied(:not_author)
58
+ end
59
+ end
60
+ end
61
+
62
+ # Usage
63
+ policy = ArticlePolicy.build(current_user, article)
64
+
65
+ # Check permissions
66
+ if policy.view?
67
+ # User can view the article
68
+ end
69
+
70
+ # Get detailed result object
71
+ result = policy.edit
72
+ if result.permitted?
73
+ # User can edit
74
+ else
75
+ # Show error message
76
+ flash[:error] = result.message
77
+ end
78
+ ```
79
+
80
+ ### Policy DSL
81
+
82
+ The `define_policy` method creates three methods for each policy rule:
83
+
84
+ 1. The base method (e.g., `edit`) that returns a `Result` object
85
+ 2. A predicate method (e.g., `edit?`) that returns a boolean
86
+ 3. An underlying implementation method
87
+
88
+ The policy methods support using `return` statements within the block. If a policy method returns `nil` or doesn't explicitly return a `permitted` or `denied` result, it will automatically default to a generic `denied` result. This simplifies policy implementations by not requiring explicit denials for all paths.
89
+
90
+ ```ruby
91
+ define_policy :publish do
92
+ # Early returns work fine
93
+ return permitted if user.admin?
94
+ return denied(:not_verified) unless user.verified?
95
+
96
+ # If this evaluates to nil, it automatically becomes a generic denial
97
+ permitted if record.draft? && record.author == user
98
+
99
+ # Reaching the end of the method without returning a result
100
+ # will also produce a generic denial
101
+ end
102
+ ```
103
+
104
+ ### Policy Results
105
+
106
+ Every policy check returns a `Result` object, which can be either permitted or denied with a specific reason.
107
+
108
+ ```ruby
109
+ # Allow access
110
+ permitted
111
+
112
+ # Deny access with a reason code
113
+ denied(:not_author)
114
+
115
+ # Deny with additional data
116
+ denied(:quota_exceeded, data: { limit: 10, usage: 12 })
117
+ ```
118
+
119
+ ### Internationalization
120
+
121
+ Hubbado Policy integrates with I18n for translating error messages:
122
+
123
+ ```ruby
124
+ # config/locales/en.yml
125
+ en:
126
+ article_policy:
127
+ not_published: "This article hasn't been published yet"
128
+ not_author: "Only the author can edit this article"
129
+ hubbado_policy:
130
+ errors:
131
+ denied: "Access denied"
132
+ ```
133
+
134
+ ## Result Objects
135
+
136
+ Result objects represent the outcome of a policy check, containing:
137
+
138
+ - Permission status (permitted or denied)
139
+ - Reason code for denial
140
+ - Optional additional data
141
+ - I18n integration for error messages
142
+
143
+ ### Usage
144
+
145
+ ```ruby
146
+ result = ArticlePolicy.build(current_user, article).view
147
+
148
+ if result.permitted?
149
+ # Proceed with action
150
+ elsif result.denied?
151
+ # Handle denial
152
+ error_message = result.message # Automatically translated
153
+ additional_data = result.data # Any extra context provided
154
+ end
155
+ ```
156
+
157
+ ### Built-in Methods
158
+
159
+ - `permitted?` - Returns true if access is allowed
160
+ - `denied?` - Returns true if access is denied
161
+ - `generic_deny?` - Returns true if using the default denial reason
162
+ - `message` - Returns the localized error message
163
+ - `data` - Returns any additional context data
164
+
165
+ ## Scope Objects
166
+
167
+ Scope objects filter collections based on what a user is authorized to access.
168
+
169
+ ### Basic Usage
170
+
171
+ ```ruby
172
+ class ArticleScope < Hubbado::Policy::Scope
173
+ def self.default_scope
174
+ Article.all
175
+ end
176
+
177
+ def resolve(record, scope, **options)
178
+ return scope if record.admin?
179
+
180
+ scope.where(published: true).or(scope.where(author_id: record.id))
181
+ end
182
+ end
183
+
184
+ # Usage
185
+ visible_articles = ArticleScope.call(current_user)
186
+ ```
187
+
188
+ ### Custom Scopes
189
+
190
+ You can pass custom base scopes:
191
+
192
+ ```ruby
193
+ # Scope only to a specific category
194
+ category_articles = ArticleScope.call(
195
+ current_user,
196
+ Article.where(category_id: params[:category_id])
197
+ )
198
+
199
+ # Pass additional options
200
+ recent_articles = ArticleScope.call(
201
+ current_user,
202
+ Article.all,
203
+ only_recent: true
204
+ )
205
+ ```
206
+
207
+ ### Testing with Substitutes
208
+
209
+ Scope objects include a Substitute module for testing:
210
+
211
+ ```ruby
212
+ # In your tests
213
+ scope = ArticleScope.new
214
+ scope.extend(Hubbado::Policy::Scope::Substitute)
215
+ scope.result = [article1, article2]
216
+
217
+ # Now you can assert the scope was called with expected arguments
218
+ scope.call(user)
219
+ assert scope.scoped?(user)
220
+ ```
221
+
222
+ ### Using with Eventide Dependency
223
+
224
+ Hubbado Policy is designed to work seamlessly with the [eventide-project/dependency](https://github.com/eventide-project/dependency) gem. Creating substitutes for testing is as simple as:
225
+
226
+ ```ruby
227
+ # Create a substitute instance of ArticleScope
228
+ article_scope = Dependency::Substitute.build(ArticleScope)
229
+
230
+ # Configure the result
231
+ article_scope.result = [article1, article2]
232
+
233
+ # Use in tests
234
+ service = SomeService.new
235
+ service.article_scope = article_scope
236
+
237
+ # Run the service
238
+ result = service.list_articles(user)
239
+
240
+ # Verify the scope was called with expected arguments
241
+ assert article_scope.scoped?(user)
242
+ ```
243
+
244
+ This approach makes testing with substitutes straightforward while maintaining all the benefits of dependency injection.
245
+
246
+ ## Dependency Configuration
247
+
248
+ Policy and Scope objects support the `configure` instance method that can be defined in a subclass. This method is called when the object is initialized and is intended to be used for configuring dependencies.
249
+
250
+ ### Basic Configuration
251
+
252
+ ```ruby
253
+ class ComplexPolicy < HubbadoPolicy::Policy
254
+ attr_reader :permission_service
255
+
256
+ def configure
257
+ @permission_service = PermissionService.new
258
+ end
259
+
260
+ define_policy :complex_rule do
261
+ if permission_service.check_permission(user, record)
262
+ permitted
263
+ else
264
+ denied(:no_permission)
265
+ end
266
+ end
267
+ end
268
+ ```
269
+
270
+ ### Integration with Eventide Dependency
271
+
272
+ Hubbado Policy is designed to work seamlessly with the [eventide-project/dependency](https://github.com/eventide-project/dependency) gem for dependency management. This provides a powerful way to handle service dependencies in your policies and scopes.
273
+
274
+ ```ruby
275
+ # First, set up your dependencies
276
+ require 'dependency'; Dependency.activate
277
+ require 'hubbado-policy'
278
+
279
+ class Services
280
+ dependency :permission_service, PermissionService
281
+ dependency :audit_logger, AuditLogger
282
+ end
283
+
284
+ # Then use them in your policy
285
+ class ArticlePolicy < HubbadoPolicy::Policy
286
+ dependency :permission_service, PermissionService
287
+ dependency :audit_logger, AuditLogger
288
+
289
+ def configure
290
+ Services.configure(self)
291
+ end
292
+
293
+ define_policy :publish do
294
+ # Log the attempt
295
+ audit_logger.log_action("publish_attempt", user: user, record: record)
296
+
297
+ # Check permissions
298
+ if permission_service.can_publish?(user, record)
299
+ permitted
300
+ else
301
+ denied(:cannot_publish)
302
+ end
303
+ end
304
+ end
305
+ ```
306
+
307
+ ### Benefits of Using Dependency
308
+
309
+ Using the `dependency` gem with Hubbado Policy offers several advantages:
310
+
311
+ 1. **Clear dependency declaration** - Dependencies are explicitly declared at the class level
312
+ 2. **Consistent initialization** - The `configure` method provides a standard place for setting up dependencies
313
+ 3. **Testability** - Dependencies can be easily substituted in tests
314
+ 4. **Service reuse** - Common services can be configured once and reused across policies and scopes
315
+
316
+ ### Using Dependency with Scopes
317
+
318
+ The same pattern works for Scope objects:
319
+
320
+ ```ruby
321
+ class ArticleScope < HubbadoPolicy::Scope
322
+ include Dependency
323
+
324
+ dependency :visibility_service, VisibilityService
325
+
326
+ def configure
327
+ Services.configure(self)
328
+ end
329
+
330
+ def self.default_scope
331
+ Article.all
332
+ end
333
+
334
+ def resolve(record, scope, **options)
335
+ visibility_service.filter_visible_for(record, scope, **options)
336
+ end
337
+ end
338
+ ```
339
+
340
+ ## Rails Integration
341
+
342
+ Hubbado Policy includes built-in Rails integration through a Railtie that automatically loads the necessary components and configurations.
343
+
344
+ ### Automatic Loading
345
+
346
+ When used with Rails, the gem automatically loads its default locale file:
347
+
348
+ ```ruby
349
+ # lib/hubbado/policy/railtie.rb
350
+ module Hubbado
351
+ module Policy
352
+ class Railtie < ::Rails::Railtie
353
+ I18n.load_path << File.expand_path("../../../../config/locales/en.yml", __FILE__)
354
+ end
355
+ end
356
+ end
357
+ ```
358
+
359
+ The Railtie is loaded automatically when Rails is detected:
360
+
361
+ ```ruby
362
+ # lib/hubbado-policy.rb
363
+ require "hubbado/policy/railtie" if defined?(Rails::Railtie)
364
+ ```
365
+
366
+ ### Recommended Rails Setup
367
+
368
+ For Rails applications, we recommend organizing your policies in the `app/policies` directory:
369
+
370
+ ```
371
+ app/
372
+ ├── policies/
373
+ │ ├── application_policy.rb
374
+ │ ├── article_policy.rb
375
+ │ └── user_policy.rb
376
+ ├── scopes/
377
+ │ ├── article_scope.rb
378
+ │ └── user_scope.rb
379
+ ├── controllers/
380
+ ├── models/
381
+ └── ...
382
+ ```
383
+
384
+ You may want to create a base `ApplicationPolicy` that all your policies inherit from:
385
+
386
+ ```ruby
387
+ # app/policies/application_policy.rb
388
+ class ApplicationPolicy < Hubbado::Policy::Base
389
+ # Common methods for all policies
390
+ end
391
+ ```
392
+
393
+
394
+ ## Contributing
395
+
396
+ 1. Fork it
397
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
398
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
399
+ 4. Push to the branch (`git push origin my-new-feature`)
400
+ 5. Create new Pull Request
401
+
402
+ ## License
403
+
404
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,4 @@
1
+ en:
2
+ hubbado_policy:
3
+ errors:
4
+ denied: error denied message
@@ -0,0 +1,35 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "hubbado-policy"
3
+ s.version = "1.0.1"
4
+ s.summary = "A lightweight, flexible policy framework for Ruby applications"
5
+
6
+ s.authors = ["Hubbado Devs"]
7
+ s.email = ["devs@hubbado.com"]
8
+ s.homepage = 'https://github.com/hubbado/hubbado-policy'
9
+ s.license = "MIT"
10
+
11
+ s.metadata["homepage_uri"] = s.homepage
12
+ s.metadata["source_code_uri"] = s.homepage
13
+ s.metadata["changelog_uri"] = "#{s.homepage}/blob/master/CHANGELOG.md"
14
+
15
+ s.require_paths = ["lib"]
16
+ s.files = Dir.glob(%w[
17
+ lib/**/*.rb
18
+ config/**/*.yml
19
+ *.gemspec
20
+ LICENSE*
21
+ README*
22
+ CHANGELOG*
23
+ ])
24
+ s.platform = Gem::Platform::RUBY
25
+ s.required_ruby_version = ">= 3.2"
26
+
27
+ s.add_runtime_dependency "i18n"
28
+ s.add_runtime_dependency "evt-casing"
29
+ s.add_runtime_dependency "evt-record_invocation"
30
+ s.add_runtime_dependency "evt-template_method"
31
+
32
+ s.add_development_dependency "debug"
33
+ s.add_development_dependency "hubbado-style"
34
+ s.add_development_dependency "test_bench"
35
+ end
@@ -0,0 +1,78 @@
1
+ module Hubbado
2
+ module Policy
3
+ class Base
4
+ module PolicyDSL
5
+ def self.included(klass)
6
+ klass.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def define_policy(policy, *args, **kwargs, &block)
11
+ @policies ||= []
12
+ @policies << policy
13
+
14
+ # NOTE: This uses the technique described here so that the block given to
15
+ # define_policy can have return statements without causing LocalJumpError
16
+ # http://blog.jayfields.com/2007/03/ruby-localjumperror-workaround.html
17
+ define_method policy, &block
18
+ new_method = instance_method(policy)
19
+ define_method policy do |*args, **kwargs|
20
+ new_method.bind(self).call(*args, **kwargs) || denied
21
+ end
22
+
23
+ define_method "#{policy}?" do |*args, **kwargs|
24
+ send(policy, *args, **kwargs).permitted?
25
+ end
26
+ end
27
+
28
+ attr_reader :policies
29
+ end
30
+ end
31
+
32
+ include PolicyDSL
33
+
34
+ attr_reader :user, :record
35
+
36
+ def self.build(user, record)
37
+ instance = new(user, record)
38
+ instance.configure
39
+ instance
40
+ end
41
+
42
+ # Define this in a subclass if there are dependencies to be configure
43
+ template_method :configure
44
+
45
+ def self.denied(reason = nil, data: nil)
46
+ reason ||= :denied
47
+ Result.new(false, reason, i18n_scope: i18n_scope, data: data)
48
+ end
49
+
50
+ def self.permitted
51
+ Result.new(true, :permitted, i18n_scope: i18n_scope)
52
+ end
53
+
54
+ def self.i18n_scope
55
+ @i18n_scope ||= Casing::Underscore::String.(name).gsub('/', '.')
56
+ end
57
+
58
+ def initialize(user, record)
59
+ raise "User not provided" unless user
60
+
61
+ @user = user
62
+ @record = record
63
+ end
64
+
65
+ def ==(other)
66
+ self.class == other.class && user == other.user && record == other.record
67
+ end
68
+
69
+ def denied(reason = nil, data: nil)
70
+ self.class.denied(reason, data: data)
71
+ end
72
+
73
+ def permitted
74
+ self.class.permitted
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,7 @@
1
+ module Hubbado
2
+ module Policy
3
+ class Railtie < ::Rails::Railtie
4
+ ::I18n.load_path << ::File.expand_path("../../../../config/locales/en.yml", __FILE__)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,40 @@
1
+ module Hubbado
2
+ module Policy
3
+ class Result
4
+ attr_reader :reason
5
+ attr_reader :data
6
+
7
+ def initialize(permitted, reason, i18n_scope: nil, data: nil)
8
+ data ||= {}
9
+ i18n_scope ||= "hubbado_policy"
10
+
11
+ @permitted = permitted
12
+ @reason = reason
13
+ @i18n_scope = i18n_scope
14
+ @data = data
15
+ end
16
+
17
+ def permitted?
18
+ !!@permitted
19
+ end
20
+
21
+ def denied?
22
+ !@permitted
23
+ end
24
+
25
+ def generic_deny?
26
+ @reason == :denied
27
+ end
28
+
29
+ def message
30
+ return if permitted?
31
+ return ::I18n.t('hubbado_policy.errors.denied') if generic_deny?
32
+ ::I18n.t(@reason, scope: @i18n_scope)
33
+ end
34
+
35
+ def ==(other)
36
+ self.class == other.class && permitted? == other.permitted? && @reason == other.reason
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ module Hubbado
2
+ module Policy
3
+ class Scope
4
+ class << self
5
+ # Define this in a subclass
6
+ template_method :default_scope
7
+ end
8
+
9
+ def self.call(record, scope = nil, **options)
10
+ build.(record, scope, **options)
11
+ end
12
+
13
+ def self.build
14
+ instance = new
15
+ instance.configure
16
+ instance
17
+ end
18
+
19
+ # Define this in a subclass if there are dependencies to be configure
20
+ template_method :configure
21
+
22
+ def call(record, scope = nil, **options)
23
+ scope ||= self.class.default_scope
24
+
25
+ resolve(record, scope, **options)
26
+ end
27
+
28
+ # Implement this in a subclass
29
+ template_method :resolve do |_record, _scope, **_options|
30
+ raise MethodMissing
31
+ end
32
+
33
+ module Substitute
34
+ include RecordInvocation
35
+
36
+ attr_writer :result
37
+
38
+ record def call(record, scope = nil, **options) = result
39
+
40
+ def result
41
+ @result ||= []
42
+ end
43
+
44
+ def scoped?(record, scope = nil, **options)
45
+ invoked?(:call, record: record, scope: scope, options: options)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ require "i18n"
2
+ require "casing"
3
+ require "record_invocation"
4
+ require "template_method"; TemplateMethod.activate
5
+
6
+ I18n.load_path += Dir[File.expand_path("config/locales") + "/*.yml"]
7
+ I18n.default_locale = :en
8
+
9
+ require "hubbado/policy/railtie" if defined?(Rails::Railtie)
10
+
11
+ require "hubbado/policy/scope"
12
+ require "hubbado/policy/result"
13
+ require "hubbado/policy/base"
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hubbado-policy
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hubbado Devs
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: i18n
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: evt-casing
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: evt-record_invocation
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: evt-template_method
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: debug
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: hubbado-style
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: test_bench
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ email:
111
+ - devs@hubbado.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - CHANGELOG.md
117
+ - LICENSE
118
+ - README.md
119
+ - config/locales/en.yml
120
+ - hubbado-policy.gemspec
121
+ - lib/hubbado-policy.rb
122
+ - lib/hubbado/policy/base.rb
123
+ - lib/hubbado/policy/railtie.rb
124
+ - lib/hubbado/policy/result.rb
125
+ - lib/hubbado/policy/scope.rb
126
+ homepage: https://github.com/hubbado/hubbado-policy
127
+ licenses:
128
+ - MIT
129
+ metadata:
130
+ homepage_uri: https://github.com/hubbado/hubbado-policy
131
+ source_code_uri: https://github.com/hubbado/hubbado-policy
132
+ changelog_uri: https://github.com/hubbado/hubbado-policy/blob/master/CHANGELOG.md
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '3.2'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubygems_version: 3.6.7
148
+ specification_version: 4
149
+ summary: A lightweight, flexible policy framework for Ruby applications
150
+ test_files: []