rolify 4.1.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.hakiri.yml +1 -0
  3. data/.travis.yml +40 -31
  4. data/Appraisals +54 -0
  5. data/CHANGELOG.rdoc +44 -8
  6. data/CONTRIBUTORS +11 -0
  7. data/Gemfile +5 -13
  8. data/LICENSE +1 -1
  9. data/README.md +74 -26
  10. data/Rakefile +6 -1
  11. data/gemfiles/activerecord_4.gemfile +21 -0
  12. data/gemfiles/activerecord_5.gemfile +28 -0
  13. data/gemfiles/activerecord_6.gemfile +28 -0
  14. data/gemfiles/mongoid_5.gemfile +21 -0
  15. data/gemfiles/mongoid_6.gemfile +20 -0
  16. data/gemfiles/mongoid_7.gemfile +21 -0
  17. data/lib/generators/active_record/rolify_generator.rb +45 -11
  18. data/lib/generators/active_record/templates/migration.rb +2 -3
  19. data/lib/generators/active_record/templates/model.rb +15 -0
  20. data/lib/generators/rolify/templates/initializer.rb +4 -1
  21. data/lib/rolify.rb +3 -4
  22. data/lib/rolify/adapters/active_record/resource_adapter.rb +14 -8
  23. data/lib/rolify/adapters/active_record/role_adapter.rb +26 -9
  24. data/lib/rolify/adapters/mongoid/role_adapter.rb +24 -1
  25. data/lib/rolify/configure.rb +4 -2
  26. data/lib/rolify/dynamic.rb +2 -17
  27. data/lib/rolify/matchers.rb +2 -2
  28. data/lib/rolify/role.rb +11 -2
  29. data/lib/rolify/version.rb +1 -1
  30. data/rolify.gemspec +14 -7
  31. data/spec/common_helper.rb +16 -0
  32. data/spec/generators/rolify/rolify_activerecord_generator_spec.rb +112 -6
  33. data/spec/generators_helper.rb +9 -2
  34. data/spec/rolify/config_spec.rb +2 -0
  35. data/spec/rolify/custom_spec.rb +1 -1
  36. data/spec/rolify/resource_spec.rb +6 -0
  37. data/spec/rolify/shared_examples/shared_examples_for_has_role.rb +65 -0
  38. data/spec/rolify/utils_spec.rb +19 -0
  39. data/spec/spec_helper.rb +19 -6
  40. data/spec/support/adapters/active_record.rb +4 -3
  41. data/spec/support/adapters/mongoid.rb +19 -2
  42. data/spec/support/adapters/{mongoid.yml → mongoid_5.yml} +2 -2
  43. data/spec/support/adapters/mongoid_6.yml +6 -0
  44. data/spec/support/adapters/mongoid_7.yml +6 -0
  45. data/spec/support/adapters/utils/active_record.rb +12 -0
  46. data/spec/support/adapters/utils/mongoid.rb +13 -0
  47. metadata +41 -22
  48. data/gemfiles/Gemfile.rails-3.2 +0 -27
  49. data/gemfiles/Gemfile.rails-4.0 +0 -33
  50. data/gemfiles/Gemfile.rails-4.1 +0 -37
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'bundler'
2
2
  require 'rspec/core/rake_task'
3
3
  require 'coveralls/rake/task'
4
+ require 'appraisal'
4
5
 
5
6
  Bundler::GemHelper.install_tasks
6
7
 
@@ -14,7 +15,11 @@ RSpec::Core::RakeTask.new(:rolify) do |task|
14
15
  task.pattern = 'spec/rolify/**/*_spec.rb'
15
16
  end
16
17
 
17
- task :default => [ :spec, 'coveralls:push' ]
18
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
19
+ task :default => :appraisal
20
+ else
21
+ task :default => [ :spec, 'coveralls:push' ]
22
+ end
18
23
 
19
24
  desc 'Run all specs'
20
25
  task 'spec' do
