lib/terrestrial/configurations/conventional_association_configuration.rb in terrestrial-0.3.0 vs lib/terrestrial/configurations/conventional_association_configuration.rb in terrestrial-0.5.0

- old
+ new

@@ -8,26 +8,26 @@ require "terrestrial/collection_mutability_proxy" require "terrestrial/lazy_collection" require "terrestrial/lazy_object_proxy" class ConventionalAssociationConfiguration - def initialize(mapping_name, mappings, datastore) - @local_mapping_name = mapping_name - @mappings = mappings - @local_mapping = mappings.fetch(local_mapping_name) + def initialize(inflector, datastore, mapping_name, mappings) + @inflector = inflector @datastore = datastore + @target_mapping = mappings.fetch(mapping_name) + @mappings = mappings end - attr_reader :local_mapping_name, :local_mapping, :mappings, :datastore - private :local_mapping_name, :local_mapping, :mappings, :datastore + attr_reader :inflector, :datastore, :target_mapping, :mappings + private :inflector, :datastore, :target_mapping, :mappings DEFAULT = Module.new def has_many(association_name, key: DEFAULT, foreign_key: DEFAULT, mapping_name: DEFAULT, order_fields: DEFAULT, order_direction: DEFAULT) defaults = { mapping_name: association_name, - foreign_key: [INFLECTOR.singularize(local_mapping_name), "_id"].join.to_sym, + foreign_key: [singular_name, "_id"].join.to_sym, key: :id, order_fields: [], order_direction: "ASC", } @@ -43,21 +43,18 @@ config = defaults.merge(specified) associated_mapping_name = config.fetch(:mapping_name) associated_mapping = mappings.fetch(associated_mapping_name) - local_mapping.add_association( - association_name, - has_many_mapper(**config) - ) + has_many_mapper(**config) end def belongs_to(association_name, key: DEFAULT, foreign_key: DEFAULT, mapping_name: DEFAULT) defaults = { key: :id, foreign_key: [association_name, "_id"].join.to_sym, - mapping_name: INFLECTOR.pluralize(association_name).to_sym, + mapping_name: pluralize(association_name).to_sym, } specified = { mapping_name: mapping_name, foreign_key: foreign_key, @@ -69,23 +66,21 @@ config = defaults.merge(specified) associated_mapping_name = config.fetch(:mapping_name) associated_mapping = mappings.fetch(associated_mapping_name) - local_mapping.add_association( - association_name, - belongs_to_mapper(**config) - ) + belongs_to_mapper(**config) end - def has_many_through(association_name, key: DEFAULT, foreign_key: DEFAULT, mapping_name: DEFAULT, through_mapping_name: DEFAULT, association_key: DEFAULT, association_foreign_key: DEFAULT, order_fields: DEFAULT, order_direction: DEFAULT) + def has_many_through(association_name, key: DEFAULT, foreign_key: DEFAULT, mapping_name: DEFAULT, through_table_name: DEFAULT, association_key: DEFAULT, association_foreign_key: DEFAULT, order_fields: DEFAULT, order_direction: DEFAULT) + # TODO: join_dataset as mutually exclusive option with join_table_name defaults = { mapping_name: association_name, key: :id, association_key: :id, - foreign_key: [INFLECTOR.singularize(local_mapping_name), "_id"].join.to_sym, - association_foreign_key: [INFLECTOR.singularize(association_name), "_id"].join.to_sym, + foreign_key: [singular_name, "_id"].join.to_sym, + association_foreign_key: [singularize(association_name), "_id"].join.to_sym, order_fields: [], order_direction: "ASC", } specified = { @@ -99,30 +94,34 @@ }.reject { |_k,v| v == DEFAULT } config = defaults.merge(specified) + associated_mapping = mappings.fetch(config.fetch(:mapping_name)) + default_through_table_name = [associated_mapping.name, target_mapping.name].sort.join("_to_").to_sym - if through_mapping_name == DEFAULT - through_mapping_name = [ - associated_mapping.name, - local_mapping.name, - ].sort.join("_to_").to_sym + if through_table_name == DEFAULT + through_table_name = default_through_table_name end - join_table_name = mappings.fetch(through_mapping_name).namespace - config = config - .merge( - through_mapping_name: through_mapping_name, - through_dataset: datastore[join_table_name.to_sym], - ) + join_mapping = create_virtual_mapping( + default_mapping_name: default_through_table_name, + namespace: through_table_name, + primary_key: [config[:foreign_key], config[:association_foreign_key]], + ) - local_mapping.add_association( - association_name, - has_many_through_mapper(**config) + mappings[join_mapping.name] = join_mapping + + join_dataset = datastore[through_table_name.to_sym] + + config = config.merge( + join_mapping_name: join_mapping.name, + join_dataset: join_dataset, ) + + has_many_through_mapper(**config) end private def has_many_mapper(mapping_name:, key:, foreign_key:, order_fields:, order_direction:) @@ -142,23 +141,42 @@ key: key, proxy_factory: single_object_proxy_factory, ) end - def has_many_through_mapper(mapping_name:, key:, foreign_key:, association_key:, association_foreign_key:, through_mapping_name:, through_dataset:, order_fields:, order_direction:) + def has_many_through_mapper(mapping_name:, key:, foreign_key:, association_key:, association_foreign_key:, join_mapping_name:, join_dataset:, order_fields:, order_direction:) ManyToManyAssociation.new( mapping_name: mapping_name, - join_mapping_name: through_mapping_name, + join_mapping_name: join_mapping_name, + join_dataset: join_dataset, # TODO: this dataset is not used key: key, foreign_key: foreign_key, association_key: association_key, association_foreign_key: association_foreign_key, proxy_factory: collection_proxy_factory, order: query_order(order_fields, order_direction), ) end + def create_virtual_mapping(default_mapping_name:, namespace:, primary_key:) + mapping_name = "__generated_virtual_mapping_#{default_mapping_name}" + + RelationMapping.new( + name: mapping_name, + namespace: namespace, + primary_key: primary_key, + factory: ->(*_) { }, + serializer: :to_h.to_proc, + fields: [], + associations: [], + subsets: [], + database_owned_fields: [], + database_default_fields: [], + observers: [], + ) + end + def single_object_proxy_factory ->(query:, loader:, preloaded_data:) { LazyObjectProxy.new( ->{ loader.call(query.first) }, preloaded_data, @@ -178,9 +196,21 @@ } end def query_order(fields, direction) QueryOrder.new(fields: fields, direction: direction) + end + + def singular_name + inflector.singularize(target_mapping.name) + end + + def singularize(string) + inflector.singularize(string) + end + + def pluralize(string) + inflector.pluralize(string) end end end end