commit 90809ae4611daed224702e4f6083c42f9277a4a4
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sun Oct 18 19:22:09 2009 +0200

    [cleanup] internal rework #2

diff --git a/bin/shadowwalker b/bin/shadowwalker
index b1a82ad..4485f7b 100755
--- a/bin/shadowwalker
+++ b/bin/shadowwalker
@@ -101,7 +101,7 @@ class Command < CmdParse::Command
     raise SyntaxError, _("no object name given") if args.empty?
 
     obj_hdl = args.shift.downcase.singularize
-    obj_klass = $shadow.find_klass(obj_hdl)
+    obj_klass = $shadow.get_object(obj_hdl)
     raise PreProcessingError, _("No such object '%s'") % obj_hdl if obj_klass.nil?
     obj_klass
   end
@@ -323,7 +323,7 @@ class TreeCommand < Command
       sconfig = $shadow.get_config()
 
       (sconfig[:presentation][:tree_objects] || []).each do |obj_hdl|
-        obj_klass = $shadow.find_klass(obj_hdl.downcase)
+        obj_klass = $shadow.get_object(obj_hdl.downcase)
         unless obj_klass
           raise PreProcessingError, _("Location object '%s' not defined") % obj_hdl
         end
diff --git a/conf/ldap_shadows/shadows/MilkyPond/hooks/aspects/fs.rb b/conf/ldap_shadows/shadows/MilkyPond/hooks/aspects/fs.rb
index e32fe6b..00c4e17 100644
--- a/conf/ldap_shadows/shadows/MilkyPond/hooks/aspects/fs.rb
+++ b/conf/ldap_shadows/shadows/MilkyPond/hooks/aspects/fs.rb
@@ -15,7 +15,7 @@ end
 
 def self.hook_common(shadow, item)
   unless item.attribute_present?('gidNumber')
-    item.primaryGroup = shadow.find_klass(:group).find(:first, DEFAULT_GROUP)
+    item.primaryGroup = shadow.get_object(:group).find(:first, DEFAULT_GROUP)
   end
 
   if item.attribute_present?('uidNumber')
diff --git a/lib/ldap_shadows/config.rb b/lib/ldap_shadows/config.rb
index 5bae69b..8af9519 100644
--- a/lib/ldap_shadows/config.rb
+++ b/lib/ldap_shadows/config.rb
@@ -93,8 +93,9 @@ module LdapShadows
         shadow.add_object(c_name, c_config)
       end
 
-      shadow.load_relations
+      shadow.cast
 
+      # TODO: find a way to handle translations per-shadow
       translation_path = File.join(shadow_config_path, "translations")
       if File.exists? translation_path
         # load interface translation
diff --git a/lib/ldap_shadows/elements.rb b/lib/ldap_shadows/elements.rb
new file mode 100644
index 0000000..badc0c2
--- /dev/null
+++ b/lib/ldap_shadows/elements.rb
@@ -0,0 +1,51 @@
+  #--
+# LdapShadows, a Medium-level LDAP Access Library and Tool.
+# Copyright (c) 2009 Marc Dequènes (Duck) <Duck@DuckCorp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#++
+
+
+module LdapShadows
+  module Elements
+    module LdapElement
+      def self.included(base)
+        base.extend(ClassMethods)
+      end
+
+      module ClassMethods
+        # initializers
+        def cast; end
+        def cast_relations; end
+
+        # default empty hooks
+        def hook_before_create(shadow, item); end
+        def hook_before_modify(shadow, item); end
+        def hook_before_delete(shadow, item); end
+        def hook_after_create(shadow, item); end
+        def hook_after_modify(shadow, item); end
+        def hook_after_delete(shadow, item); end
+
+        protected
+
+        def self.raise_error(msg)
+          raise ProcessingError, msg
+        end
+      end
+    end
+  end
+end
+
+require 'ldap_shadows/elements/object'
+require 'ldap_shadows/elements/aspect'
diff --git a/lib/ldap_shadows/elements/aspect.rb b/lib/ldap_shadows/elements/aspect.rb
index cbc6ca4..940c06f 100644
--- a/lib/ldap_shadows/elements/aspect.rb
+++ b/lib/ldap_shadows/elements/aspect.rb
@@ -17,13 +17,10 @@
 #++
 
 
-require 'ldap_shadows/hooks'
-
-
 module LdapShadows
 module Elements
   class LdapAspect
-    include Hooks
+    include LdapElement
   end
 end
 end
diff --git a/lib/ldap_shadows/elements/object.rb b/lib/ldap_shadows/elements/object.rb
index 21ba1ea..c0917ee 100755
--- a/lib/ldap_shadows/elements/object.rb
+++ b/lib/ldap_shadows/elements/object.rb
@@ -17,18 +17,66 @@
 #++
 
 
-require 'ldap_shadows/hooks'
-
-
 module LdapShadows
 module Elements
   class LdapObject < ActiveLdap::Base
-    include Hooks
+    include LdapElement
 
-    class_inheritable_accessor :presentation, :relations_info
+    class_inheritable_accessor :presentation
+    @relations_info = {}
 
