troles 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/Gemfile.lock +1 -1
  2. data/README.textile +75 -16
  3. data/VERSION +1 -1
  4. data/lib/trole/adapters/active_record/config.rb +3 -3
  5. data/lib/trole/adapters/mongoid/config.rb +5 -5
  6. data/lib/trole/config.rb +2 -2
  7. data/lib/trole_groups.rb +10 -0
  8. data/lib/trole_groups/READ THIS.textile +4 -0
  9. data/lib/trole_groups/Rolegroups design.textile +218 -0
  10. data/lib/trole_groups/adapters/active_record.rb +8 -0
  11. data/lib/trole_groups/adapters/active_record/config.rb +0 -0
  12. data/lib/trole_groups/adapters/active_record/storage.rb +0 -0
  13. data/lib/trole_groups/adapters/active_record/strategy.rb +11 -0
  14. data/lib/trole_groups/adapters/mongoid.rb +8 -0
  15. data/lib/trole_groups/adapters/mongoid/config.rb +0 -0
  16. data/lib/trole_groups/api.rb +29 -0
  17. data/lib/trole_groups/api/cache.rb +13 -0
  18. data/lib/trole_groups/api/config.rb +4 -0
  19. data/lib/trole_groups/api/core.rb +50 -0
  20. data/lib/trole_groups/api/event.rb +29 -0
  21. data/lib/trole_groups/api/read.rb +56 -0
  22. data/lib/trole_groups/api/validation.rb +44 -0
  23. data/lib/trole_groups/api/write.rb +60 -0
  24. data/lib/trole_groups/config.rb +134 -0
  25. data/lib/trole_groups/config/schema.rb +65 -0
  26. data/lib/trole_groups/config/schema/helpers.rb +11 -0
  27. data/lib/trole_groups/config/schema/role_group_helpers.rb +11 -0
  28. data/lib/trole_groups/config/valid_role_groups.rb +21 -0
  29. data/lib/trole_groups/macros.rb +42 -0
  30. data/lib/trole_groups/macros/configuration.rb +89 -0
  31. data/lib/trole_groups/macros/configuration/base_loader.rb +35 -0
  32. data/lib/trole_groups/macros/configuration/config_loader.rb +19 -0
  33. data/lib/trole_groups/macros/configuration/storage_loader.rb +20 -0
  34. data/lib/trole_groups/macros/configuration/strategy_loader.rb +38 -0
  35. data/lib/trole_groups/macros/static_roles.rb +9 -0
  36. data/lib/trole_groups/macros/strategy_options.rb +21 -0
  37. data/lib/trole_groups/operations.rb +25 -0
  38. data/lib/trole_groups/operations/read.rb +36 -0
  39. data/lib/trole_groups/operations/write.rb +42 -0
  40. data/lib/trole_groups/storage.rb +27 -0
  41. data/lib/trole_groups/storage/base_many.rb +93 -0
  42. data/lib/trole_groups/storage/embed_many.rb +58 -0
  43. data/lib/trole_groups/storage/ref_many.rb +47 -0
  44. data/lib/trole_groups/strategy.rb +30 -0
  45. data/lib/troles/adapters/active_record/config.rb +32 -9
  46. data/lib/troles/adapters/mongoid/config.rb +7 -7
  47. data/lib/troles/common/api/core.rb +1 -1
  48. data/lib/troles/common/config.rb +49 -14
  49. data/lib/troles/common/config/schema.rb +17 -23
  50. data/lib/troles/common/config/schema/helpers.rb +77 -0
  51. data/lib/troles/common/config/schema/role_helpers.rb +27 -0
  52. data/lib/troles/common/config/static_roles.rb +4 -4
  53. data/lib/troles/common/macros.rb +4 -4
  54. data/lib/troles/common/macros/configuration.rb +8 -8
  55. data/lib/troles/common/macros/strategy_options.rb +4 -4
  56. data/lib/troles/common/storage.rb +1 -0
  57. data/lib/troles/config.rb +2 -2
  58. data/lib/troles/storage/ref_many.rb +2 -3
  59. data/spec/active_record/models/ref_many.rb +3 -0
  60. data/spec/active_record/strategies/many/ref_many_spec.rb +2 -1
  61. data/spec/generic/models/role.rb +2 -1
  62. data/spec/trole_groups/api/core_api_spec.rb +14 -0
  63. data/spec/trole_groups/api/read_api_spec.rb +36 -0
  64. data/spec/trole_groups/api/write_api_spec.rb +19 -0
  65. data/spec/trole_groups/api_spec.rb +27 -0
  66. data/spec/trole_groups/generic/models.rb +3 -0
  67. data/spec/trole_groups/generic/models/role_group.rb +44 -0
  68. data/spec/trole_groups/generic/models/user.rb +9 -0
  69. data/spec/trole_groups/strategies/ref_many.rb +51 -0
  70. data/spec/trole_groups/strategy_helper.rb +9 -0
  71. data/spec/trole_groups_spec.rb +11 -0
  72. data/spec/trole_spec_helper.rb +9 -0
  73. data/spec/troles/api_spec.rb +12 -18
  74. data/troles.gemspec +52 -4
  75. metadata +85 -37
  76. data/development.sqlite3 +0 -0
  77. 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
