dude_policy 0.1.0 → 0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1b4e1e0aa981141b42efcca848389438987fb99a890d4dfcb2375b9567cf2de
4
- data.tar.gz: e65b4dfdce1fbfb939b5e73caaae218c72c1b6e895daecdbd9bb5351ce8fe4ce
3
+ metadata.gz: 66aa43b5017af33bec8c323d9cba09e09e7f1537654dc462da4bcd20bc4e63ad
4
+ data.tar.gz: 7c937f1c730de57477f4062cf289d5d350de7e708f32b753fcde8401c4c8d896
5
5
  SHA512:
6
- metadata.gz: 4a1d56704ffafee188c45bf81937b2663cf5d638ff4441bd11bd8aa780f1ffbc1a0f451f611e728fda2aaf7d40963c9505309e5371e8717a27881a48d8907116
7
- data.tar.gz: 281ae48292156284ee965d9554969b4476923d53921f5fcdcd91214756386676bef6c5aceeff392849b8f34a867e634a1b7e01cfca863238fba257c54b5d4145
6
+ metadata.gz: 9ad555c4f1279d15c6c9dd3a171f4d575f9fa54b142a1e57819e7b1683d25bb5a350aaf669502daf827551642f057e5bd1777438f7db8cb63a3a9676f3982088
7
+ data.tar.gz: eb12c6da901ddd9ed616f027d023f3e8d2e7dd96dc02a350605d42dbbab6c654acae8fe3cc132058a3e0aff3b8756b2e7510d69dd4a0d260be7d8b42912d4632
data/.travis.yml CHANGED
@@ -1,6 +1,8 @@
1
- ---
2
1
  language: ruby
3
- cache: bundler
4
2
  rvm:
5
- - 2.6.3
6
- before_install: gem install bundler -v 2.1.1
3
+ - 2.5.3
4
+ - 2.6.5
5
+ - 2.7.0
6
+ before_install:
7
+ - yes | gem update --system --force
8
+ - gem install bundler
data/Gemfile.lock CHANGED
@@ -1,41 +1,50 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dude_policy (0.1.0)
4
+ dude_policy (0.1.1)
5
5
  activesupport (> 4.2)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (6.0.2.2)
10
+ activesupport (7.1.1)
11
+ base64
12
+ bigdecimal
11
13
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
- i18n (>= 0.7, < 2)
13
- minitest (~> 5.1)
14
- tzinfo (~> 1.1)
15
- zeitwerk (~> 2.2)
16
- concurrent-ruby (1.1.6)
17
- diff-lcs (1.3)
18
- i18n (1.8.2)
14
+ connection_pool (>= 2.2.5)
15
+ drb
16
+ i18n (>= 1.6, < 2)
17
+ minitest (>= 5.1)
18
+ mutex_m
19
+ tzinfo (~> 2.0)
20
+ base64 (0.1.1)
21
+ bigdecimal (3.1.4)
22
+ concurrent-ruby (1.2.2)
23
+ connection_pool (2.4.1)
24
+ diff-lcs (1.5.0)
25
+ drb (2.1.1)
26
+ ruby2_keywords
27
+ i18n (1.14.1)
19
28
  concurrent-ruby (~> 1.0)
20
- minitest (5.14.0)
29
+ minitest (5.20.0)
30
+ mutex_m (0.1.2)
21
31
  rake (12.3.3)
22
- rspec (3.9.0)
23
- rspec-core (~> 3.9.0)
24
- rspec-expectations (~> 3.9.0)
25
- rspec-mocks (~> 3.9.0)
26
- rspec-core (3.9.1)
27
- rspec-support (~> 3.9.1)
28
- rspec-expectations (3.9.1)
32
+ rspec (3.12.0)
33
+ rspec-core (~> 3.12.0)
34
+ rspec-expectations (~> 3.12.0)
35
+ rspec-mocks (~> 3.12.0)
36
+ rspec-core (3.12.2)
37
+ rspec-support (~> 3.12.0)
38
+ rspec-expectations (3.12.3)
29
39
  diff-lcs (>= 1.2.0, < 2.0)
30
- rspec-support (~> 3.9.0)
31
- rspec-mocks (3.9.1)
40
+ rspec-support (~> 3.12.0)
41
+ rspec-mocks (3.12.6)
32
42
  diff-lcs (>= 1.2.0, < 2.0)
