rom-sql 1.0.1 → 1.0.2

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile +1 -0
  4. data/lib/rom/sql/attribute.rb +1 -1
  5. data/lib/rom/sql/projection_dsl.rb +6 -0
  6. data/lib/rom/sql/version.rb +1 -1
  7. data/rom-sql.gemspec +1 -1
  8. data/spec/integration/association/many_to_many/custom_fks_spec.rb +9 -13
  9. data/spec/integration/association/many_to_many/from_view_spec.rb +9 -8
  10. data/spec/integration/association/many_to_many_spec.rb +103 -102
  11. data/spec/integration/association/many_to_one/custom_fks_spec.rb +6 -7
  12. data/spec/integration/association/many_to_one/from_view_spec.rb +8 -4
  13. data/spec/integration/association/many_to_one_spec.rb +61 -54
  14. data/spec/integration/association/one_to_many/custom_fks_spec.rb +7 -6
  15. data/spec/integration/association/one_to_many/from_view_spec.rb +7 -10
  16. data/spec/integration/association/one_to_many/self_ref_spec.rb +6 -6
  17. data/spec/integration/association/one_to_many_spec.rb +0 -3
  18. data/spec/integration/association/one_to_one_spec.rb +17 -11
  19. data/spec/integration/association/one_to_one_through_spec.rb +3 -5
  20. data/spec/integration/commands/create_spec.rb +33 -22
  21. data/spec/integration/commands/update_spec.rb +3 -3
  22. data/spec/integration/commands/upsert_spec.rb +1 -1
  23. data/spec/integration/gateway_spec.rb +12 -8
  24. data/spec/integration/migration_spec.rb +4 -3
  25. data/spec/integration/plugins/associates/many_to_many_spec.rb +2 -2
  26. data/spec/integration/plugins/associates_spec.rb +1 -1
  27. data/spec/integration/relation_schema_spec.rb +4 -5
  28. data/spec/integration/schema/call_spec.rb +1 -1
  29. data/spec/integration/schema/inferrer/mysql_spec.rb +22 -23
  30. data/spec/integration/schema/inferrer/postgres_spec.rb +83 -82
  31. data/spec/integration/schema/inferrer/sqlite_spec.rb +18 -19
  32. data/spec/integration/schema/inferrer_spec.rb +54 -33
  33. data/spec/integration/schema/prefix_spec.rb +9 -11
  34. data/spec/integration/schema/qualified_spec.rb +9 -11
  35. data/spec/integration/schema/rename_spec.rb +13 -15
  36. data/spec/integration/schema/view_spec.rb +2 -2
  37. data/spec/integration/sequel_api_spec.rb +1 -1
  38. data/spec/integration/setup_spec.rb +5 -5
  39. data/spec/integration/support/active_support_notifications_spec.rb +2 -2
  40. data/spec/integration/support/rails_log_subscriber_spec.rb +2 -2
  41. data/spec/shared/accounts.rb +44 -0
  42. data/spec/shared/database_setup.rb +42 -81
  43. data/spec/shared/notes.rb +21 -0
  44. data/spec/shared/posts.rb +32 -0
  45. data/spec/shared/puppies.rb +13 -0
  46. data/spec/shared/relations.rb +1 -1
  47. data/spec/shared/users.rb +29 -0
  48. data/spec/shared/users_and_tasks.rb +32 -18
  49. data/spec/spec_helper.rb +18 -30
  50. data/spec/support/env_helper.rb +25 -0
  51. data/spec/support/oracle/create_users.sql +7 -0
  52. data/spec/support/oracle/set_sys_passwords.sql +2 -0
  53. data/spec/unit/plugin/pagination_spec.rb +2 -2
  54. data/spec/unit/plugin/timestamp_spec.rb +1 -1
  55. data/spec/unit/projection_dsl_spec.rb +8 -0
  56. data/spec/unit/relation/group_spec.rb +5 -3
  57. data/spec/unit/relation/max_spec.rb +1 -1
  58. data/spec/unit/relation/select_spec.rb +7 -0
  59. metadata +33 -5
  60. data/spec/shared/users_and_accounts.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dd4d5efa9ebc24506ae85cf772c6d57f6d59530b
