models.rb 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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 = load_adapter(entry)
  34. next if !model_class
  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)
  86. object_name = object_name.to_s
  87. # check if model exists
  88. object_model = load_adapter(object_name)
  89. object_model.find(object_id)
  90. list = all
  91. references = {}
  92. # find relations via attributes
  93. ref_attributes = ["#{object_name.downcase}_id"]
  94. # for users we do not define relations for created_by_id &
  95. # updated_by_id - add it here directly
  96. if object_name == 'User'
  97. ref_attributes.push 'created_by_id'
  98. ref_attributes.push 'updated_by_id'
  99. end
  100. list.each do |model_class, model_attributes|
  101. if !references[model_class.to_s]
  102. references[model_class.to_s] = {}
  103. end
  104. next if !model_attributes[:attributes]
  105. ref_attributes.each do |item|
  106. next if !model_attributes[:attributes].include?(item)
  107. count = model_class.where("#{item} = ?", object_id).count
  108. next if count.zero?
  109. if !references[model_class.to_s][item]
  110. references[model_class.to_s][item] = 0
  111. end
  112. Rails.logger.debug { "FOUND (by id) #{model_class}->#{item} #{count}!" }
  113. references[model_class.to_s][item] += count
  114. end
  115. end
  116. # find relations via reflections
  117. list.each do |model_class, model_attributes|
  118. next if !model_attributes[:reflections]
  119. model_attributes[:reflections].each_value do |reflection_value|
  120. next if reflection_value.macro != :belongs_to
  121. col_name = "#{reflection_value.name}_id"
  122. next if ref_attributes.include?(col_name)
  123. if reflection_value.options[:class_name] == object_name
  124. count = model_class.where("#{col_name} = ?", object_id).count
  125. next if count.zero?
  126. if !references[model_class.to_s][col_name]
  127. references[model_class.to_s][col_name] = 0
  128. end
  129. Rails.logger.debug { "FOUND (by ref without class) #{model_class}->#{col_name} #{count}!" }
  130. references[model_class.to_s][col_name] += count
  131. end
  132. next if reflection_value.options[:class_name]
  133. next if reflection_value.name != object_name.downcase.to_sym
  134. count = model_class.where("#{col_name} = ?", object_id).count
  135. next if count.zero?
  136. if !references[model_class.to_s][col_name]
  137. references[model_class.to_s][col_name] = 0
  138. end
  139. Rails.logger.debug { "FOUND (by ref with class) #{model_class}->#{col_name} #{count}!" }
  140. references[model_class.to_s][col_name] += count
  141. end
  142. end
  143. # cleanup, remove models with empty references
  144. references.each do |k, v|
  145. next if v.present?
  146. references.delete(k)
  147. end
  148. references
  149. end
  150. =begin
  151. get reference total of a models
  152. count = Models.references_total('User', 2)
  153. returns
  154. count # 1234
  155. =end
  156. def self.references_total(object_name, object_id)
  157. references = references(object_name, object_id)
  158. total = 0
  159. references.each_value do |model_references|
  160. model_references.each_value do |count|
  161. total += count
  162. end
  163. end
  164. total
  165. end
  166. =begin
  167. merge model references to other model
  168. result = Models.merge('User', 2, 4711) # Object, object_id_of_primary, object_id_which_should_be_merged
  169. returns
  170. true # false
  171. =end
  172. def self.merge(object_name, object_id_primary, object_id_to_merge, force = false)
  173. # if lower x references to update, do it right now
  174. if force
  175. total = references_total(object_name, object_id_to_merge)
  176. if total > 1000
  177. raise "Can't merge object because object has more then 1000 (#{total}) references, please contact your system administrator."
  178. end
  179. end
  180. # update references
  181. references = references(object_name, object_id_to_merge)
  182. references.each do |model, attributes|
  183. model_object = Object.const_get(model)
  184. # collect items and attributes to update
  185. items_to_update = {}
  186. attributes.each_key do |attribute|
  187. Rails.logger.debug { "#{object_name}: #{model}.#{attribute}->#{object_id_to_merge}->#{object_id_primary}" }
  188. model_object.where("#{attribute} = ?", object_id_to_merge).each do |item|
  189. if !items_to_update[item.id]
  190. items_to_update[item.id] = item
  191. end
  192. items_to_update[item.id][attribute.to_sym] = object_id_primary
  193. end
  194. end
  195. # update items
  196. ActiveRecord::Base.transaction do
  197. items_to_update.each_value(&:save!)
  198. end
  199. end
  200. true
  201. end
  202. end