rom-sql 0.8.0 → 0.9.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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +16 -12
  3. data/CHANGELOG.md +23 -0
  4. data/Gemfile +11 -3
  5. data/README.md +1 -7
  6. data/lib/rom/sql.rb +4 -7
  7. data/lib/rom/sql/association.rb +1 -1
  8. data/lib/rom/sql/association/one_to_many.rb +44 -1
  9. data/lib/rom/sql/association/one_to_one.rb +1 -38
  10. data/lib/rom/sql/commands.rb +0 -3
  11. data/lib/rom/sql/commands/error_wrapper.rb +1 -1
  12. data/lib/rom/sql/errors.rb +4 -1
  13. data/lib/rom/sql/extensions.rb +19 -0
  14. data/lib/rom/sql/{support → extensions}/active_support_notifications.rb +0 -0
  15. data/lib/rom/sql/extensions/postgres.rb +3 -0
  16. data/lib/rom/sql/{commands/postgres.rb → extensions/postgres/commands.rb} +38 -0
  17. data/lib/rom/sql/extensions/postgres/inferrer.rb +64 -0
  18. data/lib/rom/sql/extensions/postgres/types.rb +65 -0
  19. data/lib/rom/sql/{support → extensions}/rails_log_subscriber.rb +0 -0
  20. data/lib/rom/sql/gateway.rb +15 -4
  21. data/lib/rom/sql/relation.rb +6 -2
  22. data/lib/rom/sql/relation/reading.rb +18 -0
  23. data/lib/rom/sql/schema/dsl.rb +7 -4
  24. data/lib/rom/sql/schema/inferrer.rb +44 -31
  25. data/lib/rom/sql/types.rb +5 -1
  26. data/lib/rom/sql/version.rb +1 -1
  27. data/rom-sql.gemspec +14 -13
  28. data/spec/extensions/postgres/inferrer_spec.rb +40 -0
  29. data/spec/extensions/postgres/integration_spec.rb +38 -0
  30. data/spec/extensions/postgres/types_spec.rb +115 -0
  31. data/spec/integration/association/many_to_many_spec.rb +2 -1
  32. data/spec/integration/association/one_to_one_spec.rb +6 -4
  33. data/spec/integration/combine_spec.rb +1 -1
  34. data/spec/integration/commands/create_spec.rb +46 -21
  35. data/spec/integration/commands/delete_spec.rb +13 -38
  36. data/spec/integration/commands/update_spec.rb +19 -41
  37. data/spec/integration/commands/upsert_spec.rb +1 -1
  38. data/spec/integration/gateway_spec.rb +5 -9
  39. data/spec/integration/migration_spec.rb +6 -7
  40. data/spec/integration/read_spec.rb +30 -38
  41. data/spec/integration/schema_inference_spec.rb +211 -49
  42. data/spec/integration/setup_spec.rb +5 -5
  43. data/spec/integration/support/active_support_notifications_spec.rb +4 -3
  44. data/spec/integration/support/rails_log_subscriber_spec.rb +5 -4
  45. data/spec/shared/database_setup.rb +21 -6
  46. data/spec/spec_helper.rb +44 -35
  47. data/spec/unit/association/one_to_many_spec.rb +20 -0
  48. data/spec/unit/association/one_to_one_spec.rb +23 -2
  49. data/spec/unit/association_errors_spec.rb +1 -1
  50. data/spec/unit/gateway_spec.rb +9 -8
  51. data/spec/unit/logger_spec.rb +1 -1
  52. data/spec/unit/migration_tasks_spec.rb +3 -3
  53. data/spec/unit/migrator_spec.rb +3 -2
  54. data/spec/unit/plugin/assoc_macros/combined_associations_spec.rb +1 -1
  55. data/spec/unit/plugin/assoc_macros/many_to_many_spec.rb +1 -1
  56. data/spec/unit/plugin/assoc_macros/many_to_one_spec.rb +1 -1
  57. data/spec/unit/plugin/assoc_macros/one_to_many_spec.rb +1 -1
  58. data/spec/unit/relation/associations_spec.rb +27 -0
  59. data/spec/unit/relation/avg_spec.rb +11 -0
  60. data/spec/unit/relation/by_pk_spec.rb +15 -0
  61. data/spec/unit/relation/dataset_spec.rb +48 -0
  62. data/spec/unit/relation/distinct_spec.rb +14 -0
  63. data/spec/unit/relation/exclude_spec.rb +13 -0
  64. data/spec/unit/relation/fetch_spec.rb +21 -0
  65. data/spec/unit/relation/having_spec.rb +20 -0
  66. data/spec/unit/relation/inner_join_spec.rb +22 -0
  67. data/spec/unit/relation/inspect_spec.rb +11 -0
  68. data/spec/unit/relation/invert_spec.rb +12 -0
  69. data/spec/unit/relation/left_join_spec.rb +16 -0
  70. data/spec/unit/relation/map_spec.rb +16 -0
  71. data/spec/unit/relation/max_spec.rb +11 -0
  72. data/spec/unit/relation/min_spec.rb +11 -0
  73. data/spec/unit/relation/pluck_spec.rb +11 -0
  74. data/spec/unit/relation/prefix_spec.rb +27 -0
  75. data/spec/unit/relation/primary_key_spec.rb +27 -0
  76. data/spec/unit/relation/project_spec.rb +22 -0
  77. data/spec/unit/relation/qualified_columns_spec.rb +27 -0
  78. data/spec/unit/relation/rename_spec.rb +21 -0
  79. data/spec/unit/relation/sum_spec.rb +11 -0
  80. data/spec/unit/relation/union_spec.rb +19 -0
  81. data/spec/unit/relation/unique_predicate_spec.rb +18 -0
  82. data/spec/unit/schema_spec.rb +1 -1
  83. data/spec/unit/types_spec.rb +4 -21
  84. metadata +79 -11
  85. data/lib/rom/sql/commands_ext/postgres.rb +0 -45
  86. data/lib/rom/sql/types/pg.rb +0 -26
  87. data/spec/unit/relation_spec.rb +0 -272