+    class << self
+      attr_accessor :relations_info
+      protected :relations_info
+    end
+
+    # default
     ldap_mapping :prefix => '', :classes => ['top'], :scope => :sub
 
+    def self.cast
+      ldap_mapping self.parameters[:mapping]
+    end
+
+    def self.cast_relations
+      object_rel = {}
+      object_rel.merge!(self.parameters[:relations]) if self.parameters.include?(:relations)
+      if self.parameters[:presentation].has_key?(:allowed_aspects)
+        self.parameters[:presentation][:allowed_aspects].each do |aspect|
+          aspect_data = self.shadow.get_aspect(aspect)
+          if aspect_data.nil?
+            raise PreProcessingError, _("Aspect '%s' is missing for object '%s'") % [aspect, self.handle]
+          end
+          object_rel.merge!(aspect_data[:relations]) if aspect_data.has_key?(:relations) and aspect_data[:relations]
+        end
+      end
+      return if object_rel.empty?
+
+      object_relations_info = {}
+      object_rel.each_pair do |field_name, rel|
+        foreign_klass = self.shadow.get_object(rel[:object])
+        if foreign_klass.nil?
+          raise PreProcessingError, _("Relation '%s' for object '%s' is impossible: foreign object '%s' is missing") % [field_name, self.handle, rel[:object]]
+        end
+        rel[:class_name] = foreign_klass.to_s
+
+        case rel[:type]
+        when 'belongs_to'
+          belongs_to field_name, rel.reject {|key, val| not ActiveLdap::Associations::ClassMethods::VALID_BELONGS_TO_OPTIONS.include?(key) }
+        when 'has_many'
+          has_many field_name, rel.reject {|key, val| not ActiveLdap::Associations::ClassMethods::VALID_HAS_MANY_OPTIONS.include?(key) }
+        else
+          raise "bug in '#{self.handle}' object relations (wrong type)"
+        end
+
+        object_relations_info[field_name] = {
+          :foreign_klass => foreign_klass,
+          :single_value => ActiveLdap::Base.schema.attribute(rel[:foreign_key]).single_value?,
+          :read_only => rel[:read_only] || false
+        }
+      end
+      self.relations_info = object_relations_info
+    end
+
     # temporary method until active_ldap is fixed: return a DN object (see activeldap#23932)
     def dn_obj
       ActiveLdap::DistinguishedName.parse(self.dn)
@@ -436,7 +484,7 @@ module Elements
     def self.ldap_search_string_objects(shadow, field, op, val_list)
       ldap_search_parts = val_list.split(",").collect do |val|
         obj_hdl = val.downcase.singularize
-        obj_klass = shadow.find_klass(obj_hdl)
+        obj_klass = shadow.get_object(obj_hdl)
         raise PreProcessingError, _("No such object '%s'") % val if obj_klass.nil?
 
         ldap_classes = obj_klass.required_classes
@@ -478,7 +526,7 @@ module Elements
 
     def self.find_raw_item_object(shadow, raw_item)
       shadow.objects.each do |obj_hdl|
-        obj_klass = shadow.find_klass(obj_hdl)
+        obj_klass = shadow.get_object(obj_hdl)
         ldap_classes = obj_klass.required_classes
         return obj_hdl if raw_item.classes & ldap_classes == ldap_classes
       end
@@ -488,7 +536,7 @@ module Elements
     def self.raw_item_info(shadow, raw_item)
       obj_hdl = self.find_raw_item_object(shadow, raw_item)
       if obj_hdl
-        obj_klass = shadow.find_klass(obj_hdl)
+        obj_klass = shadow.get_object(obj_hdl)
         item = obj_klass.new(raw_item.dn)
         {:name => "#{obj_hdl}/#{item.name}", :item => item, :object => obj_klass}
       else
diff --git a/lib/ldap_shadows/hooks.rb b/lib/ldap_shadows/hooks.rb
deleted file mode 100644
index 6a6d2a6..0000000
--- a/lib/ldap_shadows/hooks.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-  #--
-# LdapShadows, a Medium-level LDAP Access Library and Tool.
-# Copyright (c) 2009 Marc Dequènes (Duck) <Duck@DuckCorp.org>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#++
-
-
-module LdapShadows
-  module Hooks
-    def self.included(base)
-      base.extend(ClassMethods)
-    end
-
-    module ClassMethods
-      # default empty hooks
-      def hook_before_create(shadow, item); end
-      def hook_before_modify(shadow, item); end
-      def hook_before_delete(shadow, item); end
-      def hook_after_create(shadow, item); end
-      def hook_after_modify(shadow, item); end
-      def hook_after_delete(shadow, item); end
-
-      protected
-
-      def self.raise_error(msg)
-        raise ProcessingError, msg
-      end
-    end
-  end
-end
diff --git a/lib/ldap_shadows/shadow.rb b/lib/ldap_shadows/shadow.rb
index fed42b1..4ca3806 100755
--- a/lib/ldap_shadows/shadow.rb
+++ b/lib/ldap_shadows/shadow.rb
@@ -22,8 +22,7 @@ require 'jcode'
 require 'active_ldap'
 require 'ldap_shadows/activeldap_fixes'
 require 'facets/metaid'
