class Terrestrial::Configurations::ConventionalConfiguration

Attributes

clock[R]
datastore[R]
dirty_map[R]
identity_map[R]
inflector[R]
mappings[R]
overrides[R]

Public Class Methods

new(datastore:, clock:, dirty_map:, identity_map:, inflector: Inflector.new) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 30
def initialize(datastore:, clock:, dirty_map:, identity_map:, inflector: Inflector.new)
  @datastore = datastore
  @inflector = inflector
  @dirty_map = dirty_map
  @identity_map = identity_map
  @clock = clock

  @overrides = {}
  @subset_queries = {}
  @associations_by_mapping = {}
end

Public Instance Methods

add_association(mapping_name, type, options) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 74
def add_association(mapping_name, type, options)
  @associations_by_mapping.fetch(mapping_name).push([type, options])
end
add_override(mapping_name, attrs) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 59
def add_override(mapping_name, attrs)
  overrides = @overrides.fetch(mapping_name, {}).merge(attrs)

  @overrides.store(mapping_name, overrides)
end
add_subset(mapping_name, subset_name, block) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 65
def add_subset(mapping_name, subset_name, block)
  @subset_queries.store(
    mapping_name,
    @subset_queries.fetch(mapping_name, {}).merge(
      subset_name => block,
    )
  )
end
setup_mapping(mapping_name, &block) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 44
def setup_mapping(mapping_name, &block)
  @associations_by_mapping[mapping_name] ||= []
  @overrides[mapping_name] ||= {}

  block && block.call(
    MappingConfigOptionsProxy.new(self, mapping_name)
  )

  self
end

Private Instance Methods

