├── .rspec ├── Gemfile ├── lib ├── voltex │ ├── version.rb │ ├── engine.rb │ ├── cancan.rb │ ├── pundit.rb │ ├── scope.rb │ └── setup.rb ├── voltex.rb ├── generators │ ├── active_record │ │ ├── templates │ │ │ ├── user │ │ │ │ ├── migration_existing.rb │ │ │ │ └── migration.rb │ │ │ ├── role │ │ │ │ └── migration.rb │ │ │ ├── permission │ │ │ │ ├── migration_existing.rb │ │ │ │ └── migration.rb │ │ │ └── permissions_role │ │ │ │ ├── migration.rb │ │ │ │ └── migration_existing.rb │ │ ├── voltex_permissions_role_generator.rb │ │ ├── voltex_permission_generator.rb │ │ ├── voltex_user_generator.rb │ │ ├── voltex_role_generator.rb │ │ └── voltex_generator.rb │ └── voltex │ │ ├── views_generator.rb │ │ ├── install_generator.rb │ │ ├── templates │ │ └── voltex.rb │ │ └── resources_generator.rb └── tasks │ └── voltex_tasks.rake ├── config └── routes.rb ├── app ├── controllers │ └── voltex │ │ ├── application_controller.rb │ │ └── roles_controller.rb ├── helpers │ └── voltex │ │ └── application_helper.rb └── views │ └── voltex │ └── roles │ └── edit.html.erb ├── CHANGELOG.md ├── .gitignore ├── features ├── support │ └── env.rb ├── permissions.feature ├── install_generator.feature ├── include_permissions.feature ├── exclude_permissions.feature ├── views_generator.feature ├── step_definitions │ └── voltex.rb ├── resources_generator.feature └── custom_resources_generator.feature ├── bin └── rails ├── Rakefile ├── spec ├── voltex │ ├── cancan_spec.rb │ ├── setup_spec.rb │ └── pundit_spec.rb ├── controllers │ └── roles_controller_spec.rb ├── rails_helper.rb └── spec_helper.rb ├── voltex.gemspec ├── LICENSE.txt ├── README.md └── Gemfile.lock /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /lib/voltex/version.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | VERSION = '0.1.0' 3 | end 4 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Voltex::Engine.routes.draw do 2 | resources :roles, only: [:edit, :update] 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/voltex/application_controller.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | class ApplicationController < ::ApplicationController 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### Version 0.0.8 4 | * Add migration version in rails 5+. 5 | 6 | ### Version 0.0.7 7 | * Fix parameterize deprecation. 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | .rvmrc 5 | .ruby-gemset 6 | .ruby-version 7 | /tmp 8 | 9 | spec/dummy/db/*.sqlite3 10 | spec/dummy/log/*.log 11 | spec/dummy/tmp/ 12 | -------------------------------------------------------------------------------- /lib/voltex.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'request_store' 3 | require 'voltex/scope' 4 | require 'voltex/engine' 5 | require 'voltex/setup' 6 | 7 | module Voltex 8 | extend Setup 9 | end 10 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | require 'aruba/cucumber' 2 | 3 | APP_NAME = 'test_app' 4 | VOLTEX_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze 5 | APP_ROOT = "#{VOLTEX_ROOT}/tmp/aruba/#{APP_NAME}" 6 | -------------------------------------------------------------------------------- /lib/voltex/engine.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | class Engine < ::Rails::Engine 3 | isolate_namespace Voltex 4 | 5 | ActiveSupport.on_load(:action_controller) do 6 | include Voltex::Scope 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/helpers/voltex/application_helper.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | module ApplicationHelper 3 | def method_missing(method, *args, &block) 4 | main_app.send(method, *args, &block) 5 | rescue NoMethodError 6 | super 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/generators/active_record/templates/user/migration_existing.rb: -------------------------------------------------------------------------------- 1 | class AddVoltexTo<%= table_name.camelize %> < <%= migration_class_name %> 2 | def change 3 | change_table(:<%= table_name %>) do |t| 4 | t.references :<%= Voltex.role_name %> 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/voltex/cancan.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | module CanCan 3 | def define_voltex_abilities(user) 4 | Voltex.current_permissions.each do |permission| 5 | can permission.action.to_sym, permission.resource.constantize 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/generators/active_record/templates/role/migration.rb: -------------------------------------------------------------------------------- 1 | class VoltexCreate<%= table_name.camelize %> < <%= migration_class_name %> 2 | def change 3 | create_table(:<%= table_name %>) do |t| 4 | t.string :name 5 | t.timestamps null: false 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/generators/active_record/templates/permission/migration_existing.rb: -------------------------------------------------------------------------------- 1 | class AddVoltexTo<%= table_name.camelize %> < <%= migration_class_name %> 2 | def change 3 | change_table(:<%= table_name %>) do |t| 4 | t.string :resource 5 | t.string :action 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/voltex/pundit.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | module Pundit 3 | def permission?(resource, action) 4 | !!Voltex.current_permissions.detect do |permission| 5 | permission.resource == resource.to_s && 6 | permission.action == action.to_s 7 | end 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/generators/active_record/templates/permission/migration.rb: -------------------------------------------------------------------------------- 1 | class VoltexCreate<%= table_name.camelize %> < <%= migration_class_name %> 2 | def change 3 | create_table(:<%= table_name %>) do |t| 4 | t.string :resource 5 | t.string :action 6 | t.timestamps null: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/generators/active_record/templates/permissions_role/migration.rb: -------------------------------------------------------------------------------- 1 | class VoltexCreate<%= table_name.camelize %> < <%= migration_class_name %> 2 | def change 3 | create_table(:<%= table_name %>) do |t| 4 | t.references :<%= Voltex.permission_name %> 5 | t.references :<%= Voltex.role_name %> 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/generators/active_record/templates/user/migration.rb: -------------------------------------------------------------------------------- 1 | class VoltexCreate<%= table_name.camelize %> < <%= migration_class_name %> 2 | def change 3 | create_table(:<%= table_name %>) do |t| 4 | t.string :name 5 | t.references :<%= Voltex.role_name %> 6 | t.timestamps null: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/generators/active_record/templates/permissions_role/migration_existing.rb: -------------------------------------------------------------------------------- 1 | class AddVoltexTo<%= table_name.camelize %> < <%= migration_class_name %> 2 | def change 3 | change_table(:<%= table_name %>) do |t| 4 | t.references :<%= Voltex.permission_name %> 5 | t.references :<%= Voltex.role_name %> 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/generators/voltex/views_generator.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | module Generators 3 | class ViewsGenerator < Rails::Generators::Base 4 | source_root File.expand_path('../../../../app/views/voltex', __FILE__) 5 | desc 'Creates voltex views.' 6 | 7 | def create_views 8 | directory 'roles', 'app/views/voltex/roles' 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/generators/voltex/install_generator.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | module Generators 3 | class InstallGenerator < Rails::Generators::Base 4 | source_root File.expand_path('../templates', __FILE__) 5 | desc 'Creates a voltex initializer.' 6 | 7 | def create_initializer 8 | template 'voltex.rb', 'config/initializers/voltex.rb' 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/voltex/scope.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | module Scope 3 | extend ActiveSupport::Concern 4 | 5 | protected 6 | 7 | def set_current_permissions 8 | return unless voltex_user || voltex_user.respond_to?(Voltex.permissions_name) 9 | Voltex.current_permissions = voltex_user.send(Voltex.permissions_name).to_a 10 | end 11 | 12 | def voltex_user 13 | return unless defined?(current_user) 14 | current_user 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application. 3 | 4 | ENGINE_ROOT = File.expand_path('../..', __FILE__) 5 | ENGINE_PATH = File.expand_path('../../lib/voltex/engine', __FILE__) 6 | 7 | # Set up gems listed in the Gemfile. 8 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 9 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 10 | 11 | require 'rails/all' 12 | require 'rails/engine/commands' 13 | -------------------------------------------------------------------------------- /app/views/voltex/roles/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Defining Role Permissions

