models.rb 6.5 KB

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