lib/rom/repository.rb in rom-repository-1.4.0 vs lib/rom/repository.rb in rom-repository-2.0.0.beta1

- old
+ new

@@ -1,14 +1,9 @@ require 'dry/core/deprecations' require 'rom/initializer' require 'rom/repository/class_interface' -require 'rom/repository/mapper_builder' -require 'rom/repository/relation_proxy' -require 'rom/repository/command_compiler' - -require 'rom/repository/changeset' require 'rom/repository/session' module ROM # Abstract repository class to inherit from # @@ -53,17 +48,10 @@ # # @see Repository::Root # # @api public class Repository - # Mapping for supported changeset classes used in #changeset(type => relation) method - CHANGESET_TYPES = { - create: Changeset::Create, - update: Changeset::Update, - delete: Changeset::Delete - }.freeze - extend ClassInterface extend Initializer extend Dry::Core::ClassAttributes # @!method self.auto_struct @@ -84,10 +72,15 @@ # @!method self.auto_struct # Get or set struct namespace defines :struct_namespace + # @!method self.relation_reader + # Get or set relation reader module + # @return [RelationReader] + defines :relation_reader + struct_namespace ROM::Struct # @!attribute [r] container # @return [ROM::Container] The container used to set up a repo param :container, allow: ROM::Container @@ -102,181 +95,21 @@ # @!attribute [r] relations # @return [RelationRegistry] The relation proxy registry used by a repo attr_reader :relations - # @!attribute [r] mappers - # @return [MapperBuilder] The auto-generated mappers for repo relations - attr_reader :mappers - - # @!attribute [r] commmand_compiler - # @return [Method] Function for compiling commands bound to a repo instance - attr_reader :command_compiler - # Initializes a new repo by establishing configured relation proxies from # the passed container # # @param container [ROM::Container] The rom container with relations and optional commands # - # @api public - def initialize(container, opts = EMPTY_HASH) + # @api private + def initialize(container, options = EMPTY_HASH) super - - @mappers = MapperBuilder.new(struct_namespace: struct_namespace) - - @relations = RelationRegistry.new do |registry, relations| - self.class.relations.each do |name| - relation = container.relations[name] - relation = relation.with(mappers: container.mappers[name]) if container.mappers.key?(name) - - proxy = RelationProxy.new( - relation, name: name, mappers: mappers, registry: registry, auto_struct: auto_struct - ) - - instance_variable_set("@#{name}", proxy) - - relations[name] = proxy - end - end - - @command_compiler = method(:command) + @relations = {} end - # Return a command for a relation - # - # @overload command(type, relation) - # Returns a command for a relation - # - # @example - # repo.command(:create, repo.users) - # - # @param type [Symbol] The command type (:create, :update or :delete) - # @param relation [RelationProxy] The relation for which command should be built for - # - # @overload command(options) - # Builds a command for a given relation identifier - # - # @example - # repo.command(create: :users) - # - # @param options [Hash<Symbol=>Symbol>] A type => rel_name map - # - # @overload command(rel_name) - # Returns command registry for a given relation identifier - # - # @example - # repo.command(:users)[:my_custom_command] - # - # @param rel_name [Symbol] The relation identifier from the container - # - # @return [CommandRegistry] - # - # @overload command(rel_name, &block) - # Yields a command graph composer for a given relation identifier - # - # @param rel_name [Symbol] The relation identifier from the container - # - # @return [ROM::Command] - # - # @api public - def command(*args, **opts, &block) - all_args = args + opts.to_a.flatten - - if all_args.size > 1 - commands.fetch_or_store(all_args.hash) do - compile_command(*args, **opts) - end - else - container.command(*args, &block) - end - end - - # Return a changeset for a relation - # - # @overload changeset(name, attributes) - # Return a create changeset for a given relation identifier - # - # @example - # repo.changeset(:users, name: "Jane") - # - # @param name [Symbol] The relation container identifier - # @param attributes [Hash] - # - # @return [Changeset::Create] - # - # @overload changeset(name, primary_key, attributes) - # Return an update changeset for a given relation identifier - # - # @example - # repo.changeset(:users, 1, name: "Jane Doe") - # - # @param name [Symbol] The relation container identifier - # @param restriction_arg [Object] The argument passed to restricted view - # - # @return [Changeset::Update] - # - # @overload changeset(changeset_class) - # Return a changeset object using provided class - # - # @example - # repo.changeset(NewUserChangeset).data(attributes) - # - # @param [Class] changeset_class Custom changeset class - # - # @return [Changeset] - # - # @overload changeset(opts) - # Return a changeset object using provided changeset type and relation - # - # @example - # repo.changeset(delete: repo.users.where { id > 10 }) - # - # @param [Hash<Symbol=>Relation] opts Command type => Relation config - # - # @return [Changeset] - # - # @api public - def changeset(*args) - opts = { command_compiler: command_compiler } - - if args.size == 2 - name, data = args - elsif args.size == 3 - name, pk, data = args - elsif args.size == 1 - if args[0].is_a?(Class) - klass = args[0] - - if klass < Changeset - return klass.new(relations[klass.relation], opts) - else - raise ArgumentError, "+#{klass.name}+ is not a Changeset subclass" - end - else - type, relation = args[0].to_a[0] - end - else - raise ArgumentError, 'Repository#changeset accepts 1-3 arguments' - end - - if type - klass = CHANGESET_TYPES.fetch(type) { - raise ArgumentError, "+#{type.inspect}+ is not a valid changeset type. Must be one of: #{CHANGESET_TYPES.keys.inspect}" - } - - klass.new(relation, opts) - else - relation = relations[name] - - if pk - Changeset::Update.new(relation.by_pk(pk), opts.update(__data__: data)) - else - Changeset::Create.new(relation, opts.update(__data__: data)) - end - end - end - # Open a database transaction # # @example commited transaction # user = transaction do |t| # create(changeset(name: 'Jane')) @@ -303,11 +136,11 @@ # # @return [String] # # @api public def inspect - %(#<#{self.class} relations=[#{self.class.relations.map(&:inspect).join(' ')}]>) + %(#<#{self.class} struct_namespace=#{struct_namespace} auto_struct=#{auto_struct}>) end # Start a session for multiple changesets # # TODO: this is partly done, needs tweaks in changesets so that we can gather @@ -318,49 +151,9 @@ # @api public def session(&block) session = Session.new(self) yield(session) transaction { session.commit! } - end - - private - - # Local command cache - # - # @api private - def commands - @__commands__ ||= Concurrent::Map.new - end - - # Build a new command or return existing one - # - # @api private - def compile_command(*args, mapper: nil, use: nil, **opts) - type, name = args + opts.to_a.flatten(1) - - relation = name.is_a?(Symbol) ? relations[name] : name - - ast = relation.to_ast - adapter = relations[relation.name].adapter - - if mapper - mapper_instance = container.mappers[relation.name.relation][mapper] - elsif mapper.nil? - mapper_instance = mappers[ast] - end - - command = CommandCompiler[container, type, adapter, ast, use, opts] - - if mapper_instance - command >> mapper_instance - else - command.new(relation) - end - end - - # @api private - def map_tuple(relation, tuple) - relations[relation.name].mapper.([tuple]).first end end end require 'rom/repository/root'