simple-sql 0.5.37 → 0.9.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
  SHA256:
3
- metadata.gz: eb13412c57185b5bd356c4dc94f52232b22bdef58e032d67ceb79127811ec639
4
- data.tar.gz: adc67bad73619d640834d8b821676bfbc5530ca003adfcabe3012535350cea39
3
+ metadata.gz: 75d235ac454cec6f58662af61d97c7fddf980da80c8bb6f9369db3f0e28e5c68
4
+ data.tar.gz: 6f047d1c86fdc3308d0b4f32616640d9c083c9bcab2360539ecf16d940bbf1da
5
5
  SHA512:
6
- metadata.gz: 1c5597472257a4b487c2b9b1a87545cb37d70e1e625cdd31ced80d3aaf650f547ccd6931cac3e9e81e97df25caaa6fe3be519ae905ea206b5a573717fcbc3a18
7
- data.tar.gz: a69336914733676aa4465489b71576b1274284337d9295cc786d5e050a72942fe8b54f90cb6eccad7165e8f24ad635e6376342e49dd63bf4d2cbe174fc173620
6
+ metadata.gz: d7e366a2dd129d99b3ca48fb9b1bd49e37f7bb403f97b7c2fcc87c7f305417fc23ba333e76bbfa457fe57e27bf2df707c2214adf75382ae280cdde9f9c155b34
7
+ data.tar.gz: 07ed66bd39a8c7db53807ad0f54bab797d6a72d6ee63f38a011e5da753ba170ff865fb00705eee82835d53b8fbdd5c97cc494926f2944ec5089972db4897d309
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ Gemfile.lock
8
8
  .rspec.status
9
9
  .DS_Store
10
10
  tmp
11
+ log/integration_tests.log
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.3
2
+ TargetRubyVersion: 3.2
3
+ NewCops: disable
3
4
  Exclude:
4
5
  - 'spec/**/*'
5
6
  - 'test/**/*'
@@ -79,3 +80,7 @@ Style/TrailingUnderscoreVariable:
79
80
 
80
81
  Style/StderrPuts:
81
82
  Enabled: false
83
+
84
+ # https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/HashSyntax
85
+ Style/HashSyntax:
86
+ EnforcedShorthandSyntax: never
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7
1
+ 3.3.8
data/Gemfile CHANGED
@@ -1,7 +1,11 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'pry-byebug'
4
- gem 'rubocop', '~> 0.57.2'
4
+ gem 'rubocop', '~> 1.54.1'
5
+
6
+ gem 'rake', '>= 12.3.3'
7
+ gem 'rspec', '~> 3.7'
8
+ gem 'simplecov', '~> 0'
5
9
 
6
10
  # Specify your gem's dependencies in {gemname}.gemspec
7
11
  gemspec
data/Makefile CHANGED
@@ -1,16 +1,8 @@
1
- tests: test4 test5 test6
1
+ default:
2
+ bundle exec rspec
2
3
 
3
- test4:
4
- SIMPLE_SQL_ACTIVERECORD_SPECS="> 4,< 5" bundle
5
- rspec
6
-
7
- test5:
8
- SIMPLE_SQL_ACTIVERECORD_SPECS="> 5,< 6" bundle update activerecord
9
- rspec
10
-
11
- test6:
12
- SIMPLE_SQL_ACTIVERECORD_SPECS="> 6,< 7" bundle update activerecord
13
- rspec
4
+ tests:
5
+ ./scripts/integration_tests
14
6
 
15
7
  stats:
16
8
  @scripts/stats lib/simple/sql
data/README.md CHANGED
@@ -1,85 +1,112 @@
1
1
  # simple-sql
2
2
 
3
- The simple-sql gem defines a module `Simple::SQL`, which you can use to execute
4
- SQL statements on a Postgresql database. Care has been taken to provide the
5
- simplest interface we could come up with.
3
+ The simple-sql gem defines a module `Simple::SQL`, which you can use to execute SQL statements on a Postgresql database. Care has been taken to provide the simplest interface we could come up with.
6
4
 
7
- **Note:** Databases other than Postgresql are not supported, and there are no
8
- plans to do so. If you deem that necessary, feel free to fork this code into a
9
- `simple-sql-<yourdatabase>` gem to provide the same or a similar interface.
5
+ However, apart from providing a simple interface `simple-sql` also provides a huge performance boon over `ActiveRecord` when interacting with your database, by cutting out all the `ActiveRecord` layers between the `pg` gem and you ruby code. However, `simple-sql` is still using ActiveRecord to manage connections.
10
6
 
11
- ## Installation
7
+ **Note:** Databases other than Postgresql are not supported, and there are no plans to do so.
8
+
9
+ <!-- TOC -->
12
10
 
13
- The gem is available on rubygems as `simple-sql`. To use it add the following
14
- line to your `Gemfile` and bundle us usual:
11
+ ## Installation
15
12
 
16
- gem 'simple-sql' # + version
13
+ The gem is available on rubygems as `simple-sql`.
17
14
 
18
- ## Usage
15
+ Also make sure to add the `pg` gem to your Gemfile. `simple-sql` should be pretty flexible about the `pg` version: the earliest supported version is `0.21`.
19
16
 
20
- ### Connecting to a database
17
+ To use it add the following lines to your `Gemfile` and bundle up usual:
21
18
 
22
- Before you can send SQL commands to a database you need to connect first. `simple-sql`
23
- gives you the following options:
19
+ ```ruby
20
+ gem 'simple-sql' # + version
21
+ gem 'pg' # + version
22
+ ```
24
23
 
25
- 1. **Use the current ActiveRecord connection:** when running inside a Rails application
26
- you typically have a connection to Postgresql configured already. In that case you
27
- don't need to do anything; `simple-sql` will just use the current database connection.
28
-
29
- This is usually the right thing, especially since `simple-sql`, when called from inside
30
- a controller action, is using the connection valid in the current context.
24
+ ## Connecting to a database
31
25
 
