rom-core 4.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +603 -0
- data/LICENSE +20 -0
- data/README.md +18 -0
- data/lib/rom-core.rb +1 -0
- data/lib/rom/array_dataset.rb +44 -0
- data/lib/rom/association_set.rb +16 -0
- data/lib/rom/associations/abstract.rb +135 -0
- data/lib/rom/associations/definitions.rb +5 -0
- data/lib/rom/associations/definitions/abstract.rb +116 -0
- data/lib/rom/associations/definitions/many_to_many.rb +24 -0
- data/lib/rom/associations/definitions/many_to_one.rb +11 -0
- data/lib/rom/associations/definitions/one_to_many.rb +11 -0
- data/lib/rom/associations/definitions/one_to_one.rb +11 -0
- data/lib/rom/associations/definitions/one_to_one_through.rb +11 -0
- data/lib/rom/associations/many_to_many.rb +81 -0
- data/lib/rom/associations/many_to_one.rb +37 -0
- data/lib/rom/associations/one_to_many.rb +37 -0
- data/lib/rom/associations/one_to_one.rb +8 -0
- data/lib/rom/associations/one_to_one_through.rb +8 -0
- data/lib/rom/associations/through_identifier.rb +39 -0
- data/lib/rom/auto_curry.rb +55 -0
- data/lib/rom/cache.rb +46 -0
- data/lib/rom/command.rb +488 -0
- data/lib/rom/command_compiler.rb +239 -0
- data/lib/rom/command_proxy.rb +24 -0
- data/lib/rom/command_registry.rb +141 -0
- data/lib/rom/commands.rb +3 -0
- data/lib/rom/commands/class_interface.rb +270 -0
- data/lib/rom/commands/composite.rb +53 -0
- data/lib/rom/commands/create.rb +13 -0
- data/lib/rom/commands/delete.rb +14 -0
- data/lib/rom/commands/graph.rb +88 -0
- data/lib/rom/commands/graph/class_interface.rb +62 -0
- data/lib/rom/commands/graph/input_evaluator.rb +62 -0
- data/lib/rom/commands/lazy.rb +99 -0
- data/lib/rom/commands/lazy/create.rb +23 -0
- data/lib/rom/commands/lazy/delete.rb +27 -0
- data/lib/rom/commands/lazy/update.rb +34 -0
- data/lib/rom/commands/result.rb +96 -0
- data/lib/rom/commands/update.rb +14 -0
- data/lib/rom/configuration.rb +114 -0
- data/lib/rom/configuration_dsl.rb +87 -0
- data/lib/rom/configuration_dsl/command.rb +41 -0
- data/lib/rom/configuration_dsl/command_dsl.rb +35 -0
- data/lib/rom/configuration_dsl/relation.rb +26 -0
- data/lib/rom/configuration_plugin.rb +17 -0
- data/lib/rom/constants.rb +64 -0
- data/lib/rom/container.rb +147 -0
- data/lib/rom/core.rb +46 -0
- data/lib/rom/create_container.rb +60 -0
- data/lib/rom/data_proxy.rb +94 -0
- data/lib/rom/enumerable_dataset.rb +68 -0
- data/lib/rom/environment.rb +70 -0
- data/lib/rom/gateway.rb +184 -0
- data/lib/rom/global.rb +58 -0
- data/lib/rom/global/plugin_dsl.rb +47 -0
- data/lib/rom/initializer.rb +64 -0
- data/lib/rom/lint/enumerable_dataset.rb +54 -0
- data/lib/rom/lint/gateway.rb +120 -0
- data/lib/rom/lint/linter.rb +78 -0
- data/lib/rom/lint/spec.rb +20 -0
- data/lib/rom/lint/test.rb +98 -0
- data/lib/rom/mapper_registry.rb +24 -0
- data/lib/rom/memory.rb +4 -0
- data/lib/rom/memory/associations.rb +4 -0
- data/lib/rom/memory/associations/many_to_many.rb +10 -0
- data/lib/rom/memory/associations/many_to_one.rb +10 -0
- data/lib/rom/memory/associations/one_to_many.rb +10 -0
- data/lib/rom/memory/associations/one_to_one.rb +10 -0
- data/lib/rom/memory/commands.rb +56 -0
- data/lib/rom/memory/dataset.rb +97 -0
- data/lib/rom/memory/gateway.rb +64 -0
- data/lib/rom/memory/relation.rb +62 -0
- data/lib/rom/memory/schema.rb +23 -0
- data/lib/rom/memory/storage.rb +59 -0
- data/lib/rom/memory/types.rb +9 -0
- data/lib/rom/pipeline.rb +105 -0
- data/lib/rom/plugin.rb +25 -0
- data/lib/rom/plugin_base.rb +45 -0
- data/lib/rom/plugin_registry.rb +197 -0
- data/lib/rom/plugins/command/schema.rb +37 -0
- data/lib/rom/plugins/configuration/configuration_dsl.rb +21 -0
- data/lib/rom/plugins/relation/instrumentation.rb +51 -0
- data/lib/rom/plugins/relation/registry_reader.rb +44 -0
- data/lib/rom/plugins/schema/timestamps.rb +58 -0
- data/lib/rom/registry.rb +71 -0
- data/lib/rom/relation.rb +548 -0
- data/lib/rom/relation/class_interface.rb +282 -0
- data/lib/rom/relation/commands.rb +23 -0
- data/lib/rom/relation/composite.rb +46 -0
- data/lib/rom/relation/curried.rb +103 -0
- data/lib/rom/relation/graph.rb +197 -0
- data/lib/rom/relation/loaded.rb +127 -0
- data/lib/rom/relation/materializable.rb +66 -0
- data/lib/rom/relation/name.rb +111 -0
- data/lib/rom/relation/view_dsl.rb +64 -0
- data/lib/rom/relation/wrap.rb +83 -0
- data/lib/rom/relation_registry.rb +10 -0
- data/lib/rom/schema.rb +437 -0
- data/lib/rom/schema/associations_dsl.rb +195 -0
- data/lib/rom/schema/attribute.rb +419 -0
- data/lib/rom/schema/dsl.rb +164 -0
- data/lib/rom/schema/inferrer.rb +66 -0
- data/lib/rom/schema_plugin.rb +27 -0
- data/lib/rom/setup.rb +68 -0
- data/lib/rom/setup/auto_registration.rb +74 -0
- data/lib/rom/setup/auto_registration_strategies/base.rb +16 -0
- data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +63 -0
- data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +20 -0
- data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +18 -0
- data/lib/rom/setup/finalize.rb +103 -0
- data/lib/rom/setup/finalize/finalize_commands.rb +60 -0
- data/lib/rom/setup/finalize/finalize_mappers.rb +56 -0
- data/lib/rom/setup/finalize/finalize_relations.rb +135 -0
- data/lib/rom/support/configurable.rb +85 -0
- data/lib/rom/support/memoizable.rb +58 -0
- data/lib/rom/support/notifications.rb +103 -0
- data/lib/rom/transaction.rb +24 -0
- data/lib/rom/types.rb +26 -0
- data/lib/rom/version.rb +5 -0
- metadata +289 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rom/plugin_base'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
# ConfigurationPlugin is a simple object used to store configuration plugin configurations
|
5
|
+
#
|
6
|
+
# @private
|
7
|
+
class ConfigurationPlugin < PluginBase
|
8
|
+
# Apply this plugin to the provided configuration
|
9
|
+
#
|
10
|
+
# @param [ROM::Configuration] configuration
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def apply_to(configuration, options = {})
|
14
|
+
mod.apply(configuration, options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'dry/core/constants'
|
2
|
+
|
3
|
+
# Constants and errors common in the whole library
|
4
|
+
module ROM
|
5
|
+
include Dry::Core::Constants
|
6
|
+
|
7
|
+
AdapterLoadError = Class.new(StandardError)
|
8
|
+
|
9
|
+
class AdapterNotPresentError < StandardError
|
10
|
+
def initialize(adapter, component)
|
11
|
+
super(
|
12
|
+
"Failed to find #{component} class for #{adapter} adapter. " \
|
13
|
+
"Make sure ROM setup was started and the adapter identifier is correct."
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
EnvAlreadyFinalizedError = Class.new(StandardError)
|
19
|
+
RelationAlreadyDefinedError = Class.new(StandardError)
|
20
|
+
MapperAlreadyDefinedError = Class.new(StandardError)
|
21
|
+
NoRelationError = Class.new(StandardError)
|
22
|
+
CommandError = Class.new(StandardError)
|
23
|
+
KeyMissing = Class.new(ROM::CommandError)
|
24
|
+
TupleCountMismatchError = Class.new(CommandError)
|
25
|
+
UnknownPluginError = Class.new(StandardError)
|
26
|
+
UnsupportedRelationError = Class.new(StandardError)
|
27
|
+
MissingAdapterIdentifierError = Class.new(StandardError)
|
28
|
+
|
29
|
+
class ElementNotFoundError < KeyError
|
30
|
+
def initialize(key, registry)
|
31
|
+
super(set_message(key, registry))
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_message(key, registry)
|
35
|
+
"#{key.inspect} doesn't exist in #{registry.class.name} registry"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
MapperMissingError = Class.new(ElementNotFoundError)
|
40
|
+
|
41
|
+
CommandNotFoundError = Class.new(ElementNotFoundError) do
|
42
|
+
def set_message(key, registry)
|
43
|
+
"There is no :#{key} command for :#{registry.relation_name} relation"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
MissingSchemaClassError = Class.new(StandardError) do
|
48
|
+
def initialize(klass)
|
49
|
+
super("#{klass.inspect} relation is missing schema_class")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
MissingSchemaError = Class.new(StandardError) do
|
54
|
+
def initialize(klass)
|
55
|
+
super("#{klass.inspect} relation is missing schema definition")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
DuplicateConfigurationError = Class.new(StandardError)
|
60
|
+
DuplicateContainerError = Class.new(StandardError)
|
61
|
+
|
62
|
+
InvalidOptionValueError = Class.new(StandardError)
|
63
|
+
InvalidOptionKeyError = Class.new(StandardError)
|
64
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'dry/container'
|
2
|
+
|
3
|
+
require 'rom/cache'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# ROM container is an isolated environment with no global state where all
|
7
|
+
# components are registered. Container objects provide access to your
|
8
|
+
# relations, commands and mappers. ROM containers are usually configured and
|
9
|
+
# handled via framework integrations, although it is easy to use them
|
10
|
+
# standalone.
|
11
|
+
#
|
12
|
+
# There are 3 types of container setup:
|
13
|
+
#
|
14
|
+
# * Setup DSL - a simple block-based configuration which allows configuring
|
15
|
+
# all components and gives you back a container instance. This type is suitable
|
16
|
+
# for small scripts, or in some cases rake tasks
|
17
|
+
# * Explicit setup - this type requires creating a configuration object,
|
18
|
+
# registering component classes (ie relation classes) and passing the config
|
19
|
+
# to container builder function. This type is suitable when your environment
|
20
|
+
# is not typical and you need full control over component registration
|
21
|
+
# * Explicit setup with auto-registration - same as explicit setup but allows
|
22
|
+
# you to configure auto-registration mechanism which will register component
|
23
|
+
# classes for you, based on dir/file naming conventions. This is the most
|
24
|
+
# common type of setup that's used by framework integrations
|
25
|
+
#
|
26
|
+
# @example in-line setup
|
27
|
+
# rom = ROM.container(:sql, 'sqlite::memory') do |config|
|
28
|
+
# config.default.create_table :users do
|
29
|
+
# primary_key :id
|
30
|
+
# column :name, String, null: false
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# config.relation(:users) do
|
34
|
+
# schema(infer: true)
|
35
|
+
#
|
36
|
+
# def by_name(name)
|
37
|
+
# where(name: name)
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# rom.relations[:users].insert(name: "Jane")
|
43
|
+
#
|
44
|
+
# rom.relations[:users].by_name("Jane").to_a
|
45
|
+
# # [{:id=>1, :name=>"Jane"}]
|
46
|
+
#
|
47
|
+
# @example multi-step setup with explicit component classes
|
48
|
+
# config = ROM::Configuration.new(:sql, 'sqlite::memory')
|
49
|
+
#
|
50
|
+
# config.default.create_table :users do
|
51
|
+
# primary_key :id
|
52
|
+
# column :name, String, null: false
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# class Users < ROM::Relation[:sql]
|
56
|
+
# schema(:users, infer: true)
|
57
|
+
#
|
58
|
+
# def by_name(name)
|
59
|
+
# where(name: name)
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# config.register_relation(Users)
|
64
|
+
#
|
65
|
+
# rom = ROM.container(config)
|
66
|
+
#
|
67
|
+
# rom.relations[:users].insert(name: "Jane")
|
68
|
+
#
|
69
|
+
# rom.relations[:users].by_name("Jane").to_a
|
70
|
+
# # [{:id=>1, :name=>"Jane"}]
|
71
|
+
#
|
72
|
+
#
|
73
|
+
# @example multi-step setup with auto-registration
|
74
|
+
# config = ROM::Configuration.new(:sql, 'sqlite::memory')
|
75
|
+
# config.auto_registration('./persistence', namespace: false)
|
76
|
+
#
|
77
|
+
# config.default.create_table :users do
|
78
|
+
# primary_key :id
|
79
|
+
# column :name, String, null: false
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# # ./persistence/relations/users.rb
|
83
|
+
# class Users < ROM::Relation[:sql]
|
84
|
+
# schema(infer: true)
|
85
|
+
#
|
86
|
+
# def by_name(name)
|
87
|
+
# where(name: name)
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# rom = ROM.container(config)
|
92
|
+
#
|
93
|
+
# rom.relations[:users].insert(name: "Jane")
|
94
|
+
#
|
95
|
+
# rom.relations[:users].by_name("Jane").to_a
|
96
|
+
# # [{:id=>1, :name=>"Jane"}]
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
class Container
|
100
|
+
include Dry::Container::Mixin
|
101
|
+
include Dry::Equalizer(:gateways, :relations, :mappers, :commands)
|
102
|
+
|
103
|
+
# @api private
|
104
|
+
def self.new(gateways, relations, mappers, commands)
|
105
|
+
super().tap do |container|
|
106
|
+
container.register(:gateways, gateways)
|
107
|
+
container.register(:mappers, mappers)
|
108
|
+
container.register(:commands, commands)
|
109
|
+
container.register(:relations, relations)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# @api public
|
114
|
+
def gateways
|
115
|
+
self[:gateways]
|
116
|
+
end
|
117
|
+
|
118
|
+
# @api public
|
119
|
+
def mappers
|
120
|
+
self[:mappers]
|
121
|
+
end
|
122
|
+
|
123
|
+
# @api public
|
124
|
+
def relations
|
125
|
+
self[:relations]
|
126
|
+
end
|
127
|
+
|
128
|
+
# @api public
|
129
|
+
def commands
|
130
|
+
self[:commands]
|
131
|
+
end
|
132
|
+
|
133
|
+
# Disconnect all gateways
|
134
|
+
#
|
135
|
+
# @example
|
136
|
+
# rom = ROM.container(:sql, 'sqlite://my_db.sqlite')
|
137
|
+
# rom.relations[:users].insert(name: "Jane")
|
138
|
+
# rom.disconnect
|
139
|
+
#
|
140
|
+
# @return [Hash<Symbol=>Gateway>] a hash with disconnected gateways
|
141
|
+
#
|
142
|
+
# @api public
|
143
|
+
def disconnect
|
144
|
+
gateways.each_value(&:disconnect)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
data/lib/rom/core.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'dry/equalizer'
|
2
|
+
require 'dry/core/constants'
|
3
|
+
|
4
|
+
require 'rom/version'
|
5
|
+
require 'rom/constants'
|
6
|
+
|
7
|
+
# core parts
|
8
|
+
require 'rom/configuration_plugin'
|
9
|
+
require 'rom/plugin'
|
10
|
+
require 'rom/schema_plugin'
|
11
|
+
require 'rom/relation'
|
12
|
+
require 'rom-mapper'
|
13
|
+
require 'rom/mapper/configuration_plugin'
|
14
|
+
require 'rom/commands'
|
15
|
+
|
16
|
+
# rom Global
|
17
|
+
require 'rom/global'
|
18
|
+
|
19
|
+
# rom configurations
|
20
|
+
require 'rom/configuration'
|
21
|
+
|
22
|
+
# container with registries
|
23
|
+
require 'rom/container'
|
24
|
+
|
25
|
+
# container factory
|
26
|
+
require 'rom/create_container'
|
27
|
+
|
28
|
+
# register core plugins
|
29
|
+
require 'rom/plugins/configuration/configuration_dsl'
|
30
|
+
require 'rom/plugins/relation/registry_reader'
|
31
|
+
require 'rom/plugins/relation/instrumentation'
|
32
|
+
require 'rom/plugins/command/schema'
|
33
|
+
require 'rom/plugins/schema/timestamps'
|
34
|
+
|
35
|
+
module ROM
|
36
|
+
extend Global
|
37
|
+
|
38
|
+
plugins do
|
39
|
+
register :mappers, ROM::Mapper::ConfigurationPlugin, type: :configuration
|
40
|
+
register :macros, ROM::ConfigurationPlugins::ConfigurationDSL, type: :configuration
|
41
|
+
register :timestamps, ROM::Plugins::Schema::Timestamps, type: :schema
|
42
|
+
register :registry_reader, ROM::Plugins::Relation::RegistryReader, type: :relation
|
43
|
+
register :instrumentation, ROM::Plugins::Relation::Instrumentation, type: :relation
|
44
|
+
register :schema, ROM::Plugins::Command::Schema, type: :command
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rom/configuration'
|
2
|
+
require 'rom/environment'
|
3
|
+
require 'rom/setup'
|
4
|
+
require 'rom/setup/finalize'
|
5
|
+
|
6
|
+
module ROM
|
7
|
+
class CreateContainer
|
8
|
+
attr_reader :container
|
9
|
+
|
10
|
+
def initialize(environment, setup)
|
11
|
+
@container = finalize(environment, setup)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def finalize(environment, setup)
|
17
|
+
environment.configure do |config|
|
18
|
+
environment.gateways.each_key do |key|
|
19
|
+
gateway_config = config.gateways[key]
|
20
|
+
gateway_config.infer_relations = true unless gateway_config.key?(:infer_relations)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
finalize = Finalize.new(
|
25
|
+
gateways: environment.gateways,
|
26
|
+
relation_classes: setup.relation_classes,
|
27
|
+
command_classes: setup.command_classes,
|
28
|
+
mappers: setup.mapper_classes,
|
29
|
+
plugins: setup.plugins,
|
30
|
+
notifications: setup.notifications,
|
31
|
+
config: environment.config.dup.freeze
|
32
|
+
)
|
33
|
+
|
34
|
+
finalize.run!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class InlineCreateContainer < CreateContainer
|
39
|
+
def initialize(*args, &block)
|
40
|
+
case args.first
|
41
|
+
when Configuration
|
42
|
+
environment = args.first.environment
|
43
|
+
setup = args.first.setup
|
44
|
+
when Environment
|
45
|
+
environment = args.first
|
46
|
+
setup = args[1]
|
47
|
+
else
|
48
|
+
configuration = Configuration.new(*args, &block)
|
49
|
+
environment = configuration.environment
|
50
|
+
setup = configuration.setup
|
51
|
+
end
|
52
|
+
|
53
|
+
super(environment, setup)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.container(*args, &block)
|
58
|
+
InlineCreateContainer.new(*args, &block).container
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module ROM
|
2
|
+
# Helper module for dataset classes
|
3
|
+
#
|
4
|
+
# It provides a constructor accepting data, header and an optional row_proc.
|
5
|
+
# This module is used internally by EnumerableDataset and ArrayDataset.
|
6
|
+
#
|
7
|
+
# @private
|
8
|
+
module DataProxy
|
9
|
+
NON_FORWARDABLE = [
|
10
|
+
:each, :to_a, :to_ary, :kind_of?, :instance_of?, :is_a?
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
# Wrapped data array
|
14
|
+
#
|
15
|
+
# @return [Object] Data object for the iterator
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
attr_reader :data
|
19
|
+
|
20
|
+
# @return [Proc] tuple processing proc
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
attr_reader :row_proc
|
24
|
+
|
25
|
+
# Extends the class with `forward` DSL and Equalizer using `data` attribute
|
26
|
+
#
|
27
|
+
# @see ClassMethods#forward
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
def self.included(klass)
|
31
|
+
klass.class_eval do
|
32
|
+
extend ClassMethods
|
33
|
+
|
34
|
+
include Dry::Equalizer(:data)
|
35
|
+
|
36
|
+
option :row_proc, default: -> { self.class.row_proc }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Iterate over data using row_proc
|
41
|
+
#
|
42
|
+
# @return [Enumerator] if block is not given
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def each
|
46
|
+
return to_enum unless block_given?
|
47
|
+
data.each { |tuple| yield(row_proc[tuple]) }
|
48
|
+
end
|
49
|
+
|
50
|
+
module ClassMethods
|
51
|
+
# Default no-op tuple proc
|
52
|
+
#
|
53
|
+
# @return [Proc]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
def row_proc
|
57
|
+
-> tuple { tuple }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Forward provided methods to the underlaying data object
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
#
|
64
|
+
# class MyDataset
|
65
|
+
# include DataProxy
|
66
|
+
#
|
67
|
+
# forward(:find_all, :map)
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# @return [undefined]
|
71
|
+
#
|
72
|
+
# @api public
|
73
|
+
def forward(*methods)
|
74
|
+
# FIXME: we should probably raise if one of the non-forwardable methods
|
75
|
+
# was provided
|
76
|
+
(methods - NON_FORWARDABLE).each do |method_name|
|
77
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
78
|
+
def #{method_name}(*args, &block)
|
79
|
+
response = data.public_send(#{method_name.inspect}, *args, &block)
|
80
|
+
|
81
|
+
if response.equal?(data)
|
82
|
+
self
|
83
|
+
elsif response.is_a?(data.class)
|
84
|
+
self.class.new(response)
|
85
|
+
else
|
86
|
+
response
|
87
|
+
end
|
88
|
+
end
|
89
|
+
RUBY
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rom/initializer'
|
2
|
+
require 'rom/data_proxy'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
# A helper module that adds data-proxy behavior to an enumerable object
|
6
|
+
#
|
7
|
+
# This module is intended to be used by gateways
|
8
|
+
#
|
9
|
+
# Class that includes this module can define `row_proc` class method which
|
10
|
+
# must return a proc-like object which will be used to process each element
|
11
|
+
# in the enumerable
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# class MyDataset
|
15
|
+
# include ROM::EnumerableDataset
|
16
|
+
#
|
17
|
+
# def self.row_proc
|
18
|
+
# -> tuple { tuple.each_with_object({}) { |(k,v), h| h[k.to_sym] = v } }
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# ds = MyDataset.new([{ 'name' => 'Jane' }, [:name])
|
23
|
+
# ds.to_a # => { :name => 'Jane' }
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
module EnumerableDataset
|
27
|
+
extend DataProxy::ClassMethods
|
28
|
+
include Enumerable
|
29
|
+
|
30
|
+
# Coerce a dataset to an array
|
31
|
+
#
|
32
|
+
# @return [Array]
|
33
|
+
#
|
34
|
+
# @api public
|
35
|
+
alias_method :to_ary, :to_a
|
36
|
+
|
37
|
+
# Included hook which extends a class with DataProxy behavior
|
38
|
+
#
|
39
|
+
# This module can also be included into other modules so we apply the
|
40
|
+
# extension only for classes
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def self.included(klass)
|
44
|
+
return unless klass.is_a?(Class)
|
45
|
+
|
46
|
+
klass.class_eval do
|
47
|
+
extend Initializer
|
48
|
+
include DataProxy
|
49
|
+
|
50
|
+
param :data
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
forward :take
|
55
|
+
|
56
|
+
[
|
57
|
+
:chunk, :collect, :collect_concat, :drop_while, :find_all, :flat_map,
|
58
|
+
:grep, :map, :reject, :select, :sort, :sort_by, :take_while
|
59
|
+
].each do |method|
|
60
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
61
|
+
def #{method}(*args, &block)
|
62
|
+
return to_enum unless block
|
63
|
+
self.class.new(super(*args, &block), options)
|
64
|
+
end
|
65
|
+
RUBY
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|