rom-sql 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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