models.rb 6.9 KB

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