Class: Stepford::CircularRefChecker
- Inherits:
-
Object
- Object
- Stepford::CircularRefChecker
- Defined in:
- lib/stepford/circular_ref_checker.rb
Constant Summary collapse
- @@offenders =
[]
- @@circles_sorted =
[]
- @@circles =
[]
- @@selected_offenders =
[]
Class Method Summary collapse
- .check_associations(model_class, model_and_association_names = []) ⇒ Object
-
.check_refs(options = {}) ⇒ Object
Check refs on all models or models specified in comma delimited list in options like: Stepford.CircularRefChecker.check_refs models: ‘user, post, comment’.
Class Method Details
.check_associations(model_class, model_and_association_names = []) ⇒ Object
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 |
# File 'lib/stepford/circular_ref_checker.rb', line 68 def self.check_associations(model_class, model_and_association_names = []) model_class.reflections.collect {|association_name, reflection| puts "warning: #{model_class}'s association #{reflection.name}'s foreign_key was nil. can't check." unless reflection.foreign_key assc_sym = reflection.name.to_sym clas_sym = reflection.class_name.underscore.to_sym next_class = clas_sym.to_s.camelize.constantize has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(ActiveModel::Validations::PresenceValidator) required = false if reflection.macro == :belongs_to # note: supports composite_primary_keys gem which stores primary_key as an array foreign_key_is_also_primary_key = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym}.include?(reflection.foreign_key.to_sym) is_not_null_fkey_that_is_not_primary_key = model_class.columns.any?{|c| !c.null && c.name.to_sym == reflection.foreign_key.to_sym && !foreign_key_is_also_primary_key} required = is_not_null_fkey_that_is_not_primary_key || has_presence_validator else # no nullable metadata on column if no foreign key in this table. we'd figure out the null requirement on the column if inspecting the child model required = has_presence_validator end if required key = [model_class.table_name.to_sym, reflection.foreign_key.to_sym, next_class.table_name.to_sym] if model_and_association_names.include?(key) @@offenders << model_and_association_names.last unless @@offenders.include?(model_and_association_names.last) short = model_and_association_names.dup # drop all preceding keys that have nothing to do with the circle (short.index(key)).times {short.delete_at(0)} sorted = short.sort unless @@circles_sorted.include?(sorted) @@circles_sorted << sorted @@circles << "#{(short + [key]).collect{|b|"#{b[0]}.#{b[1]}"}.join(' -> ')}".to_sym end else model_and_association_names << key check_associations(next_class, model_and_association_names) end end } model_and_association_names.pop model_and_association_names end |
.check_refs(options = {}) ⇒ Object
Check refs on all models or models specified in comma delimited list in options like:
Stepford.CircularRefChecker.check_refs models: 'user, post, comment'
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 |
# File 'lib/stepford/circular_ref_checker.rb', line 11 def self.check_refs(={}) models = [] included_models = [:models] ? [:models].split(',').collect{|s|s.strip}.compact : nil Dir[File.join('app','models','*.rb').to_s].each do |filename| model_name = File.basename(filename).sub(/.rb$/, '') next if included_models && !included_models.include?(model_name) load File.join('app','models',"#{model_name}.rb") model_class = model_name.camelize.constantize next unless model_class.ancestors.include?(ActiveRecord::Base) models << model_class end models.each do |model_class| check_associations(model_class) end if @@circles.size == 0 puts puts "No circular dependencies." puts return true end puts "The following non-nullable foreign keys used in ActiveRecord model associations are involved in circular dependencies:" @@circles.sort.each do |c| puts puts "#{c}" end puts puts puts "Distinct foreign keys involved in a circular dependency:" puts @@offenders.sort.each do |c| puts "#{c[0]}.#{c[1]}" end totals = {} @@circles_sorted.each do |arr| arr.each do |key| totals[key] = 0 unless totals[key] totals[key] = totals[key] + 1 end end puts puts puts "Foreign keys by number of circular dependency chains involved with:" puts totals.sort_by {|k,v| v}.reverse.each do |arr| c = arr[0] t = arr[1] puts "#{t} (out of #{@@circles_sorted.size}): #{c[0]}.#{c[1]} -> #{c[2]}" end puts return false end |