rom-sql 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5da70a1471f9c0b3a27f10cacf6af54f060090be
4
- data.tar.gz: 1f391fdbd269bbf0df9d0af23a5fc112948ce1f2
3
+ metadata.gz: 4fdb75ab8064473be4552789976b049b939ed356
4
+ data.tar.gz: 3c2cc1dd4f7ee9428f6a2f45a2ecc1368c6e5882
5
5
  SHA512:
6
- metadata.gz: f91c5854d45dc52b26ddb0eaca6dc465ff02db69bd145d6ce010c6eaa93571ac46842d68a6897e0b88fffb7639efe26c94435a035c0d4a0790be37defd846fa2
7
- data.tar.gz: 879cf6a660eb79ab84b1adbfb33ede96613d786356457182d81bc3f00f2a7165fbe939d25b7bd0755242710469cffae650976252c9c19eeda37067a89a0407ec
6
+ metadata.gz: a96f739270f64f4828b7d777d949b6b20bfd7161c6a19813347d1f0fdbdbe4f125c387195ab01dc184fbac06ab3faf83cfda76e608f212937336529663fd4d87
7
+ data.tar.gz: af5dbaba037a0ef816b32af01ba4b2c504eaf6310463d244b38e79304b8b4face04137857b9c12f9b30c87c73cb044e369153a0e6b8322f9d5949b0ef689cc91
data/.travis.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
- sudo: false
2
+ dist: trusty
3
+ sudo: required
3
4
  cache: bundler
4
5
  services:
5
6
  - postgresql
@@ -7,27 +8,23 @@ services:
7
8
  bundler_args: --without yard guard benchmarks tools
8
9
  before_script:
9
10
  - psql -c 'create database rom_sql;' -U postgres
10
- - mysql -e 'create database rom_sql;'
11
+ - mysql -u root -e 'create database rom_sql;'
12
+ after_success:
13
+ - '[ "$TRAVIS_RUBY_VERSION" = "2.3.1" ] && [ "$TRAVIS_BRANCH" = "master" ] && bundle exec codeclimate-test-reporter'
11
14
  script: "bundle exec rake ci"
12
15
  rvm:
13
- - 2.1.9
16
+ - 2.1.10
14
17
  - 2.2.5
15
18
  - 2.3.1
16
- - rbx-2
17
- - jruby-9.0.5.0
18
- - ruby-head
19
+ - rbx-3
20
+ - jruby
19
21
  env:
20
22
  global:
21
23
  - CODECLIMATE_REPO_TOKEN=03d7f66589572702b12426d2bc71c4de6281a96139e33b335b894264b1f8f0b0
22
24
  - JRUBY_OPTS='--dev -J-Xmx1024M'
23
- - PG_LTE_95='false'
24
25
  matrix:
25
26
  allow_failures:
26
- - rvm: ruby-head
27
- - rvm: jruby-head
28
- include:
29
- - rvm: jruby-head
30
- before_install: gem install bundler --no-ri --no-rdoc
27
+ - rvm: rbx
31
28
  notifications:
32
29
  webhooks:
33
30
  urls:
@@ -35,3 +32,10 @@ notifications:
35
32
  on_success: change
36
33
  on_failure: always
37
34
  on_start: false
