Top Level Namespace
Defined Under Namespace
Modules: Automodel
Instance Method Summary collapse
-
#automodel(spec) ⇒ ActiveRecord::Base
The main (really only) entrypoint for the Automodel gem.
Instance Method Details
#automodel(spec) ⇒ ActiveRecord::Base
The main (really only) entrypoint for the Automodel gem. This is the method the end-user calls to trigger a database scrape and model generation.
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 67 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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/automodel.rb', line 40 def automodel(spec) ## Build out a connection spec Hash from the given value. ## resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver connection_spec = resolver.new(ActiveRecord::Base.configurations).resolve(spec).symbolize_keys ## We need a base class for all of the models we're about to create, but don't want to pollute ## ActiveRecord::Base's own connection pool, so we'll need a subclass. This will serve as both ## our base class for new models and as the connection pool handler. We're defining it with names ## that reflect both uses just to keep the code more legible. ## connection_handler_name = "CH_#{SecureRandom.uuid.delete('-')}" base_class_for_new_models = connection_handler = Class.new(ActiveRecord::Base) Automodel::Helpers.register_class(connection_handler, as: connection_handler_name, within: :'Automodel::Connectors') ## Establish a connection with the given params. ## connection_handler.establish_connection(connection_spec) ## Map out the table structures. ## tables = Automodel::Helpers.map_tables(connection_handler, subschema: connection_spec[:subschema]) ## Safeguard against class name collisions. ## defined_names = Array((connection_spec[:namespace] || :Kernel).to_s.safe_constantize&.constants) potential_names = tables.map { |table| table[:model_name].to_sym } name_collisions = defined_names & potential_names if name_collisions.present? connection_handler.connection_pool.disconnect! Automodel::Connectors.send(:remove_const, connection_handler_name) raise Automodel::NameCollisionError, name_collisions end ## Define the table models. ## tables.each do |table| table[:model] = Class.new(base_class_for_new_models) do ## We can't assume table properties confom to any standard. ## self.table_name = table[:name] self.primary_key = table[:primary_key] ## Don't allow `#find` for tables with a composite primary key. ## def find(*args) raise Automodel::FindOnCompoundPrimaryKeyError if table[:composite_primary_key] super end ## Create railsy column name aliases whenever possible. ## table[:columns].each do |column| railsy_name = Automodel::Helpers.railsy_column_name(column) unless table[:column_aliases].key? railsy_name table[:column_aliases][railsy_name] = column alias_attribute(railsy_name, column.name) end end end ## Register the model class. ## Automodel::Helpers.register_class(table[:model], as: table[:model_name], within: connection_spec[:namespace]) end ## With all models registered, we can safely declare relationships. ## tables.map { |table| table[:foreign_keys] }.flatten.each do |fk| from_table = tables.find { |table| table[:base_name] == fk.from_table.delete('"') } next unless from_table.present? to_table = tables.find { |table| table[:base_name] == fk.to_table.delete('"') } next unless to_table.present? association_setup = <<~END_OF_HEREDOC belongs_to #{to_table[:base_name].to_sym.inspect}, class_name: #{to_table[:model].to_s.inspect}, primary_key: #{fk.[:primary_key].to_sym.inspect}, foreign_key: #{fk.[:column].to_sym.inspect} alias #{to_table[:model_name].underscore.to_sym.inspect} #{to_table[:base_name].to_sym.inspect} END_OF_HEREDOC from_table[:model].class_eval(association_setup, __FILE__, __LINE__) end ## There's no obvious value we can return that would be of any use, except maybe the base class, ## in case the end user wants to procure a list of all the models (via `#subclasses`). ## base_class_for_new_models end |