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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f14569badb2ed3049d91f233ebc208c09bef0251
4
- data.tar.gz: 3513ef3efd8d057645db818927a1939338e75f17
3
+ metadata.gz: 38b9e2235461124930a00545c8f280a149e720e2
4
+ data.tar.gz: e554b29f4b015da645780962aa44985d751ec177
5
5
  SHA512:
6
- metadata.gz: 87b088344c4b07d36ef607bd145a6a4480b6397e07c47b3495e40462d7bf9a4ac564e4590d4addd01deae8353c7abd757ac60fbdd5ded1b2a22e3e71355fa37e
7
- data.tar.gz: 70b3761d903aee08b8a36a3fd16dc53b8bfb416b23dc8445e26421d38ec289d9e6cc548f1dcda139fba0be407f12a4d90aaccb1632d142394a83954d3815818b
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
- ## Usage
23
+ ## Examples
24
24
 
25
- TODO: Write usage instructions here
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/[my-github-username]/relation_to_struct/fork )
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 != struct_class.members.size
9
- raise ArgumentError, 'Expected struct fields and columns lengths to be equal'
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.size != result.column_types.size
13
- raise ArgumentError, 'Expected unique column names count and column count to be equal'
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.column_types.size
15
- raise ArgumentError, 'Expected unique column names count and column count to be equal'
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)
@@ -1,3 +1,3 @@
1
1
  module RelationToStruct
2
- VERSION = "0.0.5"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -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.5
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: 2015-10-06 00:00:00.000000000 Z
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.4.5
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: