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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b801c94dd51c4ee11f624cb542d784a42e445622e0090c42cb9d47322b87e855
4
- data.tar.gz: 518c58daf408e477f80cdac52837a0e2d3256938352c888a73e369f3eca5b7a2
3
+ metadata.gz: 210581882359ef05a1b09c69b172f259b0b5010b61a254442e9844158a4f9456
4
+ data.tar.gz: 1dd3f0fcc8399e0145abc50166495a87826bb1b32e52de4acb45ea114f7650d5
5
5
  SHA512:
6
- metadata.gz: ae1452c9b77c6d50f9aa8b05b85f9970c801369e7e3a67a7a88436762fbacea4191390e114be066458d7bc2dc563f834cda93a364185354c74267db86798a889
7
- data.tar.gz: 2707b44943ea63f17cd0f613c4fa08bdfc0663e8fde17f604c8c6ce9ba8f1fff23b4fa1641dfe42f77810a55fda6794491f5e8d8cb756c4fc19adb1cbb8cba76
6
+ metadata.gz: 2c209c735eb50327d4bb7b54754530cd155dec884e32661f046de73dfb53fb036877e1e25f1ade565ef51a0517254096d24934f3d6e47393febc99318ea71118
7
+ data.tar.gz: a5a8326418216e1558ffea4b93e52a2369420fed2c8d9633dda5ffd8ee34b457e06aaaea2018001ba8725e51c1ef44386a78e2abaa51819628eba93cec38da99
data/README.md CHANGED
@@ -28,49 +28,62 @@ Or install it yourself as:
28
28
  ### Database connection
29
29
 
30
30
  ```ruby
31
- DB = Sequel.connect ...
31
+ DB_1 = Sequel.connect ...
32
+ DB_2 = Sequel.connect ...
32
33
  ```
33
34
 
34
35
  ### Database schema
35
36
 
36
37
  ```ruby
37
- DB.create_table :users do
38
+ DB_1.create_table :users do
38
39
  primary_key :id
39
40
  String :name, null: false, unique: true
40
41
  end
41
42
 
42
- DB.create_table :posts do
43
+ DB_1.create_table :posts do
43
44
  primary_key :id
44
45
  String :title, null: false, unique: true
45
46
  String :body, null: false
47
+ Integer :language_id, null: false, index: true
46
48
  foreign_key :user_id, :users, null: false, index: true
47
49
  end
48
50
 
49
- DB.create_table :comments do
51
+ DB_1.create_table :comments do
50
52
  primary_key :id
51
53
  String :text, null: false
52
54
  foreign_key :user_id, :users, null: false, index: true
53
55
  foreign_key :post_id, :posts, null: false, index: true
54
56
  end
55
57
 
56
- DB.create_table :categories do
58
+ DB_1.create_table :categories do
57
59
  primary_key :id
58
60
  String :name, null: false, unique: true
59
61
  end
60
62
 
61
- DB.create_table :categories_posts do
63
+ DB_1.create_table :categories_posts do
62
64
  foreign_key :category_id, :categories, null: false, index: true
63
65
  foreign_key :post_id, :posts, null: false, index: true
64
66
  primary_key [:category_id, :post_id]
65
67
  end
66
68
 
67
- DB.create_table :people do
69
+ DB_1.create_table :people do
68
70
  String :document_number, null: false, primary_key: true
69
71
  String :first_name, null: false
70
72
  String :last_name, null: false
71
73
  Date :birth_date, null: false
72
74
  foreign_key :user_id, :users, null: false, unique: true
73
75
  end
76
+
77
+ DB_1.create_table :languages_people do
78
+ Integer :language_id, null: false, index: true
79
+ foreign_key :document_number, :people, type: String, null: false, index: true
80
+ primary_key [:language_id, :document_number]
81
+ end
82
+
83
+ DB_2.create_table :languages do
84
+ primary_key :id
85
+ String :name, null: false, unique: true
86
+ end
74
87
  ```
