appquery 0.6.0.rc9 → 0.6.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 +4 -4
- data/README.md +54 -23
- data/lib/app_query/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d2b91bbf7a82c936eaa700c842aadc4cf3275eafbf3dab96d1f57fe0d2c208cd
|
|
4
|
+
data.tar.gz: 8b9180cf1818c8a7275075fc5d4fd3c3ac336146ea2ef9492205a0c0988945b7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '073497cc044bfa98614a5cdafb9b59931a9c30e635f7bd6e32c3ebb7342bab1139a7f6c651fad9fa5d804d715b61afacb412ed597f45a8225ee2a33cb50f1c19'
|
|
7
|
+
data.tar.gz: 183aa21ef6e49c094974deccf0edf6b6a46b9eba0bca3a04a70cce3236e720817ba5ec76d729924dd4dce3d54797372d0faaeb920e2208eb7a149ae7f65c55e0
|
data/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://badge.fury.io/rb/appquery)
|
|
4
4
|
[](https://eval.github.io/appquery/)
|
|
5
5
|
|
|
6
|
-
A Ruby gem
|
|
6
|
+
A Ruby gem providing ergonomic raw SQL queries for ActiveRecord. Inline or stored queries in `app/queries/`, execute them with proper type casting, filter/transform results using CTEs and have parameterization via ERB.
|
|
7
7
|
|
|
8
8
|
```ruby
|
|
9
9
|
# Load and execute
|
|
@@ -12,6 +12,8 @@ week.entries
|
|
|
12
12
|
#=> [{"week" => 2025-01-13, "category" => "Electronics", "revenue" => 12500, "target_met" => true}, ...]
|
|
13
13
|
|
|
14
14
|
# Filter results (query wraps in CTE, :_ references it)
|
|
15
|
+
week.count
|
|
16
|
+
#=> 5
|
|
15
17
|
week.count("SELECT * FROM :_ WHERE NOT target_met")
|
|
16
18
|
#=> 3
|
|
17
19
|
|
|
@@ -36,12 +38,14 @@ query.prepend_cte("sales AS (SELECT * FROM mock_data)")
|
|
|
36
38
|
**Highlights**: query files with generator · `select_all`/`select_one`/`select_value`/`count`/`column`/`ids` · query transformation via CTEs · immutable (derive new queries from existing) · named binds · ERB helpers (`order_by`, `paginate`, `values`, `bind`) · automatic + custom type casting · RSpec integration
|
|
37
39
|
|
|
38
40
|
> [!IMPORTANT]
|
|
39
|
-
> **Status**:
|
|
41
|
+
> **Status**: using it in production for multiple projects, but API might change pre v1.0. See [the CHANGELOG](./CHANGELOG.md) for breaking changes when upgrading.
|
|
40
42
|
>
|
|
41
43
|
|
|
42
44
|
## Rationale
|
|
43
45
|
|
|
44
|
-
Sometimes ActiveRecord doesn't cut it,
|
|
46
|
+
Sometimes ActiveRecord doesn't cut it: you need performance, would rather use raw SQL instead of Arel and hash-maps are fine instead of full-fledge ActiveRecord instances.
|
|
47
|
+
That, however, introduces some new problems. First of all, you'll run into the not-so-intuitive use of [select_(all|one|value)](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-select_all) — for example, how they differ with respect to type casting, and how their behavior can vary between ActiveRecord versions. Then there's the testability, introspection, and maintainability of the resulting SQL queries.
|
|
48
|
+
|
|
45
49
|
This library aims to alleviate all of these issues by providing a consistent interface across select_* methods and ActiveRecord versions. It should make inspecting and testing queries easier—especially when they're built from CTEs.
|
|
46
50
|
|
|
47
51
|
## Installation
|
|
@@ -74,29 +78,15 @@ The prompt indicates what adapter the example uses:
|
|
|
74
78
|
```ruby
|
|
75
79
|
# showing select_(all|one|value)
|
|
76
80
|
[postgresql]> AppQuery(%{select date('now') as today}).select_all.entries
|
|
77
|
-
=> [{"today" =>
|
|
81
|
+
=> [{"today" => Fri, 02 Jan 2026}]
|
|
78
82
|
[postgresql]> AppQuery(%{select date('now') as today}).select_one
|
|
79
|
-
=> {"today" =>
|
|
83
|
+
=> {"today" => Fri, 02 Jan 2026}
|
|
80
84
|
[postgresql]> AppQuery(%{select date('now') as today}).select_value
|
|
81
|
-
=>
|
|
82
|
-
|
|
83
|
-
# binds
|
|
84
|
-
## named binds
|
|
85
|
-
[postgresql]> AppQuery(%{select now() - (:interval)::interval as date}).select_value(binds: {interval: '2 days'})
|
|
86
|
-
|
|
87
|
-
## not all binds need to be provided (ie they are nil by default) - so defaults can be added in SQL:
|
|
88
|
-
[postgresql]> AppQuery(<<~SQL).select_all(binds: {ts1: 2.days.ago, ts2: Time.now, interval: '1 hour'}).column("series")
|
|
89
|
-
SELECT generate_series(
|
|
90
|
-
:ts1::timestamp,
|
|
91
|
-
:ts2::timestamp,
|
|
92
|
-
COALESCE(:interval, '5 minutes')::interval
|
|
93
|
-
) AS series
|
|
94
|
-
SQL
|
|
85
|
+
=> Fri, 02 Jan 2026
|
|
95
86
|
|
|
96
87
|
# casting
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
=> {"today" => Sat, 10 May 2025}
|
|
88
|
+
As can be seen from these examples, values are automatically casted.
|
|
89
|
+
|
|
100
90
|
## compare ActiveRecord
|
|
101
91
|
[postgresql]> ActiveRecord::Base.connection.select_one(%{select date('now') as today})
|
|
102
92
|
=> {"today" => "2025-12-20"}
|
|
@@ -109,6 +99,24 @@ cast = {today: :date}
|
|
|
109
99
|
[sqlite]> AppQuery(%{select date('now') as today}).select_one(cast:)
|
|
110
100
|
=> {"today" => Mon, 12 May 2025}
|
|
111
101
|
|
|
102
|
+
# binds
|
|
103
|
+
## named binds
|
|
104
|
+
[postgresql]> AppQuery(%{select now() - (:interval)::interval as date}).select_value(binds: {interval: '2 days'})
|
|
105
|
+
=> 2025-12-31 12:57:27.41132 UTC
|
|
106
|
+
|
|
107
|
+
## not all binds need to be provided (ie they are nil by default) - so defaults can be added in SQL:
|
|
108
|
+
[postgresql]> AppQuery(<<~SQL).select_all(binds: {ts1: 2.days.ago, ts2: Time.now, interval: '1 hour'}).column("series")
|
|
109
|
+
SELECT generate_series(
|
|
110
|
+
:ts1::timestamp,
|
|
111
|
+
:ts2::timestamp,
|
|
112
|
+
COALESCE(:interval, '5 minutes')::interval
|
|
113
|
+
) AS series
|
|
114
|
+
SQL
|
|
115
|
+
=>
|
|
116
|
+
[2025-12-31 12:57:46.969709 UTC,
|
|
117
|
+
2025-12-31 13:57:46.969709 UTC,
|
|
118
|
+
2025-12-31 14:57:46.969709 UTC,
|
|
119
|
+
...]
|
|
112
120
|
|
|
113
121
|
# rewriting queries (using CTEs)
|
|
114
122
|
[postgresql]> articles = [
|
|
@@ -376,7 +384,30 @@ $ bin/run rails_head console
|
|
|
376
384
|
|
|
377
385
|
Run `rake spec` to run the tests.
|
|
378
386
|
|
|
379
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
387
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
388
|
+
|
|
389
|
+
### Releasing
|
|
390
|
+
|
|
391
|
+
Create a signed git tag and push:
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
# Regular release
|
|
395
|
+
git tag -s 1.2.3 -m "Release 1.2.3"
|
|
396
|
+
|
|
397
|
+
# Prerelease
|
|
398
|
+
git tag -s 1.2.3.rc1 -m "Release 1.2.3.rc1"
|
|
399
|
+
|
|
400
|
+
# Push the tag
|
|
401
|
+
git push origin --tags
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
CI will build the gem, sign it (Sigstore attestation), push to RubyGems, and create a GitHub release (see [release.yml](https://github.com/eval/appquery/blob/3ed2adfacf952acc191a21a44b7c43a375b8975b/.github/workflows/release.yml#L34)).
|
|
405
|
+
|
|
406
|
+
After the release, update version.rb to the next dev version:
|
|
407
|
+
|
|
408
|
+
```ruby
|
|
409
|
+
VERSION = "1.2.4.dev"
|
|
410
|
+
```
|
|
380
411
|
|
|
381
412
|
## Contributing
|
|
382
413
|
|
data/lib/app_query/version.rb
CHANGED