appquery 0.8.0 β†’ 0.9.0.rc1

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: 631dc9e88f2e84a72e51c4f03843aedc1a187c18a410fc4c8b519800c24c5ab2
4
- data.tar.gz: 3f006856e51702d2483598b94647e63f00fd9989d67851e6bf31dcbc2497165f
3
+ metadata.gz: de70b2690d884b36f8d29f8a2e76c7edefba25ca9f9fcc9785e70389c35f3aa0
4
+ data.tar.gz: 3c81f2fb6dd4679a86a99df91485df234ccfd449c6de0cb5317c6ca8f8e5751b
5
5
  SHA512:
6
- metadata.gz: 6fe8f0822ea850a912a03eceec3e6cc7db6b4df52ce7734a67dc0bf242983720867b98dc63e37afce4ff6bd2daba600170a7eb1e90c85c97978b7e959fe7f70e
7
- data.tar.gz: 9617c613888fd722b258d0d956bb0cd92dabbd9d4b4a1f1cb04a38aa37b69b2e7d2b0cc25fd306923f94cc9953aa36634171a7b7ecd95698d884118eb09c50ee
6
+ metadata.gz: e2a0d5f7c2a320426cf292e4f5afdcbe8ca9d499eca85cb8139d7c44d0dc2498443fc70853bc274c6c912e9753ee7a176ad552d9e4f6fe0d0f1adc4d2ad00bba
7
+ data.tar.gz: 7a038760466d79804b06e64ba18d6161a2070fe728b2ec529a1dbd62c04729866b8af6def94f9dafcec7965d060710c46bbe3e874b6cb67df2e32413a4a62cc1
@@ -0,0 +1,40 @@
1
+ # Worktree configuration for bonchi.
2
+ # See https://github.com/eval/bonchi
3
+
4
+ # Minimum bonchi version required.
5
+ # min_version: 0.6.0
6
+
7
+ # Files to copy from the main worktree before setup.
8
+ copy:
9
+ - mise.toml
10
+ - mise.local.toml
11
+ - _irbrc
12
+ - bin/worktree-dev
13
+
14
+ # Files to symlink from the main worktree (useful for large directories).
15
+ # link:
16
+ # - node_modules
17
+
18
+ # Env var names to allocate unique ports for (from global pool).
19
+ # ports:
20
+ # - PORT
21
+
22
+ # Regex replacements in copied files. Env vars ($VAR) are expanded.
23
+ # Short form:
24
+ # replace:
25
+ # .env.local:
26
+ # - "^PORT=.*": "PORT=$PORT"
27
+ # Full form (with optional missing: warn, default: halt):
28
+ # replace:
29
+ # .env.local:
30
+ # - match: "^PORT=.*"
31
+ # with: "PORT=$PORT"
32
+ # missing: warn
33
+
34
+ # Commands to run before the setup command (port env vars are available).
35
+ pre_setup:
36
+ - mise trust
37
+ - bin/setup
38
+
39
+ # The setup command to run (default: bin/setup).
40
+ setup: bin/worktree-dev
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ### πŸ’₯ Breaking Changes
4
+
5
+ - ⚠️ **`AppQuery::Mappable` extension API changed.**
6
+ Row-level middleware now appends a transformer to the underlying `Q`'s `row_builder` pipeline via an overridden `#query`, instead of overriding `select_all`/`select_one`. The previous pattern of overriding those two methods will silently do nothing on row-returning paths it didn't cover (`entries`, `first`, `last`, `take`, `with_select(non_nil).first`, …). Any custom middleware that overrode `select_all`/`select_one` should migrate to:
7
+ ```ruby
8
+ def query
9
+ @query ||= super.tap { |q| q.row_builder << method(:build_row) }
10
+ end
11
+ ```
12
+ - ⚠️ **`Q#column` now raises `ArgumentError` for unknown columns.**
13
+ Previously, on SQLite, `q.column(:typo)` silently returned a row per record containing the *string* `"typo"` (the SQLite "double-quoted strings are identifiers OR string literals" quirk masked the missing column). It now pre-validates against `column_names` and raises with the available column list β€” consistently across SQLite and PostgreSQL.
14
+
15
+ ### ✨ Features
16
+
17
+ - 🧩 **`AppQuery::RowBuilder`** β€” composable pipeline of row transformers exposed as `Q#row_builder`. Append with `q.row_builder << callable`; transformers run in registration order. Multiple row-level middlewares stack cleanly in `include` order. The pipeline is applied everywhere `Q` exposes rows (`entries`, `first`, `last`, `take`, `take_last`, `with_select(...).first`, …) and is independently copied across `deep_dup` so chained queries don't mutate their parent.
18
+ - 🎯 **`Mappable` is now one method.** Maps everywhere β€” including `entries`, `last`, `take(n)`, `with_select("…").first` paths that previously slipped through. `raw` bypass still works.
19
+ - πŸ› **`Q#column` typo protection** β€” see breaking-change note above.
20
+ - πŸ› **Comments inside CTE selects** no longer break tokenization; the whole `(SELECT … -- foo … )` is preserved as a single `CTE_SELECT` token.
21
+ - Publishing gem requires MFA
22
+
3
23
  ## 0.8.0
