rom 0.7.0 → 0.7.1

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: 9513b27090115add08f6f4f4d8e446b7e805d0d8
4
- data.tar.gz: 4a33b39b54ee48c1d551138688e864a3b6052970
3
+ metadata.gz: fef04bd27871ee67a85b39c0cfb4a2a9c588a01f
4
+ data.tar.gz: 6f5e0515a4cdd8d0df9816c574bf176aa9634783
5
5
  SHA512:
6
- metadata.gz: 17c5de3ec96c11fcf6d95fd06c89ef1f76af66f6468bdbb139a421f5a7125ae1aae228021303b778e8150abf4a3650753ec164eb4aa393997e3a44cd42bbfcaf
7
- data.tar.gz: 5ded7613f606353f66371b9bc537c6a24c30f72e58d8a741f24133e976fa2d898609ca56bd4bf41a993827b3bda3f937b69dbc1f9c7ce9b432439ddfad0a1786
6
+ metadata.gz: 9ea6324c33c9c7701ab5452f291d7c397e41062ea706c2345c66d167b9cad8e7914f39af536fa065cb76e3bdca612898bc89de9e0fa6d46a1e58a4c532b88576
7
+ data.tar.gz: 9954ad8c0413dc7e94d4170703b47dd3a80387a22236c37a86d71ce7b6a01b8588658673a0cd10224013cf7b166c6592f800585f5c2fa4ecc691825701d3dcb0
data/.rubocop.yml CHANGED
@@ -6,10 +6,6 @@ AllCops:
6
6
  Exclude:
7
7
  - tmp/**/*
8
8
 
9
- # It’s quite readable when we know what we are doing
10
- Lint/AssignmentInCondition:
11
- Enabled: false
12
-
13
9
  Style/BlockDelimiters:
14
10
  EnforcedStyle: semantic
15
11
  FunctionalMethods:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## v0.7.1 2015-05-22
2
+
3
+ ### Added
4
+
5
+ * Support for passing a block for custom coercion to `attribute` (gotar)
6
+ * `fold` mapping operation which groups keys from input tuples to array
7
+ of values from the first of listed keys (nepalez)
8
+ * Adapter `Relation` and command classes can specify `adapter` identifier
9
+ which allows using adapter-specific plugins w/o the need to specify adapter
10
+ when calling `use` (solnic)
11
+
12
+ ### Changed
13
+
14
+ * [rom/memory] `restrict` operation supports array as a value (gotar)
15
+ * [rom/memory] `restrict` operation supports regexp as a value (gotar)
16
+
17
+ [Compare v0.7.0...HEAD](https://github.com/rom-rb/rom/compare/v0.7.0...HEAD)
18
+
1
19
  ## v0.7.0 2015-05-17
2
20
 
3
21
  ### Added
data/lib/rom/command.rb CHANGED
@@ -9,7 +9,7 @@ module ROM
9
9
 
10
10
  include Equalizer.new(:relation, :options)
11
11
 
12
- defines :relation, :result, :input, :validator, :register_as
12
+ defines :adapter, :relation, :result, :input, :validator, :register_as
13
13
 
14
14
  input Hash
15
15
  validator proc {}
@@ -85,7 +85,6 @@ module ROM
85
85
  #
86
86
  # @api public
87
87
  def self.use(plugin, options = {})
88
- adapter = options.fetch(:adapter, :default)
89
88
  ROM.plugin_registry.commands.fetch(plugin, adapter).apply_to(self)
90
89
  end
91
90
 
@@ -47,6 +47,8 @@ module ROM
47
47
  Unwrap
48
48
  elsif type.equal?(:hash)
49
49
  meta[:wrap] ? Wrap : Hash
50
+ elsif meta[:fold]
51
+ Group
50
52
  elsif type.equal?(:array)
51
53
  meta[:group] ? Group : Array
52
54
  else
@@ -30,17 +30,22 @@ module ROM
30
30
  @reject_keys = options.fetch(:reject_keys)
31
31
  end
32
32
 
33
- # Define a mapping attribute with its options
33
+ # Define a mapping attribute with its options and/or block
34
34
  #
35
35
  # @example
36
36
  # dsl = AttributeDSL.new([])
37
37
  #
38
38
  # dsl.attribute(:name)
39
39
  # dsl.attribute(:email, from: 'user_email')
40
+ # dsl.attribute(:name) { 'John' }
41
+ # dsl.attribute(:name) { |t| t.upcase }
40
42
  #
41
43
  # @api public
42
- def attribute(name, options = EMPTY_HASH)
44
+ def attribute(name, options = EMPTY_HASH, &block)
43
45
  with_attr_options(name, options) do |attr_options|
46
+ raise ArgumentError,
47
+ "can't specify type and block at the same time" if options[:type] && block
48
+ attr_options[:coercer] = block if block
44
49
  add_attribute(name, attr_options)
45
50
  end
46
51
  end
@@ -166,6 +171,26 @@ module ROM
166
171
  end
167
172
  end
168
173
 
174
+ # Define an embedded hash attribute that requires "fold" transformation
175
+ #
176
+ # Typically this is used in sql context to fold single joined field
177
+ # to the array of values.
178
+ #
179
+ # @example
180
+ # dsl = AttributeDSL.new([])
181
+ #
182
+ # dsl.fold(tags: [:name])
183
+ #
184
+ # @see AttributeDSL#embedded
185
+ #
186
+ # @api public
187
+ def fold(*args)
188
+ with_name_or_options(*args) do |name, *|
189
+ fold_options = { type: :array, fold: true }
190
+ dsl(name, fold_options)
191
+ end
192
+ end
193
+
169
194
  # Define an embedded combined attribute that requires "combine" transformation
170
195
  #
171
196
  # Typically this can be used to process results of eager-loading
@@ -192,7 +217,7 @@ module ROM
192
217
  type: options.fetch(:type, :array),
193
218
  keys: options.fetch(:on),
194
219
  combine: true,
195
- header: dsl.header,
220
+ header: dsl.header
196
221
  }