@@ -0,0 +1,65 @@
1
+ require 'dry-types'
2
+ require 'sequel'
3
+
4
+ Sequel.extension(*%i(pg_array pg_array_ops pg_json pg_json_ops))
5
+
6
+ module ROM
7
+ module SQL
8
+ module Types
9
+ module PG
10
+ # UUID
11
+
12
+ UUID = Types::String
13
+
14
+ # Array
15
+
16
+ Array = Dry::Types::Definition
17
+ .new(Sequel::Postgres::PGArray)
18
+
19
+ def self.Array(db_type)
20
+ Array.constructor(-> (v) { Sequel.pg_array(v, db_type) }).meta(type: db_type)
21
+ end
22
+
23
+ # JSON
24
+
25
+ JSONArray = Dry::Types::Definition
26
+ .new(Sequel::Postgres::JSONArray)
27
+ .constructor(Sequel.method(:pg_json))
28
+
29
+ JSONHash = Dry::Types::Definition
30
+ .new(Sequel::Postgres::JSONHash)
31
+ .constructor(Sequel.method(:pg_json))
32
+
33
+ JSONOp = Dry::Types::Definition
34
+ .new(Sequel::Postgres::JSONOp)
35
+ .constructor(Sequel.method(:pg_json))
36
+
37
+ JSON = JSONArray | JSONHash | JSONOp
38
+
39
+ # JSONB
40
+
41
+ JSONBArray = Dry::Types::Definition
42
+ .new(Sequel::Postgres::JSONBArray)
43
+ .constructor(Sequel.method(:pg_jsonb))
44
+
45
+ JSONBHash = Dry::Types::Definition
46
+ .new(Sequel::Postgres::JSONBHash)
47
+ .constructor(Sequel.method(:pg_jsonb))
48
+
49
+ JSONBOp = Dry::Types::Definition
50
+ .new(Sequel::Postgres::JSONBOp)
51
+ .constructor(Sequel.method(:pg_jsonb))
52
+
53
+ JSONB = JSONBArray | JSONBHash | JSONBOp
54
+
55
+ Bytea = Dry::Types::Definition
56
+ .new(Sequel::SQL::Blob)
57
+ .constructor(Sequel::SQL::Blob.method(:new))
58
+
59
+ # MONEY
60
+
61
+ Money = Types::Decimal
62
+ end
63
+ end
64
+ end
65
+ end
@@ -23,6 +23,10 @@ module ROM
23
23
  attr_accessor :instance
24
24
  end
25
25
 
26
+ CONNECTION_EXTENSIONS = {
27
+ postgres: %i(pg_array pg_json)
28
+ }.freeze
29
+
26
30
  # Return optionally configured logger