32
- 2. **Explicitely connect** on standalone applications you need to connect to a Postgresql
33
- server. **Note that in this case there are no pooled connections!** simple-sql is not
34
- thread-/fiber-safe in this mode.
26
+ Before you can send SQL commands to a database you need to connect first. `simple-sql` gives you the following options:
35
27
 
36
- You can explictely connect to a server by calling
28
+ ### Explicitly connect to the database
37
29
 
38
- ::Simple::SQL.connect! "postgres://user:password@server[:port]/database"
30
+ In a standalone applications you need to connect to a Postgresql server. `simple-sql` uses ActiveRecord's connection handling.
39
31
 
40
- Alternatively, you can have `simple-sql` figure out the details automatically:
32
+ You can explicitly connect to a server by calling
41
33
 
42
- ::Simple::SQL.connect!
34
+ ```ruby
35
+ db = ::Simple::SQL.connect! "postgres://user:password@server[:port]/database"
36
+ ```
43
37
 
44
- In that case we try to find connection parameters in the following places:
45
-
46
- - the `DATABASE_URL` environment value
47
- - the `config/database.yml` file from the current directory, taking `RAILS_ENV`/`RACK_ENV` into account.
38
+ You can then use `db.ask`, `db.all`, etc. (See below)
48
39
 
49
- ### Using placeholders
40
+ ### Automatically determine connection details
50
41
 
51
- Note: whenever you run a query `simple-sql` takes care of sending query parameters over the wire properly. That means that you use placeholders `$1`, `$2`, etc. to use these inside your queries; the following is a correct example:
42
+ Alternatively, you can have `simple-sql` figure out the details automatically:
52
43
 
53
44
  ```ruby
54
- Simple::SQL.all "SELECT * FROM users WHERE email=$1", "foo@bar.local"
45
+ db = ::Simple::SQL.connect!
55
46
  ```
56
47
 
57
- Also note that it is not possible to use an array as the argument for the `IN(?)` SQL construct. Instead you want to use `ANY`, for example:
48
+ In that case we try to find connection parameters in the following places:
49
+
50
+ - the `DATABASE_URL` environment value
51
+ - the `config/database.yml` file from the current directory, taking `RAILS_ENV`/`RACK_ENV` into account.
52
+
53
+ ### Using the default connection
54
+
55
+ You don't need to call `::Simple::SQL.connect!` inside a Rails application. `simple-sql` automatically uses the default connection when using `Simple::SQL` instead of an explicit database handle; i.e. typically you can use
58
56
 
59
57
  ```ruby
60
- Simple::SQL.all "SELECT * FROM users WHERE id = ANY($1)", [1,2,3]
58
+ Simple::SQL.ask "SELECT ..."
61
59
  ```
62
60
 
61
+ and `simple-sql` will be doing the right thing.
62
+
63
+ This is especially true in a Rails' applications controller action, because it allows you to mix Simple::SQL and ActiveRecord interactions with the database.
64
+
65
+ ## Simple queries
66
+
63
67
  ### Simple::SQL.all: Fetching all results of a query
64
68
 
65
69
  `Simple::SQL.all` runs a query, with optional arguments, and returns the result. Usage example:
66
70
 
67
- Simple::SQL.all "SELECT id, email FROM users WHERE id = ANY($1)", [1,2,3]
71
+ ```ruby
72
+ # returns an array of arrays `[ <id>, <email> ]`.
73
+ Simple::SQL.all "SELECT id, email FROM users WHERE id = ANY($1)", [1,2,3]
74
+ ```
75
+
76
+ If a block is passed to SQL.all, each row is yielded into the block:
77
+
78
+ ```ruby
79
+ Simple::SQL.all "SELECT id, email FROM users" do |id, email|
80
+ # do something
81
+ end
82
+ ```
68
83
 
69
- If the SQL query returns rows with one column, this method returns an array of these values.
70
- Otherwise it returns an array of arrays.
84
+ Typically the SQL query returns an array of arrays (1 per row). If, however, the query is set up to only return a single column, this method returns an array of these values instead. This is especially useful with the block version:
71
85
 
72
86
  Examples:
73
87
 
74
88
  ```ruby
75
89
  Simple::SQL.all("SELECT id FROM users") # returns an array of id values, but
76
- Simple::SQL.all("SELECT id, email FROM users") # returns an array of arrays `[ <id>, <email> ]`.
90
+
91
+ Simple::SQL.all "SELECT id FROM users" do |id|
92
+ # do something with the users' ids.
93
+ end
77
94
  ```
78
95
 
79
- If a block is passed to SQL.all, each row is yielded into the block:
96
+ ### Simple::SQL.each: run a block with each result of a query
97
+
98
+ This is identical to `.all`. It might offer beneficial optimisations, but this is theoretical at this point.
80
99
 
81
100
  ```ruby
82
- Simple::SQL.all "SELECT id, email FROM users" do |id, email|
101
+ Simple::SQL.each "SELECT id, email FROM users" do |id, email|
102
+ # do something
103
+ end
104
+ ```
105
+
106
+ ### Simple::SQL.print: Printing results of a query
107
+
108
+ ```ruby
109
+ Simple::SQL.print "SELECT id, email FROM users" do |id, email|
83
110
  # do something
84
111
  end
85
112
  ```
@@ -88,9 +115,13 @@ end
88
115
 
89
116
  `Simple::SQL.ask` runs a query, with optional arguments, and returns the first result row or nil, if there was no result.
90
117
 
91
- Simple::SQL.ask "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1", [1,2,3]
118
+ ```ruby
119
+ Simple::SQL.ask "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1", [1,2,3]
120
+ ```
121
+
122
+ If the SQL query only returns a single column, this method returns the column value of the first row; otherwise it returns an array (or `nil` if there was no result).
92
123
 
93
- If the SQL query returns rows with one column, this method returns the column value of the first row; otherwise it returns an array (or `nil` if there was no result).
124
+ Usually all attributes are converted to its corresponding ruby type.
94
125
 
95
126
  Examples:
96
127
 