197
222
 
198
223
  add_attribute(name, attr_opts)
@@ -260,7 +285,7 @@ module ROM
260
285
  dsl = new(options, &block)
261
286
  header = dsl.header
262
287
  add_attribute(name, options.update(header: header))
263
- header.each { |attr| exclude(attr.key) }
288
+ header.each { |attr| exclude(attr.key) unless name == attr.key }
264
289
  end
265
290
 
266
291
  # Define attributes from the `name => attributes` hash syntax
@@ -272,7 +297,7 @@ module ROM
272
297
  hash.each do |name, header|
273
298
  with_attr_options(name, options) do |attr_options|
274
299
  add_attribute(name, attr_options.update(header: header.zip))
275
- header.each { |attr| exclude(attr) }
300
+ header.each { |attr| exclude(attr) unless name == attr }
276
301
  end
277
302
  end
278
303
  end
@@ -10,6 +10,8 @@ module ROM
10
10
  #
11
11
  # @api public
12
12
  class Create < ROM::Commands::Create
13
+ adapter :memory
14
+
13
15
  # @see ROM::Commands::Create#execute
14
16
  def execute(tuple)
15
17
  attributes = input[tuple]
@@ -22,6 +24,8 @@ module ROM
22
24
  #
23
25
  # @api public
24
26
  class Update < ROM::Commands::Update
27
+ adapter :memory
28
+
25
29
  # @see ROM::Commands::Update#execute
26
30
  def execute(params)
27
31
  attributes = input[params]
@@ -34,6 +38,8 @@ module ROM
34
38
  #
35
39
  # @api public
36
40
  class Delete < ROM::Commands::Delete
41
+ adapter :memory
42
+
37
43
  # @see ROM::Commands::Delete#execute
38
44
  def execute
39
45
  tuples = target.to_a
@@ -30,10 +30,15 @@ module ROM
30
30
  #
31
31
  # @api public
32
32
  def restrict(criteria = nil)
33
- if criteria
34
- find_all { |tuple| criteria.all? { |k, v| tuple[k].eql?(v) } }
35
- else
36
- find_all { |tuple| yield(tuple) }
33
+ return find_all { |tuple| yield(tuple) } unless criteria
34
+ find_all do |tuple|
35
+ criteria.all? do |k, v|
36
+ case v
37
+ when Array then v.include?(tuple[k])
38
+ when Regexp then tuple[k].match(v)
39
+ else tuple[k].eql?(v)
40
+ end
41
+ end
37
42
  end
