rasti-db 1.5.0 → 2.0.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.
@@ -33,25 +33,39 @@ module Rasti
33
33
  self.class == OneToOne
34
34
  end
35
35
 
36
- def join_relation_name(prefix)
37
- with_prefix prefix, name
36
+ def from_one?
37
+ one_to_one? || one_to_many?
38
38
  end
39
39
 
40
- private
40
+ def from_many?
41
+ many_to_one? || many_to_many?
42
+ end
41
43
 
42
- attr_reader :options
44
+ def to_one?
45
+ one_to_one? || many_to_one?
46
+ end
43
47
 
44
- def qualified_source_collection_name(schema=nil)
45
- schema.nil? ? Sequel[source_collection_class.collection_name] : Sequel[schema][source_collection_class.collection_name]
48
+ def to_many?
49
+ one_to_many? || many_to_many?
46
50
  end
47
51
 
48
- def qualified_target_collection_name(schema=nil)
49
- schema.nil? ? Sequel[target_collection_class.collection_name] : Sequel[schema][target_collection_class.collection_name]
52
+ def join_relation_name(prefix)
53
+ with_prefix prefix, name
50
54
  end
51
55
 
56
+ private
57
+
58
+ attr_reader :options
59
+
52
60
  def with_prefix(prefix, name)
53
61
  [prefix, name].compact.join('__').to_sym
54
62
  end
63
+
64
+ def validate_join!
65
+ if source_collection_class.data_source_name != target_collection_class.data_source_name
66
+ raise "Invalid join of multiple data sources: #{source_collection_class.data_source_name}.#{source_collection_class.collection_name} > #{target_collection_class.data_source_name}.#{target_collection_class.collection_name}"
67
+ end
68
+ end
55
69
 
56
70
  end
57
71
  end
@@ -3,9 +3,8 @@ module Rasti
3
3
  module Relations
4
4
  class Graph
5
5
 
6
- def initialize(db, schema, collection_class, relations=[], selected_attributes={}, excluded_attributes={})
7
- @db = db
8
- @schema = schema
6
+ def initialize(environment, collection_class, relations=[], selected_attributes={}, excluded_attributes={})
7
+ @environment = environment
9
8
  @collection_class = collection_class
10
9
  @graph = build_graph relations,
11
10
  Hash::Indifferent.new(selected_attributes),
@@ -13,8 +12,7 @@ module Rasti
13
12
  end
14
13
 
15
14
  def merge(relations:[], selected_attributes:{}, excluded_attributes:{})
16
- Graph.new db,
17
- schema,
15
+ Graph.new environment,
18
16
  collection_class,
19
17
  (flat_relations | relations),
20
18
  flat_selected_attributes.merge(selected_attributes),
@@ -38,9 +36,8 @@ module Rasti
38
36
  return if rows.empty?
39
37
 
40
38
  graph.roots.each do |node|
41
- relation_of(node).fetch_graph rows,
42
- db,
43
- schema,
39
+ relation_of(node).fetch_graph environment,
40
+ rows,
44
41
  node[:selected_attributes],
45
42
  node[:excluded_attributes] ,
46
43
  subgraph_of(node)
@@ -48,18 +45,16 @@ module Rasti
48
45
  end
49
46
 
50
47
  def add_joins(dataset, prefix=nil)
51
- graph.roots.each do |node|
48
+ graph.roots.inject(dataset) do |ds, node|
52
49
  relation = relation_of node
53
- dataset = relation.add_join dataset, schema, prefix
54
- dataset = subgraph_of(node).add_joins dataset, relation.join_relation_name(prefix)
50
+ dataset_with_relation = relation.add_join environment, ds, prefix
51
+ subgraph_of(node).add_joins dataset_with_relation, relation.join_relation_name(prefix)
55
52
  end
56
-
57
- dataset
58
53
  end
59
54
 
60
55
  private
61
56
 
62
- attr_reader :db, :schema, :collection_class, :graph
57
+ attr_reader :environment, :collection_class, :graph
63
58
 
64
59
  def relation_of(node)
65
60
  collection_class.relations.fetch(node[:name])
@@ -93,8 +88,7 @@ module Rasti
93
88
  excluded[id] = descendant[:excluded_attributes]
94
89
  end
95
90
 
96
- Graph.new db,
97
- schema,
91
+ Graph.new environment,
98
92
  relation_of(node).target_collection_class,
99
93
  relations,
100
94
  selected,
@@ -15,23 +15,42 @@ module Rasti
15
15
  @relation_collection_name ||= options[:relation_collection_name] || [source_collection_class.collection_name, target_collection_class.collection_name].sort.join('_').to_sym
16
16
  end
17
17
 
18
- def qualified_relation_collection_name(schema=nil)
19
- schema.nil? ? Sequel[relation_collection_name] : Sequel[schema][relation_collection_name]
18
+ def relation_data_source_name
19
+ @relation_data_source_name ||= options[:relation_data_source_name] || source_collection_class.data_source_name
20
20
  end
21
21
 
