rom 0.4.2 → 0.5.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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +81 -0
  3. data/.travis.yml +2 -1
  4. data/CHANGELOG.md +41 -0
  5. data/Gemfile +12 -8
  6. data/Guardfile +17 -11
  7. data/README.md +7 -7
  8. data/Rakefile +29 -0
  9. data/lib/rom.rb +9 -66
  10. data/lib/rom/adapter.rb +45 -12
  11. data/lib/rom/adapter/memory.rb +0 -4
  12. data/lib/rom/adapter/memory/commands.rb +0 -10
  13. data/lib/rom/adapter/memory/dataset.rb +18 -6
  14. data/lib/rom/adapter/memory/storage.rb +0 -3
  15. data/lib/rom/command_registry.rb +24 -43
  16. data/lib/rom/commands.rb +5 -6
  17. data/lib/rom/commands/create.rb +5 -5
  18. data/lib/rom/commands/delete.rb +8 -6
  19. data/lib/rom/commands/result.rb +82 -0
  20. data/lib/rom/commands/update.rb +5 -4
  21. data/lib/rom/commands/with_options.rb +1 -4
  22. data/lib/rom/config.rb +70 -0
  23. data/lib/rom/env.rb +11 -3
  24. data/lib/rom/global.rb +107 -0
  25. data/lib/rom/header.rb +122 -89
  26. data/lib/rom/header/attribute.rb +148 -0
  27. data/lib/rom/mapper.rb +46 -67
  28. data/lib/rom/mapper_builder.rb +20 -73
  29. data/lib/rom/mapper_builder/mapper_dsl.rb +114 -0
  30. data/lib/rom/mapper_builder/model_dsl.rb +29 -0
  31. data/lib/rom/mapper_registry.rb +21 -0
  32. data/lib/rom/model_builder.rb +11 -17
  33. data/lib/rom/processor.rb +28 -0
  34. data/lib/rom/processor/transproc.rb +105 -0
  35. data/lib/rom/reader.rb +81 -21
  36. data/lib/rom/reader_builder.rb +14 -4
  37. data/lib/rom/relation.rb +19 -5
  38. data/lib/rom/relation_builder.rb +20 -6
  39. data/lib/rom/repository.rb +0 -2
  40. data/lib/rom/setup.rb +156 -0
  41. data/lib/rom/{boot → setup}/base_relation_dsl.rb +4 -8
  42. data/lib/rom/setup/command_dsl.rb +46 -0
  43. data/lib/rom/setup/finalize.rb +125 -0
  44. data/lib/rom/setup/mapper_dsl.rb +19 -0
  45. data/lib/rom/{boot → setup}/relation_dsl.rb +1 -4
  46. data/lib/rom/setup/schema_dsl.rb +33 -0
  47. data/lib/rom/support/registry.rb +10 -6
  48. data/lib/rom/version.rb +1 -1
  49. data/rom.gemspec +3 -1
  50. data/spec/integration/adapters/extending_relations_spec.rb +0 -2
  51. data/spec/integration/commands/create_spec.rb +2 -9
  52. data/spec/integration/commands/delete_spec.rb +4 -5
  53. data/spec/integration/commands/error_handling_spec.rb +4 -3
  54. data/spec/integration/commands/update_spec.rb +3 -8
  55. data/spec/integration/mappers/deep_embedded_spec.rb +52 -0
  56. data/spec/integration/mappers/definition_dsl_spec.rb +0 -118
  57. data/spec/integration/mappers/embedded_spec.rb +82 -0
  58. data/spec/integration/mappers/group_spec.rb +170 -0
  59. data/spec/integration/mappers/prefixing_attributes_spec.rb +2 -2
  60. data/spec/integration/mappers/renaming_attributes_spec.rb +8 -6
  61. data/spec/integration/mappers/symbolizing_attributes_spec.rb +80 -0
  62. data/spec/integration/mappers/wrap_spec.rb +162 -0
  63. data/spec/integration/multi_repo_spec.rb +64 -0
  64. data/spec/integration/relations/reading_spec.rb +12 -8
  65. data/spec/integration/relations/registry_dsl_spec.rb +1 -3
  66. data/spec/integration/schema_spec.rb +10 -0
  67. data/spec/integration/setup_spec.rb +57 -6
  68. data/spec/spec_helper.rb +2 -1
  69. data/spec/unit/config_spec.rb +60 -0
  70. data/spec/unit/rom/adapter/memory/dataset_spec.rb +52 -0
  71. data/spec/unit/rom/adapter_spec.rb +31 -11
  72. data/spec/unit/rom/header_spec.rb +60 -16
  73. data/spec/unit/rom/mapper_builder_spec.rb +311 -0
  74. data/spec/unit/rom/mapper_registry_spec.rb +25 -0
  75. data/spec/unit/rom/mapper_spec.rb +4 -5
  76. data/spec/unit/rom/model_builder_spec.rb +15 -13
  77. data/spec/unit/rom/processor/transproc_spec.rb +331 -0
  78. data/spec/unit/rom/reader_spec.rb +73 -0
  79. data/spec/unit/rom/registry_spec.rb +38 -0
  80. data/spec/unit/rom/relation_spec.rb +0 -1
  81. data/spec/unit/rom/setup_spec.rb +55 -0
  82. data/spec/unit/rom_spec.rb +14 -0
  83. metadata +62 -22
  84. data/Gemfile.devtools +0 -71
  85. data/lib/rom/boot.rb +0 -197
  86. data/lib/rom/boot/command_dsl.rb +0 -48
  87. data/lib/rom/boot/dsl.rb +0 -37
  88. data/lib/rom/boot/mapper_dsl.rb +0 -23
  89. data/lib/rom/boot/schema_dsl.rb +0 -27
  90. data/lib/rom/ra.rb +0 -172
  91. data/lib/rom/ra/operation/group.rb +0 -47
  92. data/lib/rom/ra/operation/join.rb +0 -39
  93. data/lib/rom/ra/operation/wrap.rb +0 -45
  94. data/lib/rom/transformer.rb +0 -77
  95. data/spec/integration/ra/group_spec.rb +0 -46
  96. data/spec/integration/ra/join_spec.rb +0 -50
  97. data/spec/integration/ra/wrap_spec.rb +0 -37
  98. data/spec/unit/rom/ra/operation/group_spec.rb +0 -55
  99. data/spec/unit/rom/ra/operation/wrap_spec.rb +0 -29
  100. data/spec/unit/rom/transformer_spec.rb +0 -41
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Mappers / embedded' do
4
+ let(:setup) { ROM.setup('memory://test') }
5
+ let(:rom) { setup.finalize }
6
+
7
+ it 'allows mapping embedded tuples' do
8
+ setup.schema do
9
+ base_relation(:users) do
10
+ repository :default
11
+ attribute 'name'
12
+ attribute 'tasks'
13
+ end
14
+ end
15
+
16
+ setup.relation(:users)
17
+
18
+ setup.mappers do
19
+ define(:users) do
20
+ model name: 'User'
21
+
22
+ attribute :name, from: 'name'
23
+
24
+ embedded :tasks, from: 'tasks' do
25
+ attribute :title, from: 'title'
26
+ end
27
+ end
28
+ end
29
+
30
+ rom.schema.users << {
31
+ 'name' => 'Jane',
32
+ 'tasks' => [{ 'title' => 'Task One' }, { 'title' => 'Task Two' }]
33
+ }
34
+
35
+ jane = rom.read(:users).to_a.first
36
+
37
+ expect(rom.read(:users).mapper.header.map(&:name)).to eql([:name, :tasks])
38
+
39
+ expect(jane.name).to eql('Jane')
40
+ expect(jane.tasks).to eql([{ title: 'Task One' }, { title: 'Task Two' }])
41
+ end
42
+
43
+ it 'allows mapping embedded tuple' do
44
+ setup.schema do
45
+ base_relation(:users) do
46
+ repository :default
47
+ attribute 'name'
48
+ attribute 'address'
49
+ end
50
+ end
51
+
52
+ setup.relation(:users)
53
+
54
+ setup.mappers do
55
+ define(:users) do
56
+ model name: 'User'
57
+
58
+ attribute :name, from: 'name'
59
+
60
+ embedded :address, from: 'address', type: :hash do
61
+ model name: 'Address'
62
+ attribute :street, from: 'street'
63
+ attribute :city, from: 'city'
64
+ end
65
+ end
66
+ end
67
+
68
+ rom.schema.users << {
69
+ 'name' => 'Jane',
70
+ 'address' => { 'street' => 'Somewhere 1', 'city' => 'NYC' }
71
+ }
72
+
73
+ jane = rom.read(:users).to_a.first
74
+
75
+ expect(rom.read(:users).mapper.header.map(&:name)).to eql([:name, :address])
76
+
77
+ Address.send(:include, Equalizer.new(:street, :city))
78
+
79
+ expect(jane.name).to eql('Jane')
80
+ expect(jane.address).to eql(Address.new(street: 'Somewhere 1', city: 'NYC'))
81
+ end
82
+ end
@@ -0,0 +1,170 @@
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
+ before do
9
+ setup.relation(:users) do
10
+ def email_index
11
+ project(:email)
12
+ end
13
+ end
14
+ end
15
+
16
+ describe 'grouped relation mapper' do
17
+ before do
18
+ setup.relation(:tasks) do
19
+ def with_users
20
+ join(users)
21
+ end
22
+ end
23
+
24
+ setup.relation(:users) do
25
+ def with_tasks
26
+ join(tasks)
27
+ end
28
+ end
29
+
30
+ setup.mappers do
31
+ define(:users) do
32
+ model name: 'User'
33
+
34
+ attribute :name
35
+ attribute :email
36
+ end
37
+ end
38
+ end
39
+
40
+ it 'allows defining grouped attributes via options hash' do
41
+ setup.mappers do
42
+ define(:with_tasks, parent: :users) do
43
+ model name: 'UserWithTasks'
44
+
45
+ attribute :name
46
+ attribute :email
47
+
48
+ group tasks: [:title, :priority]
49
+ end
50
+ end
51
+
52
+ rom = setup.finalize
53
+
54
+ UserWithTasks.send(:include, Equalizer.new(:name, :email, :tasks))
55
+
56
+ jane = rom.read(:users).with_tasks.to_a.last
57
+
58
+ expect(jane).to eql(
59
+ UserWithTasks.new(
60
+ name: 'Jane',
61
+ email: 'jane@doe.org',
62
+ tasks: [{ title: 'be cool', priority: 2 }]
63
+ )
64
+ )
65
+ end
66
+
67
+ it 'allows defining grouped attributes via block' do
68
+ setup.mappers do
69
+ define(:with_tasks, parent: :users) do
70
+ model name: 'UserWithTasks'
71
+
72
+ attribute :name
73
+ attribute :email
74
+
75
+ group :tasks do
76
+ attribute :title
77
+ attribute :priority
78
+ end
79
+ end
80
+ end
81
+
82
+ rom = setup.finalize
83
+
84
+ UserWithTasks.send(:include, Equalizer.new(:name, :email, :tasks))
85
+
86
+ jane = rom.read(:users).with_tasks.to_a.last
87
+
88
+ expect(jane).to eql(
89
+ UserWithTasks.new(
90
+ name: 'Jane',
91
+ email: 'jane@doe.org',
92
+ tasks: [{ title: 'be cool', priority: 2 }]
93
+ )
94
+ )
95
+ end
96
+
97
+ it 'allows defining grouped attributes mapped to a model via block' do
98
+ setup.mappers do
99
+ define(:with_tasks, parent: :users) do
100
+ model name: 'UserWithTasks'
101
+
102
+ attribute :name
103
+ attribute :email
104
+
105
+ group :tasks do
106
+ model name: 'Task'
107
+
108
+ attribute :title
109
+ attribute :priority
110
+ end
111
+ end
112
+ end
113
+
114
+ rom = setup.finalize
115
+
116
+ UserWithTasks.send(:include, Equalizer.new(:name, :email, :tasks))
117
+ Task.send(:include, Equalizer.new(:title, :priority))
118
+
119
+ jane = rom.read(:users).with_tasks.to_a.last
120
+
121
+ expect(jane).to eql(
122
+ UserWithTasks.new(
123
+ name: 'Jane',
124
+ email: 'jane@doe.org',
125
+ tasks: [Task.new(title: 'be cool', priority: 2)]
126
+ )
127
+ )
128
+ end
129
+
130
+ it 'allows defining nested grouped attributes mapped to a model via block' do
131
+ setup.mappers do
132
+ define(:tasks)
133
+
134
+ define(:with_users, parent: :tasks, inherit_header: false) do
135
+ model name: 'TaskWithUsers'
136
+
137
+ attribute :title
138
+ attribute :priority
139
+
140
+ group :users do
141
+ model name: 'TaskUser'
142
+
143
+ attribute :name
144
+
145
+ group :contacts do
146
+ model name: 'Contact'
147
+ attribute :email
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ rom = setup.finalize
154
+
155
+ TaskWithUsers.send(:include, Equalizer.new(:title, :priority, :users))
156
+ TaskUser.send(:include, Equalizer.new(:name, :contacts))
157
+ Contact.send(:include, Equalizer.new(:email))
158
+
159
+ task = rom.read(:tasks).with_users.to_a.first
160
+
161
+ expect(task).to eql(
162
+ TaskWithUsers.new(title: 'be nice', priority: 1, users: [
163
+ TaskUser.new(name: 'Joe', contacts: [
164
+ Contact.new(email: 'joe@doe.org')
165
+ ])
166
+ ])
167
+ )
168
+ end
169
+ end
170
+ end
@@ -32,11 +32,11 @@ describe 'Mappers / Prefixing attributes' do
32
32
 