all_available_fields(relation_name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 177
def all_available_fields(relation_name)
  datastore.relation_fields(relation_name)
end
association_configurator(mappings, mapping_name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 80
def association_configurator(mappings, mapping_name)
  ConventionalAssociationConfiguration.new(
    inflector,
    datastore,
    mapping_name,
    mappings,
  )
end
build_mapping(name:, relation_name:, primary_key:, use_database_id:, database_id_setter:, database_owned_fields_setter_map:, database_default_fields_setter_map:, updated_at_field:, updated_at_setter:, created_at_field:, created_at_setter:, factory:, serializer:, fields:, associations:, subsets:) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 202
def build_mapping(name:, relation_name:, primary_key:, use_database_id:, database_id_setter:, database_owned_fields_setter_map:, database_default_fields_setter_map:, updated_at_field:, updated_at_setter:, created_at_field:, created_at_setter:, factory:, serializer:, fields:, associations:, subsets:)
  if use_database_id
    database_id_setter ||= object_setter(primary_key.first)
  end
  if created_at_field
    created_at_field = created_at_field == Default ? :created_at : created_at_field
    created_at_setter ||= object_setter(created_at_field)
  end
  if updated_at_field
    updated_at_field = updated_at_field == Default ? :updated_at : updated_at_field
    updated_at_setter ||= object_setter(updated_at_field)
  end

  timestamp_observer = TimestampObserver.new(
    clock,
    dirty_map,
    created_at_field,
    created_at_setter,
    updated_at_field,
    updated_at_setter,
  )

  database_owned_field_observers = database_owned_fields_setter_map.map { |field, setter|
    setter ||= ->(object, value) { object.send("#{field}=", value) }
    ArbitraryDatabaseOwnedValueObserver.new(field, setter)
  }

  database_default_field_observers = database_default_fields_setter_map.map { |field, setter|
    setter ||= ->(object, value) { object.send("#{field}=", value) }
    ArbitraryDatabaseDefaultValueObserver.new(field, setter)
  }

  observers = [
    use_database_id && DatabaseIDObserver.new(database_id_setter),
    (created_at_field || updated_at_field) && timestamp_observer,
    *database_owned_field_observers,
    *database_default_field_observers,
  ].select(&:itself)

  RelationMapping.new(
    name: name,
    namespace: relation_name,
    primary_key: primary_key,
    factory: factory,
    serializer: serializer,
    fields: fields,
    database_owned_fields: database_owned_fields_setter_map.keys,
    database_default_fields: database_default_fields_setter_map.keys,
    associations: associations,
    subsets: subsets,
    observers: observers,
  )
end
class_name(name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 285
def class_name(name)
  inflector.classify(name)
end
class_to_factory(klass) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 273
def class_to_factory(klass)
  if klass.ancestors.include?(Struct)
    StructFactory.new(klass)
  else
    klass.method(:new)
  end
end
class_with_same_name_as_mapping_factory(name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 264
def class_with_same_name_as_mapping_factory(name)
  target_class = string_to_class(name)
  ClassFactory.new(target_class)
end
consolidate_overrides(opts) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 161
def consolidate_overrides(opts)
  new_opts = opts.dup

  if new_opts.has_key?(:class_name)
    new_opts.merge!(factory: string_to_factory(new_opts.fetch(:class_name)))
    new_opts.delete(:class_name)
  end

  if new_opts.has_key?(:class)
    new_opts.merge!(factory: class_to_factory(new_opts.fetch(:class)))
    new_opts.delete(:class)
  end

  new_opts
end
default_mapping_args(table_name, mapping_name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 130
def default_mapping_args(table_name, mapping_name)
  {
    name: mapping_name,
    relation_name: table_name,
    fields: all_available_fields(table_name),
    primary_key: get_primary_key(table_name),
    use_database_id: false,
    database_id_setter: nil,
    database_owned_fields_setter_map: {},
    database_default_fields_setter_map: {},
    updated_at_field: nil,
    updated_at_setter: nil,
    created_at_field: nil,
    created_at_setter: nil,
    factory: ok_if_class_is_not_defined_factory(mapping_name),
    serializer: hash_coercion_serializer,
    associations: {},
    subsets: subset_queries_proxy(@subset_queries.fetch(mapping_name, {})),
  }
end
generate_associations_config(mappings) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 111
def generate_associations_config(mappings)
  # TODO: the ConventionalAssociationConfiguration takes all the mappings
  # as a dependency and then sends mutating messages to them.
  # This mutation based approach was originally a spike but now just
  # seems totally bananas!
  @associations_by_mapping.each do |mapping_name, association_data|
    association_data.each do |(assoc_type, assoc_args)|
      association = association_configurator(mappings, mapping_name)
        .public_send(assoc_type, *assoc_args)

      name = assoc_args.fetch(0)
      mappings.fetch(mapping_name).add_association(name, association)
      associated_mapping = mappings.fetch(association.mapping_name)

      associated_mapping.register_foreign_key(association.outgoing_foreign_keys)
    end
  end
end
generate_mappings() click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 89
def generate_mappings
  custom_mappings = @overrides.map { |mapping_name, overrides|
    [mapping_name, {relation_name: mapping_name}.merge(consolidate_overrides(overrides))]
  }

  Hash[
    (custom_mappings).map { |(mapping_name, overrides)|
      table_name = overrides.fetch(:relation_name) { raise no_table_error(mapping_name) }

      [
        mapping_name,
        build_mapping(
          **default_mapping_args(table_name, mapping_name).merge(overrides)
        ),
      ]
    }
  ]
  .tap { |mappings|
    generate_associations_config(mappings)
  }
end
get_primary_key(table_name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 181
def get_primary_key(table_name)
  datastore.schema(table_name)
    .select { |field_name, properties|
      properties.fetch(:primary_key, false)
    }
    .map { |field_name, _| field_name }
end
hash_coercion_serializer() click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 194
def hash_coercion_serializer
  HashCoercionSerializer.new
end
no_table_error(table_name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 289
def no_table_error(table_name)
  TableNameNotSpecifiedError.new(table_name)
end
object_setter(field_name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 256
def object_setter(field_name)
  SetterMethodCaller.new(field_name)
end
ok_if_class_is_not_defined_factory(name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 269
def ok_if_class_is_not_defined_factory(name)
  LazyClassLookupFactory.new(class_name(name))
end
overrides_for_table(table_name) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 151
def overrides_for_table(table_name)
  overrides = @overrides.values.detect { |config|
    table_name == config.fetch(:relation_name, nil)
  } || {}

  { relation_name: table_name }.merge(
    consolidate_overrides(overrides)
  )
end
simple_setter_method_caller(primary_key) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 260
def simple_setter_method_caller(primary_key)
  SetterMethodCaller.new(primary_key.first)
end
string_to_class(string) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 281
def string_to_class(string)
  Object.const_get(class_name(string))
end
subset_queries_proxy(subset_map) click to toggle source
# File lib/terrestrial/configurations/conventional_configuration.rb, line 198
def subset_queries_proxy(subset_map)
  SubsetQueriesProxy.new(subset_map)
end
tables() click to toggle source

TODO: inconsisent naming

# File lib/terrestrial/configurations/conventional_configuration.rb, line 190
def tables
  datastore.relations
end