22
- def fetch_graph(rows, db, schema=nil, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
22
+ def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
23
23
  pks = rows.map { |row| row[source_collection_class.primary_key] }
24
24
 
25
- target_collection = target_collection_class.new db, schema
25
+ if target_collection_class.data_source_name == relation_data_source_name
26
+ target_data_source = environment.data_source_of target_collection_class
26
27
 
27
- relation_name = qualified_relation_collection_name schema
28
+ dataset = target_data_source.db.from(environment.qualify_collection(target_collection_class))
29
+ .join(qualified_relation_collection_name(environment), target_foreign_key => target_collection_class.primary_key)
30
+ .where(Sequel[relation_collection_name][source_foreign_key] => pks)
31
+ .select_all(target_collection_class.collection_name)
28
32
 
29
- join_rows = target_collection.dataset
30
- .join(relation_name, target_foreign_key => target_collection_class.primary_key)
31
- .where(Sequel[relation_name][source_foreign_key] => pks)
32
- .select_all(target_collection_class.collection_name)
33
- .select_append(Sequel[relation_name][source_foreign_key].as(:source_foreign_key))
34
- .all
33
+ selected_attributes ||= target_collection_class.collection_attributes - excluded_attributes if excluded_attributes
34
+ dataset = dataset.select(*selected_attributes.map { |a| Sequel[target_collection_class.collection_name][a] }) if selected_attributes
35
+
36
+ join_rows = dataset.select_append(Sequel[relation_collection_name][source_foreign_key].as(:source_foreign_key)).all
37
+ else
38
+ relation_data_source = environment.data_source relation_data_source_name
39
+
40
+ relation_index = relation_data_source.db.from(relation_data_source.qualify(relation_collection_name))
41
+ .where(source_foreign_key => pks)
42
+ .select_hash_groups(target_foreign_key, source_foreign_key)
43
+
44
+ query = target_collection_class.new environment
45
+ query = query.exclude_attributes(*excluded_attributes) if excluded_attributes
46
+ query = query.select_attributes(*selected_attributes) if selected_attributes
47
+
48
+ join_rows = query.where(target_collection_class.primary_key => relation_index.keys).raw.flat_map do |row|
49
+ relation_index[row[target_collection_class.primary_key]].map do |source_primary_key|
50
+ row.merge(source_foreign_key: source_primary_key)
51
+ end
52
+ end
53
+ end
35
54
 
36
55
  relations_graph.fetch_graph join_rows if relations_graph
37
56
 
@@ -41,17 +60,19 @@ module Rasti
41
60
  end
42
61
 
43
62
  rows.each do |row|
44
- row[name] = relation_rows.fetch row[target_collection_class.primary_key], []
63
+ row[name] = relation_rows.fetch row[source_collection_class.primary_key], []
45
64
  end
46
65
  end
47
66
 
48
- def add_join(dataset, schema=nil, prefix=nil)
67
+ def add_join(environment, dataset, prefix=nil)
68
+ validate_join!
69
+
49
70
  many_to_many_relation_alias = with_prefix prefix, "#{relation_collection_name}_#{SecureRandom.base64}"
50
71
 
51
- qualified_relation_source = prefix ? Sequel[prefix] : qualified_source_collection_name(schema)
72
+ relation_name = prefix ? Sequel[prefix] : Sequel[source_collection_class.collection_name]
52
73
 
53
74
  many_to_many_condition = {
54
- Sequel[many_to_many_relation_alias][source_foreign_key] => qualified_relation_source[source_collection_class.primary_key]
75
+ Sequel[many_to_many_relation_alias][source_foreign_key] => relation_name[source_collection_class.primary_key]
55
76
  }
56
77
 
57
78
  relation_alias = join_relation_name prefix
@@ -60,17 +81,30 @@ module Rasti
60
81
  Sequel[relation_alias][target_collection_class.primary_key] => Sequel[many_to_many_relation_alias][target_foreign_key]
61
82
  }
62
83
 
63
- dataset.join(qualified_relation_collection_name(schema).as(many_to_many_relation_alias), many_to_many_condition)
64
- .join(qualified_target_collection_name(schema).as(relation_alias), relation_condition)
84
+ dataset.join(qualified_relation_collection_name(environment).as(many_to_many_relation_alias), many_to_many_condition)
85
+ .join(environment.qualify_collection(target_collection_class).as(relation_alias), relation_condition)
86
+ end
87
+
88
+ def apply_filter(environment, dataset, primary_keys)
89
+ if source_collection_class.data_source_name == relation_data_source_name
90
+ dataset.join(qualified_relation_collection_name(environment), source_foreign_key => source_collection_class.primary_key)
91
+ .where(Sequel[relation_collection_name][target_foreign_key] => primary_keys)
92
+ .select_all(source_collection_class.collection_name)
93
+ .distinct
94
+ else
95
+ data_source = environment.data_source relation_data_source_name
96
+ fks = data_source.db.from(data_source.qualify(relation_collection_name))
97
+ .where(target_collection_class.foreign_key => primary_keys)
98
+ .select_map(source_collection_class.foreign_key)
99
+ .uniq
100
+ dataset.where(source_collection_class.primary_key => fks)
101
+ end
65
102
  end
