rom-core 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|