rasti-db 0.4.1 → 1.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 +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
|