diff --git a/lib/generators/active_record/rolify_generator.rb b/lib/generators/active_record/rolify_generator.rb index 8dfd6ea3..3da21c74 100644 --- a/lib/generators/active_record/rolify_generator.rb +++ b/lib/generators/active_record/rolify_generator.rb @@ -5,46 +5,42 @@ module ActiveRecord module Generators class RolifyGenerator < ActiveRecord::Generators::Base source_root File.expand_path("../templates", __FILE__) - + argument :user_cname, :type => :string, :default => "User", :banner => "User" - + def generate_model invoke "active_record:model", [ name ], :migration => false end - + def inject_role_class inject_into_class(model_path, class_name, model_content) end - + def copy_rolify_migration migration_template "migration.rb", "db/migrate/rolify_create_#{table_name}" end - - def join_table - user_cname.constantize.table_name + "_" + table_name - end - + def user_reference user_cname.demodulize.underscore end - + def role_reference class_name.demodulize.underscore end - + def model_path File.join("app", "models", "#{file_path}.rb") end - + def model_content content = < :%{join_table} + belongs_to :%{user_cname} belongs_to :resource, :polymorphic => true - + scopify RUBY - content % { :user_cname => user_cname.constantize.table_name, :join_table => "#{user_cname.constantize.table_name}_#{table_name}"} + content % { :user_cname => user_cname.demodulize.underscore } end end end -end \ No newline at end of file +end diff --git a/lib/generators/active_record/templates/migration.rb b/lib/generators/active_record/templates/migration.rb index 1f4eff98..612f5d6e 100644 --- a/lib/generators/active_record/templates/migration.rb +++ b/lib/generators/active_record/templates/migration.rb @@ -2,18 +2,13 @@ class RolifyCreate<%= table_name.camelize %> < ActiveRecord::Migration def change create_table(:<%= table_name %>) do |t| t.string :name + t.belongs_to :user t.references :resource, :polymorphic => true t.timestamps end - create_table(:<%= join_table %>, :id => false) do |t| - t.references :<%= user_reference %> - t.references :<%= role_reference %> - end - add_index(:<%= table_name %>, :name) add_index(:<%= table_name %>, [ :name, :resource_type, :resource_id ]) - add_index(:<%= join_table %>, [ :<%= user_reference %>_id, :<%= role_reference %>_id ]) end end diff --git a/lib/generators/rolify/rolify_generator.rb b/lib/generators/rolify/rolify_generator.rb index c24dfd03..7303a9f8 100644 --- a/lib/generators/rolify/rolify_generator.rb +++ b/lib/generators/rolify/rolify_generator.rb @@ -2,7 +2,7 @@ module Rolify module Generators class RolifyGenerator < Rails::Generators::NamedBase Rails::Generators::ResourceHelpers - + source_root File.expand_path('../templates', __FILE__) argument :user_cname, :type => :string, :default => "User" @@ -16,15 +16,15 @@ def self.start(args, config) args.insert(1, user_cname) # 0 being the view name super end - + def inject_user_class invoke "rolify:user", [ user_cname, class_name ], :orm => options.orm end - + def copy_initializer_file template "initializer.rb", "config/initializers/rolify.rb" end - + def show_readme if behavior == :invoke readme "README" diff --git a/lib/generators/rolify/templates/initializer.rb b/lib/generators/rolify/templates/initializer.rb index e873de71..6d8dcc1d 100644 --- a/lib/generators/rolify/templates/initializer.rb +++ b/lib/generators/rolify/templates/initializer.rb @@ -1,8 +1,8 @@ Rolify.configure<%= "(\"#{class_name.camelize.to_s}\")" if class_name != "Role" %> do |config| # By default ORM adapter is ActiveRecord. uncomment to use mongoid <%= "# " if options.orm == :active_record || !options.orm %>config.use_mongoid - + # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false # Enable this feature _after_ running rake db:migrate as it relies on the roles table # config.use_dynamic_shortcuts -end \ No newline at end of file +end diff --git a/lib/generators/rolify/templates/role-active_record.rb b/lib/generators/rolify/templates/role-active_record.rb index 02ee848d..447c35c4 100644 --- a/lib/generators/rolify/templates/role-active_record.rb +++ b/lib/generators/rolify/templates/role-active_record.rb @@ -4,8 +4,8 @@ def self.table_name_prefix <%= table_prefix(role_cname) %>_ end <% end %> - has_and_belongs_to_many :<%= user_cname.tableize %>, :join_table => :<%= "#{table_name(user_cname, true)}_#{table_name(role_cname, true)}" %> + belongs_to <%= user_cname.singularize %> belongs_to :resource, :polymorphic => true - + scopify end diff --git a/lib/generators/rolify/user_generator.rb b/lib/generators/rolify/user_generator.rb index 508df566..902f3d41 100644 --- a/lib/generators/rolify/user_generator.rb +++ b/lib/generators/rolify/user_generator.rb @@ -6,7 +6,7 @@ module Generators class UserGenerator < Rails::Generators::NamedBase argument :role_cname, :type => :string, :default => "Role" class_option :orm, :type => :string, :default => "active_record" - + desc "Inject rolify method in the User class." def inject_user_content @@ -14,7 +14,7 @@ def inject_user_content " rolify#{role_association}\n" end end - + def inject_rolify_method if options.orm == :active_record /class #{class_name.camelize}\n|class #{class_name.camelize} .*\n|class #{class_name.demodulize.camelize}\n|class #{class_name.demodulize.camelize} .*\n/ @@ -22,11 +22,11 @@ def inject_rolify_method /include Mongoid::Document\n|include Mongoid::Document .*\n/ end end - + def model_path File.join("app", "models", "#{file_path}.rb") end - + def role_association if role_cname != "Role" " :role_cname => '#{role_cname.camelize}'" diff --git a/lib/rolify.rb b/lib/rolify.rb index 4c1ded20..c946e400 100644 --- a/lib/rolify.rb +++ b/lib/rolify.rb @@ -9,39 +9,36 @@ module Rolify extend Configure - attr_accessor :role_cname, :adapter, :role_join_table_name, :role_table_name + attr_accessor :role_cname, :adapter, :role_table_name def rolify(options = {}) include Role extend Dynamic if Rolify.dynamic_shortcuts - options.reverse_merge!({:role_cname => 'Role'}) + options.reverse_merge!({ :role_cname => 'Role', :dependent => :destroy }) self.role_cname = options[:role_cname] self.role_table_name = self.role_cname.tableize.gsub(/\//, "_") - default_join_table = "#{self.to_s.tableize.gsub(/\//, "_")}_#{self.role_table_name}" - options.reverse_merge!({:role_join_table_name => default_join_table}) - self.role_join_table_name = options[:role_join_table_name] - - rolify_options = { :class_name => options[:role_cname].camelize } - rolify_options.merge!({ :join_table => self.role_join_table_name }) if Rolify.orm == "active_record" + rolify_options = { :class_name => options[:role_cname].camelize, :dependent => options[:dependent] } rolify_options.merge!(options.reject{ |k,v| ![ :before_add, :after_add, :before_remove, :after_remove ].include? k.to_sym }) - has_and_belongs_to_many :roles, rolify_options + has_many :roles, rolify_options + has_many :groups, :through => :roles, :source => :resource, :source_type => 'Group' self.adapter = Rolify::Adapter::Base.create("role_adapter", self.role_cname, self.name) load_dynamic_methods if Rolify.dynamic_shortcuts end - def resourcify(association_name = :roles, options = {}) + def resourcify(role_association_name = :roles, target_association_name = :users, options = {}) include Resource - options.reverse_merge!({ :role_cname => 'Role', :dependent => :destroy }) - resourcify_options = { :class_name => options[:role_cname].camelize, :as => :resource, :dependent => options[:dependent] } + options.reverse_merge!({ :role_cname => 'Role', :dependent => :destroy, :as => :resource }) + resourcify_options = { :class_name => options[:role_cname].camelize, :dependent => options[:dependent], :as => options[:as] } self.role_cname = options[:role_cname] self.role_table_name = self.role_cname.tableize.gsub(/\//, "_") - has_many association_name, resourcify_options + has_many role_association_name, resourcify_options + has_many target_association_name, :through => role_association_name, :as => :resource self.adapter = Rolify::Adapter::Base.create("resource_adapter", self.role_cname, self.name) end diff --git a/lib/rolify/adapters/active_record/scopes.rb b/lib/rolify/adapters/active_record/scopes.rb index 4ec81e57..c026e987 100644 --- a/lib/rolify/adapters/active_record/scopes.rb +++ b/lib/rolify/adapters/active_record/scopes.rb @@ -4,13 +4,13 @@ module Scopes def global where(:resource_type => nil, :resource_id => nil) end - + def class_scoped(resource_type = nil) where_conditions = "resource_type IS NOT NULL AND resource_id IS NULL" where_conditions = [ "resource_type = ? AND resource_id IS NULL", resource_type.name ] if resource_type where(where_conditions) end - + def instance_scoped(resource_type = nil) where_conditions = "resource_type IS NOT NULL AND resource_id IS NOT NULL" if resource_type @@ -24,4 +24,4 @@ def instance_scoped(resource_type = nil) end end end -end \ No newline at end of file +end diff --git a/lib/rolify/configure.rb b/lib/rolify/configure.rb index 28fbc7a7..a1cc4679 100644 --- a/lib/rolify/configure.rb +++ b/lib/rolify/configure.rb @@ -2,7 +2,7 @@ module Rolify module Configure @@dynamic_shortcuts = false @@orm = "active_record" - + def configure(*role_cnames) return if !sanity_check(role_cnames) yield self if block_given? @@ -27,7 +27,7 @@ def orm=(orm) def use_mongoid self.orm = "mongoid" end - + def use_dynamic_shortcuts self.dynamic_shortcuts = true end @@ -38,9 +38,9 @@ def use_defaults config.orm = "active_record" end end - + private - + def sanity_check(role_cnames) role_cnames = [ "Role" ] if role_cnames.empty? role_cnames.each do |role_cname| @@ -52,7 +52,7 @@ def sanity_check(role_cnames) end true end - + def role_table_missing?(role_class) role_class.connected? && !role_class.table_exists? end diff --git a/lib/rolify/dynamic.rb b/lib/rolify/dynamic.rb index aab0081b..6f8da200 100644 --- a/lib/rolify/dynamic.rb +++ b/lib/rolify/dynamic.rb @@ -7,7 +7,7 @@ def load_dynamic_methods end def define_dynamic_method(role_name, resource) - class_eval do + class_eval do define_method("is_#{role_name}?".to_sym) do has_role?("#{role_name}") end if !method_defined?("is_#{role_name}?".to_sym) @@ -18,4 +18,4 @@ def define_dynamic_method(role_name, resource) end end end -end \ No newline at end of file +end diff --git a/lib/rolify/finders.rb b/lib/rolify/finders.rb index 446a4387..4756693b 100644 --- a/lib/rolify/finders.rb +++ b/lib/rolify/finders.rb @@ -22,9 +22,9 @@ def with_any_role(*args) users.uniq end end - + private - + def parse_args(args, users, &block) args.each do |arg| if arg.is_a? Hash @@ -37,4 +37,4 @@ def parse_args(args, users, &block) block.call(users_to_add) end end -end \ No newline at end of file +end diff --git a/lib/rolify/railtie.rb b/lib/rolify/railtie.rb index 9b0b31bc..cd582798 100644 --- a/lib/rolify/railtie.rb +++ b/lib/rolify/railtie.rb @@ -7,7 +7,7 @@ class Railtie < Rails::Railtie ActiveSupport.on_load(:active_record) do ActiveRecord::Base.send :extend, Rolify end - + config.before_initialize do ::Mongoid::Document.module_eval do def self.included(base) @@ -17,4 +17,4 @@ def self.included(base) end if defined?(Mongoid) end end -end \ No newline at end of file +end diff --git a/lib/rolify/resource.rb b/lib/rolify/resource.rb index fc067928..c17fbc66 100644 --- a/lib/rolify/resource.rb +++ b/lib/rolify/resource.rb @@ -4,7 +4,7 @@ def self.included(base) base.extend ClassMethods end - module ClassMethods + module ClassMethods def find_roles(role_name = nil, user = nil) roles = user && (user != :any) ? user.roles : self.role_class roles = roles.where(:resource_type => self.to_s) @@ -23,9 +23,9 @@ def with_role(role_name, user = nil) end alias :with_roles :with_role end - + def applied_roles self.roles + self.class.role_class.where(:resource_type => self.class.to_s, :resource_id => nil) end end -end \ No newline at end of file +end diff --git a/lib/rolify/role.rb b/lib/rolify/role.rb index 795484f5..be522b5b 100644 --- a/lib/rolify/role.rb +++ b/lib/rolify/role.rb @@ -3,14 +3,14 @@ module Rolify module Role extend Utils - + def self.included(base) base.extend Finders end - + def add_role(role_name, resource = nil) - role = self.class.adapter.find_or_create_by(role_name.to_s, - (resource.is_a?(Class) ? resource.to_s : resource.class.name if resource), + role = self.class.adapter.find_or_create_by(role_name.to_s, + (resource.is_a?(Class) ? resource.to_s : resource.class.name if resource), (resource.id if resource && !resource.is_a?(Class))) if !roles.include?(role) @@ -50,7 +50,7 @@ def has_any_role?(*args) self.class.adapter.where(self.roles, *args).size > 0 end end - + def only_has_role?(role_name, resource = nil) return self.has_role?(role_name,resource) && self.roles.count == 1 end @@ -58,7 +58,7 @@ def only_has_role?(role_name, resource = nil) def remove_role(role_name, resource = nil) self.class.adapter.remove(self, role_name.to_s, resource) end - + alias_method :revoke, :remove_role deprecate :has_no_role, :remove_role diff --git a/spec/generators/rolify/rolify_activerecord_generator_spec.rb b/spec/generators/rolify/rolify_activerecord_generator_spec.rb index bfb1f663..2a473fce 100644 --- a/spec/generators/rolify/rolify_activerecord_generator_spec.rb +++ b/spec/generators/rolify/rolify_activerecord_generator_spec.rb @@ -7,11 +7,11 @@ # Tell the generator where to put its output (what it thinks of as Rails.root) destination File.expand_path("../../../../tmp", __FILE__) teardown :cleanup_destination_root - - before { + + before { prepare_destination } - + def cleanup_destination_root FileUtils.rm_rf destination_root end @@ -19,7 +19,7 @@ def cleanup_destination_root describe 'specifying only Role class name' do before(:all) { arguments %w(Role) } - before { + before { capture(:stdout) { generator.create_file "app/models/user.rb" do <<-RUBY @@ -29,9 +29,9 @@ class User < ActiveRecord::Base end } require File.join(destination_root, "app/models/user.rb") - run_generator + run_generator } - + describe 'config/initializers/rolify.rb' do subject { file('config/initializers/rolify.rb') } it { should exist } @@ -39,33 +39,32 @@ class User < ActiveRecord::Base it { should contain "# config.use_dynamic_shortcuts" } it { should contain "# config.use_mongoid" } end - + describe 'app/models/role.rb' do subject { file('app/models/role.rb') } it { should exist } it { should contain "class Role < ActiveRecord::Base" } - it { should contain "has_and_belongs_to_many :users, :join_table => :users_roles" } + it { should contain "belongs_to :user" } it { should contain "belongs_to :resource, :polymorphic => true" } end - + describe 'app/models/user.rb' do subject { file('app/models/user.rb') } it { should contain /class User < ActiveRecord::Base\n rolify\n/ } end - + describe 'migration file' do subject { migration_file('db/migrate/rolify_create_roles.rb') } - + it { should be_a_migration } it { should contain "create_table(:roles) do" } - it { should contain "create_table(:users_roles, :id => false) do" } end end describe 'specifying User and Role class names' do before(:all) { arguments %w(AdminRole AdminUser) } - - before { + + before { capture(:stdout) { generator.create_file "app/models/admin_user.rb" do "class AdminUser < ActiveRecord::Base\nend" @@ -74,44 +73,43 @@ class User < ActiveRecord::Base require File.join(destination_root, "app/models/admin_user.rb") run_generator } - + describe 'config/initializers/rolify.rb' do subject { file('config/initializers/rolify.rb') } - + it { should exist } it { should contain "Rolify.configure(\"AdminRole\") do |config|"} it { should contain "# config.use_dynamic_shortcuts" } it { should contain "# config.use_mongoid" } end - + describe 'app/models/admin_role.rb' do subject { file('app/models/admin_role.rb') } - + it { should exist } it { should contain "class AdminRole < ActiveRecord::Base" } - it { should contain "has_and_belongs_to_many :admin_users, :join_table => :admin_users_admin_roles" } + it { should contain "belongs_to :admin_user" } it { should contain "belongs_to :resource, :polymorphic => true" } end - + describe 'app/models/admin_user.rb' do subject { file('app/models/admin_user.rb') } - + it { should contain /class AdminUser < ActiveRecord::Base\n rolify :role_cname => 'AdminRole'\n/ } end - + describe 'migration file' do subject { migration_file('db/migrate/rolify_create_admin_roles.rb') } - + it { should be_a_migration } it { should contain "create_table(:admin_roles)" } - it { should contain "create_table(:admin_users_admin_roles, :id => false) do" } end end - + describe 'specifying namespaced User and Role class names' do before(:all) { arguments %w(Admin::Role Admin::User) } - - before { + + before { capture(:stdout) { generator.create_file "app/models/admin/user.rb" do <<-RUBY @@ -126,37 +124,36 @@ class User < ActiveRecord::Base require File.join(destination_root, "app/models/admin/user.rb") run_generator } - + describe 'config/initializers/rolify.rb' do subject { file('config/initializers/rolify.rb') } - + it { should exist } it { should contain "Rolify.configure(\"Admin::Role\") do |config|"} it { should contain "# config.use_dynamic_shortcuts" } it { should contain "# config.use_mongoid" } end - + describe 'app/models/admin/role.rb' do subject { file('app/models/admin/role.rb') } - + it { should exist } it { should contain "class Admin::Role < ActiveRecord::Base" } - it { should contain "has_and_belongs_to_many :admin_users, :join_table => :admin_users_admin_roles" } + it { should contain "belongs_to :user" } it { should contain "belongs_to :resource, :polymorphic => true" } end - + describe 'app/models/admin/user.rb' do subject { file('app/models/admin/user.rb') } - + it { should contain /class User < ActiveRecord::Base\n rolify :role_cname => 'Admin::Role'\n/ } end - + describe 'migration file' do subject { migration_file('db/migrate/rolify_create_admin_roles.rb') } - + it { should be_a_migration } it { should contain "create_table(:admin_roles)" } - it { should contain "create_table(:admin_users_admin_roles, :id => false) do" } end end end diff --git a/spec/rolify/config_spec.rb b/spec/rolify/config_spec.rb index ea088cde..dbf43d99 100644 --- a/spec/rolify/config_spec.rb +++ b/spec/rolify/config_spec.rb @@ -16,7 +16,7 @@ class MUser Rolify.use_defaults end - describe :dynamic_shortcuts do + describe :dynamic_shortcuts do context "using defaults values" do subject { Rolify.dynamic_shortcuts } @@ -34,29 +34,29 @@ class MUser end end - describe :orm do + describe :orm do context "using defaults values", :if => ENV['ADAPTER'] == 'active_record' do subject { Rolify.orm } it { should eq("active_record") } - + context "on the User class" do before do subject.rolify end - + subject { ARUser } - + its("adapter.class") { should be(Rolify::Adapter::RoleAdapter) } end - + context "on the Forum class" do before do subject.resourcify end - + subject { Forum } - + its("adapter.class") { should be(Rolify::Adapter::ResourceAdapter) } end end @@ -70,14 +70,14 @@ class MUser subject { Rolify.orm } it { should eq("mongoid") } - + context "on the User class" do before do MUser.rolify end subject { MUser } - + its("adapter.class") { should be(Rolify::Adapter::RoleAdapter) } end @@ -91,7 +91,7 @@ class MUser its("adapter.class") { should be(Rolify::Adapter::ResourceAdapter) } end end - + context "using :use_mongoid method" do before do Rolify.use_mongoid @@ -100,14 +100,14 @@ class MUser subject { Rolify.orm } it { should eq("mongoid") } - + context "on the User class" do before do MUser.rolify end subject { MUser } - + its("adapter.class") { should be(Rolify::Adapter::RoleAdapter) } end @@ -122,14 +122,14 @@ class MUser end end end - + describe :dynamic_shortcuts do context "using defaults values" do subject { Rolify.dynamic_shortcuts } it { should be_false } end - + context "using custom values" do context "using :dynamic_shortcuts setter method" do before do @@ -153,7 +153,7 @@ class MUser end end end - + describe :configure do before do Rolify.configure do |config| @@ -161,17 +161,17 @@ class MUser config.orm = "mongoid" end end - + its(:dynamic_shortcuts) { should be_true } its(:orm) { should eq("mongoid") } - + context "on the User class", :if => ENV['ADAPTER'] == 'mongoid' do before do MUser.rolify end subject { MUser } - + it { should satisfy { |u| u.include? Rolify::Role }} it { should satisfy { |u| u.singleton_class.include? Rolify::Dynamic } } its("adapter.class") { should be(Rolify::Adapter::RoleAdapter) } @@ -183,9 +183,9 @@ class MUser end subject { Forum } - + it { should satisfy { |u| u.include? Rolify::Resource }} its("adapter.class") { should be(Rolify::Adapter::ResourceAdapter) } end end -end \ No newline at end of file +end diff --git a/spec/rolify/role_spec.rb b/spec/rolify/role_spec.rb index f03e672e..e8bf1a2b 100644 --- a/spec/rolify/role_spec.rb +++ b/spec/rolify/role_spec.rb @@ -12,7 +12,7 @@ def user_class def role_class Role end - + it_behaves_like Rolify::Role it_behaves_like "Role.scopes" it_behaves_like Rolify::Dynamic diff --git a/spec/support/adapters/active_record.rb b/spec/support/adapters/active_record.rb index 7c60db71..8ac2687e 100644 --- a/spec/support/adapters/active_record.rb +++ b/spec/support/adapters/active_record.rb @@ -12,7 +12,7 @@ class User < ActiveRecord::Base end class Role < ActiveRecord::Base - has_and_belongs_to_many :users, :join_table => :users_roles + belongs_to :user belongs_to :resource, :polymorphic => true extend Rolify::Adapter::Scopes @@ -30,7 +30,7 @@ class Customer < ActiveRecord::Base end class Privilege < ActiveRecord::Base - has_and_belongs_to_many :customers, :join_table => :customers_privileges + belongs_to :customer belongs_to :resource, :polymorphic => true extend Rolify::Adapter::Scopes @@ -41,17 +41,17 @@ module Admin def self.table_name_prefix 'admin_' end - + class Moderator < ActiveRecord::Base - rolify :role_cname => "Admin::Right", :role_join_table_name => "moderators_rights" + rolify :role_cname => "Admin::Right" end class Right < ActiveRecord::Base - has_and_belongs_to_many :moderators, :class_name => "Admin::Moderator", :join_table => "moderators_rights" + belongs_to :moderator belongs_to :resource, :polymorphic => true extend Rolify::Adapter::Scopes - end + end end @@ -71,6 +71,6 @@ def subgroups class Team < ActiveRecord::Base #resourcify done during specs setup to be able to use custom user classes self.primary_key = "team_code" - + default_scope { order(:team_code) } -end \ No newline at end of file +end diff --git a/spec/support/schema.rb b/spec/support/schema.rb index 6c130e02..8a517bcb 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -4,8 +4,9 @@ [ :roles, :privileges, :admin_rights ].each do |table| create_table(table) do |t| t.string :name + t.belongs_to :user t.references :resource, :polymorphic => true - + t.timestamps end end @@ -16,26 +17,6 @@ end end - create_table(:users_roles, :id => false) do |t| - t.references :user - t.references :role - end - - create_table(:human_resources_roles, :id => false) do |t| - t.references :human_resource - t.references :role - end - - create_table(:customers_privileges, :id => false) do |t| - t.references :customer - t.references :privilege - end - - create_table(:moderators_rights, :id => false) do |t| - t.references :moderator - t.references :right - end - create_table(:forums) do |t| t.string :name end @@ -44,7 +25,7 @@ t.integer :parent_id t.string :name end - + create_table(:teams, :id => false) do |t| t.primary_key :team_code t.string :name