troles 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.textile +75 -16
- data/VERSION +1 -1
- data/lib/trole/adapters/active_record/config.rb +3 -3
- data/lib/trole/adapters/mongoid/config.rb +5 -5
- data/lib/trole/config.rb +2 -2
- data/lib/trole_groups.rb +10 -0
- data/lib/trole_groups/READ THIS.textile +4 -0
- data/lib/trole_groups/Rolegroups design.textile +218 -0
- data/lib/trole_groups/adapters/active_record.rb +8 -0
- data/lib/trole_groups/adapters/active_record/config.rb +0 -0
- data/lib/trole_groups/adapters/active_record/storage.rb +0 -0
- data/lib/trole_groups/adapters/active_record/strategy.rb +11 -0
- data/lib/trole_groups/adapters/mongoid.rb +8 -0
- data/lib/trole_groups/adapters/mongoid/config.rb +0 -0
- data/lib/trole_groups/api.rb +29 -0
- data/lib/trole_groups/api/cache.rb +13 -0
- data/lib/trole_groups/api/config.rb +4 -0
- data/lib/trole_groups/api/core.rb +50 -0
- data/lib/trole_groups/api/event.rb +29 -0
- data/lib/trole_groups/api/read.rb +56 -0
- data/lib/trole_groups/api/validation.rb +44 -0
- data/lib/trole_groups/api/write.rb +60 -0
- data/lib/trole_groups/config.rb +134 -0
- data/lib/trole_groups/config/schema.rb +65 -0
- data/lib/trole_groups/config/schema/helpers.rb +11 -0
- data/lib/trole_groups/config/schema/role_group_helpers.rb +11 -0
- data/lib/trole_groups/config/valid_role_groups.rb +21 -0
- data/lib/trole_groups/macros.rb +42 -0
- data/lib/trole_groups/macros/configuration.rb +89 -0
- data/lib/trole_groups/macros/configuration/base_loader.rb +35 -0
- data/lib/trole_groups/macros/configuration/config_loader.rb +19 -0
- data/lib/trole_groups/macros/configuration/storage_loader.rb +20 -0
- data/lib/trole_groups/macros/configuration/strategy_loader.rb +38 -0
- data/lib/trole_groups/macros/static_roles.rb +9 -0
- data/lib/trole_groups/macros/strategy_options.rb +21 -0
- data/lib/trole_groups/operations.rb +25 -0
- data/lib/trole_groups/operations/read.rb +36 -0
- data/lib/trole_groups/operations/write.rb +42 -0
- data/lib/trole_groups/storage.rb +27 -0
- data/lib/trole_groups/storage/base_many.rb +93 -0
- data/lib/trole_groups/storage/embed_many.rb +58 -0
- data/lib/trole_groups/storage/ref_many.rb +47 -0
- data/lib/trole_groups/strategy.rb +30 -0
- data/lib/troles/adapters/active_record/config.rb +32 -9
- data/lib/troles/adapters/mongoid/config.rb +7 -7
- data/lib/troles/common/api/core.rb +1 -1
- data/lib/troles/common/config.rb +49 -14
- data/lib/troles/common/config/schema.rb +17 -23
- data/lib/troles/common/config/schema/helpers.rb +77 -0
- data/lib/troles/common/config/schema/role_helpers.rb +27 -0
- data/lib/troles/common/config/static_roles.rb +4 -4
- data/lib/troles/common/macros.rb +4 -4
- data/lib/troles/common/macros/configuration.rb +8 -8
- data/lib/troles/common/macros/strategy_options.rb +4 -4
- data/lib/troles/common/storage.rb +1 -0
- data/lib/troles/config.rb +2 -2
- data/lib/troles/storage/ref_many.rb +2 -3
- data/spec/active_record/models/ref_many.rb +3 -0
- data/spec/active_record/strategies/many/ref_many_spec.rb +2 -1
- data/spec/generic/models/role.rb +2 -1
- data/spec/trole_groups/api/core_api_spec.rb +14 -0
- data/spec/trole_groups/api/read_api_spec.rb +36 -0
- data/spec/trole_groups/api/write_api_spec.rb +19 -0
- data/spec/trole_groups/api_spec.rb +27 -0
- data/spec/trole_groups/generic/models.rb +3 -0
- data/spec/trole_groups/generic/models/role_group.rb +44 -0
- data/spec/trole_groups/generic/models/user.rb +9 -0
- data/spec/trole_groups/strategies/ref_many.rb +51 -0
- data/spec/trole_groups/strategy_helper.rb +9 -0
- data/spec/trole_groups_spec.rb +11 -0
- data/spec/trole_spec_helper.rb +9 -0
- data/spec/troles/api_spec.rb +12 -18
- data/troles.gemspec +52 -4
- metadata +85 -37
- data/development.sqlite3 +0 -0
- data/lib/troles/common/config/schema_helpers.rb +0 -95
@@ -1,16 +1,17 @@
|
|
1
1
|
module Troles::Common
|
2
2
|
class Config
|
3
3
|
module Schema
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
autoload :Helpers, 'troles/common/config/schema/helpers'
|
5
|
+
autoload :RoleHelpers, 'troles/common/config/schema/role_helpers'
|
6
|
+
|
7
|
+
def configure_models
|
7
8
|
configure_generic
|
8
|
-
configure_field
|
9
|
-
configure_relation
|
9
|
+
configure_field if auto_config?(:fields)
|
10
|
+
configure_relation if auto_config?(:relations)
|
10
11
|
end
|
11
12
|
|
12
13
|
def configure_generic
|
13
|
-
|
14
|
+
subject_class.send(:attr_accessor, role_field) if generic? || orm == :generic # create troles accessor
|
14
15
|
end
|
15
16
|
|
16
17
|
# Adapter should customize this as needed
|
@@ -21,18 +22,6 @@ module Troles::Common
|
|
21
22
|
def configure_relation
|
22
23
|
end
|
23
24
|
|
24
|
-
def join_model
|
25
|
-
@join_model ||= begin
|
26
|
-
return UsersRoles if defined? UsersRoles
|
27
|
-
raise "Join model not defined"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def join_model= model_class
|
32
|
-
@join_model = model_class and return if model_class.kind_of?(Class)
|
33
|
-
raise "The role model must be a Class, was: #{model_class}"
|
34
|
-
end
|
35
|
-
|
36
25
|
# Sets the role model to use
|
37
26
|
# allows different role subject classes (fx User Accounts) to have different role schemas
|
38
27
|
# @param [Class] the model class
|
@@ -46,17 +35,21 @@ module Troles::Common
|
|
46
35
|
# @return [Class] the model class (defaults to Role)
|
47
36
|
def role_model
|
48
37
|
@role_model_found ||= begin
|
49
|
-
models = [@role_model,
|
50
|
-
try_class(class_name.to_s)
|
38
|
+
models = [@role_model, role_class_name].select do |class_name|
|
39
|
+
try_class(class_name.to_s.camelize)
|
51
40
|
end.compact
|
52
41
|
# puts "role models found: #{models}"
|
53
|
-
raise "No
|
42
|
+
raise "No #{role_class_name} class defined, define a #{role_class_name} class or set which class to use, using the :role_model option on configuration" if models.empty?
|
54
43
|
models.first.to_s.constantize
|
55
44
|
end
|
56
45
|
end
|
57
46
|
|
58
47
|
protected
|
59
48
|
|
49
|
+
def role_class_name
|
50
|
+
'Role'
|
51
|
+
end
|
52
|
+
|
60
53
|
def try_class clazz
|
61
54
|
begin
|
62
55
|
clazz = clazz.constantize if clazz.kind_of?(String)
|
@@ -66,7 +59,8 @@ module Troles::Common
|
|
66
59
|
end
|
67
60
|
end
|
68
61
|
|
69
|
-
include
|
70
|
-
|
62
|
+
include Helpers
|
63
|
+
include RoleHelpers
|
64
|
+
end
|
71
65
|
end
|
72
66
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Troles::Common
|
2
|
+
class Config
|
3
|
+
module Schema
|
4
|
+
module Helpers
|
5
|
+
def valid_field_name? name
|
6
|
+
return false if !name || name.empty?
|
7
|
+
raise ArgumentException, "Role field must not be named role or roles as these names are reserved by troles!" if [:role, :roles].include? name.to_sym
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
def boolean? value
|
12
|
+
[true, false].include? value
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: Needs extraction into helper module!
|
16
|
+
|
17
|
+
def belongs_to_for from, to, options = {}
|
18
|
+
make_relationship :belongs_to, from, to, options
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_many_for from, to, options = {}
|
22
|
+
make_relationship :has_many, from, to, options
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_one_for from, to, options = {}
|
26
|
+
make_relationship :has_one, from, to, options
|
27
|
+
end
|
28
|
+
|
29
|
+
# To setup sth like this:
|
30
|
+
#
|
31
|
+
# class UserAccount < ActiveRecord::Base
|
32
|
+
# has_and_belongs_to_many :troles, :class_name => 'Role'
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# class Role < ActiveRecord::Base
|
36
|
+
# has_and_belongs_to_many :user_accounts, :class_name => 'User'
|
37
|
+
# end
|
38
|
+
def has_and_belongs_many from, to, options = {}
|
39
|
+
make_relationship :has_and_belongs_to_many, from, to, :key => role_field
|
40
|
+
make_relationship :has_and_belongs_to_many, to, from, options
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_model_type class_name
|
44
|
+
return :user if class_name == subject_class
|
45
|
+
return :role if class_name == object_model
|
46
|
+
raise "Not a known model: #{class_name}"
|
47
|
+
end
|
48
|
+
|
49
|
+
# options:
|
50
|
+
# - :opts, extras options, fx to set the :through relationship
|
51
|
+
# - :key (usually to enforce use of role_field as key name)
|
52
|
+
def make_relationship type, from, to, options = {}
|
53
|
+
# puts "type: #{type}, #{from}, #{to}"
|
54
|
+
from_type = get_model_type from
|
55
|
+
to_type = get_model_type to
|
56
|
+
|
57
|
+
model_key = options[:key] ? options[:key] : send("#{from_type}_key")
|
58
|
+
|
59
|
+
class_name = send "#{to_type}_class_name"
|
60
|
+
|
61
|
+
options = {:class_name => class_name}
|
62
|
+
options.merge!(options[:opts]) if options[:opts]
|
63
|
+
puts "#{from}.#{type} :#{model_key}, #{options.inspect}" if log_on?
|
64
|
+
from.send(type, model_key, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def object_key
|
68
|
+
make_key object_class_name
|
69
|
+
end
|
70
|
+
|
71
|
+
def make_key name
|
72
|
+
name.to_s.gsub(/::/, '__').underscore.pluralize
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Troles::Common
|
2
|
+
class Config
|
3
|
+
module Schema
|
4
|
+
module RoleHelpers
|
5
|
+
def subject_class_name
|
6
|
+
subject_class.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def subject_key
|
10
|
+
make_key subject_class_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def join_class_name
|
14
|
+
join_model.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def join_key
|
18
|
+
make_key join_class_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def object_class_name
|
22
|
+
object_model.to_s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Troles::Common
|
2
2
|
class Config
|
3
3
|
module StaticRoles
|
4
|
-
def
|
4
|
+
def static_roles= value
|
5
5
|
raise ArgumentError, "Must be a boolean" if !boolean?(value)
|
6
|
-
@
|
6
|
+
@static_roles = value
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
@
|
9
|
+
def static_roles?
|
10
|
+
@static_roles || false
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/lib/troles/common/macros.rb
CHANGED
@@ -9,8 +9,6 @@
|
|
9
9
|
# troles_strategy :bit_many
|
10
10
|
#
|
11
11
|
|
12
|
-
puts "Troles macros enabled!"
|
13
|
-
|
14
12
|
module Troles
|
15
13
|
module Macros
|
16
14
|
autoload :Configuration, 'troles/common/macros/configuration'
|
@@ -19,12 +17,14 @@ module Troles
|
|
19
17
|
configuration = Configuration.new self, strategy, options
|
20
18
|
|
21
19
|
configuration.load_adapter
|
22
|
-
|
20
|
+
puts "strategy module: #{configuration.strategy_module}"
|
21
|
+
puts configuration.strategy_module.methods.grep /store/
|
22
|
+
|
23
23
|
send :include, configuration.strategy_module
|
24
24
|
|
25
25
|
configuration.define_hooks
|
26
26
|
configuration.apply_strategy_options!
|
27
|
-
|
27
|
+
|
28
28
|
if strategy == :bit_one
|
29
29
|
troles_config.valid_roles = [:user, :admin] # default binary roles
|
30
30
|
end
|
@@ -6,10 +6,10 @@ module Troles
|
|
6
6
|
autoload :StrategyLoader, 'troles/common/macros/configuration/strategy_loader'
|
7
7
|
autoload :StorageLoader, 'troles/common/macros/configuration/storage_loader'
|
8
8
|
|
9
|
-
attr_reader :strategy, :singularity, :orm, :auto_load, :options, :
|
9
|
+
attr_reader :strategy, :singularity, :orm, :auto_load, :options, :subject_class
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@
|
11
|
+
def initialize subject_class, strategy, options = {}
|
12
|
+
@subject_class = subject_class
|
13
13
|
@strategy = strategy
|
14
14
|
@orm = options[:orm] || Troles::Config.default_orm
|
15
15
|
@auto_load = options[:auto_load] || Troles::Config.auto_load?
|
@@ -33,23 +33,23 @@ module Troles
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def apply_strategy_options!
|
36
|
-
|
36
|
+
subject_class.troles_config.apply_options! options
|
37
37
|
|
38
|
-
# StrategyOptions.new(
|
38
|
+
# StrategyOptions.new(subject_class)
|
39
39
|
# extract_macros(options).each{|m| apply_macro m}
|
40
40
|
end
|
41
41
|
|
42
42
|
def define_hooks
|
43
43
|
storage_class = storage_loader.storage_class
|
44
|
-
|
44
|
+
subject_class.send :define_method, :storage do
|
45
45
|
@storage ||= storage_class
|
46
46
|
end
|
47
47
|
|
48
48
|
config_class = config_loader.config_class
|
49
49
|
puts "config_class: #{config_class}" if Troles::Config.log_on
|
50
|
-
|
50
|
+
subject_class.singleton_class.class_eval %{
|
51
51
|
def troles_config
|
52
|
-
@troles_config ||= #{config_class}.new #{
|
52
|
+
@troles_config ||= #{config_class}.new #{subject_class}, #{options.inspect}
|
53
53
|
end
|
54
54
|
}
|
55
55
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module Troles
|
2
2
|
module Macros
|
3
3
|
class StrategyOptions
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :subject_class
|
5
5
|
|
6
|
-
def initialize
|
7
|
-
@
|
6
|
+
def initialize subject_class
|
7
|
+
@subject_class = subject_class
|
8
8
|
end
|
9
9
|
|
10
10
|
# @param [Symbol] name of the macro to run
|
11
11
|
def apply_macro name
|
12
12
|
# overrides default method that returns false
|
13
13
|
begin
|
14
|
-
|
14
|
+
subject_class.send :include, "Troles::Macros::#{strategy_name.to_s.camelize}".constantize
|
15
15
|
rescue
|
16
16
|
end
|
17
17
|
end
|
@@ -28,6 +28,7 @@ module Troles::Common
|
|
28
28
|
# sets the value of the role field (@trole or @troles) and persists the value (in the data store)
|
29
29
|
# @param [Object] the value to set on the role field of the role subject
|
30
30
|
def set_ds_field value
|
31
|
+
return if ds_field_value == value
|
31
32
|
role_subject.send(:"#{ds_field_name}=", value)
|
32
33
|
persist_role_changes!
|
33
34
|
end
|
data/lib/troles/config.rb
CHANGED
@@ -26,9 +26,8 @@ module Troles::Storage
|
|
26
26
|
|
27
27
|
# saves the role for the user in the data store
|
28
28
|
def set_roles *roles
|
29
|
-
# finds and sets references to existing Role instances from symbols
|
30
|
-
|
31
|
-
set_ds_field found_roles
|
29
|
+
# finds and sets references to existing Role instances from symbols
|
30
|
+
set_ds_field find_roles(*roles)
|
32
31
|
end
|
33
32
|
|
34
33
|
# clears the role of the user in the data store
|
data/spec/generic/models/role.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
shared_examples_for "TroleGroup Core API" do
|
2
|
+
# Core API
|
3
|
+
specify { lambda { user.rolegroup_field }.should raise_error } # no, role_field is a class method
|
4
|
+
specify { User.rolegroup_field.should_not be_nil } # yes, role_field is a class method
|
5
|
+
|
6
|
+
subject { user }
|
7
|
+
its(:rolegroup_list) { should include(:blog_admin) }
|
8
|
+
its(:rolegroups) { should be_a TroleGroups::Operations }
|
9
|
+
|
10
|
+
# specify { user.static_role_groups?.should be_false }
|
11
|
+
# specify { User.static_role_groups?.should be_false }
|
12
|
+
|
13
|
+
# TODO: Add examples with other users?
|
14
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
shared_examples_for "TroleGroup Read API" do
|
2
|
+
|
3
|
+
specify { user.has_rolegroup?(:blog_admin).should be_true }
|
4
|
+
|
5
|
+
it "only be in role groups" do
|
6
|
+
user.only_in_rolegroup?(:blog_admin).should be_true
|
7
|
+
user.add_rolegroups :super_admin
|
8
|
+
user.only_in_rolegroup?(:blog_admin).should be_false
|
9
|
+
user.remove_rolegroups :super_admin
|
10
|
+
end
|
11
|
+
|
12
|
+
specify { user.has_rolegroups?(:blog_admin).should be_true }
|
13
|
+
|
14
|
+
it "should be in 2 role groups" do
|
15
|
+
user.add_rolegroups :super_admin
|
16
|
+
user.has_rolegroups?(:blog_admin, :super_admin).should be_true
|
17
|
+
user.has_any_rolegroup?(:super_admin).should be_true
|
18
|
+
|
19
|
+
# adding single roles and role groups together produces union of roles from all :)
|
20
|
+
user.add_roles :editor
|
21
|
+
user.role_list.should include(:editor, :blogger, :blog_editor, :admin, :blog_admin)
|
22
|
+
end
|
23
|
+
|
24
|
+
specify { user.has_any_rolegroup?(:blog_admin, :admin).should be_true }
|
25
|
+
specify { user.has_any_rolegroup?(:blip).should be_false }
|
26
|
+
|
27
|
+
subject { user }
|
28
|
+
its(:rolegroup_list) { should include(:blog_admin) }
|
29
|
+
|
30
|
+
it 'should cache the rolegroup list' do
|
31
|
+
user.rolegroup_list.should include(:blog_admin)
|
32
|
+
# calling rolegroup_list multiple times should NOT invalidate the cache :)
|
33
|
+
expect { user.rolegroup_list }.to_not change{ user.rolegroup_list_value }
|
34
|
+
user.rolegroup_list.should include(:blog_admin)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
shared_examples_for "TroleGroup Write API" do
|
2
|
+
|
3
|
+
describe '#clear_rolegroups!' do
|
4
|
+
it "should clear rolegroups and invalidate rolegroups cache" do
|
5
|
+
user.set_rolegroups :admin
|
6
|
+
user.clear_rolegroups!
|
7
|
+
expect { user.rolegroup_list }.to change{user.rolegroup_list_value }
|
8
|
+
user.rolegroup_list.should be_empty
|
9
|
+
end
|
10
|
+
|
11
|
+
it "successive clear rolegroups should not invalidate rolegroups cache" do
|
12
|
+
user.set_rolegroups :admin
|
13
|
+
user.clear_rolegroups!
|
14
|
+
expect { user.rolegroup_list }.to change{user.rolegroup_list_value }
|
15
|
+
user.clear_rolegroups!
|
16
|
+
expect { user.rolegroup_list }.to_not change{user.rolegroup_list_value }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# See http://blog.davidchelimsky.net/2010/11/07/specifying-mixins-with-shared-example-groups-in-rspec-2/
|
2
|
+
# And: http://relishapp.com/rspec/rspec-core/v/2-6/dir/example-groups/shared-examples
|
3
|
+
# Also: http://stackoverflow.com/questions/6152359/dynamically-generating-shared-examples-in-rspec-2
|
4
|
+
|
5
|
+
require_all File.dirname(__FILE__) + '/api'
|
6
|
+
|
7
|
+
def define_users
|
8
|
+
let(:no_roles_user) { create_no_roles_user }
|
9
|
+
let(:user) { create_user }
|
10
|
+
let(:admin_user) { create_admin_user }
|
11
|
+
end
|
12
|
+
|
13
|
+
shared_examples_for "TroleGroup API" do
|
14
|
+
include UserSetup
|
15
|
+
|
16
|
+
it_behaves_like "TroleGroup Core API" do
|
17
|
+
define_users
|
18
|
+
end
|
19
|
+
|
20
|
+
it_behaves_like "TroleGroup Read API" do
|
21
|
+
define_users
|
22
|
+
end
|
23
|
+
|
24
|
+
it_behaves_like "TroleGroup Write API" do
|
25
|
+
define_users
|
26
|
+
end
|
27
|
+
end
|