27
31
  #
28
32
  # @return [Object] logger
@@ -56,7 +60,7 @@ module ROM
56
60
  conn_options = options.reject { |k, _| repo_options.include?(k) }
57
61
 
58
62
  @connection = connect(uri, conn_options)
59
- add_extensions(Array(options[:extensions])) if options[:extensions]
63
+ load_extensions(Array(options[:extensions]))
60
64
 
61
65
  super(uri, options.reject { |k, _| conn_options.keys.include?(k) })
62
66
 
@@ -177,11 +181,18 @@ module ROM
177
181
  end
178
182
  end
179
183
 
180
- # Add extensions to the database connection
184
+ # Load database-specific extensions
181
185
  #
182
186
  # @api private
183
- def add_extensions(exts)
184
- connection.extension(*exts)
187
+ def load_extensions(exts)
188
+ db_type = connection.database_type.to_sym
189
+
190
+ if ROM::SQL.available_extension?(db_type)
191
+ ROM::SQL.load_extensions(db_type)
192
+ end
193
+
194
+ extensions = (CONNECTION_EXTENSIONS.fetch(db_type) { [] } + exts).uniq
195
+ connection.extension(*extensions)
185
196
  end
186
197
  end
187
198
  end
@@ -42,14 +42,18 @@ module ROM
42
42
 
43
43
  klass.class_eval do
44
44
  schema_dsl SQL::Schema::DSL
45
- schema_inferrer ROM::SQL::Schema::Inferrer
45
+ schema_inferrer -> (dataset, gateway) do
46
+ inferrer_for_db = ROM::SQL::Schema::Inferrer.get(gateway.connection.database_type.to_sym)
47
+ inferrer_for_db.new.call(dataset, gateway)
48
+ end
46
49
 
47
50
  dataset do
48
51
  table = opts[:from].first
49
52
 
50
53
  if db.table_exists?(table)
51
54
  pk_header = klass.primary_key_header(db, table)
52
- select(*columns).order(*pk_header.qualified)
55
+ col_names = klass.schema ? klass.schema.attributes.keys : columns
56
+ select(*col_names).order(*pk_header.qualified)
53
57
  else
54
58
  self
55
59
  end
@@ -305,6 +305,24 @@ module ROM
305
305
  __new__(dataset.__send__(__method__, *args, &block))
306
306
  end
307
307
 
308
+ # Restrict a relation to match grouping criteria
309
+ #
310
+ # @example
311
+ # users.with_task_count.having( task_count: 2 )
312
+ #
313
+ # users.with_task_count.having { task_count > 3 }
314
+ #
315
+ # @param [Hash] *args An optional hash with conditions for HAVING clause
316
+ #
317
+ # @return [Relation]
318
+ #
319
+ # @see http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
320
+ #
321
+ # @api public
322
+ def having(*args, &block)
323
+ __new__(dataset.__send__(__method__, *args, &block))
324
+ end
325
+
308
326
  # Inverts the current WHERE and HAVING clauses. If there is neither a
309
327
  # WHERE or HAVING clause, adds a WHERE clause that is always false.
310
328
  #
@@ -16,10 +16,13 @@ module ROM
16
16
  end
17
17
 
18
18
  def opts
19
- opts = {}
20
- opts[:associations] = associations_dsl.call if associations_dsl
21
- opts[:inferrer] = inferrer.new(self) if inferrer
22
- opts
19
+ opts = { inferrer: inferrer }
20
+
21
+ if associations_dsl
22
+ { **opts, associations: associations_dsl.call }
23
+ else
24
+ opts
25
+ end
23
26
  end
24
27
  end
25
28
  end
@@ -1,27 +1,39 @@
1
1
  module ROM
2
2
  module SQL
3
3
  class Schema < ROM::Schema
4
+ # @api private
4
5
  class Inferrer
5
6
  extend ClassMacros
6
7
 
7
- defines :type_mapping, :pk_type
8
+ defines :ruby_type_mapping, :numeric_pk_type, :db_type, :db_registry
8
9
 
