bitrix_on_rails 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.4"
12
+ gem "rcov", ">= 0"
13
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,20 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.4)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rake (0.9.2)
10
+ rcov (0.9.9)
11
+ shoulda (2.11.3)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ bundler (~> 1.0.0)
18
+ jeweler (~> 1.6.4)
19
+ rcov
20
+ shoulda
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Danil Pismenny
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
File without changes
data/README.rdoc ADDED
@@ -0,0 +1,86 @@
1
+ = bitrix_on_rails
2
+
3
+ Приблуды для использования под рельсами базы 1С:Битрикс.
4
+
5
+ Для каждого инфоблока что лежит в b_iblock есть своя таблица название которой с номером инфоблока, например b_iblock_element_prop_s7
6
+ prop_s - для одиночных свойтсв, prop_m - для множестенный.
7
+ У каждого инфоблока нескоолько секций (iblock_section), в каждой секции несколько элементов (iblock_element) у каждого элемента разные свойства которые лежат в prop_s/_m.
8
+ Соответсвенно я эти свойства вытащил в одно место - они все доступны из модели IblockElement. Или напрямую; IblockElement.find(123).НАЗВАНИЕ_СВОЙСТВА или через метод properties
9
+
10
+ == Что есть:
11
+
12
+ === mysql2_downcase адаптер
13
+ 1. Адаптер mysql2_downcase для автоматической конвертации заглавных наименований полей в прописные.
14
+
15
+ === Инфоблоки
16
+
17
+ 1. Все модели для таблиц b_iblock_*
18
+ 2. Автоматическая установка свойств из таблиц prop_s*/prop_m* в методы объекта IblockElement.
19
+ Например IblockElement.find(1).name_emittents, где name_emittent - свойство из prop_s.
20
+ Все подобные свойства также доступны через метод IblockElement#properties.
21
+
22
+ 3. IblockElementS*
23
+ 1. Тоже самое и для моделей IblockElementPropertyS*, плюс в них значения полей можно еще и устанавливать:
24
+
25
+ p = IblockElementPropertyS3.find(1)
26
+ p.name_emittents='новое имя'
27
+ p.save
28
+
29
+ 2. Поиск по кодовым названиям полей таблицы
30
+
31
+ IblockElementPropertyS3.find_by_post_id(123)
32
+
33
+ вместо
34
+
35
+ .find_by_property_149(123)
36
+
37
+ Таким образом если мы знаем что инфоблок S3 связан с таблицей постов и хотим получить все свойства поста 63, делаем:
38
+
39
+ IblockElementPropS3.find_by_post_id(63).iblock_element.properties
40
+
41
+ 3. IblockElement.properties выдает хеш соответсвия кодов свойств и названия их полей в prop_s
42
+ Автоматическое определение привязки элемента к объекту. Например: если мы спрашиваем iblock_element.post
43
+ то он ищет ключ :post_id в свойствах элемента, и если находит, то возвращает Post.find_by_id(properties[:post_id])
44
+
45
+ 4. Все свойства IblockProperty кешируются и доступны через find(id)
46
+
47
+ 5. В IblockElement автоматически добавляются has_one :iblock_element_prop_s* и has_many :iblock_element_prop_m* при создании соответвующих классов.
48
+
49
+ 6. Модель можно расширять инфоблоком с помощью:
50
+
51
+ class Post << ActiveRecord::Base
52
+
53
+ has_infoblock(3, :property_19) # где property_19 поле ссылающееся на Post
54
+
55
+ для этой модели автоматически создадутся ассоциации
56
+
57
+ :iblock_element - класс PostElement
58
+
59
+ Класс PostElement будет иметь ассоциации
60
+
61
+ :property_set (он же iblock_element_prop_sNUMBER) и
62
+ :m_prop_values
63
+
64
+ а также default_scope с номером инфоблока, поэтому PostElement.all выдаст только элементы к специфичному инфоблоку
65
+
66
+
67
+ === Битрикс-авторизация
68
+
69
+ == mysql2_downcase
70
+
71
+ Подключать просто:
72
+
73
+ development:
74
+ adapter: mysql2_downcase
75
+ encoding: cp1251
76
+ ...
77
+
78
+ == Contributing to bitrix_on_rails
79
+
80
+ * С нетерпением жду форков, пулеквестов, замечаний и предложений.
81
+
82
+ == Copyright
83
+
84
+ Copyright (c) 2011 Danil Pismenny. See LICENSE.txt for
85
+ further details.
86
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "bitrix_on_rails"
18
+ gem.homepage = "http://github.com/dapi/bitrix_on_rails"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Работа с инфоблоками 1С-Битрикс}
21
+ gem.description = %Q{Использование инфоблоков 1С-Битрикс в Ruby On Rails проектах}
22
+ gem.email = "danil@orionet.ru"
23
+ gem.authors = ["Danil Pismenny"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "bitrix_on_rails #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,87 @@
1
+ # -*- coding: utf-8 -*-
2
+ class Iblock < ActiveRecord::Base
3
+ set_table_name :b_iblock
4
+
5
+ # version - 1 или 2
6
+
7
+ # iblock_type_id это строка, поэтом он срабатывает только если его указывать явано в foreign_key
8
+ belongs_to :iblock_type, :foreign_key=>'iblock_type_id'
9
+
10
+ # Они еще связаны между собой через iblock_section_element
11
+ has_many :iblock_sections
12
+ has_many :iblock_elements
13
+
14
+ # has_many :iblock_section_elements, :through=>:iblock_sections
15
+
16
+ # Список типов свойств по номерам которых создаются поля в
17
+ # таблицах iblock_element_prop_sN
18
+ has_many :iblock_properties
19
+
20
+ has_many :iblock_fields
21
+ has_many :iblock_groups
22
+
23
+ class << self
24
+ def s_props_class(id)
25
+ # raise 'Только для инфоблоков 2-й версии' unless version==2
26
+ "IblockElementPropS#{id}".constantize
27
+ end
28
+
29
+ def m_props_class(id)
30
+ # raise 'Только для инфоблоков 2-й версии' unless version==2
31
+ "IblockElementPropM#{id}".constantize
32
+ end
33
+
34
+ def all
35
+ @cached_all ||= super
36
+ end
37
+ end
38
+
39
+ def to_s
40
+ name
41
+ end
42
+
43
+
44
+ def init_property_models
45
+ return unless version==2
46
+ iblock_id = self.id
47
+
48
+ # Создаем классы IblockElementPropSНОМЕР
49
+ #
50
+ const_name = "IblockElementPropS#{iblock_id}"
51
+ unless Kernel.const_defined? const_name
52
+ e = Class.new(ActiveRecord::Base) do
53
+ extend BitrixOnRails::IblockElementPropS
54
+
55
+ class << self
56
+ @m_prop_class = nil
57
+ @m_props = nil
58
+
59
+ def m_prop_class
60
+ Kernel.const_get(@m_prop_class)
61
+ end
62
+
63
+ def m_props
64
+ @m_props
65
+ end
66
+ end
67
+
68
+ acts_as_iblock_element_prop_s(iblock_id)
69
+ end
70
+ Kernel.const_set const_name, e
71
+ end
72
+ Kernel.const_get(const_name).init
73
+
74
+ # Создаем классы IblockElementPropMНОМЕР
75
+ #
76
+ const_name = "IblockElementPropM#{iblock_id}"
77
+ unless Kernel.const_defined? const_name
78
+ e = Class.new(ActiveRecord::Base) do
79
+ extend BitrixOnRails::IblockElementPropM
80
+ acts_as_iblock_element_prop_m(iblock_id)
81
+ end
82
+ Kernel.const_set "IblockElementPropM#{iblock_id}", e
83
+ end
84
+ Kernel.const_get(const_name).init
85
+ end
86
+
87
+ end
@@ -0,0 +1,108 @@
1
+ # -*- coding: utf-8 -*-
2
+ class IblockElement < ActiveRecord::Base
3
+ class << self
4
+ @block_id = nil
5
+ @iblock_properties = nil
6
+
7
+ def iblock_id
8
+ @iblock_id
9
+ end
10
+
11
+ def iblock_properties
12
+ @iblock_properties
13
+ end
14
+ end
15
+
16
+ set_table_name :b_iblock_element
17
+
18
+ belongs_to :iblock
19
+ belongs_to :iblock_section
20
+
21
+ # Применяется только для iblock-ов 1-й версии
22
+ #
23
+ has_many :iblock_element_properties
24
+
25
+ # Применяется для iblock-ов 2-й версии
26
+ #
27
+ has_many :iblock_section_elements
28
+ has_many :iblock_sections, :through => :iblock_section_elements
29
+
30
+ after_find :initialize_properties
31
+
32
+ attr_accessor :properties
33
+
34
+ def to_s
35
+ name
36
+ end
37
+
38
+ def s_props
39
+ # has_one
40
+ @s_props ||= Iblock.s_props_class(iblock_id).find_by_iblock_element_id( id )
41
+ end
42
+
43
+ def iblock_element_prop_m
44
+ Iblock.m_props_class(iblock_id).where(:iblock_element_id=>id)
45
+ end
46
+
47
+ def m_props
48
+ # TODO Cache
49
+ @m_props={}
50
+ iblock_element_prop_m.each do |p|
51
+ @m_props[p.code]||=[]
52
+ @m_props[p.code] << p.get_value
53
+ end
54
+ @m_props
55
+ end
56
+
57
+ # Если мы спрашиваем iblock_element.post
58
+ # то он ищет ключ :post_id в свойствах элемента
59
+ # и если находит, то возвращает Post.find_by_id(properties[:post_id])
60
+ #
61
+ def method_missing(method, *args)
62
+ id_name = "#{method}_id".to_sym
63
+ if properties and properties.include?(id_name)
64
+ # TODO Проверять на наличие такого класса и то что он ActiveRecord
65
+ # Кешировать
66
+ method.to_s.capitalize.constantize.find_by_id(properties[id_name])
67
+ else
68
+ super
69
+ end
70
+ end
71
+
72
+ def self.set_iblock_id(id)
73
+ @iblock_id = id
74
+ @iblock_properties = IblockProperty.where(:iblock_id => id).inject({}){ |a,e| a[e.code] = e.id; a }
75
+
76
+ prop_s_class = "::IblockElementPropS#{id}"
77
+ prop_m_class = "::IblockElementPropM#{id}"
78
+
79
+ has_one :property_set, :class_name => prop_s_class, :foreign_key => 'iblock_element_id', :autosave => true
80
+ has_many :m_prop_values, :class_name => prop_m_class, :foreign_key => 'iblock_element_id'
81
+
82
+ default_scope where(:iblock_id => id, :active => 'Y')
83
+ end
84
+
85
+ private
86
+
87
+ def initialize_properties
88
+ # TODO Могу повторяться именя в m_props, ругаться чтоли
89
+ @properties = (s_props ? s_props.properties : {}).merge m_props
90
+
91
+ # TODO Переписать на автоматическое определение названия свойства в моделях property_set и m_prop_values
92
+ # чтобы уйти от хеша @properties (оставить его массивом для дебаггинга)
93
+
94
+ # TODO Сделать в элементе автоделегейт
95
+ @properties.keys.each do |prop|
96
+ # TODO Перенести на прямой вызов объектов s_props/m_props
97
+ instance_eval "def #{prop}; @properties[:#{prop}]; end"
98
+ instance_eval "def #{prop}=(val); @properties[:#{prop}]=property_set.#{prop}=val; end"
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+
105
+ # Это здесь для того чтобы при перезагрузке в development
106
+ # режиме заново создавались ассоциации iblock_element_prop_s3
107
+ # TODO переделать на более элегантное решение
108
+ Iblock.all.map &:init_property_models
@@ -0,0 +1,6 @@
1
+ class IblockElementProperty < ActiveRecord::Base
2
+ set_table_name :b_iblock_element_property
3
+
4
+ belongs_to :iblock_property
5
+ belongs_to :iblock_element
6
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ class IblockField < ActiveRecord::Base
3
+ set_table_name :b_iblock_fields
4
+
5
+ belongs_to :iblock
6
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ class IblockGroup < ActiveRecord::Base
3
+ set_table_name :b_iblock_group
4
+
5
+ belongs_to :iblock
6
+ end
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8 -*-
2
+ class IblockProperty < ActiveRecord::Base
3
+ set_table_name :b_iblock_property
4
+
5
+ belongs_to :iblock
6
+
7
+ # TODO Что это за link_iblock?
8
+ belongs_to :link_iblock, :class_name=>'Iblock'
9
+
10
+ has_many :iblock_property_enums, :foreign_key=>:property_id
11
+ has_many :iblock_element_properties
12
+
13
+ cattr_accessor :cache
14
+
15
+ class << self
16
+ def find(id)
17
+ cache_all unless @@cache
18
+ @@cache[id] or raise "Не найдено такое свойство iblock_property #{id}"
19
+ end
20
+
21
+ # Запускается в ./lib/bitrix_on_rails при загрузке
22
+ def cache_all
23
+ @@cache = IblockProperty.all.inject({}) { |hash, prop| hash[prop.id]=prop; hash }
24
+ end
25
+ end
26
+
27
+ def code
28
+ val = read_attribute('code')
29
+ return nil if val.nil?
30
+ val.downcase.to_sym
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ class IblockPropertyEnum < ActiveRecord::Base
3
+ set_table_name :b_iblock_property_enum
4
+
5
+ belongs_to :iblock_property, :foreign_key=>:property_id
6
+ end
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ class IblockSection < ActiveRecord::Base
3
+ class << self
4
+ @iblock_id = nil
5
+
6
+ def iblock_id
7
+ @iblock_id
8
+ end
9
+ end
10
+
11
+ set_table_name :b_iblock_section
12
+
13
+ belongs_to :iblock
14
+
15
+ default_scope order(:sort)
16
+
17
+ has_many :direct_iblock_elements, :class_name=>'IblockElement'
18
+
19
+ has_many :iblock_section_elements
20
+ has_many :iblock_elements, :through => :iblock_section_elements
21
+
22
+ def self.set_iblock_id(id)
23
+ @iblock_id = id
24
+ default_scope where(:iblock_id => id, :active => 'Y')
25
+ end
26
+
27
+ def to_s
28
+ name
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+ class IblockSectionElement < ActiveRecord::Base
3
+ set_table_name :b_iblock_section_element
4
+
5
+ belongs_to :iblock_section
6
+ belongs_to :iblock_element
7
+ belongs_to :additional_property, :class_name=>'IblockProperty'
8
+ end
@@ -0,0 +1,5 @@
1
+ class IblockType < ActiveRecord::Base
2
+ set_table_name :b_iblock_type
3
+
4
+ has_many :blocks, :class_name=>"Iblock"
5
+ end
@@ -0,0 +1,77 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{bitrix_on_rails}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Danil Pismenny"]
12
+ s.date = %q{2011-08-02}
13
+ s.description = %q{Использование инфоблоков 1С-Битрикс в Ruby On Rails проектах}
14
+ s.email = %q{danil@orionet.ru}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "app/models/iblock.rb",
30
+ "app/models/iblock_element.rb",
31
+ "app/models/iblock_element_property.rb",
32
+ "app/models/iblock_field.rb",
33
+ "app/models/iblock_group.rb",
34
+ "app/models/iblock_property.rb",
35
+ "app/models/iblock_property_enum.rb",
36
+ "app/models/iblock_section.rb",
37
+ "app/models/iblock_section_element.rb",
38
+ "app/models/iblock_type.rb",
39
+ "bitrix_on_rails.gemspec",
40
+ "init.rb",
41
+ "lib/active_record/connection_adapters/mysql2_downcase_adapter.rb",
42
+ "lib/bitrix_on_rails.rb",
43
+ "lib/bitrix_on_rails/active_record.rb",
44
+ "lib/bitrix_on_rails/engine.rb",
45
+ "lib/bitrix_on_rails/iblock_element_prop_m.rb",
46
+ "lib/bitrix_on_rails/iblock_element_prop_s.rb",
47
+ "test/helper.rb",
48
+ "test/test_bitrix_on_rails.rb"
49
+ ]
50
+ s.homepage = %q{http://github.com/dapi/bitrix_on_rails}
51
+ s.licenses = ["MIT"]
52
+ s.require_paths = ["lib"]
53
+ s.rubygems_version = %q{1.6.2}
54
+ s.summary = %q{Работа с инфоблоками 1С-Битрикс}
55
+
56
+ if s.respond_to? :specification_version then
57
+ s.specification_version = 3
58
+
59
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
60
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
61
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
62
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
63
+ s.add_development_dependency(%q<rcov>, [">= 0"])
64
+ else
65
+ s.add_dependency(%q<shoulda>, [">= 0"])
66
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
67
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
68
+ s.add_dependency(%q<rcov>, [">= 0"])
69
+ end
70
+ else
71
+ s.add_dependency(%q<shoulda>, [">= 0"])
72
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
73
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
74
+ s.add_dependency(%q<rcov>, [">= 0"])
75
+ end
76
+ end
77
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'bitrix_on_rails'
@@ -0,0 +1,76 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Just use 'mysql2_downcase' adapter in database.yml
3
+ #
4
+
5
+ require 'active_record/connection_adapters/mysql2_adapter'
6
+
7
+ module ActiveRecord
8
+ class Base
9
+ # Establishes a connection to the database that's used by all Active Record objects.
10
+ def self.mysql2_downcase_connection(config)
11
+ config[:username] = 'root' if config[:username].nil?
12
+
13
+ if Mysql2::Client.const_defined? :FOUND_ROWS
14
+ config[:flags] = Mysql2::Client::FOUND_ROWS
15
+ end
16
+
17
+ client = Mysql2::Client.new(config.symbolize_keys)
18
+ options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
19
+ ConnectionAdapters::Mysql2DowncaseAdapter.new(client, logger, options, config)
20
+ # ActiveRecord::Base.mysql2_connection(config)
21
+ end
22
+ end
23
+
24
+ class Result
25
+
26
+ # Это выполняется через ActiveRecord::Result.new в mysql2_adapter
27
+ # при выполнении запроса
28
+ def initialize(columns, rows)
29
+ @columns = columns.map { |c| c.downcase }
30
+ @rows = rows
31
+ @hash_rows = nil
32
+ end
33
+ end
34
+
35
+ module ConnectionAdapters
36
+ class Column
37
+
38
+ alias_method :old_initialize, :initialize
39
+
40
+ # Это выполняется при выборке списка аттрибутов на уровне класса
41
+ #
42
+ def initialize(name, default, sql_type = nil, null = true)
43
+ old_initialize name.downcase, default, sql_type, null
44
+ end
45
+
46
+ end
47
+
48
+ class Mysql2DowncaseAdapter < Mysql2Adapter
49
+
50
+ ADAPTER_NAME = 'Mysql2Downcase'
51
+
52
+ # Надо бы глобально сделать тут:
53
+ # def execute(sql, name = nil)
54
+ # debugger
55
+ # end
56
+
57
+ # Это чтобы нормально определся primary_key
58
+ def pk_and_sequence_for(table)
59
+ keys = []
60
+ result = execute("DESCRIBE #{quote_table_name(table)}", 'SCHEMA')
61
+ result.each(:symbolize_keys => true, :as => :hash) do |row|
62
+ keys << row[:Field].downcase if row[:Key] == "PRI"
63
+ end
64
+ keys.length == 1 ? [keys.first, nil] : nil
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+ # Возможно понадобится исправить этот метод
73
+ # quote_column_name
74
+
75
+ # Хороший пример адаптера - http://virtuoso.rubyforge.org/activerecord-mysql2spatial-adapter/
76
+ # https://github.com/dazuma/activerecord-mysql2spatial-adapter
@@ -0,0 +1,21 @@
1
+ # -*- coding: utf-8 -*-
2
+ # require 'rails'
3
+
4
+ module BitrixOnRails
5
+ end
6
+
7
+ require 'active_record'
8
+
9
+ require 'bitrix_on_rails/active_record'
10
+ require 'bitrix_on_rails/iblock_element_prop_s'
11
+ require 'bitrix_on_rails/iblock_element_prop_m'
12
+ # require 'bitrix_on_rails/engine'
13
+
14
+ # ActiveRecord::Base.connection.tables
15
+
16
+ ActiveRecord::Base.extend BitrixOnRails::ActiveRecord
17
+
18
+ # Просто подгружаем модель, тогда в ней появятся ассоциации типа iblock_element_prop_s3
19
+ IblockElement.class
20
+
21
+ # IblockProperty.cache_all
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ IblockElement.class
4
+
5
+ module BitrixOnRails
6
+ module ActiveRecord
7
+
8
+ def has_infoblock(iblock_id, property_name, &blk)
9
+ prop_s_name = "iblock_element_prop_s#{iblock_id}".to_sym
10
+ has_one prop_s_name, :foreign_key=>property_name, :class_name=>"::IblockElementPropS#{iblock_id}", :autosave => true
11
+
12
+ element_class_name = self.name + 'Element'
13
+ unless Kernel.const_defined?(element_class_name)
14
+ element_class = Class.new(IblockElement) do
15
+ set_iblock_id iblock_id
16
+ end
17
+
18
+ if block_given?
19
+ element_class.instance_eval(&blk)
20
+ end
21
+
22
+ self.const_set(element_class_name, element_class)
23
+ end
24
+
25
+ has_one :iblock_element, :through => prop_s_name, :class_name => element_class_name, :autosave => true
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,4 @@
1
+ module BitrixOnRails
2
+ class Engine < Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,64 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -*- coding: utf-8 -*-
3
+ module BitrixOnRails::IblockElementPropM
4
+ def acts_as_iblock_element_prop_m(id)
5
+ extend ClassMethods
6
+ include InstanceMethods
7
+
8
+ # Убираем лишнюю s вконце
9
+ #set_table_name 'b_'+table_name.chop
10
+ set_table_name "b_iblock_element_prop_m#{id}"
11
+
12
+ # delegate :code, :to=>:property
13
+
14
+ belongs_to :iblock_element
15
+ belongs_to :iblock_property
16
+ end
17
+
18
+ module ClassMethods
19
+ def init
20
+ self.to_s=~/(\d+)/
21
+ iblock_id = $1.to_i
22
+ IblockElement.send :has_many, "iblock_element_prop_m#{iblock_id}".to_sym, :class_name=>"::IblockElementPropM#{iblock_id}"
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+
28
+ def property
29
+ # Достаем кешированный вариант
30
+ IblockProperty.find(iblock_property_id)
31
+ end
32
+
33
+ def code
34
+ @code||=property.code
35
+ end
36
+
37
+ def get_value
38
+ case property.property_type
39
+ when 'S' # String
40
+ self.value
41
+ when 'N' # Numeric
42
+ self.value_num.to_i # Когда BigDecimal - не приятно
43
+ # when 'E' # Enum?
44
+ # self.value_enum
45
+ # when 'L' # WTF?
46
+ else
47
+ raise "Не установленный тип (#{iblock_property.property_type}) свойства (#{self.class} #{id})"
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ # Table b_iblock_element_prop_m7
55
+ # ==============================
56
+ # ID, IBLOCK_ELEMENT_ID, IBLOCK_PROPERTY_ID, VALUE, VALUE_ENUM, VALUE_NUM, DESCRIPTION
57
+ # ------------------------------
58
+ # ID int(11) PK
59
+ # IBLOCK_ELEMENT_ID int(11)
60
+ # IBLOCK_PROPERTY_ID int(11)
61
+ # VALUE text
62
+ # VALUE_ENUM int(11)
63
+ # VALUE_NUM decimal(18,4)
64
+ # DESCRIPTION varchar(255)
@@ -0,0 +1,119 @@
1
+ # -*- coding: utf-8 -*-
2
+ module BitrixOnRails::IblockElementPropS
3
+ def acts_as_iblock_element_prop_s(id)
4
+ extend ClassMethods
5
+ include InstanceMethods
6
+
7
+ @m_prop_class = "IblockElementPropM#{id}"
8
+ # Название свойства сохраняется только для наглядности, чтобы можно было через консоль понять
9
+ # что за свойство.
10
+ @m_props = IblockProperty.where(:iblock_id => id).inject({}){ |a,e| a[e.code] = e.id if e.multiple == 'Y'; a }
11
+
12
+ # Убираем лишнюю s вконце
13
+ #set_table_name 'b_'+table_name.chop
14
+ set_table_name "b_iblock_element_prop_s#{id}"
15
+
16
+ belongs_to :iblock_element
17
+
18
+ # Хеш соответсвия кодов свойств названию полей: post_id -> property_149
19
+ cattr_accessor :properties
20
+
21
+ # Хеш свойств объекта по кодам
22
+ attr_accessor :properties
23
+
24
+ after_find do
25
+ self.properties = self.class.properties.keys.inject({}) { |hash, prop| hash[prop] = send prop; hash }
26
+ end
27
+
28
+ before_save do
29
+ self.class.m_props.each_value { |id|
30
+ values = m_prop_values(id)
31
+ self.send("property_#{id}=", PHP.serialize({'VALUE' => values, 'DESCRIPTION' => Array.new(values.size, nil)}))
32
+ # self.send("property_#{id}=", PhpSerialization.dump({'VALUE' => values, 'DESCRIPTION' => Array.new(values.size, nil)}))
33
+ }
34
+ end
35
+
36
+ end
37
+
38
+ def create_element_association
39
+ IblockElement.send :has_one, "iblock_element_prop_s#{id}".to_sym, :class_name=>"::IblockElementPropS#{id}"
40
+ end
41
+
42
+ module ClassMethods
43
+
44
+ # code - врое как кодовое название свойства
45
+ # name - русское описание
46
+
47
+ # Дальше идут пары:
48
+ # property_120
49
+ # description_120
50
+ # ..
51
+ # определяются они в iblock.iblock_properties
52
+
53
+ # Берем список свойств из таблицы iblock_properties
54
+ # и создаем такие же методы для опроса и установки, чтобы можно было использовать
55
+ # их напрямую:
56
+ #
57
+ # self.кодовое_имя_свойства
58
+ #
59
+ # Например:
60
+ #
61
+ # self.post_id вместо self.property_120
62
+ #
63
+ def init
64
+ self.to_s=~/(\d+)/
65
+ iblock_id = $1.to_i
66
+
67
+ IblockElement.send :has_one, "iblock_element_prop_s#{iblock_id}".to_sym, :class_name=>"::IblockElementPropS#{iblock_id}"
68
+
69
+ self.properties = {}
70
+ attribute_names.select {|a| a[0,4]=='prop' }.each do |name|
71
+ prop_id = name.slice(9,5).to_i
72
+ iblock_property = IblockProperty.find(prop_id)
73
+ code = iblock_property.code.downcase.to_sym
74
+ self.properties[code] = name
75
+ define_method code do
76
+ val = send name
77
+ return val.to_i if val.is_a?(BigDecimal)
78
+
79
+ # Вот так мы проверяем сериализацию
80
+ # Есть еще вариант что поле сериализовано если iblock_property.user_type=='HTML'
81
+ #
82
+ if val.is_a? String and val.length>5 and val[0,3]=~/[a-z]:\d/
83
+ res = PHP.unserialize(val)
84
+ # res = PhpSerialization.load(val)
85
+ return iblock_property.property_type=='S' && res.is_a?(Hash) && res.include?('TEXT') ? res['TEXT'] : res
86
+ end
87
+ val
88
+ end
89
+ define_method "#{code}=" do |val|
90
+ send "#{name}=", val
91
+ end
92
+ eval "def self.find_by_#{code}(val); find_by_#{name}(val); end"
93
+ end
94
+ end
95
+ end
96
+
97
+ module InstanceMethods
98
+
99
+ # Возвращает десериализованное (при необходимости) значение свойства
100
+ #
101
+ # prop - код свойства (post_id к примеру)
102
+ def get_value(prop)
103
+ send prop
104
+ end
105
+
106
+ def m_prop_values(prop_id)
107
+ self.class.m_prop_class.where(:iblock_element_id => self.id, :iblock_property_id => prop_id).collect { |e| e.value }
108
+ end
109
+
110
+ def create_m_prop_value(prop_id, value)
111
+ self.class.m_prop_class.create(:iblock_element_id => self.id, :iblock_property_id => prop_id, :value => value)
112
+ end
113
+
114
+ def destroy_m_prop_value(prop_id, value)
115
+ m_props = self.class.m_prop_class.where(:iblock_element_id => self.iblock_element_id, :iblock_property_id => prop_id, :value => value)
116
+ m_props.each { |p| p.destroy } if m_props.any?
117
+ end
118
+ end
119
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'bitrix_on_rails'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestBitrixOnRails < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitrix_on_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Danil Pismenny
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-02 00:00:00.000000000 +04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: shoulda
17
+ requirement: &2155895220 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *2155895220
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: &2155894360 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *2155894360
37
+ - !ruby/object:Gem::Dependency
38
+ name: jeweler
39
+ requirement: &2155893580 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 1.6.4
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *2155893580
48
+ - !ruby/object:Gem::Dependency
49
+ name: rcov
50
+ requirement: &2155892440 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *2155892440
59
+ description: Использование инфоблоков 1С-Битрикс в Ruby On Rails проектах
60
+ email: danil@orionet.ru
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files:
64
+ - LICENSE.txt
65
+ - README
66
+ - README.rdoc
67
+ files:
68
+ - .document
69
+ - Gemfile
70
+ - Gemfile.lock
71
+ - LICENSE.txt
72
+ - README
73
+ - README.rdoc
74
+ - Rakefile
75
+ - VERSION
76
+ - app/models/iblock.rb
77
+ - app/models/iblock_element.rb
78
+ - app/models/iblock_element_property.rb
79
+ - app/models/iblock_field.rb
80
+ - app/models/iblock_group.rb
81
+ - app/models/iblock_property.rb
82
+ - app/models/iblock_property_enum.rb
83
+ - app/models/iblock_section.rb
84
+ - app/models/iblock_section_element.rb
85
+ - app/models/iblock_type.rb
86
+ - bitrix_on_rails.gemspec
87
+ - init.rb
88
+ - lib/active_record/connection_adapters/mysql2_downcase_adapter.rb
89
+ - lib/bitrix_on_rails.rb
90
+ - lib/bitrix_on_rails/active_record.rb
91
+ - lib/bitrix_on_rails/engine.rb
92
+ - lib/bitrix_on_rails/iblock_element_prop_m.rb
93
+ - lib/bitrix_on_rails/iblock_element_prop_s.rb
94
+ - test/helper.rb
95
+ - test/test_bitrix_on_rails.rb
96
+ has_rdoc: true
97
+ homepage: http://github.com/dapi/bitrix_on_rails
98
+ licenses:
99
+ - MIT
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ segments:
111
+ - 0
112
+ hash: 3152042687155228433
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 1.6.2
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: Работа с инфоблоками 1С-Битрикс
125
+ test_files: []