active_merge 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/active_merge/service.rb +23 -55
- data/lib/active_merge/simple_service.rb +77 -0
- data/lib/active_merge/version.rb +1 -1
- data/lib/active_merge.rb +2 -0
- metadata +22 -33
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9f04e96b337986ab01516a571764953292c4cf46
|
4
|
+
data.tar.gz: e51483094adf87e9f17596876c74feda706dddad
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b1c2930d0ffb8c6c0e996fb2aeef7701d211031dfcad1ff6e90f0ef117620de45f676028347c1763785f2a51acab8311b26592873ccd483ea982972f449aecc2
|
7
|
+
data.tar.gz: a4365f27dd04f85929042e8f07a25837386af8bfc0c96d12dd5f7c0ddaca69d867a74b50c5853c319f8dd503cd86399fc2b59269a5f13b265a5b56a69a52d9f2
|
data/lib/active_merge/service.rb
CHANGED
@@ -67,99 +67,67 @@ module ActiveMerge
|
|
67
67
|
# то объединения не произойдет!
|
68
68
|
#
|
69
69
|
class Service
|
70
|
+
include ActiveModel::Validations
|
70
71
|
|
71
72
|
def initialize(list = [])
|
72
|
-
list =
|
73
|
+
list = extract_from list
|
73
74
|
@item, @items = list.first, Array(list[1..-1])
|
74
75
|
end
|
75
76
|
|
76
77
|
attr_reader :item, :items, :klass, :klasses
|
77
|
-
|
78
|
-
# Возвращает класс объединяемых объектов
|
79
|
-
def klass
|
80
|
-
@klass ||= item.class
|
81
|
-
end
|
82
|
-
|
83
|
-
# Возвращает хэш, в котором ключами выступают связи has_many,
|
84
|
-
# а значениями - foreign keys
|
85
|
-
def klasses
|
86
|
-
@klasses ||= klass.reflect_on_all_associations(:has_many).
|
87
|
-
inject({}){ |hash, item| hash.merge(item.name => item.foreign_key) }
|
88
|
-
end
|
78
|
+
validates :items, presence: true
|
89
79
|
|
90
80
|
# Объединяет все записи из переменной #items с записью в переменной #item
|
91
81
|
#
|
92
82
|
# При этом:
|
93
|
-
# * все ссылки на записи из массива #items
|
83
|
+
# * все ссылки на записи из массива #items перепривязываются к #item
|
94
84
|
# * все записи #items удаляются
|
95
85
|
#
|
96
86
|
# При любой ошибке все сделанные изменения отменяются.
|
97
87
|
#
|
98
88
|
def provide
|
99
89
|
ActiveRecord::Base.transaction requires_new: true do
|
100
|
-
|
101
|
-
items.each{ |item|
|
90
|
+
raise unless valid?
|
91
|
+
items.each{ |item| merge self.item, item }
|
102
92
|
end
|
103
93
|
end
|
104
94
|
|
105
95
|
private
|
106
96
|
|
107
|
-
# Вызывает исключение указанного типа с переданными парамертами
|
108
|
-
def _raise(type, item = nil)
|
109
|
-
options = item ? { type: item.class.name.underscore, id: item.id } : {}
|
110
|
-
options.merge! scope: %w(active_merge errors service)
|
111
|
-
raise I18n.t(type, options)
|
112
|
-
end
|
113
|
-
|
114
97
|
# Извлекает из списка только сохраненные объекты ActiveRecord
|
115
98
|
# Если список содержит элементы ActiveRecord разных классов, возвращает
|
116
99
|
# пустой массив.
|
117
|
-
def
|
100
|
+
def extract_from(list)
|
118
101
|
begin
|
119
|
-
list
|
120
|
-
|
121
|
-
list.each { |item| raise unless item.class == klass }
|
122
|
-
list.sort_by { |item| item.id }
|
102
|
+
list = select_active_records_from list
|
103
|
+
list = select_by_class_from list
|
123
104
|
rescue
|
124
105
|
[]
|
125
106
|
end
|
126
107
|
end
|
127
108
|
|
128
|
-
#
|
129
|
-
def
|
130
|
-
|
109
|
+
# Выбирает сохраненные объекты ActiveRecord
|
110
|
+
def select_active_records_from(list)
|
111
|
+
list.select do |item|
|
131
112
|
item.class.ancestors.include?(ActiveRecord::Base) && item.persisted?
|
132
|
-
rescue
|
133
|
-
false
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# Объединяет указанный объект с объектом из переменной #item
|
138
|
-
# * Перепривязывает ссылки на объект #item
|
139
|
-
# * Удаляет объект, переданный в аргументе
|
140
|
-
def _merge_one(item)
|
141
|
-
klasses.each do |list, foreign_key|
|
142
|
-
item.send(list).each { |ref| _rebind! ref, foreign_key }
|
143
113
|
end
|
144
|
-
_destroy! item
|
145
114
|
end
|
146
115
|
|
147
|
-
#
|
148
|
-
def
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
rescue
|
153
|
-
_raise :rebind, item
|
154
|
-
end
|
116
|
+
# Выбирает объекты того же класса, что и у первой записи списка
|
117
|
+
def select_by_class_from(list)
|
118
|
+
klass = list.first.class
|
119
|
+
list.each { |item| raise unless item.class == klass }
|
120
|
+
list.sort_by { |item| item.id }
|
155
121
|
end
|
156
122
|
|
157
|
-
#
|
158
|
-
|
123
|
+
# Объединяет две записи.
|
124
|
+
# Переносит ошибки в текущий объект.
|
125
|
+
def merge(first, second)
|
126
|
+
service = ActiveMerge::SimpleService.new(first, second)
|
159
127
|
begin
|
160
|
-
|
128
|
+
service.provide
|
161
129
|
rescue
|
162
|
-
|
130
|
+
service.errors.each{ |key, val| errors.add key, val } and raise
|
163
131
|
end
|
164
132
|
end
|
165
133
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module ActiveMerge
|
3
|
+
|
4
|
+
# Сервисный объект (паттерн Service Object), отвечающий за объединение
|
5
|
+
# двух записей ActiveRecord
|
6
|
+
#
|
7
|
+
# В ходе объединения все ссылки на вторую запись перепривязываются к первой,
|
8
|
+
# затем вторая запись удаляется
|
9
|
+
#
|
10
|
+
# В случае любой ошибки вызывается исключение, а ошибка добавляется в массив
|
11
|
+
# <tt>errors</tt>
|
12
|
+
#
|
13
|
+
class SimpleService
|
14
|
+
include ActiveModel::Validations
|
15
|
+
|
16
|
+
def initialize(first, second)
|
17
|
+
|
18
|
+
if first.class.ancestors.include?(ActiveRecord::Base) && first.persisted?
|
19
|
+
@first = first
|
20
|
+
end
|
21
|
+
|
22
|
+
if @first && (second.class == @first.class) && second.persisted?
|
23
|
+
@second = second
|
24
|
+
end
|
25
|
+
end
|
26
|
+
attr_reader :first, :second
|
27
|
+
|
28
|
+
validates :second, presence: true
|
29
|
+
|
30
|
+
def provide
|
31
|
+
ActiveRecord::Base.transaction requires_new: true do
|
32
|
+
raise unless valid?
|
33
|
+
refs.each{ |item, key| rebind(item, key) }
|
34
|
+
destroy
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Возвращает хэш, в котором ключами выступают связи has_many,
|
41
|
+
# а значениями - foreign keys
|
42
|
+
def klasses
|
43
|
+
second.class.reflect_on_all_associations(:has_many).
|
44
|
+
inject({}){ |hash, item| hash.merge(item.name => item.foreign_key) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Возвращает хэш, в котором ключами выступают объекты, ссылающихся на
|
48
|
+
# #second, а значениями - методы присвоения ссылки на #first.
|
49
|
+
def refs
|
50
|
+
@refs = {}
|
51
|
+
klasses.each do |list, key|
|
52
|
+
second.send(list).each{ |item| @refs[item] = "#{ key }=" }
|
53
|
+
end
|
54
|
+
return @refs
|
55
|
+
end
|
56
|
+
|
57
|
+
# Выполняет перепривязку указанного объекта к первому аргументу
|
58
|
+
# Ошибки накапливаются в массиве <tt>errors</tt>
|
59
|
+
def rebind(item, key)
|
60
|
+
begin
|
61
|
+
item.send key, first.id
|
62
|
+
item.save!
|
63
|
+
rescue
|
64
|
+
item.errors.each{ |key, val| errors.add key, val } and raise
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Удаляет объединяемый объект
|
69
|
+
def destroy
|
70
|
+
begin
|
71
|
+
second.destroy!
|
72
|
+
rescue
|
73
|
+
second.errors.each{ |key, val| errors.add key, val } and raise
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/active_merge/version.rb
CHANGED
data/lib/active_merge.rb
CHANGED
metadata
CHANGED
@@ -1,94 +1,83 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_merge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Andrew Kozin
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-17 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rails
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: 4.0.3
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- - ~>
|
24
|
+
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: 4.0.3
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: sqlite3
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rspec
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - ">="
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - ">="
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: yard
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - ">="
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: '0'
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - ">="
|
76
67
|
- !ruby/object:Gem::Version
|
77
68
|
version: '0'
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: database_cleaner
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
|
-
- -
|
73
|
+
- - ">="
|
84
74
|
- !ruby/object:Gem::Version
|
85
75
|
version: '0'
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
|
-
- -
|
80
|
+
- - ">="
|
92
81
|
- !ruby/object:Gem::Version
|
93
82
|
version: '0'
|
94
83
|
description: Declares the ActiveMerge module with the 'merge!' class method.
|
@@ -98,36 +87,36 @@ executables: []
|
|
98
87
|
extensions: []
|
99
88
|
extra_rdoc_files: []
|
100
89
|
files:
|
101
|
-
- lib/active_merge/service.rb
|
102
|
-
- lib/active_merge/version.rb
|
103
|
-
- lib/active_merge.rb
|
104
90
|
- MIT-LICENSE
|
105
|
-
- Rakefile
|
106
91
|
- README.rdoc
|
92
|
+
- Rakefile
|
93
|
+
- lib/active_merge.rb
|
94
|
+
- lib/active_merge/service.rb
|
95
|
+
- lib/active_merge/simple_service.rb
|
96
|
+
- lib/active_merge/version.rb
|
107
97
|
homepage: https://github.com/nepalez/active_merge
|
108
98
|
licenses:
|
109
99
|
- MIT
|
100
|
+
metadata: {}
|
110
101
|
post_install_message:
|
111
102
|
rdoc_options: []
|
112
103
|
require_paths:
|
113
104
|
- lib
|
114
105
|
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
-
none: false
|
116
106
|
requirements:
|
117
|
-
- -
|
107
|
+
- - ">="
|
118
108
|
- !ruby/object:Gem::Version
|
119
109
|
version: '0'
|
120
110
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
111
|
requirements:
|
123
|
-
- -
|
112
|
+
- - ">="
|
124
113
|
- !ruby/object:Gem::Version
|
125
114
|
version: '0'
|
126
115
|
requirements: []
|
127
116
|
rubyforge_project:
|
128
|
-
rubygems_version:
|
117
|
+
rubygems_version: 2.2.2
|
129
118
|
signing_key:
|
130
|
-
specification_version:
|
119
|
+
specification_version: 4
|
131
120
|
summary: Merges instances of an ActiveRecord class.
|
132
121
|
test_files: []
|
133
122
|
has_rdoc:
|