4
24
 
5
25
  **Releasedate**: 14-1-2026
data/README.md CHANGED
@@ -290,6 +290,115 @@ end
290
290
 
291
291
  See [the API docs](https://eval.github.io/appquery/AppQuery/RSpec/Helpers.html) for more RSpec examples.
292
292
 
293
+ ### Writing a Middleware
294
+
295
+ A `BaseQuery` middleware is a `Module` you `include` into a query class. There are three layers to extend at, depending on *where* you want to act. The three compose cleanly on the same query class.
296
+
297
+ | You want to… | Layer | How |
298
+ |---|---|---|
299
+ | change/decorate each row | **row-level** | append to `q.row_builder` |
300
+ | filter, wrap, cap, paginate, cache the collection | **result-level** | override `#entries` (or `#first`, `#last`, …) |
301
+ | change the SQL/binds before it runs | **query-level** | override `#query`, return a different `Q` |
302
+
303
+ #### Row-level (the `row_builder` pipeline)
304
+
305
+ `Q#row_builder` is a composable pipeline of callables that each receive a row Hash and return whatever should replace it β€” a Hash, a `Data`, a Struct, your own model.
306
+
307
+ ```ruby
308
+ module Stamping
309
+ extend ActiveSupport::Concern
310
+
311
+ def query
312
+ @query ||= super.tap { |q| q.row_builder << ->(row) { row.merge("stamped_at" => Time.now) } }
313
+ end
314
+ end
315
+
316
+ class ArticlesQuery < ApplicationQuery
317
+ include Stamping
318
+ end
319
+
320
+ ArticlesQuery.new.first # => {"id" => 1, ..., "stamped_at" => 2026-...}
321
+ ArticlesQuery.new.entries.first # same β€” every row-returning path flows through row_builder
322
+ ```
323
+
324
+ The pipeline propagates through `with_select(non_nil)`, `add_binds`, `with_binds`, `with_cast`, `with_sql`, and CTE focusing (`#cte`), so chained calls keep the same mapping. Each child gets an independent copy β€” mutating it doesn't affect the parent.
325
+
326
+ **Stacking row-level middlewares.** Transformers run in **`include` order** β€” earliest `include` first, latest `include` last. With
327
+
328
+ ```ruby
329
+ class MyQuery < ApplicationQuery
330
+ include Stamping # runs first β€” its row goes into…
331
+ include AppQuery::Mappable # …which sees the stamped hash and builds an Item
332
+ end
333
+ ```
334
+
335
+ `Stamping`'s lambda runs first, then `Mappable.build_row` consumes the already-stamped hash. ⚠️ Once a transformer returns a non-Hash (e.g. a `Data`), downstream transformers see that object β€” so a hash-merging transformer placed *after* `Mappable` would fail. Order the chain accordingly.
336
+
337
+ **Doesn't fit row_builder:** filtering ("drop rows the viewer can't see") and collapsing rows. Both act on the collection, not a single row β€” use the result-level layer instead.
338
+
339
+ #### Result-level (wrap a row-returning method)
340
+
341
+ When you want to act on the whole collection β€” wrap, cap, cache, filter, paginate β€” override the row-returning method and call `super`. `super` returns rows that have *already* been through `row_builder`, so this composes with row-level middleware without thinking.
342
+
343
+ `Paginatable` is the canonical example (wraps `#entries` in a `PaginatedResult`). Some others:
344
+
345
+ ```ruby
346
+ module Caching
347
+ # memoise the whole result on the instance
348
+ def entries = @_entries ||= super
349
+ def first = @_first ||= super
350
+ end
351
+
352
+ module ScopedToTenant
353
+ def entries = super.select { |r| r["tenant_id"] == Current.tenant.id }
354
+ end
355
+
356
+ module Capped
357
+ CapResult = Data.define(:records, :hit_cap?) do
358
+ include Enumerable
359
+ def each(&b) = records.each(&b)
360
+ end
361
+
362
+ def entries
363
+ rows = super
364
+ CapResult.new(records: rows.first(self.class.cap), hit_cap?: rows.size > self.class.cap)
365
+ end
366
+ end
367
+ ```
368
+
369
+ #### Query-level (rewrite SQL/binds before execution)
370
+
371
+ When you want to change what the database actually sees β€” tenant scoping, soft-delete filtering, default ordering β€” override `#query` and return a transformed `Q`. Use `with_select` / `with_binds` / `add_binds` etc.; they propagate the `row_builder` pipeline via `deep_dup`, so row-level middleware keeps working.
372
+
373
+ ```ruby
374
+ module TenantScoped
375
+ def query
376
+ @query ||= super.add_binds(tenant_id: Current.tenant.id)
377
+ end
378
+ end
379
+
380
+ module HidesDeleted
381
+ def query
382
+ @query ||= super.with_select("SELECT * FROM :_ WHERE deleted_at IS NULL")
383
+ end
384
+ end
385
+ ```
386
+
387
+ #### Putting it together
388
+
389
+ All three layers can sit on one class:
390
+
391
+ ```ruby
392
+ class ArticlesQuery < ApplicationQuery
393
+ include HidesDeleted # query-level: rewrites SQL
394
+ include Stamping # row-level: adds "stamped_at"
395
+ include AppQuery::Mappable # row-level: builds Item (must come after row-mutating middleware)
396
+ include AppQuery::Paginatable # result-level: wraps entries
397
+ end
398
+ ```
399
+
400
+ Pipeline at run time, top to bottom: SQL is rewritten to filter `deleted_at IS NULL` β†’ DB returns rows β†’ each row gets `stamped_at` β†’ each row becomes an `Item` β†’ the array of Items is wrapped in a `PaginatedResult`.
401
+
293
402
  ## API Documentation
294
403
 
295
404
  See the [YARD documentation](https://eval.github.io/appquery/) for the full API reference.
@@ -322,7 +431,8 @@ rake spec
322
431
  bin/yard-dev
323
432
  ```
324
433
 
325
- Using [mise](https://mise.jdx.dev/) for env-vars is recommended.
434
+ Using [mise](https://mise.jdx.dev/) for env-vars is recommended.
435
+ Using [bonchi](https://rubygems.org/gems/bonchi) allows for agentic working via git worktrees. See `.worktree.yml.example` for a config.
326
436
 
327
437
  ### Releasing
328
438
 
@@ -154,7 +154,8 @@ module AppQuery
154
154
  end
155
155
  end
156
156
 
157
- delegate :cte, :entries, :with_select, :select_all, :select_one, :count, :to_s, :column, :first, :ids, :copy_to, to: :query
157
+ delegate :cte, :entries, :with_select, :select_all, :select_one, :count, :to_s,
158
+ :column, :first, :last, :take, :take_last, :ids, :copy_to, to: :query
158
159
 
159
160
  def query
160
161
  @query ||= base_query
@@ -66,31 +66,20 @@ module AppQuery
66
66
  self
67
67
  end
68
68
 
69
- def select_all
70
- map_result(super)
71
- end
72
-
73
- def select_one
74
- map_one(super)
69
+ # Append our transform to the underlying Q's RowBuilder pipeline so every
70
+ # row-returning path (entries, first, last, take, with_select(...).first,
71
+ # …) sees mapped rows. Stacks with other row-level middlewares in
72
+ # include-order β€” earlier `include`s run first.
73
+ def query
74
+ @query ||= super.tap { |q| q.row_builder << method(:build_row) }
75
75
  end
76
76
 
77
77
  private
78
78
 
79
- def map_result(result)
80
- return result if @raw
81
- return result unless (klass = resolve_map_klass)
82
-
83
- attrs = klass.members
84
- result.transform! { |row| klass.new(**row.symbolize_keys.slice(*attrs)) }
85
- end
86
-
87
- def map_one(result)
88
- return result if @raw
89
- return result unless (klass = resolve_map_klass)
90
- return result unless result
91
-
92
- attrs = klass.members
93
- klass.new(**result.symbolize_keys.slice(*attrs))
79
+ def build_row(row)
80
+ return row if @raw
81
+ return row unless (klass = resolve_map_klass)
82
+ klass.new(**row.symbolize_keys.slice(*klass.members))
94
83
  end
95
84
 
96
85
  def resolve_map_klass
@@ -257,9 +257,15 @@ module AppQuery
257
257
 
258
258
  level = 1
259
259
  loop do
260
- read_until(/\)|\(|'/)
260
+ read_until(%r{\)|\(|'|--|/\*})
261
261
  if eos?
262
262
  err "CTE select ended prematurely"
263
+ elsif match?("--")
264
+ read_until(/\n/)
265
+ elsif match?(%r{/\*})
266
+ read_until(%r{\*/})
267
+ err "CTE select ended prematurely" if eos?
268
+ read_char 2
263
269
  elsif match?(/'/)
264
270
  # Skip string literal (handle escaped quotes '')
265
271
  read_char
@@ -3,5 +3,5 @@
3
3
  module AppQuery
4
4
  # This should just contain the .dev of the upcoming version.
5
5
  # When doing the actual release, CI will write the tag here before pushing the gem.
6
- VERSION = "0.8.0"
6
+ VERSION = "0.9.0.rc1"
7
7
  end
data/lib/app_query.rb CHANGED
@@ -155,17 +155,62 @@ module AppQuery
155
155
  Q.new("SELECT * FROM #{quote_table(name)}", name: "AppQuery.table(#{name})", **opts)
156
156
  end
157
157
 
158
+ # Composable pipeline of row transformers.
159
+ #
160
+ # Appended via `<<` and applied in registration order β€” earliest pushed
161
+ # runs first, latest pushed runs last (so its return value is what callers
162
+ # see). An empty pipeline is a no-op.
163
+ #
164
+ # @example
165
+ # rb = AppQuery::RowBuilder.new
166
+ # rb << ->(row) { row.merge("a" => 1) }
167
+ # rb << ->(row) { row.merge("b" => 2) }
168
+ # rb.call({}) # => {"a" => 1, "b" => 2}
169
+ class RowBuilder
170
+ def initialize(procs = [])
171
+ @procs = procs
172
+ end
173
+
174
+ # Append a transformer. Returns self so `q.row_builder << proc` reads
175
+ # naturally and `<<=` also works.
176
+ def <<(callable)
177
+ @procs << callable
178
+ self
179
+ end
180
+
181
+ def call(row)
182
+ @procs.reduce(row) { |acc, p| p.call(acc) }
183
+ end
184
+
185
+ def empty? = @procs.empty?
186
+
187
+ def size = @procs.size
188
+
189
+ # Independent copy β€” used by Q#deep_dup so chained queries don't share
190
+ # the parent's pipeline.
191
+ def dup
192
+ self.class.new(@procs.dup)
193
+ end
194
+ end
195
+
158
196
  class Result < ActiveRecord::Result
159
- attr_accessor :cast
197
+ attr_accessor :cast, :row_builder
160
198
  alias_method :cast?, :cast
161
199
 
162
- def initialize(columns, rows, overrides = nil, cast: false)
200
+ def initialize(columns, rows, overrides = nil, cast: false, row_builder: nil)
163
201
  super(columns, rows, overrides)
164
202
  @cast = cast
203
+ @row_builder = row_builder
165
204
  # Rails v6.1: prevent mutate on frozen object on #first
166
205
  @hash_rows = [] if columns.empty?
167
206
  end
168
207
 
208
+ # AR::Result#first reads @hash_rows directly when not yet memoized,
209
+ # bypassing our hash_rows override. Force it through.
210
+ def first
211
+ hash_rows.first
212
+ end
213
+
169
214
  # Returns an array of values for a single column.
170
215
  #
171
216
  # @note If you only need a single column, prefer {Q#column} which selects
@@ -204,9 +249,13 @@ module AppQuery
204
249
  private
205
250
 
206
251
  # Override to provide indifferent access (string or symbol keys).
252
+ # Applies the RowBuilder pipeline lazily so callers see built rows
253
+ # everywhere (first, last, each, entries, to_a, [] …). An empty/absent
254
+ # builder is a no-op.
207
255
  def hash_rows
208
256
  @hash_rows ||= rows.map do |row|
209
- columns.zip(row).to_h.with_indifferent_access
257
+ hash = columns.zip(row).to_h.with_indifferent_access
258
+ row_builder ? row_builder.call(hash) : hash
210
259
  end
211
260
  end
212
261
 
@@ -243,9 +292,9 @@ module AppQuery
243
292
  end
244
293
  end
245
294
 
246
- def self.from_ar_result(r, cast = nil)
295
+ def self.from_ar_result(r, cast = nil, row_builder: nil)
247
296
  if r.empty?
248
- r.columns.empty? ? EMPTY : new(r.columns, [], r.column_types)
297
+ r.columns.empty? ? EMPTY : new(r.columns, [], r.column_types, row_builder:)
249
298
  else
250
299
  cast &&= case cast
251
300
  when Array
@@ -257,7 +306,7 @@ module AppQuery
257
306
  end
258
307
  if !cast || (cast.empty? && r.column_types.empty?)
259
308
  # nothing to cast
260
- new(r.columns, r.rows, r.column_types)
309
+ new(r.columns, r.rows, r.column_types, row_builder:)
261
310
  else
262
311
  overrides = (r.column_types || {}).merge(cast)
263
312
  rows = r.cast_values(overrides)
@@ -267,7 +316,7 @@ module AppQuery
267
316
  # > ActiveRecord::Base.connection.select_all("select array[1,2]").cast_values
268
317
  # => [[1, 2]]
269
318
  rows = rows.zip if r.columns.one?
270
- new(r.columns, rows, overrides, cast: true)
319
+ new(r.columns, rows, overrides, cast: true, row_builder:)
271
320
  end
272
321
  end
273
322
  end
@@ -317,6 +366,15 @@ module AppQuery
317
366
  # @return [Boolean, Hash, Array] casting configuration
318
367
  attr_reader :sql, :name, :filename, :binds, :cast
319
368
 
369
+ # Middleware extension point. The {RowBuilder} is a composable pipeline:
370
+ # middlewares append transformers with `q.row_builder << ->(row) { … }`
371
+ # and the result is applied to every row everywhere Q exposes rows
372
+ # (entries, first, last, take, take_last, with_select(...).first, …).
373
+ # Propagated through {#deep_dup} (with an independent copy) so chained
374
+ # queries inherit the pipeline but don't mutate the parent's.
375
+ # @return [RowBuilder]
376
+ attr_accessor :row_builder
377
+
320
378
  # Creates a new query object.
321
379
  #
322
380
  # @param sql [String] the SQL query string (may contain ERB)
@@ -330,13 +388,14 @@ module AppQuery
330
388
  #
331
389
  # @example With ERB and binds
332
390
  # Q.new("SELECT * FROM users WHERE id = :id", binds: {id: 1})
333
- def initialize(sql, name: nil, filename: nil, binds: {}, cast: true, cte_depth: 0)
391
+ def initialize(sql, name: nil, filename: nil, binds: {}, cast: true, cte_depth: 0, row_builder: nil)
334
392
  @sql = sql
335
393
  @name = name
336
394
  @filename = filename
337
395
  @binds = binds
338
396
  @cast = cast
339
397
  @cte_depth = cte_depth
398
+ @row_builder = row_builder || RowBuilder.new
340
399
  @binds = binds_with_defaults(sql, binds)
341
400
  end
342
401
 
@@ -359,8 +418,8 @@ module AppQuery
359
418
  end
360
419
  end
361
420
 
362
- def deep_dup(sql: self.sql, name: self.name, filename: self.filename, binds: self.binds.dup, cast: self.cast, cte_depth: self.cte_depth)
363
- self.class.new(sql, name:, filename:, binds:, cast:, cte_depth:)
421
+ def deep_dup(sql: self.sql, name: self.name, filename: self.filename, binds: self.binds.dup, cast: self.cast, cte_depth: self.cte_depth, row_builder: self.row_builder.dup)
422
+ self.class.new(sql, name:, filename:, binds:, cast:, cte_depth:, row_builder:)
364
423
  end
365
424
 
366
425
  # @!group Rendering
@@ -468,7 +527,7 @@ module AppQuery
468
527
  ActiveRecord::Base.sanitize_sql_array([aq.to_s, aq.binds])
469
528
  end
470
529
  ActiveRecord::Base.connection.select_all(sql, aq.name).then do |result|
471
- Result.from_ar_result(result, cast)
530
+ Result.from_ar_result(result, cast, row_builder: aq.row_builder)
472
531
  end
473
532
  end
474
533
  rescue NameError => e
@@ -492,7 +551,8 @@ module AppQuery
492
551
  def select_one(s = nil, binds: {}, cast: self.cast)
493
552
  with_select(s).select_all("SELECT * FROM :_ LIMIT 1", binds:, cast:).first
494
553
  end
495
- alias_method :first, :select_one
554
+
555
+ def first(...) = select_one(...)
496
556
 
497
557
  # Executes the query and returns the last row.
498
558
  #
@@ -550,8 +610,9 @@ module AppQuery
550
610
  #
551
611
  # @see #last
552
612
  def take_last(n, s = nil, binds: {}, cast: self.cast)
613
+ offset_expr = greatest("(SELECT COUNT(*) FROM :_) - #{n.to_i}", "0")
553
614
  with_select(s).select_all(
554
- "SELECT * FROM :_ LIMIT #{n.to_i} OFFSET GREATEST((SELECT COUNT(*) FROM :_) - #{n.to_i}, 0)",
615
+ "SELECT * FROM :_ LIMIT #{n.to_i} OFFSET #{offset_expr}",
555
616
  binds:, cast:
556
617
  ).entries
557
618
  end
@@ -652,7 +713,16 @@ module AppQuery
652
713
  # @example Extract unique values
653
714
  # AppQuery("SELECT * FROM products").column(:category, unique: true)
654
715
  # # => ["Electronics", "Clothing", "Home"]
716
+ #
717
+ # @raise [ArgumentError] if the column doesn't exist in the (optionally
718
+ # selected) query. Pre-validating catches typos consistently across
719
+ # databases β€” e.g. without this, SQLite's "double-quoted strings"
720
+ # quirk would silently return rows of the column-name as a string.
655
721
  def column(c, s = nil, binds: {}, unique: false)
722
+ available = column_names(s, binds:)
723
+ unless available.include?(c.to_s)
724
+ raise ArgumentError, "Unknown column #{c.inspect}. Available: #{available.inspect}."
725
+ end
656
726
  quoted = quote_column(c)
657
727
  select_expr = unique ? "DISTINCT #{quoted}" : quoted
658
728
  with_select(s).select_all("SELECT #{select_expr} AS column FROM :_", binds:).column("column")
@@ -1222,6 +1292,16 @@ module AppQuery
1222
1292
  def quote_column(name)
1223
1293
  AppQuery.quote_column(name)
1224
1294
  end
1295
+
1296
+ # Returns SQL for max(a, b) that works across adapters.
1297
+ # PostgreSQL uses GREATEST, SQLite uses MAX for scalar comparison.
1298
+ def greatest(a, b)
1299
+ if /sqlite/i.match?(ActiveRecord::Base.connection.adapter_name)
1300
+ "MAX(#{a}, #{b})"
1301
+ else
1302
+ "GREATEST(#{a}, #{b})"
1303
+ end
1304
+ end
1225
1305
  end
1226
1306
  end
1227
1307
 
@@ -1,5 +1,5 @@
1
1
  [env]
2
2
  # used for tests
3
- PG_DATABASE_URL="postgres://localhost:5432/some_db
3
+ SPEC_DATABASE_URL="postgres://localhost:5432/some_db"
4
4
  # used from console
5
- DATABASE_URL="postgres://localhost:5432/some_db
5
+ CONSOLE_DATABASE_URL="postgres://localhost:5432/some_db"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appquery
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gert Goet
@@ -9,6 +9,20 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activerecord
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: appraisal
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -43,6 +57,7 @@ files:
43
57
  - ".irbrc"
44
58
  - ".rspec"
45
59
  - ".standard.yml"
60
+ - ".worktree.yml.example"
46
61
  - ".yard/templates/default/fulldoc/html/css/dark.css"
47
62
  - ".yard/templates/default/fulldoc/html/js/app.js"
48
63
  - ".yard/templates/default/fulldoc/html/setup.rb"
@@ -94,6 +109,7 @@ metadata:
94
109
  homepage_uri: https://github.com/eval/appquery
95
110
  source_code_uri: https://github.com/eval/appquery
96
111
  changelog_uri: https://github.com/eval/appquery/blob/main/CHANGELOG.md
112
+ rubygems_mfa_required: 'true'
97
113
  rdoc_options: []
98
114
  require_paths:
99
115
  - lib