@@ -99,75 +130,146 @@ Simple::SQL.ask "SELECT id FROM users WHERE email=$1", "foo@local" # ret
99
130
  Simple::SQL.ask "SELECT id, email FROM users WHERE email=$?", "foo@local" # returns an array `[ <id>, <email> ]` (or `nil`)
100
131
  ```
101
132
 
102
- ### Simple::SQL.ask/Simple::SQL.all: fetching hashes
133
+ ### Using placeholders
134
+
135
+ Note: whenever you run a query `simple-sql` takes care of sending query parameters over the wire properly. That means that you use placeholders `$1`, `$2`, etc. to use these inside your queries; the following is a correct example:
136
+
137
+ ```ruby
138
+ Simple::SQL.all "SELECT * FROM users WHERE email=$1", "foo@bar.local"
139
+ ```
140
+
141
+ Usually arguments are converted correctly when sending over to the database. One notable exception is sending `jsonb` data - you must use JSON.encode on the argument:
142
+
143
+ ```ruby
144
+ Simple::SQL.ask "INSERT INTO table (column) VALUES($1)", JSON.encode(..)
145
+ ```
146
+
147
+ It is probably worth pointing out that you cannotto use an array as the argument for the `IN(?)` SQL construct. Instead you want to use `ANY`, for example:
148
+
149
+ ```ruby
150
+ Simple::SQL.all "SELECT * FROM users WHERE id = ANY($1)", [1,2,3]
151
+ ```
152
+
153
+ ### Determining the result type
154
+
155
+ By default `ask` and `all` convert each result row into an Array. Sometimes you might want to use Hashes or similar objects instead. To do so, you use the `into:` keyword argument:
156
+
157
+ ```ruby
158
+ # returns a single Hash (or nil)
159
+ Simple::SQL.ask("SELECT id FROM users", into: Hash)
160
+ ```
161
+
162
+ If you want the returned record to be in a structure which is not a Hash, you can use the `into: <klass>` option. The following would return an array of up to two `OpenStruct` objects:
103
163
 
104
- While `ask` and `all` convert each result row into an Array, sometimes you might want
105
- to use Hashes or similar objects instead. To do so, you use the `into:` keyword argument:
164
+ ```ruby
165
+ sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1",
166
+ Simple::SQL.all sql, [1,2,3], into: OpenStruct
167
+ ```
106
168
 
107
- # returns a single Hash (or nil)
108
- Simple::SQL.ask("SELECT id FROM users", into: Hash)
169
+ This supports all target types that take a constructor accepting Hash arguments.
109
170
 
110
- If you want the returned record to be in a structure which is not a Hash, you can use
111
- the `into: <klass>` option. The following would return an array of up to two `OpenStruct`
112
- objects:
171
+ It also supports a `:struct` argument, in which case simple-sql creates uses a Struct-class. Struct classes are reused when possible, and are maintained by `Simple::SQL`. This is potentially the best performing option when you want to use dot-notation.
113
172
 
114
- sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1",
115
- Simple::SQL.all sql, [1,2,3], into: OpenStruct
173
+ ```ruby
174
+ sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1",
175
+ Simple::SQL.all sql, [1,2,3], into: :struct
176
+ ```
116
177
 
117
- This supports all target types that take a contructor which acceps Hash arguments.
178
+ ### Running non-`SELECT` queries
118
179
 
119
- It also supports a :struct argument, in which case simple-sql creates uses a Struct-class.
120
- Struct classes are reused when possible, and are maintained by Simple::SQL.
180
+ You can use `all` and `ask` with other queries as well. However, both only support running a single query. If you want to run multiple queries (i.e. a SQL script) you would probably look into `Simple::SQL.exec` instead.
121
181
 
122
- sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1",
123
- Simple::SQL.all sql, [1,2,3], into: :struct
182
+ Be aware that `Simple::SQL.exec` does not support placeholders: you cannot pass in arguments into `Simple::SQL.exec`.
124
183
 
125
184
  ### Transaction support
126
185
 
127
- `simple-sql` has limited support for nested transactions. When running with a ActiveRecord
128
- connection, we use ActiveRecord's transaction implementation (which uses savepoints for nested
129
- transactions, so you might be able to rollback from inside a nested transaction).
186
+ `simple-sql` borrows transaction support from `ActiveRecord`.
130
187
 
131
- When connecting via `Simple::SQL.connect!` we do not support the same level of nesting support (yet). You can still nest transactions, but raising an error terminates *all* current transactions.
188
+ ## Using scopes
132
189
 
133
- ## Logging
190
+ A scope lets you build a condition over time, like this:
191
+
192
+ ```ruby
193
+ scope = db.scope "SELECT * FROM users"
194
+ scope = scope.where(email: ["foo@foobar.test", "bar@foobar.test"])
195
+ scope = scope.where("deleted_at is NULL")
196
+ users = scope.all
197
+ ```
134
198
 
135
- `simple-sql` builds a logger which logs all queries. The logger, by default, is
136
- created to write to STDERR; to get another logger use code like
199
+ This also works with the default connection, via `scope = Simple::SQL.scope ...`.
137
200
 
138
- Simple::SQL.logger = Rails.logger
201
+ A scope supports the following methods to set up a scope:
139
202
 
140
- ## Bugs and Limitations
203
+ | | |
204
+ |---------------------------|---------------------------------------------|
205
+ | `where(args...)` | add additional conditions. `where` also has additional support for using `?` instead of `$nn`, and supports JSONB query conditions. See [where.rb](lib/simple/sql/connection/scope/where.rb) for details.|
206
+ | `order_by(sql_fragment)` ||
207
+ | `limit(limit)` ||
208
+ | `offset(offset)` ||
209
+ | `paginate(per:, page:)` | adds pagination (calls `limit` and `offset`) |
141
210
 
142
- ### 1. Multiple connections
143
211
 
144
- It is currently not possible to run SQL queries against a database which is not
145
- connected via ActiveRecord::Base.connection.
212
+ These methods can then be used to evaluate the scope:
146
213
 
147
- ### 2. Postgresql only
214
+ | | |
215
+ |---------------------------|---------------------------------------------|
216
+ | `all(into: ...)` | returns all matching entries |
217
+ | `first(into: ...)` | returns the first matching entry|
218
+ | `count` | returns the exact count of matching records|
219
+ | `count_estimate` | returns a fast estimate of the count of matching records. Note that this needs suitable and up-to-date indices.|
220
+ | `enumerate_groups(sql)` | returns all groups |
221
+ | `count_by(sql)` | counts by groups |
222
+ | `print` | print all matching entries|
223
+ | `explain` | returns the query plan|
148
224
 
149
- Only Postgresql is supported.
225
+ ## Inserting objects
150
226
 
151
- ### 3. Limited support for types
227
+ Inserting objects is much faster via `simple-sql`. You should be able to insert ~1000 or so records per second into a table with no trouble.
152
228
 
153
- This gem does not use `pg`'s support for encoding and decoding types, since
154
- that might probably interfere with how ActiveRecord is setting up the `pg`
155
- gem.
229
+ ```ruby
230
+ Simple::SQL.insert :users, first_name: "foo", last_name: "bar"
231
+ ```
232
+
233
+ ```ruby
234
+ users = []
235
+ users.push first_name: "first", last_name: "user"
236
+ users.push first_name: "second", last_name: "user"
237
+
238
+ Simple::SQL.insert :users, users
239
+ ```
240
+
241
+ The `.insert` method lets you set up conflict resolution, via
242
+
243
+ ```ruby
244
+ Simple::SQL.insert :users, users, on_conflict: :ignore
245
+ ```
246
+
247
+ ## Advisory Locks
248
+
249
+ ```ruby
250
+ Simple::SQL.transaction do
251
+ Simple::SQL.lock!(4711)
252
+
253
+ # do something.
254
+ end
255
+ ```
256
+
257
+ ## Logging
156
258
 
157
- It therefore assumes ActiveRecord is used in the same project, which sets up
158
- pg to not decode data in any meaningful way, and provides some code to decode
159
- the data returned from the database. Only a handful of types is currently
160
- supported by the Decoder - it is fairly easy to add new types, though.
259
+ `simple-sql` builds a logger which logs all queries. The logger, by default, is created to write to STDERR; to get another logger use code like
161
260
 
162
- ### 4. text arrays
261
+ ```ruby
262
+ Simple::SQL.logger = Rails.logger
263
+ ```
264
+
265
+ ## Bugs and Limitations
266
+
267
+ **Limited support for types:** This gem does not use `pg`'s support for encoding and decoding types, since that might probably interfere with how ActiveRecord is setting up the `pg` gem.
163
268
 
164
- The library used to parse array results seems to be buggy if the array contains
165
- strings containing the "`" character.
269
+ It therefore assumes ActiveRecord is used in the same project, which sets up pg to not decode data in any meaningful way, and provides some code to decode the data returned from the database. Only a handful of types is currently supported by the Decoder - it is fairly easy to add new types, though.
166
270
 
