copper 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4c57e369367d36662be3a0317e3ee6cabc8c238f
4
+ data.tar.gz: ba75d48b50adffbaf75a0504da75de196a135ca2
5
+ SHA512:
6
+ metadata.gz: 09ba2b239b044fbd225fa29474059127d0bd670f8325cfc6c3c458900bd5dcb86b746c22d297208d56238205bfa208fa19d5330e9bf2e0119dcf675a90e49f83
7
+ data.tar.gz: 0bbe1ffa142e1cc39fe3c9344f8877e9aedd99171bfc7f1e5fdc78e430b8d777c63cd19021acc91f6c6af7955948935bc3463fdf7ef1ec3d280484da9097943e
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in copper.gemspec
4
+ gemspec
5
+
6
+ gem 'byebug'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 John D'Agostino
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Copper
2
+
3
+ User Permissions and Policies. Named after the aussie word for police officer (https://www.youtube.com/watch?v=tKNOgX-u8ao)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'copper'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install copper
18
+
19
+ ## Permission module and Policies
20
+
21
+ Cancancan provides an Ability class to control permissions but it is limited in its ability to define
22
+ more specific controls around certain permissions. So we introduce the Permission module and Policies.
23
+
24
+ You can create your own Permission class (say an ActiveRecord model) which stores permissions (specifically
25
+ the `object_type` and `action_name`.
26
+
27
+ For example:
28
+
29
+ class Permission < ActiveRecord::Base
30
+ include Copper::Permission
31
+ end
32
+
33
+ Permission.create(
34
+ object_type: 'User',
35
+ action_name: 'manage',
36
+ description: 'Allow management of users'
37
+ )
38
+
39
+ Modify your cancancan Ability class as follows (or similar, the key is applying the permissions to the ability):
40
+
41
+ def initialize(user)
42
+ if user.is_admin?
43
+ can :manage, :all
44
+ else
45
+ user.permissions.each do |permission|
46
+ permission.apply_to(self)
47
+ end
48
+ end
49
+ end
50
+
51
+ From here everything will work as normal, BUT lets say you want to limit the managing of users to a certain group.
52
+ You could create a policy:
53
+
54
+ class UserPolicy
55
+ def initialize(ability)
56
+ @ability = ability
57
+ end
58
+
59
+ def apply!
60
+ @ability.can(:manage, User, group_id: groups.pluck(:id))
61
+ end
62
+
63
+ def groups
64
+ @ability.user.groups
65
+ end
66
+ end
67
+
68
+ This policy is called a Type Policy as it applies to any action taken on that Type. You can also define Action Policies
69
+ which will overide the type policy for the given action.
70
+
71
+ class DestroyUserPolicy
72
+ # ...destroy specific logic
73
+ end
74
+
75
+ ## Contributing
76
+
77
+ 1. Fork it ( http://github.com/jobready/copper/fork )
78
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
79
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
80
+ 4. Push to the branch (`git push origin my-new-feature`)
81
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/copper.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'copper/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "copper"
8
+ spec.version = Copper::VERSION
9
+ spec.authors = ["John D'Agostino", "Dan Draper"]
10
+ spec.email = ["john.dagostino@gmail.com", "dan@codercan.co"]
11
+ spec.summary = %q{Roles / Permissions / User switching for Rails}
12
+ spec.description = %q{Roles, Permissions, User switching}
13
+ spec.homepage = "http://github.com/jobready/copper"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "activesupport", ">= 4.0"
25
+ end
data/lib/copper.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'active_support/core_ext/string'
2
+ require "copper/version"
3
+ require "copper/permission"
4
+
5
+ module Copper
6
+ end
@@ -0,0 +1,48 @@
1
+ module Copper::Permission
2
+
3
+ class Naming
4
+ def self.action_class_name(type, action)
5
+ [action.camelize, type, 'Policy'].join
6
+ end
7
+
8
+ def self.class_name(type)
9
+ [type, 'Policy'].join
10
+ end
11
+ end
12
+
13
+ # @return true if there is a Policy class specific to the action available to complement the permission
14
+ def has_action_policy?
15
+ self.class.const_defined?(Naming.action_class_name(object_type, action_name))
16
+ end
17
+
18
+ # @return true if there is a Policy class for the type available to complement the permission
19
+ def has_type_policy?
20
+ self.class.const_defined?(Naming.class_name(object_type))
21
+ end
22
+
23
+ # The Policy class for the specific action if one is present
24
+ # @raise NameError if none present
25
+ def policy_action_class
26
+ Naming.action_class_name(object_type, action_name).constantize
27
+ end
28
+
29
+ # The Policy class for the type if one is present
30
+ # (Only used if there is no specific action class)
31
+ # @raise NameError if none present
32
+ def policy_type_class
33
+ Naming.class_name(object_type).constantize
34
+ end
35
+
36
+ # Apply this permission to the ability, directly via cancan
37
+ # or via the Policy object if one is available
38
+ #
39
+ def apply_to(ability)
40
+ if has_action_policy?
41
+ policy_action_class.new(ability).apply!
42
+ elsif has_type_policy?
43
+ policy_type_class.new(ability).apply!
44
+ else
45
+ ability.can(action_name.to_sym, object_type.constantize)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module Copper
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ module Conpper
2
+ module Generators
3
+ class CopperGenerator < Rails::Generators::NamedBase
4
+ Rails::Generators::ResourceHelpers
5
+
6
+ source_root File.expand_path('../templates', __FILE__)
7
+ argument :user_cname, :type => :string, :default => "User"
8
+
9
+ namespace :copper
10
+ hook_for :orm, :required => true
11
+
12
+ desc "Generates a model with the given NAME and a migration file."
13
+
14
+ def self.start(args, config)
15
+ user_cname = args.size > 1 ? args[1] : "User"
16
+ args.insert(1, user_cname) # 0 being the view name
17
+ super
18
+ end
19
+
20
+ def inject_user_class
21
+ invoke "copper:user", [ user_cname, class_name ], :orm => options.orm
22
+ end
23
+
24
+ def show_readme
25
+ if behavior == :invoke
26
+ readme "README"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,4 @@
1
+ class Permission < ActiveRecord::Base
2
+ include Copper::Permission
3
+ end
4
+
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe Copper::Permission do
4
+ class ManageUserPolicy; end
5
+ class DeleteTaskPolicy; end
6
+ class TaskPolicy; end
7
+
8
+ class Permission
9
+ include Copper::Permission
10
+ end
11
+
12
+ let(:permission) { Permission.new }
13
+
14
+ describe 'has_action_policy?' do
15
+
16
+ describe 'where there is only a policy for one action on the object' do
17
+ context 'for the specific action' do
18
+ before do
19
+ allow(permission).to receive(:action_name).and_return('manage')
20
+ allow(permission).to receive(:object_type).and_return('User')
21
+ end
22
+
23
+ specify 'that there is a policy available' do
24
+ expect(permission).to have_action_policy
25
+ end
26
+ end
27
+
28
+ context 'for any other action' do
29
+ before do
30
+ allow(permission).to receive(:action_name).and_return('create')
31
+ allow(permission).to receive(:object_type).and_return('User')
32
+ end
33
+
34
+ specify 'that there is NO policy available' do
35
+ expect(permission).to_not have_action_policy
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ describe 'has_type_policy?' do
42
+ describe 'when there is a fallback policy' do
43
+ context 'for any action' do
44
+ before do
45
+ allow(permission).to receive(:action_name).and_return('create')
46
+ allow(permission).to receive(:object_type).and_return('Task')
47
+ end
48
+
49
+ specify 'that there is a policy available' do
50
+ expect(permission).to have_type_policy
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#apply_to' do
57
+ let(:action_class) { double(:action_class) }
58
+ let(:type_class) { double(:type_class) }
59
+ let(:action_instance) { double(:action_instance) }
60
+ let(:type_instance) { double(:type_instance) }
61
+ let(:ability) { double(:ability) }
62
+
63
+ before do
64
+ allow(permission).to receive(:policy_action_class).and_return(action_class)
65
+ allow(permission).to receive(:policy_type_class).and_return(type_class)
66
+ end
67
+
68
+ context 'the permission has an action policy' do
69
+ before do
70
+ allow(permission).to receive(:has_action_policy?).and_return(true)
71
+ end
72
+
73
+ specify do
74
+ expect(action_class).to receive(:new).with(ability).and_return(action_instance)
75
+ expect(action_instance).to receive(:apply!)
76
+ expect(type_class).to_not receive(:new)
77
+ expect(ability).to_not receive(:can)
78
+ permission.apply_to(ability)
79
+ end
80
+ end
81
+
82
+ context 'the permission has a type policy' do
83
+ before do
84
+ allow(permission).to receive(:has_action_policy?).and_return(false)
85
+ allow(permission).to receive(:has_type_policy?).and_return(true)
86
+ end
87
+
88
+ specify do
89
+ expect(action_class).to_not receive(:new)
90
+ expect(type_class).to receive(:new).with(ability).and_return(type_instance)
91
+ expect(type_instance).to receive(:apply!)
92
+ expect(ability).to_not receive(:can)
93
+ permission.apply_to(ability)
94
+ end
95
+ end
96
+
97
+ context 'the permission has no policies defined' do
98
+ before do
99
+ allow(permission).to receive(:has_action_policy?).and_return(false)
100
+ allow(permission).to receive(:has_type_policy?).and_return(false)
101
+ allow(permission).to receive(:action_name).and_return('action')
102
+ allow(permission).to receive(:object_type).and_return('Object')
103
+ end
104
+
105
+ specify do
106
+ expect(action_class).to_not receive(:new)
107
+ expect(type_class).to_not receive(:new)
108
+ expect(ability).to receive(:can).with(:action, Object)
109
+ permission.apply_to(ability)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,81 @@
1
+ require 'byebug'
2
+ require File.dirname(__FILE__) + '/../lib/copper'
3
+
4
+ # This file was generated by the `rspec --init` command. Conventionally, all
5
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
6
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
7
+ # file to always be loaded, without a need to explicitly require it in any files.
8
+ #
9
+ # Given that it is always loaded, you are encouraged to keep this file as
10
+ # light-weight as possible. Requiring heavyweight dependencies from this file
11
+ # will add to the boot time of your test suite on EVERY test run, even for an
12
+ # individual file that may not need all of that loaded. Instead, make a
13
+ # separate helper file that requires this one and then use it only in the specs
14
+ # that actually need it.
15
+ #
16
+ # The `.rspec` file also contains a few flags that are not defaults but that
17
+ # users commonly want.
18
+ #
19
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
20
+ RSpec.configure do |config|
21
+ # The settings below are suggested to provide a good initial experience
22
+ # with RSpec, but feel free to customize to your heart's content.
23
+ =begin
24
+ # These two settings work together to allow you to limit a spec run
25
+ # to individual examples or groups you care about by tagging them with
26
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
27
+ # get run.
28
+ config.filter_run :focus
29
+ config.run_all_when_everything_filtered = true
30
+
31
+ # Many RSpec users commonly either run the entire suite or an individual
32
+ # file, and it's useful to allow more verbose output when running an
33
+ # individual spec file.
34
+ if config.files_to_run.one?
35
+ # Use the documentation formatter for detailed output,
36
+ # unless a formatter has already been configured
37
+ # (e.g. via a command-line flag).
38
+ config.default_formatter = 'doc'
39
+ end
40
+
41
+ # Print the 10 slowest examples and example groups at the
42
+ # end of the spec run, to help surface which specs are running
43
+ # particularly slow.
44
+ config.profile_examples = 10
45
+
46
+ # Run specs in random order to surface order dependencies. If you find an
47
+ # order dependency and want to debug it, you can fix the order by providing
48
+ # the seed, which is printed after each run.
49
+ # --seed 1234
50
+ config.order = :random
51
+
52
+ # Seed global randomization in this process using the `--seed` CLI option.
53
+ # Setting this allows you to use `--seed` to deterministically reproduce
54
+ # test failures related to randomization by passing the same `--seed` value
55
+ # as the one that triggered the failure.
56
+ Kernel.srand config.seed
57
+
58
+ # rspec-expectations config goes here. You can use an alternate
59
+ # assertion/expectation library such as wrong or the stdlib/minitest
60
+ # assertions if you prefer.
61
+ config.expect_with :rspec do |expectations|
62
+ # Enable only the newer, non-monkey-patching expect syntax.
63
+ # For more details, see:
64
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
65
+ expectations.syntax = :expect
66
+ end
67
+
68
+ # rspec-mocks config goes here. You can use an alternate test double
69
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
70
+ config.mock_with :rspec do |mocks|
71
+ # Enable only the newer, non-monkey-patching expect syntax.
72
+ # For more details, see:
73
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
74
+ mocks.syntax = :expect
75
+
76
+ # Prevents you from mocking or stubbing a method that does not exist on
77
+ # a real object. This is generally recommended.
78
+ mocks.verify_partial_doubles = true
79
+ end
80
+ =end
81
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: copper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - John D'Agostino
8
+ - Dan Draper
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-08-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.5'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.5'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: activesupport
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '4.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '4.0'
70
+ description: Roles, Permissions, User switching
71
+ email:
72
+ - john.dagostino@gmail.com
73
+ - dan@codercan.co
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - copper.gemspec
84
+ - lib/copper.rb
85
+ - lib/copper/permission.rb
86
+ - lib/copper/version.rb
87
+ - lib/generators/copper/copper.rb
88
+ - lib/generators/copper/templates/permission.rb
89
+ - spec/copper/permission_spec.rb
90
+ - spec/spec_helper.rb
91
+ homepage: http://github.com/jobready/copper
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.2.2
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Roles / Permissions / User switching for Rails
115
+ test_files:
116
+ - spec/copper/permission_spec.rb
117
+ - spec/spec_helper.rb