relation_to_struct 1.5.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b36da83b17102b8a6f19d1bd7b55ba8486d2bd9b
4
- data.tar.gz: 3693bc1c95c15877d4e58a66b5700210e4861ff6
2
+ SHA256:
3
+ metadata.gz: f11031515a2afe3d55c8cfb98a664eb984079514c727233c74f341be071a825d
4
+ data.tar.gz: 4b97b2856f8529291ea4db5a97bc95b6b58ce5d4b4fbb3eaba066b54a3b317bf
5
5
  SHA512:
6
- metadata.gz: a1a43b34975434afd52f51ddd8ab1f3ba135e976248865a67ffc4ddc7fcfb6d9423c25168f4228c538b6c617d9d5a85559ebeebc99ff460f3a3df368cf973c8b
7
- data.tar.gz: 7ea17f61be34643cadeaa7377b0ca481ef574d32c2327a8986bf915a4b6d5e0bdae260477547d51f0ee49c197032f1471d8f28d5e25a386fea0906ffa7f27081
6
+ metadata.gz: 48ab91406c6acb33100fc8849b2f7ded38ca7a32b199f090d4bd388219b22a615c89f0853d211fa64b2f9b57ba83cf17de4499dc114f2a70578f449c3f389029
7
+ data.tar.gz: 9f004fded83df12de47eb94047e0245989f700edd63256af1ee9049b7657126603a0460d49ff54ee1ea67267f4ecfac8b0b7843ee28b8ae33af384ae46b0b53a
data/Appraisals CHANGED
@@ -1,5 +1,10 @@
1
- %w(5.0 5.1 5.2).each do |version|
1
+ %w(5.0 5.1 5.2 6.0 6.1 7.0).each do |version|
2
2
  appraise "rails-#{version.gsub(/\./, "-")}" do
3
3
  gem "rails", "~> #{version}.0"
4
+ if Gem::Version.new(version) >= Gem::Version.new("6.0.0")
5
+ gem "sqlite3", "~> 1.4.0"
6
+ else
7
+ gem "sqlite3", "~> 1.3.0"
8
+ end
4
9
  end
5
10
  end
data/README.md CHANGED
@@ -36,6 +36,8 @@ relation.to_structs(UserPostsSummary) # => array of structs
36
36
 
37
37
  ### From raw SQL
38
38
 