75
88
 
76
89
  ### Models
@@ -81,6 +94,7 @@ Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :cat
81
94
  Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post]
82
95
  Category = Rasti::DB::Model[:id, :name, :posts]
83
96
  Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user]
97
+ Language = Rasti::DB::Model[:id, :name, :people]
84
98
  ```
85
99
 
86
100
  ### Collections
@@ -105,8 +119,8 @@ class Posts < Rasti::DB::Collection
105
119
 
106
120
  query :commented_by do |user_id|
107
121
  chainable do
108
- dataset.join(with_schema(:comments), post_id: :id)
109
- .where(with_schema(:comments, :user_id) => user_id)
122
+ dataset.join(qualify(:comments), post_id: :id)
123
+ .where(Sequel[:comments][:user_id] => user_id)
110
124
  .select_all(:posts)
111
125
  .distinct
112
126
  end
@@ -129,19 +143,30 @@ class People < Rasti::DB::Collection
129
143
  set_model Person
130
144
 
131
145
  many_to_one :user
146
+ many_to_many :languages
132
147
  end
133
148
 
134
- users = Users.new DB
135
- posts = Posts.new DB
136
- comments = Comments.new DB
137
- categories = Categories.new DB
138
- people = People.new DB
149
+ class Languages < Rasti::DB::Collection
150
+ set_data_source_name :other
151
+
152
+ one_to_many :posts
153
+ many_to_many :people, collection: People, relation_data_source_name: :default
154
+ end
155
+
156
+ environment = Rasti::DB::Environment.new default: Rasti::DB::DataSource.new(DB_1),
157
+ other: Rasti::DB::DataSource.new(DB_2, 'custom_schema')
158
+
159
+ users = Users.new environment
160
+ posts = Posts.new environment
161
+ comments = Comments.new environment
162
+ categories = Categories.new environment
163
+ people = People.new environment
139
164
  ```
140
165
 
141
166
  ### Persistence
142
167
 