66
103
 
67
- def apply_filter(dataset, schema=nil, primary_keys=[])
68
- relation_name = qualified_relation_collection_name schema
104
+ private
69
105
 
70
- dataset.join(relation_name, source_foreign_key => target_collection_class.primary_key)
71
- .where(Sequel[relation_name][target_foreign_key] => primary_keys)
72
- .select_all(source_collection_class.collection_name)
73
- .distinct
106
+ def qualified_relation_collection_name(environment)
107
+ environment.qualify relation_data_source_name, relation_collection_name
74
108
  end
75
109
 
76
110
  end
@@ -7,10 +7,10 @@ module Rasti
7
7
  @foreign_key ||= options[:foreign_key] || target_collection_class.foreign_key
8
8
  end
9
9
 
10
- def fetch_graph(rows, db, schema=nil, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
10
+ def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
11
11
  fks = rows.map { |row| row[foreign_key] }.uniq
12
12
 
13
- target_collection = target_collection_class.new db, schema
13
+ target_collection = target_collection_class.new environment
14
14
 
15
15
  query = target_collection.where(source_collection_class.primary_key => fks)
16
16
  query = query.exclude_attributes(*excluded_attributes) if excluded_attributes
@@ -26,19 +26,21 @@ module Rasti
26
26
  end
27
27
  end
28
28
 
29
- def add_join(dataset, schema=nil, prefix=nil)
29
+ def add_join(environment, dataset, prefix=nil)
30
+ validate_join!
31
+
30
32
  relation_alias = join_relation_name prefix
31
33
 
32
- qualified_relation_source = prefix ? Sequel[prefix] : qualified_source_collection_name(schema)
34
+ relation_name = prefix ? Sequel[prefix] : Sequel[source_collection_class.collection_name]
33
35
 
34
36
  relation_condition = {
35
- Sequel[relation_alias][target_collection_class.primary_key] => qualified_relation_source[foreign_key]
37
+ Sequel[relation_alias][target_collection_class.primary_key] => relation_name[foreign_key]
36
38
  }
37
39
 
38
- dataset.join(qualified_target_collection_name(schema).as(relation_alias), relation_condition)
40
+ dataset.join(environment.qualify_collection(target_collection_class).as(relation_alias), relation_condition)
39
41
  end
40
42
 
41
- def apply_filter(dataset, schema=nil, primary_keys=[])
43
+ def apply_filter(environment, dataset, primary_keys)
42
44
  dataset.where(foreign_key => primary_keys)
43
45
  end
44
46
 
@@ -7,10 +7,10 @@ module Rasti
7
7
  @foreign_key ||= options[:foreign_key] || source_collection_class.foreign_key
8
8
  end
9
9
 
10
- def fetch_graph(rows, db, schema=nil, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
10
+ def fetch_graph(environment, rows, selected_attributes=nil, excluded_attributes=nil, relations_graph=nil)
11
11
  pks = rows.map { |row| row[source_collection_class.primary_key] }.uniq
12
12
 
13
- target_collection = target_collection_class.new db, schema
13
+ target_collection = target_collection_class.new environment
14
14
 
15
15
  query = target_collection.where(foreign_key => pks)
16
16
  query = query.exclude_attributes(*excluded_attributes) if excluded_attributes
@@ -24,25 +24,33 @@ module Rasti
24
24
  end
25
25
  end
26
26
 
27
- def add_join(dataset, schema=nil, prefix=nil)
27
+ def add_join(environment, dataset, prefix=nil)
28
+ validate_join!
29
+
28
30
  relation_alias = join_relation_name prefix
29
31
 
30
- qualified_relation_source = prefix ? Sequel[prefix] : qualified_source_collection_name(schema)
32
+ relation_name = prefix ? Sequel[prefix] : Sequel[source_collection_class.collection_name]
31
33
 
32
34
  relation_condition = {
33
- Sequel[relation_alias][foreign_key] => qualified_relation_source[source_collection_class.primary_key]
35
+ Sequel[relation_alias][foreign_key] => relation_name[source_collection_class.primary_key]
34
36
  }
35
37
 
36
- dataset.join(qualified_target_collection_name(schema).as(relation_alias), relation_condition)
38
+ dataset.join(environment.qualify_collection(target_collection_class).as(relation_alias), relation_condition)
37
39
  end
38
40
 
39
- def apply_filter(dataset, schema=nil, primary_keys=[])
40
- target_name = qualified_target_collection_name schema
41
-
42
- dataset.join(target_name, foreign_key => source_collection_class.primary_key)
43
- .where(Sequel[target_name][target_collection_class.primary_key] => primary_keys)
44
- .select_all(qualified_source_collection_name(schema))
45
- .distinct
41
+ def apply_filter(environment, dataset, primary_keys)
42
+ if source_collection_class.data_source_name == target_collection_class.data_source_name
43
+ dataset.join(environment.qualify_collection(target_collection_class), foreign_key => source_collection_class.primary_key)
44
+ .where(Sequel[target_collection_class.collection_name][target_collection_class.primary_key] => primary_keys)
45
+ .select_all(target_collection_class.collection_name)
46
+ .distinct
47
+ else
48
+ target_collection = target_collection_class.new environment
49
+ fks = target_collection.where(target_collection_class.primary_key => primary_keys)
50
+ .pluck(foreign_key)
51
+ .uniq
52
+ dataset.where(source_collection_class.primary_key => fks)
53
+ end
46
54
  end
47
55
 
48
56
  private
@@ -1,5 +1,5 @@
1
1
  module Rasti
2
2
  module DB
3
- VERSION = '1.5.0'
3
+ VERSION = '2.0.0'
4
4
  end
5
5
  end
@@ -10,6 +10,7 @@ describe 'Collection' do
10
10
  Users.model.must_equal User
11
11
  Users.primary_key.must_equal :id
12
12
  Users.foreign_key.must_equal :user_id
13
+ Users.data_source_name.must_equal :default
13
14
  end
14
15
 
15
16
  it 'Explicit' do
@@ -18,6 +19,8 @@ describe 'Collection' do
18
19
  People.model.must_equal Person
19
20
  People.primary_key.must_equal :document_number
20
21
  People.foreign_key.must_equal :document_number
22
+
23
+ Languages.data_source_name.must_equal :custom
21
24
  end
22
25
 
23
26
  it 'Lazy model name' do
@@ -42,11 +45,11 @@ describe 'Collection' do
42
45
  user_id = db[:users].insert name: 'User 1'
43
46
 
44
47
  1.upto(2) do |i|
45
- db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...'
48
+ db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...', language_id: 1
46
49
  db[:categories].insert name: "Category #{i}"
47
50
  end
48
51
 
49
- post_id = posts.insert user_id: user_id, title: 'Post title', body: '...', categories: [1,2]
52
+ post_id = posts.insert user_id: user_id, title: 'Post title', body: '...', categories: [1,2], language_id: 1
50
53
  category_id = categories.insert name: 'Category', posts: [1,2]
51
54
 
52
55
  db[:categories_posts].where(post_id: post_id).map(:category_id).must_equal [1,2]
@@ -59,7 +62,7 @@ describe 'Collection' do
59
62
  end
60
63
 
61
64
  user_id = db[:users].insert name: 'User 1'
62
- post_id = db[:posts].insert user_id: user_id, title: 'Post title', body: '...'
65
+ post_id = db[:posts].insert user_id: user_id, title: 'Post title', body: '...', language_id: 1
63
66
  1.upto(2) { |category_id| db[:categories_posts].insert post_id: post_id, category_id: category_id }
64
67
 
65
68
  posts.insert_relations post_id, categories: [3]
@@ -91,7 +94,7 @@ describe 'Collection' do
91
94
  user_id = db[:users].insert name: 'User 1'
92
95
 
93
96
  1.upto(3) do |i|
94
- db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...'
97
+ db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...', language_id: 1
95
98
  db[:categories].insert name: "Category #{i}"
96
99
  end
97
100
 
@@ -114,7 +117,7 @@ describe 'Collection' do
114
117
 
115
118
  it 'Bulk update' do
116
119
  user_id = db[:users].insert name: 'User 1'
117
- 1.upto(3) { |i| db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...' }
120
+ 1.upto(3) { |i| db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...', language_id: 1 }
118
121
 
119
122
  posts.bulk_update(body: 'Updated ...') { where id: [1,2] }
120
123
 
@@ -139,7 +142,7 @@ describe 'Collection' do
139
142
  end
140
143
 
141
144
  user_id = db[:users].insert name: 'User 1'
142
- post_id = db[:posts].insert user_id: user_id, title: 'Post title', body: '...'
145
+ post_id = db[:posts].insert user_id: user_id, title: 'Post title', body: '...', language_id: 1
143
146
  1.upto(3) { |category_id| db[:categories_posts].insert post_id: post_id, category_id: category_id }
144
147
 
145
148
  posts.delete_relations post_id, categories: [3]
@@ -170,7 +173,7 @@ describe 'Collection' do
170
173
  category_id = db[:categories].insert name: "Category #{i}"
171
174
 
172
175
  1.upto(3) do |n|
173
- post_id = db[:posts].insert user_id: user_id, title: "Post #{i}.#{n}", body: '...'
176
+ post_id = db[:posts].insert user_id: user_id, title: "Post #{i}.#{n}", body: '...', language_id: 1
174
177
  db[:categories_posts].insert post_id: post_id, category_id: category_id
175
178
  end
176
179
  end
@@ -227,6 +230,65 @@ describe 'Collection' do
227
230
 
228
231
  end
229
232
 
233
+ describe 'Multiple data sources' do
234
+
235
+ before do
236
+ 1.upto(3) do |i|
237
+ db[:users].insert name: "User #{i}"
238
+ db[:people].insert document_number: "document_#{i}",
239
+ first_name: "John #{i}",
240
+ last_name: "Doe #{i}",
241
+ birth_date: Time.now - i,
242
+ user_id: i
243
+ end
244
+ end
245
+
246
+ it 'Insert' do
247
+ id = languages.insert name: 'Spanish', people: ['document_1', 'document_2']
248
+
249
+ custom_db[:languages][id: id][:name].must_equal 'Spanish'
250
+ db[:languages_people].where(language_id: id).select_map(:document_number).must_equal ['document_1', 'document_2']
251
+ end
252
+
253
+ it 'Update' do
254
+ id = custom_db[:languages].insert name: 'Spanish'
255
+ db[:languages_people].insert language_id: id, document_number: 'document_1'
256
+
257
+ custom_db[:languages][id: id][:name].must_equal 'Spanish'
258
+ db[:languages_people].where(language_id: id).select_map(:document_number).must_equal ['document_1']
259
+
260
+ languages.update id, name: 'English', people: ['document_2', 'document_3']
261
+
262
+ custom_db[:languages][id: id][:name].must_equal 'English'
263
+ db[:languages_people].where(language_id: id).select_map(:document_number).must_equal ['document_2', 'document_3']
264
+ end
265
+
266
+ it 'Delete' do
267
+ id = custom_db[:languages].insert name: 'Spanish'
268
+ db[:languages_people].insert language_id: id, document_number: 'document_1'
269
+ db[:posts].insert user_id: 1, title: 'Post 1', body: '...', language_id: id
270
+
271
+ languages.delete id
272
+
273
+ custom_db[:languages].count.must_equal 0
274
+ db[:languages_people].where(language_id: id).count.must_equal 1
275
+ db[:posts].where(language_id: id).count.must_equal 1
276
+ end
277
+
278
+ it 'Delete cascade' do
279
+ id = custom_db[:languages].insert name: 'Spanish'
280
+ db[:languages_people].insert language_id: id, document_number: 'document_1'
281
+ db[:posts].insert user_id: 1, title: 'Post 1', body: '...', language_id: id
282
+
283
+ languages.delete_cascade id
284
+
285
+ custom_db[:languages].count.must_equal 0
286
+ db[:languages_people].where(language_id: id).count.must_equal 0
287
+ db[:posts].where(language_id: id).count.must_equal 0
288
+ end
289
+
290
+ end
291
+
230
292
  end
231
293
 
232
294
  describe 'Queries' do
@@ -239,7 +301,7 @@ describe 'Collection' do
239
301
 
240
302
  it 'Find graph' do
241
303
  user_id = db[:users].insert name: 'User 1'
242
- db[:posts].insert user_id: user_id, title: 'Post 1', body: '...'
304
+ db[:posts].insert user_id: user_id, title: 'Post 1', body: '...', language_id: 1
243
305
 
244
306
  users.find_graph(user_id, :posts).must_equal User.new id: user_id, name: 'User 1', posts: posts.all
245
307
  end
@@ -316,42 +378,51 @@ describe 'Collection' do
316
378
 
317
379
  it 'Chain dataset as query' do
318
380
  1.upto(2) { |i| db[:users].insert name: "User #{i}" }
319
- 1.upto(3) { |i| db[:posts].insert user_id: 1, title: "Post #{i}", body: '...' }
381
+ 1.upto(3) { |i| db[:posts].insert user_id: 1, title: "Post #{i}", body: '...', language_id: 1 }
320
382
  1.upto(2) { |i| db[:comments].insert post_id: i, user_id: 2, text: 'Comment' }
321
383
 
322
384
  models = posts.commented_by(2).all
323
- models.must_equal [1,2].map { |i| Post.new(id: i, user_id: 1, title: "Post #{i}", body: '...') }
385
+ models.must_equal [1,2].map { |i| Post.new(id: i, user_id: 1, title: "Post #{i}", body: '...', language_id: 1) }
324
386
  end
325
387
 
326
388
  it 'Custom query' do
327
389
  1.upto(2) { |i| db[:users].insert name: "User #{i}" }
328
- 1.upto(3) { |i| db[:posts].insert user_id: 1, title: "Post #{i}", body: '...' }
390
+ 1.upto(3) { |i| db[:posts].insert user_id: 1, title: "Post #{i}", body: '...', language_id: 1 }
329
391
  1.upto(2) { |i| db[:comments].insert post_id: i, user_id: 2, text: 'Comment' }
330
392
 
331
393
  models = comments.posts_commented_by(2)
332
- models.must_equal [1,2].map { |i| Post.new(id: i, user_id: 1, title: "Post #{i}", body: '...') }
394
+ models.must_equal [1,2].map { |i| Post.new(id: i, user_id: 1, title: "Post #{i}", body: '...', language_id: 1) }
333
395
  end
334
396
 
335
397
  describe 'Named queries' do
336
398
 
337
399
  before do
400
+ custom_db[:languages].insert name: 'Spanish'
401
+ custom_db[:languages].insert name: 'English'
402
+
338
403
  1.upto(2) do |i|
339
404
  db[:categories].insert name: "Category #{i}"
405
+
340
406
  db[:users].insert name: "User #{i}"
407
+
341
408
  db[:people].insert document_number: "document_#{i}",
342
409
  first_name: "John #{i}",
343
410
  last_name: "Doe #{i}",
344
411
  birth_date: Time.now - i,
345
412
  user_id: i
413
+
346
414
  end
347
415
 
416
+ db[:languages_people].insert language_id: 1, document_number: 'document_1'
417
+ db[:languages_people].insert language_id: 2, document_number: 'document_2'
418
+
348
419
  1.upto(3) do |i|
349
- db[:posts].insert user_id: 1, title: "Post #{i}", body: '...'
420
+ db[:posts].insert user_id: 1, title: "Post #{i}", body: '...', language_id: 1
350
421
  db[:categories_posts].insert category_id: 1, post_id: i
351
422
  end
352
423
 
353
424
  4.upto(5) do |i|
354
- db[:posts].insert user_id: 2, title: "Post #{i}", body: '...'
425
+ db[:posts].insert user_id: 2, title: "Post #{i}", body: '...', language_id: 2
355
426
  db[:categories_posts].insert category_id: 2, post_id: i
356
427
  end
357
428
  end
@@ -374,6 +445,23 @@ describe 'Collection' do
374
445
  users.with_people('document_1').primary_keys.must_equal [1]
375
446
  end
376
447
 
448
+ describe 'Multiple data sources' do
449
+
450
+ it 'One to many' do
451
+ languages.with_posts([1,2]).primary_keys.must_equal [1]
452
+ end
453
+
454
+ it 'Many to one' do
455
+ posts.with_languages([2]).primary_keys.must_equal [4,5]
456
+ end
457
+
458
+ it 'Many to Many' do
459
+ languages.with_people(['document_1']).primary_keys.must_equal [1]
460
+ people.with_languages([2]).primary_keys.must_equal ['document_2']
461
+ end
462
+
463
+ end
464
+
377
465
  end
378
466
 
379
467
  it 'Global' do
@@ -400,7 +488,7 @@ describe 'Collection' do
400
488
  birth_date: Time.now - i,
401
489
  user_id: i
402
490
  db[:categories].insert name: "Category #{i}"
403
- db[:posts].insert user_id: i, title: "Post #{i}", body: '...'
491
+ db[:posts].insert user_id: i, title: "Post #{i}", body: '...', language_id: 1
404
492
  db[:categories_posts].insert post_id: i, category_id: i
405
493
  end
406
494
 
@@ -448,21 +536,21 @@ describe 'Collection' do
448
536
  stubs = Proc.new do |sql|
449
537
  case sql
450
538
 
451
- when 'SELECT users.* FROM custom_schema.users',
452
- 'SELECT users.* FROM custom_schema.users WHERE (id IN (2, 1))'
539
+ when 'SELECT users.* FROM schema_1.users',
540
+ 'SELECT users.* FROM schema_1.users WHERE (id IN (2, 1))'
453
541
  [
454
542
  {id: 1},
455
543
  {id: 2}
456
544
  ]
457
545
 
458
- when 'SELECT posts.* FROM custom_schema.posts',
459
- 'SELECT posts.* FROM custom_schema.posts WHERE (user_id IN (1, 2))'
546
+ when 'SELECT posts.* FROM schema_1.posts',
547
+ 'SELECT posts.* FROM schema_1.posts WHERE (user_id IN (1, 2))'
460
548
  [
461
- {id: 3, user_id: 1},
462
- {id: 4, user_id: 2}
549
+ {id: 3, user_id: 1, language_id: 1},
550
+ {id: 4, user_id: 2, language_id: 2}
463
551
  ]
464
552
 
465
- when 'SELECT comments.* FROM custom_schema.comments WHERE (post_id IN (3, 4))'
553
+ when 'SELECT comments.* FROM schema_1.comments WHERE (post_id IN (3, 4))'
466
554
  [
467
555
  {id: 5, user_id: 2, post_id: 3},
468
556
  {id: 6, user_id: 1, post_id: 3},
@@ -470,88 +558,152 @@ describe 'Collection' do
470
558
  {id: 8, user_id: 2, post_id: 4}
471
559
  ]
472
560
 
473
- else
474
- nil
561
+ when 'SELECT languages.* FROM schema_2.languages WHERE (id IN (1, 2))'
562
+ [
563
+ {id: 1},
564
+ {id: 2}
565
+ ]
566
+
475
567
  end
476
568
  end
477
569
 
478
- Sequel.mock(fetch: stubs, autoid: 1).tap do |mock|
479
- get_schema_block = ->(table_name) { db.schema table_name }
480
- mock.define_singleton_method(:schema_parse_table) do |table_name, opts|
481
- get_schema_block.call table_name
570
+ last_id = 0
571
+ autoid = Proc.new do |sql|
572
+ case sql
573
+ when "INSERT INTO schema_1.people (document_number, first_name, last_name, birth_date, user_id) VALUES ('document_1', 'John', 'Doe', '2020-04-24 00:00:00.000000', 1)"
574
+ 'document_1'
575
+ else
576
+ last_id += 1
482
577
  end
483
578
  end
579
+
580
+ Sequel.mock fetch: stubs, autoid: autoid
581
+ end
582
+
583
+ let :stub_environment do
584
+ Rasti::DB::Environment.new default: Rasti::DB::DataSource.new(stub_db, :schema_1),
585
+ custom: Rasti::DB::DataSource.new(stub_db, :schema_2)
484
586
  end
485
587
 
486
- let(:stub_users) { Users.new stub_db, :custom_schema }
487
- let(:stub_posts) { Posts.new stub_db, :custom_schema }
488
- let(:stub_comments) { Comments.new stub_db, :custom_schema }
588
+ let(:stub_users) { Users.new stub_environment }
589
+ let(:stub_posts) { Posts.new stub_environment }
590
+ let(:stub_comments) { Comments.new stub_environment }
591
+ let(:stub_people) { People.new stub_environment }
592
+ let(:stub_languages) { Languages.new stub_environment }
489
593
 
490
594
  it 'Insert' do
491
595
  stub_users.insert name: 'User 1'
596
+
492
597
  stub_db.sqls.must_equal [
493
598
  'BEGIN',
494
- "INSERT INTO custom_schema.users (name) VALUES ('User 1')",
599
+ "INSERT INTO schema_1.users (name) VALUES ('User 1')",
495
600
  'COMMIT'
496
601
  ]
497
602
  end
498
603
 
499
604
  it 'Insert with many to many relation' do
500
- stub_posts.insert user_id: 1, title: 'Post 1', body: '...', categories: [2,3]
605
+ stub_posts.insert user_id: 1, title: 'Post 1', body: '...', categories: [2,3], language_id: 1
606
+
607
+ stub_db.sqls.must_equal [
608
+ 'BEGIN',
609
+ "INSERT INTO schema_1.posts (user_id, title, body, language_id) VALUES (1, 'Post 1', '...', 1)",
610
+ 'DELETE FROM schema_1.categories_posts WHERE (post_id IN (1))',
611
+ 'INSERT INTO schema_1.categories_posts (post_id, category_id) VALUES (1, 2)',
612
+ 'INSERT INTO schema_1.categories_posts (post_id, category_id) VALUES (1, 3)',
613
+ 'COMMIT'
614
+ ]
615
+ end
616
+
617
+ it 'Insert in multiple schemas' do
618
+ stub_languages.insert name: 'Spanish'
619
+
620
+ stub_users.insert name: 'User 1'
621
+
622
+ stub_people.insert document_number: 'document_1',
623
+ first_name: 'John',
624
+ last_name: 'Doe',
625
+ birth_date: Time.parse('2020-04-24'),
626
+ user_id: 1,
627
+ languages: [1]
628
+
501
629
  stub_db.sqls.must_equal [
502
630
  'BEGIN',
503
- "INSERT INTO custom_schema.posts (user_id, title, body) VALUES (1, 'Post 1', '...')",
504
- 'DELETE FROM custom_schema.categories_posts WHERE (post_id IN (1))',
505
- 'INSERT INTO custom_schema.categories_posts (post_id, category_id) VALUES (1, 2)',
506
- 'INSERT INTO custom_schema.categories_posts (post_id, category_id) VALUES (1, 3)',
631
+ "INSERT INTO schema_2.languages (name) VALUES ('Spanish')",
632
+ 'COMMIT',
633
+ 'BEGIN',
634
+ "INSERT INTO schema_1.users (name) VALUES ('User 1')",
635
+ 'COMMIT',
636
+ 'BEGIN',
637
+ "INSERT INTO schema_1.people (document_number, first_name, last_name, birth_date, user_id) VALUES ('document_1', 'John', 'Doe', '2020-04-24 00:00:00.000000', 1)",
638
+ "DELETE FROM schema_1.languages_people WHERE (document_number IN ('document_1'))",
639
+ "INSERT INTO schema_1.languages_people (document_number, language_id) VALUES ('document_1', 1)",
507
640
  'COMMIT'
508
641
  ]
509
642
  end
510
643
 
511
644
  it 'Update' do
512
645
  stub_users.update 1, name: 'Updated name'
646
+
513
647
  stub_db.sqls.must_equal [
514
648
  'BEGIN',
515
- "UPDATE custom_schema.users SET name = 'Updated name' WHERE (id = 1)",
649
+ "UPDATE schema_1.users SET name = 'Updated name' WHERE (id = 1)",
516
650
  'COMMIT'
517
651
  ]
518
652
  end
519
653
 
520
654
  it 'Delete' do
521
655
  stub_users.delete 1
522
- stub_db.sqls.must_equal ['DELETE FROM custom_schema.users WHERE (id = 1)']
656
+
657
+ stub_db.sqls.must_equal [
658
+ 'DELETE FROM schema_1.users WHERE (id = 1)'
659
+ ]
523
660
  end
524
661
 
525
662
  it 'Chained query' do
526
663
  stub_users.where(id: [1,2]).limit(1).order(:name).all
527
- stub_db.sqls.must_equal ['SELECT users.* FROM custom_schema.users WHERE (id IN (1, 2)) ORDER BY name LIMIT 1']
664
+
665
+ stub_db.sqls.must_equal [
666
+ 'SELECT users.* FROM schema_1.users WHERE (id IN (1, 2)) ORDER BY name LIMIT 1'
667
+ ]
528
668
  end
529
669
 
530
670
  it 'Graph' do
531
- stub_posts.graph(:user, :categories, 'comments.user.posts.categories').all
671
+ stub_posts.graph(:user, :categories, 'comments.user.posts.categories', 'language.people').all
672
+
673
+ stub_db.sqls.must_equal [
674
+ 'SELECT posts.* FROM schema_1.posts',
675
+ 'SELECT categories.*, categories_posts.post_id AS source_foreign_key FROM schema_1.categories INNER JOIN schema_1.categories_posts ON (schema_1.categories_posts.category_id = schema_1.categories.id) WHERE (categories_posts.post_id IN (3, 4))',
676
+ 'SELECT comments.* FROM schema_1.comments WHERE (post_id IN (3, 4))',
677
+ 'SELECT users.* FROM schema_1.users WHERE (id IN (2, 1))',
678
+ 'SELECT posts.* FROM schema_1.posts WHERE (user_id IN (1, 2))',
679
+ 'SELECT categories.*, categories_posts.post_id AS source_foreign_key FROM schema_1.categories INNER JOIN schema_1.categories_posts ON (schema_1.categories_posts.category_id = schema_1.categories.id) WHERE (categories_posts.post_id IN (3, 4))',
680
+ 'SELECT languages.* FROM schema_2.languages WHERE (id IN (1, 2))',
681
+ 'SELECT people.*, languages_people.language_id AS source_foreign_key FROM schema_1.people INNER JOIN schema_1.languages_people ON (schema_1.languages_people.document_number = schema_1.people.document_number) WHERE (languages_people.language_id IN (1, 2))',
682
+ 'SELECT users.* FROM schema_1.users WHERE (id IN (1, 2))'
683
+ ]
684
+ end
685
+
686
+ it 'Join' do
687
+ stub_posts.join('user.person').where(document_number: 'document_1').all
532
688
 
533
689
  stub_db.sqls.must_equal [
534
- 'SELECT posts.* FROM custom_schema.posts',
535
- 'SELECT categories.*, custom_schema.categories_posts.post_id AS source_foreign_key FROM custom_schema.categories INNER JOIN custom_schema.categories_posts ON (custom_schema.categories_posts.category_id = custom_schema.categories.id) WHERE (custom_schema.categories_posts.post_id IN (3, 4))',
536
- 'SELECT comments.* FROM custom_schema.comments WHERE (post_id IN (3, 4))',
537
- 'SELECT users.* FROM custom_schema.users WHERE (id IN (2, 1))',
538
- 'SELECT posts.* FROM custom_schema.posts WHERE (user_id IN (1, 2))',
539
- 'SELECT categories.*, custom_schema.categories_posts.post_id AS source_foreign_key FROM custom_schema.categories INNER JOIN custom_schema.categories_posts ON (custom_schema.categories_posts.category_id = custom_schema.categories.id) WHERE (custom_schema.categories_posts.post_id IN (3, 4))',
540
- 'SELECT users.* FROM custom_schema.users WHERE (id IN (1, 2))'
690
+ "SELECT DISTINCT posts.* FROM schema_1.posts INNER JOIN schema_1.users AS user ON (user.id = posts.user_id) INNER JOIN schema_1.people AS user__person ON (user__person.user_id = user.id) WHERE (document_number = 'document_1')"
541
691
  ]
542
692
  end
543
693
 
544
694
  it 'Named query' do
545
695
  stub_posts.commented_by(1).all
696
+
546
697
  stub_db.sqls.must_equal [
547
- 'SELECT DISTINCT posts.* FROM custom_schema.posts INNER JOIN custom_schema.comments ON (custom_schema.comments.post_id = custom_schema.posts.id) WHERE (custom_schema.comments.user_id = 1)'
698
+ 'SELECT DISTINCT posts.* FROM schema_1.posts INNER JOIN schema_1.comments ON (schema_1.comments.post_id = schema_1.posts.id) WHERE (comments.user_id = 1)'
548
699
  ]
549
700
  end
550
701
 
551
702
  it 'Custom query' do
552
703
  stub_comments.posts_commented_by(2)
704
+
553
705
  stub_db.sqls.must_equal [
554
- 'SELECT posts.* FROM custom_schema.comments INNER JOIN custom_schema.posts ON (custom_schema.posts.id = custom_schema.comments.post_id) WHERE (comments.user_id = 2)'
706
+ 'SELECT posts.* FROM schema_1.comments INNER JOIN schema_1.posts ON (schema_1.posts.id = schema_1.comments.post_id) WHERE (comments.user_id = 2)'
555
707
  ]
556
708
  end
557
709