rasti-db 1.3.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aab2813290f3080c9a50cfed449183cc5da14e04915f6593fa9ab7fa5e5f24a7
4
- data.tar.gz: 3d46e38905af30b1a50a8b186b5ebf3f3253db29b58eed2526859c12862cc8af
3
+ metadata.gz: 8b41f6198798c9bee6da1f94742378639bb3b900f3bda6471f791b42a71a24c7
4
+ data.tar.gz: f470e1f1ef5673546e54e6c144e16ebba577b0f848b8fd59c8822489808a7cbf
5
5
  SHA512:
6
- metadata.gz: 5ae412446ff6d62b5cd1043e1958aaafe7dbc391ba98173adcc6ab5b31ce0d1daa8dc7086b62ae27ca50b7c03e6c2a1f6ac905a06a30a932a5e367e30698d8cd
7
- data.tar.gz: 68e99deac8ffb297263a56a1e3f4deff341518f94aba48bbbb8a760983e86d9268a9a7d96a36626ca7fcf4173c512f23c3585bcbae047dc964de493b2a7256e4
6
+ metadata.gz: f5e836d580a42656efe1d78ee2e193449edd1adf45dbccd0a4b4c9c4d6f263a0d92e844a142abb908a683a8dcad4865d5926aec10b5a0260c0240425dcd063dd
7
+ data.tar.gz: 71148abafe56a6fe60613f2ebfa38138df2aaa656bdbe7116a834d41f3920fb49b97bfffac3235a520a6ffbb293184310d2d7fcefa552178d1cb767b94ce8e4d
@@ -1,15 +1,14 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 2.0
5
4
  - 2.1
6
5
  - 2.2
7
- - 2.3.0
8
- - 2.4.0
9
- - 2.5.0
10
- - 2.6.0
11
- - jruby-9.1.7.0
12
- - jruby-9.1.16.0
6
+ - 2.3
7
+ - 2.4
8
+ - 2.5
9
+ - 2.6
10
+ - 2.7
11
+ - jruby-9.2.9.0
13
12
  - ruby-head
14
13
  - jruby-head
15
14
 
@@ -20,8 +19,4 @@ matrix:
20
19
  - rvm: jruby-head
21
20
 
22
21
  jdk:
