vorpal 0.1.0.rc3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore DELETED
@@ -1,16 +0,0 @@
1
- /.bundle/
2
- /.idea/
3
- /.yardoc
4
- /Gemfile.lock
5
- /_yardoc/
6
- /coverage/
7
- /doc/
8
- /pkg/
9
- /spec/reports/
10
- /tmp/
11
- *.bundle
12
- *.so
13
- *.o
14
- *.a
15
- mkmf.log
16
- /projectFilesBackup
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --colour
@@ -1 +0,0 @@
1
- 2.1.6
data/.yardopts DELETED
@@ -1 +0,0 @@
1
- --no-private -m markdown lib/**/*.*
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in vorpal.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env rake
2
- begin
3
- require "bundler/gem_tasks"
4
- rescue LoadError
5
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks associated with creating new versions of the gem.'
6
- end
7
-
8
- require 'rspec/core/rake_task'
9
- RSpec::Core::RakeTask.new(:spec)
10
- task :default => :spec
@@ -1,143 +0,0 @@
1
- require 'vorpal/util/string_utils.rb'
2
-
3
- module Vorpal
4
- # Interface between the database and Vorpal
5
- #
6
- # Currently only works for PostgreSQL via ActiveRecord.
7
- class DbDriver
8
- def initialize
9
- @sequence_names = {}
10
- end
11
-
12
- def insert(class_config, db_objects)
13
- if defined? ActiveRecord::Import
14
- class_config.db_class.import(db_objects, validate: false)
15
- else
16
- db_objects.each do |db_object|
17
- db_object.save!(validate: false)
18
- end
19
- end
20
- end
21
-
22
- def update(class_config, db_objects)
23
- db_objects.each do |db_object|
24
- db_object.save!(validate: false)
25
- end
26
- end
27
-
28
- def destroy(class_config, ids)
29
- class_config.db_class.delete_all(id: ids)
30
- end
31
-
32
- # Loads instances of the given class by primary key.
33
- #
34
- # @param class_config [ClassConfig]
35
- # @return [[Object]] An array of entities.
36
- def load_by_id(class_config, ids)
37
- class_config.db_class.where(id: ids).to_a
38
- end
39
-
40
- # Loads instances of the given class whose foreign key has the given value.
41
- #
42
- # @param class_config [ClassConfig]
43
- # @param foreign_key_info [ForeignKeyInfo]
44
- # @return [[Object]] An array of entities.
45
- def load_by_foreign_key(class_config, id, foreign_key_info)
46
- arel = class_config.db_class.where(foreign_key_info.fk_column => id)
47
- arel = arel.where(foreign_key_info.fk_type_column => foreign_key_info.fk_type) if foreign_key_info.polymorphic?
48
- arel.order(:id).to_a
49
- end
50
-
51
- # Fetches primary key values to be used for new entities.
52
- #
53
- # @param class_config [ClassConfig] Config of the entity whose primary keys are being fetched.
54
- # @return [[Integer]] An array of unused primary keys.
55
- def get_primary_keys(class_config, count)
56
- result = execute("select nextval($1) from generate_series(1,$2);", [sequence_name(class_config), count])
57
- result.rows.map(&:first).map(&:to_i)
58
- end
59
-
60
- # Builds an ORM Class for accessing data in the given DB table.
61
- #
62
- # @param model_class [Class] The PORO class that we are creating a DB interface class for.
63
- # @param table_name [String] Name of the DB table the DB class should interface with.
64
- # @return [Class] ActiveRecord::Base Class
65
- def build_db_class(model_class, table_name)
66
- db_class = Class.new(ActiveRecord::Base) do
67
- class << self
68
- # This is overridden for two reasons:
69
- # 1) For anonymous classes, #name normally returns nil. Class names in Ruby come from the
70
- # name of the constant they are assigned to.
71
- # 2) Because the default implementation for Class#name for anonymous classes is very, very
72
- # slow. https://bugs.ruby-lang.org/issues/11119
73
- # Remove this override once #2 has been fixed!
74
- def name
75
- @name ||= "Vorpal_generated_ActiveRecord__Base_class_for_#{vorpal_model_class_name}"
76
- end
77
-
78
- # Overridden because, like #name, the default implementation for anonymous classes is very,
79
- # very slow.
80
- def to_s
81
- name
82
- end
83
-
84
- attr_accessor :vorpal_model_class_name
85
- end
86
- end
87
-
88
- db_class.vorpal_model_class_name = Util::StringUtils.escape_class_name(model_class.name)
89
- db_class.table_name = table_name
90
- db_class
91
- end
92
-
93
- # Builds a composable query object (e.g. ActiveRecord::Relation) with Vorpal methods mixed in.
94
- #
95
- # @param class_config [ClassConfig] Config of the entity whose db representations should be returned.
96
- def query(class_config, aggregate_mapper)
97
- class_config.db_class.unscoped.extending(ArelQueryMethods.new(aggregate_mapper))
98
- end
99
-
100
- private
101
-
102
- def sequence_name(class_config)
103
- @sequence_names[class_config] ||= execute(
104
- "SELECT substring(column_default from '''(.*)''') FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1 AND column_name = 'id' LIMIT 1",
105
- [class_config.db_class.table_name]
106
- ).rows.first.first
107
- end
108
-
109
- def execute(sql, binds)
110
- binds = binds.map { |row| [nil, row] }
111
- ActiveRecord::Base.connection.exec_query(sql, 'SQL', binds)
112
- end
113
- end
114
-
115
- class ArelQueryMethods < Module
116
- def initialize(mapper)
117
- @mapper = mapper
118
- end
119
-
120
- def extended(descendant)
121
- super
122
- descendant.extend(Methods)
123
- descendant.vorpal_aggregate_mapper = @mapper
124
- end
125
-
126
- # Methods in this module will appear on any composable
127
- module Methods
128
- attr_writer :vorpal_aggregate_mapper
129
-
130
- # See {AggregateMapper#load_many}.
131
- def load_many
132
- db_roots = self.to_a
133
- @vorpal_aggregate_mapper.load_many(db_roots)
134
- end
135
-
136
- # See {AggregateMapper#load_one}.
137
- def load_one
138
- db_root = self.first
139
- @vorpal_aggregate_mapper.load_one(db_root)
140
- end
141
- end
142
- end
143
- end
@@ -1,51 +0,0 @@
1
- module DbHelpers
2
- module_function
3
-
4
- CONNECTION_SETTINGS = {
5
- adapter: 'postgresql',
6
- host: 'localhost',
7
- database: 'vorpal_test',
8
- min_messages: 'error',
9
- # Change the following to reflect your database settings
10
- # username: 'vorpal',
11
- # password: 'pass',
12
- }
13
-
14
- def ensure_database_exists
15
- test_database_name = CONNECTION_SETTINGS.fetch(:database)
16
- if !db_exists?(test_database_name)
17
- db_connection.create_database(test_database_name)
18
- end
19
- end
20
-
21
- def db_exists?(db_name)
22
- ActiveRecord::Base.establish_connection(CONNECTION_SETTINGS.merge(database: 'template1'))
23
-
24
- return db_connection.exec_query("SELECT 1 from pg_database WHERE datname='#{db_name}';").present?
25
- end
26
-
27
- def db_connection
28
- ActiveRecord::Base.connection
29
- end
30
-
31
- def establish_connection
32
- ActiveRecord::Base.establish_connection(CONNECTION_SETTINGS)
33
- end
34
-
35
- # when you change a table's columns, set force to true to re-generate the table in the DB
36
- def define_table(table_name, columns, force)
37
- if !db_connection.table_exists?(table_name) || force
38
- db_connection.create_table(table_name, force: true) do |t|
39
- columns.each do |name, type|
40
- t.send(type, name)
41
- end
42
- end
43
- end
44
- end
45
-
46
- def defineAr(table_name)
47
- Class.new(ActiveRecord::Base) do
48
- self.table_name = table_name
49
- end
50
- end
51
- end
@@ -1,26 +0,0 @@
1
- require 'ruby-prof'
2
-
3
- # In order to use these helpers do the following:
4
- # 1) Add `spec.add_development_dependency "ruby-prof"` to the vorpal.gemspec file.
5
- # 2) Do a `bundle update`
6
- # 3) Add `require 'helpers/profile_helpers'` to the spec where you wish to profile.
7
- module ProfileHelpers
8
- module_function
9
-
10
- # Runs a block a given number of times and outputs the profiling results in a 'Call Tree'
11
- # format suitable for display by a tool like KCacheGrind.
12
- #
13
- # - Installing QCacheGrind on OSX: http://nickology.com/2014/04/16/view-xdebug-cachegrind-files-on-mac-os/
14
- def output_callgrind(description, times=1, &block)
15
- RubyProf.measure_mode = RubyProf::PROCESS_TIME
16
- RubyProf.start
17
-
18
- times.times(&block)
19
-
20
- result = RubyProf.stop
21
- printer = RubyProf::CallTreePrinter.new(result)
22
- File.open("#{description}_#{DateTime.now.strftime("%FT%H:%M:%S%z")}.callgrind", "w") do |file|
23
- printer.print(file)
24
- end
25
- end
26
- end
@@ -1,36 +0,0 @@
1
- require 'active_record'
2
- require 'pg'
3
- require 'helpers/db_helpers'
4
-
5
- DbHelpers.ensure_database_exists
6
- DbHelpers.establish_connection
7
-
8
- RSpec.configure do |config|
9
- config.include DbHelpers
10
-
11
- # implements `use_transactional_fixtures = true`
12
- config.before(:each) do
13
- connection = ActiveRecord::Base.connection
14
- if ActiveRecord::VERSION::MAJOR == 3
15
- # from lib/active_record/fixtures.rb
16
- connection.increment_open_transactions
17
- connection.transaction_joinable = false
18
- connection.begin_db_transaction
19
- else
20
- connection.begin_transaction(joinable: false)
21
- end
22
- end
23
-
24
- config.after(:each) do
25
- connection = ActiveRecord::Base.connection
26
- if ActiveRecord::VERSION::MAJOR == 3
27
- if connection.open_transactions != 0
28
- connection.rollback_db_transaction
29
- connection.decrement_open_transactions
30
- end
31
- else
32
- connection.rollback_transaction if connection.transaction_open?
33
- end
34
- ActiveRecord::Base.clear_active_connections!
35
- end
36
- end
File without changes
@@ -1,911 +0,0 @@
1
- require 'integration_spec_helper'
2
- require 'vorpal'
3
- require 'virtus'
4
- require 'activerecord-import/base'
5
-
6
- describe 'AggregateMapper' do
7
-
8
- # for testing polymorphic associations
9
- class Bug
10
- include Virtus.model
11
-
12
- attribute :id, Integer
13
- attribute :name, String
14
- attribute :lives_on, Object
15
- end
16
-
17
- class Tree; end
18
-
19
- class Trunk
20
- include Virtus.model
21
-
22
- attribute :id, Integer
23
- attribute :length, Decimal
24
- attribute :bugs, Array[Bug]
25
- attribute :tree, Tree
26
- end
27
-
28
- class Branch
29
- include Virtus.model
30
-
31
- attribute :id, Integer
32
- attribute :length, Decimal
33
- attribute :tree, Tree
34
- attribute :branches, Array[Branch]
35
- attribute :bugs, Array[Bug]
36
- end
37
-
38
- class Fissure < ActiveRecord::Base; end
39
- class Swamp < ActiveRecord::Base; end
40
-
41
- class Tree
42
- include Virtus.model
43
-
44
- attribute :id, Integer
45
- attribute :name, String
46
- attribute :trunk, Trunk
47
- attribute :environment, Object
48
- attribute :fissures, Array[Fissure]
49
- attribute :branches, Array[Branch]
50
- end
51
-
52
- before(:all) do
53
- define_table('branches', {length: :decimal, tree_id: :integer, branch_id: :integer}, false)
54
- define_table('bugs', {name: :text, lives_on_id: :integer, lives_on_type: :string}, false)
55
- define_table('fissures', {length: :decimal, tree_id: :integer}, false)
56
- define_table('trees', {name: :text, trunk_id: :integer, environment_id: :integer, environment_type: :string}, false)
57
- define_table('trunks', {length: :decimal}, false)
58
- define_table('swamps', {}, false)
59
- end
60
-
61
- describe 'new records' do
62
- it 'saves attributes' do
63
- test_mapper = configure
64
-
65
- tree = Tree.new(name: 'backyard tree')
66
- test_mapper.persist(tree)
67
-
68
- tree_db = db_class_for(Tree, test_mapper).first
69
- expect(tree_db.name).to eq 'backyard tree'
70
- end
71
-
72
- it 'sets the id when first saved' do
73
- test_mapper = configure
74
-
75
- tree = Tree.new()
76
- test_mapper.persist(tree)
77
-
78
- expect(tree.id).to_not be nil
79
-
80
- tree_db = db_class_for(Tree, test_mapper).first
81
- expect(tree_db.id).to eq tree.id
82
- end
83
-
84
- it 'saves AR::Base objects' do
85
- test_mapper = configure
86
-
87
- fissure = Fissure.new(length: 21)
88
- tree = Tree.new(fissures: [fissure])
89
-
90
- test_mapper.persist(tree)
91
-
92
- expect(Fissure.first.length).to eq 21
93
- end
94
- end
95
-
96
- describe 'on error' do
97
- it 'nils ids of new objects' do
98
- db_driver = Vorpal::DbDriver.new
99
- test_mapper = configure(db_driver: db_driver)
100
-
101
- tree_db = db_class_for(Tree, test_mapper).create!
102
-
103
- expect(db_driver).to receive(:update).and_raise('not so good')
104
-
105
- fissure = Fissure.new
106
- tree = Tree.new(id: tree_db.id, fissures: [fissure])
107
-
108
- expect {
109
- test_mapper.persist(tree)
110
- }.to raise_error(Exception)
111
-
112
- expect(fissure.id).to eq nil
113
- expect(tree.id).to_not eq nil
114
- end
115
- end
116
-
117
- describe 'existing records' do
118
- it 'updates attributes' do
119
- test_mapper = configure
120
-
121
- tree = Tree.new(name: 'little tree')
122
- test_mapper.persist(tree)
123
-
124
- tree.name = 'big tree'
125
- test_mapper.persist(tree)
126
-
127
- tree_db = db_class_for(Tree, test_mapper).first
128
- expect(tree_db.name).to eq 'big tree'
129
- end
130
-
131
- it 'does not change the id on update' do
132
- test_mapper = configure
133
-
134
- tree = Tree.new
135
- test_mapper.persist(tree)
136
-
137
- original_id = tree.id
138
-
139
- tree.name = 'change it'
140
- test_mapper.persist(tree)
141
-
142
- expect(tree.id).to eq original_id
143
- end
144
-
145
- it 'does not create additional records' do
146
- test_mapper = configure
147
-
148
- tree = Tree.new
149
- test_mapper.persist(tree)
150
-
151
- tree.name = 'change it'
152
- test_mapper.persist(tree)
153
-
154
- expect(db_class_for(Tree, test_mapper).count).to eq 1
155
- end
156
-
157
- it 'removes orphans' do
158
- test_mapper = configure
159
-
160
- tree_db = db_class_for(Tree, test_mapper).create!
161
- db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
162
-
163
- tree = Tree.new(id: tree_db.id, branches: [])
164
-
165
- test_mapper.persist(tree)
166
-
167
- expect(db_class_for(Branch, test_mapper).count).to eq 0
168
- end
169
-
170
- it 'does not remove orphans from unowned associations' do
171
- test_mapper = configure_unowned
172
-
173
- tree_db = db_class_for(Tree, test_mapper).create!
174
- db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
175
-
176
- tree = Tree.new(id: tree_db.id, branches: [])
177
-
178
- test_mapper.persist(tree)
179
-
180
- expect(db_class_for(Branch, test_mapper).count).to eq 1
181
- end
182
- end
183
-
184
- it 'copies attributes to domain' do
185
- test_mapper = configure
186
-
187
- tree_db = db_class_for(Tree, test_mapper).create! name: 'tree name'
188
- tree = test_mapper.load_one(tree_db)
189
-
190
- expect(tree.id).to eq tree_db.id
191
- expect(tree.name).to eq 'tree name'
192
- end
193
-
194
- it 'hydrates ActiveRecord::Base associations' do
195
- test_mapper = configure
196
-
197
- tree_db = db_class_for(Tree, test_mapper).create!
198
- Fissure.create! length: 21, tree_id: tree_db.id
199
-
200
- tree = test_mapper.load_one(tree_db)
201
-
202
- expect(tree.fissures.first.length).to eq 21
203
- end
204
-
205
- describe 'cycles' do
206
- it 'persists' do
207
- test_mapper = configure_with_cycle
208
-
209
- tree = Tree.new
210
- long_branch = Branch.new(length: 100, tree: tree)
211
- tree.branches << long_branch
212
-
213
- test_mapper.persist(tree)
214
-
215
- expect(db_class_for(Tree, test_mapper).count).to eq 1
216
- end
217
-
218
- it 'hydrates' do
219
- test_mapper = configure_with_cycle
220
-
221
- tree_db = db_class_for(Tree, test_mapper).create!
222
- db_class_for(Branch, test_mapper).create!(length: 50, tree_id: tree_db.id)
223
-
224
- tree = test_mapper.load_one(tree_db)
225
-
226
- expect(tree).to be tree.branches.first.tree
227
- end
228
- end
229
-
230
- describe 'recursive associations' do
231
- it 'persists' do
232
- test_mapper = configure_recursive
233
-
234
- tree = Tree.new
235
- long_branch = Branch.new(length: 100, tree: tree)
236
- tree.branches << long_branch
237
- short_branch = Branch.new(length: 50, tree: tree)
238
- long_branch.branches << short_branch
239
-
240
- test_mapper.persist(tree)
241
-
242
- expect(db_class_for(Branch, test_mapper).count).to eq 2
243
- end
244
-
245
- it 'hydrates' do
246
- test_mapper = configure_recursive
247
-
248
- tree_db = db_class_for(Tree, test_mapper).create!
249
- long_branch = db_class_for(Branch, test_mapper).create!(length: 100, tree_id: tree_db.id)
250
- db_class_for(Branch, test_mapper).create!(length: 50, branch_id: long_branch.id)
251
-
252
- tree = test_mapper.load_one(tree_db)
253
-
254
- expect(tree.branches.first.branches.first.length).to eq 50
255
- end
256
- end
257
-
258
- describe 'belongs_to associations' do
259
- it 'saves attributes' do
260
- test_mapper = configure
261
- trunk = Trunk.new(length: 12)
262
- tree = Tree.new(trunk: trunk)
263
-
264
- test_mapper.persist(tree)
265
-
266
- trunk_db = db_class_for(Trunk, test_mapper).first
267
- expect(trunk_db.length).to eq 12
268
- end
269
-
270
- it 'saves foreign keys' do
271
- test_mapper = configure
272
- trunk = Trunk.new
273
- tree = Tree.new(trunk: trunk)
274
-
275
- test_mapper.persist(tree)
276
-
277
- tree_db = db_class_for(Tree, test_mapper).first
278
- expect(tree_db.trunk_id).to eq trunk.id
279
- end
280
-
281
- it 'updating does not create additional rows' do
282
- test_mapper = configure
283
- trunk = Trunk.new
284
- tree = Tree.new(trunk: trunk)
285
-
286
- test_mapper.persist(tree)
287
-
288
- trunk.length = 21
289
-
290
- expect{ test_mapper.persist(tree) }.to_not change{ db_class_for(Trunk, test_mapper).count }
291
- end
292
-
293
- it 'only saves entities that are owned' do
294
- test_mapper = configure_unowned
295
-
296
- trunk = Trunk.new
297
- tree = Tree.new(trunk: trunk)
298
-
299
- test_mapper.persist(tree)
300
-
301
- expect(db_class_for(Trunk, test_mapper).count).to eq 0
302
- end
303
-
304
- it 'hydrates' do
305
- test_mapper = configure
306
- trunk_db = db_class_for(Trunk, test_mapper).create!(length: 21)
307
- tree_db = db_class_for(Tree, test_mapper).create!(trunk_id: trunk_db.id)
308
-
309
- new_tree = test_mapper.load_one(tree_db)
310
- expect(new_tree.trunk.length).to eq 21
311
- end
312
- end
313
-
314
- describe 'has_many associations' do
315
- it 'saves' do
316
- test_mapper = configure
317
- tree = Tree.new
318
- tree.branches << Branch.new(length: 100)
319
- tree.branches << Branch.new(length: 3)
320
-
321
- test_mapper.persist(tree)
322
-
323
- branches = db_class_for(Branch, test_mapper).all
324
- expect(branches.size).to eq 2
325
- expect(branches.first.length).to eq 100
326
- expect(branches.second.length).to eq 3
327
- end
328
-
329
- it 'saves foreign keys' do
330
- test_mapper = configure
331
- tree = Tree.new
332
- tree.branches << Branch.new(length: 100)
333
-
334
- test_mapper.persist(tree)
335
-
336
- branches = db_class_for(Branch, test_mapper).all
337
- expect(branches.first.tree_id).to eq tree.id
338
- end
339
-
340
- it 'updates' do
341
- test_mapper = configure
342
- tree = Tree.new
343
- long_branch = Branch.new(length: 100)
344
- tree.branches << long_branch
345
-
346
- test_mapper.persist(tree)
347
-
348
- long_branch.length = 120
349
-
350
- test_mapper.persist(tree)
351
-
352
- branches = db_class_for(Branch, test_mapper).all
353
- expect(branches.first.length).to eq 120
354
- end
355
-
356
- it 'only saves entities that are owned' do
357
- test_mapper = configure_unowned
358
-
359
- tree = Tree.new
360
- long_branch = Branch.new(length: 100)
361
- tree.branches << long_branch
362
-
363
- test_mapper.persist(tree)
364
-
365
- expect(db_class_for(Branch, test_mapper).count).to eq 0
366
- end
367
-
368
- it 'hydrates' do
369
- test_mapper = configure
370
-
371
- tree_db = db_class_for(Tree, test_mapper).create!
372
- db_class_for(Branch, test_mapper).create!(length: 50, tree_id: tree_db.id)
373
-
374
- tree = test_mapper.load_one(tree_db)
375
-
376
- expect(tree.branches.first.length).to eq 50
377
- end
378
- end
379
-
380
- describe 'has_one associations' do
381
- it 'saves' do
382
- test_mapper = configure_has_one
383
- tree = Tree.new(name: 'big tree')
384
- trunk = Trunk.new(tree: tree)
385
-
386
- test_mapper.persist(trunk)
387
-
388
- expect(db_class_for(Tree, test_mapper).first.name).to eq 'big tree'
389
- end
390
-
391
- it 'saves foreign keys' do
392
- test_mapper = configure_has_one
393
- tree = Tree.new(name: 'big tree')
394
- trunk = Trunk.new(tree: tree)
395
-
396
- test_mapper.persist(trunk)
397
-
398
- expect(db_class_for(Tree, test_mapper).first.trunk_id).to eq trunk.id
399
- end
400
-
401
- it 'only saves entities that are owned' do
402
- test_mapper = configure_unowned_has_one
403
- tree = Tree.new
404
- trunk = Trunk.new(tree: tree)
405
-
406
- test_mapper.persist(trunk)
407
-
408
- expect(db_class_for(Tree, test_mapper).count).to eq 0
409
- end
410
-
411
- it 'hydrates' do
412
- test_mapper = configure_has_one
413
-
414
- trunk_db = db_class_for(Trunk, test_mapper).create!
415
- db_class_for(Tree, test_mapper).create!(name: 'big tree', trunk_id: trunk_db.id)
416
-
417
- trunk = test_mapper.load_one(trunk_db)
418
-
419
- expect(trunk.tree.name).to eq 'big tree'
420
- end
421
- end
422
-
423
- describe 'polymorphic associations' do
424
- it 'saves with has_manys' do
425
- test_mapper = configure_polymorphic_has_many
426
- trunk = Trunk.new
427
- branch = Branch.new
428
- tree = Tree.new(trunk: trunk, branches: [branch])
429
-
430
- trunk_bug = Bug.new
431
- trunk.bugs << trunk_bug
432
- branch_bug = Bug.new
433
- branch.bugs << branch_bug
434
-
435
- test_mapper.persist(tree)
436
-
437
- expect(db_class_for(Bug, test_mapper).find(trunk_bug.id).lives_on_type).to eq Trunk.name
438
- expect(db_class_for(Bug, test_mapper).find(branch_bug.id).lives_on_type).to eq Branch.name
439
- end
440
-
441
- it 'restores with has_manys' do
442
- test_mapper = configure_polymorphic_has_many
443
-
444
- trunk_db = db_class_for(Trunk, test_mapper).create!
445
- tree_db = db_class_for(Tree, test_mapper).create!(trunk_id: trunk_db.id)
446
- db_class_for(Bug, test_mapper).create!(name: 'trunk bug', lives_on_id: trunk_db.id, lives_on_type: Trunk.name)
447
- db_class_for(Bug, test_mapper).create!(name: 'not a trunk bug!', lives_on_id: trunk_db.id, lives_on_type: 'some other table')
448
-
449
- tree = test_mapper.load_one(tree_db)
450
-
451
- expect(tree.trunk.bugs.map(&:name)).to eq ['trunk bug']
452
- end
453
-
454
- it 'saves with belongs_tos' do
455
- test_mapper = configure_polymorphic_belongs_to
456
-
457
- trunk_bug = Bug.new(lives_on: Trunk.new)
458
- branch_bug = Bug.new(lives_on: Branch.new)
459
-
460
- test_mapper.persist([trunk_bug, branch_bug])
461
-
462
- expect(db_class_for(Bug, test_mapper).find(trunk_bug.id).lives_on_type).to eq Trunk.name
463
- expect(db_class_for(Bug, test_mapper).find(branch_bug.id).lives_on_type).to eq Branch.name
464
- end
465
-
466
- it 'saves associations to unowned entities via belongs_to' do
467
- test_mapper = configure_unowned_polymorphic_belongs_to
468
- trunk = Trunk.new
469
-
470
- trunk_bug = Bug.new(lives_on: trunk)
471
-
472
- test_mapper.persist(trunk_bug)
473
-
474
- expect(db_class_for(Bug, test_mapper).find(trunk_bug.id).lives_on_type).to eq Trunk.name
475
- end
476
-
477
- it 'restores with belongs_tos' do
478
- test_mapper = configure_polymorphic_belongs_to
479
-
480
- # makes sure that we are using the fk_type to discriminate against
481
- # two entities with the same primary key value
482
- trunk_db = db_class_for(Trunk, test_mapper).new(length: 99)
483
- trunk_db.id = 99
484
- trunk_db.save!
485
- trunk_bug_db = db_class_for(Bug, test_mapper).create!(lives_on_id: trunk_db.id, lives_on_type: Trunk.name)
486
- branch_db = db_class_for(Branch, test_mapper).new(length: 5)
487
- branch_db.id = 99
488
- branch_db.save!
489
- branch_bug_db = db_class_for(Bug, test_mapper).create!(lives_on_id: branch_db.id, lives_on_type: Branch.name)
490
-
491
- trunk_bug, branch_bug = test_mapper.load_many([trunk_bug_db, branch_bug_db])
492
-
493
- expect(trunk_bug.lives_on.length).to eq 99
494
- expect(branch_bug.lives_on.length).to eq 5
495
- end
496
-
497
- it 'restores active record objects' do
498
- test_mapper = configure_ar_polymorphic_belongs_to
499
-
500
- swamp = Swamp.create!
501
- tree_db = db_class_for(Tree, test_mapper).create!(environment_id: swamp.id, environment_type: Swamp.name)
502
-
503
- tree = test_mapper.load_one(tree_db)
504
-
505
- expect(tree.environment).to eq swamp
506
- end
507
- end
508
-
509
- describe 'arel' do
510
- it 'loads many' do
511
- test_mapper = configure
512
-
513
- db_class_for(Tree, test_mapper).create!
514
- tree_db = db_class_for(Tree, test_mapper).create!
515
-
516
- trees = test_mapper.query.where(id: tree_db.id).load_many
517
-
518
- expect(trees.map(&:id)).to eq [tree_db.id]
519
- end
520
-
521
- it 'loads one' do
522
- test_mapper = configure
523
-
524
- db_class_for(Tree, test_mapper).create!
525
- tree_db = db_class_for(Tree, test_mapper).create!
526
-
527
- trees = test_mapper.query.where(id: tree_db.id).load_one
528
-
529
- expect(trees.id).to eq tree_db.id
530
- end
531
- end
532
-
533
- describe 'load_many' do
534
- it 'maps given db objects' do
535
- test_mapper = configure
536
-
537
- db_class_for(Tree, test_mapper).create!
538
- tree_db = db_class_for(Tree, test_mapper).create!
539
-
540
- trees = test_mapper.load_many([tree_db])
541
-
542
- expect(trees.map(&:id)).to eq [tree_db.id]
543
- end
544
-
545
- it 'only returns roots' do
546
- test_mapper = configure
547
-
548
- db_class_for(Tree, test_mapper).create!
549
- tree_db = db_class_for(Tree, test_mapper).create!
550
- db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
551
-
552
- trees = test_mapper.load_many([tree_db])
553
-
554
- expect(trees.map(&:id)).to eq [tree_db.id]
555
- end
556
- end
557
-
558
- describe 'destroy' do
559
- it 'removes the entity from the database' do
560
- test_mapper = configure
561
-
562
- tree_db = db_class_for(Tree, test_mapper).create!
563
-
564
- test_mapper.destroy([Tree.new(id: tree_db.id)])
565
-
566
- expect(db_class_for(Tree, test_mapper).count).to eq 0
567
- end
568
-
569
- it 'removes has many children from the database' do
570
- test_mapper = configure
571
-
572
- tree_db = db_class_for(Tree, test_mapper).create!
573
- db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
574
-
575
- test_mapper.destroy(Tree.new(id: tree_db.id))
576
-
577
- expect(db_class_for(Branch, test_mapper).count).to eq 0
578
- end
579
-
580
- it 'removes belongs to children from the database' do
581
- test_mapper = configure
582
-
583
- trunk_db = db_class_for(Trunk, test_mapper).create!
584
- tree_db = db_class_for(Tree, test_mapper).create!(trunk_id: trunk_db.id)
585
-
586
- test_mapper.destroy(Tree.new(id: tree_db.id))
587
-
588
- expect(db_class_for(Trunk, test_mapper).count).to eq 0
589
- end
590
-
591
- it 'removes AR children from the database' do
592
- test_mapper = configure
593
-
594
- tree_db = db_class_for(Tree, test_mapper).create!
595
- Fissure.create!(tree_id: tree_db.id)
596
-
597
- test_mapper.destroy(Tree.new(id: tree_db.id))
598
-
599
- expect(Fissure.count).to eq 0
600
- end
601
-
602
- it 'leaves unowned belongs to children in the database' do
603
- test_mapper = configure_unowned
604
-
605
- trunk_db = db_class_for(Trunk, test_mapper).create!
606
- tree_db = db_class_for(Tree, test_mapper).create!(trunk_id: trunk_db.id)
607
-
608
- test_mapper.destroy(Tree.new(id: tree_db.id))
609
-
610
- expect(db_class_for(Trunk, test_mapper).count).to eq 1
611
- end
612
-
613
- it 'leaves unowned has many children in the database' do
614
- test_mapper = configure_unowned
615
-
616
- tree_db = db_class_for(Tree, test_mapper).create!
617
- db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
618
-
619
- test_mapper.destroy(Tree.new(id: tree_db.id))
620
-
621
- expect(db_class_for(Branch, test_mapper).count).to eq 1
622
- end
623
- end
624
-
625
- describe 'destroy_by_id' do
626
- it 'removes the entity from the database' do
627
- test_mapper = configure
628
-
629
- tree_db = db_class_for(Tree, test_mapper).create!
630
-
631
- test_mapper.destroy_by_id([tree_db.id])
632
-
633
- expect(db_class_for(Tree, test_mapper).count).to eq 0
634
- end
635
- end
636
-
637
- describe 'non-existent values' do
638
- it 'load_many returns an empty array when given an empty array' do
639
- test_mapper = configure
640
-
641
- results = test_mapper.load_many([])
642
- expect(results).to eq []
643
- end
644
-
645
- it 'load_many throws an exception when given a nil db_object' do
646
- test_mapper = configure
647
-
648
- expect {
649
- test_mapper.load_many([nil])
650
- }.to raise_error(Vorpal::InvalidAggregateRoot, "Nil aggregate roots are not allowed.")
651
- end
652
-
653
- it 'load_one returns nil when given nil' do
654
- test_mapper = configure
655
-
656
- result = test_mapper.load_one(nil)
657
- expect(result).to eq nil
658
- end
659
-
660
- it 'persist ignores empty arrays' do
661
- test_mapper = configure
662
-
663
- results = test_mapper.persist([])
664
- expect(results).to eq []
665
- end
666
-
667
- it 'persist throws an exception when given a collection with a nil root' do
668
- test_mapper = configure
669
- expect {
670
- test_mapper.persist([nil])
671
- }.to raise_error(Vorpal::InvalidAggregateRoot, "Nil aggregate roots are not allowed.")
672
- end
673
-
674
- it 'persist throws an exception when given a nil root' do
675
- test_mapper = configure
676
- expect {
677
- test_mapper.persist(nil)
678
- }.to raise_error(Vorpal::InvalidAggregateRoot, "Nil aggregate roots are not allowed.")
679
- end
680
-
681
- it 'destroy ignores empty arrays' do
682
- test_mapper = configure
683
-
684
- results = test_mapper.destroy([])
685
- expect(results).to eq []
686
- end
687
-
688
- it 'destroy throws an exception when given a nil root' do
689
- test_mapper = configure
690
-
691
- expect {
692
- test_mapper.destroy(nil)
693
- }.to raise_error(Vorpal::InvalidAggregateRoot, "Nil aggregate roots are not allowed.")
694
- end
695
-
696
- it 'destroy throws an exception when given a collection with a nil root' do
697
- test_mapper = configure
698
-
699
- expect {
700
- test_mapper.destroy([nil])
701
- }.to raise_error(Vorpal::InvalidAggregateRoot, "Nil aggregate roots are not allowed.")
702
- end
703
-
704
- it 'destroy_by_id ignores empty arrays' do
705
- test_mapper = configure
706
-
707
- results = test_mapper.destroy_by_id([])
708
- expect(results).to eq []
709
- end
710
-
711
- it 'destroy_by_id ignores ids that do not exist' do
712
- test_mapper = configure
713
-
714
- test_mapper.destroy_by_id([99])
715
- end
716
-
717
- it 'destroy_by_id throws an exception when given a collection with a nil id' do
718
- test_mapper = configure
719
-
720
- expect {
721
- test_mapper.destroy_by_id([nil])
722
- }.to raise_error(Vorpal::InvalidPrimaryKeyValue, "Nil primary key values are not allowed.")
723
- end
724
-
725
- it 'destroy_by_id throws an exception when given a nil id' do
726
- test_mapper = configure
727
-
728
- expect {
729
- test_mapper.destroy_by_id(nil)
730
- }.to raise_error(Vorpal::InvalidPrimaryKeyValue, "Nil primary key values are not allowed.")
731
- end
732
- end
733
-
734
- private
735
-
736
- def db_class_for(clazz, mapper)
737
- mapper.engine.mapper_for(clazz).db_class
738
- end
739
-
740
- def configure_polymorphic_has_many
741
- engine = Vorpal.define do
742
- map Tree do
743
- attributes :name
744
- has_many :branches
745
- belongs_to :trunk
746
- end
747
-
748
- map Trunk do
749
- attributes :length
750
- has_many :bugs, fk: :lives_on_id, fk_type: :lives_on_type
751
- end
752
-
753
- map Branch do
754
- attributes :length
755
- has_many :bugs, fk: :lives_on_id, fk_type: :lives_on_type
756
- end
757
-
758
- map Bug do
759
- attributes :name
760
- end
761
- end
762
- engine.mapper_for(Tree)
763
- end
764
-
765
- def configure_polymorphic_belongs_to
766
- engine = Vorpal.define do
767
- map Bug do
768
- attributes :name
769
- belongs_to :lives_on, fk: :lives_on_id, fk_type: :lives_on_type, child_classes: [Trunk, Branch]
770
- end
771
-
772
- map Trunk do
773
- attributes :length
774
- end
775
-
776
- map Branch do
777
- attributes :length
778
- end
779
- end
780
- engine.mapper_for(Bug)
781
- end
782
-
783
- def configure_ar_polymorphic_belongs_to
784
- engine = Vorpal.define do
785
- map Tree do
786
- attributes :name
787
- belongs_to :environment, owned: false, fk: :environment_id, fk_type: :environment_type, child_class: Swamp
788
- end
789
-
790
- map Swamp, to: Swamp
791
- end
792
- engine.mapper_for(Tree)
793
- end
794
-
795
- def configure_unowned_polymorphic_belongs_to
796
- engine = Vorpal.define do
797
- map Bug do
798
- attributes :name
799
- belongs_to :lives_on, owned: false, fk: :lives_on_id, fk_type: :lives_on_type, child_classes: [Trunk, Branch]
800
- end
801
-
802
- map Trunk do
803
- attributes :length
804
- end
805
-
806
- map Branch do
807
- attributes :length
808
- end
809
- end
810
- engine.mapper_for(Bug)
811
- end
812
-
813
- def configure_unowned
814
- engine = Vorpal.define do
815
- map Tree do
816
- attributes :name
817
- has_many :branches, owned: false
818
- belongs_to :trunk, owned: false
819
- end
820
-
821
- map Trunk do
822
- attributes :length
823
- end
824
-
825
- map Branch do
826
- attributes :length
827
- end
828
- end
829
- engine.mapper_for(Tree)
830
- end
831
-
832
- def configure_recursive
833
- engine = Vorpal.define do
834
- map Branch do
835
- attributes :length
836
- has_many :branches
837
- end
838
-
839
- map Tree do
840
- attributes :name
841
- has_many :branches
842
- end
843
- end
844
- engine.mapper_for(Tree)
845
- end
846
-
847
- def configure_with_cycle
848
- engine = Vorpal.define do
849
- map Branch do
850
- attributes :length
851
- belongs_to :tree
852
- end
853
-
854
- map Tree do
855
- attributes :name
856
- has_many :branches
857
- end
858
- end
859
- engine.mapper_for(Tree)
860
- end
861
-
862
- def configure(options={})
863
- engine = Vorpal.define(options) do
864
- map Tree do
865
- attributes :name
866
- belongs_to :trunk
867
- has_many :fissures
868
- has_many :branches
869
- end
870
-
871
- map Trunk do
872
- attributes :length
873
- end
874
-
875
- map Branch do
876
- attributes :length
877
- end
878
-
879
- map Fissure, to: Fissure
880
- end
881
- engine.mapper_for(Tree)
882
- end
883
-
884
- def configure_has_one
885
- engine = Vorpal.define do
886
- map Trunk do
887
- attributes :length
888
- has_one :tree
889
- end
890
-
891
- map Tree do
892
- attributes :name
893
- end
894
- end
895
- engine.mapper_for(Trunk)
896
- end
897
-
898
- def configure_unowned_has_one
899
- engine = Vorpal.define do
900
- map Trunk do
901
- attributes :length
902
- has_one :tree, owned: false
903
- end
904
-
905
- map Tree do
906
- attributes :name
907
- end
908
- end
909
- engine.mapper_for(Trunk)
910
- end
911
- end