38
43
  end
39
44
 
@@ -10,6 +10,8 @@ module ROM
10
10
  class Relation < ROM::Relation
11
11
  include Enumerable
12
12
 
13
+ adapter :memory
14
+
13
15
  forward :take, :join, :project, :restrict, :order
14
16
 
15
17
  # Insert tuples into the relation
@@ -129,6 +129,6 @@ module ROM
129
129
  raise(UnknownPluginError, name)
130
130
  end
131
131
 
132
- alias [] fetch
132
+ alias_method :[], :fetch
133
133
  end
134
134
  end
@@ -91,13 +91,18 @@ module ROM
91
91
 
92
92
  # Visit plain attribute
93
93
  #
94
+ # It will call block transformation if it's used
95
+ #
94
96
  # If it's a typed attribute a coercion transformation is added
95
97
  #
96
98
  # @param [Header::Attribute] attribute
97
99
  #
98
100
  # @api private
99
101
  def visit_attribute(attribute)
100
- if attribute.typed?
102
+ coercer = attribute.meta[:coercer]
103
+ if coercer
104
+ t(:map_value, attribute.name, coercer)
105
+ elsif attribute.typed?
101
106
  t(:map_value, attribute.name, t(:"to_#{attribute.type}"))
102
107
  end
103
108
  end
@@ -189,6 +194,7 @@ module ROM
189
194
  name = attribute.name
190
195
  header = attribute.header
191
196
  keys = attribute.tuple_keys
197
+ fold = attribute.meta[:fold]
192
198
 
193
199
  other = header.groups
194
200
 
@@ -196,9 +202,13 @@ module ROM
196
202
  ops << t(:group, name, keys)
197
203
  ops << t(:map_array, t(:map_value, name, FILTER_EMPTY))
198
204
 
199
- ops << other.map { |attr|
200
- t(:map_array, t(:map_value, name, visit_group(attr, true)))
201
- }
205
+ if fold
206
+ ops << t(:map_array, t(:fold, name, keys.first))
207
+ else
208
+ ops << other.map { |attr|
209
+ t(:map_array, t(:map_value, name, visit_group(attr, true)))
210
+ }
211
+ end
202
212
  end
203
213
  else
204
214
  visit_array(attribute)
@@ -12,11 +12,12 @@ module ROM
12
12
  def inherited(klass)
13
13
  super
14
14
 
15
+ klass.extend ClassMacros
16
+ klass.defines :adapter
17
+
15
18
  return if klass.superclass == ROM::Relation
16
19
 
17
20
  klass.class_eval do
18
- extend ClassMacros
19
-
20
21
  use :registry_reader
21
22
 
22
23
  defines :repository, :dataset, :register_as, :exposed_relations
@@ -128,7 +129,6 @@ module ROM
128
129
  #
129
130
  # @api public
130
131
  def use(plugin, options = {})
131
- adapter = options.fetch(:adapter, :default)
132
132
  ROM.plugin_registry.relations.fetch(plugin, adapter).apply_to(self)
133
133
  end
134
134
 
@@ -6,8 +6,9 @@ module ROM
6
6
  define_method(old_name) do |*args, &block|
7
7
  warn <<-MSG.gsub(/^\s+/, '')
8
8
  #{self.class}##{old_name} is deprecated and will be removed in 1.0.0.
9
- Please use #{self.class}##{new_name} instead."
9
+ Please use #{self.class}##{new_name} instead.
10
10
  #{msg}
11
+ #{caller.detect { |l| !l.include?('lib/rom')}}
11
12
  MSG
12
13
  __send__(new_name, *args, &block)
13
14
  end
data/lib/rom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ROM
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.7.1'.freeze
3
3
  end
data/rom.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.test_files = `git ls-files -- {spec}/*`.split("\n")
16
16
  gem.license = 'MIT'
17
17
 
