class DynamicModel::RelationsAnalyser
This class is in charge to analyse the relationships between the tables of the domain. It will then add association methods to the classes.
Constants
- KEY_IDENTIFIER
Attributes
alterations[R]
Public Class Methods
new(klasses)
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 10 def initialize(klasses) @klasses = klasses @alterations = {} end
Public Instance Methods
run()
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 15 def run raise "cannot rerun analysis with the same object... Create a new instance !" if @did_run return if @klasses.empty? @domain = @klasses[0].domain introspect_belongs_to verify_if_has_many_relations_could_be_actually_has_one discover_has_many_through_from_belongs_to apply_alterations @did_run = true end
Private Instance Methods
add_belongs_to_behaviour(model, description)
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 146 def add_belongs_to_behaviour(model, description) field_name = description[:class].list_name.singularize.to_sym options = {foreign_key: description[:key], class_name: description[:class].name} model.belongs_to field_name, options puts " - belongs_to :#{field_name}, #{options.inspect}" end
add_has_many_behaviour(model, description)
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 153 def add_has_many_behaviour(model, description) field_name = description[:class].list_name.to_sym options = {class_name: description[:class].name} model.has_many field_name, options # Attribute for mass assignment attr = field_name.to_s.singularize + "_ids" model.attr_accessible *(model.attr_accessible[:default].to_a << attr) puts " - has_many :#{field_name}, #{options.inspect}" end
add_has_many_through_behaviour(model, description)
click to toggle source
TODO: maybe check the intermediate class and depending upon the fact it contains a primary key, if only 2 fields
exist. It may be a habtm instead of has_many :through...
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 165 def add_has_many_through_behaviour(model, description) field_name = description[:class].list_name.to_sym options = {through: description[:middle_class].list_name.to_sym, source: description[:class].list_name.singularize} model.has_many field_name, options # Attribute for mass assignment attr = field_name.to_s.singularize + "_ids" model.attr_accessible *(model.attr_accessible[:default].to_a << attr) puts " - has_many :#{field_name}, #{options.inspect}" end
add_has_one_behaviour(model, description)
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 175 def add_has_one_behaviour(model, description) field_name = description[:class].list_name.singularize.to_sym options = {class_name: description[:class].name} model.has_one field_name, options puts " - has_one :#{field_name}, #{options.inspect}" end
analyses_has_many_association(model, description)
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 126 def analyses_has_many_association(model, description) # If one day I figure out how to determine if a has_many relation could be a has_one, # should be implemented here... Doesn't look like solvable... false end
analyses_has_many_through_association(model, associations)
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 102 def analyses_has_many_through_association(model, associations) # As there are multiple belongs_to in this class, all combinations # should lead to a has_many :through # Wow, Ruby rocks !! associations.combination(2).each do |left, right| @alterations[left[:class]][:has_many_through] ||= [] @alterations[right[:class]][:has_many_through] ||= [] @alterations[left[:class]][:has_many_through] << { self_key: left[:key], key: right[:key], middle_class: model, class: right[:class] } @alterations[right[:class]][:has_many_through] << { self_key: right[:key], key: left[:key], middle_class: model, class: left[:class] } end end
apply_alterations()
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 133 def apply_alterations @alterations.each do |model, alterations| puts "Processing alterations for #{model.list_name}" alterations.each do |association_type, associations| associations.each do |description| method_name = "add_#{association_type}_behaviour" self.send method_name, model, description end end end end
discover_has_many_through_from_belongs_to()
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 29 def discover_has_many_through_from_belongs_to puts "Has_many_through analysis started." @alterations.each do |model, alterations| alterations[:has_many_though] ||= [] alterations.each do |association_type, associations| next unless association_type == :belongs_to # If there is only one belongs_to, there cannot be a has_many_through next if associations.size < 2 analyses_has_many_through_association model, associations end end ensure puts "Has_many_though analysis completed." end
introspect_belongs_to()
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 44 def introspect_belongs_to puts "Belongs_to analysis started." # scoped_table_names_hash is a hash with keys made of table names (both scoped and unscoped) # and the values are the associated classes. scoped_table_names_hash = Hash[@domain.scoped_table_names.zip @domain.model_classes] scoped_table_names_hash.merge! Hash[@domain.table_names.zip @domain.model_classes] @klasses.each do |klass| @alterations[klass] ||= {} # Find attributes ending by "_id" klass.attribute_names.grep(KEY_IDENTIFIER) do |attr_name| if klass.columns_hash[attr_name].type == :integer # Check if there is a table in the domain that may be linked to this field candidate_table_name = attr_name.gsub(KEY_IDENTIFIER, '').pluralize candidate_target_class = scoped_table_names_hash[candidate_table_name] # Creates a belongs_to relation if scoped_table_names_hash.keys.include? candidate_table_name @alterations[klass][:belongs_to] ||= [] @alterations[klass][:belongs_to] << { key: attr_name, class: candidate_target_class } # and the reverse, by default has_many @alterations[candidate_target_class] ||= {} @alterations[candidate_target_class][:has_many] ||= [] @alterations[candidate_target_class][:has_many] << { class: klass } end end end end ensure puts "Belongs_to analysis completed." end
verify_if_has_many_relations_could_be_actually_has_one()
click to toggle source
# File lib/database_introspection/dynamic_model/relations_analyser.rb, line 80 def verify_if_has_many_relations_could_be_actually_has_one puts "Has_many analysis started." @alterations.each do |model, alterations| alterations[:has_one] ||= [] alterations.each do |association_type, associations| next unless association_type == :has_many associations.map! do |description| if analyses_has_many_association model, description # This is actually a has_one alterations[:has_one] << description nil else description end end associations.compact! end end ensure puts "Has_many analysis completed." end