143
168
  ```ruby
144
- DB.transaction do
169
+ DB_1.transaction do
145
170
  id = users.insert name: 'User 1'
146
171
  users.update id, name: 'User updated'
147
172
  users.delete id
@@ -174,7 +199,7 @@ posts.created_by(1) # => [Post, ...]
174
199
  posts.created_by(1).entitled('...').commented_by(2) # => [Post, ...]
175
200
  posts.with_categories([1,2]) # => [Post, ...]
176
201
 
177
- posts.graph(:user, :categories, 'comments.user') # => [Post(User, [Categories, ...], [Comments(User)]), ...]
202
+ posts.graph(:user, :language, :categories, 'comments.user') # => [Post(User, Language, [Categories, ...], [Comments(User)]), ...]
178
203
 
179
204
  posts.join(:user).where(name: 'User 4') # => [Post, ...]
180
205
 
data/lib/rasti/db.rb CHANGED
@@ -14,7 +14,6 @@ module Rasti
14
14
  extend MultiRequire
15
15
  extend ClassConfig
16
16
 
17
- require_relative 'db/helpers'
18
17
  require_relative 'db/query'
19
18
  require_relative_pattern 'db/relations/*'
20
19
  require_relative_pattern 'db/type_converters/postgres_types/*'
@@ -4,8 +4,6 @@ module Rasti
4
4
 
5
5
  QUERY_METHODS = Query.public_instance_methods - Object.public_instance_methods
6
6
 
7
- include Helpers::WithSchema
8
-
9
7
  class << self
10
8
 
11
9
  extend Sequel::Inflections
@@ -37,12 +35,16 @@ module Rasti
37
35
  end
38
36
  end
39
37
 
38
+ def data_source_name
39
+ @data_source_name ||= superclass.respond_to?(:data_source_name) ? superclass.data_source_name : :default
40
+ end
41
+
40
42
  def relations
41
- @relations ||= {}
43
+ @relations ||= Hash::Indifferent.new
42
44
  end
43
45
 
44
46
  def queries
45
- @queries ||= {}
47
+ @queries ||= Hash::Indifferent.new
46
48
  end
47
49
 
48
50
  private
@@ -63,11 +65,17 @@ module Rasti
63
65
  @model = model
64
66
  end
65
67
 
68
+ def set_data_source_name(name)
69
+ @data_source_name = name.to_sym
70
+ end
71
+
66
72
  [Relations::OneToMany, Relations::ManyToOne, Relations::ManyToMany, Relations::OneToOne].each do |relation_class|
67
73
  define_method underscore(demodulize(relation_class.name)) do |name, options={}|
68
74
  relations[name] = relation_class.new name, self, options
69
75
 
70
- query "with_#{pluralize(name)}".to_sym do |primary_keys|
76
+ query_name = relations[name].to_many? ? name : pluralize(name)
77
+
78
+ query "with_#{query_name}" do |primary_keys|
71
79
  with_related name, primary_keys
72
80
  end
73
81
  end
@@ -85,11 +93,8 @@ module Rasti
85
93
 
86
94
  end
87
95
 
88
- attr_reader :db, :schema
89
-
90
- def initialize(db, schema=nil)
91
- @db = db
92
- @schema = schema ? schema.to_sym : nil
96
+ def initialize(environment)
97
+ @environment = environment
93
98
  end
94
99
 
95
100
  QUERY_METHODS.each do |method|
@@ -98,12 +103,8 @@ module Rasti
98
103
  end
99
104
  end
100
105
 
101
- def dataset
102
- db[qualified_collection_name]
103
- end
104
-
105
106
  def insert(attributes)
106
- db.transaction do
107
+ data_source.db.transaction do
107
108
  db_attributes = transform_attributes_to_db attributes
108
109
  collection_attributes, relations_primary_keys = split_related_attributes db_attributes
109
110
  primary_key = dataset.insert collection_attributes
@@ -126,7 +127,7 @@ module Rasti
126
127
  end
127
128
 
128
129
  def update(primary_key, attributes)
129
- db.transaction do
130
+ data_source.db.transaction do
130
131
  db_attributes = transform_attributes_to_db attributes
131
132
  collection_attributes, relations_primary_keys = split_related_attributes db_attributes
132
133
  dataset.where(self.class.primary_key => primary_key).update(collection_attributes) unless collection_attributes.empty?
@@ -152,7 +153,7 @@ module Rasti
152
153
  end
153
154
 
154
155
  def delete_relations(primary_key, relations)
155
- db.transaction do
156
+ data_source.db.transaction do
156
157
  relations.each do |relation_name, relation_primary_keys|
157
158
  relation = self.class.relations[relation_name]
158
159
  delete_relation_table relation, primary_key, relation_primary_keys
@@ -162,7 +163,7 @@ module Rasti
162
163
  end
163
164
 
164
165
  def delete_cascade(*primary_keys)
165
- db.transaction do
166
+ data_source.db.transaction do
166
167
  delete_cascade_relations primary_keys
167
168
  bulk_delete { |q| q.where self.class.primary_key => primary_keys }
168
169
  end
@@ -187,33 +188,48 @@ module Rasti
187
188
 
188
189
  private
189
190
 
190
- def transform_attributes_to_db(attributes)
191
- attributes.each_with_object({}) do |(attribute_name, value), result|
192
- transformed_value = Rasti::DB.to_db db, qualified_collection_name, attribute_name, value
193
- result[attribute_name] = transformed_value
194
- end
191
+ attr_reader :environment
192
+
193
+ def data_source
194
+ @data_source ||= environment.data_source_of self.class
195
+ end
196
+
197
+ def dataset
198
+ data_source.db[qualified_collection_name]
195
199
  end
196
200
 
197
201
  def qualified_collection_name
198
- schema.nil? ? Sequel[self.class.collection_name] : Sequel[schema][self.class.collection_name]
202
+ data_source.qualify self.class.collection_name
199
203
  end
200
204
 
205
+ def qualify(collection_name, data_source_name: nil)
206
+ data_source_name ||= self.class.data_source_name
207
+ environment.qualify data_source_name, collection_name
208
+ end
209
+
201
210
  def default_query
202
211
  Query.new collection_class: self.class,
203
212
  dataset: dataset.select_all(self.class.collection_name),
204
- schema: schema
213
+ environment: environment
205
214
  end
206
215
 
207
216
  def build_query(filter=nil, &block)
208
217
  raise ArgumentError, 'must specify filter hash or block' if filter.nil? && block.nil?
209
218
 
210
219
  if filter
211
- default_query.where filter
220
+ default_query.where(filter)
212
221
  else
213
222
  block.arity == 0 ? default_query.instance_eval(&block) : block.call(default_query)
214
223
  end
215
224
  end
216
225
 
226
+ def transform_attributes_to_db(attributes)
227
+ attributes.each_with_object({}) do |(attribute_name, value), result|
228
+ transformed_value = Rasti::DB.to_db data_source.db, qualified_collection_name, attribute_name, value
229
+ result[attribute_name] = transformed_value
230
+ end
231
+ end
232
+
217
233
  def split_related_attributes(attributes)
218
234
  relation_names = self.class.relations.values.select(&:many_to_many?).map(&:name)
219
235
 
@@ -239,18 +255,22 @@ module Rasti
239
255
  end
240
256
 
241
257
  relations.select { |r| r.one_to_many? || r.one_to_one? }.each do |relation|
242
- relation_collection_name = with_schema(relation.target_collection_class.collection_name)
243
- relations_ids = db[relation_collection_name].where(relation.foreign_key => primary_keys)
244
- .select(relation.target_collection_class.primary_key)
245
- .map(relation.target_collection_class.primary_key)
258
+ relation_data_source = environment.data_source_of relation.target_collection_class
259
+ relation_collection_name = relation_data_source.qualify relation.target_collection_class.collection_name
260
+
261
+ relations_ids = relation_data_source.db[relation_collection_name]
262
+ .where(relation.foreign_key => primary_keys)
263
+ .select(relation.target_collection_class.primary_key)
264
+ .map(relation.target_collection_class.primary_key)
246
265
 
247
- target_collection = relation.target_collection_class.new db, schema
266
+ target_collection = relation.target_collection_class.new environment
248
267
  target_collection.delete_cascade(*relations_ids) unless relations_ids.empty?
249
268
  end
250
269
  end
251
270
 
252
271
  def insert_relation_table(relation, primary_key, relation_primary_keys)
253
- relation_collection_name = relation.qualified_relation_collection_name(schema)
272
+ relation_data_source = environment.data_source relation.relation_data_source_name
273
+ relation_collection_name = relation_data_source.qualify relation.relation_collection_name
254
274
 
255
275
  values = relation_primary_keys.map do |relation_pk|
256
276
  {
@@ -259,12 +279,14 @@ module Rasti
259
279
  }
260
280
  end
261
281
 
262
- db[relation_collection_name].multi_insert values
282
+ relation_data_source.db[relation_collection_name].multi_insert values
263
283
  end
264
284
 
265
285
  def delete_relation_table(relation, primary_keys, relation_primary_keys=nil)
266
- relation_collection_name = relation.qualified_relation_collection_name(schema)
267
- ds = db[relation_collection_name].where(relation.source_foreign_key => primary_keys)
286
+ relation_data_source = environment.data_source relation.relation_data_source_name
287
+ relation_collection_name = relation_data_source.qualify relation.relation_collection_name
288
+
289
+ ds = relation_data_source.db[relation_collection_name].where(relation.source_foreign_key => primary_keys)
268
290
  ds = ds.where(relation.target_foreign_key => relation_primary_keys) if relation_primary_keys
269
291
  ds.delete
270
292
  end
@@ -0,0 +1,18 @@
1
+ module Rasti
2
+ module DB
3
+ class DataSource
4
+
5
+ attr_reader :db, :schema
6
+
7
+ def initialize(db, schema=nil)
8
+ @db = db
9
+ @schema = schema ? schema.to_sym : nil
10
+ end
11
+
12
+ def qualify(collection_name)
13
+ schema ? Sequel[schema][collection_name] : Sequel[collection_name]
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ module Rasti
2
+ module DB
3
+ class Environment
4
+
5
+ def initialize(data_sources)
6
+ @data_sources = data_sources
7
+ end
8
+
9
+ def data_source(name)
10
+ raise "Undefined data source #{name}" unless data_sources.key? name
11
+ data_sources[name]
12
+ end
13
+
14
+ def data_source_of(collection_class)
15
+ data_source collection_class.data_source_name
16
+ end
17
+
18
+ def qualify(data_source_name, collection_name)
19
+ data_source(data_source_name).qualify collection_name
20
+ end
21
+
22
+ def qualify_collection(collection_class)
23
+ data_source_of(collection_class).qualify collection_class.collection_name
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :data_sources
29
+
30
+ end
31
+ end
32
+ end
@@ -5,13 +5,12 @@ module Rasti
5
5
  DATASET_CHAINED_METHODS = [:where, :exclude, :or, :order, :reverse_order, :limit, :offset].freeze
6
6
 
7
7
  include Enumerable
8
- include Helpers::WithSchema
9
8
 
10
- def initialize(collection_class:, dataset:, relations_graph:nil, schema:nil)
9
+ def initialize(environment:, collection_class:, dataset:, relations_graph:nil)
10
+ @environment = environment
11
11
  @collection_class = collection_class
12
12
  @dataset = dataset
13
- @relations_graph = relations_graph || Relations::Graph.new(dataset.db, schema, collection_class)
14
- @schema = schema
13
+ @relations_graph = relations_graph || Relations::Graph.new(environment, collection_class)
15
14
  end
16
15
 
17
16
  DATASET_CHAINED_METHODS.each do |method|
@@ -74,7 +73,7 @@ module Rasti
74
73
  end
75
74
 
76
75
  def join(*relations)
77
- graph = Relations::Graph.new dataset.db, schema, collection_class, relations
76
+ graph = Relations::Graph.new environment, collection_class, relations
78
77
 
79
78
  ds = graph.add_joins(dataset)
80
79
  .distinct
@@ -127,14 +126,14 @@ module Rasti
127
126
 
128
127
  private
129
128
 
130
- attr_reader :collection_class, :dataset, :relations_graph, :schema
129
+ attr_reader :environment, :collection_class, :dataset, :relations_graph
131
130
 
132
131
  def build_query(**args)
133
132
  current_args = {
133
+ environment: environment,
134
134
  collection_class: collection_class,
135
135
  dataset: dataset,
136
- relations_graph: relations_graph,
137
- schema: schema
136
+ relations_graph: relations_graph
138
137
  }
139
138
 
140
139
  Query.new(**current_args.merge(args))
@@ -145,7 +144,7 @@ module Rasti
145
144
  end
146
145
 
147
146
  def with_related(relation_name, primary_keys)
148
- ds = collection_class.relations[relation_name].apply_filter dataset, schema, primary_keys
147
+ ds = collection_class.relations[relation_name].apply_filter environment, dataset, primary_keys
149
148
  build_query dataset: ds
150
149
  end
151
150
 
@@ -155,6 +154,11 @@ module Rasti
155
154
  data
156
155
  end
157
156
 
157
+ def qualify(collection_name, data_source_name: nil)
158
+ data_source_name ||= collection_class.data_source_name
159
+ environment.qualify data_source_name, collection_name
160
+ end
161
+
158
162
  def nql_parser
159
163
  NQL::SyntaxParser.new
160
164
  end