lib/groupify/adapter/mongoid.rb in groupify-0.6.0.rc1 vs lib/groupify/adapter/mongoid.rb in groupify-0.6.0.rc2

- old
+ new

@@ -1,509 +1,16 @@ require 'mongoid' require 'set' -# Groups and members module Groupify module Mongoid - module Adapter - extend ActiveSupport::Concern - - included do - def none; where(:id => nil); end - end - - module ClassMethods - def acts_as_group(opts = {}) - include Groupify::Mongoid::Group - - if (member_klass = opts.delete :default_members) - self.default_member_class = member_klass.to_s.classify.constantize - end - - if (member_klasses = opts.delete :members) - member_klasses.each do |member_klass| - has_members(member_klass) - end - end - end - - def acts_as_group_member(opts = {}) - @group_class_name = opts[:class_name] || 'Group' - include Groupify::Mongoid::GroupMember - end - - def acts_as_named_group_member(opts = {}) - include Groupify::Mongoid::NamedGroupMember - end - end - end + require 'groupify/adapter/mongoid/model' - # Usage: - # class Group - # include Mongoid::Document - # - # acts_as_group, :members => [:users] - # ... - # end - # - # group.add(member) - # - module Group - extend ActiveSupport::Concern - - included do - @default_member_class = nil - @member_klasses ||= Set.new - end - - # def members - # self.class.default_member_class.any_in(:group_ids => [self.id]) - # end - - def member_classes - self.class.member_classes - end - - def add(*args) - opts = args.extract_options! - membership_type = opts[:as] - members = args.flatten - return unless members.present? - - members.each do |member| - member.groups << self - membership = member.group_memberships.find_or_initialize_by(as: membership_type) - membership.groups << self - membership.save! - end - end - - # Merge a source group into this group. - def merge!(source) - self.class.merge!(source, self) - end - - module ClassMethods - def with_member(member) - member.groups - end - - def default_member_class - @default_member_class ||= User rescue false - end - - def default_member_class=(klass) - @default_member_class = klass - end - - # Returns the member classes defined for this class, as well as for the super classes - def member_classes - (@member_klasses ||= Set.new).merge(superclass.method_defined?(:member_classes) ? superclass.member_classes : []) - end - - # Define which classes are members of this group - def has_members(name) - klass = name.to_s.classify.constantize - register(klass) - end - - # Merge two groups. The members of the source become members of the destination, and the source is destroyed. - def merge!(source_group, destination_group) - # Ensure that all the members of the source can be members of the destination - invalid_member_classes = (source_group.member_classes - destination_group.member_classes) - invalid_member_classes.each do |klass| - if klass.any_in(:group_ids => [source_group.id]).count > 0 - raise ArgumentError.new("#{source_group.class} has members that cannot belong to #{destination_group.class}") - end - end - - source_group.member_classes.each do |klass| - klass.any_in(:group_ids => [source_group.id]).update_all(:$set => {:"group_ids.$" => destination_group.id}) - end - - source_group.delete - end - - protected - - def register(member_klass) - (@member_klasses ||= Set.new) << member_klass - associate_member_class(member_klass) - member_klass - end - - module MemberAssociationExtensions - def as(membership_type) - return self unless membership_type - where(:group_memberships.elem_match => { as: membership_type.to_s, group_ids: [base.id] }) - end - - def destroy(*args) - delete(*args) - end - - def delete(*args) - opts = args.extract_options! - members = args - - if opts[:as] - members.each do |member| - member.group_memberships.as(opts[:as]).first.groups.delete(base) - end - else - members.each do |member| - member.group_memberships.in(groups: base).each do |membership| - membership.groups.delete(base) - end - end - - super(*members) - end - end - end - - def associate_member_class(member_klass) - association_name = member_klass.name.to_s.pluralize.underscore.to_sym - - has_many association_name, class_name: member_klass.to_s, dependent: :nullify, foreign_key: 'group_ids', extend: MemberAssociationExtensions - - if member_klass == default_member_class - has_many :members, class_name: member_klass.to_s, dependent: :nullify, foreign_key: 'group_ids', extend: MemberAssociationExtensions - end - end - end - end - - module MemberScopedAs - extend ActiveSupport::Concern - - module ClassMethods - def as(membership_type) - group_ids = criteria.selector["group_ids"] - named_groups = criteria.selector["named_groups"] - criteria = self.criteria - - # If filtering by groups or named groups, merge into the group membership criteria - if group_ids || named_groups - elem_match = {as: membership_type} - - if group_ids - elem_match.merge!(group_ids: group_ids) - end - - if named_groups - elem_match.merge!(named_groups: named_groups) - end - - criteria = where(:group_memberships.elem_match => elem_match) - criteria.selector.delete("group_ids") - criteria.selector.delete("named_groups") - else - criteria = where(:"group_memberships.as" => membership_type) - end - - criteria - end - end - end - - # Usage: - # class User - # include Mongoid::Document - # - # acts_as_group_member - # ... - # end - # - # user.groups << group - # - module GroupMember - extend ActiveSupport::Concern - include MemberScopedAs - - included do - has_and_belongs_to_many :groups, autosave: true, dependent: :nullify, inverse_of: nil, class_name: @group_class_name do - def as(membership_type) - return self unless membership_type - group_ids = base.group_memberships.as(membership_type).first.group_ids - - if group_ids.present? - self.and(:id.in => group_ids) - else - self.and(:id => nil) - end - end - - def destroy(*args) - delete(*args) - end - - def delete(*args) - opts = args.extract_options! - groups = args.flatten - - - if opts[:as] - base.group_memberships.as(opts[:as]).each do |membership| - membership.groups.delete(*groups) - end - else - super(*groups) - end - end - end - - class GroupMembership - include ::Mongoid::Document - - embedded_in :member, polymorphic: true - - field :named_groups, type: Array, default: -> { [] } - - after_initialize do - named_groups.extend NamedGroupCollection - end - - field :as, as: :membership_type, type: String - end - - GroupMembership.send :has_and_belongs_to_many, :groups, class_name: @group_class_name, inverse_of: nil - - embeds_many :group_memberships, class_name: GroupMembership.to_s, as: :member do - def as(membership_type) - where(membership_type: membership_type.to_s) - end - end - end - - def in_group?(group, opts={}) - groups.as(opts[:as]).include?(group) - end - - def in_any_group?(*args) - opts = args.extract_options! - groups = args - - groups.flatten.each do |group| - return true if in_group?(group, opts) - end - return false - end - - def in_all_groups?(*args) - opts = args.extract_options! - groups = args - - groups.flatten.to_set.subset? self.groups.as(opts[:as]).to_set - end - - def in_only_groups?(*args) - opts = args.extract_options! - groups = args.flatten - - groups.to_set == self.groups.as(opts[:as]).to_set - end - - def shares_any_group?(other, opts={}) - in_any_group?(other.groups.to_a, opts) - end - - module ClassMethods - def group_class_name; @group_class_name ||= 'Group'; end - def group_class_name=(klass); @group_class_name = klass; end - - def in_group(group) - group.present? ? self.in(group_ids: group.id) : none - end - - def in_any_group(*groups) - groups.present? ? self.in(group_ids: groups.flatten.map(&:id)) : none - end - - def in_all_groups(*groups) - groups.present? ? where(:group_ids.all => groups.flatten.map(&:id)) : none - end - - def in_only_groups(*groups) - groups.present? ? where(:group_ids => groups.flatten.map(&:id)) : none - end - - def shares_any_group(other) - in_any_group(other.groups.to_a) - end - - end - end - - module NamedGroupCollection - # Criteria to filter by membership type - def as(membership_type) - return self unless membership_type - - membership = @member.group_memberships.as(membership_type).first - if membership - membership.named_groups - else - self.class.new - end - end - - def <<(named_group, opts={}) - named_group = named_group.to_sym - super(named_group) - uniq! - - if @member && opts[:as] - membership = @member.group_memberships.find_or_initialize_by(as: opts[:as]) - membership.named_groups << named_group - membership.save! - end - - self - end - - def merge(*args) - opts = args.extract_options! - named_groups = args.flatten - - named_groups.each do |named_group| - add(named_group, opts) - end - end - - def delete(*args) - opts = args.extract_options! - named_groups = args.flatten - - if @member - if opts[:as] - membership = @member.group_memberships.as(opts[:as]).first - if membership - if ::Mongoid::VERSION > "4" - membership.pull_all(named_groups: named_groups) - else - membership.pull_all(:named_groups, named_groups) - end - end - - return - else - memberships = @member.group_memberships.where(:named_groups.in => named_groups) - memberships.each do |membership| - if ::Mongoid::VERSION > "4" - membership.pull_all(named_groups: named_groups) - else - membership.pull_all(:named_groups, named_groups) - end - end - end - end - - named_groups.each do |named_group| - super(named_group) - end - end - - def self.extended(base) - base.class_eval do - attr_accessor :member - - alias_method :delete_all, :clear - alias_method :destroy_all, :clear - alias_method :push, :<< - alias_method :add, :<< - alias_method :concat, :merge - alias_method :destroy, :delete - end - end - end - - # Usage: - # class User - # include Mongoid::Document - # - # acts_as_named_group_member - # ... - # end - # - # user.named_groups << :admin - # - module NamedGroupMember - extend ActiveSupport::Concern - include MemberScopedAs - - included do - field :named_groups, type: Array, default: -> { [] } - - after_initialize do - named_groups.extend NamedGroupCollection - named_groups.member = self - end - end - - def in_named_group?(named_group, opts={}) - named_groups.as(opts[:as]).include?(named_group) - end - - def in_any_named_group?(*args) - opts = args.extract_options! - group_names = args.flatten - - group_names.each do |named_group| - return true if in_named_group?(named_group) - end - - return false - end - - def in_all_named_groups?(*args) - opts = args.extract_options! - named_groups = args.flatten.to_set - - named_groups.subset? self.named_groups.as(opts[:as]).to_set - end - - def in_only_named_groups?(*args) - opts = args.extract_options! - named_groups = args.flatten.to_set - named_groups == self.named_groups.as(opts[:as]).to_set - end - - def shares_any_named_group?(other, opts={}) - in_any_named_group?(other.named_groups, opts) - end - - module ClassMethods - def in_named_group(named_group, opts={}) - in_any_named_group(named_group, opts) - end - - def in_any_named_group(*named_groups) - named_groups.flatten! - return none unless named_groups.present? - - self.in(named_groups: named_groups.flatten) - end - - def in_all_named_groups(*named_groups) - named_groups.flatten! - return none unless named_groups.present? - - where(:named_groups.all => named_groups.flatten) - end - - def in_only_named_groups(*named_groups) - named_groups.flatten! - return none unless named_groups.present? - - where(named_groups: named_groups.flatten) - end - - def shares_any_named_group(other, opts={}) - in_any_named_group(other.named_groups, opts) - end - end - end + autoload :Group, 'groupify/adapter/mongoid/group' + autoload :GroupMember, 'groupify/adapter/mongoid/group_member' + autoload :MemberScopedAs, 'groupify/adapter/mongoid/member_scoped_as' + autoload :NamedGroupCollection, 'groupify/adapter/mongoid/named_group_collection' + autoload :NamedGroupMember, 'groupify/adapter/mongoid/named_group_member' end end -Mongoid::Document.send :include, Groupify::Mongoid::Adapter +