18
- gem.add_runtime_dependency 'transproc', '~> 0.2', '>= 0.2.1'
18
+ gem.add_runtime_dependency 'transproc', '~> 0.2', '>= 0.2.2'
19
19
  gem.add_runtime_dependency 'equalizer', '~> 0.0', '>= 0.0.9'
20
20
 
21
21
  gem.add_development_dependency 'rake', '~> 10.3'
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Mapper definition DSL' do
4
+ include_context 'users and tasks'
5
+
6
+ let(:header) { mapper.header }
7
+
8
+ describe 'folded relation mapper' do
9
+ before do
10
+ setup.relation(:tasks) do
11
+ def with_users
12
+ join(users)
13
+ end
14
+ end
15
+
16
+ setup.relation(:users) do
17
+ def with_tasks
18
+ join(tasks)
19
+ end
20
+ end
21
+ end
22
+
23
+ let(:rom) { setup.finalize }
24
+ let(:actual) do
25
+ rom.relation(:users).with_tasks.map_with(:users).to_a
26
+ end
27
+
28
+ it 'groups all attributes and folds the first key' do
29
+ setup.mappers do
30
+ define(:users) do
31
+ fold tasks: [:title, :priority]
32
+ end
33
+ end
34
+
35
+ expect(actual).to eql [
36
+ { name: 'Joe', email: 'joe@doe.org', tasks: ['be nice', 'sleep well'] },
37
+ { name: 'Jane', email: 'jane@doe.org', tasks: ['be cool'] }
38
+ ]
39
+ end
40
+
41
+ it 'is sensitive to the order of keys' do
42
+ setup.mappers do
43
+ define(:users) do
44
+ fold priorities: [:priority, :title]
45
+ end
46
+ end
47
+
48
+ expect(actual).to eql [
49
+ { name: 'Joe', email: 'joe@doe.org', priorities: [1, 2] },
50
+ { name: 'Jane', email: 'jane@doe.org', priorities: [2] }
51
+ ]
52
+ end
53
+ end
54
+ end
@@ -158,5 +158,27 @@ describe 'Mapper definition DSL' do
158
158
  ])
159
159
  )
160
160
  end
161
+
162
+ it 'allows defining grouped attributes with the same name as their keys' do
163
+ setup.mappers do
164
+ define(:with_tasks, parent: :users) do
165
+
166
+ attribute :name
167
+ attribute :email
168
+
169
+ group title: [:title, :priority]
170
+ end
171
+ end
172
+
173
+ rom = setup.finalize
174
+
175
+ jane = rom.relation(:users).with_tasks.map_with(:with_tasks).to_a.last
176
+
177
+ expect(jane).to eql(
178
+ name: 'Jane',
179
+ email: 'jane@doe.org',
180
+ title: [{ title: 'be cool', priority: 2 }]
181
+ )
182
+ end
161
183
  end
162
184
  end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'rom/memory'
3
+
4
+ describe 'Mappers / Attributes value' do
5
+ let(:setup) { ROM.setup(:memory) }
6
+
7
+ before do
8
+ setup.relation(:users)
9
+ end
10
+
11
+ it 'allows to manipulate attribute value' do
12
+ class Test::UserMapper < ROM::Mapper
13
+ relation :users
14
+
15
+ attribute :id
16
+ attribute :name, from: :first_name do
17
+ 'John'
18
+ end
19
+ attribute :age do
20
+ 9+9
21
+ end
22
+ attribute :weight do |t|
23
+ t+15
24
+ end
25
+ end
26
+
27
+ rom = setup.finalize
28
+
29
+ rom.relations.users << {
30
+ id: 123,
31
+ first_name: 'Jane',
32
+ weight: 75
33
+ }
34
+
35
+ jane = rom.relation(:users).as(:users).first
36
+
37
+ expect(jane).to eql(id: 123, name: 'John', weight: 90, age: 18)
38
+ end
39
+
40
+ it 'raise ArgumentError if type and block used at the same time' do
41
+ expect {
42
+ class Test::UserMapper < ROM::Mapper
43
+ relation :users
44
+
45
+ attribute :name, type: :string do
46
+ 'John'
47
+ end
48
+ end
49
+ }.to raise_error(ArgumentError, "can't specify type and block at the same time")
50
+ end
51
+ end
@@ -2,7 +2,7 @@ module Mutant
2
2
  class Selector