@@ -0,0 +1,21 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.6"
6
+ gem "activerecord", "~> 4.2.11", require: "active_record"
7
+ gem "bigdecimal", "1.4.2"
8
+
9
+ group :test do
10
+ gem "appraisal"
11
+ gem "coveralls", require: false
12
+ gem "its"
13
+ gem "pry", "< 0.13.0"
14
+ gem "byebug"
15
+ gem "pry-byebug", "< 3.8.0"
16
+ gem "test-unit"
17
+ gem "database_cleaner", "~> 1.6.2"
18
+ gem "codeclimate-test-reporter", require: nil
19
+ end
20
+
21
+ gemspec path: "../"
@@ -0,0 +1,28 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.6"
6
+ gem "activerecord", "~> 5.2.4", require: "active_record"
7
+ gem "actionpack", "~> 5.2.4"
8
+ gem "activemodel", "~> 5.2.4"
9
+ gem "railties", "~> 5.2.4"
10
+ gem "rspec-rails", github: "rspec/rspec-rails"
11
+ gem "rspec-core", github: "rspec/rspec-core"
12
+ gem "rspec-expectations", github: "rspec/rspec-expectations"
13
+ gem "rspec-mocks", github: "rspec/rspec-mocks"
14
+ gem "rspec-support", github: "rspec/rspec-support"
15
+
16
+ group :test do
17
+ gem "appraisal"
18
+ gem "coveralls", require: false
19
+ gem "its"
20
+ gem "pry", "< 0.13.0"
21
+ gem "byebug"
22
+ gem "pry-byebug", "< 3.8.0"
23
+ gem "test-unit"
24
+ gem "database_cleaner", "~> 1.6.2"
25
+ gem "codeclimate-test-reporter", require: nil
26
+ end
27
+
28
+ gemspec path: "../"
@@ -0,0 +1,28 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.4", platform: "ruby"
6
+ gem "activerecord", ">= 6.0.0", require: "active_record"
7
+ gem "actionpack", ">= 6.0.0"
8
+ gem "activemodel", ">= 6.0.0"
9
+ gem "railties", ">= 6.0.0"
10
+ gem "rspec-rails", github: "rspec/rspec-rails"
11
+ gem "rspec-core", github: "rspec/rspec-core"
12
+ gem "rspec-expectations", github: "rspec/rspec-expectations"
13
+ gem "rspec-mocks", github: "rspec/rspec-mocks"
14
+ gem "rspec-support", github: "rspec/rspec-support"
15
+
16
+ group :test do
17
+ gem "appraisal"
18
+ gem "coveralls", require: false
19
+ gem "its"
20
+ gem "pry", "< 0.13.0"
21
+ gem "byebug"
22
+ gem "pry-byebug", "< 3.8.0"
23
+ gem "test-unit"
24
+ gem "database_cleaner", "~> 1.6.2"
25
+ gem "codeclimate-test-reporter", require: nil
26
+ end
27
+
28
+ gemspec path: "../"
@@ -0,0 +1,21 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mongoid", "~> 5"
6
+ gem "bson_ext", "1.5.1"
7
+ gem "bigdecimal", "1.4.2"
8
+
9
+ group :test do
10
+ gem "appraisal"
11
+ gem "coveralls", require: false
12
+ gem "its"
13
+ gem "pry", "< 0.13.0"
14
+ gem "byebug"
15
+ gem "pry-byebug", "< 3.8.0"
16
+ gem "test-unit"
17
+ gem "database_cleaner", "~> 1.6.2"
18
+ gem "codeclimate-test-reporter", require: nil
19
+ end
20
+
21
+ gemspec path: "../"
@@ -0,0 +1,20 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mongoid", "~> 6"
6
+ gem "bson_ext", "1.5.1"
7
+
8
+ group :test do
9
+ gem "appraisal"
10
+ gem "coveralls", require: false
11
+ gem "its"
12
+ gem "pry", "< 0.13.0"
13
+ gem "byebug"
14
+ gem "pry-byebug", "< 3.8.0"
15
+ gem "test-unit"
16
+ gem "database_cleaner", "~> 1.6.2"
17
+ gem "codeclimate-test-reporter", require: nil
18
+ end
19
+
20
+ gemspec path: "../"
@@ -0,0 +1,21 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mongoid", "~> 7"
6
+ gem "bson_ext", "1.5.1"
7
+ gem "railties", "5.2.4.1"
8
+
9
+ group :test do
10
+ gem "appraisal"
11
+ gem "coveralls", require: false
12
+ gem "its"
13
+ gem "pry", "< 0.13.0"
14
+ gem "byebug"
15
+ gem "pry-byebug", "< 3.8.0"
16
+ gem "test-unit"
17
+ gem "database_cleaner", "~> 1.6.2"
18
+ gem "codeclimate-test-reporter", require: nil
19
+ end
20
+
21
+ gemspec path: "../"
@@ -1,5 +1,6 @@
1
1
  require 'rails/generators/active_record'
