dude_policy 0.1.0 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []