simple-sql 0.4.11 → 0.4.12
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 +5 -5
- data/lib/simple/sql/connection_adapter.rb +1 -1
- data/lib/simple/sql/helpers/row_converter.rb +53 -12
- data/lib/simple/sql/result.rb +45 -3
- data/lib/simple/sql/result/association_loader.rb +10 -11
- data/lib/simple/sql/result/records.rb +2 -3
- data/lib/simple/sql/version.rb +1 -1
- data/spec/simple/sql_associations_spec.rb +39 -4
- metadata +3 -4
- data/lib/simple/sql/result/rows.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 219be87ab80103dce585c2c71a8be92dc8274448
|
4
|
+
data.tar.gz: aacb51c639d27d3348695c075a9e1e12c27fe915
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f8250eac83d27aa869fdc01814f1c565e71470d8805b876eac12c875466f780341259ee9e6f10f84e27a0f3dada384a2044c109267c5c8bcf34d4eb2d14ffcd
|
7
|
+
data.tar.gz: 4260387271f5c4ab8e25d6bdc4d457a94412453598f0e4ce876800a89db0862871950efe9849ed1f5823c1bce8727f47d81df81f4c28ed4c0851c8cefe6c8405
|
@@ -41,7 +41,7 @@ module Simple::SQL::ConnectionAdapter
|
|
41
41
|
#
|
42
42
|
# Even if into is set to something different than a Hash, we'll convert
|
43
43
|
# each row into a Hash initially, and only later convert it to the final
|
44
|
-
# target type (via RowConverter.
|
44
|
+
# target type (via RowConverter.convert_ary). This is to allow to fill in
|
45
45
|
# more entries later on.
|
46
46
|
records = enumerate(pg_result, into: into)
|
47
47
|
|
@@ -1,14 +1,46 @@
|
|
1
|
-
module Simple::SQL::Helpers::RowConverter
|
1
|
+
module Simple::SQL::Helpers::RowConverter # :private:
|
2
|
+
SELF = self
|
3
|
+
|
2
4
|
# returns an array of converted records
|
3
|
-
def self.
|
5
|
+
def self.convert_ary(records, into:)
|
4
6
|
hsh = records.first
|
5
7
|
return records unless hsh
|
6
8
|
|
7
|
-
if into == :struct
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
converter = if into == :struct
|
10
|
+
StructConverter.for(attributes: hsh.keys)
|
11
|
+
else
|
12
|
+
TypeConverter.for(type: into)
|
13
|
+
end
|
14
|
+
|
15
|
+
records.map { |record| converter.convert_ary(record) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.convert(record, into:) # :nodoc:
|
19
|
+
ary = convert_ary([record], into: into)
|
20
|
+
ary.first
|
21
|
+
end
|
22
|
+
|
23
|
+
class TypeConverter #:nodoc:
|
24
|
+
def self.for(type:)
|
25
|
+
new(type: type)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(type:)
|
29
|
+
@type = type
|
30
|
+
end
|
31
|
+
|
32
|
+
def convert_ary(hsh)
|
33
|
+
updates = {}
|
34
|
+
hsh.each do |key, value|
|
35
|
+
case value
|
36
|
+
when Hash then updates[key] = SELF.convert(value, into: @type)
|
37
|
+
when Array then updates[key] = SELF.convert_ary(value, into: @type)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
hsh = hsh.merge(updates)
|
42
|
+
|
43
|
+
@type.new hsh
|
12
44
|
end
|
13
45
|
end
|
14
46
|
|
@@ -18,16 +50,25 @@ module Simple::SQL::Helpers::RowConverter
|
|
18
50
|
@cache[attributes] ||= new(attributes)
|
19
51
|
end
|
20
52
|
|
21
|
-
private
|
22
|
-
|
23
53
|
def initialize(attributes)
|
24
54
|
@klass = Struct.new(*attributes)
|
25
55
|
end
|
26
56
|
|
27
|
-
|
28
|
-
|
29
|
-
def convert(hsh)
|
57
|
+
def convert_ary(hsh)
|
30
58
|
values = hsh.values_at(*@klass.members)
|
59
|
+
updates = {}
|
60
|
+
|
61
|
+
values.each_with_index do |value, idx|
|
62
|
+
case value
|
63
|
+
when Hash then updates[idx] = SELF.convert(value, into: :struct)
|
64
|
+
when Array then updates[idx] = SELF.convert_ary(value, into: :struct)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
updates.each do |idx, updated_value|
|
69
|
+
values[idx] = updated_value
|
70
|
+
end
|
71
|
+
|
31
72
|
@klass.new(*values)
|
32
73
|
end
|
33
74
|
end
|
data/lib/simple/sql/result.rb
CHANGED
@@ -1,27 +1,69 @@
|
|
1
|
+
# rubocop:disable Metrics/AbcSize
|
2
|
+
# rubocop:disable Naming/AccessorMethodName
|
3
|
+
|
1
4
|
require_relative "helpers"
|
2
5
|
|
3
6
|
class ::Simple::SQL::Result < Array
|
4
7
|
end
|
5
8
|
|
6
|
-
require_relative "result/rows"
|
7
9
|
require_relative "result/records"
|
8
10
|
|
9
11
|
# The result of SQL.all
|
10
12
|
#
|
11
|
-
# This class implements the interface of a Result.
|
13
|
+
# This class implements the basic interface of a Result set. Record result sets
|
14
|
+
# support the conversion of a record into a custom type of the callers choice,
|
15
|
+
# via the :into option for <tt>SQL.all</tt> and <tt>SQL.ask</tt>.
|
16
|
+
#
|
17
|
+
#
|
12
18
|
class ::Simple::SQL::Result < Array
|
13
19
|
# A Result object is requested via ::Simple::SQL::Result.build, which then
|
14
20
|
# chooses the correct implementation, based on the <tt>target_type:</tt>
|
15
21
|
# parameter.
|
16
22
|
def self.build(records, target_type:, pg_source_oid:) # :nodoc:
|
17
23
|
if target_type.nil?
|
18
|
-
|
24
|
+
new(records)
|
19
25
|
else
|
20
26
|
Records.new(records, target_type: target_type, pg_source_oid: pg_source_oid)
|
21
27
|
end
|
22
28
|
end
|
23
29
|
|
30
|
+
def initialize(records) # :nodoc:
|
31
|
+
replace(records)
|
32
|
+
end
|
33
|
+
|
34
|
+
# returns the total_count of search hits
|
35
|
+
#
|
36
|
+
# This is filled in when resolving a paginated scope.
|
24
37
|
attr_reader :total_count
|
38
|
+
|
39
|
+
# returns the total number of pages of search hits
|
40
|
+
#
|
41
|
+
# This is filled in when resolving a paginated scope. It takes
|
42
|
+
# into account the scope's "per" option.
|
25
43
|
attr_reader :total_pages
|
44
|
+
|
45
|
+
# returns the current page number in a paginated search
|
46
|
+
#
|
47
|
+
# This is filled in when resolving a paginated scope.
|
26
48
|
attr_reader :current_page
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def set_pagination_info(scope)
|
53
|
+
raise ArgumentError, "per must be > 0" unless scope.per > 0
|
54
|
+
|
55
|
+
if scope.page <= 1 && empty?
|
56
|
+
# This branch is an optimization: the call to the database to count is
|
57
|
+
# not necessary if we know that there are not even any results on the
|
58
|
+
# first page.
|
59
|
+
@total_count = 0
|
60
|
+
@current_page = 1
|
61
|
+
else
|
62
|
+
sql = "SELECT COUNT(*) FROM (#{scope.order_by(nil).to_sql(pagination: false)}) simple_sql_count"
|
63
|
+
@total_count = ::Simple::SQL.ask(sql, *scope.args)
|
64
|
+
@current_page = scope.page
|
65
|
+
end
|
66
|
+
|
67
|
+
@total_pages = (@total_count * 1.0 / scope.per).ceil
|
68
|
+
end
|
27
69
|
end
|
@@ -103,17 +103,16 @@ module ::Simple::SQL::Result::AssociationLoader # :nodoc:
|
|
103
103
|
|
104
104
|
# preloads a has_one or has_many association.
|
105
105
|
def preload_has_one_or_many(records, relation, as:, order_by:, limit:)
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
106
|
+
# To really make sense limit must be implemented using window
|
107
|
+
# functions, because one (or, at lieast, I) would expect this code
|
108
|
+
#
|
109
|
+
# organizations = SQL.all "SELECT * FROM organizations", into: Hash
|
110
|
+
# organizations.preload :users, limit: 2, order_by: "id DESC"
|
111
|
+
#
|
112
|
+
# to return up to two users **per organization**.
|
113
|
+
#
|
114
|
+
raise "Support for limit: is not implemented yet!" if limit
|
115
|
+
raise "Support for order_by: is not implemented yet!" if order_by && as.to_s.singularize == as.to_s
|
117
116
|
|
118
117
|
belonging_column = relation.belonging_column.to_sym
|
119
118
|
having_column = relation.having_column.to_sym
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative "association_loader"
|
4
4
|
|
5
|
-
class ::Simple::SQL::Result::Records < ::Simple::SQL::Result
|
5
|
+
class ::Simple::SQL::Result::Records < ::Simple::SQL::Result
|
6
6
|
def initialize(records, target_type:, pg_source_oid:) # :nodoc:
|
7
7
|
expect! records.first => Hash unless records.empty?
|
8
8
|
|
@@ -64,8 +64,7 @@ class ::Simple::SQL::Result::Records < ::Simple::SQL::Result::Rows
|
|
64
64
|
|
65
65
|
def materialize
|
66
66
|
records = @hash_records
|
67
|
-
records = RowConverter.
|
68
|
-
|
67
|
+
records = RowConverter.convert_ary(records, into: @target_type) if @target_type != Hash
|
69
68
|
replace(records)
|
70
69
|
end
|
71
70
|
end
|
data/lib/simple/sql/version.rb
CHANGED
@@ -66,7 +66,7 @@ describe "Simple::SQL::Result#preload" do
|
|
66
66
|
it "detects a has_one association" do
|
67
67
|
organizations = SQL.all "SELECT * FROM organizations", into: Hash
|
68
68
|
organizations.preload :user
|
69
|
-
|
69
|
+
|
70
70
|
organization = organizations.first
|
71
71
|
users_of_organization = SQL.all "SELECT * FROM users WHERE organization_id=$1", organization[:id], into: Hash
|
72
72
|
expect(users_of_organization).to include(organization[:user])
|
@@ -94,13 +94,48 @@ describe "Simple::SQL::Result#preload" do
|
|
94
94
|
organizations = SQL.all "SELECT * FROM organizations", into: Hash
|
95
95
|
organizations.preload :user, as: :usr
|
96
96
|
expect(organizations.first.keys).not_to include(:user)
|
97
|
-
|
97
|
+
|
98
98
|
organization = organizations.first
|
99
99
|
users_of_organization = SQL.all "SELECT * FROM users WHERE organization_id=$1", organization[:id], into: Hash
|
100
100
|
expect(users_of_organization).to include(organization[:usr])
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
+
|
105
|
+
describe ":into option" do
|
106
|
+
it "creates a :struct for singular association" do
|
107
|
+
users = SQL.all "SELECT * FROM users WHERE organization_id=$1", org1.id, into: :struct
|
108
|
+
users.preload :organization
|
109
|
+
|
110
|
+
expect(users.first.class.superclass).to eq(Struct)
|
111
|
+
expect(users.first.organization.class.superclass).to eq(Struct)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "creates a :struct for array associations" do
|
115
|
+
organizations = SQL.all "SELECT * FROM organizations", into: :struct
|
116
|
+
organizations.preload :users
|
117
|
+
expect(organizations.first.class.superclass).to eq(Struct)
|
118
|
+
|
119
|
+
expect(organizations.first.users.first.class.superclass).to eq(Struct)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "creates a OpenStruct for singular association" do
|
123
|
+
users = SQL.all "SELECT * FROM users WHERE organization_id=$1", org1.id, into: OpenStruct
|
124
|
+
users.preload :organization
|
125
|
+
|
126
|
+
expect(users.first).to be_a(OpenStruct)
|
127
|
+
expect(users.first.organization).to be_a(OpenStruct)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "creates a OpenStruct for array associations" do
|
131
|
+
organizations = SQL.all "SELECT * FROM organizations", into: OpenStruct
|
132
|
+
organizations.preload :users
|
133
|
+
expect(organizations.first).to be_a(OpenStruct)
|
134
|
+
|
135
|
+
expect(organizations.first.users.first).to be_a(OpenStruct)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
104
139
|
describe ":order_by" do
|
105
140
|
it "supports order_by" do
|
106
141
|
organizations = SQL.all "SELECT * FROM organizations", into: Hash
|
@@ -110,11 +145,11 @@ describe "Simple::SQL::Result#preload" do
|
|
110
145
|
ordered_user_ids = SQL.all("SELECT id FROM users WHERE organization_id=$1 ORDER BY id", organizations.first[:id])
|
111
146
|
expect(users.pluck(:id)).to eq(ordered_user_ids)
|
112
147
|
end
|
113
|
-
|
148
|
+
|
114
149
|
it "supports order_by DESC" do
|
115
150
|
organizations = SQL.all "SELECT * FROM organizations", into: Hash
|
116
151
|
organizations.preload :users, order_by: "id DESC"
|
117
|
-
users = organizations.first[:users]
|
152
|
+
users = organizations.first[:users]
|
118
153
|
|
119
154
|
ordered_user_ids = SQL.all("SELECT id FROM users WHERE organization_id=$1 ORDER BY id", organizations.first[:id])
|
120
155
|
expect(users.pluck(:id)).to eq(ordered_user_ids.reverse)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple-sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- radiospiel
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-08-
|
12
|
+
date: 2018-08-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pg_array_parser
|
@@ -196,7 +196,6 @@ files:
|
|
196
196
|
- lib/simple/sql/result.rb
|
197
197
|
- lib/simple/sql/result/association_loader.rb
|
198
198
|
- lib/simple/sql/result/records.rb
|
199
|
-
- lib/simple/sql/result/rows.rb
|
200
199
|
- lib/simple/sql/scope.rb
|
201
200
|
- lib/simple/sql/scope/filters.rb
|
202
201
|
- lib/simple/sql/scope/order.rb
|
@@ -244,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
244
243
|
version: '0'
|
245
244
|
requirements: []
|
246
245
|
rubyforge_project:
|
247
|
-
rubygems_version: 2.
|
246
|
+
rubygems_version: 2.5.1
|
248
247
|
signing_key:
|
249
248
|
specification_version: 4
|
250
249
|
summary: SQL with a simple interface
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# rubocop:disable Metrics/AbcSize
|
2
|
-
# rubocop:disable Naming/AccessorMethodName
|
3
|
-
|
4
|
-
class ::Simple::SQL::Result::Rows < ::Simple::SQL::Result
|
5
|
-
def initialize(records)
|
6
|
-
replace(records)
|
7
|
-
end
|
8
|
-
|
9
|
-
# -- pagination info ------------------------------------------------------
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def set_pagination_info(scope)
|
14
|
-
raise ArgumentError, "per must be > 0" unless scope.per > 0
|
15
|
-
|
16
|
-
if scope.page <= 1 && empty?
|
17
|
-
# This branch is an optimization: the call to the database to count is
|
18
|
-
# not necessary if we know that there are not even any results on the
|
19
|
-
# first page.
|
20
|
-
@total_count = 0
|
21
|
-
@current_page = 1
|
22
|
-
else
|
23
|
-
sql = "SELECT COUNT(*) FROM (#{scope.order_by(nil).to_sql(pagination: false)}) simple_sql_count"
|
24
|
-
@total_count = ::Simple::SQL.ask(sql, *scope.args)
|
25
|
-
@current_page = scope.page
|
26
|
-
end
|
27
|
-
|
28
|
-
@total_pages = (@total_count * 1.0 / scope.per).ceil
|
29
|
-
end
|
30
|
-
end
|