- attr_accessor :join_model
5
-
6
- def configure_role_field
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
- clazz.send(:attr_accessor, role_field) if generic? || orm == :generic # create troles accessor
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, 'Role'].select do |class_name|
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 Role class defined, define Role one or set using #role_model method on config" if models.empty?
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 SchemaHelpers
70
- end
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 static_roles_for_class= value
4
+ def static_roles= value
5
5
  raise ArgumentError, "Must be a boolean" if !boolean?(value)
6
- @static_roles_for_class = value
6
+ @static_roles = value
7
7
  end
8
8
 
9
- def static_roles_for_class?
10
- @static_roles_for_class || false
9
+ def static_roles?
10
+ @static_roles || false
11
11
  end
12
12
  end
13
13
  end
@@ -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
- # puts "strategy module: #{configuration.strategy_module}"
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, :role_subject_class
9
+ attr_reader :strategy, :singularity, :orm, :auto_load, :options, :subject_class
10
10
 
11
- def initialize role_subject_class, strategy, options = {}
12
- @role_subject_class = role_subject_class
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
- role_subject_class.troles_config.apply_options! options
36
+ subject_class.troles_config.apply_options! options
37
37
 
38
- # StrategyOptions.new(clazz)
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
- role_subject_class.send :define_method, :storage do
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
- role_subject_class.singleton_class.class_eval %{
50
+ subject_class.singleton_class.class_eval %{
51
51
  def troles_config
52
- @troles_config ||= #{config_class}.new #{role_subject_class}, #{options.inspect}
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 :clazz
4
+ attr_reader :subject_class
5
5
 
6
- def initialize clazz
7
- @clazz = clazz
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
- clazz.send :include, "Troles::Macros::#{strategy_name.to_s.camelize}".constantize
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
@@ -1,10 +1,10 @@
1
1
  module Troles
2
2
  class Config < Troles::Common::Config
3
- def initialize clazz, options = {}
3
+ def initialize subject_class, options = {}
4
4
  super
5
5
  end
6
6
 
7
- def configure_role_field
7
+ def configure_models
8
8
  super
9
9
  end
10
10
 
@@ -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
- found_roles = find_roles(*roles)
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
@@ -6,5 +6,8 @@ end
6
6
 
7
7
  class Role < ActiveRecord::Base
8
8
  # has_and_belongs_to_many :accounts, :class_name => 'User'
9
+ end
10
+
11
+ class MyUsersRoles < ActiveRecord::Base
9
12
  end
10
13
 
@@ -12,7 +12,8 @@ end
12
12
 
13
13
  User.troles_strategy :ref_many do |c|
14
14
  c.valid_roles = [:user, :admin, :blogger, :editor]
15
- end.configure!
15
+ c.auto_config[:relations] = false
16
+ end.configure! :role_join_model => 'MyUsersRoles'
16
17
 
17
18
  module UserSetup
18
19
  def find_role name
@@ -31,7 +31,8 @@ class Role
31
31
  end
32
32
 
33
33
  def where options = {}
34
- Role.roles.select {|r| r.name == options[:name].to_sym }
34
+ names = [options[:name]].flatten
35
+ Role.roles.select {|r| names.include?(r.name.to_sym) }
35
36
  end
36
37
  end
37
38
  end
@@ -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