rom 0.6.0.beta3 → 0.6.0.rc1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3860ac36d7e9d938342e022c7fb0e227fc6273e1
4
- data.tar.gz: 1a650f4573310fd5d543839c258338344827989b
3
+ metadata.gz: 0ea7e1a611937b1db33e9bb5f6495c116ff163bd
4
+ data.tar.gz: 98243b1e80fc05db0038fdeea7465a7b913990f8
5
5
  SHA512:
6
- metadata.gz: b1239e346a1626da09367e8d4c37a222a43d9a7f70dd248d427dc2046fc1272033913859b38c9e5183d316bd31296c72bdbff4d0a55be590ab9f94f9c907d669
7
- data.tar.gz: 4a711b1a776e137d1e953acc7eb8230f5c7c1695d5e8a812b9a7ef23f2673f3fe91619c83b9a93e317dc72e66f2a39b6ce37d082a19ab03110c84abbea8be7e8
6
+ metadata.gz: fde6f6d9efc44fe43c2bffefdfcf809e6f7d3991a4059185ac58909c3455c3e821dda71843503c3a4c31f36178eaff0454ef48c2d71c4ae1106beb30f95623c0
7
+ data.tar.gz: 1fa7191e19578e32cfc72c71db3eeebb7a28a7410dd98db8064af3f19f85bfa19e7397f22d46c86eaf42013943a91cc7f754f89fd2f62e96f6de12737fc7e7c9
data/.gitignore CHANGED
@@ -12,6 +12,7 @@ test/tmp
12
12
  test/version_tmp
13
13
  tmp
14
14
  Gemfile.lock
15
+ vendor/
15
16
 
16
17
  .idea/
17
18
 
@@ -19,5 +20,3 @@ Gemfile.lock
19
20
  .yardoc
20
21
  _yardoc
21
22
  doc/
22
-
23
- Gemfile.lock
data/CHANGELOG.md CHANGED
@@ -20,8 +20,9 @@
20
20
 
21
21
  ### Changed
22
22
 
23
- * [BREAKING] Command API was simplified - commands should be accessed directly in `.try` block (solnic)
24
23
  * [BREAKING] Schema DSL was **removed** - attributes can be specified only in mapper DSL
24
+ * [BREAKING] Reader was **removed** in favor of relation interface with explicit mapping (solnic)
25
+ * [BREAKING] Command API was simplified - commands should be accessed directly in `.try` block (solnic)
25
26
  and default repository can be changed when defining a relation (solnic)
26
27
  * `.setup` interface requires either an adapter identifier or can accept a repository
27
28
  instance (aflatter)
data/Gemfile CHANGED
@@ -29,7 +29,7 @@ end
29
29
 
30
30
  group :benchmarks do
31
31
  gem 'activerecord', '4.2.0'
32
- gem 'benchmark-ips'
32
+ gem 'benchmark-ips', git: 'https://github.com/evanphx/benchmark-ips.git', branch: 'master'
33
33
  end
34
34
 
35
35
  group :tools do
data/lib/rom.rb CHANGED
@@ -13,7 +13,6 @@ require 'rom/support/class_builder'
13
13
  # core parts
14
14
  require 'rom/relation'
15
15
  require 'rom/mapper'
16
- require 'rom/reader'
17
16
  require 'rom/command'
18
17
 
19
18
  # default mapper processor using Transproc gem
@@ -33,5 +32,4 @@ module ROM
33
32
  extend Global
34
33
 
35
34
  RelationRegistry = Class.new(Registry)
36
- ReaderRegistry = Class.new(Registry)
37
35
  end
@@ -21,7 +21,7 @@ module ROM
21
21
  option :target
22
22
  option :validator, reader: true
23
23
  option :input, reader: true
24
- option :curry_args, type: Array, reader: true, default: []
24
+ option :curry_args, type: Array, reader: true, default: EMPTY_ARRAY
25
25
 
