rom 0.3.1 → 0.4.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.
data/lib/rom/relation.rb CHANGED
@@ -43,24 +43,6 @@ module ROM
43
43
  dataset.each(&block)
44
44
  end
45
45
 
46
- # @api private
47
- def insert(tuple)
48
- dataset.insert(tuple)
49
- self
50
- end
51
-
52
- # @api private
53
- def update(tuple)
54
- dataset.update(tuple)
55
- self
56
- end
57
-
58
- # @api private
59
- def delete
60
- dataset.delete
61
- self
62
- end
63
-
64
46
  end
65
47
 
66
48
  end
@@ -16,6 +16,24 @@ module ROM
16
16
  adapter[name]
17
17
  end
18
18
 
19
+ # Set a logger for the adapter
20
+ #
21
+ # @param [Object] logger
22
+ #
23
+ # @api public
24
+ def use_logger(logger)
25
+ adapter.logger = logger
26
+ end
27
+
28
+ # Return logger used by the adapter
29
+ #
30
+ # @return [Object] logger
31
+ #
32
+ # @api public
33
+ def logger
34
+ adapter.logger
35
+ end
36
+
19
37
  # Return the database connection provided by the adapter
20
38
  #
21
39
  # @api public
@@ -32,14 +50,18 @@ module ROM
32
50
 
33
51
  # @api private
34
52
  def respond_to_missing?(name, include_private = false)
35
- adapter[name]
53
+ adapter.dataset?(name) || super
36
54
  end
37
55
 
38
56
  private
39
57
 
40
58
  # @api private
41
- def method_missing(name)
42
- adapter[name]
59
+ def method_missing(name, *args, &block)
60
+ if adapter.dataset?(name)
61
+ adapter[name]
62
+ else
63
+ super
64
+ end
43
65
  end
44
66
  end
45
67
 
