rom-sql 1.0.0.beta2 → 1.0.0.beta3
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 +1 -1
- data/CHANGELOG.md +4 -0
- data/circle.yml +1 -1
- data/lib/rom/plugins/relation/sql/auto_combine.rb +1 -1
- data/lib/rom/sql/association.rb +5 -1
- data/lib/rom/sql/association/many_to_many.rb +8 -4
- data/lib/rom/sql/association/many_to_one.rb +2 -3
- data/lib/rom/sql/association/one_to_many.rb +2 -3
- data/lib/rom/sql/commands/create.rb +8 -1
- data/lib/rom/sql/commands/update.rb +7 -0
- data/lib/rom/sql/extensions.rb +8 -0
- data/lib/rom/sql/extensions/active_support_notifications.rb +7 -18
- data/lib/rom/sql/extensions/mysql.rb +1 -0
- data/lib/rom/sql/extensions/mysql/inferrer.rb +10 -0
- data/lib/rom/sql/extensions/postgres/commands.rb +1 -1
- data/lib/rom/sql/extensions/postgres/inferrer.rb +4 -0
- data/lib/rom/sql/extensions/postgres/types.rb +20 -22
- data/lib/rom/sql/extensions/sqlite.rb +1 -0
- data/lib/rom/sql/extensions/sqlite/inferrer.rb +10 -0
- data/lib/rom/sql/function.rb +6 -2
- data/lib/rom/sql/order_dsl.rb +1 -1
- data/lib/rom/sql/plugin/associates.rb +28 -5
- data/lib/rom/sql/relation.rb +18 -0
- data/lib/rom/sql/relation/reading.rb +2 -2
- data/lib/rom/sql/relation/sequel_api.rb +119 -0
- data/lib/rom/sql/schema/inferrer.rb +26 -2
- data/lib/rom/sql/tasks/migration_tasks.rake +1 -1
- data/lib/rom/sql/type.rb +13 -2
- data/lib/rom/sql/types.rb +5 -3
- data/lib/rom/sql/version.rb +1 -1
- data/spec/extensions/postgres/types_spec.rb +29 -0
- data/spec/integration/association/many_to_many_spec.rb +8 -0
- data/spec/integration/association/many_to_one_spec.rb +11 -0
- data/spec/integration/association/one_to_many_spec.rb +9 -0
- data/spec/integration/schema/inferrer/mysql_spec.rb +36 -0
- data/spec/integration/schema/inferrer/postgres_spec.rb +118 -0
- data/spec/integration/schema/inferrer/sqlite_spec.rb +36 -0
- data/spec/integration/{schema_inference_spec.rb → schema/inferrer_spec.rb} +44 -15
- data/spec/integration/sequel_api_spec.rb +31 -0
- data/spec/support/helpers.rb +4 -0
- data/spec/unit/function_spec.rb +35 -0
- data/spec/unit/order_dsl_spec.rb +35 -0
- data/spec/unit/relation/assoc_spec.rb +38 -0
- data/spec/unit/relation/inner_join_spec.rb +15 -0
- data/spec/unit/types_spec.rb +53 -1
- metadata +23 -6
- data/spec/extensions/postgres/inferrer_spec.rb +0 -59
data/lib/rom/sql/relation.rb
CHANGED
@@ -3,6 +3,7 @@ require 'rom/sql/schema'
|
|
3
3
|
|
4
4
|
require 'rom/sql/relation/reading'
|
5
5
|
require 'rom/sql/relation/writing'
|
6
|
+
require 'rom/sql/relation/sequel_api'
|
6
7
|
|
7
8
|
require 'rom/plugins/relation/key_inference'
|
8
9
|
require 'rom/plugins/relation/sql/auto_combine'
|
@@ -88,6 +89,23 @@ module ROM
|
|
88
89
|
|
89
90
|
option :primary_key, reader: true, default: -> rel { rel.schema.primary_key_name }
|
90
91
|
|
92
|
+
# Return relation that will load associated tuples of this relation
|
93
|
+
#
|
94
|
+
# This method is useful for defining custom relation views for relation
|
95
|
+
# composition when you want to enhance default association query
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# assoc(:tasks).where(tasks[:title] => "Task One")
|
99
|
+
#
|
100
|
+
# @param [Symbol] name The association name
|
101
|
+
#
|
102
|
+
# @return [Relation]
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
def assoc(name)
|
106
|
+
associations[name].(__registry__)
|
107
|
+
end
|
108
|
+
|
91
109
|
# Return raw column names
|
92
110
|
#
|
93
111
|
# @return [Array<Symbol>]
|
@@ -558,10 +558,10 @@ module ROM
|
|
558
558
|
private
|
559
559
|
|
560
560
|
# @api private
|
561
|
-
def __join__(type, other, opts = EMPTY_HASH, &block)
|
561
|
+
def __join__(type, other, join_cond = EMPTY_HASH, opts = EMPTY_HASH, &block)
|
562
562
|
case other
|
563
563
|
when Symbol, Association::Name
|
564
|
-
new(dataset.__send__(type, other.to_sym, opts, &block))
|
564
|
+
new(dataset.__send__(type, other.to_sym, join_cond, opts, &block))
|
565
565
|
when Relation
|
566
566
|
__send__(type, other.name.dataset, join_keys(other))
|
567
567
|
else
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module ROM
|
2
|
+
module SQL
|
3
|
+
# Query API for SQL::Relation
|
4
|
+
#
|
5
|
+
# @api public
|
6
|
+
module SequelAPI
|
7
|
+
# Select specific columns for select clause
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# users.select(:id, :name).first
|
11
|
+
# # {:id => 1, :name => "Jane" }
|
12
|
+
#
|
13
|
+
# @return [Relation]
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
def select(*args, &block)
|
17
|
+
new(dataset.__send__(__method__, *args, &block))
|
18
|
+
end
|
19
|
+
|
20
|
+
# Append specific columns to select clause
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# users.select(:id, :name).select_append(:email)
|
24
|
+
# # {:id => 1, :name => "Jane", :email => "jane@doe.org"}
|
25
|
+
#
|
26
|
+
# @param [Array<Symbol>] *args A list with column names
|
27
|
+
#
|
28
|
+
# @return [Relation]
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def select_append(*args, &block)
|
32
|
+
new(dataset.__send__(__method__, *args, &block))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Restrict a relation to match criteria
|
36
|
+
#
|
37
|
+
# If block is passed it'll be executed in the context of a condition
|
38
|
+
# builder object.
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# users.where(name: 'Jane')
|
42
|
+
#
|
43
|
+
# users.where { age >= 18 }
|
44
|
+
#
|
45
|
+
# @param [Hash] *args An optional hash with conditions for WHERE clause
|
46
|
+
#
|
47
|
+
# @return [Relation]
|
48
|
+
#
|
49
|
+
# @see http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
def where(*args, &block)
|
53
|
+
new(dataset.__send__(__method__, *args, &block))
|
54
|
+
end
|
55
|
+
|
56
|
+
# Restrict a relation to match grouping criteria
|
57
|
+
#
|
58
|
+
# @example
|
59
|
+
# users.with_task_count.having( task_count: 2 )
|
60
|
+
#
|
61
|
+
# users.with_task_count.having { task_count > 3 }
|
62
|
+
#
|
63
|
+
# @param [Hash] *args An optional hash with conditions for HAVING clause
|
64
|
+
#
|
65
|
+
# @return [Relation]
|
66
|
+
#
|
67
|
+
# @see http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
def having(*args, &block)
|
71
|
+
new(dataset.__send__(__method__, *args, &block))
|
72
|
+
end
|
73
|
+
|
74
|
+
# Set order for the relation
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# users.order(:name)
|
78
|
+
#
|
79
|
+
# @param [Array<Symbol>] *args A list with column names
|
80
|
+
#
|
81
|
+
# @return [Relation]
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
def order(*args, &block)
|
85
|
+
new(dataset.__send__(__method__, *args, &block))
|
86
|
+
end
|
87
|
+
|
88
|
+
# Join with another relation using INNER JOIN
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# users.inner_join(:tasks, id: :user_id)
|
92
|
+
#
|
93
|
+
# @param [Symbol] relation name
|
94
|
+
# @param [Hash] join keys
|
95
|
+
#
|
96
|
+
# @return [Relation]
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
def inner_join(*args, &block)
|
100
|
+
new(dataset.__send__(__method__, *args, &block))
|
101
|
+
end
|
102
|
+
|
103
|
+
# Join other relation using LEFT OUTER JOIN
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# users.left_join(:tasks, id: :user_id)
|
107
|
+
#
|
108
|
+
# @param [Symbol] relation name
|
109
|
+
# @param [Hash] join keys
|
110
|
+
#
|
111
|
+
# @return [Relation]
|
112
|
+
#
|
113
|
+
# @api public
|
114
|
+
def left_join(*args, &block)
|
115
|
+
new(dataset.__send__(__method__, *args, &block))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -26,6 +26,7 @@ module ROM
|
|
26
26
|
db_registry Hash.new(self)
|
27
27
|
|
28
28
|
CONSTRAINT_DB_TYPE = 'add_constraint'.freeze
|
29
|
+
DECIMAL_REGEX = /(?:decimal|numeric)\((\d+)(?:,\s*(\d+))?\)/.freeze
|
29
30
|
|
30
31
|
def self.inherited(klass)
|
31
32
|
super
|
@@ -72,9 +73,16 @@ module ROM
|
|
72
73
|
mapped_type = map_type(type, db_type, rest)
|
73
74
|
|
74
75
|
if mapped_type
|
76
|
+
read_type = mapped_type.meta[:read]
|
75
77
|
mapped_type = mapped_type.optional if allow_null
|
76
78
|
mapped_type = mapped_type.meta(foreign_key: true, target: foreign_key) if foreign_key
|
77
|
-
|
79
|
+
if read_type && allow_null
|
80
|
+
mapped_type.meta(read: read_type.optional)
|
81
|
+
elsif read_type
|
82
|
+
mapped_type.meta(read: read_type)
|
83
|
+
else
|
84
|
+
mapped_type
|
85
|
+
end
|
78
86
|
end
|
79
87
|
end
|
80
88
|
end
|
@@ -87,7 +95,7 @@ module ROM
|
|
87
95
|
type = self.class.ruby_type_mapping[ruby_type]
|
88
96
|
|
89
97
|
if db_type.is_a?(String) && db_type.include?('numeric') || db_type.include?('decimal')
|
90
|
-
|
98
|
+
map_decimal_type(db_type)
|
91
99
|
else
|
92
100
|
type
|
93
101
|
end
|
@@ -110,6 +118,22 @@ module ROM
|
|
110
118
|
columns[0]
|
111
119
|
end
|
112
120
|
end
|
121
|
+
|
122
|
+
# @api private
|
123
|
+
def map_decimal_type(type)
|
124
|
+
precision = DECIMAL_REGEX.match(type)
|
125
|
+
|
126
|
+
if precision
|
127
|
+
prcsn, scale = precision[1..2].map(&:to_i)
|
128
|
+
|
129
|
+
self.class.ruby_type_mapping[:decimal].meta(
|
130
|
+
precision: prcsn,
|
131
|
+
scale: scale
|
132
|
+
)
|
133
|
+
else
|
134
|
+
self.class.ruby_type_mapping[:decimal]
|
135
|
+
end
|
136
|
+
end
|
113
137
|
end
|
114
138
|
end
|
115
139
|
end
|
@@ -24,11 +24,11 @@ module ROM
|
|
24
24
|
end
|
25
25
|
|
26
26
|
namespace :db do
|
27
|
-
desc "Perform migration reset (full erase and migration up)"
|
28
27
|
task :rom_configuration do
|
29
28
|
Rake::Task["db:setup"].invoke
|
30
29
|
end
|
31
30
|
|
31
|
+
desc "Perform migration reset (full erase and migration up)"
|
32
32
|
task reset: :rom_configuration do
|
33
33
|
ROM::SQL::RakeSupport.run_migrations(target: 0)
|
34
34
|
ROM::SQL::RakeSupport.run_migrations
|
data/lib/rom/sql/type.rb
CHANGED
@@ -3,6 +3,8 @@ require 'rom/schema/type'
|
|
3
3
|
module ROM
|
4
4
|
module SQL
|
5
5
|
class Type < ROM::Schema::Type
|
6
|
+
QualifyError = Class.new(StandardError)
|
7
|
+
|
6
8
|
# Return a new type marked as a FK
|
7
9
|
#
|
8
10
|
# @return [SQL::Type]
|
@@ -13,9 +15,10 @@ module ROM
|
|
13
15
|
end
|
14
16
|
|
15
17
|
# @api public
|
16
|
-
def
|
18
|
+
def aliased(name)
|
17
19
|
super.meta(sql_expr: sql_expr.as(name))
|
18
20
|
end
|
21
|
+
alias_method :as, :aliased
|
19
22
|
|
20
23
|
# Return a new type marked as qualified
|
21
24
|
#
|
@@ -23,7 +26,15 @@ module ROM
|
|
23
26
|
#
|
24
27
|
# @api public
|
25
28
|
def qualified
|
26
|
-
|
29
|
+
return self if qualified?
|
30
|
+
|
31
|
+
case sql_expr
|
32
|
+
when Sequel::SQL::AliasedExpression, Sequel::SQL::Identifier
|
33
|
+
type = meta(qualified: true)
|
34
|
+
type.meta(qualified: true, sql_expr: Sequel[type.to_sym])
|
35
|
+
else
|
36
|
+
raise QualifyError, "can't qualify #{name.inspect} (#{sql_expr.inspect})"
|
37
|
+
end
|
27
38
|
end
|
28
39
|
|
29
40
|
# Return a new type marked as joined
|
data/lib/rom/sql/types.rb
CHANGED
@@ -5,11 +5,13 @@ module ROM
|
|
5
5
|
module Types
|
6
6
|
include ROM::Types
|
7
7
|
|
8
|
+
def self.Constructor(*args, &block)
|
9
|
+
ROM::Types.Constructor(*args, &block)
|
10
|
+
end
|
11
|
+
|
8
12
|
Serial = Int.constrained(gt: 0).meta(primary_key: true)
|
9
13
|
|
10
|
-
Blob =
|
11
|
-
.new(Sequel::SQL::Blob)
|
12
|
-
.constructor(Sequel::SQL::Blob.method(:new))
|
14
|
+
Blob = Constructor(Sequel::SQL::Blob, &Sequel::SQL::Blob.method(:new))
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
data/lib/rom/sql/version.rb
CHANGED
@@ -112,4 +112,33 @@ RSpec.describe 'ROM::SQL::Types' do
|
|
112
112
|
expect(output).to be_instance_of(BigDecimal)
|
113
113
|
end
|
114
114
|
end
|
115
|
+
|
116
|
+
describe ROM::SQL::Types::PG::IPAddress do
|
117
|
+
it 'converts IPAddr to a string' do
|
118
|
+
expect(described_class[IPAddr.new('127.0.0.1')]).to eql('127.0.0.1')
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'coerces to builtin IPAddr type on read' do
|
122
|
+
expect(described_class.meta[:read]['127.0.0.1']).to eql(IPAddr.new('127.0.0.1'))
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'supports networks' do
|
126
|
+
class_a = described_class.meta[:read]['10.0.0.0/8']
|
127
|
+
|
128
|
+
expect(class_a).to eql(IPAddr.new('10.0.0.0/8'))
|
129
|
+
expect(class_a).to include(IPAddr.new('10.8.8.8'))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe ROM::SQL::Types::PG::PointT do
|
134
|
+
let(:point) { ROM::SQL::Types::PG::Point.new(7.5, 30.5) }
|
135
|
+
|
136
|
+
it 'serializes a point down to a string' do
|
137
|
+
expect(described_class[point]).to eql('(7.5,30.5)')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'reads serialized format' do
|
141
|
+
expect(described_class.meta[:read]['(7.5,30.5)']).to eql(point)
|
142
|
+
end
|
143
|
+
end
|
115
144
|
end
|
@@ -133,6 +133,14 @@ RSpec.describe ROM::SQL::Association::ManyToMany do
|
|
133
133
|
|
134
134
|
expect(relation.to_a).to eql([id: 1, name: 'important', task_id: 1])
|
135
135
|
end
|
136
|
+
|
137
|
+
it 'maintains original relation' do
|
138
|
+
relation = tags.
|
139
|
+
select_append(tags[:name].as(:tag)).
|
140
|
+
for_combine(assoc).call(tasks.call)
|
141
|
+
|
142
|
+
expect(relation.to_a).to eql([id: 1, tag: 'important', name: 'important', task_id: 1])
|
143
|
+
end
|
136
144
|
end
|
137
145
|
end
|
138
146
|
end
|
@@ -66,6 +66,17 @@ RSpec.describe ROM::SQL::Association::ManyToOne, helpers: true do
|
|
66
66
|
{ id: 1, task_id: 2, name: 'Jane' }
|
67
67
|
])
|
68
68
|
end
|
69
|
+
|
70
|
+
it 'maintains original relation' do
|
71
|
+
users.accounts.insert(user_id: 2, number: 'a1', balance: 0)
|
72
|
+
|
73
|
+
relation = users.
|
74
|
+
join(:accounts, user_id: :id).
|
75
|
+
select_append(users.accounts[:number].as(:account_num)).
|
76
|
+
for_combine(assoc).call(tasks.call)
|
77
|
+
|
78
|
+
expect(relation.to_a).to eql([{ id: 2, task_id: 1, name: 'Joe', account_num: 'a1' }])
|
79
|
+
end
|
69
80
|
end
|
70
81
|
|
71
82
|
context 'arbitrary name conventions' do
|
@@ -53,6 +53,15 @@ RSpec.describe ROM::SQL::Association::OneToMany do
|
|
53
53
|
{ id: 2, user_id: 1, title: "Jane's task" }
|
54
54
|
])
|
55
55
|
end
|
56
|
+
|
57
|
+
it 'maintains original relation' do
|
58
|
+
relation = tasks.
|
59
|
+
join(:task_tags, tag_id: :id).
|
60
|
+
select_append(tasks.task_tags[:tag_id].qualified).
|
61
|
+
for_combine(assoc).call(users.call)
|
62
|
+
|
63
|
+
expect(relation.to_a).to eql([{ id: 1, user_id: 2, title: "Joe's task", tag_id: 1 }])
|
64
|
+
end
|
56
65
|
end
|
57
66
|
end
|
58
67
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
RSpec.describe 'ROM::SQL::Schema::MysqlInferrer', :mysql do
|
2
|
+
include_context 'database setup'
|
3
|
+
|
4
|
+
before do
|
5
|
+
conn.drop_table?(:test_inferrence)
|
6
|
+
|
7
|
+
conn.create_table :test_inferrence do
|
8
|
+
tinyint :tiny
|
9
|
+
mediumint :medium
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
conn.drop_table?(:test_inferrence)
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:dataset) { :test_inferrence }
|
18
|
+
|
19
|
+
let(:schema) { container.relations[dataset].schema }
|
20
|
+
|
21
|
+
before do
|
22
|
+
dataset = self.dataset
|
23
|
+
conf.relation(dataset) do
|
24
|
+
schema(dataset, infer: true)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'can infer attributes for dataset' do
|
29
|
+
source = container.relations[:test_inferrence].name
|
30
|
+
|
31
|
+
expect(schema.to_h).to eql(
|
32
|
+
tiny: ROM::SQL::Types::Int.optional.meta(name: :tiny, source: source),
|
33
|
+
medium: ROM::SQL::Types::Int.optional.meta(name: :medium, source: source),
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
|
2
|
+
RSpec.describe 'ROM::SQL::Schema::PostgresInferrer', :postgres do
|
3
|
+
include_context 'database setup'
|
4
|
+
|
5
|
+
colors = %w(red orange yellow green blue purple)
|
6
|
+
|
7
|
+
before do
|
8
|
+
conn.extension :pg_enum
|
9
|
+
|
10
|
+
conn.drop_table?(:test_inferrence)
|
11
|
+
conn.drop_enum(:rainbow, if_exists: true)
|
12
|
+
|
13
|
+
conn.create_enum(:rainbow, colors)
|
14
|
+
|
15
|
+
conn.create_table :test_inferrence do
|
16
|
+
primary_key :id, :uuid
|
17
|
+
Json :json_data
|
18
|
+
Jsonb :jsonb_data
|
19
|
+
Decimal :money, null: false
|
20
|
+
column :tags, "text[]"
|
21
|
+
column :tag_ids, "bigint[]"
|
22
|
+
column :ip, "inet"
|
23
|
+
column :subnet, "cidr"
|
24
|
+
column :hw_address, "macaddr"
|
25
|
+
rainbow :color
|
26
|
+
point :center
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
after do
|
31
|
+
conn.drop_table?(:test_inferrence)
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:dataset) { :test_inferrence }
|
35
|
+
|
36
|
+
let(:schema) { container.relations[dataset].schema }
|
37
|
+
|
38
|
+
context 'inferring db-specific attributes' do
|
39
|
+
before do
|
40
|
+
dataset = self.dataset
|
41
|
+
conf.relation(dataset) do
|
42
|
+
schema(dataset, infer: true)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'can infer attributes for dataset' do
|
47
|
+
source = container.relations[:test_inferrence].name
|
48
|
+
|
49
|
+
expect(schema.to_h).to eql(
|
50
|
+
id: ROM::SQL::Types::PG::UUID.meta(name: :id, source: source, primary_key: true),
|
51
|
+
json_data: ROM::SQL::Types::PG::JSON.optional.meta(name: :json_data, source: source),
|
52
|
+
jsonb_data: ROM::SQL::Types::PG::JSONB.optional.meta(name: :jsonb_data, source: source),
|
53
|
+
money: ROM::SQL::Types::Decimal.meta(name: :money, source: source),
|
54
|
+
tags: ROM::SQL::Types::PG::Array('text').optional.meta(name: :tags, source: source),
|
55
|
+
tag_ids: ROM::SQL::Types::PG::Array('biging').optional.meta(name: :tag_ids, source: source),
|
56
|
+
color: ROM::SQL::Types::String.enum(*colors).optional.meta(name: :color, source: source),
|
57
|
+
ip: ROM::SQL::Types::PG::IPAddress.optional.meta(
|
58
|
+
name: :ip,
|
59
|
+
source: source,
|
60
|
+
read: ROM::SQL::Types::PG::IPAddressR.optional
|
61
|
+
),
|
62
|
+
subnet: ROM::SQL::Types::PG::IPAddress.optional.meta(
|
63
|
+
name: :subnet,
|
64
|
+
source: source,
|
65
|
+
read: ROM::SQL::Types::PG::IPAddressR.optional
|
66
|
+
),
|
67
|
+
hw_address: ROM::SQL::Types::String.optional.meta(name: :hw_address, source: source),
|
68
|
+
center: ROM::SQL::Types::PG::PointT.optional.meta(
|
69
|
+
name: :center,
|
70
|
+
source: source,
|
71
|
+
read: ROM::SQL::Types::PG::PointTR.optional
|
72
|
+
)
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with a table without columns' do
|
78
|
+
before do
|
79
|
+
conn.create_table(:dummy) unless conn.table_exists?(:dummy)
|
80
|
+
conf.relation(:dummy) { schema(infer: true) }
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'does not fail with a weird error when a relation does not have attributes' do
|
84
|
+
expect(container.relations[:dummy].schema).to be_empty
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'with a column with bi-directional mapping' do
|
89
|
+
before do
|
90
|
+
conn.drop_table?(:test_bidirectional)
|
91
|
+
conn.create_table(:test_bidirectional) do
|
92
|
+
primary_key :id
|
93
|
+
inet :ip
|
94
|
+
point :center
|
95
|
+
end
|
96
|
+
|
97
|
+
conf.relation(:test_bidirectional) { schema(infer: true) }
|
98
|
+
|
99
|
+
conf.commands(:test_bidirectional) do
|
100
|
+
define(:create) do
|
101
|
+
result :one
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
let(:point) { ROM::SQL::Types::PG::Point.new(7.5, 30.5) }
|
107
|
+
let(:dns) { IPAddr.new('8.8.8.8') }
|
108
|
+
|
109
|
+
let(:relation) { container.relations[:test_bidirectional] }
|
110
|
+
let(:create) { commands[:test_bidirectional].create }
|
111
|
+
|
112
|
+
it 'writes and reads data' do
|
113
|
+
inserted = create.call(id: 1, center: point, ip: dns)
|
114
|
+
expect(inserted).to eql(id: 1, center: point, ip: dns)
|
115
|
+
expect(relation.to_a).to eql([inserted])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|