35
+ addons:
36
+ postgresql: 9.5
37
+ apt:
38
+ packages:
39
+ - mysql-server-5.6
40
+ - mysql-client-core-5.6
41
+ - mysql-client-5.6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## v0.9.0 2016-11-08
2
+
3
+ ### Added
4
+
5
+ * `Associations::{OneToMany,OneToOne}#associate` for merging FKs into child tuple (jodosha)
6
+ * Added support for PostgreSQL types: UUID, Array, JSONB and Money (jodosha)
7
+ * Support for DB specific schema inferrers (flash-gordon)
8
+ * Automatically infer PG arrays and JSON(B) types (jodosha + flash-gordon)
9
+ * Support for `Relation#having` (cflipse)
10
+
11
+ ### Changed
12
+
13
+ * Inferred types in schemas **are no longer strict** (flash-gordon)
14
+ * PG-specific types are handled by `:postgres` extension and it loads connection extensions automatically (flash-gordon)
15
+ * Make `OneToOne` inherit from `OneToMany` (beauby)
16
+ * Default dataset will use column names from schema if it's available (solnic)
17
+
18
+ ### Fixed
19
+
20
+ * Floats are inferred by schemas (cflipse)
21
+
22
+ [Compare v0.8.0...v0.9.0](https://github.com/rom-rb/rom-sql/compare/v0.8.0...v0.9.0)
23
+
1
24
  ## v0.8.0 2016-07-27
2
25
 
3
26
  ### Added
data/Gemfile CHANGED
@@ -2,14 +2,22 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ gem 'rom', github: 'rom-rb/rom', branch: 'master'
6
+
5
7
  group :test do
6
8
  gem 'byebug', platforms: :mri
7
- gem 'anima', '~> 0.2.0'
8
- gem 'virtus'
9
+ gem 'dry-struct'
9
10
  gem 'activesupport', '~> 4.2'
10
11
  gem 'rspec', '~> 3.1'
11
12
  gem 'codeclimate-test-reporter', require: false
12
- gem 'pg', platforms: [:mri, :rbx]
13
+ gem 'simplecov', require: false
14
+
15
+ if RUBY_ENGINE == 'rbx'
16
+ gem 'pg', '~> 0.18.0', platforms: :rbx
17
+ else
18
+ gem 'pg', '~> 0.19', platforms: :mri
19
+ end
20
+
13
21
  gem 'mysql2', platforms: [:mri, :rbx]
14
22
  gem 'jdbc-postgres', platforms: :jruby
15
23
  gem 'jdbc-mysql', platforms: :jruby
data/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![Test Coverage](https://codeclimate.com/github/rom-rb/rom-sql/badges/coverage.svg)][codeclimate]
14
14
  [![Inline docs](http://inch-ci.org/github/rom-rb/rom-sql.svg?branch=master)][inchpages]
15
15
 
16
- RDBMS support for [Ruby Object Mapper](https://github.com/rom-rb/rom).
16
+ SQL support for [rom-rb](https://github.com/rom-rb/rom).
17
17
 
18
18
  Resources:
19
19
 
@@ -36,12 +36,6 @@ Or install it yourself as:
36
36
 
37
37
  $ gem install rom-sql
38
38
 
39
- Check out [SQL guide](http://rom-rb.org/learn/adapters/sql/) for usage examples.
40
-
41
- ## ROADMAP
42
-
43
- For details please refer to [issues](https://github.com/rom-rb/rom-sql/issues).
44
-
45
39
  ## License
46
40
 
47
41
  See `LICENSE` file.
data/lib/rom/sql.rb CHANGED
@@ -2,22 +2,19 @@ require 'dry-equalizer'
2
2
  require 'sequel'
3
3
  require 'rom'
4
4
 
5
- module ROM
6
- MissingConfigurationError = Class.new(StandardError)
7
- end
5
+ require 'rom/sql/version'
6
+ require 'rom/sql/errors'
8
7
 
9
8
  require 'rom/configuration_dsl'
10
9
 
11
- require 'rom/sql/version'
12
- require 'rom/sql/errors'
13
10
  require 'rom/sql/plugins'
14
11
  require 'rom/sql/relation'
15
12
  require 'rom/sql/gateway'
16
13
  require 'rom/sql/migration'
14
+ require 'rom/sql/extensions'
17
15
 
18
16
  if defined?(Rails)
19
- require 'rom/sql/support/active_support_notifications'
20
- require 'rom/sql/support/rails_log_subscriber'
17
+ ROM::SQL.load_extensions(:active_support_notifications, :rails_log_subscriber)
21
18
  end
22
19
 
23
20
  ROM.register_adapter(:sql, ROM::SQL)
@@ -68,8 +68,8 @@ module ROM
68
68
  end
69
69
  end
70
70
 
71
- require 'rom/sql/association/one_to_one'
72
71
  require 'rom/sql/association/one_to_many'
72
+ require 'rom/sql/association/one_to_one'
73
73
  require 'rom/sql/association/many_to_many'
74
74
  require 'rom/sql/association/many_to_one'
75
75
  require 'rom/sql/association/one_to_one_through'
@@ -1,8 +1,51 @@
1
1
  module ROM
2
2
  module SQL
3
3
  class Association
4
- class OneToMany < OneToOne
4
+ class OneToMany < Association
5
5
  result :many
6
+
7
+ # @api public
8
+ def call(relations)
9
+ with_keys(relations) do |left_pk, right_fk|
10
+ right = relations[target.relation]
11
+ columns = right.header.qualified.to_a
12
+
13
+ relation = right
14
+ .inner_join(source, left_pk => right_fk)
15
+ .select(*columns)
16
+ .order(*right.header.project(*right.primary_key).qualified)
17
+
18
+ relation.with(attributes: relation.header.names)
19
+ end
20
+ end
21
+
22
+ # @api public
23
+ def combine_keys(relations)
24
+ Hash[*with_keys(relations)]
25
+ end
26
+
27
+ # @api public
28
+ def join_keys(relations)
29
+ with_keys(relations) { |source_key, target_key|
30
+ { qualify(source, source_key) => qualify(target, target_key) }
31
+ }
32
+ end
33
+
34
+ # @api private
35
+ def associate(relations, child, parent)
36
+ pk, fk = join_key_map(relations)
37
+ child.merge(fk => parent.fetch(pk))
38
+ end
39
+
40
+ protected
41
+
42
+ # @api private
43
+ def with_keys(relations, &block)
44
+ source_key = relations[source.relation].primary_key
45
+ target_key = relations[target.relation].foreign_key(source.relation)
46
+ return [source_key, target_key] unless block
47
+ yield(source_key, target_key)
48
+ end
6
49
  end
7
50
  end
8
51
  end
@@ -1,45 +1,8 @@
1
1
  module ROM
2
2
  module SQL
3
3
  class Association
4
- class OneToOne < Association
4
+ class OneToOne < OneToMany
5
5
  result :one
6
-
7
- # @api public
8
- def call(relations)
9
- with_keys(relations) do |left_pk, right_fk|
10
- right = relations[target.relation]
11
- columns = right.header.qualified.to_a
12
-
13
- relation = right
14
- .inner_join(source, left_pk => right_fk)
15
- .select(*columns)
16
- .order(*right.header.project(*right.primary_key).qualified)
17
-
18
- relation.with(attributes: relation.header.names)
19
- end
20
- end
21
-
22
- # @api public
23
- def combine_keys(relations)
24
- Hash[*with_keys(relations)]
25
- end
26
-
27
- # @api public
28
- def join_keys(relations)
29
- with_keys(relations) { |source_key, target_key|
30
- { qualify(source, source_key) => qualify(target, target_key) }
31
- }
32
- end
33
-
34
- protected
35
-
36
- # @api private
37
- def with_keys(relations, &block)
38
- source_key = relations[source.relation].primary_key
39
- target_key = relations[target.relation].foreign_key(source.relation)
40
- return [source_key, target_key] unless block
41
- yield(source_key, target_key)
42
- end
43
6
  end
44
7
  end
45
8
  end
@@ -3,6 +3,3 @@ require 'rom/commands'
3
3
  require 'rom/sql/commands/create'
4
4
  require 'rom/sql/commands/update'
5
5
  require 'rom/sql/commands/delete'
6
-
7
- require 'rom/sql/commands/postgres'
8
- require 'rom/sql/commands_ext/postgres'
@@ -11,7 +11,7 @@ module ROM
11
11
  def call(*args)
12
12
  super
13
13
  rescue *ERROR_MAP.keys => e
14
- raise ERROR_MAP[e.class], e
14
+ raise ERROR_MAP.fetch(e.class, Error), e
15
15
  end
16
16
 
17
17
  alias_method :[], :call
@@ -1,7 +1,8 @@
1
- require "rom/sql/error"
1
+ require 'rom/sql/error'
2
2
 
3
3
  module ROM
4
4
  module SQL
5
+ MissingConfigurationError = Class.new(StandardError)
5
6
  NoAssociationError = Class.new(StandardError)
6
7
  DatabaseError = Class.new(Error)
7
8
  ConstraintError = Class.new(Error)
@@ -9,9 +10,11 @@ module ROM
9
10
  UniqueConstraintError = Class.new(ConstraintError)
10
11
  ForeignKeyConstraintError = Class.new(ConstraintError)
11
12
  CheckConstraintError = Class.new(ConstraintError)
13
+ UnknownDBTypeError = Class.new(StandardError)
12
14
 
13
15
  ERROR_MAP = {
14
16
  Sequel::DatabaseError => DatabaseError,
17
+ Sequel::ConstraintViolation => ConstraintError,
15
18
  Sequel::NotNullConstraintViolation => NotNullConstraintError,
16
19
  Sequel::UniqueConstraintViolation => UniqueConstraintError,
17
20
  Sequel::ForeignKeyConstraintViolation => ForeignKeyConstraintError,
@@ -0,0 +1,19 @@
1
+ require 'dry/core/extensions'
2
+
3
+ module ROM
4
+ module SQL
5
+ extend Dry::Core::Extensions
6
+
7
+ register_extension(:postgres) do
8
+ require 'rom/sql/extensions/postgres'
9
+ end
10
+
11
+ register_extension(:active_support_notifications) do
12
+ require 'rom/sql/extensions/active_support_notifications'
13
+ end
14
+
15
+ register_extension(:rails_log_subscriber) do
16
+ require 'rom/sql/extensions/rails_log_subscriber'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ require 'rom/sql/extensions/postgres/commands'
2
+ require 'rom/sql/extensions/postgres/types'
3
+ require 'rom/sql/extensions/postgres/inferrer'
@@ -1,7 +1,45 @@
1
+ require 'rom/sql/commands/create'
2
+ require 'rom/sql/commands/update'
3
+
1
4
  module ROM
2
5
  module SQL
3
6
  module Commands
4
7
  module Postgres
8
+ module Create
9
+ # Executes insert statement and returns inserted tuples
10
+ #
11
+ # @api private
12
+ def insert(tuples)
13
+ tuples.map do |tuple|
14
+ relation.dataset.returning(*relation.columns).insert(tuple)
15
+ end.flatten
16
+ end
17
+
18
+ # Executes multi_insert statement and returns inserted tuples
19
+ #
20
+ # @api private
21
+ def multi_insert(tuples)
22
+ relation.dataset.returning(*relation.columns).multi_insert(tuples)
23
+ end
24
+
25
+ # Executes upsert statement (INSERT with ON CONFLICT clause)
26
+ # and returns inserted/updated tuples
27
+ #
28
+ # @api private
29
+ def upsert(tuple, opts = EMPTY_HASH)
30
+ relation.dataset.returning(*relation.columns).insert_conflict(opts).insert(tuple)
31
+ end
32
+ end
33
+
34
+ module Update
35
+ # Executes update statement and returns updated tuples
36
+ #
37
+ # @api private
38
+ def update(tuple)
39
+ relation.dataset.returning(*relation.columns).update(tuple)
40
+ end
41
+ end
42
+
5
43
  # Upsert command
6
44
  #
7
45
  # Uses a feature of PostgreSQL 9.5 commonly called an "upsert".
@@ -0,0 +1,64 @@
1
+ require 'rom/sql/schema/inferrer'
2
+ require 'rom/sql/extensions/postgres/types'
3
+
4
+ module ROM
5
+ module SQL
6
+ class Schema
7
+ class PostgresInferrer < Inferrer[:postgres]
8
+ defines :db_numeric_types, :db_type_mapping, :db_array_type_matcher
9
+
10
+ db_numeric_types(
11
+ 'smallint' => true,
12
+ 'integer' => true,
13
+ 'bigint' => true,
14
+ 'decimal' => true,
15
+ 'numeric' => true,
16
+ 'real' => true,
17
+ 'double precision' => true,
18
+ 'serial' => true,
19
+ 'bigserial' => true,
20
+ ).freeze
21
+
22
+ db_type_mapping(
23
+ 'uuid' => Types::PG::UUID,
24
+ 'money' => Types::PG::Money,
25
+ 'bytea' => Types::Blob,
26
+ 'json' => Types::PG::JSON,
27
+ 'jsonb' => Types::PG::JSONB,
28
+ ).freeze
29
+
30
+ db_array_type_matcher Sequel::Postgres::PGArray::EMPTY_BRACKET
31
+
32
+ private
33
+
34
+ def map_pk_type(type, db_type)
35
+ if numeric?(type, db_type)
36
+ type = self.class.numeric_pk_type
37
+ else
38
+ type = map_type(type, db_type)
39
+ end
40
+
41
+ type.meta(primary_key: true)
42
+ end
43
+
44
+ def map_type(ruby_type, db_type)
45
+ if db_type.end_with?(self.class.db_array_type_matcher)
46
+ Types::PG::Array(db_type)
47
+ else
48
+ map_db_type(db_type) || super
49
+ end
50
+ end
51
+
52
+ def map_db_type(db_type)
53
+ self.class.db_type_mapping[db_type]
54
+ end
55
+
56
+ def numeric?(ruby_type, db_type)
57
+ self.class.db_numeric_types.fetch(db_type) do
58
+ ruby_type == :integer
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end