diff --git a/lib/rom/associations/definitions/abstract.rb b/lib/rom/associations/definitions/abstract.rb index d57cbd026..638f02855 100644 --- a/lib/rom/associations/definitions/abstract.rb +++ b/lib/rom/associations/definitions/abstract.rb @@ -7,6 +7,7 @@ require "rom/initializer" require "rom/relation/name" require "rom/associations/through_identifier" +require "rom/support/inflector" module ROM module Associations @@ -94,7 +95,12 @@ def self.process_options(target, options) through = options[:through] if through - options[:through] = ThroughIdentifier[through, target.relation, options[:assoc]] + options[:through] = ThroughIdentifier[ + through, + target.relation, + options[:assoc], + inflector: options.fetch(:inflector, Inflector) + ] end options[:name] = target.relation diff --git a/lib/rom/associations/through_identifier.rb b/lib/rom/associations/through_identifier.rb index 8df16e187..33bebc320 100644 --- a/lib/rom/associations/through_identifier.rb +++ b/lib/rom/associations/through_identifier.rb @@ -16,13 +16,13 @@ class ThroughIdentifier attr_reader :assoc_name # @api private - def self.[](source, target, assoc_name = nil) - new(source, target, assoc_name || default_assoc_name(target)) + def self.[](source, target, assoc_name = nil, inflector: Inflector) + new(source, target, assoc_name || default_assoc_name(target, inflector: inflector)) end # @api private - def self.default_assoc_name(relation) - Inflector.singularize(relation).to_sym + def self.default_assoc_name(relation, inflector:) + inflector.singularize(relation).to_sym end # @api private diff --git a/lib/rom/command_compiler.rb b/lib/rom/command_compiler.rb index d6d78931e..f5c4aba1b 100644 --- a/lib/rom/command_compiler.rb +++ b/lib/rom/command_compiler.rb @@ -66,6 +66,11 @@ def self.registry # @return [Cache] local cache instance option :cache, default: -> { Cache.new } + # @!attribute [r] inflector + # @return [Dry::Inflector] String inflector + # @api private + option :inflector, default: -> { Inflector } + # Return a specific command type for a given adapter and relation AST # # This class holds its own registry where all generated commands are being @@ -102,7 +107,8 @@ def call(*args) command = ROM::Commands::Graph.build(registry, graph_opts) if command.graph? - CommandProxy.new(command) + root = inflector.singularize(command.name.relation).to_sym + CommandProxy.new(command, root) elsif command.lazy? command.unwrap else @@ -114,7 +120,7 @@ def call(*args) # @api private def type - @_type ||= Commands.const_get(Inflector.classify(id))[adapter] + @_type ||= Commands.const_get(inflector.classify(id))[adapter] rescue NameError nil end @@ -139,7 +145,7 @@ def visit_relation(node, parent_relation = nil) if meta[:combine_type] == :many name else - {Inflector.singularize(name).to_sym => name} + {inflector.singularize(name).to_sym => name} end mapping = @@ -190,7 +196,7 @@ def visit_attribute(*_args) def register_command(rel_name, type, rel_meta, parent_relation = nil) relation = relations[rel_name] - type.create_class(rel_name, type) do |klass| + type.create_class(rel_name, type, inflector: inflector) do |klass| klass.result(rel_meta.fetch(:combine_type, result)) meta.each do |name, value| @@ -237,7 +243,7 @@ def setup_associates(klass, relation, _meta, parent_relation) if relation.associations.key?(parent_relation) parent_relation else - singular_name = Inflector.singularize(parent_relation).to_sym + singular_name = inflector.singularize(parent_relation).to_sym singular_name if relation.associations.key?(singular_name) end diff --git a/lib/rom/command_proxy.rb b/lib/rom/command_proxy.rb index f0c67372b..7551ac014 100644 --- a/lib/rom/command_proxy.rb +++ b/lib/rom/command_proxy.rb @@ -8,10 +8,12 @@ module ROM # # @api private class CommandProxy - attr_reader :command, :root + attr_reader :command + + attr_reader :root # @api private - def initialize(command, root = Inflector.singularize(command.name.relation).to_sym) + def initialize(command, root) @command = command @root = root end @@ -23,7 +25,7 @@ def call(input) # @api private def >>(other) - self.class.new(command >> other) + self.class.new(command >> other, root) end # @api private diff --git a/lib/rom/commands/class_interface.rb b/lib/rom/commands/class_interface.rb index cbac6f300..f55a50a26 100644 --- a/lib/rom/commands/class_interface.rb +++ b/lib/rom/commands/class_interface.rb @@ -85,9 +85,9 @@ def build(relation, **options) # @return [Class, Object] # # @api public - def create_class(name, type, &block) + def create_class(name, type, inflector: Inflector, &block) klass = Dry::Core::ClassBuilder - .new(name: "#{Inflector.classify(type)}[:#{name}]", parent: type) + .new(name: "#{inflector.classify(type)}[:#{name}]", parent: type) .call if block diff --git a/lib/rom/configuration.rb b/lib/rom/configuration.rb index b94337218..88cb5d0d0 100644 --- a/lib/rom/configuration.rb +++ b/lib/rom/configuration.rb @@ -6,6 +6,7 @@ require "rom/setup" require "rom/configuration_dsl" require "rom/support/notifications" +require "rom/support/inflector" module ROM class Configuration @@ -38,7 +39,7 @@ class Configuration def_delegators :@setup, :register_relation, :register_command, :register_mapper, :register_plugin, :command_classes, :mapper_classes, - :auto_registration + :auto_registration, :inflector, :inflector= def_delegators :@environment, :gateways, :gateways_map, :configure, :config diff --git a/lib/rom/configuration_dsl/command.rb b/lib/rom/configuration_dsl/command.rb index 90feadfa9..137c4d4e4 100644 --- a/lib/rom/configuration_dsl/command.rb +++ b/lib/rom/configuration_dsl/command.rb @@ -16,10 +16,11 @@ class Command # @api private def self.build_class(name, relation, options = EMPTY_HASH, &block) type = options.fetch(:type) { name } - command_type = Inflector.classify(type) + inflector = options.fetch(:inflector) { Inflector } + command_type = inflector.classify(type) adapter = options.fetch(:adapter) parent = ROM::Command.adapter_namespace(adapter).const_get(command_type) - class_name = generate_class_name(adapter, command_type, relation) + class_name = generate_class_name(adapter, command_type, relation, inflector) Dry::Core::ClassBuilder.new(name: class_name, parent: parent).call do |klass| klass.register_as(name) @@ -31,11 +32,11 @@ def self.build_class(name, relation, options = EMPTY_HASH, &block) # Create a command subclass name based on adapter, type and relation # # @api private - def self.generate_class_name(adapter, command_type, relation) + def self.generate_class_name(adapter, command_type, relation, inflector) pieces = ["ROM"] - pieces << Inflector.classify(adapter) + pieces << inflector.classify(adapter) pieces << "Commands" - pieces << "#{command_type}[#{Inflector.classify(relation)}s]" + pieces << "#{command_type}[#{inflector.classify(relation)}s]" pieces.join("::") end end diff --git a/lib/rom/configuration_dsl/relation.rb b/lib/rom/configuration_dsl/relation.rb index bef19c7c8..0a175ce57 100644 --- a/lib/rom/configuration_dsl/relation.rb +++ b/lib/rom/configuration_dsl/relation.rb @@ -15,7 +15,8 @@ class Relation # # @api private def self.build_class(name, options = EMPTY_HASH) - class_name = "ROM::Relation[#{Inflector.camelize(name)}]" + inflector = options.fetch(:inflector) { Inflector } + class_name = "ROM::Relation[#{inflector.camelize(name)}]" adapter = options.fetch(:adapter) Dry::Core::ClassBuilder.new(name: class_name, parent: ROM::Relation[adapter]).call do |klass| diff --git a/lib/rom/create_container.rb b/lib/rom/create_container.rb index 3c8f78f1e..40845f64e 100644 --- a/lib/rom/create_container.rb +++ b/lib/rom/create_container.rb @@ -34,7 +34,8 @@ def finalize(environment, setup) mappers: setup.mapper_classes, plugins: setup.plugins, notifications: setup.notifications, - config: environment.config.dup.freeze + config: environment.config.dup.freeze, + inflector: setup.inflector ) finalize.run! diff --git a/lib/rom/relation.rb b/lib/rom/relation.rb index 2e0399b59..0f23c68a6 100644 --- a/lib/rom/relation.rb +++ b/lib/rom/relation.rb @@ -6,6 +6,7 @@ require "rom/constants" require "rom/initializer" require "rom/support/memoizable" +require "rom/support/inflector" require "rom/relation/class_interface" @@ -136,17 +137,26 @@ class Relation # @api public param :dataset + # @!attribute [r] inflector + # @return [Dry::Inflector] String inflector + # @api private + option :inflector, reader: true, default: -> { Inflector } + # @!attribute [r] schema # @return [Schema] relation schema, defaults to class-level canonical # schema (if it was defined) and sets an empty one as # the fallback # @api public - option :schema, default: -> { self.class.schema || self.class.default_schema } + option :schema, default: -> { + self.class.schema || self.class.default_schema(inflector: inflector) + } # @!attribute [r] name # @return [Object] The relation name # @api public - option :name, default: -> { self.class.schema ? self.class.schema.name : self.class.default_name } + option :name, default: -> { + self.class.schema ? self.class.schema.name : self.class.default_name(inflector) + } # @!attribute [r] input_schema # @return [Object#[]] tuple processing function, uses schema or defaults to Hash[] @@ -596,7 +606,7 @@ def foreign_key(name) if attr attr.name else - :"#{Inflector.singularize(name.dataset)}_id" + :"#{inflector.singularize(name.dataset)}_id" end end diff --git a/lib/rom/relation/class_interface.rb b/lib/rom/relation/class_interface.rb index 116d30b32..cb5be35fd 100644 --- a/lib/rom/relation/class_interface.rb +++ b/lib/rom/relation/class_interface.rb @@ -104,14 +104,14 @@ def schema(dataset = nil, as: nil, infer: false, &block) @relation_name = Name[relation, ds_name] - @schema_proc = proc do |*args, &inner_block| + @schema_proc = proc do |**kwargs, &inner_block| schema_dsl.new( relation_name, schema_class: schema_class, attr_class: schema_attr_class, inferrer: schema_inferrer.with(enabled: infer), &block - ).call(*args, &inner_block) + ).call(**kwargs, &inner_block) end end end @@ -294,15 +294,15 @@ def schemas # @return [Name] # # @api private - def default_name - Name[Inflector.underscore(name).tr("/", "_").to_sym] + def default_name(inflector = Inflector) + Name[inflector.underscore(name).tr("/", "_").to_sym] end # @api private - def default_schema(klass = self) + def default_schema(klass = self, inflector: Inflector) klass.schema || if klass.schema_proc - klass.set_schema!(klass.schema_proc.call) + klass.set_schema!(klass.schema_proc.(inflector: inflector)) else klass.schema_class.define(klass.default_name) end diff --git a/lib/rom/schema/associations_dsl.rb b/lib/rom/schema/associations_dsl.rb index f520f968e..44fa94313 100644 --- a/lib/rom/schema/associations_dsl.rb +++ b/lib/rom/schema/associations_dsl.rb @@ -11,18 +11,29 @@ class Schema # This DSL is exposed in `associations do .. end` blocks in schema defintions. # # @api public - class AssociationsDSL < BasicObject + class AssociationsDSL < ::BasicObject + class << self + define_method(:const_missing, ::Object.method(:const_get)) + end + + include Associations::Definitions + # @!attribute [r] source # @return [Relation::Name] The source relation attr_reader :source + # @!attribute [r] inflector + # @return [Dry::Inflector] String inflector + attr_reader :inflector + # @!attribute [r] registry # @return [RelationRegistry] Relations registry from a rom container attr_reader :registry # @api private - def initialize(source, &block) + def initialize(source, inflector = Inflector, &block) @source = source + @inflector = inflector @registry = {} instance_exec(&block) end @@ -61,9 +72,9 @@ def initialize(source, &block) # @api public def one_to_many(target, **options) if options[:through] - many_to_many(target, **options) + many_to_many(target, **options, inflector: inflector) else - add(::ROM::Associations::Definitions::OneToMany.new(source, target, **options)) + add(build(OneToMany, target, options)) end end alias_method :has_many, :one_to_many @@ -88,7 +99,7 @@ def one_to_one(target, **options) if options[:through] one_to_one_through(target, **options) else - add(::ROM::Associations::Definitions::OneToOne.new(source, target, **options)) + add(build(OneToOne, target, options)) end end @@ -101,7 +112,7 @@ def one_to_one(target, **options) # # @api public def one_to_one_through(target, **options) - add(::ROM::Associations::Definitions::OneToOneThrough.new(source, target, **options)) + add(build(OneToOneThrough, target, options)) end # Establish a many-to-many association @@ -118,7 +129,7 @@ def one_to_one_through(target, **options) # # @api public def many_to_many(target, **options) - add(::ROM::Associations::Definitions::ManyToMany.new(source, target, **options)) + add(build(ManyToMany, target, options)) end # Establish a many-to-one association @@ -135,7 +146,7 @@ def many_to_many(target, **options) # # @api public def many_to_one(target, **options) - add(::ROM::Associations::Definitions::ManyToOne.new(source, target, **options)) + add(build(ManyToOne, target, options)) end # Shortcut for many_to_one which sets alias automatically @@ -183,6 +194,10 @@ def call private + def build(definition, target, options) + definition.new(source, target, **options, inflector: inflector) + end + # @api private def add(association) key = association.as || association.name @@ -199,7 +214,7 @@ def add(association) # @api private def dataset_name(name) - Inflector.pluralize(name).to_sym + inflector.pluralize(name).to_sym end end end diff --git a/lib/rom/schema/dsl.rb b/lib/rom/schema/dsl.rb index d429b75de..ba0a2346e 100644 --- a/lib/rom/schema/dsl.rb +++ b/lib/rom/schema/dsl.rb @@ -5,6 +5,7 @@ require "rom/types" require "rom/attribute" require "rom/schema/associations_dsl" +require "rom/support/inflector" module ROM class Schema @@ -56,6 +57,11 @@ class DSL < BasicObject # @return [AssociationDSL] Associations defined within a block attr_reader :associations_dsl + # @!attribute [r] inflector + # @return [Dry::Inflector] String inflector + # @api private + attr_reader :inflector + # @api private def initialize(*, &block) super @@ -115,7 +121,7 @@ def attribute(name, type_or_options, options = EMPTY_HASH) # # @api public def associations(&block) - @associations_dsl = AssociationsDSL.new(relation, &block) + @associations_dsl = AssociationsDSL.new(relation, inflector, &block) end # Builds a representation of the information needed to create an @@ -181,7 +187,9 @@ def app_plugin(plugin, options = ::ROM::EMPTY_HASH) end # @api private - def call(&block) + def call(inflector: Inflector, &block) + @inflector = inflector + instance_exec(&block) if block instance_exec(&definition) if definition @@ -205,7 +213,12 @@ def plugin_options(plugin) # # @api private def opts - opts = {attributes: attributes.values, inferrer: inferrer, attr_class: attr_class} + opts = { + attributes: attributes.values, + inferrer: inferrer, + attr_class: attr_class, + inflector: Inflector + } if associations_dsl {**opts, associations: associations_dsl.call} diff --git a/lib/rom/setup.rb b/lib/rom/setup.rb index 3c270b83b..5d2507979 100644 --- a/lib/rom/setup.rb +++ b/lib/rom/setup.rb @@ -28,6 +28,9 @@ class Setup # @api private attr_reader :notifications + # @api private + attr_accessor :inflector + # @api private def initialize(notifications) @relation_classes = [] @@ -35,6 +38,7 @@ def initialize(notifications) @mapper_classes = [] @plugins = [] @notifications = notifications + @inflector = Inflector end # Enable auto-registration for a given setup object @@ -47,7 +51,7 @@ def initialize(notifications) # # @api public def auto_registration(directory, **options) - auto_registration = AutoRegistration.new(directory, **options) + auto_registration = AutoRegistration.new(directory, inflector: inflector, **options) auto_registration.relations.map { |r| register_relation(r) } auto_registration.commands.map { |r| register_command(r) } auto_registration.mappers.map { |r| register_mapper(r) } @@ -58,21 +62,21 @@ def auto_registration(directory, **options) # # @api private def register_relation(*klasses) - klasses.reduce(@relation_classes, :<<) + @relation_classes.concat(klasses) end # Mapper sub-classes are being registered with this method during setup # # @api private def register_mapper(*klasses) - klasses.reduce(@mapper_classes, :<<) + @mapper_classes.concat(klasses) end # Command sub-classes are being registered with this method during setup # # @api private def register_command(*klasses) - klasses.reduce(@command_classes, :<<) + @command_classes.concat(klasses) end # @api private diff --git a/lib/rom/setup/auto_registration.rb b/lib/rom/setup/auto_registration.rb index eb17ff029..4f56d9ae5 100644 --- a/lib/rom/setup/auto_registration.rb +++ b/lib/rom/setup/auto_registration.rb @@ -21,6 +21,8 @@ class AutoRegistration PathnameType = Types.Constructor(Pathname, &Kernel.method(:Pathname)) + InflectorType = Types.Strict(Dry::Inflector) + DEFAULT_MAPPING = { relations: :relations, mappers: :mappers, @@ -51,6 +53,11 @@ class AutoRegistration ] } + # @!attribute [r] inflector + # @return [Dry::Inflector] String inflector + # @api private + option :inflector, type: InflectorType, default: -> { Inflector } + # Load relation files # # @api private @@ -84,19 +91,25 @@ def load_entities(entity) case namespace when String AutoRegistrationStrategies::CustomNamespace.new( - namespace: namespace, file: file, directory: directory + namespace: namespace, + file: file, + directory: directory, + inflector: inflector ).call when TrueClass AutoRegistrationStrategies::WithNamespace.new( - file: file, directory: directory + file: file, directory: directory, inflector: inflector ).call when FalseClass AutoRegistrationStrategies::NoNamespace.new( - file: file, directory: directory, entity: component_dirs.fetch(entity) + file: file, + directory: directory, + entity: component_dirs.fetch(entity), + inflector: inflector ).call end - Inflector.constantize(klass_name) + inflector.constantize(klass_name) end end end diff --git a/lib/rom/setup/auto_registration_strategies/base.rb b/lib/rom/setup/auto_registration_strategies/base.rb index 56310ed63..6d5d9f876 100644 --- a/lib/rom/setup/auto_registration_strategies/base.rb +++ b/lib/rom/setup/auto_registration_strategies/base.rb @@ -2,6 +2,7 @@ require "rom/types" require "rom/initializer" +require "rom/support/inflector" module ROM module AutoRegistrationStrategies @@ -18,6 +19,11 @@ class Base # @!attribute [r] file # @return [String] Name of a component file option :file, type: Types::Strict::String + + # @!attribute [r] inflector + # @return [Dry::Inflector] String inflector + # @api private + option :inflector, reader: true, default: -> { Inflector } end end end diff --git a/lib/rom/setup/auto_registration_strategies/custom_namespace.rb b/lib/rom/setup/auto_registration_strategies/custom_namespace.rb index 1239f05bf..58fea65d0 100644 --- a/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +++ b/lib/rom/setup/auto_registration_strategies/custom_namespace.rb @@ -25,7 +25,7 @@ class CustomNamespace < Base # # @api private def call - parts = path_arr.map { |part| Inflector.camelize(part) } + parts = path_arr.map { |part| inflector.camelize(part) } potential = parts.map.with_index do |_, i| parts[(i - parts.size)..parts.size] end @@ -66,7 +66,7 @@ def filename # @api private def ns_const - @namespace_constant ||= Inflector.constantize(namespace) + @namespace_constant ||= inflector.constantize(namespace) end # @api private diff --git a/lib/rom/setup/auto_registration_strategies/no_namespace.rb b/lib/rom/setup/auto_registration_strategies/no_namespace.rb index 105b86076..a535b42f8 100644 --- a/lib/rom/setup/auto_registration_strategies/no_namespace.rb +++ b/lib/rom/setup/auto_registration_strategies/no_namespace.rb @@ -24,7 +24,7 @@ class NoNamespace < Base # # @api private def call - Inflector.camelize( + inflector.camelize( file.sub(%r{^#{directory}/#{entity}/}, "").sub(EXTENSION_REGEX, "") ) end diff --git a/lib/rom/setup/auto_registration_strategies/with_namespace.rb b/lib/rom/setup/auto_registration_strategies/with_namespace.rb index 5efafb48f..1b113fffa 100644 --- a/lib/rom/setup/auto_registration_strategies/with_namespace.rb +++ b/lib/rom/setup/auto_registration_strategies/with_namespace.rb @@ -20,7 +20,7 @@ class WithNamespace < Base # # @api private def call - Inflector.camelize( + inflector.camelize( file.sub(%r{^#{directory.dirname}/}, "").sub(EXTENSION_REGEX, "") ) end diff --git a/lib/rom/setup/finalize.rb b/lib/rom/setup/finalize.rb index 3b75a9946..b7c93283c 100644 --- a/lib/rom/setup/finalize.rb +++ b/lib/rom/setup/finalize.rb @@ -22,13 +22,14 @@ module ROM # # @private class Finalize - attr_reader :gateways, :repo_adapter, + attr_reader :gateways, :repo_adapter, :inflector, :relation_classes, :mapper_classes, :mapper_objects, :command_classes, :plugins, :config, :notifications # @api private def initialize(options) @gateways = options.fetch(:gateways) + @inflector = options.fetch(:inflector) @relation_classes = options.fetch(:relation_classes) @command_classes = options.fetch(:command_classes) @@ -84,7 +85,8 @@ def load_relations(mappers) relation_classes, mappers: mappers, plugins: global_plugins, - notifications: notifications + notifications: notifications, + inflector: inflector ).run! end @@ -99,7 +101,13 @@ def load_mappers # # @api private def load_commands(relations) - FinalizeCommands.new(relations, gateways, command_classes, notifications).run! + FinalizeCommands.new( + relations, + gateways, + command_classes, + notifications: notifications, + inflector: inflector + ).run! end end end diff --git a/lib/rom/setup/finalize/finalize_commands.rb b/lib/rom/setup/finalize/finalize_commands.rb index 18b9d5677..99ed83581 100644 --- a/lib/rom/setup/finalize/finalize_commands.rb +++ b/lib/rom/setup/finalize/finalize_commands.rb @@ -9,6 +9,8 @@ class Finalize class FinalizeCommands attr_reader :notifications + attr_reader :inflector + # Build command registry hash for provided relations # # @param [RelationRegistry] relations registry @@ -16,11 +18,12 @@ class FinalizeCommands # @param [Array] command_classes a list of command subclasses # # @api private - def initialize(relations, gateways, command_classes, notifications) + def initialize(relations, gateways, command_classes, **options) @relations = relations @gateways = gateways @command_classes = command_classes - @notifications = notifications + @inflector = options.fetch(:inflector, Inflector) + @notifications = options.fetch(:notifications) end # @return [Hash] @@ -42,7 +45,13 @@ def run! end registry = Registry.new - compiler = CommandCompiler.new(@gateways, @relations, registry, notifications) + compiler = CommandCompiler.new( + @gateways, + @relations, + registry, + notifications, + inflector: inflector + ) @relations.each do |(name, relation)| rel_commands = commands.select { |c| c.relation.name == relation.name } diff --git a/lib/rom/setup/finalize/finalize_relations.rb b/lib/rom/setup/finalize/finalize_relations.rb index 34a87f3be..6c3888785 100644 --- a/lib/rom/setup/finalize/finalize_relations.rb +++ b/lib/rom/setup/finalize/finalize_relations.rb @@ -3,12 +3,15 @@ require "rom/constants" require "rom/relation_registry" require "rom/mapper_registry" +require "rom/support/inflector" module ROM class Finalize class FinalizeRelations attr_reader :notifications + attr_reader :inflector + # Build relation registry of specified descendant classes # # This is used by the setup @@ -17,12 +20,13 @@ class FinalizeRelations # @param [Array] relation_classes a list of relation descendants # # @api private - def initialize(gateways, relation_classes, notifications:, mappers: nil, plugins: EMPTY_ARRAY) + def initialize(gateways, relation_classes, **options) @gateways = gateways @relation_classes = relation_classes - @mappers = mappers - @plugins = plugins - @notifications = notifications + @inflector = options.fetch(:inflector, Inflector) + @mappers = options.fetch(:mappers, nil) + @plugins = options.fetch(:plugins, EMPTY_ARRAY) + @notifications = options.fetch(:notifications) end # @return [Hash] @@ -103,7 +107,13 @@ def build_relation(klass, registry) dataset: dataset, relation: klass, adapter: klass.adapter ) - options = {__registry__: registry, mappers: mapper_registry(rel_key, klass), schema: schema, **plugin_options} + options = { + __registry__: registry, + mappers: mapper_registry(rel_key, klass), + schema: schema, + inflector: inflector, + **plugin_options + } klass.new(dataset, **options) end diff --git a/lib/rom/struct_compiler.rb b/lib/rom/struct_compiler.rb index d70f90a9d..c867a8e9a 100644 --- a/lib/rom/struct_compiler.rb +++ b/lib/rom/struct_compiler.rb @@ -17,8 +17,11 @@ class StructCompiler < Dry::Types::Compiler extend Initializer param :registry, default: -> { Dry::Types } + option :cache, default: -> { Cache.new } + option :inflector, reader: true, default: -> { Inflector } + # @api private def initialize(*) super @@ -105,7 +108,7 @@ def build_class(name, parent, ns, &block) # @api private def class_name(name) - Inflector.classify(name) + inflector.classify(name) end end end diff --git a/spec/fixtures/xml_space/xml_commands/create_customer.rb b/spec/fixtures/xml_space/xml_commands/create_customer.rb new file mode 100644 index 000000000..c5d2cd8b7 --- /dev/null +++ b/spec/fixtures/xml_space/xml_commands/create_customer.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module XMLSpace + module XMLCommands + class CreateCustomer + end + end +end diff --git a/spec/fixtures/xml_space/xml_mappers/customer_list.rb b/spec/fixtures/xml_space/xml_mappers/customer_list.rb new file mode 100644 index 000000000..097965922 --- /dev/null +++ b/spec/fixtures/xml_space/xml_mappers/customer_list.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module XMLSpace + module XMLMappers + class CustomerList + end + end +end diff --git a/spec/fixtures/xml_space/xml_relations/customers.rb b/spec/fixtures/xml_space/xml_relations/customers.rb new file mode 100644 index 000000000..29c01893a --- /dev/null +++ b/spec/fixtures/xml_space/xml_relations/customers.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module XMLSpace + module XMLRelations + class Customers + end + end +end diff --git a/spec/unit/rom/configuration_spec.rb b/spec/unit/rom/configuration_spec.rb index 10d0bfc69..b492516e0 100644 --- a/spec/unit/rom/configuration_spec.rb +++ b/spec/unit/rom/configuration_spec.rb @@ -80,4 +80,16 @@ expect(conf.relation_classes(:custom)).to eql([rel_custom]) end end + + describe "setting inflector" do + example "is supported" do + inflector = double(:inflector) + + configuration = ROM::Configuration.new(:memory, "something") do |conf| + conf.inflector = inflector + end + + expect(configuration.inflector).to be(inflector) + end + end end diff --git a/spec/unit/rom/relation/schema_spec.rb b/spec/unit/rom/relation/schema_spec.rb index 64d6dd837..4b1dd69c0 100644 --- a/spec/unit/rom/relation/schema_spec.rb +++ b/spec/unit/rom/relation/schema_spec.rb @@ -306,12 +306,12 @@ class Test::Users < ROM::Relation[:memory] schema = Test::Users.schema_proc.call - expect( - schema[:admin] - ).to eql(ROM::Attribute.new( - ROM::Types::Bool.meta(source: ROM::Relation::Name[:test_users]), - name: :admin - )) + expect(schema[:admin]).to eql( + ROM::Attribute.new( + ROM::Types::Bool.meta(source: ROM::Relation::Name[:test_users]), + name: :admin + ) + ) end it "raises an error on double definition" do @@ -362,8 +362,36 @@ class Test::Users < ROM::Relation[:memory] end end - expect(Test::Users.schema_proc.call.finalize_attributes!) - .to eql(Test::Users.schema_proc.call.finalize_attributes!) + expect(Test::Users.schema_proc.call.finalize_attributes!).to eql( + Test::Users.schema_proc.call.finalize_attributes! + ) + end + + context "custom inflector" do + let(:inflector) do + Dry::Inflector.new do |i| + i.plural("article", "posts") + end + end + + it "accepts a custom inflector" do + class Test::Users < ROM::Relation[:memory] + schema do + attribute :id, Types::Integer.meta(primary_key: true) + attribute :name, Types::String + attribute :admin, Types::Bool + + associations do + has_one :article + end + end + end + + schema = Test::Users.schema_proc.(inflector: inflector) + association = schema.associations[:article] + + expect(association.target.relation).to eql(:posts) + end end end diff --git a/spec/unit/rom/schema/associations_dsl_spec.rb b/spec/unit/rom/schema/associations_dsl_spec.rb index 85505fef3..0480e249a 100644 --- a/spec/unit/rom/schema/associations_dsl_spec.rb +++ b/spec/unit/rom/schema/associations_dsl_spec.rb @@ -3,9 +3,12 @@ require "rom/schema/associations_dsl" RSpec.describe ROM::Schema::AssociationsDSL do + let(:args) { [ROM::Relation::Name[:users]] } + subject(:dsl) do - ROM::Schema::AssociationsDSL.new(ROM::Relation::Name[:users]) do + ROM::Schema::AssociationsDSL.new(*args) do has_many :posts + has_many :labels, through: :posts end end @@ -16,5 +19,21 @@ expect(association_set.type).to eql("ROM::AssociationSet[:users]") expect(association_set.key?(:posts)).to be(true) end + + context "using custom inflector" do + let(:inflector) do + Dry::Inflector.new do |i| + i.singular("labels", "tag") + end + end + + let(:args) { [*super(), inflector] } + + it do + association_set = dsl.call + expect(association_set.type).to eql("ROM::AssociationSet[:users]") + expect(association_set[:labels].options[:through].assoc_name).to eql(:tag) + end + end end end diff --git a/spec/unit/rom/setup/auto_registration_spec.rb b/spec/unit/rom/setup/auto_registration_spec.rb index 9b4bd9660..00fb85fe9 100644 --- a/spec/unit/rom/setup/auto_registration_spec.rb +++ b/spec/unit/rom/setup/auto_registration_spec.rb @@ -11,7 +11,7 @@ let(:notifications) { instance_double(ROM::Notifications::EventBus) } after do - %i[Persistence Users CreateUser UserList My].each do |const| + %i[Persistence Users CreateUser UserList My XMLSpace].each do |const| Object.send(:remove_const, const) if Object.const_defined?(const) end @@ -237,6 +237,44 @@ module Customers end end end + + context "when custom inflector" do + let(:inflector) do + Dry::Inflector.new do |i| + i.acronym("XML") + end + end + + before do + setup.inflector = inflector + setup.auto_registration( + SPEC_ROOT.join("fixtures/xml_space"), + component_dirs: { + relations: :xml_relations, + mappers: :xml_mappers, + commands: :xml_commands + } + ) + end + + describe "#relations" do + it "loads files and returns constants" do + expect(setup.relation_classes).to eql([XMLSpace::XMLRelations::Customers]) + end + end + + describe "#commands" do + it "loads files and returns constants" do + expect(setup.command_classes).to eql([XMLSpace::XMLCommands::CreateCustomer]) + end + end + + describe "#mappers" do + it "loads files and returns constants" do + expect(setup.mapper_classes).to eql([XMLSpace::XMLMappers::CustomerList]) + end + end + end end end end