simple-sql 0.4.11 → 0.4.12

Sign up to get free protection for your applications and to get access to all the features.
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