26
26
  attr_reader :relation
27
27
 
data/lib/rom/env.rb CHANGED
@@ -88,15 +88,17 @@ module ROM
88
88
  #
89
89
  # @param [Symbol] name of the registered reader
90
90
  #
91
+ # @deprecated
92
+ #
91
93
  # @api public
92
94
  def read(name, &block)
93
- reader = readers[name]
94
-
95
- if block
96
- yield(reader)
97
- else
98
- reader
99
- end
95
+ warn <<-MSG
96
+ #{self.class}#read is deprecated.
97
+ Please use `#{self.class}#relation(#{name.inspect})` instead.
98
+ For mapping append `.map_with(:your_mapper_name)`
99
+ [#{caller[0]}]
100
+ MSG
101
+ relation(name, &block)
100
102
  end
101
103
 
102
104
  # Returns commands registry for the given relation
@@ -1,4 +1,9 @@
1
- require 'thread_safe'
1
+ begin
2
+ require 'thread_safe'
3
+ rescue LoadError
4
+ raise LoadError, 'Please install the `thread_safe` gem.'
5
+ end
6
+
2
7
  require 'rom/memory/dataset'
3
8
 
4
9
  module ROM
data/lib/rom/relation.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'set'
2
- require 'rom/relation/registry_reader'
1
+ require 'rom/relation/class_interface'
2
+
3
3
  require 'rom/relation/lazy'
4
4
  require 'rom/relation/curried'
5
5
 
@@ -19,147 +19,26 @@ module ROM
19
19
  #
20
20
  # @api public
21
21
  class Relation
22
- extend ClassMacros
22
+ extend ClassInterface
23
23
 
24
24
  include Options
25
25
  include Equalizer.new(:dataset)
26
26
 
27
- defines :repository, :dataset, :register_as, :exposed_relations
28
-
29
- repository :default
30
-
31
- attr_reader :name, :dataset, :exposed_relations
32
-
33
- # Register adapter relation subclasses during setup phase
34
- #
35
- # In adition those subclasses are extended with an interface for accessing
36
- # relation registry and to define `register_as` setting
37
- #
38
- # @api private
39
- def self.inherited(klass)
40
- super
41
-
42
- return if self == ROM::Relation
43
-
44
- klass.class_eval do
45
- include ROM::Relation::RegistryReader
46
-
47
- dataset(default_name)
48
- exposed_relations Set.new
49
-
50
- def self.register_as(value = Undefined)
51
- if value == Undefined
52
- @register_as || dataset
53
- else
54
- super
55
- end
56
- end
57
-
58
- def self.method_added(name)
59
- super
60
- exposed_relations << name if public_instance_methods.include?(name)
61
- end
62
- end
63
-
64
- ROM.register_relation(klass)
65
- end
66
-
67
- # Return adapter-specific relation subclass
68
- #
69
- # @example
70
- # ROM::Relation[:memory]
71
- # # => ROM::Memory::Relation
27
+ # Dataset used by the relation
72
28
  #
73
- # @return [Class]
74
- #
75
- # @api public
76
- def self.[](type)
77
- ROM.adapters.fetch(type).const_get(:Relation)
78
- end
79
-
80
- # Dynamically define a method that will forward to the dataset and wrap
81
- # response in the relation itself
82
- #
83
- # @example
84
- # class SomeAdapterRelation < ROM::Relation
85
- # forward :super_query
86
- # end
87
- #
88
- # @api public
89
- def self.forward(*methods)
90
- methods.each do |method|
91
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
92
- def #{method}(*args, &block)
93
- __new__(dataset.__send__(:#{method}, *args, &block))
94
- end
95
- RUBY
96
- end
97
- end
98
-
99
- # Return default relation name used for `register_as` setting
29
+ # This object is provided by the repository during the setup
100
30
  #
101
- # @return [Symbol]
31
+ # @return [Object]
102
32
  #
103
33
  # @api private
104
- def self.default_name
105
- return unless name
106
- Inflector.underscore(name).gsub('/', '_').to_sym
107
- end
108
-
109
- # Build relation registry of specified descendant classes
110
- #
111
- # This is used by the setup
112
- #
113
- # @param [Hash] repositories
114
- # @param [Array] descendants a list of relation descendants
115
- #
116
- # @return [Hash]
117
- #
118
- # @api private
119
- def self.registry(repositories, descendants)
120
- registry = {}
121
-
122
- descendants.each do |klass|
123
- # TODO: raise a meaningful error here and add spec covering the case
124
- # where klass' repository points to non-existant repo
125
- repository = repositories.fetch(klass.repository)
126
- dataset = repository.dataset(klass.dataset)
127
-
128
- relation = klass.new(dataset, __registry__: registry)
129
-
130
- name = klass.register_as
131
-
132
- if registry.key?(name)
133
- raise RelationAlreadyDefinedError,
134
- "Relation with `register_as #{name.inspect}` registered more " \
135
- "than once"
136
- end
137
-
138
- registry[name] = relation
139
- end
140
-
141
- registry.each_value do |relation|
142
- relation.class.finalize(registry, relation)
143
- end
144
-
145
- registry
146
- end
34
+ attr_reader :dataset
147
35
 
148
36
  # @api private
149
37
  def initialize(dataset, options = {})
150
38
  @dataset = dataset
151
- @name = self.class.dataset
152
- @exposed_relations = self.class.exposed_relations
153
39
  super
154
40
  end
155
41
 
156
- # Hook to finalize a relation after its instance was created
157
- #
158
- # @api private
159
- def self.finalize(_env, _relation)
160
- # noop
161
- end
162
-
163
42
  # Yield dataset tuples
164
43
  #
165
44
  # @yield [Hash]
@@ -170,7 +49,7 @@ module ROM
170
49
  dataset.each { |tuple| yield(tuple) }
171
50
  end
172
51
 
173
- # Materialize relation into an array
52
+ # Materialize a relation into an array
174
53
  #
175
54
  # @return [Array<Hash>]
176
55
  #
@@ -179,11 +58,12 @@ module ROM
179
58
  to_enum.to_a
180
59
  end
181
60
 
182
- # @api private
183
- def repository
184
- self.class.repository
185
- end
186
-
61
+ # Turn relation into a lazy-loadable and composable relation
62
+ #
63
+ # @see Lazy
64
+ #
65
+ # @return [Lazy]
66
+ #
187
67
  # @api public
188
68
  def to_lazy(*args)
189
69
  Lazy.new(self, *args)
@@ -0,0 +1,180 @@
1
+ require 'set'
2
+ require 'rom/relation/registry_reader'
3
+
4
+ module ROM
5
+ class Relation
6
+ module ClassInterface
7
+ # Register adapter relation subclasses during setup phase
8
+ #
9
+ # In adition those subclasses are extended with an interface for accessing
10
+ # relation registry and to define `register_as` setting
11
+ #
12
+ # @api private
13
+ def inherited(klass)
14
+ super
15
+
16
+ return if klass.superclass == ROM::Relation
17
+
18
+ klass.class_eval do
19
+ extend ClassMacros
20
+ include RegistryReader
21
+
22
+ defines :repository, :dataset, :register_as, :exposed_relations
23
+
24
+ repository :default
25
+
26
+ dataset(default_name)
27
+ exposed_relations Set.new
28
+
29
+ # Relation's dataset name
30
+ #
31
+ # In example a table name in an SQL database
32
+ #
33
+ # @return [Symbol]
34
+ #
35
+ # @api public
36
+ attr_reader :name
37
+
38
+ # A set with public method names that return "virtual" relations
39
+ #
40
+ # Only those methods are exposed directly on relations return by
41
+ # Env#relation interface
42
+ #
43
+ # @return [Set]
44
+ #
45
+ # @api private
46
+ attr_reader :exposed_relations
47
+
48
+ # Set or get name under which a relation will be registered
49
+ #
50
+ # This defaults to `dataset` name
51
+ #
52
+ # @return [Symbol]
53
+ #
54
+ # @api public
55
+ def self.register_as(value = Undefined)
56
+ if value == Undefined
57
+ super() || dataset
58
+ else
59
+ super
60
+ end
61
+ end
62
+
63
+ # Hook used to collect public method names
64
+ #
65
+ # @api private
66
+ def self.method_added(name)
67
+ super
68
+ exposed_relations << name if public_instance_methods.include?(name)
69
+ end
70
+
71
+ # @api private
72
+ def initialize(dataset, options = {})
73
+ @name = self.class.dataset
74
+ @exposed_relations = self.class.exposed_relations
75
+ super
76
+ end
77
+
78
+ # Return name of the source repository of this relation
79
+ #
80
+ # @return [Symbol]
81
+ #
82
+ # @api private
83
+ def repository
84
+ self.class.repository
85
+ end
86
+ end
87
+
88
+ ROM.register_relation(klass)
89
+ end
90
+
91
+ # Return adapter-specific relation subclass
92
+ #
93
+ # @example
94
+ # ROM::Relation[:memory]
95
+ # # => ROM::Memory::Relation
96
+ #
97
+ # @return [Class]
98
+ #
99
+ # @api public
100
+ def [](type)
101
+ ROM.adapters.fetch(type).const_get(:Relation)
102
+ end
103
+
104
+ # Dynamically define a method that will forward to the dataset and wrap
105
+ # response in the relation itself
106
+ #
107
+ # @example
108
+ # class SomeAdapterRelation < ROM::Relation
109
+ # forward :super_query
110
+ # end
111
+ #
112
+ # @api public
113
+ def forward(*methods)
114
+ methods.each do |method|
115
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
116
+ def #{method}(*args, &block)
117
+ __new__(dataset.__send__(:#{method}, *args, &block))
118
+ end
119
+ RUBY
120
+ end
121
+ end
122
+
123
+ # Return default relation name used for `register_as` setting
124
+ #
125
+ # @return [Symbol]
126
+ #
127
+ # @api private
128
+ def default_name
129
+ return unless name
130
+ Inflector.underscore(name).gsub('/', '_').to_sym
131
+ end
132
+
133
+ # Build relation registry of specified descendant classes
134
+ #
135
+ # This is used by the setup
136
+ #
137
+ # @param [Hash] repositories
138
+ # @param [Array] descendants a list of relation descendants
139
+ #
140
+ # @return [Hash]
141
+ #
142
+ # @api private
143
+ def registry(repositories, descendants)
144
+ registry = {}
145
+
146
+ descendants.each do |klass|
147
+ # TODO: raise a meaningful error here and add spec covering the case
148
+ # where klass' repository points to non-existant repo
149
+ repository = repositories.fetch(klass.repository)
150
+ dataset = repository.dataset(klass.dataset)
151
+
152
+ relation = klass.new(dataset, __registry__: registry)
153
+
154
+ name = klass.register_as
155
+
156
+ if registry.key?(name)
157
+ raise RelationAlreadyDefinedError,
158
+ "Relation with `register_as #{name.inspect}` registered more " \
159
+ "than once"
160
+ end
161
+
162
+ registry[name] = relation
163
+ end
164
+
165
+ registry.each_value do |relation|
166
+ relation.class.finalize(registry, relation)
167
+ end
168
+
169
+ registry
170
+ end
171
+
172
+ # Hook to finalize a relation after its instance was created
173
+ #
174
+ # @api private
175
+ def finalize(_env, _relation)
176
+ # noop
177
+ end
178
+ end
179
+ end
180
+ end