relation_to_struct 1.5.0 → 1.7.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.
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