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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 4f506a9925d4e81450d698169f04c60b69ca2191e1a353bbaa13b76a0ca48055
4
- data.tar.gz: c2e9f14973dfdab36c1e49da800f4ae2e6e571e567338ebcc2be627f114938c3
2
+ SHA1:
3
+ metadata.gz: 219be87ab80103dce585c2c71a8be92dc8274448
4
+ data.tar.gz: aacb51c639d27d3348695c075a9e1e12c27fe915
5
5
  SHA512:
6
- metadata.gz: 73d9bf9e990411871d8ed7d4f7a6a7d803aab52466387000441bcd777a35073ec6a6a9f5ff878e96e35d6ce469e5ddfcae5ec3de625b0024b5f6522dce1c8c9b
7
- data.tar.gz: 75e21099aa4181e6fec9b7aa5919dd964ad271b4d20e0908f32de8fd7b6b150a7647162b946f376473303aa7d11bfc58a1132d91db19fa6a18f4f23a014bfd36
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.convert). This is to allow to fill in
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.convert(records, into:)
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
- converter = StructConverter.for(attributes: hsh.keys)
9
- records.map { |record| converter.convert(record) }
10
- else
11
- records.map { |record| into.new(record) }
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
- public
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
@@ -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
- Rows.new(records)
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
- if limit
107
- # To really make sense limit must be implemented using window
108
- # functions, because one (or, at lieast, I) would expect this code
109
- #
110
- # organizations = SQL.all "SELECT * FROM organizations", into: Hash
111
- # organizations.preload :users, limit: 2, order_by: "id DESC"
112
- #
113
- # to return up to two users **per organization**.
114
- #
115
- raise "Support for limit: is not implemented yet!"
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::Rows
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.convert(records, into: @target_type) if @target_type != Hash
68
-
67
+ records = RowConverter.convert_ary(records, into: @target_type) if @target_type != Hash
69
68
  replace(records)
70
69
  end
71
70
  end
@@ -1,5 +1,5 @@
1
1
  module Simple
2
2
  module SQL
3
- VERSION = "0.4.11"
3
+ VERSION = "0.4.12"
4
4
  end
5
5
  end
@@ -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.11
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-10 00:00:00.000000000 Z
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.7.7
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