2 | 3 | <%= form_for @resource, as: Voltex.role_name, url: voltex.role_path(@resource) do |f| %> 4 | <% Voltex.permission_model.select(:resource).group(:resource).each do |permission| %> 5 |
<%= permission.resource.pluralize %>
6 | <%= f.collection_check_boxes("#{Voltex.permission_name}_ids", 7 | Voltex.permission_model.where(resource: permission.resource), :id, :action) %> 8 | <% end %> 9 | 10 |
11 | <%= f.submit %> 12 |
13 | <% end %> 14 | -------------------------------------------------------------------------------- /lib/generators/voltex/templates/voltex.rb: -------------------------------------------------------------------------------- 1 | Voltex.setup do |config| 2 | # Voltex classes configuration. 3 | # config.user_class = 'User' 4 | # config.role_class = 'Role' 5 | # config.permission_class = 'Permission' 6 | # 7 | # Default actions. 8 | # config.default_actions = %w(index show create update destroy) 9 | # 10 | # Excluding default permissions. 11 | # config.exclude = [ 12 | # { resource: 'User', action: 'destroy' }, 13 | # ] 14 | # 15 | # Including other permissions. 16 | # config.include = [ 17 | # { resource: 'User', action: 'report' }, 18 | # ] 19 | end 20 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require 'rdoc/task' 8 | 9 | RDoc::Task.new(:rdoc) do |rdoc| 10 | rdoc.rdoc_dir = 'rdoc' 11 | rdoc.title = 'Voltex' 12 | rdoc.options << '--line-numbers' 13 | rdoc.rdoc_files.include('README.md') 14 | rdoc.rdoc_files.include('lib/**/*.rb') 15 | end 16 | 17 | load 'rails/tasks/statistics.rake' 18 | Bundler::GemHelper.install_tasks 19 | 20 | require 'cucumber/rake/task' 21 | Cucumber::Rake::Task.new(:cucumber) 22 | 23 | task default: :cucumber 24 | -------------------------------------------------------------------------------- /lib/generators/active_record/voltex_permissions_role_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/active_record' 2 | require 'generators/active_record/voltex_generator' 3 | 4 | module ActiveRecord 5 | module Generators 6 | class VoltexPermissionsRoleGenerator < ActiveRecord::Generators::Base 7 | include VoltexGenerator 8 | 9 | source_root File.expand_path('../templates/permissions_role', __FILE__) 10 | 11 | def generate_model 12 | # There wont be a model. 13 | end 14 | 15 | def add_model_content 16 | # There wont be a model. 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/generators/active_record/voltex_permission_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/active_record' 2 | require 'generators/active_record/voltex_generator' 3 | 4 | module ActiveRecord 5 | module Generators 6 | class VoltexPermissionGenerator < ActiveRecord::Generators::Base 7 | include VoltexGenerator 8 | 9 | source_root File.expand_path('../templates/permission', __FILE__) 10 | 11 | private 12 | 13 | def model_content 14 | "# Voltex. 15 | # Please review the following content. 16 | has_and_belongs_to_many :#{Voltex.roles_name}" 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /features/permissions.feature: -------------------------------------------------------------------------------- 1 | Feature: Default permissions 2 | This feature creates permissions for actions index, show, create, 3 | update, destroy for each the resources (models) in parent application. 4 | 5 | Background: 6 | When I generate a new rails application 7 | And I run `bundle exec rails g voltex:install` 8 | And I run `bundle exec rails g voltex:resources` 9 | And I run `bundle exec rake db:migrate` 10 | 11 | Scenario: Creates default permissions 12 | Given I generate models "Post Comment Product" 13 | When I run `bundle exec rake voltex` 14 | Then I have "index show create update destroy" permissions for "Post Comment Product" 15 | -------------------------------------------------------------------------------- /lib/generators/active_record/voltex_user_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/active_record' 2 | require 'generators/active_record/voltex_generator' 3 | 4 | module ActiveRecord 5 | module Generators 6 | class VoltexUserGenerator < ActiveRecord::Generators::Base 7 | include VoltexGenerator 8 | 9 | source_root File.expand_path('../templates/user', __FILE__) 10 | 11 | private 12 | 13 | def model_content 14 | "# Voltex. 15 | # Please review the following content. 16 | belongs_to :#{Voltex.role_name} 17 | has_many :#{Voltex.permissions_name}, through: :#{Voltex.role_name}" 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/voltex/resources_generator.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | module Generators 3 | class ResourcesGenerator < Rails::Generators::Base 4 | desc 'Creates voltex resources.' 5 | 6 | def create_resources 7 | Rails::Generators.invoke "#{orm}:voltex_user", [Voltex.user_class] 8 | Rails::Generators.invoke "#{orm}:voltex_role", [Voltex.role_class] 9 | Rails::Generators.invoke "#{orm}:voltex_permission", [Voltex.permission_class] 10 | Rails::Generators.invoke "#{orm}:voltex_permissions_role", [Voltex.permissions_role_class] 11 | end 12 | 13 | private 14 | 15 | def orm 16 | Rails.configuration.generators.options[:rails][:orm] 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/tasks/voltex_tasks.rake: -------------------------------------------------------------------------------- 1 | desc 'Creates default permissions' 2 | task voltex: :environment do 3 | Rails.application.eager_load! 4 | Zeitwerk::Loader.eager_load_all if defined?(Zeitwerk) 5 | base_class = ApplicationRecord rescue ActiveRecord::Base 6 | 7 | base_class.descendants.each do |descendant| 8 | Voltex.default_actions.each do |action| 9 | Voltex.permission_model.where( 10 | resource: descendant.name, 11 | action: action 12 | ).first_or_create 13 | end 14 | end 15 | 16 | Voltex.exclude.each do |attrs| 17 | Voltex.permission_model.where(attrs).delete_all 18 | end 19 | 20 | Voltex.include.each do |attrs| 21 | Voltex.permission_model.where(attrs).first_or_create 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/generators/active_record/voltex_role_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/active_record' 2 | require 'generators/active_record/voltex_generator' 3 | 4 | module ActiveRecord 5 | module Generators 6 | class VoltexRoleGenerator < ActiveRecord::Generators::Base 7 | include VoltexGenerator 8 | 9 | source_root File.expand_path('../templates/role', __FILE__) 10 | 11 | private 12 | 13 | def existing_migration 14 | # There won't be a existing migration. 15 | end 16 | 17 | def model_content 18 | "# Voltex. 19 | # Please review the following content. 20 | has_many :#{Voltex.users_name} 21 | has_and_belongs_to_many :#{Voltex.permissions_name}" 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/voltex/cancan_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | require 'voltex/cancan/ability' 3 | require 'cancan' 4 | 5 | RSpec.describe Voltex::CanCan::Ability do 6 | let(:user) { User.create(name: 'User') } 7 | let(:role) { Role.create(name: 'Manager') } 8 | let(:perm) { Permission.create(resource: 'Role', action: :show) } 9 | 10 | class Ability 11 | include CanCan::Ability 12 | include Voltex::CanCan::Ability 13 | 14 | def initialize(user) 15 | define_voltex_abilities(user) 16 | end 17 | end 18 | 19 | before do 20 | role.permissions << perm 21 | user.role = role 22 | user.save 23 | Voltex.current_permissions = [perm] 24 | end 25 | 26 | subject { Ability.new(user) } 27 | 28 | describe '#define_voltex_abilities' do 29 | it { expect(subject.can?(:show, Role)).to be true } 30 | it { expect(subject.can?(:update, Role)).to be false } 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/controllers/voltex/roles_controller.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'voltex/application_controller' 2 | 3 | module Voltex 4 | class RolesController < ApplicationController 5 | before_action :set_resource 6 | 7 | # GET /voltex/roles/1/edit 8 | def edit 9 | end 10 | 11 | # PUT /voltex/roles/1 12 | def update 13 | if @resource.update(resource_params) 14 | redirect_to [main_app, @resource], notice: t('.notice', default: update_message) 15 | else 16 | render :edit 17 | end 18 | end 19 | 20 | private 21 | 22 | def set_resource 23 | @resource = Voltex.role_model.find(params[:id]) 24 | end 25 | 26 | def resource_params 27 | params.require(Voltex.role_name).permit(*permitted_attributes) 28 | end 29 | 30 | def permitted_attributes 31 | [{:"#{Voltex.permission_name}_ids" => []}] 32 | end 33 | 34 | def update_message 35 | 'Permissions were updated successfully.' 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/voltex/setup_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Voltex do 4 | describe 'default setup' do 5 | it { expect(subject.default_actions).to eq %w(index show create update destroy) } 6 | it { expect(subject.user_class).to eq 'User' } 7 | it { expect(subject.role_class).to eq 'Role' } 8 | it { expect(subject.permission_class).to eq 'Permission' } 9 | it { expect(subject.permissions_role_class).to eq 'PermissionsRole' } 10 | it { expect(subject.exclude).to eq [] } 11 | it { expect(subject.include).to eq [] } 12 | it { expect(subject.user_model).to eq User } 13 | it { expect(subject.role_model).to eq Role } 14 | it { expect(subject.permission_model).to eq Permission } 15 | it { expect(subject.user_name).to eq 'user' } 16 | it { expect(subject.role_name).to eq 'role' } 17 | it { expect(subject.permission_name).to eq 'permission' } 18 | it { expect(subject.users_name).to eq 'users' } 19 | it { expect(subject.roles_name).to eq 'roles' } 20 | it { expect(subject.permissions_name).to eq 'permissions' } 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /voltex.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path('../lib', __FILE__) 2 | require 'voltex/version' 3 | 4 | Gem::Specification.new do |s| 5 | s.name = 'voltex' 6 | s.version = Voltex::VERSION 7 | s.authors = ['Erick Fabian', 'Alejandro Gutiérrez'] 8 | s.email = ['fabianerickalfonso@gmail.com', 'alejandrodevs@gmail.com'] 9 | s.homepage = 'https://github.com/SyeSoftware/voltex' 10 | s.summary = 'Dynamic permissions authorization.' 11 | s.description = 'This engine aims to provide an easy way to work with dynamic permissions.' 12 | s.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.md'] 13 | s.license = 'MIT' 14 | 15 | s.add_dependency 'rails', '>= 4.2' 16 | s.add_dependency 'activerecord', '>= 4.2' 17 | s.add_dependency 'request_store', '~> 1.3' 18 | 19 | s.add_development_dependency 'aruba', '~> 0.14' 20 | s.add_development_dependency 'cancan', '~> 1.6' 21 | s.add_development_dependency 'sqlite3', '~> 1.4' 22 | s.add_development_dependency 'cucumber', '~> 3.1' 23 | s.add_development_dependency 'rspec-rails', '~> 3.9' 24 | end 25 | -------------------------------------------------------------------------------- /spec/voltex/pundit_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | require 'voltex/pundit' 3 | 4 | RSpec.describe Voltex::Pundit do 5 | let(:user) { User.create(name: 'User') } 6 | let(:role) { Role.create(name: 'Manager') } 7 | let(:perm) { Permission.create(resource: 'Role', action: :show) } 8 | 9 | class ApplicationPolicy 10 | include Voltex::Pundit 11 | attr_reader :user, :record 12 | 13 | def initialize(user, record) 14 | @user = user 15 | @record = record 16 | end 17 | end 18 | 19 | before do 20 | role.permissions << perm 21 | user.role = role 22 | user.save 23 | Voltex.current_permissions = [perm] 24 | end 25 | 26 | subject { ApplicationPolicy.new(user, role) } 27 | 28 | describe '#permission?' do 29 | context 'when has the permission' do 30 | it 'returns true' do 31 | expect(subject.permission?(Role, :show)).to be true 32 | end 33 | end 34 | 35 | context 'when has not the permission' do 36 | it 'returns false' do 37 | expect(subject.permission?(Role, :destroy)).to be false 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /features/install_generator.feature: -------------------------------------------------------------------------------- 1 | Feature: Install generator 2 | This feature takes place when user runs 'rails g voltex:install' 3 | after he has included voltex in the project. 4 | 5 | Background: 6 | When I generate a new rails application 7 | 8 | Scenario: Creates voltex initializer 9 | When I run `bundle exec rails g voltex:install` 10 | Then the file "config/initializers/voltex.rb" should contain: 11 | """ 12 | Voltex.setup do |config| 13 | # Voltex classes configuration. 14 | # config.user_class = 'User' 15 | # config.role_class = 'Role' 16 | # config.permission_class = 'Permission' 17 | # 18 | # Default actions. 19 | # config.default_actions = %w(index show create update destroy) 20 | # 21 | # Excluding default permissions. 22 | # config.exclude = [ 23 | # { resource: 'User', action: 'destroy' }, 24 | # ] 25 | # 26 | # Including other permissions. 27 | # config.include = [ 28 | # { resource: 'User', action: 'report' }, 29 | # ] 30 | end 31 | """ 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Alejandro Gutiérrez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /features/include_permissions.feature: -------------------------------------------------------------------------------- 1 | Feature: Include not default permissions 2 | This feature takes place when user wants to include some permissions 3 | that won't be created when runs 'rake voltex'. 4 | 5 | Background: 6 | When I generate a new rails application 7 | And I run `bundle exec rails g voltex:install` 8 | And I run `bundle exec rails g voltex:resources` 9 | And I run `bundle exec rake db:migrate` 10 | 11 | Scenario: Include permissions 12 | Given a file named "config/initializers/voltex.rb" with: 13 | """ 14 | Voltex.setup do |config| 15 | # Configurable options should be here. 16 | # config.user_class = 'User' 17 | # config.role_class = 'Role' 18 | # config.permission_class = 'Permission' 19 | # 20 | config.include = [ 21 | { resource: 'Member', action: 'enable' }, 22 | { resource: 'Payment', action: 'cancel' }, 23 | ] 24 | end 25 | """ 26 | And I generate models "Member Payment" 27 | When I run `bundle exec rake voltex` 28 | Then I have "enable" permissions for "Member" 29 | And I have "cancel" permissions for "Payment" 30 | -------------------------------------------------------------------------------- /features/exclude_permissions.feature: -------------------------------------------------------------------------------- 1 | Feature: Exclude default permissions 2 | This feature takes place when user wants to exclude some default 3 | permissions that will be created when runs 'rake voltex'. 4 | 5 | Background: 6 | When I generate a new rails application 7 | And I run `bundle exec rails g voltex:install` 8 | And I run `bundle exec rails g voltex:resources` 9 | And I run `bundle exec rake db:migrate` 10 | 11 | Scenario: Exclude permissions 12 | Given a file named "config/initializers/voltex.rb" with: 13 | """ 14 | Voltex.setup do |config| 15 | # Configurable options should be here. 16 | # config.user_class = 'User' 17 | # config.role_class = 'Role' 18 | # config.permission_class = 'Permission' 19 | # 20 | config.exclude = [ 21 | { resource: 'Post', action: 'update' }, 22 | { resource: 'Product' }, 23 | ] 24 | end 25 | """ 26 | And I generate models "Post Product" 27 | When I run `bundle exec rake voltex` 28 | Then I have not "update" permissions for "Post" 29 | And I have not "index show create update destroy" permissions for "Product" 30 | -------------------------------------------------------------------------------- /features/views_generator.feature: -------------------------------------------------------------------------------- 1 | Feature: Views generator 2 | This feature takes place when user runs 'rails g voltex:views' 3 | after he has included voltex in the project. 4 | 5 | Background: 6 | When I generate a new rails application 7 | And I run `bundle exec rails g voltex:install` 8 | And I run `bundle exec rails g voltex:resources` 9 | And I run `bundle exec rake db:migrate` 10 | 11 | Scenario: Creates view to configure role permissions 12 | When I run `bundle exec rails g voltex:views` 13 | Then the file "app/views/voltex/roles/edit.html.erb" should contain: 14 | """ 15 |

