forked from zammad/zammad
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodels.rb
271 lines (201 loc) · 6.8 KB
/
models.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
class Models
include ApplicationLib
=begin
get list of models
result = Models.all
returns
{
Some::Classname1 => {
attributes: ['id', 'name', '...'],
reflections: ...model.reflections...,
table: 'some_classname1s',
},
Some::Classname2 => {
attributes: ['id', 'name', '...']
reflections: ...model.reflections...
table: 'some_classname2s',
},
}
=end
def self.all
@all ||= begin
all = {}
dir = Rails.root.join('app', 'models').to_s
tables = ActiveRecord::Base.connection.tables
Dir.glob("#{dir}/**/*.rb") do |entry|
next if entry.match?(/application_model/i)
next if entry.match?(%r{channel/}i)
next if entry.match?(%r{observer/}i)
next if entry.match?(%r{store/provider/}i)
next if entry.match?(%r{models/concerns/}i)
next if entry.match?(%r{models/object_manager/attribute/validation/}i)
entry.gsub!(dir, '')
entry = entry.to_classname
model_class = entry.constantize
next if !model_class.respond_to? :new
next if !model_class.respond_to? :table_name
table_name = model_class.table_name # handle models where not table exists, pending migrations
next if !tables.include?(table_name)
model_object = model_class.new
next if !model_object.respond_to? :attributes
all[model_class] = {}
all[model_class][:attributes] = model_class.attribute_names
all[model_class][:reflections] = model_class.reflections
all[model_class][:table] = model_class.table_name
#puts model_class
#puts "rrrr #{all[model_class][:attributes]}"
#puts " #{model_class.attribute_names.inspect}"
end
all
end
end
=begin
get list of searchable models for UI
result = Models.searchable
returns
[Model1, Model2, Model3]
=end
def self.searchable
@searchable ||= Models.all.keys.select { |model| model.respond_to?(:search_preferences) }
end
=begin
get list of indexable models
result = Models.indexable
returns
[Model1, Model2, Model3]
=end
def self.indexable
@indexable ||= Models.all.keys.select { |model| model.method_defined?(:search_index_update_backend) }
end
=begin
get reference list of a models
result = Models.references('User', 2)
returns
{
'Some::Classname1' => {
attribute1: 12,
attribute2: 6,
},
'Some::Classname2' => {
updated_by_id: 12,
created_by_id: 6,
},
}
=end
def self.references(object_name, object_id)
object_name = object_name.to_s
# check if model exists
object_name.constantize.find(object_id)
list = all
references = {}
# find relations via attributes
ref_attributes = ["#{object_name.downcase}_id"]
# for users we do not define relations for created_by_id &
# updated_by_id - add it here directly
if object_name == 'User'
ref_attributes.push 'created_by_id'
ref_attributes.push 'updated_by_id'
end
list.each do |model_class, model_attributes|
if !references[model_class.to_s]
references[model_class.to_s] = {}
end
next if !model_attributes[:attributes]
ref_attributes.each do |item|
next if !model_attributes[:attributes].include?(item)
count = model_class.where("#{item} = ?", object_id).count
next if count.zero?
if !references[model_class.to_s][item]
references[model_class.to_s][item] = 0
end
Rails.logger.debug { "FOUND (by id) #{model_class}->#{item} #{count}!" }
references[model_class.to_s][item] += count
end
end
# find relations via reflections
list.each do |model_class, model_attributes|
next if !model_attributes[:reflections]
model_attributes[:reflections].each_value do |reflection_value|
next if reflection_value.macro != :belongs_to
col_name = "#{reflection_value.name}_id"
next if ref_attributes.include?(col_name)
if reflection_value.options[:class_name] == object_name
count = model_class.where("#{col_name} = ?", object_id).count
next if count.zero?
if !references[model_class.to_s][col_name]
references[model_class.to_s][col_name] = 0
end
Rails.logger.debug { "FOUND (by ref without class) #{model_class}->#{col_name} #{count}!" }
references[model_class.to_s][col_name] += count
end
next if reflection_value.options[:class_name]
next if reflection_value.name != object_name.downcase.to_sym
count = model_class.where("#{col_name} = ?", object_id).count
next if count.zero?
if !references[model_class.to_s][col_name]
references[model_class.to_s][col_name] = 0
end
Rails.logger.debug { "FOUND (by ref with class) #{model_class}->#{col_name} #{count}!" }
references[model_class.to_s][col_name] += count
end
end
# cleanup, remove models with empty references
references.each do |k, v|
next if v.present?
references.delete(k)
end
references
end
=begin
get reference total of a models
count = Models.references_total('User', 2)
returns
count # 1234
=end
def self.references_total(object_name, object_id)
references = references(object_name, object_id)
total = 0
references.each_value do |model_references|
model_references.each_value do |count|
total += count
end
end
total
end
=begin
merge model references to other model
result = Models.merge('User', 2, 4711) # Object, object_id_of_primary, object_id_which_should_be_merged
returns
true # false
=end
def self.merge(object_name, object_id_primary, object_id_to_merge, force = false)
# if lower x references to update, do it right now
if force
total = references_total(object_name, object_id_to_merge)
if total > 1000
raise "Can't merge object because object has more then 1000 (#{total}) references, please contact your system administrator."
end
end
# update references
references = references(object_name, object_id_to_merge)
references.each do |model, attributes|
model_object = model.constantize
# collect items and attributes to update
items_to_update = {}
attributes.each_key do |attribute|
Rails.logger.debug { "#{object_name}: #{model}.#{attribute}->#{object_id_to_merge}->#{object_id_primary}" }
model_object.where("#{attribute} = ?", object_id_to_merge).each do |item|
if !items_to_update[item.id]
items_to_update[item.id] = item
end
items_to_update[item.id][attribute.to_sym] = object_id_primary
end
end
# update items
ActiveRecord::Base.transaction do
items_to_update.each_value(&:save!)
end
end
true
end
end