rasti-db 0.4.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +7 -3
- data/lib/rasti/db/collection.rb +13 -9
- data/lib/rasti/db/model.rb +1 -5
- data/lib/rasti/db/query.rb +3 -3
- data/lib/rasti/db/type_converters/postgres.rb +65 -0
- data/lib/rasti/db/type_converters/postgres_types/array.rb +32 -0
- data/lib/rasti/db/type_converters/postgres_types/hstore.rb +31 -0
- data/lib/rasti/db/type_converters/postgres_types/json.rb +40 -0
- data/lib/rasti/db/type_converters/postgres_types/jsonb.rb +40 -0
- data/lib/rasti/db/type_converters/time_in_zone.rb +21 -0
- data/lib/rasti/db/version.rb +1 -1
- data/lib/rasti/db.rb +29 -1
- data/rasti-db.gemspec +1 -0
- data/spec/collection_spec.rb +6 -0
- data/spec/minitest_helper.rb +4 -5
- data/spec/model_spec.rb +0 -6
- data/spec/type_converters/postgres_spec.rb +229 -0
- data/spec/type_converters/time_in_zone_spec.rb +33 -0
- metadata +32 -5
- data/lib/rasti/db/type_converter.rb +0 -53
- data/spec/type_converter_spec.rb +0 -106
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01b5188397b357e5b8414eebd556115131bb8c46
|
4
|
+
data.tar.gz: 84d1beb743f8a8ff07247724013bb8c0f9394ede
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10563c028636028583d1205b0c999ad82e5281180ba16367442b1f5804510b09184db8930145e9937e2a82e675879aa9f56a8eea8f399ab6346eb6f40a6df1eb
|
7
|
+
data.tar.gz: a8ba4affb905f24c39cfab601b51cd263ccf2ab3c34c22494e4fc53475e96530e73df7e2759af381e7d1f2f288b2f168408432138a37042b40823a1697c64a46
|
data/.travis.yml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
language: ruby
|
2
2
|
|
3
3
|
rvm:
|
4
|
-
- 1.9.3
|
5
4
|
- 2.0
|
6
5
|
- 2.1
|
7
6
|
- 2.2
|
8
7
|
- 2.3.0
|
9
8
|
- 2.4.0
|
10
9
|
- 2.5.0
|
11
|
-
-
|
10
|
+
- 2.6.0
|
12
11
|
- jruby-9.1.7.0
|
12
|
+
- jruby-9.1.16.0
|
13
13
|
- ruby-head
|
14
14
|
- jruby-head
|
15
15
|
|
@@ -19,5 +19,9 @@ matrix:
|
|
19
19
|
- rvm: ruby-head
|
20
20
|
- rvm: jruby-head
|
21
21
|
|
22
|
+
jdk:
|
23
|
+
- openjdk8
|
24
|
+
|
22
25
|
before_install:
|
23
|
-
- gem
|
26
|
+
- rvm all-gemsets do gem uninstall bundler -ax || true
|
27
|
+
- gem install bundler -v "< 2"
|
data/lib/rasti/db/collection.rb
CHANGED
@@ -2,8 +2,9 @@ module Rasti
|
|
2
2
|
module DB
|
3
3
|
class Collection
|
4
4
|
|
5
|
-
QUERY_METHODS = (Query::DATASET_CHAINED_METHODS + [:graph, :count, :all, :first, :pluck, :primary_keys, :any?, :empty?, :raw]).freeze
|
5
|
+
QUERY_METHODS = (Query::DATASET_CHAINED_METHODS + [:graph, :count, :all, :each, :first, :pluck, :primary_keys, :any?, :empty?, :raw]).freeze
|
6
6
|
|
7
|
+
include Enumerable
|
7
8
|
include Helpers::WithSchema
|
8
9
|
|
9
10
|
class << self
|
@@ -75,7 +76,7 @@ module Rasti
|
|
75
76
|
queries[name] = lambda || block
|
76
77
|
|
77
78
|
define_method name do |*args|
|
78
|
-
query.instance_exec
|
79
|
+
query.instance_exec(*args, &self.class.queries[name])
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
@@ -94,7 +95,7 @@ module Rasti
|
|
94
95
|
|
95
96
|
def insert(attributes)
|
96
97
|
db.transaction do
|
97
|
-
db_attributes =
|
98
|
+
db_attributes = transform_attributes_to_db attributes
|
98
99
|
collection_attributes, relations_primary_keys = split_related_attributes db_attributes
|
99
100
|
primary_key = dataset.insert collection_attributes
|
100
101
|
save_relations primary_key, relations_primary_keys
|
@@ -103,7 +104,7 @@ module Rasti
|
|
103
104
|
end
|
104
105
|
|
105
106
|
def bulk_insert(attributes, options={})
|
106
|
-
db_attributes =
|
107
|
+
db_attributes = attributes.map { |attrs| transform_attributes_to_db attrs }
|
107
108
|
dataset.multi_insert db_attributes, options
|
108
109
|
end
|
109
110
|
|
@@ -117,7 +118,7 @@ module Rasti
|
|
117
118
|
|
118
119
|
def update(primary_key, attributes)
|
119
120
|
db.transaction do
|
120
|
-
db_attributes =
|
121
|
+
db_attributes = transform_attributes_to_db attributes
|
121
122
|
collection_attributes, relations_primary_keys = split_related_attributes db_attributes
|
122
123
|
dataset.where(self.class.primary_key => primary_key).update(collection_attributes) unless collection_attributes.empty?
|
123
124
|
save_relations primary_key, relations_primary_keys
|
@@ -126,7 +127,7 @@ module Rasti
|
|
126
127
|
end
|
127
128
|
|
128
129
|
def bulk_update(attributes, &block)
|
129
|
-
db_attributes =
|
130
|
+
db_attributes = transform_attributes_to_db attributes
|
130
131
|
build_query(&block).instance_eval { dataset.update db_attributes }
|
131
132
|
nil
|
132
133
|
end
|
@@ -183,8 +184,11 @@ module Rasti
|
|
183
184
|
|
184
185
|
private
|
185
186
|
|
186
|
-
def
|
187
|
-
|
187
|
+
def transform_attributes_to_db(attributes)
|
188
|
+
attributes.each_with_object({}) do |(attribute_name, value), result|
|
189
|
+
transformed_value = Rasti::DB.to_db db, qualified_collection_name, attribute_name, value
|
190
|
+
result[attribute_name] = transformed_value
|
191
|
+
end
|
188
192
|
end
|
189
193
|
|
190
194
|
def qualified_collection_name
|
@@ -235,7 +239,7 @@ module Rasti
|
|
235
239
|
.map(relation.target_collection_class.primary_key)
|
236
240
|
|
237
241
|
target_collection = relation.target_collection_class.new db, schema
|
238
|
-
target_collection.delete_cascade
|
242
|
+
target_collection.delete_cascade(*relations_ids) unless relations_ids.empty?
|
239
243
|
end
|
240
244
|
end
|
241
245
|
|
data/lib/rasti/db/model.rb
CHANGED
@@ -102,13 +102,9 @@ module Rasti
|
|
102
102
|
attr_reader :attributes
|
103
103
|
|
104
104
|
def fetch_attribute(name)
|
105
|
-
attributes.key?(name) ?
|
105
|
+
attributes.key?(name) ? Rasti::DB.from_db(attributes[name]) : raise(UninitializedAttributeError, name)
|
106
106
|
end
|
107
107
|
|
108
|
-
def casted_attribute(name)
|
109
|
-
attributes[name].is_a?(Time) ? Timing::TimeInZone.new(attributes[name]) : attributes[name]
|
110
|
-
end
|
111
|
-
|
112
108
|
end
|
113
109
|
end
|
114
110
|
end
|
data/lib/rasti/db/query.rb
CHANGED
@@ -35,7 +35,7 @@ module Rasti
|
|
35
35
|
alias_method :to_a, :all
|
36
36
|
|
37
37
|
def each(&block)
|
38
|
-
all.each
|
38
|
+
all.each(&block)
|
39
39
|
end
|
40
40
|
|
41
41
|
DATASET_CHAINED_METHODS.each do |method|
|
@@ -88,7 +88,7 @@ module Rasti
|
|
88
88
|
private
|
89
89
|
|
90
90
|
def chainable(&block)
|
91
|
-
ds = instance_eval
|
91
|
+
ds = instance_eval(&block)
|
92
92
|
Query.new collection_class, ds, relations, schema
|
93
93
|
end
|
94
94
|
|
@@ -105,7 +105,7 @@ module Rasti
|
|
105
105
|
|
106
106
|
def method_missing(method, *args, &block)
|
107
107
|
if collection_class.queries.key?(method)
|
108
|
-
instance_exec
|
108
|
+
instance_exec(*args, &collection_class.queries[method])
|
109
109
|
else
|
110
110
|
super
|
111
111
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Rasti
|
2
|
+
module DB
|
3
|
+
module TypeConverters
|
4
|
+
class Postgres
|
5
|
+
|
6
|
+
CONVERTERS = [PostgresTypes::JSON, PostgresTypes::JSONB, PostgresTypes::HStore, PostgresTypes::Array]
|
7
|
+
|
8
|
+
@to_db_mapping = {}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def to_db(db, collection_name, attribute_name, value)
|
13
|
+
to_db_mapping = to_db_mapping_for db, collection_name
|
14
|
+
|
15
|
+
if to_db_mapping.key? attribute_name
|
16
|
+
to_db_mapping[attribute_name][:converter].to_db value, to_db_mapping[attribute_name][:sub_type]
|
17
|
+
else
|
18
|
+
value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_db(object)
|
23
|
+
if from_db_mapping.key? object.class
|
24
|
+
from_db_mapping[object.class].from_db object
|
25
|
+
else
|
26
|
+
object
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def to_db_mapping_for(db, collection_name)
|
33
|
+
key = [db.opts[:database], collection_name]
|
34
|
+
|
35
|
+
@to_db_mapping[key] ||= begin
|
36
|
+
columns = Hash[db.schema(collection_name)]
|
37
|
+
|
38
|
+
columns.each_with_object({}) do |(name, schema), hash|
|
39
|
+
CONVERTERS.each do |converter|
|
40
|
+
unless hash.key? name
|
41
|
+
match = converter.column_type_regex.match schema[:db_type]
|
42
|
+
|
43
|
+
hash[name] = {converter: converter, sub_type: match.captures.first} if match
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def from_db_mapping
|
51
|
+
@from_db_mapping ||= begin
|
52
|
+
CONVERTERS.each_with_object({}) do |converter, result|
|
53
|
+
converter.db_classes.each do |db_class|
|
54
|
+
result[db_class] = converter
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rasti
|
2
|
+
module DB
|
3
|
+
module TypeConverters
|
4
|
+
module PostgresTypes
|
5
|
+
class Array
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def column_type_regex
|
10
|
+
/^([a-z]+)\[\]$/
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_db(value, sub_type)
|
14
|
+
array = sub_type == 'hstore' ? value.map { |v| Sequel.hstore v } : value
|
15
|
+
Sequel.pg_array array, sub_type
|
16
|
+
end
|
17
|
+
|
18
|
+
def db_classes
|
19
|
+
[Sequel::Postgres::PGArray]
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_db(object)
|
23
|
+
object.to_a
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rasti
|
2
|
+
module DB
|
3
|
+
module TypeConverters
|
4
|
+
module PostgresTypes
|
5
|
+
class HStore
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def column_type_regex
|
10
|
+
/^hstore$/
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_db(value, sub_type)
|
14
|
+
Sequel.hstore value
|
15
|
+
end
|
16
|
+
|
17
|
+
def db_classes
|
18
|
+
[Sequel::Postgres::HStore]
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_db(object)
|
22
|
+
object.to_h
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rasti
|
2
|
+
module DB
|
3
|
+
module TypeConverters
|
4
|
+
module PostgresTypes
|
5
|
+
class JSON
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def column_type_regex
|
10
|
+
/^json$/
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_db(value, sub_type)
|
14
|
+
Sequel.pg_json value
|
15
|
+
end
|
16
|
+
|
17
|
+
def db_classes
|
18
|
+
@db_classes ||= from_db_convertions.keys
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_db(object)
|
22
|
+
object.public_send from_db_convertions[object.class]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def from_db_convertions
|
28
|
+
@from_db_convertions ||= {
|
29
|
+
Sequel::Postgres::JSONHash => :to_h,
|
30
|
+
Sequel::Postgres::JSONArray => :to_a
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rasti
|
2
|
+
module DB
|
3
|
+
module TypeConverters
|
4
|
+
module PostgresTypes
|
5
|
+
class JSONB
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def column_type_regex
|
10
|
+
/^jsonb$/
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_db(value, sub_type)
|
14
|
+
Sequel.pg_jsonb value
|
15
|
+
end
|
16
|
+
|
17
|
+
def db_classes
|
18
|
+
@db_classes ||= from_db_convertions.keys
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_db(object)
|
22
|
+
object.public_send from_db_convertions[object.class]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def from_db_convertions
|
28
|
+
@from_db_convertions ||= {
|
29
|
+
Sequel::Postgres::JSONBHash => :to_h,
|
30
|
+
Sequel::Postgres::JSONBArray => :to_a
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rasti
|
2
|
+
module DB
|
3
|
+
module TypeConverters
|
4
|
+
class TimeInZone
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def to_db(db, collection_name, attribute_name, value)
|
9
|
+
value
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_db(value)
|
13
|
+
value.is_a?(Time) ? Timing::TimeInZone.new(value) : value
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/rasti/db/version.rb
CHANGED
data/lib/rasti/db.rb
CHANGED
@@ -2,6 +2,7 @@ require 'sequel'
|
|
2
2
|
require 'consty'
|
3
3
|
require 'time'
|
4
4
|
require 'timing'
|
5
|
+
require 'class_config'
|
5
6
|
|
6
7
|
require_relative 'db/version'
|
7
8
|
require_relative 'db/helpers'
|
@@ -14,4 +15,31 @@ require_relative 'db/relations/many_to_one'
|
|
14
15
|
require_relative 'db/relations/many_to_many'
|
15
16
|
require_relative 'db/collection'
|
16
17
|
require_relative 'db/model'
|
17
|
-
require_relative 'db/
|
18
|
+
require_relative 'db/type_converters/time_in_zone'
|
19
|
+
require_relative 'db/type_converters/postgres_types/array'
|
20
|
+
require_relative 'db/type_converters/postgres_types/hstore'
|
21
|
+
require_relative 'db/type_converters/postgres_types/json'
|
22
|
+
require_relative 'db/type_converters/postgres_types/jsonb'
|
23
|
+
require_relative 'db/type_converters/postgres'
|
24
|
+
|
25
|
+
module Rasti
|
26
|
+
module DB
|
27
|
+
|
28
|
+
extend ClassConfig
|
29
|
+
|
30
|
+
attr_config :type_converters, []
|
31
|
+
|
32
|
+
def self.to_db(db, collection_name, attribute_name, value)
|
33
|
+
type_converters.inject(value) do |result, type_converter|
|
34
|
+
type_converter.to_db db, collection_name, attribute_name, result
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.from_db(value)
|
39
|
+
type_converters.inject(value) do |result, type_converter|
|
40
|
+
type_converter.from_db result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/rasti-db.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_runtime_dependency 'sequel', '~> 5.0'
|
22
22
|
spec.add_runtime_dependency 'consty', '~> 1.0', '>= 1.0.3'
|
23
23
|
spec.add_runtime_dependency 'timing', '~> 0.1', '>= 0.1.3'
|
24
|
+
spec.add_runtime_dependency 'class_config', '~> 0.0', '>= 0.0.2'
|
24
25
|
|
25
26
|
spec.add_development_dependency 'bundler', '~> 1.12'
|
26
27
|
spec.add_development_dependency 'rake', '~> 11.0'
|
data/spec/collection_spec.rb
CHANGED
@@ -254,6 +254,12 @@ describe 'Collection' do
|
|
254
254
|
users.all.must_equal [User.new(id: id, name: 'User 1')]
|
255
255
|
end
|
256
256
|
|
257
|
+
it 'Map' do
|
258
|
+
1.upto(2) { |i| db[:users].insert name: "User #{i}" }
|
259
|
+
|
260
|
+
users.map(&:name).sort.must_equal ['User 1', 'User 2']
|
261
|
+
end
|
262
|
+
|
257
263
|
it 'First' do
|
258
264
|
1.upto(10) { |i| db[:users].insert name: "User #{i}" }
|
259
265
|
|
data/spec/minitest_helper.rb
CHANGED
@@ -8,6 +8,10 @@ require 'sequel/extensions/pg_hstore'
|
|
8
8
|
require 'sequel/extensions/pg_array'
|
9
9
|
require 'sequel/extensions/pg_json'
|
10
10
|
|
11
|
+
Rasti::DB.configure do |config|
|
12
|
+
config.type_converters = [Rasti::DB::TypeConverters::TimeInZone]
|
13
|
+
end
|
14
|
+
|
11
15
|
User = Rasti::DB::Model[:id, :name, :posts, :comments, :person]
|
12
16
|
Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :categories]
|
13
17
|
Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post]
|
@@ -68,11 +72,6 @@ class People < Rasti::DB::Collection
|
|
68
72
|
end
|
69
73
|
|
70
74
|
|
71
|
-
Rasti::DB::TypeConverter::CONVERTIONS[:sqlite] = {
|
72
|
-
Regexp.new('integer') => ->(value, match) { value.to_i }
|
73
|
-
}
|
74
|
-
|
75
|
-
|
76
75
|
class Minitest::Spec
|
77
76
|
|
78
77
|
let(:users) { Users.new db }
|
data/spec/model_spec.rb
CHANGED
@@ -28,12 +28,6 @@ describe 'Model' do
|
|
28
28
|
proc { post.invalid_method }.must_raise NoMethodError
|
29
29
|
end
|
30
30
|
|
31
|
-
it 'Time conversion' do
|
32
|
-
person = Person.new birth_date: Time.parse('2019-01-07T11:00:00-03:00')
|
33
|
-
|
34
|
-
person.birth_date.must_be_instance_of Timing::TimeInZone
|
35
|
-
end
|
36
|
-
|
37
31
|
end
|
38
32
|
|
39
33
|
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Rasti::DB::TypeConverters::Postgres do
|
4
|
+
|
5
|
+
let(:type_converter) { Rasti::DB::TypeConverters::Postgres }
|
6
|
+
|
7
|
+
let(:pg) do
|
8
|
+
Object.new.tap do |pg|
|
9
|
+
|
10
|
+
def pg.opts
|
11
|
+
{
|
12
|
+
database: 'database'
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def pg.schema(table_name, opts={})
|
17
|
+
[
|
18
|
+
[:hash, {db_type: 'hstore'}],
|
19
|
+
[:text_array, {db_type: 'text[]'}],
|
20
|
+
[:integer_array, {db_type: 'integer[]'}],
|
21
|
+
[:hstore_array, {db_type: 'hstore[]'}],
|
22
|
+
[:json, {db_type: 'json'}],
|
23
|
+
[:jsonb, {db_type: 'jsonb'}]
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'Default' do
|
31
|
+
|
32
|
+
it 'must not change value in to_db if column not found in mapping' do
|
33
|
+
string = type_converter.to_db pg, :table_name, :column, "hola"
|
34
|
+
|
35
|
+
string.class.must_equal String
|
36
|
+
string.must_equal "hola"
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'must not change value in from_db if class not found in mapping' do
|
40
|
+
string = type_converter.from_db "hola"
|
41
|
+
|
42
|
+
string.class.must_equal String
|
43
|
+
string.must_equal "hola"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
describe 'HStore' do
|
50
|
+
|
51
|
+
describe 'To DB' do
|
52
|
+
|
53
|
+
it 'must transform Hash to HStore' do
|
54
|
+
hstore = type_converter.to_db pg, :table_name, :hash, {key_1: 1, key_2: 2}
|
55
|
+
|
56
|
+
hstore.class.must_equal Sequel::Postgres::HStore
|
57
|
+
hstore.must_equal 'key_1' => '1', 'key_2' => '2'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'must transform empty hash to HStore' do
|
61
|
+
hstore = type_converter.to_db pg, :table_name, :hash, {}
|
62
|
+
|
63
|
+
hstore.class.must_equal Sequel::Postgres::HStore
|
64
|
+
hstore.must_be_empty
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'From DB' do
|
70
|
+
|
71
|
+
it 'must transform HStore to Hash' do
|
72
|
+
hstore = Sequel::Postgres::HStore.new 'key_1' => '1', 'key_2' => '2'
|
73
|
+
hash = type_converter.from_db hstore
|
74
|
+
|
75
|
+
hash.class.must_equal Hash
|
76
|
+
hash.must_equal hstore
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'Array' do
|
84
|
+
|
85
|
+
describe 'To DB' do
|
86
|
+
|
87
|
+
it 'must transform String[] to PGArray' do
|
88
|
+
pg_array = type_converter.to_db pg, :table_name, :text_array, %w(a b c)
|
89
|
+
|
90
|
+
pg_array.class.must_equal Sequel::Postgres::PGArray
|
91
|
+
pg_array.array_type.must_equal 'text'
|
92
|
+
pg_array.must_equal %w(a b c)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'must transform Integer[] to PGArray' do
|
96
|
+
pg_array = type_converter.to_db pg, :table_name, :integer_array, [1,2,3]
|
97
|
+
|
98
|
+
pg_array.class.must_equal Sequel::Postgres::PGArray
|
99
|
+
pg_array.array_type.must_equal 'integer'
|
100
|
+
pg_array.must_equal [1,2,3]
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'must transform Hstore[] to PGArray' do
|
104
|
+
pg_array = type_converter.to_db pg, :table_name, :hstore_array, [{key: 0}, {key: 1}]
|
105
|
+
|
106
|
+
pg_array.class.must_equal Sequel::Postgres::PGArray
|
107
|
+
pg_array.array_type.must_equal 'hstore'
|
108
|
+
|
109
|
+
pg_array.each_with_index do |element, index|
|
110
|
+
element.class.must_equal Sequel::Postgres::HStore
|
111
|
+
element.must_equal 'key' => index.to_s
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'Must transform empty array to PGArray' do
|
116
|
+
pg_array = type_converter.to_db pg, :table_name, :integer_array, []
|
117
|
+
|
118
|
+
pg_array.class.must_equal Sequel::Postgres::PGArray
|
119
|
+
pg_array.array_type.must_equal 'integer'
|
120
|
+
pg_array.must_be_empty
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'From DB' do
|
126
|
+
|
127
|
+
it 'must transform PGArray to Array' do
|
128
|
+
pg_array = Sequel::Postgres::PGArray.new [1,2,3]
|
129
|
+
array = type_converter.from_db pg_array
|
130
|
+
|
131
|
+
array.class.must_equal Array
|
132
|
+
array.must_equal pg_array
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
describe 'JSON' do
|
140
|
+
|
141
|
+
let(:json_hash) { {key_1: {key_2: [3]}} }
|
142
|
+
let(:json_array) { [{key_1: {key_2: [3]}}] }
|
143
|
+
|
144
|
+
describe 'To DB' do
|
145
|
+
|
146
|
+
it 'must transform Hash to JSONHash' do
|
147
|
+
pg_json_hash = type_converter.to_db pg, :table_name, :json, json_hash
|
148
|
+
|
149
|
+
pg_json_hash.class.must_equal Sequel::Postgres::JSONHash
|
150
|
+
pg_json_hash.must_equal json_hash
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'must transform Array to JSONArray' do
|
154
|
+
pg_json_array = type_converter.to_db pg, :table_name, :json, json_array
|
155
|
+
|
156
|
+
pg_json_array.class.must_equal Sequel::Postgres::JSONArray
|
157
|
+
pg_json_array.must_equal json_array
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
describe 'From DB' do
|
163
|
+
|
164
|
+
it 'must transform JSONHash to Hash' do
|
165
|
+
pg_json_hash = Sequel::Postgres::JSONHash.new json_hash
|
166
|
+
hash = type_converter.from_db pg_json_hash
|
167
|
+
|
168
|
+
hash.class.must_equal Hash
|
169
|
+
hash.must_equal pg_json_hash
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'must transform JSONArray to Array' do
|
173
|
+
pg_json_array = Sequel::Postgres::JSONArray.new json_array
|
174
|
+
array = type_converter.from_db pg_json_array
|
175
|
+
|
176
|
+
array.class.must_equal Array
|
177
|
+
array.must_equal pg_json_array
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
describe 'JSONB' do
|
185
|
+
|
186
|
+
let(:json_hash) { {key_1: {key_2: [3]}} }
|
187
|
+
let(:json_array) { [{key_1: {key_2: [3]}}] }
|
188
|
+
|
189
|
+
describe 'To DB' do
|
190
|
+
|
191
|
+
it 'must transform Hash to JSONBHash' do
|
192
|
+
pg_jsonb_hash = type_converter.to_db pg, :table_name, :jsonb, json_hash
|
193
|
+
|
194
|
+
pg_jsonb_hash.class.must_equal Sequel::Postgres::JSONBHash
|
195
|
+
pg_jsonb_hash.must_equal json_hash
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'must transform Array to JSONBArray' do
|
199
|
+
pg_jsonb_array = type_converter.to_db pg, :table_name, :jsonb, json_array
|
200
|
+
|
201
|
+
pg_jsonb_array.class.must_equal Sequel::Postgres::JSONBArray
|
202
|
+
pg_jsonb_array.must_equal json_array
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
describe 'From DB' do
|
208
|
+
|
209
|
+
it 'must transform JSONBHash to Hash' do
|
210
|
+
pg_jsonb_hash = Sequel::Postgres::JSONBHash.new json_hash
|
211
|
+
hash = type_converter.from_db pg_jsonb_hash
|
212
|
+
|
213
|
+
hash.class.must_equal Hash
|
214
|
+
hash.must_equal pg_jsonb_hash
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'must transform JSONBArray to Array' do
|
218
|
+
pg_jsonb_array = Sequel::Postgres::JSONBArray.new json_array
|
219
|
+
array = type_converter.from_db pg_jsonb_array
|
220
|
+
|
221
|
+
array.class.must_equal Array
|
222
|
+
array.must_equal pg_jsonb_array
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Rasti::DB::TypeConverters::TimeInZone do
|
4
|
+
|
5
|
+
let(:type_converter) { Rasti::DB::TypeConverters::TimeInZone }
|
6
|
+
|
7
|
+
describe 'To DB' do
|
8
|
+
|
9
|
+
it 'must not transform Time to TimeInZone' do
|
10
|
+
time = Time.now
|
11
|
+
|
12
|
+
converted_time = type_converter.to_db db, 'table', :time, time
|
13
|
+
|
14
|
+
converted_time.class.must_equal Time
|
15
|
+
converted_time.must_equal time
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'From DB' do
|
21
|
+
|
22
|
+
it 'must transform Time to TimeInZone' do
|
23
|
+
time = Time.now
|
24
|
+
|
25
|
+
converted_time = type_converter.from_db time
|
26
|
+
|
27
|
+
converted_time.class.must_equal Timing::TimeInZone
|
28
|
+
converted_time.must_equal time
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rasti-db
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Naiman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -64,6 +64,26 @@ dependencies:
|
|
64
64
|
- - ">="
|
65
65
|
- !ruby/object:Gem::Version
|
66
66
|
version: 0.1.3
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: class_config
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0.0'
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.0.2
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.0'
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 0.0.2
|
67
87
|
- !ruby/object:Gem::Dependency
|
68
88
|
name: bundler
|
69
89
|
requirement: !ruby/object:Gem::Requirement
|
@@ -218,7 +238,12 @@ files:
|
|
218
238
|
- lib/rasti/db/relations/many_to_one.rb
|
219
239
|
- lib/rasti/db/relations/one_to_many.rb
|
220
240
|
- lib/rasti/db/relations/one_to_one.rb
|
221
|
-
- lib/rasti/db/
|
241
|
+
- lib/rasti/db/type_converters/postgres.rb
|
242
|
+
- lib/rasti/db/type_converters/postgres_types/array.rb
|
243
|
+
- lib/rasti/db/type_converters/postgres_types/hstore.rb
|
244
|
+
- lib/rasti/db/type_converters/postgres_types/json.rb
|
245
|
+
- lib/rasti/db/type_converters/postgres_types/jsonb.rb
|
246
|
+
- lib/rasti/db/type_converters/time_in_zone.rb
|
222
247
|
- lib/rasti/db/version.rb
|
223
248
|
- rasti-db.gemspec
|
224
249
|
- spec/collection_spec.rb
|
@@ -227,7 +252,8 @@ files:
|
|
227
252
|
- spec/model_spec.rb
|
228
253
|
- spec/query_spec.rb
|
229
254
|
- spec/relations_spec.rb
|
230
|
-
- spec/
|
255
|
+
- spec/type_converters/postgres_spec.rb
|
256
|
+
- spec/type_converters/time_in_zone_spec.rb
|
231
257
|
homepage: https://github.com/gabynaiman/rasti-db
|
232
258
|
licenses:
|
233
259
|
- MIT
|
@@ -259,4 +285,5 @@ test_files:
|
|
259
285
|
- spec/model_spec.rb
|
260
286
|
- spec/query_spec.rb
|
261
287
|
- spec/relations_spec.rb
|
262
|
-
- spec/
|
288
|
+
- spec/type_converters/postgres_spec.rb
|
289
|
+
- spec/type_converters/time_in_zone_spec.rb
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module Rasti
|
2
|
-
module DB
|
3
|
-
class TypeConverter
|
4
|
-
|
5
|
-
CONVERTIONS = {
|
6
|
-
postgres: {
|
7
|
-
/^json$/ => ->(value, match) { Sequel.pg_json value },
|
8
|
-
/^jsonb$/ => ->(value, match) { Sequel.pg_jsonb value },
|
9
|
-
/^hstore$/ => ->(value, match) { Sequel.hstore value },
|
10
|
-
/^hstore\[\]$/ => ->(value, match) { Sequel.pg_array value.map { |v| Sequel.hstore v }, 'hstore' },
|
11
|
-
/^([a-z]+)\[\]$/ => ->(value, match) { Sequel.pg_array value, match.captures[0] }
|
12
|
-
}
|
13
|
-
}
|
14
|
-
|
15
|
-
def initialize(db, collection_name)
|
16
|
-
@db = db
|
17
|
-
@collection_name = collection_name
|
18
|
-
end
|
19
|
-
|
20
|
-
def apply_to(attributes)
|
21
|
-
convertions = self.class.convertions_for @db, @collection_name
|
22
|
-
|
23
|
-
return attributes if convertions.empty?
|
24
|
-
|
25
|
-
(attributes.is_a?(Array) ? attributes : [attributes]).each do |attrs|
|
26
|
-
convertions.each do |name, convertion|
|
27
|
-
attrs[name] = convertion[:block].call attrs[name], convertion[:match] if attrs.key? name
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
attributes
|
32
|
-
end
|
33
|
-
|
34
|
-
@cache ||= {}
|
35
|
-
|
36
|
-
def self.convertions_for(db, collection_name)
|
37
|
-
key = [db.database_type, collection_name]
|
38
|
-
if !@cache.key?(key)
|
39
|
-
columns = Hash[db.schema(collection_name)]
|
40
|
-
@cache[key] = columns.each_with_object({}) do |(name, schema), hash|
|
41
|
-
CONVERTIONS.fetch(db.database_type, {}).each do |type, convertion|
|
42
|
-
if !hash.key?(name) && match = type.match(schema[:db_type])
|
43
|
-
hash[name] = {match: match, block: convertion}
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
@cache[key]
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
data/spec/type_converter_spec.rb
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
require 'minitest_helper'
|
2
|
-
|
3
|
-
describe 'Type Converter' do
|
4
|
-
|
5
|
-
it 'Apply convertion' do
|
6
|
-
type_converter = Rasti::DB::TypeConverter.new db, :users
|
7
|
-
type_converter.apply_to(id: '123', name: 'User 1').must_equal id: 123, name: 'User 1'
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'Postgres' do
|
11
|
-
|
12
|
-
let(:pg) do
|
13
|
-
Object.new.tap do |pg|
|
14
|
-
def pg.database_type
|
15
|
-
:postgres
|
16
|
-
end
|
17
|
-
|
18
|
-
def pg.schema(table_name, opts={})
|
19
|
-
[
|
20
|
-
[:hash, {db_type: 'hstore'}],
|
21
|
-
[:text_array, {db_type: 'text[]'}],
|
22
|
-
[:integer_array, {db_type: 'integer[]'}],
|
23
|
-
[:hstore_array, {db_type: 'hstore[]'}],
|
24
|
-
[:json, {db_type: 'json'}],
|
25
|
-
[:jsonb, {db_type: 'jsonb'}]
|
26
|
-
]
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
let(:type_converter) { Rasti::DB::TypeConverter.new pg, :table_name }
|
32
|
-
|
33
|
-
it 'HStore' do
|
34
|
-
attributes = type_converter.apply_to hash: {key_1: 1, key_2: 2}
|
35
|
-
|
36
|
-
attributes[:hash].class.must_equal Sequel::Postgres::HStore
|
37
|
-
attributes[:hash].must_equal 'key_1' => '1', 'key_2' => '2'
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'Empty hstore' do
|
41
|
-
attributes = type_converter.apply_to hash: {}
|
42
|
-
|
43
|
-
attributes[:hash].class.must_equal Sequel::Postgres::HStore
|
44
|
-
attributes[:hash].must_equal Hash.new
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'Text array' do
|
48
|
-
attributes = type_converter.apply_to text_array: %w(a b c)
|
49
|
-
|
50
|
-
attributes[:text_array].class.must_equal Sequel::Postgres::PGArray
|
51
|
-
attributes[:text_array].array_type.must_equal 'text'
|
52
|
-
attributes[:text_array].must_equal %w(a b c)
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'Integer array' do
|
56
|
-
attributes = type_converter.apply_to integer_array: [1,2,3]
|
57
|
-
|
58
|
-
attributes[:integer_array].class.must_equal Sequel::Postgres::PGArray
|
59
|
-
attributes[:integer_array].array_type.must_equal 'integer'
|
60
|
-
attributes[:integer_array].must_equal [1,2,3]
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'Hstore array' do
|
64
|
-
attributes = type_converter.apply_to hstore_array: [{key: 0}, {key: 1}]
|
65
|
-
|
66
|
-
attributes[:hstore_array].class.must_equal Sequel::Postgres::PGArray
|
67
|
-
attributes[:hstore_array].array_type.must_equal 'hstore'
|
68
|
-
|
69
|
-
2.times do |i|
|
70
|
-
attributes[:hstore_array][i].class.must_equal Sequel::Postgres::HStore
|
71
|
-
attributes[:hstore_array][i].must_equal 'key' => i.to_s
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'Empty array' do
|
76
|
-
attributes = type_converter.apply_to integer_array: []
|
77
|
-
|
78
|
-
attributes[:integer_array].class.must_equal Sequel::Postgres::PGArray
|
79
|
-
attributes[:integer_array].array_type.must_equal 'integer'
|
80
|
-
attributes[:integer_array].must_equal []
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'Json' do
|
84
|
-
attributes = type_converter.apply_to json: {key_1: {key_2: [3]}}
|
85
|
-
|
86
|
-
attributes[:json].class.must_equal Sequel::Postgres::JSONHash
|
87
|
-
attributes[:json].must_equal key_1: {key_2: [3]}
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'Json array' do
|
91
|
-
attributes = type_converter.apply_to json: [{key_1: {key_2: [3]}}]
|
92
|
-
|
93
|
-
attributes[:json].class.must_equal Sequel::Postgres::JSONArray
|
94
|
-
attributes[:json].must_equal [{key_1: {key_2: [3]}}]
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'Json binary' do
|
98
|
-
attributes = type_converter.apply_to jsonb: {key_1: {key_2: [3]}}
|
99
|
-
|
100
|
-
attributes[:jsonb].class.must_equal Sequel::Postgres::JSONBHash
|
101
|
-
attributes[:jsonb].must_equal key_1: {key_2: [3]}
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|