167
271
  ## Test
168
272
 
169
273
  1. `createdb simple-sql-test`
170
274
  2. `bundle install`
171
275
  3. `bin/rspec`
172
-
173
- ## Test again
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.37
1
+ 0.9.0
data/bin/db_restore CHANGED
@@ -3,7 +3,13 @@ require 'yaml'
3
3
 
4
4
  env = ENV["POSTJOB_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
5
5
 
6
- configs = YAML.load_file "config/database.yml"
6
+ path = "config/database.yml"
7
+ configs = if Psych::VERSION > '4.0'
8
+ YAML.safe_load(File.read(path), aliases: true)
9
+ else
10
+ YAML.safe_load(File.read(path), [], [], true)
11
+ end
12
+
7
13
  config = configs.fetch(env) { configs.fetch("defaults") }
8
14
 
9
15
  ENV["PGHOST"] = config["host"]
data/bin/pg CHANGED
@@ -3,7 +3,13 @@ require 'yaml'
3
3
 
4
4
  env = ENV["POSTJOB_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
5
5
 
6
- configs = YAML.load_file "config/database.yml"
6
+ path = "config/database.yml"
7
+ configs = if Psych::VERSION > '4.0'
8
+ YAML.safe_load(File.read(path), aliases: true)
9
+ else
10
+ YAML.safe_load(File.read(path), [], [], true)
11
+ end
12
+
7
13
  config = configs.fetch(env) { configs.fetch("defaults") }
8
14
 
9
15
  ENV["PGHOST"] = config["host"]
data/config/database.yml CHANGED
@@ -2,8 +2,8 @@ defaults: &defaults
2
2
  adapter: postgresql
3
3
  encoding: utf8
4
4
  host: '127.0.0.1'
5
- username: admin
6
- password: admin
5
+ # username: admin
6
+ # password: admin
7
7
  pool: 5
8
8
  timeout: 5000
9
9
 
@@ -16,7 +16,7 @@ module Simple::SQL::Config
16
16
 
17
17
  config = {
18
18
  dbname: uri.path.sub(%r{^/}, ""),
19
- host: uri.hostname
19
+ host: uri.hostname
20
20
  }
21
21
  config[:port] = uri.port if uri.port
22
22
  config[:user] = uri.user if uri.user
@@ -55,7 +55,12 @@ module Simple::SQL::Config
55
55
 
56
56
  def load_activerecord_base_configuration(path:, env:)
57
57
  require "yaml"
58
- database_config = YAML.load_file(path)
58
+ database_config = if Psych::VERSION > '4.0'
59
+ YAML.safe_load(File.read(path), aliases: true)
60
+ else
61
+ YAML.safe_load(File.read(path), [], [], true)
62
+ end
63
+
59
64
  env ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
60
65
 
61
66
  database_config[env] ||
@@ -81,6 +81,7 @@ class Simple::SQL::Connection
81
81
  # - <tt>Simple::SQL.ask "SELECT id, email FROM users WHERE email=$?", "foo@local"</tt>
82
82
  # returns an array <tt>[ <id>, <email> ]</tt> (or +nil+)
83
83
  def ask(sql, *args, into: nil)
84
+ # rubocop:disable Lint/UnreachableLoop
84
85
  catch(:ok) do
85
86
  each(sql, *args, into: into) { |row| throw :ok, row }
86
87
  nil
@@ -28,9 +28,9 @@ class Simple::SQL::Connection
28
28
 
29
29
  class Inserter
30
30
  CONFICT_HANDLING = {
31
- nil => "",
31
+ nil => "",
32
32
  :nothing => "ON CONFLICT DO NOTHING",
33
- :ignore => "ON CONFLICT DO NOTHING"
33
+ :ignore => "ON CONFLICT DO NOTHING"
34
34
  }
35
35
 
36
36
  #
@@ -89,6 +89,7 @@ class Simple::SQL::Connection
89
89
 
90
90
  def json_encode(value)
91
91
  return value unless value.is_a?(Hash) || value.is_a?(Array)
92
+
92
93
  JSON.generate(value)
93
94
  end
94
95
  end
@@ -1,5 +1,7 @@
1
1
  # rubocop:disable Metrics/ClassLength
2
2
 
3
+ require "ostruct"
4
+
3
5
  class Simple::SQL::Connection
4
6
  def reset_reflection
5
7
  @reflection = nil
@@ -57,7 +57,7 @@ module Simple::SQL::Connection::Scope::Search
57
57
  return scope if filters.empty?
58
58
 
59
59
  filters.inject(scope) do |scp, (k, v)|
60
- scp.where k => resolve_static_matches(v, column_type: column_types.fetch(k))
60
+ scp.where({ k => resolve_static_matches(v, column_type: column_types.fetch(k)) })
61
61
  end
62
62
  end
63
63
 
@@ -78,6 +78,7 @@ module Simple::SQL::Connection::Scope::Search
78
78
  def empty_filter?(_key, value)
79
79
  return true if value.nil?
80
80
  return true if value.is_a?(Enumerable) && value.empty? # i.e. Hash, Array
81
+
81
82
  false
82
83
  end
83
84
 
@@ -23,11 +23,9 @@ class Simple::SQL::Connection::Scope
23
23
  # scope = scope.where(metadata: { uid: 1 }, jsonb: false)
24
24
  #
25
25
  def where(sql_fragment, arg = :__dummy__no__arg, placeholder: "?", jsonb: true)
26
- duplicate.send(:where!, sql_fragment, arg, placeholder: placeholder, jsonb: jsonb)
26
+ duplicate.where!(sql_fragment, arg, placeholder: placeholder, jsonb: jsonb)
27
27
  end
28
28
 
29
- private
30
-
31
29
  def where!(first_arg, arg = :__dummy__no__arg, placeholder: "?", jsonb: true)
32
30
  if arg != :__dummy__no__arg
33
31
  where_sql_with_argument!(first_arg, arg, placeholder: placeholder)
@@ -40,6 +38,8 @@ class Simple::SQL::Connection::Scope
40
38
  self
41
39
  end
42
40
 
41
+ private
42
+
43
43
  def where_sql!(sql_fragment)
44
44
  @where << sql_fragment
45
45
  end
@@ -57,6 +57,7 @@ class Simple::SQL::Connection
57
57
 
58
58
  def disconnect!
59
59
  return unless @connection_class && @connection_class != ::ActiveRecord::Base
60
+
60
61
  @connection_class.remove_connection
61
62
  end
62
63
 
@@ -7,6 +7,7 @@ module Simple::SQL::Helpers::Decoder
7
7
  # rubocop:disable Metrics/AbcSize
8
8
  # rubocop:disable Metrics/CyclomaticComplexity
9
9
  # rubocop:disable Naming/UncommunicativeMethodParamName
10
+ # rubocop:disable Style/MultipleComparison
10
11
  def decode_value(type, s)
11
12
  case type
12
13
  when :unknown then s
@@ -18,8 +19,8 @@ module Simple::SQL::Helpers::Decoder
18
19
  when :'integer[]' then s.scan(/-?\d+/).map { |part| Integer(part) }
19
20
  when :"character varying[]" then parse_pg_array(s)
20
21
  when :"text[]" then parse_pg_array(s)
21
- when :"timestamp without time zone" then ::Time.parse(s)
22
- when :"timestamp with time zone" then ::Time.parse(s)
22
+ when :"timestamp without time zone" then decode_time(s)
23
+ when :"timestamp with time zone" then decode_time(s)
23
24
  when :hstore then HStore.parse(s)
24
25
  when :json then ::JSON.parse(s)
25
26
  when :jsonb then ::JSON.parse(s)
@@ -36,6 +37,12 @@ module Simple::SQL::Helpers::Decoder
36
37
  require "pg_array_parser"
37
38
  extend PgArrayParser
38
39
 
40
+ def decode_time(s)
41
+ return s if s.is_a?(Time)
42
+
43
+ ::Time.parse(s)
44
+ end
45
+
39
46
  # HStore parsing
40
47
  module HStore
41
48
  extend self
@@ -68,6 +75,7 @@ end
68
75
 
69
76
  module Simple::SQL::Helpers::Decoder
70
77
  def self.new(result, into:, column_info:)
78
+ # rubocop:disable Lint/ElseLayout
71
79
  if into == Hash then HashRecord.new(column_info)
72
80
  elsif result.nfields == 1 then SingleColumn.new(column_info)
73
81
  else MultiColumns.new(column_info)
@@ -27,7 +27,7 @@ module Simple::SQL::Helpers::RowConverter
27
27
  ary.first
28
28
  end
29
29
 
30
- class TypeConverter #:nodoc:
30
+ class TypeConverter # :nodoc:
31
31
  def initialize(type:, associations:)
32
32
  @type = type
33
33
  @associations = associations
@@ -57,7 +57,7 @@ module Simple::SQL::Helpers::RowConverter
57
57
  end
58
58
  end
59
59
 
60
- class ImmutableConverter < TypeConverter #:nodoc:
60
+ class ImmutableConverter < TypeConverter # :nodoc:
61
61
  Immutable = ::Simple::Immutable
62
62
 
63
63
  def build_row_in_target_type(hsh)
@@ -65,7 +65,7 @@ module Simple::SQL::Helpers::RowConverter
65
65
  end
66
66
  end
67
67
 
68
- class TypeConverter2 < TypeConverter #:nodoc:
68
+ class TypeConverter2 < TypeConverter # :nodoc:
69
69
  def initialize(type:, associations:, fq_table_name:)
70
70
  super(type: type, associations: associations)
71
71
  @fq_table_name = fq_table_name
@@ -1,5 +1,7 @@
1
1
  # This file contains some monkey patches
2
2
 
3
+ # rubocop:disable Lint/DuplicateMethods
4
+
3
5
  module Simple::SQL::MonkeyPatches
4
6
  def self.warn(msg)
5
7
  return if ENV["SIMPLE_SQL_SILENCE"] == "1"
@@ -29,6 +31,7 @@ when /^5.2/
29
31
  class ActiveRecord::ConnectionAdapters::ConnectionPool::Reaper
30
32
  def run
31
33
  return unless frequency && frequency > 0
34
+
32
35
  Simple::SQL::MonkeyPatches.warn "simple-sql disables reapers for all connection pools, see https://github.com/rails/rails/issues/33600"
33
36
  end
34
37
  end
@@ -45,6 +48,7 @@ when /^6/
45
48
  class ActiveRecord::ConnectionAdapters::ConnectionPool::Reaper
46
49
  def run
47
50
  return unless frequency && frequency > 0
51
+
48
52
  Simple::SQL::MonkeyPatches.warn "simple-sql disables reapers for all connection pools, see https://github.com/rails/rails/issues/33600"
49
53
  end
50
54
  end
@@ -92,8 +92,8 @@ module ::Simple::SQL::Result::AssociationLoader # :nodoc:
92
92
 
93
93
  foreign_ids = H.pluck(records, belonging_column).uniq.compact
94
94
 
95
- scope = connection.scope(table: relation.having_table)
96
- scope = scope.where(having_column => foreign_ids)
95
+ scope = connection.scope({ table: relation.having_table })
96
+ scope = scope.where({ having_column => foreign_ids })
97
97
 
98
98
  recs = connection.all(scope, into: Hash)
99
99
  recs_by_id = H.by_key(recs, having_column)
@@ -122,7 +122,7 @@ module ::Simple::SQL::Result::AssociationLoader # :nodoc:
122
122
  host_ids = H.pluck(records, having_column).uniq.compact
123
123
 
124
124
  scope = connection.scope(table: relation.belonging_table)
125
- scope = scope.where(belonging_column => host_ids)
125
+ scope = scope.where({ belonging_column => host_ids })
126
126
  scope = scope.order_by(order_by) if order_by
127
127
 
128
128
  recs = connection.all(scope, into: Hash)
@@ -31,6 +31,7 @@ class ::Simple::SQL::Result < Array
31
31
  attr_reader :connection
32
32
 
33
33
  def initialize(connection, records) # :nodoc:
34
+ super()
34
35
  @connection = connection
35
36
  replace(records)
36
37
  end
@@ -6,6 +6,7 @@ module Simple
6
6
  def version(name)
7
7
  spec = Gem.loaded_specs[name]
8
8
  return "unreleased" unless spec
9
+
9
10
  version = spec.version.to_s
10
11
  version += "+unreleased" if unreleased?(spec)
11
12
  version
@@ -17,6 +18,7 @@ module Simple
17
18
  return false unless defined?(Bundler::Source::Gemspec)
18
19
  return true if spec.source.is_a?(::Bundler::Source::Gemspec)
19
20
  return true if spec.source.is_a?(::Bundler::Source::Path)
21
+
20
22
  false
21
23
  end
22
24
  end
@@ -0,0 +1,49 @@
1
+ #!/bin/bash
2
+
3
+ set -eu -o pipefail
4
+
5
+ echo "Starting integration tests. We log into log/integration_tests.log"
6
+
7
+ rm log/integration_tests.log
8
+ touch log/integration_tests.log
9
+
10
+ export SIMPLE_SQL_SILENCE=1
11
+
12
+ run_test() {
13
+ local activerecord_spec=$1
14
+ local pg_spec=$2
15
+
16
+ export SIMPLE_SQL_ACTIVERECORD_SPECS="$activerecord_spec"
17
+ export SIMPLE_SQL_PG_SPECS="$pg_spec"
18
+
19
+ printf "=== Running test w/SIMPLE_SQL_ACTIVERECORD_SPECS='%s' SIMPLE_SQL_PG_SPECS='%s'\n" "$SIMPLE_SQL_ACTIVERECORD_SPECS" "$SIMPLE_SQL_PG_SPECS" | tee -a log/integration_tests.log
20
+
21
+ if ! bundle update >> log/integration_tests.log ; then
22
+ echo "Bundling failed"
23
+ set -xv
24
+ bundle update
25
+ exit 1
26
+ fi
27
+
28
+ if ! bundle exec rspec >> log/integration_tests.log ; then
29
+ echo "Tests failed"
30
+ set -xv
31
+ bundle exec rspec
32
+ exit 1
33
+ fi
34
+ }
35
+
36
+ run_test "> 5,< 6" "~> 0.20"
37
+ run_test "> 5,< 6" "~> 1.0.0"
38
+ run_test "> 5,< 6" "~> 1.1.0"
39
+ run_test "> 5,< 6" "~> 1.2.0"
40
+ run_test "> 5,< 6" "~> 1.3.0"
41
+
42
+ run_test "> 6,< 7" "~> 1.1.0"
43
+ run_test "> 6,< 7" "~> 1.2.0"
44
+ run_test "> 6,< 7" "~> 1.3.0"
45
+
46
+ run_test "> 7,< 8" "~> 1.1.0"
47
+ run_test "> 7,< 8" "~> 1.2.0"
48
+ run_test "> 7,< 8" "~> 1.3.0"
49
+
data/simple-sql.gemspec CHANGED
@@ -21,29 +21,19 @@ Gem::Specification.new do |gem|
21
21
  # executables are used for development purposes only
22
22
  gem.executables = []
23
23
 
24
- gem.required_ruby_version = '~> 2.3'
24
+ gem.required_ruby_version = '~> 3.3'
25
25
 
26
26
  gem.add_dependency 'pg_array_parser', '~> 0', '>= 0.0.9'
27
- gem.add_dependency 'pg', '~> 0.20'
28
27
  gem.add_dependency 'expectation', '~> 1'
29
28
 
30
29
  gem.add_dependency 'digest-crc', '~> 0'
31
30
  gem.add_dependency 'simple-immutable', '~> 1.0'
32
31
 
32
+ pg_specs = ENV["SIMPLE_SQL_PG_SPECS"] || '~> 1.0'
33
+ gem.add_dependency 'pg', *(pg_specs.split(","))
34
+
33
35
  # during tests we check the SIMPLE_SQL_ACTIVERECORD_SPECS environment setting.
34
36
  # Run make tests to run all tests
35
- if ENV["SIMPLE_SQL_ACTIVERECORD_SPECS"]
36
- gem.add_dependency 'activerecord', '>= 5.2.4.5', *(ENV["SIMPLE_SQL_ACTIVERECORD_SPECS"].split(","))
37
- else
38
- gem.add_dependency 'activerecord', '>= 5.2.4.5', '< 6.1'
39
- end
40
-
41
- # optional gems (required by some of the parts)
42
-
43
- # development gems
44
- gem.add_development_dependency 'pg', '0.20'
45
- gem.add_development_dependency 'rake', '>= 12.3.3'
46
- gem.add_development_dependency 'rspec', '~> 3.7'
47
- gem.add_development_dependency 'rubocop', '~> 0.61.1'
48
- gem.add_development_dependency 'simplecov', '~> 0'
37
+ activerecord_specs = ENV["SIMPLE_SQL_ACTIVERECORD_SPECS"] || '< 6.1'
38
+ gem.add_dependency 'activerecord', '>= 5.2.4.5', *(activerecord_specs.split(","))
49
39
  end
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
 
3
3
  describe "Simple::SQL::Config" do
4
4
  describe ".determine_url" do
5
- it "reads config/database.yml" do
5
+ xit "reads config/database.yml" do
6
6
  expect(SQL::Config.determine_url).to eq "postgres://127.0.0.1/simple-sql-test"
7
7
  end
8
8
  end
@@ -7,7 +7,7 @@ describe "Simple::SQL.insert" do
7
7
  let!(:initial_ids) { SQL.all("SELECT id FROM users") }
8
8
 
9
9
  it "inserts a single user" do
10
- id = SQL.insert :users, first_name: "foo", last_name: "bar"
10
+ id = SQL.insert :users, { first_name: "foo", last_name: "bar" }
11
11
  expect(id).to be_a(Integer)
12
12
  expect(initial_ids).not_to include(id)
13
13
  expect(SQL.ask("SELECT count(*) FROM users")).to eq(USER_COUNT+1)
@@ -19,7 +19,7 @@ describe "Simple::SQL.insert" do
19
19
  end
20
20
 
21
21
  it "returns the id" do
22
- id = SQL.insert :users, first_name: "foo", last_name: "bar"
22
+ id = SQL.insert :users, { first_name: "foo", last_name: "bar" }
23
23
  expect(id).to be_a(Integer)
24
24
  expect(initial_ids).not_to include(id)
25
25
  end
@@ -33,36 +33,36 @@ describe "Simple::SQL::Connection::Scope" do
33
33
 
34
34
  context "that do not match" do
35
35
  it "does not match with string keys" do
36
- expect(SQL.ask(scope.where(id: -1))).to be_nil
36
+ expect(SQL.ask(scope.where({id: -1}))).to be_nil
37
37
  end
38
38
 
39
39
  it "does not match with symbol keys" do
40
- expect(SQL.ask(scope.where("id" => -1))).to be_nil
40
+ expect(SQL.ask(scope.where({"id" => -1}))).to be_nil
41
41
  end
42
42
  end
43
43
 
44
44
  context "that match" do
45
45
  it "matches with string keys" do
46
- expect(SQL.ask(scope.where("id" => user_id))).to eq(1)
46
+ expect(SQL.ask(scope.where({"id" => user_id}))).to eq(1)
47
47
  end
48
48
 
49
49
  it "matches with symbol keys" do
50
- expect(SQL.ask(scope.where(id: user_id))).to eq(1)
50
+ expect(SQL.ask(scope.where({id: user_id}))).to eq(1)
51
51
  end
52
52
  end
53
53
 
54
54
  context "with array arguments" do
55
55
  it "matches against array arguments" do
56
- expect(SQL.ask(scope.where("id" => [-333, user_id]))).to eq(1)
57
- expect(SQL.ask(scope.where("id" => [-333, -1]))).to be_nil
58
- expect(SQL.ask(scope.where("id" => []))).to be_nil
56
+ expect(SQL.ask(scope.where({"id" => [-333, user_id]}))).to eq(1)
57
+ expect(SQL.ask(scope.where({"id" => [-333, -1]}))).to be_nil
58
+ expect(SQL.ask(scope.where({"id" => []}))).to be_nil
59
59
  end
60
60
  end
61
61
 
62
62
  context "with invalid arguments" do
63
63
  it "raises an ArgumentError" do
64
64
  expect {
65
- scope.where(1 => 3)
65
+ scope.where({1 => 3})
66
66
  }.to raise_error(ArgumentError)
67
67
  end
68
68
  end
@@ -4,7 +4,7 @@ describe SQL do
4
4
  describe "VERSION" do
5
5
  it "defines a version string" do
6
6
  # Note: this allows for 0.12.34beta
7
- expect(SQL::VERSION).to match(/^\d+\.\d+\.\d+/)
7
+ expect(SQL::VERSION).to match(/^(\d+\.\d+\.\d+|unreleased)/)
8
8
  end
9
9
  end
10
10
  end
@@ -1,3 +1,5 @@
1
+ require "logger"
2
+
1
3
  # connect to the database and setup the schema
2
4
  require "active_record"
3
5
 
@@ -5,14 +7,21 @@ require "active_record"
5
7
  require "simple-sql"
6
8
 
7
9
  require "yaml"
8
- abc = YAML.load_file("config/database.yml")
10
+
11
+ path = "config/database.yml"
12
+ abc = if Psych::VERSION > '4.0'
13
+ YAML.safe_load(File.read(path), aliases: true)
14
+ else
15
+ YAML.safe_load(File.read(path), [], [], true)
16
+ end
17
+
9
18
  ActiveRecord::Base.establish_connection(abc["test"])
10
19
 
11
20
  if ActiveRecord::Base.respond_to?(:raise_in_transactional_callbacks=)
12
21
  ActiveRecord::Base.raise_in_transactional_callbacks = true
13
22
  end
14
23
 
15
- ActiveRecord::Base.logger = Logger.new("log/test.log")
24
+ ActiveRecord::Base.logger = ::Logger.new("log/test.log")
16
25
 
17
26
  ActiveRecord::Schema.define do
18
27
  self.verbose = false
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.5.37
4
+ version: 0.9.0
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: 2021-04-07 00:00:00.000000000 Z
12
+ date: 2025-04-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg_array_parser
@@ -32,49 +32,49 @@ dependencies:
32
32
  - !ruby/object:Gem::Version
33
33
  version: 0.0.9
34
34
  - !ruby/object:Gem::Dependency
35
- name: pg
35
+ name: expectation
36
36
  requirement: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.20'
40
+ version: '1'
41
41
  type: :runtime
42
42
  prerelease: false
43
43
  version_requirements: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.20'
47
+ version: '1'
48
48
  - !ruby/object:Gem::Dependency
49
- name: expectation
49
+ name: digest-crc
50
50
  requirement: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1'
54
+ version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
57
  version_requirements: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1'
61
+ version: '0'
62
62
  - !ruby/object:Gem::Dependency
63
- name: digest-crc
63
+ name: simple-immutable
64
64
  requirement: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '1.0'
69
69
  type: :runtime
70
70
  prerelease: false
71
71
  version_requirements: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '1.0'
76
76
  - !ruby/object:Gem::Dependency
77
- name: simple-immutable
77
+ name: pg
78
78
  requirement: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
@@ -107,76 +107,6 @@ dependencies:
107
107
  - - "<"
108
108
  - !ruby/object:Gem::Version
109
109
  version: '6.1'
110
- - !ruby/object:Gem::Dependency
111
- name: pg
112
- requirement: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - '='
115
- - !ruby/object:Gem::Version
116
- version: '0.20'
117
- type: :development
118
- prerelease: false
119
- version_requirements: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - '='
122
- - !ruby/object:Gem::Version
123
- version: '0.20'
124
- - !ruby/object:Gem::Dependency
125
- name: rake
126
- requirement: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - ">="
129
- - !ruby/object:Gem::Version
130
- version: 12.3.3
131
- type: :development
132
- prerelease: false
133
- version_requirements: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - ">="
136
- - !ruby/object:Gem::Version
137
- version: 12.3.3
138
- - !ruby/object:Gem::Dependency
139
- name: rspec
140
- requirement: !ruby/object:Gem::Requirement
141
- requirements:
142
- - - "~>"
143
- - !ruby/object:Gem::Version
144
- version: '3.7'
145
- type: :development
146
- prerelease: false
147
- version_requirements: !ruby/object:Gem::Requirement
148
- requirements:
149
- - - "~>"
150
- - !ruby/object:Gem::Version
151
- version: '3.7'
152
- - !ruby/object:Gem::Dependency
153
- name: rubocop
154
- requirement: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - "~>"
157
- - !ruby/object:Gem::Version
158
- version: 0.61.1
159
- type: :development
160
- prerelease: false
161
- version_requirements: !ruby/object:Gem::Requirement
162
- requirements:
163
- - - "~>"
164
- - !ruby/object:Gem::Version
165
- version: 0.61.1
166
- - !ruby/object:Gem::Dependency
167
- name: simplecov
168
- requirement: !ruby/object:Gem::Requirement
169
- requirements:
170
- - - "~>"
171
- - !ruby/object:Gem::Version
172
- version: '0'
173
- type: :development
174
- prerelease: false
175
- version_requirements: !ruby/object:Gem::Requirement
176
- requirements:
177
- - - "~>"
178
- - !ruby/object:Gem::Version
179
- version: '0'
180
110
  description: SQL with a simple interface. Postgres only.
181
111
  email: eno@radiospiel.org
182
112
  executables: []
@@ -236,6 +166,7 @@ files:
236
166
  - lib/simple/sql/version.rb
237
167
  - log/.gitkeep
238
168
  - scripts/benchmark1.rb
169
+ - scripts/integration_tests
239
170
  - scripts/release
240
171
  - scripts/release.rb
241
172
  - scripts/stats
@@ -281,14 +212,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
281
212
  requirements:
282
213
  - - "~>"
283
214
  - !ruby/object:Gem::Version
284
- version: '2.3'
215
+ version: '3.3'
285
216
  required_rubygems_version: !ruby/object:Gem::Requirement
286
217
  requirements:
287
218
  - - ">="
288
219
  - !ruby/object:Gem::Version
289
220
  version: '0'
290
221
  requirements: []
291
- rubygems_version: 3.1.4
222
+ rubygems_version: 3.5.22
292
223
  signing_key:
293
224
  specification_version: 4
294
225
  summary: SQL with a simple interface