capable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fa1fd9dbe475e8c9d2adee0ed641c67effba968c
4
+ data.tar.gz: 0129dfec50822cbb114a5790b72c4ec98e951bdb
5
+ SHA512:
6
+ metadata.gz: c545e3aaba428c7500f5140aecb3483f2832d188560aaf418d8eedfce3519150a7d4025f679adbb9a9e72fb006cb3a58c490c979b2e3940890fdb5053f951e66
7
+ data.tar.gz: 700965da9ea69f247dc233d27d40a0e1a1e28f706e7629734e429bf2499bce3f77c7177c04bdcfbdc652c90c1ceb63645cecd3c996e13b043ccaf9991860439b
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in capable.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 TODO: Write your name
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.
@@ -0,0 +1,147 @@
1
+ # Capable
2
+
3
+ This Gem works similar to UNIX group permissions. The server defines different 'abilities' that can then be applied to *capable* objects via a *capabilities* join table. The *capabilities* table also allows expiration dates to be defined and applies the concept of *renewer*'s where another object can renew the expiration date of the *capability*.
4
+
5
+ This scheme was originally developed to provide purchasable features in an APP via In App Purchase that would renew based on subscriptions. The *User* is the *capable* object and the *Receipt* is the renewer. If a receipt renews, it has the ability to update the *capabilities* for the *User*
6
+
7
+ One good feature about this class is if a user has a certain ability from 2 different sources, if one of them expires or is deleted, the user will still have the ability from the other source. For example
8
+
9
+ - User purchases [pro, gold] abilities. User is now [pro, gold]
10
+ - User purchases [sponsor, gold] abilities. User is now [sponsor, pro, gold]
11
+ - User expires [pro, gold] abilities. User is now [sponsor, gold]
12
+
13
+ This *ability* scheme can be applied to any object in the database by using the *acts_as_capable* method.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'capable'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install capable
30
+
31
+ ## Usage
32
+
33
+ ### Setup Database Schema
34
+
35
+ Run the following code to create the *Ability* and *Capability* models and migration file.
36
+
37
+ $ rails generate capable
38
+
39
+ In order to activate the local caching features of this Gem, you will need to add the following to your migration file for each class you are going to declare as *capable*.
40
+
41
+ ```ruby
42
+ add_column :<capable class>, :ability_list, :string
43
+ ```
44
+
45
+ **If you do not add the above attribute to all of your *capable* objects, the system will query for the abilities EVERY TIME you call *ability_array* on a *capable* object.** Once complete, migrate the database by running
46
+
47
+ $ rake db:migrate
48
+
49
+ ### Configuration
50
+
51
+ To set different configuration options, create the file "config/initializers/capable.rb" with the following code (currently you can only turn on 'verbose')
52
+
53
+ ```ruby
54
+ require 'capable'
55
+
56
+ Capable.configure do |config|
57
+ config.verbose = true
58
+ end
59
+ ```
60
+
61
+ ### Ability
62
+
63
+ The *Ability* class provides a way for the admin to create different abilities that can be assigned to *capable* objects. It has the following instance methods
64
+
65
+ ```ruby
66
+ # This will assign an ability to a capable object via the capability class (the options in []'s are optional)
67
+ ability.assign_capable(capable, [active=true, expires_at=nil, renewer=nil])
68
+ ```
69
+
70
+ ### Capability
71
+
72
+ The *Capability* class is what ties the *abilities* to *capable* objects. It also has a *renewer* hook that provides a way for something to periodically renew the expiration date. Another cool feature of this class is that when updated, it will automatically re-initialize the *ability_list* of the *capable* object if it is defined. Here are it's methods
73
+
74
+ ```ruby
75
+ # Create a capability (the options in []'s are optional)
76
+ Capability.create_capability(capable, ability, [active=true, expires_at=nil, renewer=nil])
77
+
78
+ # Expires all capabilities that have an expiration date that is not nil and is older than the passed in date (sets active to false). This will automatically update the abilities of the corresponding capable object
79
+ Capability.expire_capabilities(expiration_date)
80
+
81
+ # Renew the capability
82
+ capability.renew(expires_at)
83
+ ```
84
+
85
+ ### Acts As Capable
86
+
87
+ For each class that you would like to assign *abilities* to, do the following in your class. For this example, we will use the class *User*.
88
+
89
+ ```ruby
90
+ require 'capable'
91
+
92
+ class User < ActiveRecord::Base
93
+ acts_as_capable # Rails 4
94
+ # note for Rails <= 3.x, you need to use 'acts_as_capable_3x'
95
+ end
96
+ ```
97
+
98
+ Also note from above that you will want to create a string called *ability_list* on the class to allow for local caching of abilities in the *capable* object. This is all handled automatically by the Gem when a capability is updated.
99
+
100
+ Once an object is declared as *capable* it allows the following methods to be defined
101
+
102
+ ```ruby
103
+ # See if the user has a certain ability. Note that 'ability' can either be an ability object OR the 'ability' string attribute of an ability object
104
+ user.has_ability?(ability)
105
+
106
+ # Return a comma delimited list of strings of the different abilities. This is useful for example when serializing the object over an API. The mobile client can simply work off of this array.
107
+ user.ability_array
108
+
109
+ # Assign an ability to a user (the options in []'s are optional)
110
+ user.assign_ability(ability, [active=true, expires_at=nil, renewer=nil])
111
+
112
+ # Unassign an ability from a user (just sets active to false. The options in []'s are optional)
113
+ user.unassign_ability(ability, [renewer=nil])
114
+ ```
115
+ ### Acts As Capability Renewer
116
+
117
+ A *capability renewer* is a class that can renew the expiration date of capabilities such as a subscription. To create these classes do the following
118
+
119
+ ```ruby
120
+ require 'capable'
121
+
122
+ class Renewer < ActiveRecord::Base
123
+ acts_as_capability_renewer
124
+ end
125
+ ```
126
+
127
+ Once an object is declared as a capability renewer, it has the following instance methods available to it
128
+
129
+ ```ruby
130
+ # Create capabilities for a capable object with this as the renewer. This will only create the capabilities if capable is not nil, the abilities array has values, and the current count of the capabilities is 0
131
+ renewer.create_capabilities(capable, abilities, active, expires_at)
132
+
133
+ # Renew the expiration date for the capabilities of a renewer (note that this sets active attribute based on the current date so if it is in the past, active will be set to 'false')
134
+ renewer.renew_capabilities(expires_at)
135
+ ```
136
+
137
+ The renewer concept allows a capability to be renewed when the expiration date is updated. This is good for example when a user purchases more time on a role.
138
+
139
+ ## Contributing
140
+
141
+ 1. Fork it ( https://github.com/[my-github-username]/capable/fork )
142
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
143
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
144
+ 4. Push to the branch (`git push origin my-new-feature`)
145
+ 5. Create a new Pull Request
146
+
147
+ > Written with [StackEdit](https://stackedit.io/).
@@ -0,0 +1,55 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ require 'bundler' unless defined?(Bundler)
4
+
5
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
6
+ require 'capable/version'
7
+
8
+ begin
9
+ Bundler.setup(:default, :development)
10
+ rescue Bundler::BundlerError => e
11
+ $stderr.puts e.message
12
+ $stderr.puts "Run `bundle install` to install missing gems"
13
+ exit e.status_code
14
+ end
15
+ require 'rake'
16
+
17
+ require 'rake/testtask'
18
+ Rake::TestTask.new(:test) do |test|
19
+ test.libs << 'lib' << 'test'
20
+ test.test_files = Dir.glob("test/**/*_test.rb")
21
+ test.verbose = true
22
+ end
23
+
24
+ require 'rake'
25
+ require 'rspec/core/rake_task'
26
+
27
+ RSpec::Core::RakeTask.new(:spec) do |test|
28
+ test.pattern = Dir.glob('spec/**/*_spec.rb')
29
+ test.rspec_opts = '--format documentation'
30
+ # test.rspec_opts << ' more options'
31
+ # test.rcov = true
32
+ end
33
+ task :default => :spec
34
+
35
+ task :build do
36
+ system "gem build capable.gemspec"
37
+ end
38
+
39
+ task :release => :build do
40
+ system "gem push capable-#{Capable::VERSION}.gem"
41
+ system "rm capable-#{Capable::VERSION}.gem"
42
+ end
43
+
44
+ task :test_all_databases do
45
+ # Test MySQL, Postgres and SQLite3
46
+ # ENV['DB'] = 'mysql'
47
+ # Rake::Task['spec'].execute
48
+ # ENV['DB'] = 'postgres'
49
+ # Rake::Task['spec'].execute
50
+ ENV['DB'] = 'sqlite3'
51
+ Rake::Task['spec'].execute
52
+ end
53
+
54
+ task :default => :test_all_databases
55
+
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'capable/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "capable"
8
+ spec.version = Capable::VERSION
9
+ spec.authors = ["Eric Chapman"]
10
+ spec.email = ["contact@ericjchapman.com"]
11
+ spec.summary = %q{Gem that will allow 'abilities' to be assigned to 'capable' objects that can be independantly expired and renewed.}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
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.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency 'rspec'
24
+ if RUBY_VERSION < '1.9.3'
25
+ spec.add_runtime_dependency('activerecord', '< 4.0.0')
26
+ else
27
+ spec.add_runtime_dependency('activerecord')
28
+ end
29
+ spec.add_development_dependency('mysql2')
30
+ spec.add_development_dependency('pg')
31
+ spec.add_development_dependency('sqlite3')
32
+ end
@@ -0,0 +1,28 @@
1
+ module Capable
2
+ module ActsAsAbility
3
+
4
+ def self.included(base)
5
+ base.extend Capable::Base
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def acts_as_ability
12
+ has_many :capabilities, :dependent => :destroy
13
+ has_many :capables, :through => :capabilities
14
+
15
+ include Capable::ActsAsAbility::InstanceMethods
16
+ end
17
+
18
+ end
19
+
20
+ module InstanceMethods
21
+
22
+ def assign_capable(capable, active=true, expires_at=nil, renewer=nil)
23
+ Capability.create_capability(capable, self, active, expires_at, renewer)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,61 @@
1
+ module Capable
2
+ module ActsAsCapability
3
+
4
+ def self.included(base)
5
+ base.extend Capable::Base
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def acts_as_capability
12
+ belongs_to :ability
13
+ belongs_to :capable, :polymorphic => true
14
+ belongs_to :renewer, :polymorphic => true
15
+
16
+ after_create :update_capables
17
+ after_update :update_capables
18
+
19
+ include Capable::ActsAsCapability::InstanceMethods
20
+ end
21
+
22
+ def create_capability(capable, ability, active=true, expires_at=nil, renewer=nil)
23
+ create(
24
+ capable: capable,
25
+ ability: ability,
26
+ active: active,
27
+ expires_at: expires_at,
28
+ renewer: renewer
29
+ )
30
+ end
31
+
32
+ def expire_capabilities(expiration_date)
33
+ self.where('expires_at is not null and expires_at < ? and active = ?', expiration_date, true).find_each do |capability|
34
+ puts "CAPABILITY_EXPIRE: Capable #{capability.capable_type} (#{capability.capable_id}) expired the ability #{capability.ability.name}" if Capable.configuration[:verbose]
35
+ capability.active = false
36
+ capability.save!
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ module InstanceMethods
43
+
44
+ def update_capables
45
+ if self.capable.respond_to? :ability_list=
46
+ self.capable.ability_list = self.capable.abilities.pluck(:ability).uniq.join(",")
47
+ self.capable.save!
48
+ else
49
+ puts "CAPABILITY_ERROR: Accessor method 'ability_list' is not defined for the class '#{self.capable_type}'" if Capable.configuration[:verbose]
50
+ end
51
+ end
52
+
53
+ def renew(expires_at)
54
+ self.expires_at = expires_at
55
+ self.active = (expires_at.nil? or expires_at > Time.now)
56
+ self.save
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,39 @@
1
+ module Capable
2
+ module ActsAsCapabilityRenewer
3
+
4
+ def self.included(base)
5
+ base.extend Capable::Base
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def acts_as_capability_renewer
12
+ has_many :capabilities, as: :renewer, :dependent => :destroy
13
+
14
+ include Capable::ActsAsCapabilityRenewer::InstanceMethods
15
+ end
16
+
17
+ end
18
+
19
+ module InstanceMethods
20
+
21
+ def create_capabilities(capable, abilities, active, expires_at)
22
+ if capable.present? and abilities.present? and self.capabilities.count == 0
23
+ abilities.each do |ability|
24
+ Capability.create_capability(capable, ability, active, expires_at, self)
25
+ end
26
+ end
27
+ end
28
+
29
+ def renew_capabilities(expires_at)
30
+ self.capabilities.each do |capability|
31
+ capability.renew(expires_at)
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+
@@ -0,0 +1,65 @@
1
+ module Capable
2
+ module ActsAsCapable
3
+
4
+ def self.included(base)
5
+ base.extend Capable::Base
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def acts_as_capable_3x
12
+ has_many :capabilities, as: :capable, :dependent => :destroy
13
+ has_many :abilities, :through => :capabilities, :conditions => 'capabilities.active = true'
14
+
15
+ include Capable::ActsAsCapable::InstanceMethods
16
+ end
17
+
18
+ def acts_as_capable
19
+ has_many :capabilities, as: :capable, :dependent => :destroy
20
+ has_many :abilities, -> { where(capabilities: { active: true }) }, :through => :capabilities
21
+
22
+ include Capable::ActsAsCapable::InstanceMethods
23
+ end
24
+
25
+ end
26
+
27
+ module InstanceMethods
28
+
29
+ def ability_array
30
+ if self.respond_to? :ability_list
31
+ if self.ability_list.present?
32
+ return self.ability_list.split(",")
33
+ else
34
+ return Array.new
35
+ end
36
+ else
37
+ puts "CAPABLE_WARNING: It is recommended to create the string 'ability_list' for the class '#{self.class.name}' in order to speed up ability checking" if Capable.configuration[:verbose]
38
+ return self.abilities.pluck(:ability).uniq
39
+ end
40
+ end
41
+
42
+ def has_ability?(ability)
43
+ if ability.kind_of? String
44
+ return ability_array.include? ability
45
+ else
46
+ return ability_array.include? ability.ability
47
+ end
48
+ end
49
+
50
+ def assign_ability(ability, active=true, expires_at=nil, renewer=nil)
51
+ Capability.create_capability(self, ability, active, expires_at, renewer)
52
+ end
53
+
54
+ def unassign_ability(ability, renewer=nil)
55
+ Capability.where(capable: self, ability: ability, renewer: renewer, active: true).each do |capability|
56
+ capability.active = false
57
+ capability.save # This will kickoff the update logic
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+
65
+
@@ -0,0 +1,40 @@
1
+ require "acts_as_ability"
2
+ require "acts_as_capability"
3
+ require "acts_as_capable"
4
+ require "acts_as_capability_renewer"
5
+ require "capable/version"
6
+ require 'capable/configuration'
7
+ require 'capable/base'
8
+ require 'active_record'
9
+
10
+ module Capable
11
+
12
+ class << self
13
+
14
+ # An Capable::Configuration object. Must act like a hash and return sensible
15
+ # values for all Capable::Configuration::OPTIONS. See Capable::Configuration.
16
+ attr_writer :configuration
17
+
18
+ # Call this method to modify defaults in your initializers.
19
+ #
20
+ # @example
21
+ # Capable.configure do |config|
22
+ # config.<attr> = <value>
23
+ # end
24
+ def configure
25
+ yield(configuration)
26
+ end
27
+
28
+ # The configuration object.
29
+ # @see Capable::Configuration
30
+ def configuration
31
+ @configuration ||= Configuration.new
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ ActiveRecord::Base.send(:include, Capable::ActsAsAbility)
38
+ ActiveRecord::Base.send(:include, Capable::ActsAsCapability)
39
+ ActiveRecord::Base.send(:include, Capable::ActsAsCapable)
40
+ ActiveRecord::Base.send(:include, Capable::ActsAsCapabilityRenewer)
@@ -0,0 +1,11 @@
1
+ module Capable
2
+ module Base
3
+ def quoted_true
4
+ ActiveRecord::Base.connection.quoted_true
5
+ end
6
+
7
+ def quoted_false
8
+ ActiveRecord::Base.connection.quoted_false
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ module Capable
2
+ class Configuration
3
+
4
+ OPTIONS = [:verbose].freeze
5
+
6
+ attr_accessor :verbose
7
+
8
+ def initialize
9
+ # these defaults can be overridden in the Tokengen.config block
10
+ @verbose = false
11
+ end
12
+
13
+ # Allows config options to be read like a hash
14
+ #
15
+ # @param [Symbol] option Key for a given attribute
16
+ def [](option)
17
+ send(option)
18
+ end
19
+
20
+ # Returns a hash of all configurable options
21
+ def to_hash
22
+ OPTIONS.inject({}) do |hash, option|
23
+ hash[option.to_sym] = self.send(option)
24
+ hash
25
+ end
26
+ end
27
+
28
+ # Returns a hash of all configurable options merged with +hash+
29
+ #
30
+ # @param [Hash] hash A set of configuration options that will take precedence over the defaults
31
+ def merge(hash)
32
+ to_hash.merge(hash)
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Capable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,31 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ class CapableGenerator < Rails::Generators::Base
4
+
5
+ include Rails::Generators::Migration
6
+
7
+ source_root File.expand_path('../templates', __FILE__)
8
+
9
+ # Implement the required interface for Rails::Generators::Migration.
10
+ def self.next_migration_number(dirname) #:nodoc:
11
+ next_migration_number = current_migration_number(dirname) + 1
12
+ if ActiveRecord::Base.timestamped_migrations
13
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
14
+ else
15
+ "%.3d" % next_migration_number
16
+ end
17
+ end
18
+
19
+ def create_capable_migration
20
+ migration_template 'migration.rb', File.join('db', 'migrate', 'capable_migration.rb')
21
+ end
22
+
23
+ def move_ability_model
24
+ template 'ability.rb', File.join('app', 'models', 'ability.rb')
25
+ end
26
+
27
+ def move_capability_model
28
+ template 'capability.rb', File.join('app', 'models', 'capability.rb')
29
+ end
30
+
31
+ end
@@ -0,0 +1,5 @@
1
+ class Ability < ActiveRecord::Base
2
+ acts_as_ability
3
+
4
+ # Add custom code here
5
+ end
@@ -0,0 +1,5 @@
1
+ class Capability < ActiveRecord::Base
2
+ acts_as_capability
3
+
4
+ # Add custom code here
5
+ end
@@ -0,0 +1,33 @@
1
+ class CapableMigration < ActiveRecord::Migration
2
+ def self.up
3
+
4
+ create_table :abilities, :force => true do |t|
5
+ t.string :ability, :null => false
6
+ t.string :name, :null => false
7
+ t.string :description, :null => false
8
+ t.timestamps
9
+ end
10
+
11
+ create_table :capabilities, :force => true do |t|
12
+ t.integer :ability_id, :null => false
13
+ t.references :capable, :polymorphic => true, :null => false
14
+ t.references :renewer, :polymorphic => true
15
+ t.boolean :active, :default => true
16
+ t.datetime :expires_at, :null => true
17
+ t.timestamps
18
+ end
19
+
20
+ add_index :capabilities, [:active, :expires_at]
21
+ add_index :capabilities, [:capable_id, :capable_type, :active]
22
+ add_index :capabilities, [:capable_id, :capable_type, :ability_id, :renewer_id, :renewer_type], :name => 'capabilities_all_index'
23
+
24
+ end
25
+
26
+ def self.down
27
+
28
+ drop_table :abilities
29
+ drop_table :capabilities
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe Capable do
4
+
5
+ before do
6
+ Ability.delete_all
7
+ Capability.delete_all
8
+ User.delete_all
9
+ Renewer.delete_all
10
+ end
11
+
12
+ describe 'Capability Assignment' do
13
+ let (:non_cached_user) { NonCachedUser.create(name:'Eric') }
14
+ let (:user) { User.create(name:'Eric') }
15
+ let (:admin_ability) { Ability.create(name: 'Admin Ability', description: 'Ability for admin', ability: 'admin') }
16
+ let (:pro_ability) { Ability.create(name: 'Pro Ability', description: 'Ability for pro', ability: 'pro') }
17
+ let (:renewer) { Renewer.create() }
18
+
19
+ it 'Test Acts As Capable Instance Methods' do
20
+ expect(user.has_ability?(admin_ability)).to eq false
21
+ expect(user.has_ability?(admin_ability.ability)).to eq false
22
+
23
+ # Test assigning ability
24
+ user.assign_ability(admin_ability)
25
+ user.reload
26
+
27
+ expect(user.has_ability?(admin_ability)).to eq true
28
+ expect(user.has_ability?(admin_ability.ability)).to eq true
29
+
30
+ # Test unassigning ability
31
+ user.unassign_ability(admin_ability)
32
+ user.reload
33
+
34
+ expect(user.has_ability?(admin_ability)).to eq false
35
+ expect(user.has_ability?(admin_ability.ability)).to eq false
36
+ end
37
+
38
+ it 'Test Acts As Capable Instance Methods without array_list defined' do
39
+ expect(non_cached_user.has_ability?(admin_ability)).to eq false
40
+ expect(non_cached_user.has_ability?(admin_ability.ability)).to eq false
41
+
42
+ # Test assigning ability
43
+ non_cached_user.assign_ability(admin_ability)
44
+ non_cached_user.reload
45
+
46
+ expect(non_cached_user.has_ability?(admin_ability)).to eq true
47
+ expect(non_cached_user.has_ability?(admin_ability.ability)).to eq true
48
+
49
+ # Test unassigning ability
50
+ non_cached_user.unassign_ability(admin_ability)
51
+ non_cached_user.reload
52
+
53
+ expect(non_cached_user.has_ability?(admin_ability)).to eq false
54
+ expect(non_cached_user.has_ability?(admin_ability.ability)).to eq false
55
+ end
56
+
57
+ it 'Test Acts As Capability Renewer Instance Methods' do
58
+
59
+ # Test create_capabilities works
60
+ renewer.create_capabilities(user, [pro_ability, admin_ability], true, Time.now+4)
61
+ user.reload
62
+
63
+ expect(user.has_ability?(admin_ability)).to eq true
64
+ expect(user.has_ability?(pro_ability)).to eq true
65
+
66
+ Capability.expire_capabilities(Time.now+6)
67
+ user.reload
68
+
69
+ expect(user.has_ability?(admin_ability)).to eq false
70
+ expect(user.has_ability?(pro_ability)).to eq false
71
+
72
+ # Test renewing capabilities works
73
+ renewer.renew_capabilities(Time.now+8)
74
+ user.reload
75
+
76
+ expect(user.has_ability?(admin_ability)).to eq true
77
+ expect(user.has_ability?(pro_ability)).to eq true
78
+
79
+ # Test expiration
80
+ Capability.expire_capabilities(Time.now+10)
81
+ user.reload
82
+
83
+ expect(user.has_ability?(admin_ability)).to eq false
84
+ expect(user.has_ability?(pro_ability)).to eq false
85
+
86
+ # Test renewing capabilities does not work for the past
87
+ renewer.renew_capabilities(Time.now-1)
88
+ user.reload
89
+
90
+ expect(user.has_ability?(admin_ability)).to eq false
91
+ expect(user.has_ability?(pro_ability)).to eq false
92
+ end
93
+
94
+
95
+ it 'Test Acts As Capability Expiration Logic' do
96
+ user.assign_ability(admin_ability)
97
+ user.assign_ability(pro_ability, true, Time.now)
98
+ user.reload
99
+
100
+ expect(user.has_ability?(admin_ability)).to eq true
101
+ expect(user.has_ability?(pro_ability)).to eq true
102
+
103
+ # Test expiration
104
+ Capability.expire_capabilities(Time.now+2)
105
+ user.reload
106
+
107
+ expect(user.has_ability?(admin_ability)).to eq true
108
+ expect(user.has_ability?(pro_ability)).to eq false
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,113 @@
1
+ require 'capable'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+
6
+ require 'active_record'
7
+
8
+ config = {
9
+ :database => 'device_token_test',
10
+ :username => 'test'
11
+ }
12
+
13
+ case ENV['DB']
14
+ when 'mysql'
15
+ config = {
16
+ :adapter => 'mysql2',
17
+ :database => 'device_token_test',
18
+ :username => 'test',
19
+ :password => 'test',
20
+ :socket => '/tmp/mysql.sock'
21
+ }
22
+ if ENV['TRAVIS']
23
+ config = {
24
+ :adapter => 'mysql2',
25
+ :database => 'device_token_test',
26
+ :username => 'root'
27
+ }
28
+ end
29
+ ActiveRecord::Base.establish_connection(config)
30
+ ActiveRecord::Base.connection.drop_database(config[:database]) rescue nil
31
+ ActiveRecord::Base.connection.create_database(config[:database])
32
+ when 'postgres'
33
+ config = {
34
+ :adapter => 'postgresql',
35
+ :database => 'device_token_test',
36
+ :username => 'test',
37
+ }
38
+ if ENV['TRAVIS']
39
+ config = {
40
+ :adapter => 'postgresql',
41
+ :database => 'device_token_test',
42
+ :username => 'postgres',
43
+ }
44
+ end
45
+ ActiveRecord::Base.establish_connection(config.merge({ :database => 'postgres' }))
46
+ ActiveRecord::Base.connection.drop_database(config[:database])
47
+ ActiveRecord::Base.connection.create_database(config[:database])
48
+ when 'sqlite3'
49
+ config = {
50
+ :adapter => 'sqlite3',
51
+ :database => 'test.sqlite3',
52
+ :username => 'test',
53
+ }
54
+ end
55
+
56
+ ActiveRecord::Base.establish_connection(config)
57
+
58
+ ActiveRecord::Migration.verbose = false
59
+
60
+ ActiveRecord::Schema.define do
61
+
62
+ create_table :abilities, :force => true do |t|
63
+ t.string :ability, :null => false
64
+ t.string :name, :null => false
65
+ t.string :description, :null => false
66
+ t.timestamps
67
+ end
68
+
69
+ create_table :capabilities, :force => true do |t|
70
+ t.integer :ability_id, :null => false
71
+ t.references :capable, :polymorphic => true, :null => false
72
+ t.references :renewer, :polymorphic => true
73
+ t.boolean :active, :default => true
74
+ t.datetime :expires_at, :null => true
75
+ t.timestamps
76
+ end
77
+
78
+ create_table :users, :force => true do |t|
79
+ t.string :name
80
+ t.string :ability_list
81
+ t.timestamps
82
+ end
83
+
84
+ create_table :non_cached_users, :force => true do |t|
85
+ t.string :name
86
+ t.timestamps
87
+ end
88
+
89
+ create_table :renewers, :force => true do |t|
90
+ t.timestamps
91
+ end
92
+
93
+ end
94
+
95
+ class Ability < ActiveRecord::Base
96
+ acts_as_ability
97
+ end
98
+
99
+ class Capability < ActiveRecord::Base
100
+ acts_as_capability
101
+ end
102
+
103
+ class User < ActiveRecord::Base
104
+ acts_as_capable
105
+ end
106
+
107
+ class NonCachedUser < ActiveRecord::Base
108
+ acts_as_capable
109
+ end
110
+
111
+ class Renewer < ActiveRecord::Base
112
+ acts_as_capability_renewer
113
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Eric Chapman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mysql2
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pg
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: ''
112
+ email:
113
+ - contact@ericjchapman.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .gitignore
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - capable.gemspec
124
+ - lib/acts_as_ability.rb
125
+ - lib/acts_as_capability.rb
126
+ - lib/acts_as_capability_renewer.rb
127
+ - lib/acts_as_capable.rb
128
+ - lib/capable.rb
129
+ - lib/capable/base.rb
130
+ - lib/capable/configuration.rb
131
+ - lib/capable/version.rb
132
+ - lib/generators/capable/capable_generator.rb
133
+ - lib/generators/capable/templates/ability.rb
134
+ - lib/generators/capable/templates/capability.rb
135
+ - lib/generators/capable/templates/migration.rb
136
+ - spec/capable_spec.rb
137
+ - spec/spec_helper.rb
138
+ homepage: ''
139
+ licenses:
140
+ - MIT
141
+ metadata: {}
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - '>='
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 2.0.14
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: Gem that will allow 'abilities' to be assigned to 'capable' objects that
162
+ can be independantly expired and renewed.
163
+ test_files:
164
+ - spec/capable_spec.rb
165
+ - spec/spec_helper.rb