relation_to_struct 0.0.5 → 1.0.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 +4 -4
- data/README.md +26 -3
- data/lib/relation_to_struct/active_record_base_extension.rb +4 -4
- data/lib/relation_to_struct/active_record_relation_extension.rb +2 -2
- data/lib/relation_to_struct/version.rb +1 -1
- data/spec/active_record_base_spec.rb +76 -0
- data/spec/active_record_relation_spec.rb +82 -0
- data/spec/relation_to_struct_spec.rb +0 -137
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38b9e2235461124930a00545c8f280a149e720e2
|
4
|
+
data.tar.gz: e554b29f4b015da645780962aa44985d751ec177
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60d4d55c08833a5c9f875d06df49c307d192f5ea11985201222ecc42a8fdc5b638ca750dfd89767c8add0f5a5b33e4af47e3d4b1b80b032cf75983ffef1448de
|
7
|
+
data.tar.gz: acb30ead089b288b26f417cd985f2da323e95fd996da0a0c3ceca85d83ff123836a6d4480cd6e406dead496e13e1d9992d2797d996a3099c78b8d7d43d13f9d0
|
data/README.md
CHANGED
@@ -20,13 +20,36 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
$ gem install relation_to_struct
|
22
22
|
|
23
|
-
##
|
23
|
+
## Examples
|
24
24
|
|
25
|
-
|
25
|
+
You can query either via direct SQL or from an ActiveRecord relation.
|
26
|
+
|
27
|
+
### From an existing relation
|
28
|
+
|
29
|
+
```
|
30
|
+
UserPostsSummary = Struct.new(:user_name, :post_count)
|
31
|
+
relation = User.joins(:blog_posts).where(name: 'Hayek').group('users.id').select('users.name, COUNT(blog_posts.id)')
|
32
|
+
relation.to_structs(UserPostsSummary) # => array of structs
|
33
|
+
```
|
34
|
+
|
35
|
+
### From raw SQL
|
36
|
+
|
37
|
+
```
|
38
|
+
UserPostsSummary = Struct.new(:user_name, :post_count)
|
39
|
+
sql = <<-eos
|
40
|
+
SELECT users.name, COUNT(blog_posts.id)
|
41
|
+
FROM users
|
42
|
+
LEFT OUTER JOIN blog_posts ON blog_posts.user_id = users.id
|
43
|
+
GROUP BY users.id
|
44
|
+
eos
|
45
|
+
|
46
|
+
ActiveRecord::Base.structs_from_sql(UserPostsSummary, sql) # => array of structs
|
47
|
+
ActiveRecord::Base.pluck_from_sql(sql) # => array of tuples
|
48
|
+
```
|
26
49
|
|
27
50
|
## Contributing
|
28
51
|
|
29
|
-
1. Fork it ( https://github.com/
|
52
|
+
1. Fork it ( https://github.com/jcoleman/relation_to_struct/fork )
|
30
53
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
31
54
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
32
55
|
4. Push to the branch (`git push origin my-new-feature`)
|
@@ -5,12 +5,12 @@ module RelationToStruct::ActiveRecordBaseExtension
|
|
5
5
|
def structs_from_sql(struct_class, sql, binds=[])
|
6
6
|
result = connection.select_all(sanitize_sql(sql, nil), "Structs SQL Load", binds)
|
7
7
|
|
8
|
-
if result.columns.size !=
|
9
|
-
raise ArgumentError, 'Expected
|
8
|
+
if result.columns.size != result.columns.uniq.size
|
9
|
+
raise ArgumentError, 'Expected column names to be unique'
|
10
10
|
end
|
11
11
|
|
12
|
-
if result.columns
|
13
|
-
raise ArgumentError, 'Expected
|
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
14
|
end
|
15
15
|
|
16
16
|
if result.columns.size == 1
|
@@ -11,8 +11,8 @@ module RelationToStruct::ActiveRecordRelationExtension
|
|
11
11
|
raise ArgumentError, 'Expected struct fields and columns lengths to be equal'
|
12
12
|
end
|
13
13
|
|
14
|
-
if result.columns.size != result.
|
15
|
-
raise ArgumentError, 'Expected
|
14
|
+
if result.columns.size != result.columns.uniq.size
|
15
|
+
raise ArgumentError, 'Expected column names to be unique'
|
16
16
|
end
|
17
17
|
|
18
18
|
result.cast_values(klass.column_types)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RelationToStruct do
|
4
|
+
before(:each) do
|
5
|
+
Economist.delete_all
|
6
|
+
EconomicSchool.delete_all
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'ActiveRecord::Base should respond to :structs_from_sql' do
|
10
|
+
expect(ActiveRecord::Base.respond_to?(:structs_from_sql)).to eq(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should allow querying with SQL directly' do
|
14
|
+
test_struct = Struct.new(:number)
|
15
|
+
sql = "SELECT 1 * 23 AS number"
|
16
|
+
expect(ActiveRecord::Base.structs_from_sql(test_struct, sql)).to eq([test_struct.new(23)])
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should allow plucking with SQL directly' do
|
20
|
+
sql = "SELECT 1 * 23"
|
21
|
+
expect(ActiveRecord::Base.pluck_from_sql(sql)).to eq([23])
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should allow plucking multiple columns with SQL directly' do
|
25
|
+
sql = "SELECT 1 * 23, 25"
|
26
|
+
expect(ActiveRecord::Base.pluck_from_sql(sql)).to eq([[23, 25]])
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'structs_from_sql should properly cast a single array column' do
|
30
|
+
Economist.create!(name: 'F.A. Hayek')
|
31
|
+
Economist.create!(name: 'Ludwig von Mises')
|
32
|
+
|
33
|
+
pluck_results = Economist.select('name').order('id').limit(1).pluck('array[name]') rescue nil
|
34
|
+
if pluck_results
|
35
|
+
expect(pluck_results).to eq([['F.A. Hayek']]) # Verify ActiveRecord interface.
|
36
|
+
|
37
|
+
test_struct = Struct.new(:names)
|
38
|
+
structs_results = ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT ARRAY_AGG(name ORDER BY id) AS names FROM economists')
|
39
|
+
expect(structs_results.first.names).to eq(['F.A. Hayek', 'Ludwig von Mises'])
|
40
|
+
else
|
41
|
+
skip "DB selection doesn't support ARRAY[]"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'structs_from_sql should raise an error when column count does not match struct size' do
|
46
|
+
expect do
|
47
|
+
test_struct = Struct.new(:id, :name, :extra_field)
|
48
|
+
ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT id, name FROM economists')
|
49
|
+
end.to raise_error(ArgumentError, 'Expected column names (and their order) to match struct attribute names')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'structs_from_sql should raise an error when column names are not unique' do
|
53
|
+
expect do
|
54
|
+
test_struct = Struct.new(:id, :id2)
|
55
|
+
ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT id, id FROM economists')
|
56
|
+
end.to raise_error(ArgumentError, 'Expected column names to be unique')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'structs_from_sql should raise an error when the column names do not match the struct attribute names' do
|
60
|
+
Economist.create!(name: 'F.A. Hayek')
|
61
|
+
expect do
|
62
|
+
test_struct = Struct.new(:value_a, :value_b)
|
63
|
+
ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT 1 AS value_a, 2 AS value_b FROM economists')
|
64
|
+
end.not_to raise_error
|
65
|
+
|
66
|
+
expect do
|
67
|
+
test_struct = Struct.new(:value_a, :value_b)
|
68
|
+
ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT 1 AS value_b, 2 AS value_a FROM economists')
|
69
|
+
end.to raise_error(ArgumentError, 'Expected column names (and their order) to match struct attribute names')
|
70
|
+
|
71
|
+
expect do
|
72
|
+
test_struct = Struct.new(:value_a, :value_b)
|
73
|
+
ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT 1 AS value_a, 2 AS value_c FROM economists')
|
74
|
+
end.to raise_error(ArgumentError, 'Expected column names (and their order) to match struct attribute names')
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RelationToStruct do
|
4
|
+
before(:each) do
|
5
|
+
Economist.delete_all
|
6
|
+
EconomicSchool.delete_all
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should respond to :to_structs' do
|
10
|
+
expect(Economist.all.respond_to?(:to_structs)).to eq(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it '#to_structs should raise an error when the relation has no select_values' do
|
14
|
+
expect do
|
15
|
+
Economist.all.to_structs(Struct.new(:test_struct))
|
16
|
+
end.to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it '#to_structs should return an empty array when no results are returned' do
|
20
|
+
expect(Economist.where('1 = 0').select(:id).to_structs(Struct.new(:test_struct))).to eq([])
|
21
|
+
end
|
22
|
+
|
23
|
+
it '#to_structs should return an array with struct instances' do
|
24
|
+
hayek = Economist.create!(name: 'F.A. Hayek')
|
25
|
+
id_struct = Struct.new(:id)
|
26
|
+
expect(Economist.all.select(:id).to_structs(id_struct)).to eq([id_struct.new(hayek.id)])
|
27
|
+
end
|
28
|
+
|
29
|
+
it '#to_structs should handle joined elements properly' do
|
30
|
+
austrian = EconomicSchool.create!(name: 'Austrian Economics')
|
31
|
+
hayek = Economist.create!(name: 'F.A. Hayek', economic_school: austrian)
|
32
|
+
test_struct = Struct.new(:name, :school)
|
33
|
+
expect(
|
34
|
+
Economist
|
35
|
+
.joins(:economic_school)
|
36
|
+
.select('economists.name', 'economic_schools.name as school_name')
|
37
|
+
.to_structs(test_struct)
|
38
|
+
).to eq([test_struct.new(hayek.name, austrian.name)])
|
39
|
+
end
|
40
|
+
|
41
|
+
it '#to_structs should properly cast values from arbitrary calculated columns' do
|
42
|
+
hayek = Economist.create!(name: 'F.A. Hayek')
|
43
|
+
scope = Economist.all
|
44
|
+
pluck_results = scope.pluck("date('now')")
|
45
|
+
pluck_column_klass = pluck_results.first.class
|
46
|
+
|
47
|
+
date_struct = Struct.new(:date)
|
48
|
+
struct_scope = scope.select("date('now')")
|
49
|
+
structs_results = struct_scope.to_structs(date_struct)
|
50
|
+
struct_column_klass = structs_results.first.date.class
|
51
|
+
expect(pluck_column_klass).to eq(struct_column_klass)
|
52
|
+
end
|
53
|
+
|
54
|
+
it '#to_structs should properly cast a single array column' do
|
55
|
+
Economist.create!(name: 'F.A. Hayek')
|
56
|
+
|
57
|
+
pluck_results = Economist.select('name').pluck('array[name]') rescue nil
|
58
|
+
if pluck_results
|
59
|
+
expect(pluck_results).to eq([['F.A. Hayek']]) # Verify ActiveRecord interface.
|
60
|
+
|
61
|
+
test_struct = Struct.new(:names)
|
62
|
+
structs_results = Economist.select('array[name]').to_structs(test_struct)
|
63
|
+
expect(structs_results.first.names).to eq(['F.A. Hayek'])
|
64
|
+
else
|
65
|
+
skip "DB selection doesn't support ARRAY[]"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it '#to_structs should raise an error when column count does not match struct size' do
|
70
|
+
expect do
|
71
|
+
test_struct = Struct.new(:id, :name, :extra_field)
|
72
|
+
Economist.select('id, name').to_structs(test_struct)
|
73
|
+
end.to raise_error(ArgumentError, 'Expected struct fields and columns lengths to be equal')
|
74
|
+
end
|
75
|
+
|
76
|
+
it '#to_structs should raise an error when column names are not unique' do
|
77
|
+
expect do
|
78
|
+
test_struct = Struct.new(:id, :id2)
|
79
|
+
Economist.select('id, id').to_structs(test_struct)
|
80
|
+
end.to raise_error(ArgumentError, 'Expected column names to be unique')
|
81
|
+
end
|
82
|
+
end
|
@@ -1,144 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe RelationToStruct do
|
4
|
-
before(:each) do
|
5
|
-
Economist.delete_all
|
6
|
-
EconomicSchool.delete_all
|
7
|
-
end
|
8
|
-
|
9
4
|
it 'has a version number' do
|
10
5
|
expect(RelationToStruct::VERSION).not_to be nil
|
11
6
|
end
|
12
|
-
|
13
|
-
describe 'relation' do
|
14
|
-
it 'should respond to :to_structs' do
|
15
|
-
expect(Economist.all.respond_to?(:to_structs)).to eq(true)
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'should respond to :to_structs' do
|
19
|
-
pending 'next version'
|
20
|
-
end
|
21
|
-
|
22
|
-
it '#to_structs should raise an error when the relation has no select_values' do
|
23
|
-
expect do
|
24
|
-
Economist.all.to_structs(Struct.new(:test_struct))
|
25
|
-
end.to raise_error
|
26
|
-
end
|
27
|
-
|
28
|
-
it '#to_structs should return an empty array when no results are returned' do
|
29
|
-
expect(Economist.where('1 = 0').select(:id).to_structs(Struct.new(:test_struct))).to eq([])
|
30
|
-
end
|
31
|
-
|
32
|
-
it '#to_structs should return an array with struct instances' do
|
33
|
-
hayek = Economist.create!(name: 'F.A. Hayek')
|
34
|
-
id_struct = Struct.new(:id)
|
35
|
-
expect(Economist.all.select(:id).to_structs(id_struct)).to eq([id_struct.new(hayek.id)])
|
36
|
-
end
|
37
|
-
|
38
|
-
it '#to_structs should handle joined elements properly' do
|
39
|
-
austrian = EconomicSchool.create!(name: 'Austrian Economics')
|
40
|
-
hayek = Economist.create!(name: 'F.A. Hayek', economic_school: austrian)
|
41
|
-
test_struct = Struct.new(:name, :school)
|
42
|
-
expect(
|
43
|
-
Economist
|
44
|
-
.joins(:economic_school)
|
45
|
-
.select('economists.name', 'economic_schools.name as school_name')
|
46
|
-
.to_structs(test_struct)
|
47
|
-
).to eq([test_struct.new(hayek.name, austrian.name)])
|
48
|
-
end
|
49
|
-
|
50
|
-
it '#to_structs should properly cast values from arbitrary calculated columns' do
|
51
|
-
hayek = Economist.create!(name: 'F.A. Hayek')
|
52
|
-
scope = Economist.all
|
53
|
-
pluck_results = scope.pluck("date('now')")
|
54
|
-
pluck_column_klass = pluck_results.first.class
|
55
|
-
|
56
|
-
date_struct = Struct.new(:date)
|
57
|
-
struct_scope = scope.select("date('now')")
|
58
|
-
structs_results = struct_scope.to_structs(date_struct)
|
59
|
-
struct_column_klass = structs_results.first.date.class
|
60
|
-
expect(pluck_column_klass).to eq(struct_column_klass)
|
61
|
-
end
|
62
|
-
|
63
|
-
it '#to_structs should properly cast a single array column' do
|
64
|
-
Economist.create!(name: 'F.A. Hayek')
|
65
|
-
|
66
|
-
pluck_results = Economist.select('name').pluck('array[name]') rescue nil
|
67
|
-
if pluck_results
|
68
|
-
expect(pluck_results).to eq([['F.A. Hayek']]) # Verify ActiveRecord interface.
|
69
|
-
|
70
|
-
test_struct = Struct.new(:names)
|
71
|
-
structs_results = Economist.select('array[name]').to_structs(test_struct)
|
72
|
-
expect(structs_results.first.names).to eq(['F.A. Hayek'])
|
73
|
-
else
|
74
|
-
skip "DB selection doesn't support ARRAY[]"
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
it '#to_structs should raise an error when column count does not match struct size' do
|
79
|
-
expect do
|
80
|
-
test_struct = Struct.new(:id, :name, :extra_field)
|
81
|
-
Economist.select('id, name').to_structs(test_struct)
|
82
|
-
end.to raise_error(ArgumentError, 'Expected struct fields and columns lengths to be equal')
|
83
|
-
end
|
84
|
-
|
85
|
-
it '#to_structs should raise an error when column_type count does not match the column count' do
|
86
|
-
expect do
|
87
|
-
test_struct = Struct.new(:id, :id2)
|
88
|
-
Economist.select('id, id').to_structs(test_struct)
|
89
|
-
end.to raise_error(ArgumentError, 'Expected unique column names count and column count to be equal')
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
describe 'non-model specific querying' do
|
94
|
-
it 'ActiveRecord::Base should respond to :structs_from_sql' do
|
95
|
-
expect(ActiveRecord::Base.respond_to?(:structs_from_sql)).to eq(true)
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'should allow querying with SQL directly' do
|
99
|
-
test_struct = Struct.new(:number)
|
100
|
-
sql = "SELECT 1 * 23"
|
101
|
-
expect(ActiveRecord::Base.structs_from_sql(test_struct, sql)).to eq([test_struct.new(23)])
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'should allow plucking with SQL directly' do
|
105
|
-
sql = "SELECT 1 * 23"
|
106
|
-
expect(ActiveRecord::Base.pluck_from_sql(sql)).to eq([23])
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'should allow plucking multiple columns with SQL directly' do
|
110
|
-
sql = "SELECT 1 * 23, 25"
|
111
|
-
expect(ActiveRecord::Base.pluck_from_sql(sql)).to eq([[23, 25]])
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'structs_from_sql should properly cast a single array column' do
|
115
|
-
Economist.create!(name: 'F.A. Hayek')
|
116
|
-
Economist.create!(name: 'Ludwig von Mises')
|
117
|
-
|
118
|
-
pluck_results = Economist.select('name').order('id').limit(1).pluck('array[name]') rescue nil
|
119
|
-
if pluck_results
|
120
|
-
expect(pluck_results).to eq([['F.A. Hayek']]) # Verify ActiveRecord interface.
|
121
|
-
|
122
|
-
test_struct = Struct.new(:names)
|
123
|
-
structs_results = ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT ARRAY_AGG(name ORDER BY id) FROM economists')
|
124
|
-
expect(structs_results.first.names).to eq(['F.A. Hayek', 'Ludwig von Mises'])
|
125
|
-
else
|
126
|
-
skip "DB selection doesn't support ARRAY[]"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'structs_from_sql should raise an error when column count does not match struct size' do
|
131
|
-
expect do
|
132
|
-
test_struct = Struct.new(:id, :name, :extra_field)
|
133
|
-
ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT id, name FROM economists')
|
134
|
-
end.to raise_error(ArgumentError, 'Expected struct fields and columns lengths to be equal')
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'structs_from_sql should raise an error when column_type count does not match the column count' do
|
138
|
-
expect do
|
139
|
-
test_struct = Struct.new(:id, :id2)
|
140
|
-
ActiveRecord::Base.structs_from_sql(test_struct, 'SELECT id, id FROM economists')
|
141
|
-
end.to raise_error(ArgumentError, 'Expected unique column names count and column count to be equal')
|
142
|
-
end
|
143
|
-
end
|
144
7
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: relation_to_struct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Coleman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -140,10 +140,12 @@ files:
|
|
140
140
|
- lib/relation_to_struct/active_record_relation_extension.rb
|
141
141
|
- lib/relation_to_struct/version.rb
|
142
142
|
- relation_to_struct.gemspec
|
143
|
+
- spec/active_record_base_spec.rb
|
143
144
|
- spec/active_record_helper/economic_school.rb
|
144
145
|
- spec/active_record_helper/economist.rb
|
145
146
|
- spec/active_record_helper/schema.rb
|
146
147
|
- spec/active_record_helper/setup.rb
|
148
|
+
- spec/active_record_relation_spec.rb
|
147
149
|
- spec/relation_to_struct_spec.rb
|
148
150
|
- spec/spec_helper.rb
|
149
151
|
homepage: ''
|
@@ -166,14 +168,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
168
|
version: '0'
|
167
169
|
requirements: []
|
168
170
|
rubyforge_project:
|
169
|
-
rubygems_version: 2.
|
171
|
+
rubygems_version: 2.5.1
|
170
172
|
signing_key:
|
171
173
|
specification_version: 4
|
172
174
|
summary: Return struct results from ActiveRecord relation queries
|
173
175
|
test_files:
|
176
|
+
- spec/active_record_base_spec.rb
|
174
177
|
- spec/active_record_helper/economic_school.rb
|
175
178
|
- spec/active_record_helper/economist.rb
|
176
179
|
- spec/active_record_helper/schema.rb
|
177
180
|
- spec/active_record_helper/setup.rb
|
181
|
+
- spec/active_record_relation_spec.rb
|
178
182
|
- spec/relation_to_struct_spec.rb
|
179
183
|
- spec/spec_helper.rb
|
184
|
+
has_rdoc:
|