39
+ Note: In order to provide a consistent user experience regardless of the abstraction level used by your code, all of the following methods are available on both `ActiveRecord::Base` and `ActiveRecord::Base.connection`.
40
+
39
41
  ```
40
42
  UserPostsSummary = Struct.new(:user_name, :post_count)
41
43
  sql = <<-eos
@@ -99,7 +101,13 @@ For this reason, **all methods added to `ActiveRecord::Base` explicitly disable
99
101
 
100
102
  1. Fork it ( https://github.com/jcoleman/relation_to_struct/fork )
101
103
  2. Create your feature branch (`git checkout -b my-new-feature`)
102
- 3. Test your changes (`bundle install && appraisal install && rake`)
104
+ 3. Test your changes (`bundle install && bundle exec appraisal install && bundle exec rake`)
103
105
  4. Commit your changes (`git commit -am 'Add some feature'`)
104
106
  5. Push to the branch (`git push origin my-new-feature`)
105
107
  6. Create a new Pull Request
108
+
109
+ ## Releasing
110
+
111
+ 1. Bump version in `lib/relation_to_struct/version.rb` and commit.
112
+ 2. Run `rake build` to build the `*.gem` file.
113
+ 3. Run `rake release` to publish the gem to Rubygems.
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 5.0"
5
+ gem "rails", "~> 5.0.0"
6
+ gem "sqlite3", "~> 1.3.0"
6
7
 
7
8
  gemspec path: "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 5.1"
5
+ gem "rails", "~> 5.1.0"
6
+ gem "sqlite3", "~> 1.3.0"
6
7
 
7
8
  gemspec path: "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 5.2"
5
+ gem "rails", "~> 5.2.0"
6
+ gem "sqlite3", "~> 1.3.0"
6
7
 
7
8
  gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 6.0.0"
6
+ gem "sqlite3", "~> 1.4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 6.1.0"
6
+ gem "sqlite3", "~> 1.4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 7.0.0"
6
+ gem "sqlite3", "~> 1.4.0"
7
+
8
+ gemspec path: "../"
@@ -1,88 +1,18 @@
1
1
  module RelationToStruct::ActiveRecordBaseExtension
2
- extend ::ActiveSupport::Concern
3
-
4
2
  module ClassMethods
5
3
  def _sanitize_sql_for_relation_to_struct(sql)
6
4
  sanitized_sql = ActiveRecord::VERSION::MAJOR >= 5 ? sanitize_sql(sql) : sanitize_sql(sql, nil)
7
5
  end
8
6
 
9
- def structs_from_sql(struct_class, sql, binds=[])
10
- sanitized_sql = _sanitize_sql_for_relation_to_struct(sql)
11
- result = ActiveRecord::Base.uncached do
12
- connection.select_all(sanitized_sql, "Structs SQL Load", binds)
13
- end
14
-
15
- if result.columns.size != result.columns.uniq.size
16
- raise ArgumentError, 'Expected column names to be unique'
17
- end
18
-
19
- if result.columns != struct_class.members.collect(&:to_s)
20
- raise ArgumentError, 'Expected column names (and their order) to match struct attribute names'
21
- end
22
-
23
- if result.columns.size == 1
24
- result.cast_values().map do |tuple|
25
- struct_class.new(tuple)
26
- end
27
- else
28
- result.cast_values().map do |tuple|
29
- struct_class.new(*tuple)
30
- end
31
- end
32
- end
33
-
34
- def pluck_from_sql(sql, binds=[])
35
- sanitized_sql = _sanitize_sql_for_relation_to_struct(sql)
36
- result = ActiveRecord::Base.uncached do
37
- connection.select_all(sanitized_sql, "Pluck SQL Load", binds)
38
- end
39
- result.cast_values()
40
- end
41
-
42
- def value_from_sql(sql, binds=[])
43
- sanitized_sql = _sanitize_sql_for_relation_to_struct(sql)
44
- result = ActiveRecord::Base.uncached do
45
- connection.select_all(sanitized_sql, "Value SQL Load", binds)
46
- end
47
- raise ArgumentError, 'Expected exactly one column to be selected' unless result.columns.size == 1
48
-
49
- values = result.cast_values()
50
- case values.size
51
- when 0
52
- nil
53
- when 1
54
- values[0]
55
- else
56
- raise ArgumentError, 'Expected only a single result to be returned'
57
- end
58
- end
59
-
60
- def tuple_from_sql(sql, binds=[])
61
- sanitized_sql = _sanitize_sql_for_relation_to_struct(sql)
62
- result = ActiveRecord::Base.uncached do
63
- connection.select_all(sanitized_sql, "Value SQL Load", binds)
64
- end
65
- values = result.cast_values()
66
-
67
- case values.size
68
- when 0
69
- nil
70
- when 1
71
- result.columns.size == 1 ? values : values[0]
72
- else
73
- raise ArgumentError, 'Expected only a single result to be returned'
74
- end
75
- end
76
-
77
- def run_sql(sql, binds=[])
78
- sanitized_sql = _sanitize_sql_for_relation_to_struct(sql)
79
- # We don't need to build a result set unnecessarily; using
80
- # interface this also ensures we're clearing the result set
81
- # for manually memory managed object (e.g., when using the
82
- # PostgreSQL adaptor).
83
- connection.exec_update(sanitized_sql, "Run SQL", binds)
84
- end
7
+ delegate(
8
+ :structs_from_sql,
9
+ :pluck_from_sql,
10
+ :value_from_sql,
11
+ :tuple_from_sql,
12
+ :run_sql,
13
+ :to => :connection
14
+ )
85
15
  end
86
16
  end
87
17
 
88
- ::ActiveRecord::Base.send(:include, RelationToStruct::ActiveRecordBaseExtension)
18
+ ::ActiveRecord::Base.singleton_class.send(:prepend, RelationToStruct::ActiveRecordBaseExtension::ClassMethods)
@@ -0,0 +1,80 @@
1
+ module RelationToStruct::ActiveRecordConnectionAdapterExtension
2
+ def structs_from_sql(struct_class, sql, binds=[])
3
+ sanitized_sql = ActiveRecord::Base._sanitize_sql_for_relation_to_struct(sql)
4
+ result = ActiveRecord::Base.uncached do
5
+ select_all(sanitized_sql, "Structs SQL Load", binds)
6
+ end
7
+
8
+ if result.columns.size != result.columns.uniq.size
9
+ raise ArgumentError, 'Expected column names to be unique'
10
+ end
11
+
12
+ if result.columns != struct_class.members.collect(&:to_s)
13
+ raise ArgumentError, 'Expected column names (and their order) to match struct attribute names'
14
+ end
15
+
16
+ if result.columns.size == 1
17
+ result.cast_values().map do |tuple|
18
+ struct_class.new(tuple)
19
+ end
20
+ else
21
+ result.cast_values().map do |tuple|
22
+ struct_class.new(*tuple)
23
+ end
24
+ end
25
+ end
26
+
27
+ def pluck_from_sql(sql, binds=[])
28
+ sanitized_sql = ActiveRecord::Base._sanitize_sql_for_relation_to_struct(sql)
29
+ result = ActiveRecord::Base.uncached do
30
+ select_all(sanitized_sql, "Pluck SQL Load", binds)
31
+ end
32
+ result.cast_values()
33
+ end
34
+
35
+ def value_from_sql(sql, binds=[])
36
+ sanitized_sql = ActiveRecord::Base._sanitize_sql_for_relation_to_struct(sql)
37
+ result = ActiveRecord::Base.uncached do
38
+ select_all(sanitized_sql, "Value SQL Load", binds)
39
+ end
40
+ raise ArgumentError, 'Expected exactly one column to be selected' unless result.columns.size == 1
41
+
42
+ values = result.cast_values()
43
+ case values.size
44
+ when 0
45
+ nil
46
+ when 1
47
+ values[0]
48
+ else
49
+ raise ArgumentError, 'Expected only a single result to be returned'
50
+ end
51
+ end
52
+
53
+ def tuple_from_sql(sql, binds=[])
54
+ sanitized_sql = ActiveRecord::Base._sanitize_sql_for_relation_to_struct(sql)
55
+ result = ActiveRecord::Base.uncached do
56
+ select_all(sanitized_sql, "Value SQL Load", binds)
57
+ end
58
+ values = result.cast_values()
59
+
60
+ case values.size
61
+ when 0
62
+ nil
63
+ when 1
64
+ result.columns.size == 1 ? values : values[0]
65
+ else
66
+ raise ArgumentError, 'Expected only a single result to be returned'
67
+ end
68
+ end
69
+
70
+ def run_sql(sql, binds=[])
71
+ sanitized_sql = ActiveRecord::Base._sanitize_sql_for_relation_to_struct(sql)
72
+ # We don't need to build a result set unnecessarily; using
73
+ # interface this also ensures we're clearing the result set
74
+ # for manually memory managed object (e.g., when using the
75
+ # PostgreSQL adaptor).
76
+ exec_update(sanitized_sql, "Run SQL", binds)
77
+ end
78
+ end
79
+
80
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:prepend, RelationToStruct::ActiveRecordConnectionAdapterExtension)
@@ -1,6 +1,4 @@
1
1
  module RelationToStruct::ActiveRecordRelationExtension
2
- extend ::ActiveSupport::Concern
3
-
4
2
  def to_structs(struct_class)
5
3
  raise ArgumentError, 'Expected select_values to be present' unless self.select_values.present?
6
4
 
@@ -37,4 +35,4 @@ module RelationToStruct::ActiveRecordRelationExtension
37
35
  end
38
36
  end
39
37
 
40
- ::ActiveRecord::Relation.send(:include, RelationToStruct::ActiveRecordRelationExtension)
38
+ ::ActiveRecord::Relation.send(:prepend, RelationToStruct::ActiveRecordRelationExtension)
@@ -1,3 +1,3 @@
1
1
  module RelationToStruct
2
- VERSION = "1.5.0"
2
+ VERSION = "1.7.0"
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require "relation_to_struct/version"
2
+ require "relation_to_struct/active_record_connection_adapter_extension"
2
3
  require "relation_to_struct/active_record_base_extension"
3
4
  require "relation_to_struct/active_record_relation_extension"
4
5
 
@@ -18,14 +18,14 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "appraisal", "~> 2.1"
22
- spec.add_development_dependency "bundler", "~> 1.7"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "sqlite3", "~> 1.3"
25
- spec.add_development_dependency "rspec", "~> 3.2"
21
+ spec.add_development_dependency "appraisal", ">= 2.1"
22
+ spec.add_development_dependency "bundler", ">= 1.7"
23
+ spec.add_development_dependency "rake", ">= 10.0"
24
+ spec.add_development_dependency "sqlite3", ">= 1.3"
25
+ spec.add_development_dependency "rspec", ">= 3.2"
26
26
  spec.add_development_dependency "pry-byebug"
27
27
  spec.add_development_dependency "pg"
28
28
 
29
- spec.add_dependency "activerecord", ">= 5.0", "< 5.3"
30
- spec.add_dependency "activesupport", ">= 5.0", "< 5.3"
29
+ spec.add_dependency "activerecord", ">= 5.0", "< 7.1"
30
+ spec.add_dependency "activesupport", ">= 5.0", "< 7.1"
31
31
  end
@@ -0,0 +1,270 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRecord::ConnectionAdapters::AbstractAdapter do
4
+ before(:each) do
5
+ Economist.delete_all
6
+ EconomicSchool.delete_all
7
+ end
8
+
9
+ let(:connection) { ActiveRecord::Base.connection }
10
+
11
+ describe "#pluck_from_sql" do
12
+ it 'allows plucking with SQL directly' do
13
+ sql = "SELECT 1 * 23"
14
+ expect(connection.pluck_from_sql(sql)).to eq([23])
15
+ end
16
+
17
+ it 'allows plucking multiple columns with SQL directly' do
18
+ sql = "SELECT 1 * 23, 25"
19
+ expect(connection.pluck_from_sql(sql)).to eq([[23, 25]])
20
+ end
21
+
22
+ it 'bypasses the statement cache' do
23
+ sql = "SELECT random()"
24
+ value_1 = value_2 = nil
25
+
26
+ # Simulate a standard web request in Rails, since
27
+ # Rails enabled caching by default.
28
+ ActiveRecord::Base.cache do
29
+ value_1 = connection.pluck_from_sql(sql)
30
+ value_2 = connection.pluck_from_sql(sql)
31
+ end
32
+
33
+ expect(value_1).not_to eq(value_2)
34
+ end
35
+ end
36
+
37
+ describe "#value_from_sql" do
38
+ it 'allows selecting a value with SQL directly' do
39
+ sql = "SELECT 1 * 23"
40
+ expect(connection.value_from_sql(sql)).to eq(23)
41
+ end
42
+
43
+ it 'raises an error when multiple rows are selected' do
44
+ expect do
45
+ sql = "SELECT * FROM (VALUES (1), (2)) t"
46
+ connection.value_from_sql(sql)
47
+ end.to raise_error(ArgumentError, 'Expected only a single result to be returned')
48
+ end
49
+
50
+ it 'raises an error when multiple columns are selected' do
51
+ expect do
52
+ sql = "SELECT 1, 2"
53
+ connection.value_from_sql(sql)
54
+ end.to raise_error(ArgumentError, 'Expected exactly one column to be selected')
55
+ end
56
+
57
+ it 'supports binds' do
58
+ sql = ["SELECT 1 * ?", 5]
59
+ expect(connection.value_from_sql(sql)).to eq(5)
60
+ end
61
+
62
+ it 'supports arrays' do
63
+ if active_record_supports_arrays?
64
+ Economist.create!(name: 'F.A. Hayek')
65
+ Economist.create!(name: 'Ludwig von Mises')
66
+
67
+ result = connection.value_from_sql('SELECT ARRAY_AGG(name ORDER BY id) AS names FROM economists')
68
+ expect(result).to eq(['F.A. Hayek', 'Ludwig von Mises'])
69
+ else
70
+ skip "DB selection doesn't support ARRAY[]"
71
+ end
72
+ end
73
+
74
+ it 'bypasses the statement cache' do
75
+ sql = "SELECT random()"
76
+ value_1 = value_2 = nil
77
+
78
+ # Simulate a standard web request in Rails, since
79
+ # Rails enabled caching by default.
80
+ ActiveRecord::Base.cache do
81
+ value_1 = connection.value_from_sql(sql)
82
+ value_2 = connection.value_from_sql(sql)
83
+ end
84
+
85
+ expect(value_1).not_to eq(value_2)
86
+ end
87
+ end
88
+
89
+ describe "#tuple_from_sql" do
90
+ it 'allows selecting one value with SQL directly' do
91
+ sql = "SELECT 1"
92
+ expect(connection.tuple_from_sql(sql)).to eq([1])
93
+ end
94
+
95
+ it 'allows selecting multiple values with SQL directly' do
96
+ sql = "SELECT 1, 23"
97
+ expect(connection.tuple_from_sql(sql)).to eq([1, 23])
98
+ end
99
+
100
+ it 'raises an error when multiple rows are selected' do
101
+ expect do
102
+ sql = "SELECT * FROM (VALUES (1, 3), (2, 4)) t"
103
+ connection.tuple_from_sql(sql)
104
+ end.to raise_error(ArgumentError, 'Expected only a single result to be returned')
105
+ end
106
+
107
+ it 'supports binds' do
108
+ sql = ["SELECT ?, ?", 5, 6]
109
+ expect(connection.tuple_from_sql(sql)).to eq([5, 6])
110
+ end
111
+
112
+ it 'supports a single array' do
113
+ if active_record_supports_arrays?
114
+ Economist.create!(name: 'F.A. Hayek')
115
+ Economist.create!(name: 'Ludwig von Mises')
116
+
117
+ result = connection.tuple_from_sql(<<-SQL)
118
+ SELECT ARRAY_AGG(name ORDER BY id) AS names
119
+ FROM economists
120
+ SQL
121
+ expected_names = ['F.A. Hayek', 'Ludwig von Mises']
122
+ expect(result).to eq([expected_names])
123
+ else
124
+ skip "DB selection doesn't support ARRAY[]"
125
+ end
126
+ end
127
+
128
+ it 'supports multiple arrays' do
129
+ if active_record_supports_arrays?
130
+ Economist.create!(name: 'F.A. Hayek')
131
+ Economist.create!(name: 'Ludwig von Mises')
132
+
133
+ result = connection.tuple_from_sql(<<-SQL)
134
+ SELECT
135
+ ARRAY_AGG(name ORDER BY id) AS names,
136
+ ARRAY_AGG(CHAR_LENGTH(name) ORDER BY id) AS lengths
137
+ FROM economists
138
+ SQL
139
+ expected_names = ['F.A. Hayek', 'Ludwig von Mises']
140
+ expect(result).to eq([expected_names, expected_names.map(&:size)])
141
+ else
142
+ skip "DB selection doesn't support ARRAY[]"
143
+ end
144
+ end
145
+
146
+ it 'bypasses the statement cache' do
147
+ sql = "SELECT random()"
148
+ value_1 = value_2 = nil
149
+
150
+ # Simulate a standard web request in Rails, since
151
+ # Rails enabled caching by default.
152
+ ActiveRecord::Base.cache do
153
+ value_1 = connection.tuple_from_sql(sql)
154
+ value_2 = connection.tuple_from_sql(sql)
155
+ end
156
+
157
+ expect(value_1).not_to eq(value_2)
158
+ end
159
+ end
160
+
161
+ describe "#structs_from_sql" do
162
+ it 'ActiveRecord::Base should respond to :structs_from_sql' do
163
+ expect(connection.respond_to?(:structs_from_sql)).to eq(true)
164
+ end
165
+
166
+ it 'allows querying with SQL directly' do
167
+ test_struct = Struct.new(:number)
168
+ sql = "SELECT 1 * 23 AS number"
169
+ expect(connection.structs_from_sql(test_struct, sql)).to eq([test_struct.new(23)])
170
+ end
171
+
172
+ it 'properly casts a single array column' do
173
+ if active_record_supports_arrays?
174
+ Economist.create!(name: 'F.A. Hayek')
175
+ Economist.create!(name: 'Ludwig von Mises')
176
+
177
+ test_struct = Struct.new(:names)
178
+ structs_results = connection.structs_from_sql(test_struct, 'SELECT ARRAY_AGG(name ORDER BY id) AS names FROM economists')
179
+ expect(structs_results.first.names).to eq(['F.A. Hayek', 'Ludwig von Mises'])
180
+ else
181
+ skip "DB selection doesn't support ARRAY[]"
182
+ end
183
+ end
184
+
185
+ it 'raises an error when column count does not match struct size' do
186
+ expect do
187
+ test_struct = Struct.new(:id, :name, :extra_field)
188
+ connection.structs_from_sql(test_struct, 'SELECT id, name FROM economists')
189
+ end.to raise_error(ArgumentError, 'Expected column names (and their order) to match struct attribute names')
190
+ end
191
+
192
+ it 'raises an error when column names are not unique' do
193
+ expect do
194
+ test_struct = Struct.new(:id, :id2)
195
+ connection.structs_from_sql(test_struct, 'SELECT id, id FROM economists')
196
+ end.to raise_error(ArgumentError, 'Expected column names to be unique')
197
+ end
198
+
199
+ it 'raises an error when the column names do not match the struct attribute names' do
200
+ Economist.create!(name: 'F.A. Hayek')
201
+ expect do
202
+ test_struct = Struct.new(:value_a, :value_b)
203
+ connection.structs_from_sql(test_struct, 'SELECT 1 AS value_a, 2 AS value_b FROM economists')
204
+ end.not_to raise_error
205
+
206
+ expect do
207
+ test_struct = Struct.new(:value_a, :value_b)
208
+ connection.structs_from_sql(test_struct, 'SELECT 1 AS value_b, 2 AS value_a FROM economists')
209
+ end.to raise_error(ArgumentError, 'Expected column names (and their order) to match struct attribute names')
210
+
211
+ expect do
212
+ test_struct = Struct.new(:value_a, :value_b)
213
+ connection.structs_from_sql(test_struct, 'SELECT 1 AS value_a, 2 AS value_c FROM economists')
214
+ end.to raise_error(ArgumentError, 'Expected column names (and their order) to match struct attribute names')
215
+ end
216
+
217
+ it 'bypasses the statement cache' do
218
+ test_struct = Struct.new(:r)
219
+ sql = "SELECT random() AS r"
220
+ value_1 = value_2 = nil
221
+
222
+ # Simulate a standard web request in Rails, since
223
+ # Rails enabled caching by default.
224
+ ActiveRecord::Base.cache do
225
+ value_1 = connection.structs_from_sql(test_struct, sql)
226
+ value_2 = connection.structs_from_sql(test_struct, sql)
227
+ end
228
+
229
+ expect(value_1).not_to eq(value_2)
230
+ end
231
+ end
232
+
233
+ describe "#run_sql" do
234
+ it 'executes the provided SQL' do
235
+ sql = "INSERT INTO economic_schools(name) VALUES ('Chicago')"
236
+ expect do
237
+ ActiveRecord::Base.run_sql(sql)
238
+ end.to change { connection.value_from_sql("SELECT COUNT(*) FROM economic_schools") }.by(1)
239
+ end
240
+
241
+ it 'supports binds' do
242
+ sql = ["INSERT INTO economic_schools(name) VALUES (?)", "Chicago"]
243
+ expect do
244
+ ActiveRecord::Base.run_sql(sql)
245
+ end.to change { connection.value_from_sql("SELECT COUNT(*) FROM economic_schools") }.by(1)
246
+ end
247
+
248
+ it 'uses the exec_update API to avoid turning things in an ActiveRecord::Result' do
249
+ expect(ActiveRecord::Base.connection).to receive(:exec_update).exactly(:once)
250
+ sql = "SELECT 1"
251
+ connection.run_sql(sql)
252
+ end
253
+
254
+ it 'returns the number of rows modified for an INSERT' do
255
+ sql = "INSERT INTO economic_schools(name) VALUES ('Chicago'), ('Distributism')"
256
+ expect(connection.run_sql(sql)).to eq(2)
257
+ end
258
+
259
+ it 'bypasses the statement cache' do
260
+ # Simulate a standard web request in Rails, since
261
+ # Rails enabled caching by default.
262
+ ActiveRecord::Base.cache do
263
+ expect do
264
+ connection.run_sql("INSERT INTO economic_schools(name) VALUES ('Chicago')")
265
+ connection.run_sql("INSERT INTO economic_schools(name) VALUES ('Distributism')")
266
+ end.to change { connection.value_from_sql("SELECT COUNT(*) FROM economic_schools") }.by(2)
267
+ end
268
+ end
269
+ end
270
+ end
@@ -14,7 +14,12 @@ ActiveRecord::Base.configurations = {
14
14
  }
15
15
 
16
16
  env = ENV['DATABASE'] ||= 'sqlite'
17
- config = ActiveRecord::Base.configurations[env]
17
+ config =
18
+ if ActiveRecord::VERSION::MAJOR < 7
19
+ ActiveRecord::Base.configurations[env]
20
+ else
21
+ ActiveRecord::Base.configurations.configs_for(env_name: env).first
22
+ end
18
23
 
19
24
  case env
20
25
  when 'postgresql'
@@ -57,7 +57,7 @@ describe ActiveRecord::Relation do
57
57
  it 'properly casts values from arbitrary calculated columns' do
58
58
  hayek = Economist.create!(name: 'F.A. Hayek')
59
59
  scope = Economist.all
60
- pluck_results = scope.pluck("date('now')")
60
+ pluck_results = scope.pluck(Arel.sql("date('now')"))
61
61
  pluck_column_klass = pluck_results.first.class
62
62
 
63
63
  date_struct = Struct.new(:date)
data/spec/spec_helper.rb CHANGED
@@ -14,7 +14,7 @@ def active_record_supports_arrays?
14
14
  Economist.create!(name: 'F.A. Hayek')
15
15
  Economist.create!(name: 'Ludwig von Mises')
16
16
 
17
- pluck_results = Economist.select('name').order('id').limit(1).pluck('array[name]') rescue nil
17
+ pluck_results = Economist.select('name').order('id').limit(1).pluck(Arel.sql('array[name]')) rescue nil
18
18
  if pluck_results
19
19
  raise StandardError, "Unexpected array query results" unless pluck_results == [['F.A. Hayek']] # Verify ActiveRecord interface.
20
20
  supports_arrays = true
metadata CHANGED
@@ -1,83 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relation_to_struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Coleman
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-01 00:00:00.000000000 Z
11
+ date: 2022-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '2.1'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.7'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.7'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '10.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '10.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: sqlite3
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '1.3'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.3'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '3.2'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3.2'
83
83
  - !ruby/object:Gem::Dependency
@@ -117,7 +117,7 @@ dependencies:
117
117
  version: '5.0'
118
118
  - - "<"
119
119
  - !ruby/object:Gem::Version
120
- version: '5.3'
120
+ version: '7.1'
121
121
  type: :runtime
122
122
  prerelease: false
123
123
  version_requirements: !ruby/object:Gem::Requirement
@@ -127,7 +127,7 @@ dependencies:
127
127
  version: '5.0'
128
128
  - - "<"
129
129
  - !ruby/object:Gem::Version
130
- version: '5.3'
130
+ version: '7.1'
131
131
  - !ruby/object:Gem::Dependency
132
132
  name: activesupport
133
133
  requirement: !ruby/object:Gem::Requirement
@@ -137,7 +137,7 @@ dependencies:
137
137
  version: '5.0'
138
138
  - - "<"
139
139
  - !ruby/object:Gem::Version
140
- version: '5.3'
140
+ version: '7.1'
141
141
  type: :runtime
142
142
  prerelease: false
143
143
  version_requirements: !ruby/object:Gem::Requirement
@@ -147,7 +147,7 @@ dependencies:
147
147
  version: '5.0'
148
148
  - - "<"
149
149
  - !ruby/object:Gem::Version
150
- version: '5.3'
150
+ version: '7.1'
151
151
  description: ''
152
152
  email:
153
153
  - jtc331@gmail.com
@@ -165,12 +165,17 @@ files:
165
165
  - gemfiles/rails_5_0.gemfile
166
166
  - gemfiles/rails_5_1.gemfile
167
167
  - gemfiles/rails_5_2.gemfile
168
+ - gemfiles/rails_6_0.gemfile
169
+ - gemfiles/rails_6_1.gemfile
170
+ - gemfiles/rails_7_0.gemfile
168
171
  - lib/relation_to_struct.rb
169
172
  - lib/relation_to_struct/active_record_base_extension.rb
173
+ - lib/relation_to_struct/active_record_connection_adapter_extension.rb
170
174
  - lib/relation_to_struct/active_record_relation_extension.rb
171
175
  - lib/relation_to_struct/version.rb
172
176
  - relation_to_struct.gemspec
173
177
  - spec/active_record_base_spec.rb
178
+ - spec/active_record_connection_adapter_spec.rb
174
179
  - spec/active_record_helper/economic_school.rb
175
180
  - spec/active_record_helper/economist.rb
176
181
  - spec/active_record_helper/schema.rb
@@ -182,7 +187,7 @@ homepage: ''
182
187
  licenses:
183
188
  - MIT
184
189
  metadata: {}
185
- post_install_message:
190
+ post_install_message:
186
191
  rdoc_options: []
187
192
  require_paths:
188
193
  - lib
@@ -197,13 +202,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
202
  - !ruby/object:Gem::Version
198
203
  version: '0'
199
204
  requirements: []
200
- rubyforge_project:
201
- rubygems_version: 2.6.14
202
- signing_key:
205
+ rubygems_version: 3.1.6
206
+ signing_key:
203
207
  specification_version: 4
204
208
  summary: Return struct results from ActiveRecord relation queries
205
209
  test_files:
206
210
  - spec/active_record_base_spec.rb
211
+ - spec/active_record_connection_adapter_spec.rb
207
212
  - spec/active_record_helper/economic_school.rb
208
213
  - spec/active_record_helper/economist.rb
209
214
  - spec/active_record_helper/schema.rb