33
33
  User.send(:include, Equalizer.new(:id, :name, :email))
34
34
 
35
- rom.schema.users << { user_id: 123, user_name: 'Jane', user_email: 'jane@doe.org' }
35
+ rom.schema.users << { user_id: 123, user_name: 'Jane',
36
+ user_email: 'jane@doe.org' }
36
37
 
37
38
  jane = rom.read(:users).to_a.first
38
39
 
39
40
  expect(jane).to eql(User.new(id: 123, name: 'Jane', email: 'jane@doe.org'))
40
41
  end
41
-
42
42
  end
@@ -23,8 +23,6 @@ describe 'Mappers / Renaming attributes' do
23
23
  setup.relation(:addresses)
24
24
 
25
25
  setup.relation(:users) do
26
- include ROM::RA
27
-
28
26
  def with_address
29
27
  join(addresses)
30
28
  end
@@ -83,12 +81,14 @@ describe 'Mappers / Renaming attributes' do
83
81
  UserWithAddress.send(:include, Equalizer.new(:id, :name, :address))
84
82
 
85
83
  rom.schema.users << { _id: 123, user_name: 'Jane' }
86
- rom.schema.addresses << { _id: 123, address_id: 321, address_street: 'Street 1' }
84
+ rom.schema.addresses << { _id: 123, address_id: 321,
85
+ address_street: 'Street 1' }
87
86
 