3
3
  # Expression based test selector
4
4
  class Expression < self
5
- def call(subject)
5
+ def call(_subject)
6
6
  integration.all_tests
7
7
  end
8
8
  end # Expression
@@ -49,6 +49,20 @@ describe ROM::Memory::Relation do
49
49
  { name: 'Joe', email: 'joe@doe.org', age: 12 }
50
50
  ])
51
51
  end
52
+
53
+ it 'allows to use array as a value' do
54
+ expect(relation.restrict(age: [10, 11])).to match_array([
55
+ { name: 'Jane', email: 'jane@doe.org', age: 10 },
56
+ { name: 'Jade', email: 'jade@doe.org', age: 11 }
57
+ ])
58
+ end
59
+
60
+ it 'allows to use regexp as a value' do
61
+ expect(relation.restrict(name: /\w{4}/)).to match_array([
62
+ { name: 'Jane', email: 'jane@doe.org', age: 10 },
63
+ { name: 'Jade', email: 'jade@doe.org', age: 11 }
64
+ ])
65
+ end
52
66
  end
53
67
 
54
68
  describe '#order' do
@@ -89,7 +89,7 @@ describe "ROM::PluginRegistry" do
89
89
  end
90
90
 
91
91
  setup.relation(:users) do
92
- use :lazy, adapter: :memory
92
+ use :lazy
93
93
  end
94
94
 
95
95
  expect(env.relation(:users)).to be_lazy
@@ -97,11 +97,16 @@ describe "ROM::PluginRegistry" do
97
97
 
98
98
  it "respects adapter restrictions" do
99
99
  Test::LazyPlugin = Module.new
100
+ Test::LazyMemoryPlugin = Module.new
100
101
  Test::LazySQLPlugin = Module.new
101
102
 
102
103
  ROM.plugins do
103
104
  register :lazy, Test::LazyPlugin, type: :command
104
105
 
106
+ adapter :memory do
107
+ register :lazy_command, Test::LazyMemoryPlugin, type: :command
108
+ end
109
+
105
110
  adapter :sql do
106
111
  register :lazy, Test::LazySQLPlugin, type: :command
107
112
  end
@@ -112,10 +117,17 @@ describe "ROM::PluginRegistry" do
112
117
  Class.new(ROM::Commands::Create[:memory]) do
113
118
  relation :users
114
119
  register_as :create
115
- use :lazy, adapter: :memory
120
+ use :lazy
121
+ end
122
+
123
+ Class.new(ROM::Commands::Update[:memory]) do
124
+ relation :users
125
+ register_as :update
126
+ use :lazy_command
116
127
  end
117
128
 
118
129
  expect(env.command(:users).create).not_to be_kind_of Test::LazySQLPlugin
119
130
  expect(env.command(:users).create).to be_kind_of Test::LazyPlugin
131
+ expect(env.command(:users).update).to be_kind_of Test::LazyMemoryPlugin
120
132
  end
121
133
  end
@@ -23,7 +23,7 @@ describe ROM::Relation::Graph do
23
23
  end
24
24
 
25
25
  setup.relation(:tasks) do
26
- def for_users(users)
26
+ def for_users(_users)
27
27
  self
28
28
  end
29
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-17 00:00:00.000000000 Z
11
+ date: 2015-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: transproc
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '0.2'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 0.2.1
22
+ version: 0.2.2
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '0.2'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.2.1
32
+ version: 0.2.2
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: equalizer
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -203,7 +203,9 @@ files:
203
203
  - spec/integration/mappers/deep_embedded_spec.rb
204
204
  - spec/integration/mappers/definition_dsl_spec.rb
205
205
  - spec/integration/mappers/embedded_spec.rb
206
+ - spec/integration/mappers/fold_spec.rb
206
207
  - spec/integration/mappers/group_spec.rb
208
+ - spec/integration/mappers/overwrite_attributes_value_spec.rb
207
209
  - spec/integration/mappers/prefixing_attributes_spec.rb
208
210
  - spec/integration/mappers/registering_custom_mappers_spec.rb
209
211
  - spec/integration/mappers/renaming_attributes_spec.rb