23
- - openjdk8
24
-
25
- before_install:
26
- - rvm all-gemsets do gem uninstall bundler -ax || true
27
- - gem install bundler -v "< 2"
22
+ - openjdk8
data/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  [![Build Status](https://travis-ci.org/gabynaiman/rasti-db.svg?branch=master)](https://travis-ci.org/gabynaiman/rasti-db)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/gabynaiman/rasti-db/badge.svg?branch=master)](https://coveralls.io/github/gabynaiman/rasti-db?branch=master)
6
6
  [![Code Climate](https://codeclimate.com/github/gabynaiman/rasti-db.svg)](https://codeclimate.com/github/gabynaiman/rasti-db)
7
- [![Dependency Status](https://gemnasium.com/gabynaiman/rasti-db.svg)](https://gemnasium.com/gabynaiman/rasti-db)
8
7
 
9
8
  Database collections and relations
10
9
 
@@ -29,49 +28,62 @@ Or install it yourself as:
29
28
  ### Database connection
30
29
 
31
30
  ```ruby
32
- DB = Sequel.connect ...
31
+ DB_1 = Sequel.connect ...
32
+ DB_2 = Sequel.connect ...
33
33
  ```
34
34
 
35
35
  ### Database schema
36
36
 
37
37
  ```ruby
38
- DB.create_table :users do
38
+ DB_1.create_table :users do
39
39
  primary_key :id
40
40
  String :name, null: false, unique: true
41
41
  end
42
42
 
43
- DB.create_table :posts do
43
+ DB_1.create_table :posts do
44
44
  primary_key :id
45
45
  String :title, null: false, unique: true
46
46
  String :body, null: false
47
+ Integer :language_id, null: false, index: true
47
48
  foreign_key :user_id, :users, null: false, index: true
48
49
  end
49
50
 
50
- DB.create_table :comments do
51
+ DB_1.create_table :comments do
51
52
  primary_key :id
52
53
  String :text, null: false
53
54
  foreign_key :user_id, :users, null: false, index: true
54
55
  foreign_key :post_id, :posts, null: false, index: true
55
56
  end
56
57
 
57
- DB.create_table :categories do
58
+ DB_1.create_table :categories do
58
59
  primary_key :id
59
60
  String :name, null: false, unique: true
60
61
  end
61
62
 
62
- DB.create_table :categories_posts do
63
+ DB_1.create_table :categories_posts do
63
64
  foreign_key :category_id, :categories, null: false, index: true
64
65
  foreign_key :post_id, :posts, null: false, index: true
65
66
  primary_key [:category_id, :post_id]
66
67
  end
67
68
 
68
- DB.create_table :people do
69
+ DB_1.create_table :people do
69
70
  String :document_number, null: false, primary_key: true
70
71
  String :first_name, null: false
71
72
  String :last_name, null: false
72
73
  Date :birth_date, null: false
73
74
  foreign_key :user_id, :users, null: false, unique: true
74
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
75
87
  ```
76
88
 
77
89
  ### Models
@@ -82,6 +94,7 @@ Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :cat
82
94
  Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post]
83
95
  Category = Rasti::DB::Model[:id, :name, :posts]
84
96
  Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user]
97
+ Language = Rasti::DB::Model[:id, :name, :people]
85
98
  ```
86
99
 
87
100
  ### Collections
@@ -106,9 +119,9 @@ class Posts < Rasti::DB::Collection
106
119
 
107
120
  query :commented_by do |user_id|
108
121
  chainable do
109
- dataset.join(with_schema(:comments), post_id: :id)
110
- .where(with_schema(:comments, :user_id) => user_id)
111
- .select_all(with_schema(:posts))
122
+ dataset.join(qualify(:comments), post_id: :id)
123
+ .where(Sequel[:comments][:user_id] => user_id)
124
+ .select_all(:posts)
112
125
  .distinct
113
126
  end
114
127
  end
@@ -130,19 +143,30 @@ class People < Rasti::DB::Collection
130
143
  set_model Person
131
144
 
132
145
  many_to_one :user
146
+ many_to_many :languages
147
+ end
148
+
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
133
154
  end
134
155
 
135
- users = Users.new DB
136
- posts = Posts.new DB
137
- comments = Comments.new DB
138
- categories = Categories.new DB
139
- people = People.new DB
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
140
164
  ```
141
165
 
142
166
  ### Persistence
143
167
 
144
168
  ```ruby
145
- DB.transaction do
169
+ DB_1.transaction do
146
170
  id = users.insert name: 'User 1'
147
171
  users.update id, name: 'User updated'
148
172
  users.delete id
@@ -162,17 +186,49 @@ end
162
186
  posts.all # => [Post, ...]
163
187
  posts.first # => Post
164
188
  posts.count # => 1
189
+
165
190
  posts.where(id: [1,2]) # => [Post, ...]
166
191
  posts.where{id > 1}.limit(10).offset(20) } # => [Post, ...]
167
- posts.graph(:user, :categories, 'comments.user') # => [Post(User, [Categories, ...], [Comments(User)]), ...]
168
- posts.created_by(1) # => [Post, ...]
169
- posts.created_by(1).entitled('...').commented_by(2) # => [Post, ...]
170
- posts.with_categories([1,2]) # => [Post, ...]
192
+
171
193
  posts.where(id: [1,2]).raw # => [{id:1, ...}, {id:2, ...}]
172
194
  posts.where(id: [1,2]).primary_keys # => [1,2]
173
195
  posts.where(id: [1,2]).pluck(:id) # => [1,2]
174
196
  posts.where(id: [1,2]).pluck(:id, :title) # => [[1, ...], [2, ...]]
197
+
198
+ posts.created_by(1) # => [Post, ...]
199
+ posts.created_by(1).entitled('...').commented_by(2) # => [Post, ...]
200
+ posts.with_categories([1,2]) # => [Post, ...]
201
+
202
+ posts.graph(:user, :language, :categories, 'comments.user') # => [Post(User, Language, [Categories, ...], [Comments(User)]), ...]
203
+
175
204
  posts.join(:user).where(name: 'User 4') # => [Post, ...]
205
+
206
+ posts.select_attributes(:id, :title) # => [Post, ...]
207
+ posts.exclude_attributes(:id, :title) # => [Post, ...]
208
+ posts.all_attributes # => [Post, ...]
209
+
210
+ posts.graph('user.person').select_graph_attributes(user: [:id], 'user.person': [:last_name, :user_id]) # => [Post, ...]
211
+ posts.graph('user.person').exclude_graph_attributes(user: [:name], 'user.person': [:first_name, :last_name]) # => [Post, ...]
212
+ posts.graph('user.person').all_graph_attributes('user.person') # => [Post, ...]
213
+ ```
214
+ ### Natural Query Language
215
+
216
+ ```ruby
217
+ posts.nql('id = 1') # => Equal
218
+ posts.nql('id != 1') # => Not equal
219
+ posts.nql('title: My post') # => Include
220
+ posts.nql('title !: My post') # => Not include
221
+ posts.nql('title ~ My post') # => Insensitive like
222
+ posts.nql('id > 1') # => Greater
223
+ posts.nql('id >= 1') # => Greater or equal
224
+ posts.nql('id < 10') # => Less
225
+ posts.nql('id <= 10') # => Less or equal
226
+
227
+ posts.nql('id = 1 | id = 2') # => Or
228
+ posts.nql('id > 1 & title: "My post"') # => And
229
+ posts.nql('(id > 3 & id < 10) | title: "My post"') # => Precedence
230
+
231
+ posts.nql('comments.user.person.document_number = 7') # => Nested
176
232
  ```
177
233
 
178
234
  ## Development
@@ -3,7 +3,9 @@ require 'consty'
3
3
  require 'time'
4
4
  require 'timing'
5
5
  require 'treetop'
6
+ require 'hierarchical_graph'
6
7
  require 'class_config'
8
+ require 'hash_ext'
7
9
  require 'multi_require'
8
10
 
9
11
  module Rasti
@@ -12,7 +14,6 @@ module Rasti
12
14
  extend MultiRequire
13
15
  extend ClassConfig
14
16
 
15
- require_relative 'db/helpers'
16
17
  require_relative 'db/query'
17
18
  require_relative_pattern 'db/relations/*'
18
19
  require_relative_pattern 'db/type_converters/postgres_types/*'
@@ -2,10 +2,7 @@ module Rasti
2
2
  module DB
3
3
  class Collection
4
4
 
5
- QUERY_METHODS = (Query::DATASET_CHAINED_METHODS + [:graph, :join, :count, :all, :each, :first, :pluck, :primary_keys, :any?, :empty?, :raw, :nql]).freeze
6
-
7
- include Enumerable
8
- include Helpers::WithSchema
5
+ QUERY_METHODS = Query.public_instance_methods - Object.public_instance_methods
9
6
 
10
7
  class << self
11
8
 
@@ -16,8 +13,8 @@ module Rasti
16
13
  @collection_name ||= underscore(demodulize(name)).to_sym
17
14
  end
18
15
 
19
- def collection_fields
20
- @collection_fields ||= model.attributes - relations.keys
16
+ def collection_attributes
17
+ @collection_attributes ||= model.attributes - relations.keys
21
18
  end
22
19
 
23
20
  def primary_key
@@ -38,12 +35,16 @@ module Rasti
38
35
  end
39
36
  end
40
37
 
38
+ def data_source_name
39
+ @data_source_name ||= superclass.respond_to?(:data_source_name) ? superclass.data_source_name : :default
40
+ end
41
+
41
42
  def relations
42
- @relations ||= {}
43
+ @relations ||= Hash::Indifferent.new
43
44
  end
44
45
 
45
46
  def queries
46
- @queries ||= {}
47
+ @queries ||= Hash::Indifferent.new
47
48
  end
48
49
 
49
50
  private
@@ -64,11 +65,17 @@ module Rasti
64
65
  @model = model
65
66
  end
66
67
 
68
+ def set_data_source_name(name)
69
+ @data_source_name = name.to_sym
70
+ end
71
+
67
72
  [Relations::OneToMany, Relations::ManyToOne, Relations::ManyToMany, Relations::OneToOne].each do |relation_class|
68
73
  define_method underscore(demodulize(relation_class.name)) do |name, options={}|
69
74
  relations[name] = relation_class.new name, self, options
70
75
 
71
- 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|
72
79
  with_related name, primary_keys
73
80
  end
74
81
  end
@@ -80,25 +87,24 @@ module Rasti
80
87
  queries[name] = lambda || block
81
88
 
82
89
  define_method name do |*args|
83
- query.instance_exec(*args, &self.class.queries[name])
90
+ default_query.instance_exec(*args, &self.class.queries.fetch(name))
84
91
  end
85
92
  end
86
93
 
87
94
  end
88
95
 
89
- attr_reader :db, :schema
90
-
91
- def initialize(db, schema=nil)
92
- @db = db
93
- @schema = schema ? schema.to_sym : nil
96
+ def initialize(environment)
97
+ @environment = environment
94
98
  end
95
99
 
96
- def dataset
97
- db[qualified_collection_name]
100
+ QUERY_METHODS.each do |method|
101
+ define_method method do |*args, &block|
102
+ default_query.public_send method, *args, &block
103
+ end
98
104
  end
99
105
 
100
106
  def insert(attributes)
101
- db.transaction do
107
+ data_source.db.transaction do
102
108
  db_attributes = transform_attributes_to_db attributes
103
109
  collection_attributes, relations_primary_keys = split_related_attributes db_attributes
104
110
  primary_key = dataset.insert collection_attributes
@@ -121,7 +127,7 @@ module Rasti
121
127
  end
122
128
 
123
129
  def update(primary_key, attributes)
124
- db.transaction do
130
+ data_source.db.transaction do
125
131
  db_attributes = transform_attributes_to_db attributes
126
132
  collection_attributes, relations_primary_keys = split_related_attributes db_attributes
127
133
  dataset.where(self.class.primary_key => primary_key).update(collection_attributes) unless collection_attributes.empty?
@@ -147,7 +153,7 @@ module Rasti
147
153
  end
148
154
 
149
155
  def delete_relations(primary_key, relations)
150
- db.transaction do
156
+ data_source.db.transaction do
151
157
  relations.each do |relation_name, relation_primary_keys|
152
158
  relation = self.class.relations[relation_name]
153
159
  delete_relation_table relation, primary_key, relation_primary_keys
@@ -157,7 +163,7 @@ module Rasti
157
163
  end
158
164
 
159
165
  def delete_cascade(*primary_keys)
160
- db.transaction do
166
+ data_source.db.transaction do
161
167
  delete_cascade_relations primary_keys
162
168
  bulk_delete { |q| q.where self.class.primary_key => primary_keys }
163
169
  end
@@ -172,12 +178,6 @@ module Rasti
172
178
  where(self.class.primary_key => primary_key).graph(*relations).first
173
179
  end
174
180
 
175
- QUERY_METHODS.each do |method|
176
- define_method method do |*args, &block|
177
- query.public_send method, *args, &block
178
- end
179
- end
180
-
181
181
  def exists?(filter=nil, &block)
182
182
  build_query(filter, &block).any?
183
183
  end
@@ -188,27 +188,45 @@ module Rasti
188
188
 
189
189
  private
190
190
 
191
- def transform_attributes_to_db(attributes)
192
- attributes.each_with_object({}) do |(attribute_name, value), result|
193
- transformed_value = Rasti::DB.to_db db, qualified_collection_name, attribute_name, value
194
- result[attribute_name] = transformed_value
195
- 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]
196
199
  end
197
200
 
198
201
  def qualified_collection_name
199
- schema.nil? ? Sequel[self.class.collection_name] : Sequel[schema][self.class.collection_name]
202
+ data_source.qualify self.class.collection_name
200
203
  end
201
204
 
202
- def query
203
- Query.new self.class, dataset, [], schema
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
+
210
+ def default_query
211
+ Query.new collection_class: self.class,
212
+ dataset: dataset.select_all(self.class.collection_name),
213
+ environment: environment
204
214
  end
205
215
 
206
216
  def build_query(filter=nil, &block)
207
217
  raise ArgumentError, 'must specify filter hash or block' if filter.nil? && block.nil?
218
+
208
219
  if filter
209
- query.where filter
220
+ default_query.where(filter)
210
221
  else
211
- block.arity == 0 ? query.instance_eval(&block) : block.call(query)
222
+ block.arity == 0 ? default_query.instance_eval(&block) : block.call(default_query)
223
+ end
224
+ end
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
212
230
  end
213
231
  end
214
232
 
@@ -237,18 +255,22 @@ module Rasti
237
255
  end
238
256
 
239
257
  relations.select { |r| r.one_to_many? || r.one_to_one? }.each do |relation|
240
- relation_collection_name = with_schema(relation.target_collection_class.collection_name)
241
- relations_ids = db[relation_collection_name].where(relation.foreign_key => primary_keys)
242
- .select(relation.target_collection_class.primary_key)
243
- .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
244
260
 
245
- target_collection = relation.target_collection_class.new db, schema
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)
265
+
266
+ target_collection = relation.target_collection_class.new environment
246
267
  target_collection.delete_cascade(*relations_ids) unless relations_ids.empty?
247
268
  end
248
269
  end
249
270
 
250
271
  def insert_relation_table(relation, primary_key, relation_primary_keys)
251
- 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
252
274
 
253
275
  values = relation_primary_keys.map do |relation_pk|
254
276
  {
@@ -257,12 +279,14 @@ module Rasti
257
279
  }
258
280
  end
259
281
 
260
- db[relation_collection_name].multi_insert values
282
+ relation_data_source.db[relation_collection_name].multi_insert values
261
283
  end
262
284
 
263
285
  def delete_relation_table(relation, primary_keys, relation_primary_keys=nil)
264
- relation_collection_name = relation.qualified_relation_collection_name(schema)
265
- 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)
266
290
  ds = ds.where(relation.target_foreign_key => relation_primary_keys) if relation_primary_keys
267
291
  ds.delete
268
292
  end