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.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -3
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +13 -14
  5. data/{Changelog.md → CHANGELOG.md} +12 -3
  6. data/Gemfile +14 -10
  7. data/Gemfile.devtools +16 -16
  8. data/Guardfile +2 -5
  9. data/README.md +68 -56
  10. data/Rakefile +3 -3
  11. data/config/flay.yml +2 -2
  12. data/config/mutant.yml +2 -4
  13. data/config/rubocop.yml +30 -0
  14. data/lib/rom.rb +47 -9
  15. data/lib/rom/adapter.rb +52 -0
  16. data/lib/rom/adapter/memory.rb +70 -0
  17. data/lib/rom/boot.rb +158 -0
  18. data/lib/rom/boot/base_relation_dsl.rb +43 -0
  19. data/lib/rom/boot/dsl.rb +30 -0
  20. data/lib/rom/boot/mapper_dsl.rb +23 -0
  21. data/lib/rom/boot/relation_dsl.rb +23 -0
  22. data/lib/rom/boot/schema_dsl.rb +27 -0
  23. data/lib/rom/env.rb +48 -0
  24. data/lib/rom/header.rb +123 -0
  25. data/lib/rom/mapper.rb +38 -153
  26. data/lib/rom/mapper_builder.rb +85 -0
  27. data/lib/rom/model_builder.rb +58 -0
  28. data/lib/rom/ra.rb +196 -0
  29. data/lib/rom/ra/operation/group.rb +49 -0
  30. data/lib/rom/ra/operation/join.rb +41 -0
  31. data/lib/rom/ra/operation/wrap.rb +45 -0
  32. data/lib/rom/reader.rb +62 -0
  33. data/lib/rom/reader_builder.rb +28 -0
  34. data/lib/rom/relation.rb +41 -350
  35. data/lib/rom/relation_builder.rb +48 -0
  36. data/lib/rom/repository.rb +28 -53
  37. data/lib/rom/support/registry.rb +34 -0
  38. data/lib/rom/version.rb +3 -0
  39. data/rom.gemspec +12 -10
  40. data/spec/integration/adapters/extending_relations_spec.rb +43 -0
  41. data/spec/integration/mappers/definition_dsl_spec.rb +82 -0
  42. data/spec/integration/mappers/prefixing_attributes_spec.rb +42 -0
  43. data/spec/integration/mappers/renaming_attributes_spec.rb +138 -0
  44. data/spec/integration/ra/group_spec.rb +47 -0
  45. data/spec/integration/ra/join_spec.rb +24 -0
  46. data/spec/integration/ra/wrap_spec.rb +37 -0
  47. data/spec/integration/relations/reading_spec.rb +116 -0
  48. data/spec/integration/relations/registry_dsl_spec.rb +44 -0
  49. data/spec/integration/schema_spec.rb +29 -0
  50. data/spec/integration/setup_spec.rb +18 -0
  51. data/spec/shared/users_and_tasks.rb +34 -0
  52. data/spec/spec_helper.rb +14 -36
  53. data/spec/unit/rom/adapter_spec.rb +59 -0
  54. data/spec/unit/rom/header_spec.rb +58 -0
  55. data/spec/unit/rom/mapper_spec.rb +27 -0
  56. data/spec/unit/rom/ra/operation/group_spec.rb +55 -0
  57. data/spec/unit/rom/ra/operation/wrap_spec.rb +29 -0
  58. data/spec/unit/rom/relation_spec.rb +34 -0
  59. metadata +122 -190
  60. data/lib/rom/constants.rb +0 -16
  61. data/lib/rom/environment.rb +0 -105
  62. data/lib/rom/environment/builder.rb +0 -71
  63. data/lib/rom/mapper/attribute.rb +0 -108
  64. data/lib/rom/mapper/builder.rb +0 -58
  65. data/lib/rom/mapper/builder/definition.rb +0 -162
  66. data/lib/rom/mapper/header.rb +0 -103
  67. data/lib/rom/mapper/loader_builder.rb +0 -26
  68. data/lib/rom/schema.rb +0 -21
  69. data/lib/rom/schema/builder.rb +0 -59
  70. data/lib/rom/schema/definition.rb +0 -84
  71. data/lib/rom/schema/definition/relation.rb +0 -80
  72. data/lib/rom/schema/definition/relation/base.rb +0 -27
  73. data/lib/rom/session.rb +0 -111
  74. data/lib/rom/session/environment.rb +0 -67
  75. data/lib/rom/session/identity_map.rb +0 -43
  76. data/lib/rom/session/mapper.rb +0 -62
  77. data/lib/rom/session/relation.rb +0 -140
  78. data/lib/rom/session/state.rb +0 -59
  79. data/lib/rom/session/state/created.rb +0 -22
  80. data/lib/rom/session/state/deleted.rb +0 -25
  81. data/lib/rom/session/state/persisted.rb +0 -34
  82. data/lib/rom/session/state/transient.rb +0 -20
  83. data/lib/rom/session/state/updated.rb +0 -29
  84. data/lib/rom/session/tracker.rb +0 -62
  85. data/lib/rom/support/axiom/adapter.rb +0 -111
  86. data/lib/rom/support/axiom/adapter/data_objects.rb +0 -38
  87. data/lib/rom/support/axiom/adapter/memory.rb +0 -25
  88. data/lib/rom/support/axiom/adapter/postgres.rb +0 -19
  89. data/lib/rom/support/axiom/adapter/sqlite3.rb +0 -20
  90. data/lib/version.rb +0 -3
  91. data/spec/integration/environment_setup_spec.rb +0 -24
  92. data/spec/integration/grouped_mappers_spec.rb +0 -87
  93. data/spec/integration/join_and_group_spec.rb +0 -76
  94. data/spec/integration/join_and_wrap_spec.rb +0 -68
  95. data/spec/integration/mapping_embedded_relations_spec.rb +0 -73
  96. data/spec/integration/mapping_relations_spec.rb +0 -120
  97. data/spec/integration/schema_definition_spec.rb +0 -152
  98. data/spec/integration/session_spec.rb +0 -87
  99. data/spec/integration/wrapped_mappers_spec.rb +0 -73
  100. data/spec/shared/unit/environment_context.rb +0 -6
  101. data/spec/shared/unit/loader.rb +0 -11
  102. data/spec/shared/unit/loader_identity.rb +0 -13
  103. data/spec/shared/unit/mapper_context.rb +0 -11
  104. data/spec/shared/unit/relation_context.rb +0 -82
  105. data/spec/shared/unit/session_environment_context.rb +0 -11
  106. data/spec/shared/unit/session_relation_context.rb +0 -18
  107. data/spec/support/helper.rb +0 -34
  108. data/spec/support/ice_nine_config.rb +0 -10
  109. data/spec/support/test_mapper.rb +0 -110
  110. data/spec/unit/rom/environment/builder/mapping_spec.rb +0 -24
  111. data/spec/unit/rom/environment/builder/schema_spec.rb +0 -33
  112. data/spec/unit/rom/environment/class_methods/setup_spec.rb +0 -18
  113. data/spec/unit/rom/environment/repository_spec.rb +0 -21
  114. data/spec/unit/rom/mapper/attribute/embedded_collection/to_ast_spec.rb +0 -18
  115. data/spec/unit/rom/mapper/attribute/embedded_value/to_ast_spec.rb +0 -16
  116. data/spec/unit/rom/mapper/attribute/rename_spec.rb +0 -9
  117. data/spec/unit/rom/mapper/attribute/to_ast_spec.rb +0 -9
  118. data/spec/unit/rom/mapper/builder/class_methods/call_spec.rb +0 -61
  119. data/spec/unit/rom/mapper/class_methods/build_spec.rb +0 -55
  120. data/spec/unit/rom/mapper/dump_spec.rb +0 -11
  121. data/spec/unit/rom/mapper/group_spec.rb +0 -35
  122. data/spec/unit/rom/mapper/header/each_spec.rb +0 -26
  123. data/spec/unit/rom/mapper/header/element_reader_spec.rb +0 -21
  124. data/spec/unit/rom/mapper/header/group_spec.rb +0 -18
  125. data/spec/unit/rom/mapper/header/join_spec.rb +0 -14
  126. data/spec/unit/rom/mapper/header/keys_spec.rb +0 -29
  127. data/spec/unit/rom/mapper/header/project_spec.rb +0 -13
  128. data/spec/unit/rom/mapper/header/rename_spec.rb +0 -11
  129. data/spec/unit/rom/mapper/header/to_ast_spec.rb +0 -11
  130. data/spec/unit/rom/mapper/header/wrap_spec.rb +0 -18
  131. data/spec/unit/rom/mapper/identity_from_tuple_spec.rb +0 -11
  132. data/spec/unit/rom/mapper/identity_spec.rb +0 -11
  133. data/spec/unit/rom/mapper/join_spec.rb +0 -15
  134. data/spec/unit/rom/mapper/load_spec.rb +0 -11
  135. data/spec/unit/rom/mapper/new_object_spec.rb +0 -14
  136. data/spec/unit/rom/mapper/project_spec.rb +0 -11
  137. data/spec/unit/rom/mapper/rename_spec.rb +0 -16
  138. data/spec/unit/rom/mapper/wrap_spec.rb +0 -35
  139. data/spec/unit/rom/relation/delete_spec.rb +0 -15
  140. data/spec/unit/rom/relation/drop_spec.rb +0 -11
  141. data/spec/unit/rom/relation/each_spec.rb +0 -23
  142. data/spec/unit/rom/relation/first_spec.rb +0 -19
  143. data/spec/unit/rom/relation/group_spec.rb +0 -29
  144. data/spec/unit/rom/relation/inject_mapper_spec.rb +0 -17
  145. data/spec/unit/rom/relation/insert_spec.rb +0 -13
  146. data/spec/unit/rom/relation/last_spec.rb +0 -19
  147. data/spec/unit/rom/relation/one_spec.rb +0 -49
  148. data/spec/unit/rom/relation/rename_spec.rb +0 -21
  149. data/spec/unit/rom/relation/replace_spec.rb +0 -13
  150. data/spec/unit/rom/relation/restrict_spec.rb +0 -25
  151. data/spec/unit/rom/relation/sort_by_spec.rb +0 -25
  152. data/spec/unit/rom/relation/take_spec.rb +0 -11
  153. data/spec/unit/rom/relation/to_a_spec.rb +0 -20
  154. data/spec/unit/rom/relation/update_spec.rb +0 -25
  155. data/spec/unit/rom/relation/wrap_spec.rb +0 -29
  156. data/spec/unit/rom/repository/class_methods/build_spec.rb +0 -27
  157. data/spec/unit/rom/repository/element_reader_spec.rb +0 -21
  158. data/spec/unit/rom/repository/element_writer_spec.rb +0 -18
  159. data/spec/unit/rom/schema/builder/class_methods/build_spec.rb +0 -103
  160. data/spec/unit/rom/schema/element_reader_spec.rb +0 -15
  161. data/spec/unit/rom/session/class_methods/start_spec.rb +0 -23
  162. data/spec/unit/rom/session/clean_predicate_spec.rb +0 -21
  163. data/spec/unit/rom/session/environment/element_reader_spec.rb +0 -13
  164. data/spec/unit/rom/session/flush_spec.rb +0 -58
  165. data/spec/unit/rom/session/mapper/load_spec.rb +0 -47
  166. data/spec/unit/rom/session/relation/delete_spec.rb +0 -28
  167. data/spec/unit/rom/session/relation/dirty_predicate_spec.rb +0 -35
  168. data/spec/unit/rom/session/relation/identity_spec.rb +0 -11
  169. data/spec/unit/rom/session/relation/new_spec.rb +0 -50
  170. data/spec/unit/rom/session/relation/save_spec.rb +0 -50
  171. data/spec/unit/rom/session/relation/state_spec.rb +0 -23
  172. data/spec/unit/rom/session/relation/track_spec.rb +0 -23
  173. data/spec/unit/rom/session/relation/tracking_predicate_spec.rb +0 -23
  174. data/spec/unit/rom/session/relation/update_attributes_spec.rb +0 -45
  175. 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