models.rb 6.7 KB

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