Defining Role Permissions

16 | 17 | <%= form_for @resource, as: Voltex.role_name, url: voltex.role_path(@resource) do |f| %> 18 | <% Voltex.permission_model.select(:resource).group(:resource).each do |permission| %> 19 |
<%= permission.resource.pluralize %>
20 | <%= f.collection_check_boxes("#{Voltex.permission_name}_ids", 21 | Voltex.permission_model.where(resource: permission.resource), :id, :action) %> 22 | <% end %> 23 | 24 |
25 | <%= f.submit %> 26 |
27 | <% end %> 28 | """ 29 | -------------------------------------------------------------------------------- /lib/voltex/setup.rb: -------------------------------------------------------------------------------- 1 | module Voltex 2 | module Setup 3 | mattr_accessor :default_actions 4 | @@default_actions = %w(index show create update destroy) 5 | 6 | mattr_accessor :user_class 7 | @@user_class = 'User' 8 | 9 | mattr_accessor :role_class 10 | @@role_class = 'Role' 11 | 12 | mattr_accessor :permission_class 13 | @@permission_class = 'Permission' 14 | 15 | mattr_accessor :exclude 16 | @@exclude = [] 17 | 18 | mattr_accessor :include 19 | @@include = [] 20 | 21 | 22 | def current_permissions=(permissions) 23 | RequestStore.store[:current_permissions] = permissions 24 | end 25 | 26 | def current_permissions 27 | RequestStore.store[:current_permissions] || [] 28 | end 29 | 30 | def permissions_role_class 31 | [permission_class, role_class].map(&:pluralize).sort.join.singularize 32 | end 33 | 34 | %w(user role permission).each do |resource| 35 | define_method "#{resource}_model" do 36 | send("#{resource}_class").constantize 37 | end 38 | 39 | define_method "#{resource}_name" do 40 | send("#{resource}_class").underscore 41 | end 42 | 43 | define_method "#{resource.pluralize}_name" do 44 | send("#{resource}_name").pluralize 45 | end 46 | end 47 | 48 | def setup 49 | yield(self) if block_given? 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/controllers/roles_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Voltex::RolesController do 4 | routes { Voltex::Engine.routes } 5 | let!(:role) { Role.create(name: 'Manager') } 6 | let!(:perm1) { Permission.create(resource: 'Post', action: :index) } 7 | let!(:perm2) { Permission.create(resource: 'Post', action: :show) } 8 | let!(:perm3) { Permission.create(resource: 'Post', action: :create) } 9 | let!(:perm4) { Permission.create(resource: 'Post', action: :update) } 10 | let!(:perm5) { Permission.create(resource: 'Post', action: :destroy) } 11 | 12 | describe 'GET edit' do 13 | it 'assigns resource' do 14 | get :edit, id: role.to_param 15 | expect(assigns(:resource)).to eq role 16 | end 17 | end 18 | 19 | describe 'PUT update' do 20 | context 'with valid params' do 21 | let(:permissions) { [1, 3, 5] } 22 | 23 | before do 24 | put :update, id: role.to_param, role: { 25 | permission_ids: permissions 26 | } 27 | end 28 | 29 | it 'updates resource permissions' do 30 | expect(role.reload.permission_ids).to eql permissions 31 | end 32 | 33 | it 'assigns resource' do 34 | expect(assigns(:resource)).to eq role 35 | end 36 | 37 | it 'sets flash message' do 38 | expect(flash[:notice]).to eql 'Permissions were updated successfully.' 39 | end 40 | 41 | it 'redirects to resource' do 42 | expect(response).to redirect_to "/roles/#{role.id}" 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/generators/active_record/voltex_generator.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecord 2 | module Generators 3 | module VoltexGenerator 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | argument :attributes, type: :array, default: [] 8 | 9 | def copy_migration 10 | table_exists? ? existing_migration : new_migration 11 | end 12 | 13 | def generate_model 14 | unless model_exists? 15 | invoke 'active_record:model', [name], migration: false, skip_namespace: true 16 | end 17 | end 18 | 19 | def add_model_content 20 | inject_into_class(model_path, name, sanitized_content) if model_exists? 21 | end 22 | 23 | private 24 | 25 | def new_migration 26 | migration_template 'migration.rb', "db/migrate/voltex_create_#{table_name}.rb" 27 | end 28 | 29 | def existing_migration 30 | migration_template 'migration_existing.rb', "db/migrate/add_voltex_to_#{table_name}.rb" 31 | end 32 | 33 | def model_exists? 34 | File.exists?(File.join(destination_root, model_path)) 35 | end 36 | 37 | def model_path 38 | File.join('app', 'models', "#{class_name.underscore}.rb") 39 | end 40 | 41 | def sanitized_content 42 | model_content.split("\n").map { |line| ' ' + line.strip }.join("\n") << "\n" 43 | end 44 | 45 | def namespaced? 46 | false 47 | end 48 | 49 | def table_exists? 50 | ActiveRecord::Base.connection.table_exists? table_name 51 | end 52 | 53 | def migration_class_name 54 | Rails.version.to_i == 5 ? 55 | "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]": 56 | 'ActiveRecord::Migration' 57 | end 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /features/step_definitions/voltex.rb: -------------------------------------------------------------------------------- 1 | When 'I generate a new rails application' do 2 | steps %{ 3 | When I run `rails plugin new dummy` 4 | When I run `mv dummy/test/dummy #{APP_NAME}` 5 | When I overwrite the file named "#{APP_NAME}/config/application.rb" with: 6 | """ 7 | require_relative 'boot' 8 | 9 | require 'rails/all' 10 | 11 | Bundler.require(*Rails.groups) 12 | 13 | module Dummy 14 | class Application < Rails::Application 15 | # Initialize configuration defaults for originally generated Rails version. 16 | config.load_defaults 5.1 17 | 18 | # Settings in config/environments/* take precedence over those specified here. 19 | # Application configuration should go into files in config/initializers 20 | # -- all .rb files in that directory are automatically loaded. 21 | end 22 | end 23 | """ 24 | And I cd to "#{APP_NAME}" 25 | And I append to "Gemfile" with: 26 | """ 27 | gem 'sqlite3' 28 | gem 'voltex', path: '#{VOLTEX_ROOT}' 29 | """ 30 | And I run `bundle install --local` 31 | } 32 | 33 | ENV['RAILS_ENV'] ||= 'test' 34 | require File.expand_path("#{APP_ROOT}/config/environment.rb", __FILE__) 35 | ENV['RAILS_ROOT'] ||= APP_ROOT 36 | end 37 | 38 | When /^the model(?: named)? "([^"]*)" should (not )?contain:$/ do |model, negated, content| 39 | file = Dir["#{APP_ROOT}/app/models/*#{model.downcase}.rb"].first.split("#{APP_NAME}/").last 40 | expect(file).to have_file_content file_content_including(content.chomp) 41 | end 42 | 43 | When /^the migration(?: named)? "([^"]*)" should (not )?contain:$/ do |migration, negated, content| 44 | file = Dir["#{APP_ROOT}/db/migrate/*#{migration}.rb"].first.split("#{APP_NAME}/").last 45 | expect(file).to have_file_content file_content_including(content.chomp) 46 | end 47 | 48 | When /^I generate models "([^\"]+)"$/ do |models| 49 | models.split(' ').each do |model| 50 | steps %{ 51 | When I write to "app/models/#{model.downcase}.rb" with: 52 | """\nclass #{model} < ActiveRecord::Base\nend\n""" 53 | } 54 | end 55 | end 56 | 57 | When /^I have (not )?"([^\"]+)" permissions for "([^\"]+)"$/ do |match, actions, resources| 58 | resources.split(' ').each do |resource| 59 | actions.split(' ').each do |action| 60 | expect(Voltex.permission_class.constantize.where( 61 | resource: resource, action: action 62 | ).exists?).to be !match 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV['RAILS_ENV'] ||= 'test' 3 | require 'spec_helper' 4 | require File.expand_path('../dummy/config/environment', __FILE__) 5 | require 'rspec/rails' 6 | # Add additional requires below this line. Rails is not loaded until this point! 7 | 8 | # Requires supporting ruby files with custom matchers and macros, etc, in 9 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 10 | # run as spec files by default. This means that files in spec/support that end 11 | # in _spec.rb will both be required and run as specs, causing the specs to be 12 | # run twice. It is recommended that you do not name files matching this glob to 13 | # end with _spec.rb. You can configure this pattern with the --pattern 14 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 15 | # 16 | # The following line is provided for convenience purposes. It has the downside 17 | # of increasing the boot-up time by auto-requiring all files in the support 18 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 19 | # require only the support files necessary. 20 | # 21 | # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 22 | 23 | # Checks for pending migrations before tests are run. 24 | # If you are not using ActiveRecord, you can remove this line. 25 | ActiveRecord::Migration.maintain_test_schema! 26 | 27 | RSpec.configure do |config| 28 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 29 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 30 | 31 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 32 | # examples within a transaction, remove the following line or assign false 33 | # instead of true. 34 | config.use_transactional_fixtures = true 35 | 36 | # RSpec Rails can automatically mix in different behaviours to your tests 37 | # based on their file location, for example enabling you to call `get` and 38 | # `post` in specs under `spec/controllers`. 39 | # 40 | # You can disable this behaviour by removing the line below, and instead 41 | # explicitly tag your specs with their type, e.g.: 42 | # 43 | # RSpec.describe UsersController, :type => :controller do 44 | # # ... 45 | # end 46 | # 47 | # The different available types are documented in the features, such as in 48 | # https://relishapp.com/rspec/rspec-rails/docs 49 | config.infer_spec_type_from_file_location! 50 | end 51 | -------------------------------------------------------------------------------- /features/resources_generator.feature: -------------------------------------------------------------------------------- 1 | Feature: Resources generator 2 | This generator creates models or adds them some code in order to 3 | work properly. This creates migrations to create or update some 4 | database tables. 5 | 6 | Background: 7 | When I generate a new rails application 8 | And I run `bundle exec rails g voltex:install` 9 | 10 | Scenario: Creates default voltex resources 11 | When I run `bundle exec rails g voltex:resources` 12 | Then the model "User" should contain: 13 | """ 14 | class User < ApplicationRecord 15 | # Voltex. 16 | # Please review the following content. 17 | belongs_to :role 18 | has_many :permissions, through: :role 19 | end 20 | """ 21 | And the model "Role" should contain: 22 | """ 23 | class Role < ApplicationRecord 24 | # Voltex. 25 | # Please review the following content. 26 | has_many :users 27 | has_and_belongs_to_many :permissions 28 | end 29 | """ 30 | And the model "Permission" should contain: 31 | """ 32 | class Permission < ApplicationRecord 33 | # Voltex. 34 | # Please review the following content. 35 | has_and_belongs_to_many :roles 36 | end 37 | """ 38 | And the migration "voltex_create_users" should contain: 39 | """ 40 | class VoltexCreateUsers < ActiveRecord::Migration[5.1] 41 | def change 42 | create_table(:users) do |t| 43 | t.string :name 44 | t.references :role 45 | t.timestamps null: false 46 | end 47 | end 48 | end 49 | """ 50 | And the migration "voltex_create_roles" should contain: 51 | """ 52 | class VoltexCreateRoles < ActiveRecord::Migration[5.1] 53 | def change 54 | create_table(:roles) do |t| 55 | t.string :name 56 | t.timestamps null: false 57 | end 58 | end 59 | end 60 | """ 61 | And the migration "voltex_create_permissions" should contain: 62 | """ 63 | class VoltexCreatePermissions < ActiveRecord::Migration[5.1] 64 | def change 65 | create_table(:permissions) do |t| 66 | t.string :resource 67 | t.string :action 68 | t.timestamps null: false 69 | end 70 | end 71 | end 72 | """ 73 | And the migration "voltex_create_permissions_roles" should contain: 74 | """ 75 | class VoltexCreatePermissionsRoles < ActiveRecord::Migration[5.1] 76 | def change 77 | create_table(:permissions_roles) do |t| 78 | t.references :permission 79 | t.references :role 80 | end 81 | end 82 | end 83 | """ 84 | -------------------------------------------------------------------------------- /features/custom_resources_generator.feature: -------------------------------------------------------------------------------- 1 | Feature: Custom resources generator 2 | This generator creates not default models or adds them some code 3 | in order to work properly. This creates migrations to create or 4 | update some not default model's tables. 5 | 6 | Background: 7 | When I generate a new rails application 8 | And I run `bundle exec rails g voltex:install` 9 | 10 | Scenario: Creates custom voltex resources 11 | Given a file named "config/initializers/voltex.rb" with: 12 | """ 13 | Voltex.setup do |config| 14 | # Configurable options should be here. 15 | config.user_class = 'Member' 16 | config.role_class = 'Group' 17 | # config.permission_class = 'Permission' 18 | end 19 | """ 20 | And I run `bundle exec rails g voltex:resources` 21 | Then the model "Member" should contain: 22 | """ 23 | class Member < ApplicationRecord 24 | # Voltex. 25 | # Please review the following content. 26 | belongs_to :group 27 | has_many :permissions, through: :group 28 | end 29 | """ 30 | And the model "Group" should contain: 31 | """ 32 | class Group < ApplicationRecord 33 | # Voltex. 34 | # Please review the following content. 35 | has_many :members 36 | has_and_belongs_to_many :permissions 37 | end 38 | """ 39 | And the model "Permission" should contain: 40 | """ 41 | class Permission < ApplicationRecord 42 | # Voltex. 43 | # Please review the following content. 44 | has_and_belongs_to_many :groups 45 | end 46 | """ 47 | And the migration "voltex_create_members" should contain: 48 | """ 49 | class VoltexCreateMembers < ActiveRecord::Migration[5.1] 50 | def change 51 | create_table(:members) do |t| 52 | t.string :name 53 | t.references :group 54 | t.timestamps null: false 55 | end 56 | end 57 | end 58 | """ 59 | And the migration "voltex_create_groups" should contain: 60 | """ 61 | class VoltexCreateGroups < ActiveRecord::Migration[5.1] 62 | def change 63 | create_table(:groups) do |t| 64 | t.string :name 65 | t.timestamps null: false 66 | end 67 | end 68 | end 69 | """ 70 | And the migration "voltex_create_permissions" should contain: 71 | """ 72 | class VoltexCreatePermissions < ActiveRecord::Migration[5.1] 73 | def change 74 | create_table(:permissions) do |t| 75 | t.string :resource 76 | t.string :action 77 | t.timestamps null: false 78 | end 79 | end 80 | end 81 | """ 82 | And the migration "voltex_create_groups_permissions" should contain: 83 | """ 84 | class VoltexCreateGroupsPermissions < ActiveRecord::Migration[5.1] 85 | def change 86 | create_table(:groups_permissions) do |t| 87 | t.references :permission 88 | t.references :group 89 | end 90 | end 91 | end 92 | """ 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Voltex 2 | This engine aims to provide an easy way to work with dynamic permissions. 3 | 4 | 5 | ## Installation 6 | 1. Add Voltex to your Gemfile. 7 | 8 | ```ruby 9 | gem 'voltex' 10 | ``` 11 | 12 | 2. Generate voltex initializer. 13 | 14 | ``` 15 | rails g voltex:install 16 | ``` 17 | 18 | 3. Update initializer according your needs. 19 | 20 | ```ruby 21 | Voltex.setup do |config| 22 | # Voltex classes configuration. 23 | # config.user_class = 'User' 24 | # config.role_class = 'Role' 25 | # config.permission_class = 'Permission' 26 | ... 27 | end 28 | ``` 29 | 30 | 4. Generate voltex resources. 31 | 32 | ``` 33 | rails g voltex:resources 34 | ``` 35 | 36 | 5. Migrate your database. 37 | 38 | ``` 39 | rails db:migrate 40 | ``` 41 | 42 | 6. Create default permissions. 43 | 44 | ``` 45 | rake voltex 46 | ``` 47 | 48 | 7. Load current user permissions in your application controller. 49 | 50 | ```ruby 51 | class ApplicationController < ActionController::Base 52 | ... 53 | before_action :set_current_permissions 54 | end 55 | ``` 56 | 57 | By default this callback preloads permissions for `current_user` if this 58 | is defined. If you need that this callback preloads permission for 59 | another user just overwrite `voltex_user` method. 60 | 61 | Example: 62 | 63 | ```ruby 64 | class ApplicationController < ActionController::Base 65 | ... 66 | def voltex_user 67 | # Default is current_user. 68 | another_awesome_user 69 | end 70 | end 71 | ``` 72 | 73 | 74 | ## Using Voltex with Pundit 75 | Include voltex in your application policy. 76 | 77 | ```ruby 78 | require 'voltex/pundit' 79 | 80 | class ApplicationPolicy 81 | include Voltex::Pundit 82 | end 83 | ``` 84 | 85 | Now a new helper is available in your policies. 86 | 87 | ```ruby 88 | class PostPolicy 89 | def index? 90 | permission?(Post, :index) 91 | end 92 | end 93 | ``` 94 | 95 | 96 | ## Using Voltex with CanCan 97 | Include voltex in your ability class. 98 | 99 | ```ruby 100 | require 'voltex/cancan' 101 | 102 | class Ability 103 | include CanCan::Ability 104 | include Voltex::CanCan 105 | 106 | def initialize(user) 107 | define_voltex_abilities(user) 108 | end 109 | end 110 | ``` 111 | 112 | 113 | ## Including and Excluding permissions 114 | Update your voltex initializer: 115 | 116 | ```ruby 117 | Voltex.setup do |config| 118 | # Voltex classes configuration. 119 | # config.user_class = 'User' 120 | # config.role_class = 'Role' 121 | # config.permission_class = 'Permission' 122 | # 123 | # Excluding default permissions. 124 | config.exclude = [ 125 | { resource: 'Payment', action: 'destroy' }, 126 | { resource: 'Post', action: 'update' }, 127 | ] 128 | # 129 | # Including other permissions. 130 | config.include = [ 131 | { resource: 'User', action: 'enable' }, 132 | { resource: 'User', action: 'disable' }, 133 | ] 134 | end 135 | ``` 136 | 137 | And run voltex rake task again: 138 | 139 | ``` 140 | rake voltex 141 | ``` 142 | 143 | 144 | ## Defining role permissions 145 | Mount voltex engine in your application: 146 | 147 | ```ruby 148 | Rails.application.routes.draw do 149 | mount Voltex::Engine => '/voltex' 150 | end 151 | ``` 152 | 153 | Run voltex views generator: 154 | 155 | ``` 156 | rails g voltex:views 157 | ``` 158 | 159 | This will define a route `/voltex/roles/:id/edit` where 160 | permissions can be defined for each role. 161 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | RSpec.configure do |config| 20 | # rspec-expectations config goes here. You can use an alternate 21 | # assertion/expectation library such as wrong or the stdlib/minitest 22 | # assertions if you prefer. 23 | config.expect_with :rspec do |expectations| 24 | # This option will default to `true` in RSpec 4. It makes the `description` 25 | # and `failure_message` of custom matchers include text for helper methods 26 | # defined using `chain`, e.g.: 27 | # be_bigger_than(2).and_smaller_than(4).description 28 | # # => "be bigger than 2 and smaller than 4" 29 | # ...rather than: 30 | # # => "be bigger than 2" 31 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 32 | end 33 | 34 | # rspec-mocks config goes here. You can use an alternate test double 35 | # library (such as bogus or mocha) by changing the `mock_with` option here. 36 | config.mock_with :rspec do |mocks| 37 | # Prevents you from mocking or stubbing a method that does not exist on 38 | # a real object. This is generally recommended, and will default to 39 | # `true` in RSpec 4. 40 | mocks.verify_partial_doubles = true 41 | end 42 | 43 | # The settings below are suggested to provide a good initial experience 44 | # with RSpec, but feel free to customize to your heart's content. 45 | =begin 46 | # These two settings work together to allow you to limit a spec run 47 | # to individual examples or groups you care about by tagging them with 48 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 49 | # get run. 50 | config.filter_run :focus 51 | config.run_all_when_everything_filtered = true 52 | 53 | # Limits the available syntax to the non-monkey patched syntax that is 54 | # recommended. For more details, see: 55 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 56 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 57 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 58 | config.disable_monkey_patching! 59 | 60 | # Many RSpec users commonly either run the entire suite or an individual 61 | # file, and it's useful to allow more verbose output when running an 62 | # individual spec file. 63 | if config.files_to_run.one? 64 | # Use the documentation formatter for detailed output, 65 | # unless a formatter has already been configured 66 | # (e.g. via a command-line flag). 67 | config.default_formatter = 'doc' 68 | end 69 | 70 | # Print the 10 slowest examples and example groups at the 71 | # end of the spec run, to help surface which specs are running 72 | # particularly slow. 73 | config.profile_examples = 10 74 | 75 | # Run specs in random order to surface order dependencies. If you find an 76 | # order dependency and want to debug it, you can fix the order by providing 77 | # the seed, which is printed after each run. 78 | # --seed 1234 79 | config.order = :random 80 | 81 | # Seed global randomization in this process using the `--seed` CLI option. 82 | # Setting this allows you to use `--seed` to deterministically reproduce 83 | # test failures related to randomization by passing the same `--seed` value 84 | # as the one that triggered the failure. 85 | Kernel.srand config.seed 86 | =end 87 | end 88 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | voltex (0.1.0) 5 | activerecord (>= 4.2) 6 | rails (>= 4.2) 7 | request_store (~> 1.3) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | actioncable (6.0.0) 13 | actionpack (= 6.0.0) 14 | nio4r (~> 2.0) 15 | websocket-driver (>= 0.6.1) 16 | actionmailbox (6.0.0) 17 | actionpack (= 6.0.0) 18 | activejob (= 6.0.0) 19 | activerecord (= 6.0.0) 20 | activestorage (= 6.0.0) 21 | activesupport (= 6.0.0) 22 | mail (>= 2.7.1) 23 | actionmailer (6.0.0) 24 | actionpack (= 6.0.0) 25 | actionview (= 6.0.0) 26 | activejob (= 6.0.0) 27 | mail (~> 2.5, >= 2.5.4) 28 | rails-dom-testing (~> 2.0) 29 | actionpack (6.0.0) 30 | actionview (= 6.0.0) 31 | activesupport (= 6.0.0) 32 | rack (~> 2.0) 33 | rack-test (>= 0.6.3) 34 | rails-dom-testing (~> 2.0) 35 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 36 | actiontext (6.0.0) 37 | actionpack (= 6.0.0) 38 | activerecord (= 6.0.0) 39 | activestorage (= 6.0.0) 40 | activesupport (= 6.0.0) 41 | nokogiri (>= 1.8.5) 42 | actionview (6.0.0) 43 | activesupport (= 6.0.0) 44 | builder (~> 3.1) 45 | erubi (~> 1.4) 46 | rails-dom-testing (~> 2.0) 47 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 48 | activejob (6.0.0) 49 | activesupport (= 6.0.0) 50 | globalid (>= 0.3.6) 51 | activemodel (6.0.0) 52 | activesupport (= 6.0.0) 53 | activerecord (6.0.0) 54 | activemodel (= 6.0.0) 55 | activesupport (= 6.0.0) 56 | activestorage (6.0.0) 57 | actionpack (= 6.0.0) 58 | activejob (= 6.0.0) 59 | activerecord (= 6.0.0) 60 | marcel (~> 0.3.1) 61 | activesupport (6.0.0) 62 | concurrent-ruby (~> 1.0, >= 1.0.2) 63 | i18n (>= 0.7, < 2) 64 | minitest (~> 5.1) 65 | tzinfo (~> 1.1) 66 | zeitwerk (~> 2.1, >= 2.1.8) 67 | aruba (0.14.12) 68 | childprocess (>= 0.6.3, < 4.0.0) 69 | contracts (~> 0.9) 70 | cucumber (>= 1.3.19) 71 | ffi (~> 1.9) 72 | rspec-expectations (>= 2.99) 73 | thor (~> 0.19) 74 | backports (3.15.0) 75 | builder (3.2.3) 76 | cancan (1.6.10) 77 | childprocess (3.0.0) 78 | concurrent-ruby (1.1.5) 79 | contracts (0.16.0) 80 | crass (1.0.5) 81 | cucumber (3.1.2) 82 | builder (>= 2.1.2) 83 | cucumber-core (~> 3.2.0) 84 | cucumber-expressions (~> 6.0.1) 85 | cucumber-wire (~> 0.0.1) 86 | diff-lcs (~> 1.3) 87 | gherkin (~> 5.1.0) 88 | multi_json (>= 1.7.5, < 2.0) 89 | multi_test (>= 0.1.2) 90 | cucumber-core (3.2.1) 91 | backports (>= 3.8.0) 92 | cucumber-tag_expressions (~> 1.1.0) 93 | gherkin (~> 5.0) 94 | cucumber-expressions (6.0.1) 95 | cucumber-tag_expressions (1.1.1) 96 | cucumber-wire (0.0.1) 97 | diff-lcs (1.3) 98 | erubi (1.9.0) 99 | ffi (1.11.1) 100 | gherkin (5.1.0) 101 | globalid (0.4.2) 102 | activesupport (>= 4.2.0) 103 | i18n (1.7.0) 104 | concurrent-ruby (~> 1.0) 105 | loofah (2.3.1) 106 | crass (~> 1.0.2) 107 | nokogiri (>= 1.5.9) 108 | mail (2.7.1) 109 | mini_mime (>= 0.1.1) 110 | marcel (0.3.3) 111 | mimemagic (~> 0.3.2) 112 | method_source (0.9.2) 113 | mimemagic (0.3.3) 114 | mini_mime (1.0.2) 115 | mini_portile2 (2.4.0) 116 | minitest (5.12.2) 117 | multi_json (1.13.1) 118 | multi_test (0.1.2) 119 | nio4r (2.5.2) 120 | nokogiri (1.10.5) 121 | mini_portile2 (~> 2.4.0) 122 | rack (2.0.7) 123 | rack-test (1.1.0) 124 | rack (>= 1.0, < 3) 125 | rails (6.0.0) 126 | actioncable (= 6.0.0) 127 | actionmailbox (= 6.0.0) 128 | actionmailer (= 6.0.0) 129 | actionpack (= 6.0.0) 130 | actiontext (= 6.0.0) 131 | actionview (= 6.0.0) 132 | activejob (= 6.0.0) 133 | activemodel (= 6.0.0) 134 | activerecord (= 6.0.0) 135 | activestorage (= 6.0.0) 136 | activesupport (= 6.0.0) 137 | bundler (>= 1.3.0) 138 | railties (= 6.0.0) 139 | sprockets-rails (>= 2.0.0) 140 | rails-dom-testing (2.0.3) 141 | activesupport (>= 4.2.0) 142 | nokogiri (>= 1.6) 143 | rails-html-sanitizer (1.3.0) 144 | loofah (~> 2.3) 145 | railties (6.0.0) 146 | actionpack (= 6.0.0) 147 | activesupport (= 6.0.0) 148 | method_source 149 | rake (>= 0.8.7) 150 | thor (>= 0.20.3, < 2.0) 151 | rake (13.0.0) 152 | request_store (1.4.1) 153 | rack (>= 1.4) 154 | rspec-core (3.9.0) 155 | rspec-support (~> 3.9.0) 156 | rspec-expectations (3.9.0) 157 | diff-lcs (>= 1.2.0, < 2.0) 158 | rspec-support (~> 3.9.0) 159 | rspec-mocks (3.9.0) 160 | diff-lcs (>= 1.2.0, < 2.0) 161 | rspec-support (~> 3.9.0) 162 | rspec-rails (3.9.0) 163 | actionpack (>= 3.0) 164 | activesupport (>= 3.0) 165 | railties (>= 3.0) 166 | rspec-core (~> 3.9.0) 167 | rspec-expectations (~> 3.9.0) 168 | rspec-mocks (~> 3.9.0) 169 | rspec-support (~> 3.9.0) 170 | rspec-support (3.9.0) 171 | sprockets (4.0.0) 172 | concurrent-ruby (~> 1.0) 173 | rack (> 1, < 3) 174 | sprockets-rails (3.2.1) 175 | actionpack (>= 4.0) 176 | activesupport (>= 4.0) 177 | sprockets (>= 3.0.0) 178 | sqlite3 (1.4.1) 179 | thor (0.20.3) 180 | thread_safe (0.3.6) 181 | tzinfo (1.2.5) 182 | thread_safe (~> 0.1) 183 | websocket-driver (0.7.1) 184 | websocket-extensions (>= 0.1.0) 185 | websocket-extensions (0.1.4) 186 | zeitwerk (2.2.0) 187 | 188 | PLATFORMS 189 | ruby 190 | 191 | DEPENDENCIES 192 | aruba (~> 0.14) 193 | cancan (~> 1.6) 194 | cucumber (~> 3.1) 195 | rspec-rails (~> 3.9) 196 | sqlite3 (~> 1.4) 197 | voltex! 198 | 199 | BUNDLED WITH 200 | 1.17.2 201 | --------------------------------------------------------------------------------