2
2
  require 'active_support/core_ext'
3
+ require 'erb'
3
4
 
4
5
  module ActiveRecord
5
6
  module Generators
@@ -8,6 +9,13 @@ module ActiveRecord
8
9
 
9
10
  argument :user_cname, :type => :string, :default => "User", :banner => "User"
10
11
 
12
+ def ensure_user_class_defined
13
+ unless user_class_defined?
14
+ prompt_missing_user
15
+ abort
16
+ end
17
+ end
18
+
11
19
  def generate_model
12
20
  invoke "active_record:model", [ name ], :migration => false
13
21
  end
@@ -22,15 +30,18 @@ module ActiveRecord
22
30
  require "#{ENGINE_ROOT}/app/models/#{user_cname.downcase}.rb"
23
31
  end
24
32
  end
33
+
25
34
  inject_into_class(model_path, class_name, model_content)
26
35
  end
27
36
 
28
37
  def copy_rolify_migration
29
- migration_template "migration.rb", "db/migrate/rolify_create_#{table_name}.rb"
38
+ migration_template "migration.rb", "db/migrate/rolify_create_#{table_name}.rb", migration_version: migration_version
30
39
  end
31
40
 
41
+ private
42
+
32
43
  def join_table
33
- user_cname.constantize.table_name + "_" + table_name
44
+ user_class.table_name + "_" + table_name
34
45
  end
35
46
 
36
47
  def user_reference
@@ -46,18 +57,41 @@ module ActiveRecord
46
57
  end
47
58
 
48
59
  def model_content
49
- content = <<RUBY
50
- has_and_belongs_to_many :%{user_cname}, :join_table => :%{join_table}
51
- belongs_to :resource, :polymorphic => true
60
+ ERB.new(File.read(File.join(__dir__, 'templates/model.rb'))).result(binding)
61
+ end
62
+
63
+ def user_class
64
+ user_cname.constantize
65
+ end
52
66
 
53
- validates :resource_type,
54
- :inclusion => { :in => Rolify.resource_types },
55
- :allow_nil => true
67
+ def user_class_defined?
68
+ user_class
69
+ true
70
+ rescue NameError => ex
71
+ if ex.missing_name == user_cname
72
+ false
73
+ else
74
+ raise ex
75
+ end
76
+ end
77
+
78
+ def prompt_missing_user
79
+ puts <<MSG
80
+ Rolify expected a model named #{user_cname} to be defined but could not find one.
81
+ Please ensure that this model exists and is not mis-spelled and re-run the generator.
82
+ MSG
83
+ end
56
84
 
57
- scopify
58
- RUBY
59
- content % { :user_cname => user_cname.constantize.table_name, :join_table => "#{user_cname.constantize.table_name}_#{table_name}"}
85
+ def versioned_migrations?
86
+ Rails::VERSION::MAJOR >= 5
60
87
  end
88
+
89
+ def migration_version
90
+ if versioned_migrations?
91
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
92
+ end
93
+ end
94
+
61
95
  end