data/lib/rom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ROM
2
- VERSION = '0.3.1'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ require 'logger'
4
+
5
+ describe 'Adapters / Setting logger' do
6
+ let(:logger_class) do
7
+ Class.new do
8
+ attr_reader :messages
9
+
10
+ def initialize
11
+ @messages = []
12
+ end
13
+
14
+ def info(msg)
15
+ @messages << msg
16
+ end
17
+ end
18
+ end
19
+
20
+ let(:logger) do
21
+ logger_class.new
22
+ end
23
+
24
+ it 'sets up a logger for a given adapter' do
25
+ setup = ROM.setup(memory: 'memory://localhost')
26
+
27
+ setup.memory.use_logger(logger)
28
+
29
+ rom = setup.finalize
30
+
31
+ rom.memory.logger.info("test")
32
+
33
+ expect(logger.messages).to eql(["test"])
34
+ end
35
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Commands / Create' do
4
+ include_context 'users and tasks'
5
+
6
+ let(:users) { rom.commands.users }
7
+ let(:tasks) { rom.commands.tasks }
8
+
9
+ before do
10
+ UserValidator = Class.new do
11
+ ValidationError = Class.new(ROM::CommandError)
12
+
13
+ def self.call(params)
14
+ unless params[:name] && params[:email]
15
+ raise ValidationError, ":name and :email are required"
16
+ end
17
+ end
18
+ end
19
+
20
+ setup.relation(:users)
21
+ setup.relation(:tasks)
22
+
23
+ setup.commands(:users) do
24
+ define(:create) do
25
+ validator UserValidator
26
+ end
27
+ end
28
+
29
+ setup.commands(:tasks) do
30
+ define(:create)
31
+ end
32
+
33
+ end
34
+
35
+ it 'inserts user on successful validation' do
36
+ result = users.try { create(name: 'Piotr', email: 'piotr@solnic.eu') }
37
+
38
+ expect(result).to match_array([{ name: 'Piotr', email: 'piotr@solnic.eu' }])
39
+ end
40
+
41
+ it 'inserts user and associated task when things go well' do
42
+ result = users.try {
43
+ create(name: 'Piotr', email: 'piotr@solnic.eu')
44
+ } >-> users {
45
+ tasks.try {
46
+ create(name: users.first[:name], title: 'Finish command-api')
47
+ }
48
+ }
49
+
50
+ expect(result).to match_array([{ name: 'Piotr', title: 'Finish command-api' }])
51
+ end
52
+
53
+ it 'returns validation object with errors on failed validation' do
54
+ result = users.try { create(name: 'Piotr') }
55
+
56
+ expect(result.error).to be_instance_of(ValidationError)
57
+ expect(result.error.message).to eql(":name and :email are required")
58
+ expect(rom.relations.users.count).to be(2)
59
+ end
60
+
61
+ describe '"result" option' do
62
+
63
+ it 'returns a single tuple when set to :one' do
64
+ setup.commands(:users) do
65
+
66
+ define(:create_one, type: :create) do
67
+ result :one
68
+ end
69
+
70
+ end
71
+
72
+ tuple = { name: 'Piotr', email: 'piotr@solnic.eu' }
73
+
74
+ result = users.try {
75
+ create_one(tuple)
76
+ }
77
+
78
+ expect(result.value).to eql(tuple)
79
+ end
80
+
81
+ it 'allows only valid result types' do
82
+ expect {
83
+
84
+ setup.commands(:users) do
85
+ define(:create_one, type: :create) do
86
+ result :invalid_type
87
+ end
88
+ end
89
+ setup.finalize
90
+
91
+ }.to raise_error(ROM::InvalidOptionError)
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Commands / Delete' do
4
+ include_context 'users and tasks'
5
+
6
+ subject(:users) { rom.commands.users }
7
+
8
+ before do
9
+ setup.relation(:users) do
10
+ def by_name(name)
11
+ restrict(name: name)
12
+ end
13
+ end
14
+
15
+ setup.commands(:users) do
16
+ define(:delete)
17
+ end
18
+ end
19
+
20
+ it 'deletes all tuples when there is no restriction' do
21
+ result = users.try { delete }
22
+
23
+ expect(result).to match_array([
24
+ { name: 'Jane', email: 'jane@doe.org' },
25
+ { name: 'Joe', email: 'joe@doe.org' }
26
+ ])
27
+ end
28
+
29
+ it 'deletes tuples matching restriction' do
30
+ result = users.try { delete(:by_name, 'Joe').call }
31
+
32
+ expect(result).to match_array([{ name: 'Joe', email: 'joe@doe.org' }])
33
+ end
34
+
35
+ it 'returns untouched relation if there are no tuples to delete' do
36
+ result = users.try { delete(:by_name, 'Not here').call }
37
+
38
+ expect(result).to match_array([])
39
+ end
40
+
41
+ it 'returns deleted tuple when result is set to :one' do
42
+ setup.commands(:users) do
43
+ define(:delete_one, type: :delete) do
44
+ result :one
45
+ end
46
+ end
47
+
48
+ result = users.try { delete_one(:by_name, 'Jane').call }
49
+
50
+ expect(result.value).to eql(name: 'Jane', email: 'jane@doe.org')
51
+ end
52
+
53
+ it 'raises error when result is set to :one and relation contains more tuples' do
54
+ setup.commands(:users) do
55
+ define(:delete) do
56
+ result :one
57
+ end
58
+ end
59
+
60
+ result = users.try { delete }
61
+
62
+ expect(result.error).to be_instance_of(ROM::TupleCountMismatchError)
63
+
64
+ expect(rom.relations.users.to_a).to match_array([
65
+ { name: 'Jane', email: 'jane@doe.org' },
66
+ { name: 'Joe', email: 'joe@doe.org' }
67
+ ])
68
+ end
69
+
70
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Commands / Error handling' do
4
+ include_context 'users and tasks'
5
+
6
+ before do
7
+ setup.relation(:users)
8
+ setup.commands(:users) { define(:create) }
9
+ end
10
+
11
+ subject(:users) { rom.commands.users }
12
+
13
+ it 'rescues from ROM::CommandError' do
14
+ result = false
15
+ expect(users.try { raise ROM::CommandError } >-> test { result = true }).
16
+ to be_instance_of(ROM::Result::Failure)
17
+ expect(result).to be(false)
18
+ end
19
+
20
+ it 'raises other errors' do
21
+ expect { users.try { raise ArgumentError, 'test' } }.to raise_error(ArgumentError, 'test')
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Commands / Try api' do
4
+ include_context 'users and tasks'
5
+
6
+ before do
7
+ setup.relation(:users)
8
+
9
+ setup.commands(:users) do
10
+ define(:create)
11
+ end
12
+ end
13
+
14
+ let(:user_commands) { rom.command(:users) }
15
+
16
+ it 'exposes command functions inside the block' do
17
+ input = { name: 'Piotr', email: 'piotr@test.com' }
18
+
19
+ result = user_commands.try { create(input) }
20
+
21
+ expect(result.value).to eql([input])
22
+ end
23
+
24
+ it 'raises on method missing' do
25
+ expect { users.try { not_here } }.to raise_error(NameError)
26
+ end
27
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ require 'ostruct'
4
+
5
+ describe 'Commands / Update' do
6
+ include_context 'users and tasks'
7
+
8
+ subject(:users) { rom.commands.users }
9
+
10
+ before do
11
+ UserValidator = Class.new do
12
+ ValidationError = Class.new(ROM::CommandError)
13
+
14
+ def self.call(params)
15
+ raise ValidationError, ":email is required" unless params[:email]
16
+ end
17
+ end
18
+
19
+ setup.relation(:users) do
20
+ def all(criteria)
21
+ restrict(criteria)
22
+ end
23
+
24
+ def by_name(name)
25
+ restrict(name: name)
26
+ end
27
+ end
28
+
29
+ setup.commands(:users) do
30
+ define(:update) do
31
+ validator UserValidator
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ it 'update tuples on successful validation' do
38
+ result = users.try {
39
+ update(:all, name: 'Jane').set(email: 'jane.doe@test.com')
40
+ }
41
+
42
+ expect(result).to match_array([{ name: 'Jane', email: 'jane.doe@test.com' }])
43
+ end
44
+
45
+ it 'returns validation object with errors on failed validation' do
46
+ result = users.try { update(:all, name: 'Jane').set(email: nil) }
47
+
48
+ expect(result.error).to be_instance_of(ValidationError)
49
+ expect(result.error.message).to eql(':email is required')
50
+
51
+ expect(rom.relations.users.restrict(name: 'Jane')).to match_array([
52
+ { name: 'Jane', email: 'jane@doe.org' }
53
+ ])
54
+ end
55
+
56
+ describe '"result" option' do
57
+
58
+ it 'returns a single tuple when set to :one' do
59
+ setup.commands(:users) do
60
+ define(:update_one, type: :update) do
61
+ result :one
62
+ end
63
+ end
64
+
65
+ result = users.try {
66
+ update_one(:by_name, 'Jane').set(email: 'jane.doe@test.com')
67
+ }
68
+
69
+ expect(result.value).to eql(name: 'Jane', email: 'jane.doe@test.com')
70
+ end
71
+
72
+ it 'raises error when there is more than one tuple and result is set to :one' do
73
+ setup.commands(:users) do
74
+ define(:update_one, type: :update) do
75
+ result :one
76
+ end
77
+ end
78
+
79
+ result = users.try {
80
+ update_one.set(email: 'jane.doe@test.com')
81
+ }
82
+
83
+ expect(result.error).to be_instance_of(ROM::TupleCountMismatchError)
84
+
85
+ expect(rom.relations.users).to match_array([
86
+ { name: 'Jane', email: 'jane@doe.org' },
87
+ { name: 'Joe', email: 'joe@doe.org' }
88
+ ])
89
+ end
90
+
91
+ it 'allows only valid result types' do
92
+ expect {
93
+
94
+ setup.commands(:users) do
95
+ define(:create_one, type: :create) do
96
+ result :invalid_type
97
+ end
98
+ end
99
+ setup.finalize
100
+
101
+ }.to raise_error(ROM::InvalidOptionError)
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -30,4 +30,39 @@ describe 'Setting up ROM' do
30
30
  expect(rom.mappers).to eql(ROM::ReaderRegistry.new)
31
31
  end
32
32
  end
33
+
34
+ describe 'quick setup' do
35
+ it 'exposes boot DSL inside the setup block' do
36
+ User = Class.new { include Virtus.value_object; values { attribute :name, String } }
37
+
38
+ rom = ROM.setup(memory: 'memory://test') do
39
+ schema do
40
+ base_relation(:users) do
41
+ repository :memory
42
+ attribute :name
43
+ end
44
+ end
45
+
46
+ relation(:users) do
47
+ def by_name(name)
48
+ restrict(name: name)
49
+ end
50
+ end
51
+
52
+ commands(:users) do
53
+ define(:create)
54
+ end
55
+
56
+ mappers do
57
+ define(:users) do
58
+ model User
59
+ end
60
+ end
61
+ end
62
+
63
+ rom.command(:users).create.call(name: 'Jane')
64
+
65
+ expect(rom.read(:users).by_name('Jane').to_a).to eql([User.new(name: 'Jane')])
66
+ end
67
+ end
33
68
  end