rom 0.2.0 → 0.3.0
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/.gitignore +3 -3
- data/.ruby-version +1 -1
- data/.travis.yml +13 -14
- data/{Changelog.md → CHANGELOG.md} +12 -3
- data/Gemfile +14 -10
- data/Gemfile.devtools +16 -16
- data/Guardfile +2 -5
- data/README.md +68 -56
- data/Rakefile +3 -3
- data/config/flay.yml +2 -2
- data/config/mutant.yml +2 -4
- data/config/rubocop.yml +30 -0
- data/lib/rom.rb +47 -9
- data/lib/rom/adapter.rb +52 -0
- data/lib/rom/adapter/memory.rb +70 -0
- data/lib/rom/boot.rb +158 -0
- data/lib/rom/boot/base_relation_dsl.rb +43 -0
- data/lib/rom/boot/dsl.rb +30 -0
- data/lib/rom/boot/mapper_dsl.rb +23 -0
- data/lib/rom/boot/relation_dsl.rb +23 -0
- data/lib/rom/boot/schema_dsl.rb +27 -0
- data/lib/rom/env.rb +48 -0
- data/lib/rom/header.rb +123 -0
- data/lib/rom/mapper.rb +38 -153
- data/lib/rom/mapper_builder.rb +85 -0
- data/lib/rom/model_builder.rb +58 -0
- data/lib/rom/ra.rb +196 -0
- data/lib/rom/ra/operation/group.rb +49 -0
- data/lib/rom/ra/operation/join.rb +41 -0
- data/lib/rom/ra/operation/wrap.rb +45 -0
- data/lib/rom/reader.rb +62 -0
- data/lib/rom/reader_builder.rb +28 -0
- data/lib/rom/relation.rb +41 -350
- data/lib/rom/relation_builder.rb +48 -0
- data/lib/rom/repository.rb +28 -53
- data/lib/rom/support/registry.rb +34 -0
- data/lib/rom/version.rb +3 -0
- data/rom.gemspec +12 -10
- data/spec/integration/adapters/extending_relations_spec.rb +43 -0
- data/spec/integration/mappers/definition_dsl_spec.rb +82 -0
- data/spec/integration/mappers/prefixing_attributes_spec.rb +42 -0
- data/spec/integration/mappers/renaming_attributes_spec.rb +138 -0
- data/spec/integration/ra/group_spec.rb +47 -0
- data/spec/integration/ra/join_spec.rb +24 -0
- data/spec/integration/ra/wrap_spec.rb +37 -0
- data/spec/integration/relations/reading_spec.rb +116 -0
- data/spec/integration/relations/registry_dsl_spec.rb +44 -0
- data/spec/integration/schema_spec.rb +29 -0
- data/spec/integration/setup_spec.rb +18 -0
- data/spec/shared/users_and_tasks.rb +34 -0
- data/spec/spec_helper.rb +14 -36
- data/spec/unit/rom/adapter_spec.rb +59 -0
- data/spec/unit/rom/header_spec.rb +58 -0
- data/spec/unit/rom/mapper_spec.rb +27 -0
- data/spec/unit/rom/ra/operation/group_spec.rb +55 -0
- data/spec/unit/rom/ra/operation/wrap_spec.rb +29 -0
- data/spec/unit/rom/relation_spec.rb +34 -0
- metadata +122 -190
- data/lib/rom/constants.rb +0 -16
- data/lib/rom/environment.rb +0 -105
- data/lib/rom/environment/builder.rb +0 -71
- data/lib/rom/mapper/attribute.rb +0 -108
- data/lib/rom/mapper/builder.rb +0 -58
- data/lib/rom/mapper/builder/definition.rb +0 -162
- data/lib/rom/mapper/header.rb +0 -103
- data/lib/rom/mapper/loader_builder.rb +0 -26
- data/lib/rom/schema.rb +0 -21
- data/lib/rom/schema/builder.rb +0 -59
- data/lib/rom/schema/definition.rb +0 -84
- data/lib/rom/schema/definition/relation.rb +0 -80
- data/lib/rom/schema/definition/relation/base.rb +0 -27
- data/lib/rom/session.rb +0 -111
- data/lib/rom/session/environment.rb +0 -67
- data/lib/rom/session/identity_map.rb +0 -43
- data/lib/rom/session/mapper.rb +0 -62
- data/lib/rom/session/relation.rb +0 -140
- data/lib/rom/session/state.rb +0 -59
- data/lib/rom/session/state/created.rb +0 -22
- data/lib/rom/session/state/deleted.rb +0 -25
- data/lib/rom/session/state/persisted.rb +0 -34
- data/lib/rom/session/state/transient.rb +0 -20
- data/lib/rom/session/state/updated.rb +0 -29
- data/lib/rom/session/tracker.rb +0 -62
- data/lib/rom/support/axiom/adapter.rb +0 -111
- data/lib/rom/support/axiom/adapter/data_objects.rb +0 -38
- data/lib/rom/support/axiom/adapter/memory.rb +0 -25
- data/lib/rom/support/axiom/adapter/postgres.rb +0 -19
- data/lib/rom/support/axiom/adapter/sqlite3.rb +0 -20
- data/lib/version.rb +0 -3
- data/spec/integration/environment_setup_spec.rb +0 -24
- data/spec/integration/grouped_mappers_spec.rb +0 -87
- data/spec/integration/join_and_group_spec.rb +0 -76
- data/spec/integration/join_and_wrap_spec.rb +0 -68
- data/spec/integration/mapping_embedded_relations_spec.rb +0 -73
- data/spec/integration/mapping_relations_spec.rb +0 -120
- data/spec/integration/schema_definition_spec.rb +0 -152
- data/spec/integration/session_spec.rb +0 -87
- data/spec/integration/wrapped_mappers_spec.rb +0 -73
- data/spec/shared/unit/environment_context.rb +0 -6
- data/spec/shared/unit/loader.rb +0 -11
- data/spec/shared/unit/loader_identity.rb +0 -13
- data/spec/shared/unit/mapper_context.rb +0 -11
- data/spec/shared/unit/relation_context.rb +0 -82
- data/spec/shared/unit/session_environment_context.rb +0 -11
- data/spec/shared/unit/session_relation_context.rb +0 -18
- data/spec/support/helper.rb +0 -34
- data/spec/support/ice_nine_config.rb +0 -10
- data/spec/support/test_mapper.rb +0 -110
- data/spec/unit/rom/environment/builder/mapping_spec.rb +0 -24
- data/spec/unit/rom/environment/builder/schema_spec.rb +0 -33
- data/spec/unit/rom/environment/class_methods/setup_spec.rb +0 -18
- data/spec/unit/rom/environment/repository_spec.rb +0 -21
- data/spec/unit/rom/mapper/attribute/embedded_collection/to_ast_spec.rb +0 -18
- data/spec/unit/rom/mapper/attribute/embedded_value/to_ast_spec.rb +0 -16
- data/spec/unit/rom/mapper/attribute/rename_spec.rb +0 -9
- data/spec/unit/rom/mapper/attribute/to_ast_spec.rb +0 -9
- data/spec/unit/rom/mapper/builder/class_methods/call_spec.rb +0 -61
- data/spec/unit/rom/mapper/class_methods/build_spec.rb +0 -55
- data/spec/unit/rom/mapper/dump_spec.rb +0 -11
- data/spec/unit/rom/mapper/group_spec.rb +0 -35
- data/spec/unit/rom/mapper/header/each_spec.rb +0 -26
- data/spec/unit/rom/mapper/header/element_reader_spec.rb +0 -21
- data/spec/unit/rom/mapper/header/group_spec.rb +0 -18
- data/spec/unit/rom/mapper/header/join_spec.rb +0 -14
- data/spec/unit/rom/mapper/header/keys_spec.rb +0 -29
- data/spec/unit/rom/mapper/header/project_spec.rb +0 -13
- data/spec/unit/rom/mapper/header/rename_spec.rb +0 -11
- data/spec/unit/rom/mapper/header/to_ast_spec.rb +0 -11
- data/spec/unit/rom/mapper/header/wrap_spec.rb +0 -18
- data/spec/unit/rom/mapper/identity_from_tuple_spec.rb +0 -11
- data/spec/unit/rom/mapper/identity_spec.rb +0 -11
- data/spec/unit/rom/mapper/join_spec.rb +0 -15
- data/spec/unit/rom/mapper/load_spec.rb +0 -11
- data/spec/unit/rom/mapper/new_object_spec.rb +0 -14
- data/spec/unit/rom/mapper/project_spec.rb +0 -11
- data/spec/unit/rom/mapper/rename_spec.rb +0 -16
- data/spec/unit/rom/mapper/wrap_spec.rb +0 -35
- data/spec/unit/rom/relation/delete_spec.rb +0 -15
- data/spec/unit/rom/relation/drop_spec.rb +0 -11
- data/spec/unit/rom/relation/each_spec.rb +0 -23
- data/spec/unit/rom/relation/first_spec.rb +0 -19
- data/spec/unit/rom/relation/group_spec.rb +0 -29
- data/spec/unit/rom/relation/inject_mapper_spec.rb +0 -17
- data/spec/unit/rom/relation/insert_spec.rb +0 -13
- data/spec/unit/rom/relation/last_spec.rb +0 -19
- data/spec/unit/rom/relation/one_spec.rb +0 -49
- data/spec/unit/rom/relation/rename_spec.rb +0 -21
- data/spec/unit/rom/relation/replace_spec.rb +0 -13
- data/spec/unit/rom/relation/restrict_spec.rb +0 -25
- data/spec/unit/rom/relation/sort_by_spec.rb +0 -25
- data/spec/unit/rom/relation/take_spec.rb +0 -11
- data/spec/unit/rom/relation/to_a_spec.rb +0 -20
- data/spec/unit/rom/relation/update_spec.rb +0 -25
- data/spec/unit/rom/relation/wrap_spec.rb +0 -29
- data/spec/unit/rom/repository/class_methods/build_spec.rb +0 -27
- data/spec/unit/rom/repository/element_reader_spec.rb +0 -21
- data/spec/unit/rom/repository/element_writer_spec.rb +0 -18
- data/spec/unit/rom/schema/builder/class_methods/build_spec.rb +0 -103
- data/spec/unit/rom/schema/element_reader_spec.rb +0 -15
- data/spec/unit/rom/session/class_methods/start_spec.rb +0 -23
- data/spec/unit/rom/session/clean_predicate_spec.rb +0 -21
- data/spec/unit/rom/session/environment/element_reader_spec.rb +0 -13
- data/spec/unit/rom/session/flush_spec.rb +0 -58
- data/spec/unit/rom/session/mapper/load_spec.rb +0 -47
- data/spec/unit/rom/session/relation/delete_spec.rb +0 -28
- data/spec/unit/rom/session/relation/dirty_predicate_spec.rb +0 -35
- data/spec/unit/rom/session/relation/identity_spec.rb +0 -11
- data/spec/unit/rom/session/relation/new_spec.rb +0 -50
- data/spec/unit/rom/session/relation/save_spec.rb +0 -50
- data/spec/unit/rom/session/relation/state_spec.rb +0 -23
- data/spec/unit/rom/session/relation/track_spec.rb +0 -23
- data/spec/unit/rom/session/relation/tracking_predicate_spec.rb +0 -23
- data/spec/unit/rom/session/relation/update_attributes_spec.rb +0 -45
- data/spec/unit/rom/session/state_spec.rb +0 -79
@@ -0,0 +1,58 @@
|
|
1
|
+
module ROM
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
class ModelBuilder
|
5
|
+
attr_reader :options, :const_name, :klass
|
6
|
+
|
7
|
+
def self.[](type)
|
8
|
+
case type
|
9
|
+
when :poro then PORO
|
10
|
+
else
|
11
|
+
raise ArgumentError, "#{type.inspect} is not a supported model type"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.call(*args)
|
16
|
+
new(*args).call
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(options)
|
20
|
+
@options = options
|
21
|
+
@const_name = options[:name]
|
22
|
+
end
|
23
|
+
|
24
|
+
def define_const
|
25
|
+
Object.const_set(const_name, klass)
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(header)
|
29
|
+
define_class(header)
|
30
|
+
define_const if const_name
|
31
|
+
end
|
32
|
+
|
33
|
+
class PORO < ModelBuilder
|
34
|
+
|
35
|
+
def define_class(header)
|
36
|
+
@klass = Class.new
|
37
|
+
|
38
|
+
attributes = header.keys
|
39
|
+
|
40
|
+
@klass.send(:attr_accessor, *attributes)
|
41
|
+
|
42
|
+
ivar_list = attributes.map { |name| "@#{name}" }.join(", ")
|
43
|
+
sym_list = attributes.map { |name| ":#{name}" }.join(", ")
|
44
|
+
|
45
|
+
@klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
46
|
+
def initialize(params)
|
47
|
+
#{ivar_list} = params.values_at(#{sym_list})
|
48
|
+
end
|
49
|
+
RUBY
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/rom/ra.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'rom/ra/operation/join'
|
2
|
+
require 'rom/ra/operation/group'
|
3
|
+
require 'rom/ra/operation/wrap'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
|
7
|
+
# Experimental DSL for in-memory relational operations
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
module RA
|
11
|
+
|
12
|
+
# Exposes in-memory relational operations
|
13
|
+
#
|
14
|
+
# See examples for join, group and wrap operations
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def in_memory(&block)
|
18
|
+
DSL.new(self).instance_exec(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
class DSL
|
22
|
+
include Concord.new(:relation)
|
23
|
+
|
24
|
+
# Join two relations in-memory using natural-join
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
#
|
28
|
+
# require 'rom'
|
29
|
+
# require 'rom/adapter/memory'
|
30
|
+
#
|
31
|
+
# setup = ROM.setup(memory: 'memory://localhost')
|
32
|
+
#
|
33
|
+
# setup.schema do
|
34
|
+
# base_relation(:users) do
|
35
|
+
# repository :memory
|
36
|
+
#
|
37
|
+
# attribute :user_id
|
38
|
+
# attribute :name
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# base_relation(:tasks) do
|
42
|
+
# repository :memory
|
43
|
+
#
|
44
|
+
# attribute :user_id
|
45
|
+
# attribute :title
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# setup.relation(:tasks)
|
50
|
+
#
|
51
|
+
# setup.relation(:users) do
|
52
|
+
# def with_tasks
|
53
|
+
# in_memory { join(tasks) }
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# rom.relations.users.insert user_id: 1, name: 'Piotr'
|
58
|
+
# rom.relations.tasks.insert user_id: 1, title: 'Relax'
|
59
|
+
#
|
60
|
+
# rom.relations.users.with_tasks.to_a
|
61
|
+
# => [{:user_id=>1, :name=>"Piotr", :title=>"Relax"}]
|
62
|
+
#
|
63
|
+
# @api public
|
64
|
+
def join(*args)
|
65
|
+
left, right = args.size > 1 ? args : [relation, args.first]
|
66
|
+
Operation::Join.new(left, right)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Groups two relations in-memory using group operation
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
#
|
73
|
+
# require 'rom'
|
74
|
+
# require 'rom/adapter/memory'
|
75
|
+
#
|
76
|
+
# setup = ROM.setup(memory: 'memory://localhost')
|
77
|
+
#
|
78
|
+
# setup.schema do
|
79
|
+
# base_relation(:users) do
|
80
|
+
# repository :memory
|
81
|
+
#
|
82
|
+
# attribute :user_id
|
83
|
+
# attribute :name
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# base_relation(:tasks) do
|
87
|
+
# repository :memory
|
88
|
+
#
|
89
|
+
# attribute :user_id
|
90
|
+
# attribute :title
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# setup.relation(:tasks)
|
95
|
+
#
|
96
|
+
# setup.relation(:users) do
|
97
|
+
# def with_tasks
|
98
|
+
# in_memory { group(join(tasks), tasks: [:title]) }
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# rom.relations.users.insert user_id: 1, name: 'Piotr'
|
103
|
+
# rom.relations.tasks.insert user_id: 1, title: 'Work'
|
104
|
+
# rom.relations.tasks.insert user_id: 1, title: 'Relax'
|
105
|
+
#
|
106
|
+
# rom.relations.users.with_tasks.to_a
|
107
|
+
# => [{:user_id=>1, :name=>"Piotr", tasks: [{:title=>"Relax"}, {:title=>"Work"}]}]
|
108
|
+
#
|
109
|
+
# @api public
|
110
|
+
def group(*args)
|
111
|
+
with_options(*args) { |relation, options|
|
112
|
+
Operation::Group.new(relation, options)
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
# Embed one relation in another in-memory using wrap operation
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
#
|
120
|
+
# require 'rom'
|
121
|
+
# require 'rom/adapter/memory'
|
122
|
+
#
|
123
|
+
# setup = ROM.setup(memory: 'memory://localhost')
|
124
|
+
#
|
125
|
+
# setup.schema do
|
126
|
+
# base_relation(:users) do
|
127
|
+
# repository :memory
|
128
|
+
#
|
129
|
+
# attribute :user_id
|
130
|
+
# attribute :name
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# base_relation(:addresses) do
|
134
|
+
# repository :memory
|
135
|
+
#
|
136
|
+
# attribute :user_id
|
137
|
+
# attribute :street
|
138
|
+
# attribute :zipcode
|
139
|
+
# attribute :city
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# setup.relation(:addresses)
|
144
|
+
#
|
145
|
+
# setup.relation(:users) do
|
146
|
+
# def with_address
|
147
|
+
# in_memory { wrap(join(addresses), address: [:street, :zipcode, :city]) }
|
148
|
+
# end
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# rom = setup.finalize
|
152
|
+
#
|
153
|
+
# rom.relations.users.insert user_id: 1, name: 'Piotr'
|
154
|
+
# rom.relations.addresses.insert user_id: 1, street: 'Street 1', zipcode: '123', city: 'Kraków'
|
155
|
+
#
|
156
|
+
# rom.relations.users.with_address.to_a
|
157
|
+
# => [{:user_id=>1, :name=>"Piotr", :address=>{:street=>"Street 1", :zipcode=>"123", :city=>"Kraków"}}]
|
158
|
+
#
|
159
|
+
# @api public
|
160
|
+
def wrap(*args)
|
161
|
+
with_options(*args) { |relation, options|
|
162
|
+
Operation::Wrap.new(relation, options)
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
# @api private
|
167
|
+
def respond_to_missing?(name, include_private = false)
|
168
|
+
relation.respond_to?(name) || super
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
# @api private
|
174
|
+
def with_options(*args)
|
175
|
+
relation =
|
176
|
+
if args.size > 1
|
177
|
+
args.first
|
178
|
+
else
|
179
|
+
self.relation
|
180
|
+
end
|
181
|
+
|
182
|
+
options = args.last
|
183
|
+
|
184
|
+
yield(relation, options)
|
185
|
+
end
|
186
|
+
|
187
|
+
# @api private
|
188
|
+
def method_missing(name, *args, &block)
|
189
|
+
relation.public_send(name, *args, &block)
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ROM
|
2
|
+
module RA
|
3
|
+
class Operation
|
4
|
+
|
5
|
+
class Group
|
6
|
+
# FIXME: only reading from a relation should be allowed here so this is
|
7
|
+
# obviously too much
|
8
|
+
include Charlatan.new(:relation)
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
attr_reader :options, :header
|
13
|
+
|
14
|
+
def initialize(relation, options)
|
15
|
+
super
|
16
|
+
@options = options
|
17
|
+
@header = relation.header + options.keys - attribute_names
|
18
|
+
end
|
19
|
+
|
20
|
+
def each(&block)
|
21
|
+
return to_enum unless block
|
22
|
+
|
23
|
+
tuples = relation.to_a
|
24
|
+
|
25
|
+
result = tuples.each_with_object({}) do |tuple, grouped|
|
26
|
+
left = tuple.reject { |k,_| attribute_names.include?(k) }
|
27
|
+
right = tuple.reject { |k,_| !attribute_names.include?(k) }
|
28
|
+
|
29
|
+
grouped[left] ||= {}
|
30
|
+
grouped[left][key] ||= []
|
31
|
+
grouped[left][key] << right if right.values.any?
|
32
|
+
end
|
33
|
+
|
34
|
+
result.map { |k,v| k.merge(v) }.each(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def key
|
38
|
+
options.keys.first
|
39
|
+
end
|
40
|
+
|
41
|
+
def attribute_names
|
42
|
+
options.values.first
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ROM
|
2
|
+
module RA
|
3
|
+
class Operation
|
4
|
+
|
5
|
+
class Join
|
6
|
+
include Charlatan.new(:left)
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :right
|
10
|
+
|
11
|
+
def initialize(left, right)
|
12
|
+
super
|
13
|
+
@left, @right = left, right
|
14
|
+
end
|
15
|
+
|
16
|
+
def header
|
17
|
+
left.header + right.header
|
18
|
+
end
|
19
|
+
|
20
|
+
def each(&block)
|
21
|
+
return to_enum unless block
|
22
|
+
|
23
|
+
join_map = right.each_with_object({}) { |tuple, h|
|
24
|
+
other = left.detect { |t| (tuple.to_a & t.to_a).any? }
|
25
|
+
(h[other] ||= []) << tuple if other
|
26
|
+
}
|
27
|
+
|
28
|
+
tuples = left.map { |tuple|
|
29
|
+
others = join_map[tuple]
|
30
|
+
next unless others.any?
|
31
|
+
others.map { |other| tuple.merge(other) }
|
32
|
+
}.flatten
|
33
|
+
|
34
|
+
tuples.each(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ROM
|
2
|
+
module RA
|
3
|
+
class Operation
|
4
|
+
|
5
|
+
class Wrap
|
6
|
+
# FIXME: only reading from a relation should be allowed here so this is
|
7
|
+
# obviously too much
|
8
|
+
include Charlatan.new(:relation)
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
attr_reader :options, :header
|
13
|
+
|
14
|
+
def initialize(relation, options)
|
15
|
+
super
|
16
|
+
@options = options
|
17
|
+
@header = relation.header + options.keys - attribute_names
|
18
|
+
end
|
19
|
+
|
20
|
+
def each(&block)
|
21
|
+
return to_enum unless block
|
22
|
+
|
23
|
+
results = relation.to_a.each_with_object([]) do |tuple, wrapped|
|
24
|
+
result = tuple.reject { |k,_| attribute_names.include?(k) }
|
25
|
+
result[key] = tuple.reject { |k,_| !attribute_names.include?(k) }
|
26
|
+
|
27
|
+
wrapped << result
|
28
|
+
end
|
29
|
+
|
30
|
+
results.each(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def key
|
34
|
+
options.keys.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def attribute_names
|
38
|
+
options.values.first
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/rom/reader.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module ROM
|
2
|
+
|
3
|
+
# Exposes mapped tuples via enumerable interface
|
4
|
+
#
|
5
|
+
# See example for each method
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
class Reader
|
9
|
+
include Enumerable
|
10
|
+
include Equalizer.new(:path, :relation, :mapper)
|
11
|
+
|
12
|
+
attr_reader :path, :relation, :header, :mappers, :mapper
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
def initialize(path, relation, mappers = {})
|
16
|
+
@path = path.to_s
|
17
|
+
@relation = relation
|
18
|
+
@header = relation.header
|
19
|
+
@mappers = mappers
|
20
|
+
|
21
|
+
names = @path.split('.')
|
22
|
+
|
23
|
+
mapper_key = names.reverse.detect { |name| mappers.key?(name.to_sym) }
|
24
|
+
@mapper = mappers.fetch(mapper_key.to_sym)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Yields tuples mapped to objects
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
#
|
31
|
+
# # accessing root relation
|
32
|
+
# rom.read(:users).each { |user| # ... }
|
33
|
+
#
|
34
|
+
# # accessing virtual relations
|
35
|
+
# rom.read(:users).adults.recent.active.each { |user| # ... }
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
def each
|
39
|
+
relation.each { |tuple| yield(mapper.load(tuple)) }
|
40
|
+
end
|
41
|
+
|
42
|
+
# @api private
|
43
|
+
def respond_to_missing?(name, include_private = false)
|
44
|
+
relation.respond_to?(name)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
def method_missing(name, *args, &block)
|
51
|
+
new_relation = relation.public_send(name, *args, &block)
|
52
|
+
|
53
|
+
splits = path.split('.')
|
54
|
+
splits << name
|
55
|
+
new_path = splits.join('.')
|
56
|
+
|
57
|
+
self.class.new(new_path, new_relation, mappers)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|