62
96
  end
63
97
  end
@@ -1,4 +1,4 @@
1
- class RolifyCreate<%= table_name.camelize %> < ActiveRecord::Migration
1
+ class RolifyCreate<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
3
  create_table(:<%= table_name %>) do |t|
4
4
  t.string :name
@@ -11,8 +11,7 @@ class RolifyCreate<%= table_name.camelize %> < ActiveRecord::Migration
11
11
  t.references :<%= user_reference %>
12
12
  t.references :<%= role_reference %>
13
13
  end
14
-
15
- add_index(:<%= table_name %>, :name)
14
+ <% if ActiveRecord::Base.connection.class.to_s.demodulize != 'PostgreSQLAdapter' %><%= "\n " %>add_index(:<%= table_name %>, :name)<% end %>
16
15
  add_index(:<%= table_name %>, [ :name, :resource_type, :resource_id ])
17
16
  add_index(:<%= join_table %>, [ :<%= user_reference %>_id, :<%= role_reference %>_id ])
18
17
  end
@@ -0,0 +1,15 @@
1
+ has_and_belongs_to_many :<%= user_class.table_name %>, :join_table => :<%= join_table %>
2
+ <% if Rails::VERSION::MAJOR < 5 %>
3
+ belongs_to :resource,
4
+ :polymorphic => true
5
+ <% else %>
6
+ belongs_to :resource,
7
+ :polymorphic => true,
8
+ :optional => true
9
+ <% end %>
10
+
11
+ validates :resource_type,
12
+ :inclusion => { :in => Rolify.resource_types },
13
+ :allow_nil => true
14
+
15
+ scopify
@@ -4,4 +4,7 @@ Rolify.configure<%= "(\"#{class_name.camelize.to_s}\")" if class_name != "Role"
4
4
 
5
5
  # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false
6
6
  # config.use_dynamic_shortcuts
7
- end
7
+
8
+ # Configuration to remove roles from database once the last resource is removed. Default is: true
9
+ # config.remove_role_if_empty = false
10
+ end
@@ -25,12 +25,11 @@ module Rolify
25
25
 
26
26
  rolify_options = { :class_name => options[:role_cname].camelize }
27
27
  rolify_options.merge!({ :join_table => self.role_join_table_name }) if Rolify.orm == "active_record"
28
- rolify_options.merge!(options.reject{ |k,v| ![ :before_add, :after_add, :before_remove, :after_remove ].include? k.to_sym })
28
+ rolify_options.merge!(options.reject{ |k,v| ![ :before_add, :after_add, :before_remove, :after_remove, :inverse_of ].include? k.to_sym })
29
29
 
30
- has_and_belongs_to_many :roles, rolify_options
30
+ has_and_belongs_to_many :roles, **rolify_options
31
31
 
32
32
  self.adapter = Rolify::Adapter::Base.create("role_adapter", self.role_cname, self.name)
33
- load_dynamic_methods if Rolify.dynamic_shortcuts
34
33
 
35
34
  #use strict roles
36
35
  self.strict_rolify = true if options[:strict]
@@ -49,7 +48,7 @@ module Rolify
49
48
  self.role_cname = options[:role_cname]