9
- type_mapping(
10
- integer: Types::Strict::Int,
11
- string: Types::Strict::String,
12
- date: Types::Strict::Date,
13
- datetime: Types::Strict::Time,
14
- boolean: Types::Strict::Bool,
15
- decimal: Types::Strict::Decimal,
16
- blob: Types::Strict::String
10
+ ruby_type_mapping(
11
+ integer: Types::Int,
12
+ string: Types::String,
13
+ date: Types::Date,
14
+ datetime: Types::Time,
15
+ boolean: Types::Bool,
16
+ decimal: Types::Decimal,
17
+ float: Types::Float,
18
+ blob: Types::Blob
17
19
  ).freeze
18
20
 
19
- pk_type Types::Serial
21
+ numeric_pk_type Types::Serial
20
22
 
21
- attr_reader :dsl
23
+ db_registry Hash.new(self)
22
24
 
23
- def initialize(dsl)
24
- @dsl = dsl
25
+ def self.inherited(klass)
26
+ super
27
+
28
+ Inferrer.db_registry[klass.db_type] = klass unless klass.name.nil?
29
+ end
30
+
31
+ def self.[](type)
32
+ Class.new(self) { db_type(type) }
33
+ end
34
+
35
+ def self.get(type)
36
+ db_registry[type]
25
37
  end
26
38
 
27
39
  # @api private
@@ -29,33 +41,35 @@ module ROM
29
41
  columns = gateway.connection.schema(dataset)
30
42
  fks = fks_for(gateway, dataset)
31
43
 
32
- columns.each do |(name, definition)|
33
- dsl.attribute name, build_type(definition.merge(foreign_key: fks[name]))
44
+ columns.each_with_object({}) do |(name, definition), attrs|
45
+ type = build_type(definition.merge(foreign_key: fks[name]))
46
+ attrs[name] = type.meta(name: name)
34
47
  end
35
-
36
- pks = columns
37
- .map { |(name, definition)| name if definition.fetch(:primary_key) }
38
- .compact
39
-
40
- dsl.primary_key(*pks) if pks.any?
41
-
42
- dsl.attributes
43
48
  end
44
49
 
45
50
  private
46
51
 
47
- # @api private
48
- def build_type(primary_key: , type: , allow_null: , foreign_key: , **rest)
52
+ def build_type(primary_key:, db_type:, type:, allow_null:, foreign_key:, **rest)
49
53
  if primary_key
50
- self.class.pk_type
54
+ map_pk_type(type, db_type)
51
55
  else
52
- type = self.class.type_mapping.fetch(type)
53
- type = type.optional if allow_null
54
- type = type.meta(foreign_key: true, relation: foreign_key) if foreign_key
55
- type
56
+ mapped_type = map_type(type, db_type)
57
+ mapped_type = mapped_type.optional if allow_null
58
+ mapped_type = mapped_type.meta(foreign_key: true, relation: foreign_key) if foreign_key
59
+ mapped_type
56
60
  end
57
61
  end
58
62
 
63
+ def map_pk_type(_ruby_type, _db_type)
64
+ self.class.numeric_pk_type.meta(primary_key: true)
65
+ end
66
+
67
+ def map_type(ruby_type, db_type)
68
+ self.class.ruby_type_mapping.fetch(ruby_type) {
69
+ raise UnknownDBTypeError, "Cannot find corresponding type for #{ruby_type || db_type}"
70
+ }
71
+ end
72
+
59
73
  # @api private
60
74
  def fks_for(gateway, dataset)
61
75
  gateway.connection.foreign_key_list(dataset).each_with_object({}) do |definition, fks|
@@ -65,7 +79,6 @@ module ROM
65
79
  end
66
80
  end
67
81
 
68
- # @api private
69
82
  def build_fk(columns: , table: , **rest)
70
83
  if columns.size == 1
71
84
  [columns[0], table]
data/lib/rom/sql/types.rb CHANGED
@@ -5,7 +5,11 @@ module ROM
5
5
  module Types
6
6
  include ROM::Types
7
7
 
8
- Serial = Strict::Int.constrained(gt: 0).meta(primary_key: true)
8
+ Serial = Int.constrained(gt: 0).meta(primary_key: true)
9
+
10
+ Blob = Dry::Types::Definition
11
+ .new(Sequel::SQL::Blob)
12
+ .constructor(Sequel::SQL::Blob.method(:new))
9
13
  end
10
14
  end
11
15
  end
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  module SQL
3
- VERSION = '0.8.0'.freeze
3
+ VERSION = '0.9.0'.freeze
4
4
  end
5
5
  end
data/rom-sql.gemspec CHANGED
@@ -4,26 +4,27 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'rom/sql/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "rom-sql"
7
+ spec.name = 'rom-sql'
8
8
  spec.version = ROM::SQL::VERSION.dup
9
- spec.authors = ["Piotr Solnica"]
10
- spec.email = ["piotr.solnica@gmail.com"]
9
+ spec.authors = ['Piotr Solnica']
10
+ spec.email = ['piotr.solnica@gmail.com']
11
11
  spec.summary = 'SQL databases support for ROM'
12
12
  spec.description = spec.summary
13
- spec.homepage = "http://rom-rb.org"
14
- spec.license = "MIT"
13
+ spec.homepage = 'http://rom-rb.org'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_runtime_dependency "sequel", "~> 4.25"
22
- spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
23
- spec.add_runtime_dependency "dry-types", "~> 0.8"
24
- spec.add_runtime_dependency "rom", "~> 2.0"
25
- spec.add_runtime_dependency "rom-support", "~> 2.0"
21
+ spec.add_runtime_dependency 'sequel', '~> 4.25'
22
+ spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
23
+ spec.add_runtime_dependency 'dry-types', '~> 0.9'
24
+ spec.add_runtime_dependency 'dry-core', '~> 0.2'
25
+ spec.add_runtime_dependency 'rom', '~> 2.0'
26
+ spec.add_runtime_dependency 'rom-support', '~> 2.0'
26
27
 
27
- spec.add_development_dependency "bundler"
28
- spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency 'bundler'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
29
30
  end
@@ -0,0 +1,40 @@
1
+ RSpec.describe 'ROM::SQL::Schema::PostgresInferrer', :postgres 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
+ primary_key :id, :uuid
9
+ Json :json_data
10
+ Jsonb :jsonb_data
11
+ Decimal :money, null: false
12
+ column :tags, "text[]"
13
+ column :tag_ids, "bigint[]"
14
+ end
15
+ end
16
+
17
+ let(:dataset) { :test_inferrence }
18
+
19
+ let(:schema) { container.relations[dataset].schema }
20
+
21
+ context 'inferring db-specific attributes' do
22
+ before do
23
+ dataset = self.dataset
24
+ conf.relation(dataset) do
25
+ schema(dataset, infer: true)
26
+ end
27
+ end
28
+
29
+ it 'can infer attributes for dataset' do
30
+ expect(schema.attributes).to eql(
31
+ id: ROM::SQL::Types::PG::UUID.meta(name: :id, primary_key: true),
32
+ json_data: ROM::SQL::Types::PG::JSON.optional.meta(name: :json_data),
33
+ jsonb_data: ROM::SQL::Types::PG::JSONB.optional.meta(name: :jsonb_data),
34
+ money: ROM::SQL::Types::Decimal.meta(name: :money),
35
+ tags: ROM::SQL::Types::PG::Array('text').optional.meta(name: :tags),
36
+ tag_ids: ROM::SQL::Types::PG::Array('biging').optional.meta(name: :tag_ids)
37
+ )
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,38 @@
1
+ RSpec.describe 'PostgreSQL extension', :postgres do
2
+ include_context 'database setup'
3
+
4
+ before do
5
+ conn.drop_table?(:pg_people)
6
+ conn.drop_table?(:people)
7
+
8
+ conn.create_table :pg_people do
9
+ primary_key :id
10
+ String :name
11
+ column :tags, "text[]"
12
+ end
13
+
14
+ conf.relation(:people) do
15
+ schema(:pg_people, infer: true)
16
+ end
17
+
18
+ conf.commands(:people) do
19
+ define(:create)
20
+ end
21
+ end
22
+
23
+ let(:people_relation) { relations[:people] }
24
+
25
+ describe 'using arrays' do
26
+ let(:people) { commands[:people] }
27
+
28
+ it 'inserts array values' do
29
+ people.create.call(name: 'John Doe', tags: ['foo'])
30
+ expect(people_relation.to_a).to eq([id: 1, name: 'John Doe', tags: ['foo']])
31
+ end
32
+
33
+ it 'inserts empty arrays' do
34
+ people.create.call(name: 'John Doe', tags: [])
35
+ expect(people_relation.to_a).to eq([id: 1, name: 'John Doe', tags: []])
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,115 @@
1
+ RSpec.describe 'ROM::SQL::Types' do
2
+ describe 'ROM::SQL::Types::PG::JSON' do
3
+ it 'coerces to pg json hash' do
4
+ input = { foo: 'bar' }
5
+
6
+ expect(ROM::SQL::Types::PG::JSON[input]).to eql(Sequel.pg_json(input))
7
+ end
8
+
9
+ it 'coerces to pg json array' do
10
+ input = [1, 2, 3]
11
+ output = ROM::SQL::Types::PG::JSON[input]
12
+
13
+ expect(output).to be_instance_of(Sequel::Postgres::JSONArray)
14
+ expect(output.to_a).to eql(input)
15
+ end
16
+ end
17
+
18
+ describe 'ROM::SQL::Types::PG::Bytea' do
19
+ it 'coerces strings to Sequel::SQL::Blob' do
20
+ input = 'sutin'
21
+ output = ROM::SQL::Types::PG::Bytea[input]
22
+
23
+ expect(output).to be_instance_of(Sequel::SQL::Blob)
24
+ expect(output).to eql('sutin')
25
+ end
26
+ end
27
+
28
+ describe ROM::SQL::Types::PG::UUID do
29
+ it 'coerces strings to UUID' do
30
+ input = SecureRandom.uuid
31
+ output = described_class[input]
32
+
33
+ expect(output).to be_instance_of(String)
34
+ end
35
+ end
36
+
37
+ describe ROM::SQL::Types::PG::Array do
38
+ it 'coerces to pg array' do
39
+ input = [1, 2, 3]
40
+ output = ROM::SQL::Types::PG::Array('integer')[input]
41
+
42
+ expect(output).to be_instance_of(Sequel::Postgres::PGArray)
43
+ expect(output.to_a).to eql(input)
44
+ end
45
+
46
+ it 'accepts any other type of objects' do
47
+ input = [nil, 1, 'sutin', :sutin, 1.0, {}].sample
48
+ output = ROM::SQL::Types::PG::Array('integer')[input]
49
+
50
+ expect(output).to be_instance_of(Sequel::Postgres::ArrayOp)
51
+ expect(output.value).to eql(input)
52
+ end
53
+ end
54
+
55
+ describe ROM::SQL::Types::PG::JSON do
56
+ it 'coerces to pg json hash' do
57
+ input = { foo: 'bar' }
58
+ output = described_class[input]
59
+
60
+ expect(output).to be_instance_of(Sequel::Postgres::JSONHash)
61
+ expect(output).to eql(Sequel.pg_json(input))
62
+ end
63
+
64
+ it 'coerces to pg json array' do
65
+ input = [1, 2, 3]
66
+ output = described_class[input]
67
+
68
+ expect(output).to be_instance_of(Sequel::Postgres::JSONArray)
69
+ expect(output.to_a).to eql(input)
70
+ end
71
+
72
+ it 'accepts any other type of objects' do
73
+ input = [nil, 1, 'sutin', :sutin, 1.0].sample
74
+ output = described_class[input]
75
+
76
+ expect(output).to be_instance_of(Sequel::Postgres::JSONOp)
77
+ expect(output.value).to eql(input)
78
+ end
79
+ end
80
+
81
+ describe ROM::SQL::Types::PG::JSONB do
82
+ it 'coerces to pg jsonb hash' do
83
+ input = { foo: 'bar' }
84
+ output = described_class[input]
85
+
86
+ expect(output).to be_instance_of(Sequel::Postgres::JSONBHash)
87
+ expect(output).to eql(Sequel.pg_jsonb(input))
88
+ end
89
+
90
+ it 'coerces to pg jsonb array' do
91
+ input = [1, 2, 3]
92
+ output = described_class[input]
93
+
94
+ expect(output).to be_instance_of(Sequel::Postgres::JSONBArray)
95
+ expect(output.to_a).to eql(input)
96
+ end
97
+
98
+ it 'accepts any other type of objects' do
99
+ input = [nil, 1, 'sutin', :sutin, 1.0].sample
100
+ output = described_class[input]
101
+
102
+ expect(output).to be_instance_of(Sequel::Postgres::JSONBOp)
103
+ expect(output.value).to eql(input)
104
+ end
105
+ end
106
+
107
+ describe ROM::SQL::Types::PG::Money do
108
+ it 'coerces to pg Money' do
109
+ input = BigDecimal.new(1.0, 2)
110
+ output = described_class[input]
111
+
112
+ expect(output).to be_instance_of(BigDecimal)
113
+ end
114
+ end
115
+ end