-require 'ldap_shadows/elements/object'
-require 'ldap_shadows/elements/aspect'
+require 'lib/ldap_shadows/elements'
 
 
 module LdapShadows
@@ -39,13 +38,12 @@ module LdapShadows
       @config = Config.instance
 
       # cannot use anonymous modules/classes, as active_ldap needs named classes for relations
-      module_name = "Shadow" + name.classify
+      module_name = "Shadow" + name.camelize
       @container = LdapShadows::Shadows.module_eval(<<-EOS)
         module #{module_name}; self; end
       EOS
 
       @container_elements = {}
-      @object_definitions = {}
       @aspects = {}
     end
 
@@ -83,56 +81,23 @@ module LdapShadows
       # TODO: remove this crap
       object_def = @config.parse_and_validate(object_name, e_type, object_def_raw)
       klass.presentation = object_def[:presentation]
-      klass.ldap_mapping object_def[:mapping]
-      @object_definitions[object_name.to_sym] = object_def
     end
 
-    def find_klass(object_name)
+    def get_object(object_name)
       @container_elements['object'][object_name.to_s]
     end
 
-    # run it _once_ when all objects are loaded
-    def load_relations
-      @object_definitions.each_pair do |object_name, object_def|
-        object_rel = {}
-        object_rel.merge!(object_def[:relations]) if object_def.include?(:relations)
-        if object_def[:presentation].has_key?(:allowed_aspects)
-          object_def[:presentation][:allowed_aspects].each do |aspect|
-            aspect_data = get_aspect(aspect)
-            if aspect_data.nil?
-              raise PreProcessingError, _("Aspect '%s' is missing") % aspect
-            end
-            object_rel.merge!(aspect_data[:relations]) if aspect_data.has_key?(:relations) and aspect_data[:relations]
-          end
+    def cast
+      @container_elements.each_value do |e_models|
+        e_models.each_value do |e_klass|
+          e_klass.cast
         end
-        next if object_rel.empty?
-
-        klass = find_klass(object_name)
-
-        object_relations_info = {}
-        object_rel.each_pair do |field_name, rel|
-          foreign_klass = find_klass(rel[:object])
-          if foreign_klass.nil?
-            raise PreProcessingError, _("Relation '%s' for object '%s' is impossible: foreign object '%s' is missing") % [field_name, object_name, rel[:object]]
-          end
-          rel[:class_name] = foreign_klass.to_s
-
-          case rel[:type]
-          when 'belongs_to'
-            klass.belongs_to field_name, rel.reject {|key, val| not ActiveLdap::Associations::ClassMethods::VALID_BELONGS_TO_OPTIONS.include?(key) }
-          when 'has_many'
-            klass.has_many field_name, rel.reject {|key, val| not ActiveLdap::Associations::ClassMethods::VALID_HAS_MANY_OPTIONS.include?(key) }
-          else
-            raise "bug in '#{object_name}' object relations (wrong type)"
-          end
-
-          object_relations_info[field_name] = {
-            :foreign_klass => foreign_klass,
-            :single_value => ActiveLdap::Base.schema.attribute(rel[:foreign_key]).single_value?,
-            :read_only => rel[:read_only] || false
-          }
+      end
+
+      @container_elements.each_value do |e_models|
+        e_models.each_value do |e_klass|
+          e_klass.cast_relations
         end
-        klass.relations_info = object_relations_info
       end
     end
 
@@ -148,28 +113,29 @@ module LdapShadows
 
     def add_container_element(e_type, e_name, e_def_raw)
       superklass_location = LdapShadows::Elements
-      superklass_name = 'Ldap' + e_type.classify
+      superklass_name = 'Ldap' + e_type.camelize
       unless superklass_location.const_defined?(superklass_name)
         raise PreProcessingError, _("Element model '%s' does not exist") % e_type
       end
 
-      klass_name = superklass_name + e_name.to_s.classify
+      klass_name = superklass_name + e_name.to_s.camelize
       if @container.const_defined?(klass_name)
         raise PreProcessingError, _("Element '%s' for model '%s' as already been defined") % [e_name, e_type]
       end
 
+      e_def = @config.parse_and_validate(e_name, e_type, e_def_raw)
+
       klass = @container.module_eval(<<-EOS)
         class #{klass_name} < #{superklass_location}::#{superklass_name}; self; end
       EOS
       klass.instance_variable_set(:@handle, e_name)
       klass.instance_variable_set(:@shadow, self)
+      klass.instance_variable_set(:@parameters, e_def)
       klass.meta_eval do
-        attr_reader :handle, :shadow
+        attr_reader :handle, :shadow, :parameters
+        protected :parameters
       end
 
-      e_def = @config.parse_and_validate(e_name, e_type, e_def_raw)
-      klass.instance_variable_set(:@parameters, e_def)
-
       begin
         klass_content = @config.load_hook_content(@name, e_type, e_name)
         klass.class_eval(klass_content) unless klass_content.nil?