33
- rspec-support (~> 3.9.0)
34
- rspec-support (3.9.2)
35
- thread_safe (0.3.6)
36
- tzinfo (1.2.7)
37
- thread_safe (~> 0.1)
38
- zeitwerk (2.3.0)
43
+ rspec-support (~> 3.12.0)
44
+ rspec-support (3.12.1)
45
+ ruby2_keywords (0.0.5)
46
+ tzinfo (2.0.6)
47
+ concurrent-ruby (~> 1.0)
39
48
 
40
49
  PLATFORMS
41
50
  ruby
data/README.md CHANGED
@@ -5,14 +5,14 @@ from point of view of current_user/current_account (the **dude**)
5
5
 
6
6
  ![](https://triblive.com/wp-content/uploads/2019/01/673808_web1_gtr-liv-goldenglobes-121718.jpg)
7
7
 
8
- Here are some examples what we mean:
8
+ Here are some examples what I mean:
9
9
 
10
10
 
11
11
  ```ruby
12
- # irb
12
+ # rails console
13
13
  article = Article.find(123)
14
14
  review = Review.find(123)
15
- current_user = User.find(432) # e.g. Devise on any authentication solution
15
+ current_user = User.find(432) # e.g. Devise or any other authentication solution
16
16
 
17
17
  current_user.dude.able_to_edit_article?(article)
18
18
  # => true
@@ -22,6 +22,12 @@ current_user.dude.able_to_add_article_review?(article)
22
22
 
23
23
  current_user.dude.able_to_delete_review?(review)
24
24
  # => false
25
+
26
+ current_user.policy.able_to_view_articles?
27
+ # => true
28
+
29
+ current_user.policy.able_to_create_articles?
30
+ # => false
25
31
  ```
26
32
 
27
33
  [RSpec](https://rspec.info/) examples:
@@ -34,15 +40,23 @@ RSpec.describe 'short demo' do
34
40
  let(:different_user) { User.create }
35
41
 
36
42
  # you write tests like this:
37
- it { expect(author_user.able_to_edit_article?(article)).to be_truthy }
43
+ it { expect(author_user.dude.able_to_edit_article?(article)).to be_truthy }
38
44
 
39
45
  # or you can take advantage of native `be_` RSpec matcher that converts any questionmark ending method to matcher
40
- it { expect(author_user).to be_able_to_edit_article(article) }
41
- it { expect(different_user).not_to be_able_to_edit_article(article) }
42
- it { expect(author_user).not_to be_able_to_add_article_review(article) }
43
- it { expect(different_user).to be_able_to_add_article_review(article) }
44
- it { expect(author_user).not_to be_able_to_delete_review(article) }
45
- it { expect(different_user).to be_able_to_add_article_review(article) }
46
+ it { expect(author_user.dude).to be_able_to_edit_article(article) }
47
+ it { expect(different_user.dude).not_to be_able_to_edit_article(article) }
48
+ it { expect(author_user.dude).not_to be_able_to_add_article_review(article) }
49
+ it { expect(different_user.dude).to be_able_to_add_article_review(article) }
50
+ it { expect(author_user.dude).not_to be_able_to_delete_review(article) }
51
+ it { expect(different_user.dude).to be_able_to_add_article_review(article) }
52
+
53
+ it { expect(author_user.policy).to be_able_to_view_articles? }
54
+ it { expect(author_user.policy).not_to be_able_to_create_articles? }
55
+
56
+ context "when paid subscription" do
57
+ before { author_user.update_attributes(subscription: "paid") }
58
+ it { expect(author_user.policy).to be_able_to_create_articles? }
59
+ end
46
60
  end
47
61
  ```
48
62
 
@@ -69,10 +83,24 @@ class ArticlePolicy < DudePolicy::BasePolicy
69
83
  end
70
84
  ```
71
85
 
72
- Full example in example app
86
+ ```ruby
87
+ # app/policy/user_policy.rb
88
+ class UserPolicy < DudePolicy::BasePolicy
89
+ def able_to_view_articles?; true; end
90
+
91
+ def able_to_create_articles?
92
+ return true if resource.subscription == "paid"
93
+ false
94
+ end
95
+ end
96
+ ```
97
+
98
+ > For more examples pls check the [example app](https://github.com/equivalent/dude_policy_example1)
73
99
 
74
100
  ## Installation
75
101
 
102
+ [![Build Status](https://travis-ci.org/equivalent/dude_policy.svg?branch=master)](https://travis-ci.org/equivalent/dude_policy)
103
+
76
104
  Add this line to your application's Gemfile:
77
105
 
78
106
  ```ruby
@@ -89,17 +117,397 @@ Or install it yourself as:
89
117
 
90
118
  ## Usage
91
119
 
92
- ### Gem is responsible for Authorization
120
+ Gem is responsible for **Authorization** (what a logged in user can/cannot do)
121
+ For **Authentication** (is User logged in ?) you will need a different solution / gem (e.g. [Devise](https://github.com/heartcombo/devise), custom login solution, ...)
122
+
123
+ Once you have your Authentication solution implemeted and `dude_policy`
124
+ gem installed create a directory `app/policy` in your Ruby on Rails
125
+ app.
126
+
127
+ > Note: Since Rails version 4 files in `app/anything` directories are autoloaded. So no additional magic is needed
128
+
129
+ There create your policy file:
130
+
131
+
132
+ ```ruby
133
+ # app/policy/article_policy
134
+ class ArticlePolicy < DudePolicy::BasePolicy
135
+ def able_to_update_article?(dude:)
136
+ return true if dude.admin?
137
+ return true if dude == resource.author
138
+ false
139
+ end
140
+ end
141
+ ```
142
+
143
+
144
+ > Note: Policy should be name as the model suffixed with word "Policy". So if
145
+ > you have `ConstructiveComment` model your policy should be named `ConstructiveCommentPolicy` located in `app/policy/constructive_comment_policy.rb`
146
+
147
+
148
+ You also need to tell your models what role they play
149
+
150
+ ```ruby
151
+ class Article < ApplicationRecord
152
+ include DudePolicy::HasPolicy # will add a method `article.policy`
153
+
154
+ # ...
155
+ end
156
+ ```
157
+
158
+
159
+ ```ruby
160
+ class User < ApplicationRecord
161
+ include DudePolicy::IsADude # will add a method `user.dude`
162
+ include DudePolicy::HasPolicy # will add a method `user.policy`
163
+
164
+ # ...
165
+ end
166
+ ```
167
+
168
+ This way you will be able to call:
169
+
170
+ ```ruby
171
+ user = User.find(123)
172
+ user.dude.able_to_update_article?(@article)
173
+ ```
174
+
175
+ > Note: same model can include both `DudePolicy::IsADude` and `DudePolicy::IsADude` but don't have to.
176
+
177
+ > Note: please be sure to check the [Philosophy](https://github.com/equivalent/dude_policy#philospophy) section of this README to fully understand the flow
178
+
179
+ This way you will be implement it in your application:
180
+
181
+ #### protect views
182
+
183
+ ```erb
184
+ <%= link_to 'Edit', edit_article_path(@article) if current_user.dude.able_to_update_article?(@article) %>
185
+ ```
186
+
187
+ #### protect controllers
188
+
189
+ ```ruby
190
+ # app/controllers/articles_controller.rb
191
+ class ArticlesController < ApplicationController
192
+ # ...
193
+
194
+ def update
195
+ @article = Article.find_by(params[:id])
196
+ raise(DudePolicy::NotAuthorized) unless current_user.dude.able_to_update_article?(@article)
197
+
198
+ if @article.update(article_params)
199
+ redirect_to @article, notice: 'Article was successfully updated.'
200
+ else
201
+ render :edit
202
+ end
203
+ end
204
+ end
205
+ ```
206
+
207
+ > gem provides error class `DudePolicy::NotAuthorized` so you
208
+ > can implement [rescue_from](https://apidock.com/rails/ActiveSupport/Rescuable/ClassMethods/rescue_from) logic around not authenticated
209
+ > scenarios. If you have no idea what I'm talking about pls check [example application code](https://github.com/equivalent/dude_policy_example1/blob/master/app/controllers/application_controller.rb)
210
+
211
+ #### protect business logic
212
+
213
+ There are cases when you want to protect your business logic that is
214
+ beyond MVC level. For example:
215
+
216
+ ```ruby
217
+ # app/polcy/user_policy.rb
218
+ class UserPolicy < DudePolicy::BasePolicy
219
+ def able_to_see_user_full_name?(dude:)
220
+ return true if dude.admin?
221
+ return true if dude == resource
222
+ false
223
+ end
224
+ end
225
+
226
+
227
+ # app/helpers/application_helper.rb
228
+ def author_name(user)
229
+ return unless user
230
+ if current_user.dude.able_to_see_user_full_name?(user)
231
+ user.name
232
+ else
233
+ first_letter = user.name[0]
234
+ "#{first_letter}."
235
+ end
236
+ end
237
+ ```
238
+
239
+ #### RSpec testing
240
+
241
+
242
+ ##### policy test
243
+
244
+ You should be writing tests from perspective of current_user / current_account (the dude) and what roles they play.
245
+
246
+ If you have 2 roles (admin/regular user) test all policy methods for both
247
+ roles. If you have 8 roles (admin/moderator/client-manager/external-employee/noob/...) test all policy methods from all eight perspectives.
248
+
249
+ ```ruby
250
+ # spec/policy/article_policy_spec.rb
251
+ require 'rails_helper'
252
+ RSpec.describe ArticlePolicy do
253
+ let(:article) { create :article, author: author }
254
+ let(:author) { create :user }
255
+
256
+ context 'when nil user' do
257
+ let(:current_user) { nil }
258
+
259
+ it { expect(current_user.dude).not_to be_able_to_update_article(article) }
260
+ it { expect(current_user.dude).not_to be_able_to_delete_article(article) }
261
+ end
262
+
263
+ context 'when regular_user' do
264
+ let(:current_user) { create :user }
93
265
 
94
- Gem will provide a way how to do `Authorization` (whan logged in user can/cannot do)
266
+ it { expect(current_user.dude).not_to be_able_to_update_article(article) }
267
+ it { expect(current_user.dude).not_to be_able_to_delete_article(article) }
268
+
269
+ context ' is author of article' do
270
+ let(:author) { current_user }
271
+ it { expect(current_user.dude).to be_able_to_update_article(article) }
272
+ it { expect(current_user.dude).to be_able_to_delete_article(article) }
273
+ end
274
+ end
275
+
276
+ context 'when admin' do
277
+ let(:current_user) { create :user, admin: true }
278
+ it { expect(current_user.dude).to be_able_to_update_article(article) }
279
+ it { expect(current_user.dude).not_to be_able_to_delete_article(article) }
280
+
281
+ context ' is author of article' do
282
+ let(:author) { current_user }
283
+ it { expect(current_user.dude).to be_able_to_update_article(article) }
284
+ it { expect(current_user.dude).to be_able_to_update_article(article) }
285
+ end
286
+ end
287
+ end
288
+ ```
289
+
290
+ > note the be_*****() matcher is built in RSpec (no special magic in this gem). It's up to you if you prefere `expect(current_user.dude).to be_able_to_update_article(article)` or
291
+ > `expect(current_user.dude.able_to_update_article?(article)).to be_truthy`. Both are valid from point of native RSpec.
292
+
293
+
294
+ Do yourself a favor and **don't write low level Unit Tests** like `expect(ArticlePolicy.new(article)).to be_able_to_update_article(dude: current_user)` !
295
+ When it comes to policy tests this can lead to huge security disasters ([full explanation](https://blog.eq8.eu/assets/2019/unit-test.jpg))
296
+
297
+ ##### request test
298
+
299
+ Now that we tested policy for every possible role of a user we can stub
300
+ the policy. We want to do it on the same interface level as we tested our policies
301
+ that means `allow(current_user.dude).to receive(:able_to_update_article?).and_return(true)`
302
+
303
+ > note: simmilar approach we would apply if you write controller RSpec test
304
+
305
+
306
+ ```ruby
307
+ require 'rails_helper'
308
+ RSpec.describe "Articles", type: :request do
309
+ let(:article) { create :article }
310
+
311
+ describe "put /articles/xxxx" do
312
+ def trigger_update
313
+ if current_user
314
+ # devise login
315
+ sign_in current_user
316
+
317
+ # policies are tested with `spec/policy/article_spec.rb so we can stub
318
+ allow(current_user.dude)
319
+ .to receive(:able_to_update_article?)
320
+ .with(article)
321
+ .and_return(authorized)
322
+ end
323
+
324
+ put article_path(article), params: {format: :html, article: { title: 'cat' }}
325
+ article.reload
326
+ end
327
+
328
+ context 'when not authenticated' do
329
+ let(:current_user) { nil }
330
+
331
+ it { expect { trigger_update }.not_to change { article.title } }
332
+ it do
333
+ trigger_update
334
+ expect(response.status).to eq 302 # redirect by update
335
+ follow_redirect!
336
+ expect(response.body).to include('You need to sign in or sign up before continuing.')
337
+ end
338
+ end
339
+
340
+ context 'when authenticated' do
341
+ let(:current_user) { create :user }
342
+
343
+ context 'when not authorized' do
344
+ let(:authorized) { false }
345
+
346
+ it { expect { trigger_update }.not_to change { article.title } }
347
+ it do
348
+ trigger_update
349
+ expect(response.status).to eq 302 # redirect to root_path by `authenticate!` method is ApplicationController
350
+ follow_redirect!
351
+ expect(response.body).to include('Sorry current user is not authorized to perform this action')
352
+ end
353
+ end
354
+
355
+ context 'when authorized' do
356
+ let(:authorized) { true }
357
+
358
+ it { expect { trigger_update }.to change { article.title }.from('interesting article').to('cat') }
359
+ it do
360
+ trigger_update
361
+ expect(response.status).to eq 302
362
+ follow_redirect!
363
+ expect(response.body).to include('Article was successfully updated.')
364
+ end
365
+ end
366
+ end
367
+ end
368
+ end
369
+ ```
370
+
371
+
372
+ #### More examples
373
+
374
+ > For more examples pls check the [example app](https://github.com/equivalent/dude_policy_example1)
375
+
376
+
377
+ ## Philosophy
378
+
379
+ I've spent many years and tone of time playing around with different Authorization
380
+ solutions and philosophies. All boils down to fact that [Policy Objects](https://blog.eq8.eu/article/policy-object.html) are the best you can implement.
381
+
382
+ Problem is that although there are decent policy object solutions usually
383
+ they are not specific enough on implementation strategy and teams/teammates still create a
384
+ mess.
385
+
386
+ So here are some core principles and their benefits:
387
+
388
+ #### Access policies from model interface methods
389
+
390
+ By accessing policy from `current_account.dude.able_to_do_something?(resource)` you'll
391
+ overcome multiple challenges:
392
+
393
+ * less chaos within the team
394
+ * easier for Junior Developers to jump on the project with just basic MVC skill set
395
+ * unified way how to write code and implementation of policies
396
+ * performance - you don't load same objects as they are memoized on `model.policy` level (check source code)
397
+
398
+ #### Stub policy in tests from perspective of current user (`current_account.dude`)
399
+
400
+ This may not be an issue if you are using general login solution gems
401
+ like Devise. But in custom login solutions you may find it difficult to stub underlying resouces in request/controller tests.
402
+
403
+ By writing everything from user/account perspective `allow(current_account.dude).to receive(...)` your stubs will have less maintenance headache
404
+
405
+
406
+ #### Unified naming of policy methods
407
+
408
+ Your policy objects are just simple Ruby objects so there is no
409
+ restriction to name your methods in in anything you want.
410
+
411
+ From experience I highly advise you to name the policy methods as
412
+ `able_to_` + `action` + `resources names`
413
+
414
+ example
415
+
416
+ ```ruby
417
+ class ProductPolicy < DudePolicy::BasePolicy
418
+ def able_to_delete_product(dude:)
419
+ #...
420
+ end
421
+
422
+ def able_to_add_product_review_comment(dude:)
423
+ #...
424
+ end
425
+ end
426
+
427
+ class ReviewCommentPolicy < DudePolicy::BasePolicy
428
+
429
+ def able_to_delete_review_comment(dude:)
430
+ #...
431
+ end
432
+ end
433
+ ```
434
+
435
+ this way you will be able to take advantage of built in RSpec feature
436
+ and write tests like
437
+
438
+ `it { expect(current_account.dude).to be_able_to_delete_review_comment(review_comment) }`
439
+
440
+
441
+ Important thing is that **we write policy tests on the model method interface**
442
+ `current_account.dude` because we expect to stub them in
443
+ resource/controller test on the same interface.
444
+
445
+ > Avoid writing tests from perspective of `resource.policy` or `NameOfMyPolicy.new(current_account)`
446
+
447
+ #### Nil is a user too
448
+
449
+ Once you install gem you may notice that you are able to do
450
+ `nil.dude.can_do_anything? => false` and `nil.policy.can_do_anything? => false` this is a feature not a bug.
451
+
452
+ Sometimes your application need to deal with `nil` as current_user and
453
+ you don't want to have conditions `if current_user` all over the place.
454
+ That's why gem implements [Null Object Pattern](https://avdi.codes/null-objects-and-falsiness/) on `nil.dude` method that returns `false` all the time
455
+
456
+
457
+ #### Nothing new
458
+
459
+ The gem/lib is really tiny. Only dependency is Rails itself. If you want
460
+ to be vanilla Rails (no external gems) or implement this in other Ruby frameworks (e.g.
461
+ Sinatra) feel free to copy individual files from the `lib` directly
462
+
463
+ The whole gem is just [delegator](https://github.com/equivalent/dude_policy/blob/master/lib/dude_policy/dude.rb), [nil extensions](https://github.com/equivalent/dude_policy/blob/master/lib/dude_policy/nil_extension.rb), memoized model methods [1](https://github.com/equivalent/dude_policy/blob/master/lib/dude_policy/has_policy.rb) [2](https://github.com/equivalent/dude_policy/blob/master/lib/dude_policy/is_a_dude.rb) and your [Policy Objects](https://blog.eq8.eu/article/policy-object.html).
464
+
465
+ `PolicyObject + model method interface == Dude Policy gem` It's not a rocket science.
466
+
467
+ **The important part is the philosophy not the gem !**
468
+
469
+ ## How it works
470
+
471
+ Core of the gem is the delegator that flips dependencies (e.g. `user.dude`)
472
+
473
+ So you
474
+ define `ArticlePolicy` that works with `article` and you pass `dude` as
475
+ a keyword argument:
476
+
477
+ ```
478
+ class ArticlePolicy < DudePolicy::BasePolicy
479
+ def able_to_do_something_on_article?(dude:)
480
+ #...
481
+ # `dude' represent the user
482
+ #...
483
+ end
484
+ end
485
+ ```
486
+
487
+ ...by using `include PolicyDude::HasPolicy` your model have access to
488
+ this policy via method `article.policy`
489
+
490
+
491
+ The model responsible for representing current user (`User`) is able to
492
+ access the "flip dependency delegator" by using `include PolicyDude::IsADude` (e.g `user.dude`)
493
+
494
+ This allow us to call method of the argumet resource policy:
495
+
496
+ ```
497
+ user.dude.able_to_do_something_on_article?(article) -> article.policy.able_to_do_something_on_article(dude: user)
498
+ user.dude.able_to_do_something_on_somethig_else?(product) -> product.policy.able_to_do_something_on_somethig_else(dude: user)
499
+ ```
95
500
 
96
- For `Authentication` (is User logged in ?) you will need a different solution / gem (e.g. [Devise](https://github.com/heartcombo/devise), custom login solution, ...)
501
+ So in theory you could access the policy from resource point of view
502
+ `article.policy._____(dude: user)` but **don't do this** as the philosophy is **you should write your code from perspective of current_user**
503
+ ( `user.dude.____(article)` )
97
504
 
98
505
 
506
+ > `model.policy` is exposed mainly for debugging level & performance (model level memoization)
99
507
 
100
508
  ## Contributing
101
509
 
102
- Bug reports and pull requests are welcome on GitHub at https://github.com/equivalent/dude_policy This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/dude_policy/blob/master/CODE_OF_CONDUCT.md).
510
+ Bug reports and pull requests are welcome on GitHub at https://github.com/equivalent/dude_policy This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/equivalent/dude_policy/blob/master/CODE_OF_CONDUCT.md).
103
511
 
104
512
 
105
513
  ## License
@@ -6,6 +6,12 @@ module DudePolicy
6
6
  false
7
7
  end
8
8
 
9
+ # test frameworks like RSpec test first if method responds to before calling it
10
+ # as this is a NullObject delegator it responds to any method call
11
+ def respond_to?(*)
12
+ true
13
+ end
14
+
9
15
  def inspect
10
16
  "<#DudePolicy##{object_id} on nil>"
11
17
  end
@@ -3,5 +3,9 @@ module DudePolicy
3
3
  def dude
4
4
  DudePolicy::NilDudePolicy.instance
5
5
  end
6
+
7
+ def policy
8
+ DudePolicy::NilDudePolicy.instance
9
+ end
6
10
  end
7
11
  end
@@ -1,3 +1,3 @@
1
1
  module DudePolicy
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dude_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomas Valent
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-12 00:00:00.000000000 Z
11
+ date: 2023-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -61,7 +61,7 @@ metadata:
61
61
  homepage_uri: https://github.com/equivalent/dude_policy
62
62
  source_code_uri: https://github.com/equivalent/dude_policy
63
63
  changelog_uri: https://github.com/equivalent/dude_policy/blob/master/CHANGELOG.md
64
- post_install_message:
64
+ post_install_message:
65
65
  rdoc_options: []
66
66
  require_paths:
67
67
  - lib
@@ -76,8 +76,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
78
  requirements: []
79
- rubygems_version: 3.0.3
80
- signing_key:
79
+ rubygems_version: 3.3.7
80
+ signing_key:
81
81
  specification_version: 4
82
82
  summary: Policy objects for Ruby on Rails from perspectvie of current account
83
83
  test_files: []