88
87
  jane = rom.read(:users).with_address.first
89
88
 
90
89
  expect(jane).to eql(
91
- UserWithAddress.new(id: 123, name: 'Jane', address: { id: 321, street: 'Street 1' })
90
+ UserWithAddress.new(id: 123, name: 'Jane',
91
+ address: { id: 321, street: 'Street 1' })
92
92
  )
93
93
  end
94
94
 
@@ -119,8 +119,10 @@ describe 'Mappers / Renaming attributes' do
119
119
  UserWithAddresses.send(:include, Equalizer.new(:id, :name, :addresses))
120
120
 
121
121
  rom.schema.users << { _id: 123, user_name: 'Jane' }
122
- rom.schema.addresses << { _id: 123, address_id: 321, address_street: 'Street 1' }
123
- rom.schema.addresses << { _id: 123, address_id: 654, address_street: 'Street 2' }
122
+ rom.schema.addresses << { _id: 123, address_id: 321,
123
+ address_street: 'Street 1' }
124
+ rom.schema.addresses << { _id: 123, address_id: 654,
125
+ address_street: 'Street 2' }
124
126
 
125
127
  jane = rom.read(:users).with_addresses.first
126
128
 
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Mappers / Symbolizing atributes' do
4
+ let(:setup) { ROM.setup(memory: 'memory://test') }
5
+ let(:rom) { setup.finalize }
6
+
7
+ before do
8
+ setup.schema do
9
+ base_relation(:users) do
10
+ repository :memory
11
+
12
+ attribute 'user_id'
13
+ attribute 'first_name'
14
+ attribute 'email'
15
+ end
16
+
17
+ base_relation(:tasks) do
18
+ repository :memory
19
+
20
+ attribute 'title'
21
+ attribute 'task_priority'
22
+ attribute 'task_description'
23
+ end
24
+ end
25
+ end
26
+
27
+ it 'automatically maps all attributes using top-level settings' do
28
+ setup.mappers do
29
+ define(:users, symbolize_keys: true, inherit_header: false, prefix: 'user') do
30
+ attribute :id
31
+
32
+ wrap :details, prefix: 'first' do
33
+ attribute :name
34
+ end
35
+
36
+ wrap :contact, prefix: false do
37
+ attribute :email
38
+ end
39
+ end
40
+ end
41
+
42
+ rom.schema.users << {
43
+ 'user_id' => 123,
44
+ 'first_name' => 'Jane',
45
+ 'email' => 'jane@doe.org'
46
+ }
47
+
48
+ jane = rom.read(:users).first
49
+
50
+ expect(jane).to eql(
51
+ id: 123, details: { name: 'Jane' }, contact: { email: 'jane@doe.org' }
52
+ )
53
+ end
54
+
55
+ it 'automatically maps all attributes using settings for wrap block' do
56
+ setup.mappers do
57
+ define(:tasks, symbolize_keys: true) do
58
+ attribute :title
59
+
60
+ wrap :details, prefix: 'task' do
61
+ attribute :priority
62
+ attribute :description
63
+ end
64
+ end
65
+ end
66
+
67
+ rom.schema.tasks << {
68
+ 'title' => 'Task One',
69
+ 'task_priority' => 1,
70
+ 'task_description' => 'It is a task'
71
+ }
72
+
73
+ task = rom.read(:tasks).first
74
+
75
+ expect(task).to eql(
76
+ title: 'Task One',
77
+ details: { priority: 1, description: 'It is a task' }
78
+ )
79
+ end
80
+ end
@@ -0,0 +1,162 @@
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
+ before do
9
+ setup.relation(:users) do
10
+ def email_index
11
+ project(:email)
12
+ end
13
+ end
14
+ end
15
+
16
+ describe 'wrapped relation mapper' do
17
+ before do
18
+ setup.relation(:tasks) do
19
+ def with_user
20
+ join(users)
21
+ end
22
+ end
23
+
24
+ setup.relation(:users)
25
+
26
+ setup.mappers do
27
+ define(:tasks) do
28
+ model name: 'Task'
29
+
30
+ attribute :title
31
+ attribute :priority
32
+ end
33
+ end
34
+ end
35
+
36
+ it 'allows defining wrapped attributes via options hash' do
37
+ setup.mappers do
38
+ define(:with_user, parent: :tasks) do
39
+ model name: 'TaskWithUser'
40
+
41
+ attribute :title
42
+ attribute :priority
43
+
44
+ wrap user: [:email]
45
+ end
46
+ end
47
+
48
+ rom = setup.finalize
49
+
50
+ TaskWithUser.send(:include, Equalizer.new(:title, :priority, :user))
51
+
52
+ jane = rom.read(:tasks).with_user.to_a.last
53
+
54
+ expect(jane).to eql(
55
+ TaskWithUser.new(
56
+ title: 'be cool',
57
+ priority: 2,
58
+ user: { email: 'jane@doe.org' }
59
+ )
60
+ )
61
+ end
62
+
63
+ it 'allows defining wrapped attributes via options block' do
64
+ setup.mappers do
65
+ define(:with_user, parent: :tasks) do
66
+ model name: 'TaskWithUser'
67
+
68
+ attribute :title
69
+ attribute :priority
70
+
71
+ wrap :user do
72
+ attribute :email
73
+ end
74
+ end
75
+ end
76
+
77
+ rom = setup.finalize
78
+
79
+ TaskWithUser.send(:include, Equalizer.new(:title, :priority, :user))
80
+
81
+ jane = rom.read(:tasks).with_user.to_a.last
82
+
83
+ expect(jane).to eql(
84
+ TaskWithUser.new(
85
+ title: 'be cool',
86
+ priority: 2,
87
+ user: { email: 'jane@doe.org' }
88
+ )
89
+ )
90
+ end
91
+
92
+ it 'allows defining nested wrapped attributes via a block' do
93
+ setup.mappers do
94
+ define(:with_user, parent: :tasks, inherit_header: false) do
95
+ model name: 'TaskWithUser'
96
+
97
+ attribute :title
98
+ attribute :priority
99
+
100
+ wrap :user do
101
+ model name: 'TaskUser'
102
+ attribute :name
103
+
104
+ wrap :contact do
105
+ model name: 'Contact'
106
+ attribute :email
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ rom = setup.finalize
113
+
114
+ TaskWithUser.send(:include, Equalizer.new(:title, :priority, :user))
115
+ TaskUser.send(:include, Equalizer.new(:name, :contact))
116
+ Contact.send(:include, Equalizer.new(:email))
117
+
118
+ jane = rom.read(:tasks).with_user.to_a.last
119
+
120
+ expect(jane).to eql(
121
+ TaskWithUser.new(
122
+ title: 'be cool',
123
+ priority: 2,
124
+ user: TaskUser.new(
125
+ name: 'Jane', contact: Contact.new(email: 'jane@doe.org')
126
+ )
127
+ )
128
+ )
129
+ end
130
+
131
+ it 'allows defining wrapped attributes mapped to a model' do
132
+ setup.mappers do
133
+ define(:with_user, parent: :tasks) do
134
+ model name: 'TaskWithUser'
135
+
136
+ attribute :title
137
+ attribute :priority
138
+
139
+ wrap :user do
140
+ model name: 'User'
141
+ attribute :email
142
+ end
143
+ end
144
+ end
145
+
146
+ rom = setup.finalize
147
+
148
+ TaskWithUser.send(:include, Equalizer.new(:title, :priority, :user))
149
+ User.send(:include, Equalizer.new(:email))
150
+
151
+ jane = rom.read(:tasks).with_user.to_a.last
152
+
153
+ expect(jane).to eql(
154
+ TaskWithUser.new(
155
+ title: 'be cool',
156
+ priority: 2,
157
+ user: User.new(email: 'jane@doe.org')
158
+ )
159
+ )
160
+ end
161
+ end
162
+ end