module ActiveLDAP # Associations # # Associations provides the class methods needed for # the extension classes to create methods using # belongs_to and has_many module Associations def self.append_features(base) super base.extend(ClassMethods) end module ClassMethods # This class function is used to setup all mappings between the subclass # and ldap for use in activeldap # # Example: # ldap_mapping :dnattr => 'uid', :prefix => 'ou=People', # :classes => ['top', 'posixAccount'], scope => LDAP::LDAP_SCOPE_SUBTREE, # :parent => String def ldap_mapping(options = {}) # The immediate ancestor should be the caller.... klass = self.ancestors[0] dnattr = options[:dnattr] || 'cn' prefix = options[:prefix] || "ou=#{klass.to_s.split(':').last}" classes_array = options[:classes] || nil scope = options[:scope] || 'super' # When used, instantiates parent objects from the "parent dn". This # can be a String or a real ActiveLDAP class. This just adds the helper # Base#parent. parent = options[:parent_class] || nil classes = 'super' unless classes_array.nil? raise TypeError, ":classes must be an array" \ unless classes_array.respond_to? :size # Build classes array classes = '[' classes_array.map! {|x| x = "'#{x}'"} classes << classes_array.join(', ') classes << ']' end # This adds the methods to the local # class which can then be inherited, etc # which describe the mapping to LDAP. klass.class_eval(<<-"end_eval") class << self # Return the list of required object classes def required_classes #{classes} end # Return the full base of the class def base if "#{prefix}".empty? return "\#{super}" else return "#{prefix},\#{super}" end end # Return the expected DN attribute of an object def dnattr '#{dnattr}' end # Return the expected DN attribute of an object def ldap_scope #{scope} end end # Hide connect private_class_method :connect # Unhide class methods public_class_method :find_all public_class_method :find public_class_method :new public_class_method :dnattr end_eval # Add the parent helper if desired if parent klass.class_eval(<<-"end_eval") def parent() return #{parent}.new(@dn.split(',')[1..-1].join(',')) end end_eval end end # belongs_to # # This defines a method for an extension class map its DN key # attribute value on to multiple items which reference it by # |:foreign_key| in the other LDAP entry covered by class |:class_name|. # # Example: # belongs_to :groups, :class_name => Group, :foreign_key => memberUid, :local_key => 'uid' # def belongs_to(association_id, options = {}) klass = options[:class_name] || association_id.to_s key = options[:foreign_key] || association_id.to_s + "_id" local_key = options[:local_key] || '' class_eval <<-"end_eval" def #{association_id}(objects = nil) objects = @@config[:return_objects] if objects.nil? local_key = "#{local_key}" local_key = dnattr() if local_key.empty? results = [] #{klass}.find_all(:attribute => "#{key}", :value => send(local_key.to_sym), :objects => objects).each do |o| results << o end return results end end_eval end # has_many # # This defines a method for an extension class expand an # existing multi-element attribute into ActiveLDAP objects. # This discards any calls which result in entries that # don't exist in LDAP! # # Example: # has_many :members, :class_name => User, :local_key => memberUid, :foreign_key => 'uid' # # TODO[ENH]: def #{...}=(val) to redefine group membership def has_many(association_id, options = {}) klass = options[:class_name] || association_id.to_s key = options[:local_key] || association_id.to_s + "_id" foreign_key = options[:foreign_key] || '' class_eval <<-"end_eval" def #{association_id}(objects = nil) objects = @@config[:return_objects] if objects.nil? foreign_key = "#{foreign_key}" if foreign_key.empty? foreign_key = dnattr() end results = [] unless @data["#{key}"].nil? @data["#{key}"].each do |item| fkey = "" if foreign_key == "dn" and not item.empty? fkey = item.split(',')[0].split('=')[0] item = item.split(',')[0].split('=')[1] end # This will even yield entries that don't necessarily exist if foreign_key != "dn" fkey = foreign_key end #{klass}.find_all(:attribute => fkey, :value => item, :objects => objects).each do |match| results << match end end end return results end end_eval end end end end