troles 0.5.0 → 0.5.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.
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