Resources

 
Download Source Code

Summary

# 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