rabarber 5.0.0 → 5.1.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.
data/README.md CHANGED
@@ -1,60 +1,37 @@
1
- # Rabarber: Simplified Authorization for Rails
1
+ # Rabarber: Role-Based Authorization for Rails
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rabarber.svg)](http://badge.fury.io/rb/rabarber)
4
4
  [![Github Actions badge](https://github.com/brownboxdev/rabarber/actions/workflows/ci.yml/badge.svg)](https://github.com/brownboxdev/rabarber/actions/workflows/ci.yml)
5
5
 
6
- Rabarber is a role-based authorization library for Ruby on Rails. It provides a set of tools for managing user roles and defining authorization rules, with support for multi-tenancy and fine-grained access control.
6
+ Rabarber is a role-based authorization library for Ruby on Rails that focuses on controller-level access control. Rather than answering domain questions like "can this user create a post?", Rabarber answers "can this user access the create post endpoint?", providing a clean separation between authorization and business logic.
7
7
 
8
- ---
8
+ **Key Features:**
9
9
 
10
- **Example of Usage**:
11
-
12
- Consider a CRM system where users with different roles have distinct access levels. For instance, the role `accountant` can interact with invoices but cannot access marketing information, while the role `marketer` has access to marketing-related data. You can define such authorization rules easily with Rabarber.
13
-
14
- ---
15
-
16
- And this is how your controller might look with Rabarber:
17
-
18
- ```rb
19
- class TicketsController < ApplicationController
20
- grant_access roles: :admin
21
-
22
- grant_access action: :index, roles: :manager
23
- def index
24
- # ...
25
- end
26
-
27
- def delete
28
- # ...
29
- end
30
- end
31
- ```
32
-
33
- This means that `admin` users can access everything in `TicketsController`, while `manager` role can access only `index` action.
10
+ - Role-based authorization
11
+ - Controller-level access control
12
+ - Multi-tenancy support through contextual roles
13
+ - Dynamic authorization with conditional logic
14
+ - View helpers for role-based content rendering
34
15
 
35
16
  ## Table of Contents
36
17
 
37
- **Gem usage:**
18
+ **Gem Usage:**
38
19
  - [Installation](#installation)
39
20
  - [Configuration](#configuration)
40
- - [Role Assignments](#role-assignments)
21
+ - [User Role Methods](#user-role-methods)
41
22
  - [Role Management](#role-management)
42
- - [Authorization Rules](#authorization-rules)
43
- - [Dynamic Authorization Rules](#dynamic-authorization-rules)
44
- - [Context / Multi-tenancy](#context--multi-tenancy)
45
- - [When Unauthorized](#when-unauthorized)
46
- - [Skip Authorization](#skip-authorization)
23
+ - [Controller Authorization](#controller-authorization)
24
+ - [Dynamic Rules](#dynamic-rules)
25
+ - [Multi-tenancy / Context](#multi-tenancy--context)
47
26
  - [View Helpers](#view-helpers)
48
27
 
28
+
49
29
  **Community Resources:**
50
- - [Problems?](#problems)
51
- - [Contributing](#contributing)
30
+ - [Getting Help and Contributing](#getting-help-and-contributing)
31
+ - [License](#license)
52
32
  - [Code of Conduct](#code-of-conduct)
53
33
  - [Old Versions](#old-versions)
54
34
 
55
- **Legal:**
56
- - [License](#license)
57
-
58
35
  ## Installation
59
36
 
60
37
  Add Rabarber to your Gemfile:
@@ -69,19 +46,17 @@ Install the gem:
69
46
  bundle install
70
47
  ```
71
48
 
72
- Generate a migration to create tables for storing roles. Run the generator with the table name used by the model that represents users in your application. For example, if the table is `users`, run:
49
+ Generate the migration for role storage (replace `users` with your user table name if different):
73
50
 
74
51
  ```shell
75
- rails g rabarber:roles users
76
- ```
52
+ # For standard integer IDs
53
+ rails generate rabarber:roles users
77
54
 
78
- Rabarber supports UUIDs as primary keys. If your application uses UUIDs, add `--uuid` option:
79
-
80
- ```shell
81
- rails g rabarber:roles users --uuid
55
+ # For UUID primary keys
56
+ rails generate rabarber:roles users --uuid
82
57
  ```
83
58
 
84
- Finally, run the migration:
59
+ Run the migration:
85
60
 
86
61
  ```shell
87
62
  rails db:migrate
@@ -89,507 +64,346 @@ rails db:migrate
89
64
 
90
65
  ## Configuration
91
66
 
92
- If customization is required, Rabarber can be configured using `.configure` method in an initializer:
67
+ Configure Rabarber in an initializer if customization is needed:
93
68
 
94
69
  ```rb
95
70
  Rabarber.configure do |config|
96
- config.cache_enabled = true
97
- config.current_user_method = :current_user
98
- config.user_model_name = "User"
71
+ config.cache_enabled = true # Enable role caching (default: true)
72
+ config.current_user_method = :current_user # Method to access current user (default: :current_user)
73
+ config.user_model_name = "User" # User model name (default: "User")
99
74
  end
100
75
  ```
101
76
 
102
- - `cache_enabled` determines whether roles are cached to avoid unnecessary database queries. Roles are cached by default. If you need to clear the cache, use `Rabarber::Cache.clear` method.
103
- - `current_user_method` defines the method used to access the currently authenticated user. Default is `:current_user`.
104
- - `user_model_name` sets the name of the model representing the user in your application. Default is `"User"`.
105
-
106
- ## Role Assignments
107
-
108
- Rabarber automatically augments your user model (defined via `user_model_name` configuration) with role-related methods.
109
-
110
- **`#assign_roles(*roles, context: nil, create_new: true)`**
111
-
112
- To assign roles, use:
113
-
114
- ```rb
115
- user.assign_roles(:accountant, :marketer)
116
- ```
117
-
118
- By default, it will automatically create any roles that don't exist. To assign only existing roles and prevent automatic creation, pass `create_new: false`:
77
+ To clear the role cache manually:
119
78
 
120
79
  ```rb
121
- user.assign_roles(:accountant, :marketer, create_new: false)
80
+ Rabarber::Cache.clear
122
81
  ```
123
82
 
124
- The method returns an array of roles assigned to the user.
83
+ ## User Role Methods
125
84
 
126
- **`#revoke_roles(*roles, context: nil)`**
85
+ Your user model is automatically augmented with role management methods:
127
86
 
128
- To revoke roles, use:
87
+ ### Role Assignment
129
88
 
130
89
  ```rb
131
- user.revoke_roles(:accountant, :marketer)
132
- ```
133
-
134
- Roles the user doesn’t have are ignored.
90
+ # Assign roles (creates roles if they don't exist)
91
+ user.assign_roles(:accountant, :manager)
135
92
 
136
- The method returns an array of roles assigned to the user.
93
+ # Assign only existing roles
94
+ user.assign_roles(:accountant, :manager, create_new: false)
137
95
 
138
- **`#has_role?(*roles, context: nil)`**
96
+ # Revoke specific roles
97
+ user.revoke_roles(:accountant, :manager)
139
98
 
140
- To check whether the user has a role, use:
141
-
142
- ```rb
143
- user.has_role?(:accountant, :marketer)
99
+ # Revoke all roles
100
+ user.revoke_all_roles
144
101
  ```
145
102
 
146
- It returns `true` if the user has at least one of the given roles.
147
-
148
- **`#roles(context: nil)`**
149
-
150
- To get the list of roles assigned to the user, use:
103
+ ### Role Queries
151
104
 
152
105
  ```rb
153
- user.roles
154
- ```
106
+ # Check if user has any of the specified roles
107
+ user.has_role?(:accountant, :manager)
155
108
 
156
- **`#all_roles`**
157
-
158
- To get all roles assigned to the user, grouped by context, use:
109
+ # Get user's roles
110
+ user.roles
159
111
 
160
- ```rb
112
+ # Get all roles grouped by context
161
113
  user.all_roles
162
114
  ```
163
115
 
164
- **`.assignees(role_name, context: nil)`**
165
-
166
- To get all the users to whom the role is assigned, use:
167
-
168
- ```rb
169
- Rabarber::Role.assignees(:admin)
170
- ```
171
-
172
116
  ## Role Management
173
117
 
174
- To manipulate roles directly, you can use the following methods:
175
-
176
- **`.add(role_name, context: nil)`**
177
-
178
- To add a new role, use:
118
+ ### Direct Role Operations
179
119
 
180
120
  ```rb
121
+ # Create a new role
181
122
  Rabarber::Role.add(:admin)
182
- ```
183
-
184
- This will create a new role with the specified name and return `true`. If the role already exists, it will return `false`.
185
-
186
- **`.rename(old_role_name, new_role_name, context: nil, force: false)`**
187
-
188
- To rename a role, use:
189
123
 
190
- ```rb
124
+ # Rename a role
191
125
  Rabarber::Role.rename(:admin, :administrator)
192
- ```
193
-
194
- The old role must exist. If it doesn’t, an error is raised. If a role with the new name already exists, the method returns `false` and the rename fails.
195
-
196
- The rename also fails if the role is assigned to any user. To force it, use:
197
-
198
- ```rb
199
- Rabarber::Role.rename(:admin, :administrator, force: true)
200
- ```
201
-
202
- **`.remove(role_name, context: nil, force: false)`**
203
-
204
- To remove a role, use:
126
+ Rabarber::Role.rename(:admin, :administrator, force: true) # Force if role is assigned to users
205
127
 
206
- ```rb
128
+ # Remove a role
207
129
  Rabarber::Role.remove(:admin)
208
- ```
209
-
210
- The old role must exist. If it doesn’t, an error is raised. The method returns `true` if successful.
211
-
212
- If the role is assigned to any user, removal will fail. To force it, use:
213
-
214
- ```rb
215
- Rabarber::Role.remove(:admin, force: true)
216
- ```
217
-
218
- **`.names(context: nil)`**
219
-
220
- If you need to list the roles available in your application, use:
130
+ Rabarber::Role.remove(:admin, force: true) # Force if role is assigned to users
221
131
 
222
- ```rb
132
+ # List available roles
223
133
  Rabarber::Role.names
224
- ```
225
-
226
- **`.all_names`**
134
+ Rabarber::Role.all_names # All roles grouped by context
227
135
 
228
- If you need to list all roles available in your application, grouped by context, use:
229
-
230
- ```rb
231
- Rabarber::Role.all_names
136
+ # Get users assigned to a role
137
+ Rabarber::Role.assignees(:admin)
232
138
  ```
233
139
 
234
- ## Authorization Rules
140
+ ## Controller Authorization
235
141
 
236
- Include `Rabarber::Authorization` module in the controller where you want to define authorization rules. Typically, it is `ApplicationController`, but it can be any controller of your choice. Then use `.with_authorization(options = {})` method, which accepts the same options as Rails’ `before_action`, allowing you to perform authorization checks selectively.
142
+ ### Basic Setup
143
+
144
+ Include the authorization module and configure protection:
237
145
 
238
146
  ```rb
239
147
  class ApplicationController < ActionController::Base
240
148
  include Rabarber::Authorization
241
149
 
242
- with_authorization
243
-
244
- # ...
150
+ with_authorization # Enable authorization for all actions
245
151
  end
246
152
 
247
153
  class InvoicesController < ApplicationController
248
- with_authorization only: [:update, :destroy]
249
-
250
- # ...
154
+ with_authorization only: [:update, :destroy] # Selective authorization
251
155
  end
252
156
  ```
253
157
 
254
- You must ensure the user is authenticated before authorization checks are performed.
158
+ Authorization requires an authenticated user.
255
159
 
256
- To define authorization rules, use `.grant_access(action: nil, roles: nil, context: nil, if: nil, unless: nil)` method.
160
+ ### Skip Authorization
257
161
 
258
- The most basic usage of the method is as follows:
162
+ You can also selectively skip authorization:
259
163
 
260
164
  ```rb
261
- module Crm
262
- class InvoicesController < ApplicationController
263
- grant_access action: :index, roles: [:accountant, :admin]
264
- def index
265
- @invoices = Invoice.all
266
- @invoices = @invoices.paid if current_user.has_role?(:accountant)
267
- # ...
268
- end
269
-
270
- grant_access action: :destroy, roles: :admin
271
- def destroy
272
- # ...
273
- end
274
- end
165
+ class TicketsController < ApplicationController
166
+ skip_authorization except: [:create, :update, :destroy]
275
167
  end
276
168
  ```
277
169
 
278
- This grants access to `index` action for users with `accountant` or `admin` role, and access to `destroy` action for `admin` users only.
170
+ ### Authorization Rules
279
171
 
280
- You can also define controller-wide rules (without `action` argument):
172
+ Define access rules using `grant_access`:
281
173
 
282
174
  ```rb
283
- module Crm
284
- class BaseController < ApplicationController
285
- grant_access roles: [:admin, :manager]
286
-
287
- grant_access action: :dashboard, roles: :marketer
288
- def dashboard
289
- # ...
290
- end
291
- end
292
- end
175
+ class TicketsController < ApplicationController
176
+ # Controller-wide access
177
+ grant_access roles: :admin
293
178
 
294
- module Crm
295
- class InvoicesController < Crm::BaseController
296
- grant_access roles: :accountant
297
- def index
298
- # ...
299
- end
179
+ # Action-specific access
180
+ grant_access action: :index, roles: [:manager, :support]
181
+ def index
182
+ # Accessible to admin, manager, and support roles
183
+ end
300
184
 
301
- def delete
302
- # ...
303
- end
185
+ grant_access action: :destroy, roles: :owner, context: -> { Ticket.find(params[:id]) }
186
+ def destroy
187
+ # Accessible to admin and owner of the ticket
304
188
  end
305
189
  end
306
190
  ```
307
191
 
308
- In this example, `admin` and `manager` have access to all actions in `Crm::BaseController` and its descendants, while `accountant` role has access only to the actions in `Crm::InvoicesController`. Users with `marketer` role can only see the dashboard.
192
+ ### Additive Rules
309
193
 
310
- You can also omit roles to allow unrestricted access:
194
+ Rules are additive across inheritance chains and for the same actions:
311
195
 
312
196
  ```rb
313
- class OrdersController < ApplicationController
314
- grant_access
315
- # ...
197
+ class BaseController < ApplicationController
198
+ grant_access roles: :admin # Admin can access everything
316
199
  end
317
200
 
318
- class InvoicesController < ApplicationController
319
- grant_access action: :index
201
+ class InvoicesController < BaseController
202
+ grant_access roles: :accountant # Accountant can also access InvoicesController (along with admin)
203
+
204
+ grant_access action: :index, roles: :manager
205
+ grant_access action: :index, roles: :supervisor
320
206
  def index
321
- # ...
207
+ # Index is accessible to admin, accountant, manager, and supervisor
322
208
  end
323
209
  end
324
210
  ```
325
211
 
326
- This allows everyone to access `OrdersController` and its descendants as well as `index` action in `InvoicesController`.
212
+ ### Unrestricted Access
327
213
 
328
- Rules defined in descendant classes don't override ancestor rules but rather add to them:
214
+ Omit roles to allow unrestricted access:
329
215
 
330
216
  ```rb
331
- module Crm
332
- class BaseController < ApplicationController
333
- grant_access roles: :admin
334
- # ...
335
- end
217
+ class UnrestrictedController < ApplicationController
218
+ grant_access # Allow all users
336
219
  end
337
220
 
338
- module Crm
339
- class InvoicesController < Crm::BaseController
340
- grant_access roles: :accountant
341
- # ...
221
+ class MixedController < ApplicationController
222
+ grant_access action: :index # Unrestricted index action
223
+ def index
224
+ # Accessible to all users
342
225
  end
343
- end
344
- ```
345
226
 
346
- This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
347
-
348
- This also applies when defining multiple rules for the same controller or action:
349
-
350
- ```rb
351
- module Crm
352
- class OrdersController < ApplicationController
353
- grant_access roles: :manager, context: Order
354
- grant_access roles: :admin
355
-
356
- grant_access action: :show, roles: :client, context: -> { Order.find(params[:id]) }
357
- grant_access action: :show, roles: :accountant
358
- def show
359
- # ...
360
- end
227
+ grant_access action: :show, roles: :member # Restricted show action
228
+ def show
229
+ # Accessible to members only
361
230
  end
362
231
  end
363
232
  ```
364
233
 
365
- This will add rules for `manager` and `admin` roles for all actions in `Crm::OrdersController`, and for `client` and `accountant` roles for the `show` action.
366
-
367
- ## Dynamic Authorization Rules
234
+ ### Custom Unauthorized Handling
368
235
 
369
- For more complex scenarios, Rabarber supports dynamic authorization rules:
236
+ Override `when_unauthorized` method to customize unauthorized access behavior:
370
237
 
371
238
  ```rb
372
- module Crm
373
- class OrdersController < ApplicationController
374
- grant_access roles: :manager, if: :company_manager?, unless: :fired?
375
-
376
- def index
377
- # ...
378
- end
379
-
380
- private
239
+ class ApplicationController < ActionController::Base
240
+ include Rabarber::Authorization
381
241
 
382
- def company_manager?
383
- Company.find(params[:company_id]).manager == current_user
384
- end
242
+ with_authorization
385
243
 
386
- def fired?
387
- current_user.fired?
388
- end
389
- end
390
- end
244
+ private
391
245
 
392
- module Crm
393
- class InvoicesController < ApplicationController
394
- grant_access roles: :senior_accountant
395
-
396
- grant_access action: :index, roles: [:secretary, :accountant], if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
397
- def index
398
- @invoices = Invoice.all
399
- @invoices = @invoices.where("total < 10000") if current_user.has_role?(:accountant)
400
- @invoices = @invoices.unpaid if current_user.has_role?(:secretary)
401
- # ...
402
- end
403
-
404
- grant_access action: :show, roles: :accountant, unless: -> { Invoice.find(params[:id]).total > 10_000 }
405
- def show
406
- # ...
407
- end
246
+ def when_unauthorized
247
+ # Default behavior: redirect back (HTML) or return 403 (other formats)
248
+ # Custom behavior example:
249
+ head :not_found # Hide existence of protected resources
408
250
  end
409
251
  end
410
252
  ```
411
253
 
412
- You can pass a dynamic rule as `if` or `unless` argument. You can pass a symbol (method name) or a proc. Symbols refer to instance methods, and procs are evaluated in the controller at request time.
254
+ By default, Rabarber will redirect back (HTML format) or return 403 (other formats).
255
+
256
+ ## Dynamic Rules
413
257
 
414
- You can use only dynamic rules without specifying roles if that suits your needs:
258
+ Add conditional logic to authorization rules:
415
259
 
416
260
  ```rb
417
- class InvoicesController < ApplicationController
418
- grant_access action: :index, if: -> { current_user.company == Company.find(params[:company_id]) }
261
+ class OrdersController < ApplicationController
262
+ # Method-based conditions
263
+ grant_access roles: :manager, if: :company_manager?, unless: :suspended?
264
+
265
+ # Proc-based conditions
266
+ grant_access action: :show, roles: :client, if: -> { current_user.company_id == Order.find(params[:id]).company_id }
267
+ def show
268
+ # Accessible to company managers unless suspended, and to clients if the client's company matches the order's company
269
+ end
270
+
271
+ # Dynamic-only rules (no roles required, can be used with custom policies)
272
+ grant_access action: :index, if: -> { OrdersPolicy.new(current_user).can_access?(:index) }
419
273
  def index
420
- # ...
274
+ # Accessible to company managers unless suspended, and to users based on custom policy logic
421
275
  end
422
- end
423
- ```
424
276
 
425
- This allows you to use Rabarber as a policy-based authorization library by calling your own custom policy within a dynamic rule:
277
+ private
426
278
 
427
- ```rb
428
- class InvoicesController < ApplicationController
429
- grant_access action: :index, if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
430
- def index
431
- # ...
279
+ def company_manager?
280
+ current_user.manager_of?(Company.find(params[:company_id]))
281
+ end
282
+
283
+ def suspended?
284
+ current_user.suspended?
432
285
  end
433
286
  end
434
287
  ```
435
288
 
436
- ## Context / Multi-tenancy
437
-
438
- Rabarber supports multi-tenancy through its context feature. This allows you to define and authorize roles and rules within a specific context.
289
+ ## Multi-tenancy / Context
439
290
 
440
- Every Rabarber method can accept a context as an additional keyword argument. By default, the context is set to `nil`, meaning the roles are global. Thus, all examples from other sections of this README are valid for global roles. Besides being global, context can also be an instance of an `ActiveRecord` model or a class.
291
+ All Rabarber methods accept a `context` parameter, allowing you to work with roles within specific scopes rather than globally.
441
292
 
442
- E.g., consider a model named `Project`, where each project has its owner and regular members. Roles can be defined like this:
293
+ ### Contextual Role Assignment
443
294
 
444
295
  ```rb
296
+ # Assign roles within a specific model instance
445
297
  user.assign_roles(:owner, context: project)
446
- another_user.assign_roles(:member, context: project)
447
- ```
448
-
449
- You can then check roles like this:
298
+ user.assign_roles(:member, context: project)
450
299
 
451
- ```rb
452
- user.has_role?(:owner, context: project)
453
- another_user.has_role?(:member, context: project)
454
- ```
455
-
456
- A role can also be added using a class as a context, e.g., for project admins who can manage all projects:
457
-
458
- ```rb
300
+ # Assign roles within a model class (e.g., project admin)
459
301
  user.assign_roles(:admin, context: Project)
460
- ```
461
302
 
462
- And to check it:
463
-
464
- ```rb
303
+ # Check contextual roles
304
+ user.has_role?(:owner, context: project)
465
305
  user.has_role?(:admin, context: Project)
306
+
307
+ # Get roles within context
308
+ user.roles(context: project)
309
+ Rabarber::Role.names(context: Project)
466
310
  ```
467
311
 
468
- In authorization rules, the context can be used in the same way, but it also can be a proc or a symbol (similar to dynamic rules):
312
+ ### Contextual Authorization
469
313
 
470
314
  ```rb
471
315
  class ProjectsController < ApplicationController
316
+ # Class-based context
472
317
  grant_access roles: :admin, context: Project
473
318
 
474
- grant_access action: :show, roles: :member, context: :project
319
+ # Instance-based context (method)
320
+ grant_access action: :show, roles: :member, context: :current_project
475
321
  def show
476
- # ...
322
+ # Accessible to Project admin and members of the current project
477
323
  end
478
324
 
325
+ # Instance-based context (proc)
479
326
  grant_access action: :update, roles: :owner, context: -> { Project.find(params[:id]) }
480
327
  def update
481
- # ...
328
+ # Accessible to Project admin and owner of the current project
482
329
  end
483
330
 
484
331
  private
485
332
 
486
- def project
487
- Project.find(params[:id])
333
+ def current_project
334
+ @current_project ||= Project.find(params[:id])
488
335
  end
489
336
  end
490
337
  ```
491
338
 
492
- Role names are scoped by context, i.e. `admin` in a project is different from a global `admin`, or from an `admin` in another project.
339
+ ### Context Migrations
493
340
 
494
- If you want to see all the roles assigned to a user within a specific context, you can use:
341
+ Handle context changes when models are renamed or removed. These are irreversible data migrations.
495
342
 
496
343
  ```rb
497
- user.roles(context: project)
498
- ```
344
+ # Rename a context class (e.g., when you rename your Project model to Campaign)
345
+ migrate_authorization_context!("Project", "Campaign")
499
346
 
500
- Or if you want to get all the roles available in a specific context, you can use:
501
-
502
- ```rb
503
- Rabarber::Role.names(context: Project)
347
+ # Remove orphaned context data (e.g., when you delete a model entirely)
348
+ delete_authorization_context!("DeletedModel")
504
349
  ```
505
350
 
506
- If a context object (e.g., a project) is deleted, any roles tied to it automatically become irrelevant and are no longer returned by role queries. Rabarber treats them as orphaned and ignores them.
507
-
508
- However, if a context class is renamed (e.g., `Project` becomes `Campaign`), an exception will be raised the next time Rabarber attempts to load roles for that class. This is to ensure you explicitly handle the migration, either by migrating existing roles to the new context or by cleaning up old data.
509
-
510
- To help with such scenarios, Rabarber provides two helper methods that can be called in migrations:
511
-
512
- - `migrate_authorization_context!(old_context, new_context)` - renames a context
513
- - `delete_authorization_context!(context)` – removes roles tied to a deleted context
514
-
515
- These are irreversible data migrations.
516
-
517
- ## When Unauthorized
518
-
519
- By default, Rabarber redirects back on unauthorized access if the request format is HTML (falling back to the root path), and returns a 401 Unauthorized for other formats.
520
-
521
- You can customize this behavior by overriding the private `when_unauthorized` method:
522
-
523
- ```rb
524
- class ApplicationController < ActionController::Base
525
- include Rabarber::Authorization
526
-
527
- with_authorization
528
-
529
- # ...
530
-
531
- private
532
-
533
- def when_unauthorized
534
- head :not_found # pretend the page doesn't exist
535
- end
536
- end
537
- ```
538
-
539
- The method can be overridden in different controllers, providing flexibility in handling unauthorized access attempts.
540
-
541
- ## Skip Authorization
542
-
543
- To skip authorization, use `.skip_authorization(options = {})` method:
544
-
545
- ```rb
546
- class TicketsController < ApplicationController
547
- skip_authorization only: :index
548
- # ...
549
- end
550
- ```
551
-
552
- This method accepts the same options as Rails’ `skip_before_action`.
553
-
554
351
  ## View Helpers
555
352
 
556
- Rabarber provides two helpers for use in views: `visible_to(*roles, context: nil, &block)` and `hidden_from(*roles, context: nil, &block)`. To enable them, include `Rabarber::Helpers` in your helper module, typically `ApplicationHelper`.
353
+ Include view helpers in your application:
557
354
 
558
355
  ```rb
559
356
  module ApplicationHelper
560
357
  include Rabarber::Helpers
561
- # ...
562
358
  end
563
359
  ```
564
360
 
565
- The usage is straightforward:
361
+ Use conditional rendering based on roles:
566
362
 
567
363
  ```erb
568
364
  <%= visible_to(:admin, :manager) do %>
569
- <p>Visible only to admins and managers</p>
365
+ <div class="admin-panel">
366
+ <!-- Admin/Manager content -->
367
+ </div>
570
368
  <% end %>
571
- ```
572
369
 
573
- ```erb
574
- <%= hidden_from(:accountant) do %>
575
- <p>Accountant cannot see this</p>
370
+ <%= hidden_from(:guest) do %>
371
+ <div class="member-content">
372
+ <!-- Content hidden from guests -->
373
+ </div>
374
+ <% end %>
375
+
376
+ <!-- With context -->
377
+ <%= visible_to(:owner, context: @project) do %>
378
+ <button>Delete Project</button>
576
379
  <% end %>
577
380
  ```
578
381
 
579
- ## Problems?
382
+ ## Getting Help and Contributing
580
383
 
581
- Facing a problem or want to suggest an enhancement?
384
+ ### Getting Help
385
+ Have a question or need assistance? Open a discussion in our [discussions section](https://github.com/brownboxdev/rabarber/discussions) for:
386
+ - Usage questions
387
+ - Implementation guidance
388
+ - Feature suggestions
582
389
 
583
- - **Open a Discussion**: If you have a question, experience difficulties using the gem, or have a suggestion for improvements, feel free to use the Discussions section.
390
+ ### Reporting Issues
391
+ Found a bug? Please [create an issue](https://github.com/brownboxdev/rabarber/issues) with:
392
+ - A clear description of the problem
393
+ - Steps to reproduce the issue
394
+ - Your environment details (Rails version, Ruby version, etc.)
584
395
 
585
- Encountered a bug?
396
+ ### Contributing Code
397
+ Ready to contribute? You can:
398
+ - Fix bugs by submitting pull requests
399
+ - Improve documentation
400
+ - Add new features (please discuss first in our [discussions section](https://github.com/brownboxdev/rabarber/discussions))
586
401
 
587
- - **Create an Issue**: If you've identified a bug, please create an issue. Be sure to provide detailed information about the problem, including the steps to reproduce it.
588
- - **Contribute a Solution**: Found a fix for the issue? Feel free to create a pull request with your changes.
402
+ Before contributing, please read the [contributing guidelines](https://github.com/brownboxdev/rabarber/blob/main/CONTRIBUTING.md)
589
403
 
590
- ## Contributing
404
+ ## License
591
405
 
592
- Before creating an issue or a pull request, please read the [contributing guidelines](https://github.com/brownboxdev/rabarber/blob/main/CONTRIBUTING.md).
406
+ The gem is available as open source under the terms of the [MIT License](https://github.com/brownboxdev/rabarber/blob/main/LICENSE.txt).
593
407
 
594
408
  ## Code of Conduct
595
409
 
@@ -601,7 +415,3 @@ Only the latest major version is supported. Older versions are obsolete and not
601
415
 
602
416
  [v4.x.x](https://github.com/brownboxdev/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/brownboxdev/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
603
417
  | [v2.x.x](https://github.com/brownboxdev/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/brownboxdev/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)
604
-
605
- ## License
606
-
607
- The gem is available as open source under the terms of the [MIT License](https://github.com/brownboxdev/rabarber/blob/main/LICENSE.txt).