# Terminal
rails g scaffold product name price:decimal{5-2} stock:integer
rails g model permission user:belongs_to access:integer name_model name_attribute
rails db:migrate
# models/user.rb
has_many :permissions, dependent: :destroy
accepts_nested_attributes_for :permissions
# models/permission.rb
class Permission < ApplicationRecord
belongs_to :user
enum access: { view: 0, edit: 1, no_access: 2 }
end
# db/migrate/20200618013128_create_permissions.rb
class CreatePermissions < ActiveRecord::Migration[6.0]
def change
create_table :permissions do |t|
t.belongs_to :user, null: false, foreign_key: true
t.integer :access, default: 0, limit: 1
t.string :name_model
t.string :name_attribute
t.timestamps
end
end
end
# config/initializers/permission.rb
PERMISSION_LIST = [
{ model: :product, attribute: :name, default: :view },
{ model: :product, attribute: :price, default: :view },
{ model: :product, attribute: :stock, default: :view },
]
# views/users/_form.html.erb
<% PERMISSION_LIST.each do |hash| %>
<%= form.fields_for :permissions, user.permissions.find { |permission|
permission.name_model == hash[:model].to_s &&
permission.name_attribute == hash[:attribute].to_s } ||
user.permissions.new(name_model: hash[:model], name_attribute: hash[:attribute]) do |p| %>
<div class="field">
<%= p.hidden_field :name_model %>
<%= p.hidden_field :name_attribute %>
<%= p.label :access, hash.values.join(' - ') %>
<%= p.select :access, collection: Permission.accesses.keys.to_a %>
</div>
<% end %>
<% end %>
# users_controller.rb
def user_params
params.require(:user).permit(:first_name, :last_name, :name, :admin, permissions_attributes: [
:id,
:name_model,
:name_attribute,
:access
])
end
# helpers/permissions_helper.rb
module PermissionsHelper
def can_view?(name_model, name_attribute)
permission = PERMISSION_LIST.find { |h| h[:model] == name_model && h[:attribute] == name_attribute }
return false unless permission
return [:view, :edit].include?(permission[:default]) unless user_signed_in?
user_permission = current_user.permissions.find { |p| p.name_model == name_model.to_s &&
p.name_attribute == name_attribute.to_s }
return [:view, :edit].include?(permission[:default]) unless user_permission
user_permission.view? || user_permission.edit?
end
def can_edit?(name_model, name_attribute)
permission = PERMISSION_LIST.find { |h| h[:model] == name_model && h[:attribute] == name_attribute }
return false unless permission
return [:edit].include?(permission[:default]) unless user_signed_in?
user_permission = current_user.permissions.find { |p| p.name_model == name_model.to_s &&
p.name_attribute == name_attribute.to_s }
return [:edit].include?(permission[:default]) unless user_permission
user_permission.edit?
end
end
# views/products/_form.html.erb
<%= content_tag :div, class: 'field' do %>
<%= form.label :name %>
<%= form.text_field :name, disabled: !can_edit?(:product, :name) %>
<% end if can_view?(:product, :name) %>
<%= content_tag :div, class: 'field' do %>
<%= form.label :price %>
<%= form.text_field :price, disabled: !can_edit?(:product, :price) %>
<% end if can_view?(:product, :price) %>
<%= content_tag :div, class: 'field' do %>
<%= form.label :stock %>
<%= form.number_field :stock, disabled: !can_edit?(:product, :stock) %>
<% end if can_view?(:product, :stock) %>
# views/products/index.html.erb
<table class='table'>
<thead>
<tr>
<%= content_tag :th, 'Name' if can_view?(:product, :name) %>
<%= content_tag :th, 'Price' if can_view?(:product, :price) %>
<%= content_tag :th, 'Stock' if can_view?(:product, :stock) %>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @products.each do |product| %>
<tr>
<%= content_tag :td, product.name if can_view?(:product, :name) %>
<%= content_tag :td, product.price if can_view?(:product, :price) %>
<%= content_tag :td, product.stock if can_view?(:product, :stock) %>
<td><%= link_to 'Show', product %></td>
<td><%= link_to 'Edit', edit_product_path(product) %></td>
<td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
# products_controller.rb
include PermissionsHelper
def product_params
allow_params = [].tap do |array|
array << :name if can_edit?(:product, :name)
array << :price if can_edit?(:product, :price)
array << :stock if can_edit?(:product, :stock)
end
params.require(:product).permit(allow_params)
end