BrandyMint/bitrix_on_rails

View on GitHub
lib/bitrix_on_rails/iblock_element.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module BitrixOnRails

  # Хранит список созданных классов в виде хеша: iblock_id -> class_name
  @@infoblocks = {}

  def self.infoblocks
    @@infoblocks
  end

  def self.define_iblock_class(iblock_id, options = {})
    iblock_version = Iblock.find(iblock_id).version

    iblock_element_class = send("create_iblock_v#{iblock_version}_class", iblock_id, options[:extended_class], options[:extended_by])

    unless options[:extended_class]
      # Определяем имя класса, который нужно создать, а также namespace, в котором
      # его нужно создать.
      if class_name = options[:class_name]
        a = class_name.split('::')

        class_name = a.last

        namespace = a[0..-2].join('::')
        namespace = namespace.empty? ? Object : Object.const_get(namespace)
      else
        class_name = "IblockElement#{iblock_id}"
        namespace  = Object
      end

      namespace.const_set(class_name, iblock_element_class)
    end

    # Порядок вызова важен, так как только в этот момент у iblock_element_class появляется имя, которое
    # используется в IblockElementPropS.
    create_prop_classes(iblock_id, iblock_element_class) if iblock_version == 2

    iblock_element_class
  end

  protected

  def self.create_iblock_v1_class(iblock_id, extended_class = nil, extended_by = nil)
    create_iblock_class(iblock_id, extended_class, extended_by) do
      has_many :prop_values, :class_name => 'IblockElementProperty', :foreign_key => 'iblock_element_id'

      @iblock_properties.each { |m, property|
        define_method m do
          prop_values.where(:iblock_property_id => property[:id]).first.try(:value)
        end

        define_method "#{m}=" do |value|
          prop_values.create(:iblock_property_id => property[:id], :value => value)
        end
      }
    end
  end

  def self.create_iblock_v2_class(iblock_id, extended_class = nil, extended_by = nil)
    create_iblock_class(iblock_id, extended_class, extended_by) do
      has_one :property_set, :class_name => "IblockElementPropS#{iblock_id}", :foreign_key => 'iblock_element_id', :autosave => true
      has_many :m_props, :class_name => "IblockElementPropM#{iblock_id}", :foreign_key => 'iblock_element_id', :readonly => true

      @iblock_properties.each { |m, property|
        delegate m, :to => :property_set
        delegate "#{m}=", :to => :property_set unless property[:multiple]
      }
    end
  end

  def self.create_iblock_class(iblock_id, extended_class = nil, extended_by = nil, &blk)
    iblock_element_class = extended_class || Class.new(::IblockElement) {}

    iblock_element_class.instance_eval do
      @iblock_id = iblock_id
      @iblock_properties = Iblock.get_properties(iblock_id).inject({}){ |a,e| a[e[1].code] = {:id => e[1].id, :multiple => e[1].multiple == 'Y'}; a}

      instance_eval(&blk)

      default_scope where(:iblock_id => iblock_id)
    end

    iblock_element_class.extend Object.const_get(extended_by) if extended_by

    @@infoblocks[iblock_id] = iblock_element_class

    iblock_element_class
  end

  def self.create_prop_classes(iblock_id, iblock_element_class)
    const_name = "IblockElementPropS#{iblock_id}"
    unless Object.const_defined? const_name
      c = Class.new(::ActiveRecord::Base) do
        extend BitrixOnRails::IblockElementPropS

        class << self
          # Имя класса, хранящего значения для множественных свойств
          @m_prop_class = nil

          # Список множественных свойств
          @m_props = nil

          # Список одиночных свойств
          @s_props = nil

          def m_prop_class
            Object.const_get(@m_prop_class)
          end

          def m_props
            @m_props
          end

          def s_props
            @s_props
          end
        end

        acts_as_iblock_element_prop_s(iblock_id, iblock_element_class)
      end
      Object.const_set const_name, c
    end

    const_name = "IblockElementPropM#{iblock_id}"
    unless Object.const_defined? const_name
      c = Class.new(::ActiveRecord::Base) do
        extend BitrixOnRails::IblockElementPropM
        acts_as_iblock_element_prop_m(iblock_id)
      end
      Object.const_set "IblockElementPropM#{iblock_id}", c
    end

    # Вставляем связи с i_block_element_prop_* на уровень IblockElement. Это может быть полезно
    # в том случае, если пользователь получил объект класса IblockElement, а не создаваемого.
    # Использование этих связей полностью в компетенции пользователя объекта.
    IblockElement.instance_eval do
      has_one  "iblock_element_prop_s#{iblock_id}".to_sym
      has_many "iblock_element_prop_m#{iblock_id}".to_sym
    end
  end
end