├── .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 |
--------------------------------------------------------------------------------