50
49
  self.role_table_name = self.role_cname.tableize.gsub(/\//, "_")
51
50
 
52
- has_many association_name, resourcify_options
51
+ has_many association_name, **resourcify_options
53
52
 
54
53
  self.resource_adapter = Rolify::Adapter::Base.create("resource_adapter", self.role_cname, self.name)
55
54
  @@resource_types << self.name
@@ -17,16 +17,17 @@ module Rolify
17
17
  str << ', ' unless klass == klasses.last
18
18
  str
19
19
  end
20
- resources = relation.joins("INNER JOIN #{quote(roles_table)} ON #{quote(roles_table)}.resource_type IN (#{relations}) AND
21
- (#{quote(roles_table)}.resource_id IS NULL OR #{quote(roles_table)}.resource_id = #{quote(relation.table_name)}.#{relation.primary_key})")
22
- resources = resources.where("#{quote(roles_table)}.name IN (?) AND #{quote(roles_table)}.resource_type IN (?)", Array(role_name), klasses)
23
- resources = resources.select("#{quote(relation.table_name)}.*")
20
+
21
+ resources = relation.joins("INNER JOIN #{quote_table(roles_table)} ON #{quote_table(roles_table)}.resource_type IN (#{relations}) AND
22
+ (#{quote_table(roles_table)}.resource_id IS NULL OR #{quote_table(roles_table)}.resource_id = #{quote_table(relation.table_name)}.#{quote_column(relation.primary_key)})")
23
+ resources = resources.where("#{quote_table(roles_table)}.name IN (?) AND #{quote_table(roles_table)}.resource_type IN (?)", Array(role_name), klasses)
24
+ resources = resources.select("#{quote_table(relation.table_name)}.*")
24
25
  resources
25
26
  end
26
27
 
27
28
  def in(relation, user, role_names)
28
- roles = user.roles.where(:name => role_names).select("#{quote(role_class.table_name)}.#{role_class.primary_key}")
29
- relation.where("#{quote(role_class.table_name)}.#{role_class.primary_key} IN (?) AND ((resource_id = #{quote(relation.table_name)}.#{relation.primary_key}) OR (resource_id IS NULL))", roles)
29
+ roles = user.roles.where(:name => role_names).select("#{quote_table(role_class.table_name)}.#{quote_column(role_class.primary_key)}").to_a
30
+ relation.where("#{quote_table(role_class.table_name)}.#{quote_column(role_class.primary_key)} IN (?) AND ((#{quote_table(role_class.table_name)}.resource_id = #{quote_table(relation.table_name)}.#{quote_column(relation.primary_key)}) OR (#{quote_table(role_class.table_name)}.resource_id IS NULL))", roles)
30
31
  end
31
32
 
32
33
  def applied_roles(relation, children)
@@ -39,14 +40,19 @@ module Rolify
39
40
 
40
41
  def all_except(resource, excluded_obj)
41
42
  prime_key = resource.primary_key.to_sym
42
- resource.where(prime_key => (resource.all - excluded_obj).map(&prime_key))
43
+ resource.where.not(prime_key => excluded_obj.pluck(prime_key))
43
44
  end
44
45
 
45
46
  private
46
47
 
47
- def quote(column)
48
+ def quote_column(column)
48
49
  ActiveRecord::Base.connection.quote_column_name column
49
50
  end
51
+
52
+ def quote_table(table)
53
+ ActiveRecord::Base.connection.quote_table_name table
54
+ end
55
+
50
56
  end
51
57
  end
52
58
  end
@@ -9,6 +9,7 @@ module Rolify
9
9
  end
10
10
 
11
11
  def where_strict(relation, args)
12
+ return relation.where(:name => args[:name]) if args[:resource].blank?
12
13
  resource = if args[:resource].is_a?(Class)
13
14
  {class: args[:resource].to_s, id: nil}
14
15
  else
@@ -18,12 +19,34 @@ module Rolify
18
19
  relation.where(:name => args[:name], :resource_type => resource[:class], :resource_id => resource[:id])
19
20
  end
20
21
 
22
+ def find_cached(relation, args)
23
+ resource_id = (args[:resource].nil? || args[:resource].is_a?(Class) || args[:resource] == :any) ? nil : args[:resource].id
24
+ resource_type = args[:resource].is_a?(Class) ? args[:resource].to_s : args[:resource].class.name
25
+
26
+ return relation.find_all { |role| role.name == args[:name].to_s } if args[:resource] == :any
27
+
28
+ relation.find_all do |role|
29
+ (role.name == args[:name].to_s && role.resource_type == nil && role.resource_id == nil) ||
30
+ (role.name == args[:name].to_s && role.resource_type == resource_type && role.resource_id == nil) ||
31
+ (role.name == args[:name].to_s && role.resource_type == resource_type && role.resource_id == resource_id)
32
+ end
33
+ end
34
+
35
+ def find_cached_strict(relation, args)
36
+ resource_id = (args[:resource].nil? || args[:resource].is_a?(Class)) ? nil : args[:resource].id
37
+ resource_type = args[:resource].is_a?(Class) ? args[:resource].to_s : args[:resource].class.name
38
+
39
+ relation.find_all do |role|
40
+ role.resource_id == resource_id && role.resource_type == resource_type && role.name == args[:name].to_s
41
+ end
42
+ end
43
+
21
44
  def find_or_create_by(role_name, resource_type = nil, resource_id = nil)
22
45
  role_class.where(:name => role_name, :resource_type => resource_type, :resource_id => resource_id).first_or_create
23
46
  end
24
47
 
25
48
  def add(relation, role)
26
- relation.role_ids |= [role.id]
49
+ relation.roles << role unless relation.roles.include?(role)
27
50
  end
28
51
 
29
52
  def remove(relation, role_name, resource = nil)
@@ -45,19 +68,13 @@ module Rolify
45
68
  end
46
69
 
47
70
  def scope(relation, conditions)
48
- if Rails.version < "4.0"
49
- query = relation.scoped
50
- else
51
- query = relation.all
52
- end
53
- query = query.joins(:roles)
71
+ query = relation.joins(:roles)
54
72
  query = where(query, conditions)
55
73
  query
56
74
  end
57
75
 
58
76
  def all_except(user, excluded_obj)
59
- prime_key = user.primary_key.to_sym
60
- user.where(prime_key => (user.all - excluded_obj).map(&prime_key))
77
+ user.where.not(user.primary_key => excluded_obj)
61
78
  end
62
79
 
63
80
  private
@@ -9,13 +9,36 @@ module Rolify
9
9
  end
10
10
 
11
11
  def where_strict(relation, args)
12
+ return relation.where(:name => args[:name]) if args[:resource].blank?
12
13
  resource = if args[:resource].is_a?(Class)
13
14
  {class: args[:resource].to_s, id: nil}
14
15
  else
15
16
  {class: args[:resource].class.name, id: args[:resource].id}
16
17
  end
17
18
 
18
- relation.where(:name => args[:name], :resource_type => resource[:class], :resource_id => resource[:id])
19
+ relation.where(:name => args[:name], :resource_type => resource[:class], :resource_id => resource[:id])
20
+ end
21
+
22
+ def find_cached(relation, args)
23
+ resource_id = (args[:resource].nil? || args[:resource].is_a?(Class) || args[:resource] == :any) ? nil : args[:resource].id
24
+ resource_type = args[:resource].is_a?(Class) ? args[:resource].to_s : args[:resource].class.name
25
+
26
+ return relation.find_all { |role| role.name == args[:name].to_s } if args[:resource] == :any
27
+
28
+ relation.find_all do |role|
29
+ (role.name == args[:name].to_s && role.resource_type == nil && role.resource_id == nil) ||
30
+ (role.name == args[:name].to_s && role.resource_type == resource_type && role.resource_id == nil) ||
31
+ (role.name == args[:name].to_s && role.resource_type == resource_type && role.resource_id == resource_id)
32
+ end
33
+ end
34
+
35
+ def find_cached_strict(relation, args)
36
+ resource_id = (args[:resource].nil? || args[:resource].is_a?(Class)) ? nil : args[:resource].id
37
+ resource_type = args[:resource].is_a?(Class) ? args[:resource].to_s : args[:resource].class.name
38
+
39
+ relation.find_all do |role|
40
+ role.resource_id == resource_id && role.resource_type == resource_type && role.name == args[:name].to_s
41
+ end
19
42
  end
20
43
 
21
44
  def find_or_create_by(role_name, resource_type = nil, resource_id = nil)