4
- data.tar.gz: 1fe47e3b07ee7979e92d47257b1ecdf4a258246b
3
+ metadata.gz: e3faf5e4b8e2aa655c4a646421cf148cad3e137e
4
+ data.tar.gz: 2b1ac9258b8f15111297a71acf92f46f0f19607c
5
5
  SHA512:
6
- metadata.gz: 7338e9310e9ea94d333489edde055d213e2b466640c1366702cd08f78d636ee0577b5ec2f16e9e4c47b67847ff50f5fe23476d0440ba4e3fec9ad7c1e265c663
7
- data.tar.gz: 6c26fb847b68e9f5add7b8255fbf8d4b99ecd387dbcc5897343658c21cf75358b0dc3db369351c70fd592b2f90ce10b428512df0ffeabd747a80685a5b2da97f
6
+ metadata.gz: d11f6ab0d5a70192f29b1028d5d3acd81ce8b06f3d0edb6a74c24f8c869006837630476fcda3260aa021adbd3f0b5aa321e70df50feeb6d480b701524ff34329
7
+ data.tar.gz: b3f894dc4a8eddc3cca586186beaa530a0bf6317593622fca1ab86a568083dbeb88564bcba1c4110ee8230fb04c39792df0a760993908feaa999bed601e708a3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## v1.0.2 2017-02-16
2
+
3
+ ### Added
4
+
5
+ * Support for selecting literal strings via `select { `'foo'`.as(:bar) }` (solnic)
6
+
7
+ [Compare v1.0.1...v1.0.2](https://github.com/rom-rb/rom-sql/compare/v1.0.1...v1.0.2)
8
+
1
9
  ## v1.0.1 2017-02-09
2
10
 
3
11
  ### Added
@@ -11,6 +19,8 @@
11
19
 
12
20
  * Missing primary key now leads to a more meaningful error (flash-gordon)
13
21
 
22
+ [Compare v1.0.0...v1.0.1](https://github.com/rom-rb/rom-sql/compare/v1.0.0...v1.0.1)
23
+
14
24
  ## v1.0.0 2017-01-29
15
25
 
16
26
  This release is based on rom core 3.0.0 with its improved Schema API, which is extended with SQL-specific features.
data/Gemfile CHANGED
@@ -25,6 +25,7 @@ group :test do
25
25
  gem 'jdbc-mysql', platforms: :jruby
26
26
  gem 'sqlite3', platforms: [:mri, :rbx]
27
27
  gem 'jdbc-sqlite3', platforms: :jruby
28
+ gem 'ruby-oci8', platforms: :mri if ENV['ROM_USE_ORACLE']
28
29
  end
29
30
 
30
31
  group :tools do
@@ -21,7 +21,7 @@ module ROM
21
21
  #
22
22
  # @api public
23
23
  def aliased(name)
24
- super.meta(sql_expr: sql_expr.as(name))
24
+ super.meta(name: meta.fetch(:name, name), sql_expr: sql_expr.as(name))
25
25
  end
26
26
  alias_method :as, :aliased
27
27
 
@@ -5,6 +5,12 @@ module ROM
5
5
  module SQL
6
6
  # @api private
7
7
  class ProjectionDSL < DSL
8
+ # @api public
9
+ def `(value)
10
+ expr = ::Sequel::VIRTUAL_ROW.instance_eval { `#{value}` }
11
+ ::ROM::SQL::Attribute.new(type(:string)).meta(sql_expr: expr)
12
+ end
13
+
8
14
  # @api private
9
15
  def respond_to_missing?(name, include_private = false)
10
16
  super || type(name)
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  module SQL
3
- VERSION = '1.0.1'.freeze
3
+ VERSION = '1.0.2'.freeze
4
4
  end
5
5
  end
data/rom-sql.gemspec CHANGED
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  lib = File.expand_path('../lib', __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'rom/sql/version'
@@ -26,4 +25,5 @@ Gem::Specification.new do |spec|
26
25
 
27
26
  spec.add_development_dependency 'bundler'
28
27
  spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rspec', '~> 3.5'
29
29
  end
@@ -1,10 +1,14 @@
1
1
  RSpec.describe ROM::SQL::Association::ManyToMany, '#call' do
2
+ include_context 'users'
3
+
4
+ before do
5
+ inferrable_relations.concat %i(puzzles puzzle_solvers)
6
+ end
7
+
2
8
  subject(:assoc) do
3
9
  relations[:users].associations[:puzzles]
4
10
  end
5
11
 
6
- include_context 'database setup'
7
-
8
12
  with_adapters do
9
13
  before do
10
14
  conn.create_table(:puzzles) do
@@ -36,9 +40,6 @@ RSpec.describe ROM::SQL::Association::ManyToMany, '#call' do
36
40
  end
37
41
  end
38
42
 
39
- joe_id = relations[:users].insert(name: 'Joe')
40
- jane_id = relations[:users].insert(name: 'Jane')
41
-
42
43
  p1_id = relations[:puzzles].insert(text: 'P1')
43
44
  p2_id = relations[:puzzles].insert(text: 'P2')
44
45
  p3_id = relations[:puzzles].insert(text: 'P3')
@@ -50,23 +51,18 @@ RSpec.describe ROM::SQL::Association::ManyToMany, '#call' do
50
51
  relations[:puzzle_solvers].insert(solver_id: jane_id, puzzle_id: p3_id)
51
52
  end
52
53
 
53
- after do
54
- conn.drop_table?(:puzzle_solvers)
55
- conn.drop_table?(:puzzles)
56
- end
57
-
58
54
  it 'prepares joined relations using custom FK' do
59
- relation = assoc.call(relations).order(:puzzles__text)
55
+ relation = assoc.call(relations).order(:puzzles__text, :puzzle_solvers__solver_id)
60
56
 
61
57
  expect(relation.schema.map(&:to_sym)).
62
58
  to eql(%i[puzzles__id puzzles__text puzzle_solvers__solver_id])
63
59
 
64
60
  expect(relation.to_a).
65
61
  to eql([
66
- { id: 1, solver_id: 1, text: 'P1' },
62
+ { id: 1, solver_id: 2, text: 'P1' },
67
63
  { id: 2, solver_id: 1, text: 'P2' },
68
64
  { id: 2, solver_id: 2, text: 'P2' },
69
- { id: 3, solver_id: 2, text: 'P3' }
65
+ { id: 3, solver_id: 1, text: 'P3' }
70
66
  ])
71
67
  end
72
68
  end
@@ -1,10 +1,14 @@
1
1
  RSpec.describe ROM::SQL::Association::ManyToMany, '#call' do
2
+ include_context 'users'
3
+
4
+ before do
5
+ inferrable_relations.concat %i(puzzles puzzle_solvers)
6
+ end
7
+
2
8
  subject(:assoc) do
3
9
  relations[:users].associations[:solved_puzzles]
4
10
  end
5
11
 
6
- include_context 'database setup'
7
-
8
12
  with_adapters do
9
13
  before do
10
14
  conn.create_table(:puzzles) do
@@ -46,9 +50,6 @@ RSpec.describe ROM::SQL::Association::ManyToMany, '#call' do
46
50
  end
47
51
  end
48
52
 
49
- joe_id = relations[:users].insert(name: 'Joe')
50
- jane_id = relations[:users].insert(name: 'Jane')
51
-
52
53
  p1_id = relations[:puzzles].insert(text: 'P1')
53
54
  p2_id = relations[:puzzles].insert(text: 'P2', solved: true)
54
55
  p3_id = relations[:puzzles].insert(text: 'P3')
@@ -66,15 +67,15 @@ RSpec.describe ROM::SQL::Association::ManyToMany, '#call' do
66
67
  end
67
68
 
68
69
  it 'prepares joined relations using custom FK' do
69
- relation = assoc.call(relations).order(:puzzles__text)
70
+ relation = assoc.call(relations).order(:puzzles__text, :puzzle_solvers__user_id)
70
71
 
71
72
  expect(relation.schema.map(&:to_sym)).
72
73
  to eql(%i[puzzles__id puzzles__text puzzles__solved puzzle_solvers__user_id])
73
74
 
74
75
  expect(relation.to_a).
75
76
  to eql([
76
- { id: 2, user_id: 1, solved: true, text: 'P2' },
77
- { id: 2, user_id: 2, solved: true, text: 'P2' }
77
+ { id: 2, user_id: 1, solved: db_true, text: 'P2' },
78
+ { id: 2, user_id: 2, solved: db_true, text: 'P2' }
78
79
  ])
79
80
  end
80
81
  end
@@ -1,145 +1,146 @@
1
1
  RSpec.describe ROM::SQL::Association::ManyToMany do
2
2
  include_context 'users and tasks'
3
3
 
4
- # FIXME: Figure out what is wrong with sqlite
5
- with_adapters(:postgres, :mysql) do
6
- context 'with two associations pointing to the same target relation' do
7
- let(:container) do
8
- ROM.container(:sql, uri) do |conf|
9
- conf.default.create_table(:users_tasks) do
10
- foreign_key :user_id, :users
11
- foreign_key :task_id, :tasks
12
- primary_key [:user_id, :task_id]
13
- end
4
+ with_adapters do
5
+ context 'through a relation with a composite PK' do
6
+ subject(:assoc) {
7
+ ROM::SQL::Association::ManyToMany.new(:tasks, :tags, through: :task_tags)
8
+ }
14
9
 
15
- conf.relation(:users) do
16
- schema(infer: true) do
17
- associations do
18
- has_many :users_tasks
19
- has_many :tasks, through: :users_tasks
20
- has_many :tasks, as: :priv_tasks
21
- end
22
- end
23
- end
10
+ let(:tasks) { container.relations[:tasks] }
11
+ let(:tags) { container.relations[:tags] }
12
+
13
+ before do
14
+ conf.relation(:task_tags) do
15
+ schema do
16
+ attribute :task_id, ROM::SQL::Types::ForeignKey(:tasks)
17
+ attribute :tag_id, ROM::SQL::Types::ForeignKey(:tags)
24
18
 
25
- conf.relation(:users_tasks) do
26
- schema(infer: true) do
27
- associations do
28
- belongs_to :user
29
- belongs_to :task
30
- end
19
+ primary_key :task_id, :tag_id
20
+
21
+ associations do
22
+ many_to_one :tasks
23
+ many_to_one :tags
31
24
  end
32
25
  end
26
+ end
33
27
 
34
- conf.relation(:tasks) do
35
- schema(infer: true) do
36
- associations do
37
- has_many :users_tasks
38
- has_many :users, through: :users_tasks
39
- end
28
+ conf.relation(:tasks) do
29
+ schema do
30
+ attribute :id, ROM::SQL::Types::Serial
31
+ attribute :user_id, ROM::SQL::Types::ForeignKey(:users)
32
+ attribute :title, ROM::SQL::Types::String
33
+
34
+ associations do
35
+ one_to_many :task_tags
36
+ one_to_many :tags, through: :task_tags
40
37
  end
41
38
  end
42
39
  end
43
40
  end
44
41
 
45
- it 'does not conflict with two FKs' do
46
- users = container.relations[:users]
47
- tasks = container.relations[:tasks]
48
- assoc = users.associations[:tasks]
42
+ describe '#result' do
43
+ specify { expect(ROM::SQL::Association::ManyToMany.result).to be(:many) }
44
+ end
49
45
 
50
- relation = tasks.for_combine(assoc).call(users.call)
46
+ describe '#call' do
47
+ it 'prepares joined relations' do
48
+ relation = assoc.call(container.relations)
51
49
 
52
- expect(relation.to_a).to be_empty
50
+ expect(relation.schema.map(&:to_sym)).to eql(%i[tags__id tags__name task_tags__task_id])
51
+ expect(relation.to_a).to eql([id: 1, name: 'important', task_id: 1])
52
+ end
53
53
  end
54
54
 
55
- it 'preloads using FK' do
56
- users = container.relations[:users]
57
- tasks = container.relations[:tasks]
58
- assoc = users.associations[:priv_tasks]
55
+ describe ':through another assoc' do
56
+ subject(:assoc) do
57
+ ROM::SQL::Association::ManyToMany.new(:users, :tags, through: :tasks)
58
+ end
59
59
 
60
- relation = tasks.for_combine(assoc).call(users.where(id: 2).call)
60
+ it 'prepares joined relations through other association' do
61
+ relation = assoc.call(container.relations)
61
62
 
62
- expect(relation.to_a).to eql([id: 1, user_id: 2, title: "Joe's task"])
63
+ expect(relation.schema.map(&:to_sym)).to eql(%i[tags__id tags__name tasks__user_id])
64
+ expect(relation.to_a).to eql([id: 1, name: 'important', user_id: 2])
65
+ end
63
66
  end
64
- end
65
- end
66
-
67
- with_adapters do
68
- subject(:assoc) {
69
- ROM::SQL::Association::ManyToMany.new(:tasks, :tags, through: :task_tags)
70
- }
71
67
 
72
- let(:tasks) { container.relations[:tasks] }
73
- let(:tags) { container.relations[:tags] }
68
+ describe ROM::Plugins::Relation::SQL::AutoCombine, '#for_combine' do
69
+ it 'preloads relation based on association' do
70
+ relation = tags.for_combine(assoc).call(tasks.call)
74
71
 
75
- before do
76
- conf.relation(:task_tags) do
77
- schema do
78
- attribute :task_id, ROM::SQL::Types::ForeignKey(:tasks)
79
- attribute :tag_id, ROM::SQL::Types::ForeignKey(:tags)
72
+ expect(relation.to_a).to eql([id: 1, name: 'important', task_id: 1])
73
+ end
80
74
 
81
- primary_key :task_id, :tag_id
75
+ it 'maintains original relation' do
76
+ relation = tags.
77
+ select_append(tags[:name].as(:tag)).
78
+ for_combine(assoc).call(tasks.call)
82
79
 
83
- associations do
84
- many_to_one :tasks
85
- many_to_one :tags
86
- end
80
+ expect(relation.to_a).to eql([id: 1, tag: 'important', name: 'important', task_id: 1])
87
81
  end
88
82
  end
83
+ end
84
+
85
+ context 'with two associations pointing to the same target relation' do
86
+ before do
87
+ inferrable_relations.concat %i(users_tasks)
88
+ end
89
89
 
90
- conf.relation(:tasks) do
91
- schema do
92
- attribute :id, ROM::SQL::Types::Serial
93
- attribute :user_id, ROM::SQL::Types::ForeignKey(:users)
94
- attribute :title, ROM::SQL::Types::String
90
+ before do
91
+ conn.create_table(:users_tasks) do
92
+ foreign_key :user_id, :users
93
+ foreign_key :task_id, :tasks
94
+ primary_key [:user_id, :task_id]
95
+ end
95
96
 
96
- associations do
97
- one_to_many :task_tags
98
- one_to_many :tags, through: :task_tags
97
+ conf.relation(:users) do
98
+ schema(infer: true) do
99
+ associations do
100
+ has_many :users_tasks
101
+ has_many :tasks, through: :users_tasks
102
+ has_many :tasks, as: :priv_tasks
103
+ end
99
104
  end
100
105
  end
101
- end
102
- end
103
-
104
- describe '#result' do
105
- specify { expect(ROM::SQL::Association::ManyToMany.result).to be(:many) }
106
- end
107
106
 
108
- describe '#call' do
109
- it 'prepares joined relations' do
110
- relation = assoc.call(container.relations)
107
+ conf.relation(:users_tasks) do
108
+ schema(infer: true) do
109
+ associations do
110
+ belongs_to :user
111
+ belongs_to :task
112
+ end
113
+ end
114
+ end
111
115
 
112
- expect(relation.schema.map(&:to_sym)).to eql(%i[tags__id tags__name task_tags__task_id])
113
- expect(relation.to_a).to eql([id: 1, name: 'important', task_id: 1])
116
+ conf.relation(:tasks) do
117
+ schema(infer: true) do
118
+ associations do
119
+ has_many :users_tasks
120
+ has_many :users, through: :users_tasks
121
+ end
122
+ end
123
+ end
114
124
  end
115
- end
116
125
 
117
- describe ':through another assoc' do
118
- subject(:assoc) do
119
- ROM::SQL::Association::ManyToMany.new(:users, :tags, through: :tasks)
120
- end
126
+ it 'does not conflict with two FKs' do
127
+ users = container.relations[:users]
128
+ tasks = container.relations[:tasks]
129
+ assoc = users.associations[:tasks]
121
130
 
122
- it 'prepares joined relations through other association' do
123
- relation = assoc.call(container.relations)
131
+ relation = tasks.for_combine(assoc).call(users.call)
124
132
 
125
- expect(relation.schema.map(&:to_sym)).to eql(%i[tags__id tags__name tasks__user_id])
126
- expect(relation.to_a).to eql([id: 1, name: 'important', user_id: 2])
133
+ expect(relation.to_a).to be_empty
127
134
  end
128
- end
129
135
 
130
- describe ROM::Plugins::Relation::SQL::AutoCombine, '#for_combine' do
131
- it 'preloads relation based on association' do
132
- relation = tags.for_combine(assoc).call(tasks.call)
133
-
134
- expect(relation.to_a).to eql([id: 1, name: 'important', task_id: 1])
135
- end
136
+ it 'preloads using FK' do
137
+ users = container.relations[:users]
138
+ tasks = container.relations[:tasks]
139
+ assoc = users.associations[:priv_tasks]
136
140
 
137
- it 'maintains original relation' do
138
- relation = tags.
139
- select_append(tags[:name].as(:tag)).
140
- for_combine(assoc).call(tasks.call)
141
+ relation = tasks.for_combine(assoc).call(users.where(id: 2).call)
141
142
 
142
- expect(relation.to_a).to eql([id: 1, tag: 'important', name: 'important', task_id: 1])
143
+ expect(relation.to_a).to eql([id: 1, user_id: 2, title: "Joe's task"])
143
144
  end
144
145
  end
145
146
  end
@@ -1,11 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe ROM::SQL::Association::ManyToOne, '#call' do
4
+ include_context 'database setup'
5
+
6
+ before do
7
+ inferrable_relations.concat %i(destinations flights)
8
+ end
9
+
4
10
  let(:assoc_from) { relations[:flights].associations[:from] }
5
11
  let(:assoc_to) { relations[:flights].associations[:to] }
6
12
 
7
- include_context 'database setup'
8
-
9
13
  with_adapters do
10
14
  before do
11
15
  conn.create_table(:destinations) do
@@ -35,11 +39,6 @@ RSpec.describe ROM::SQL::Association::ManyToOne, '#call' do
35
39
  relations[:flights].insert(code: 'F1', from_id: from_id, to_id: to_id)
36
40
  end
37
41
 
38
- after do
39
- conn.drop_table(:flights)
40
- conn.drop_table(:destinations)
41
- end
42
-
43
42
  it 'prepares joined relations using correct FKs based on association aliases' do
44
43
  relation = assoc_from.call(relations)
45
44