appquery 0.2.0 → 0.3.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/Appraisals +15 -0
- data/LICENSE.txt +1 -1
- data/README.md +124 -7
- data/lib/app_query/base.rb +45 -0
- data/lib/app_query/version.rb +1 -1
- data/lib/app_query.rb +128 -23
- data/mise.local.toml.example +5 -0
- data/mise.toml +6 -0
- metadata +23 -11
- data/.envrc +0 -6
- data/.envrc.private.example +0 -2
- data/tmp/.gitkeep +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64e74167bbafa7217db5f9c0bab6efc8b55655abbbcb7e2fccefca3dfe1afae8
|
4
|
+
data.tar.gz: 16f6870106206b547ed307fb6cbcd8d9250610239ccf9dc046e6e6c9719d76dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74ce3c5a8d22b2b41bc477069e9720c7b91cad52909ee3e4db8533c0a6f357227ec7e9485f513b8306a0d4994b53556971ae09284c72c2c5370bee45a0c244e3
|
7
|
+
data.tar.gz: 61b01b05753f3a6f27194e47b40bb91aa822c5c263dbe78d0328f1b09aad02d95acbfc7ba191d4fb773b536c1b1ad03465e05c87c4ecf1dc279ed3e461ba97b2
|
data/Appraisals
ADDED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/appquery)
|
4
4
|
|
5
|
-
A Rubygem :gem: that makes working with raw SQL queries in Rails projects more convenient.
|
5
|
+
A Rubygem :gem: that makes working with raw SQL (READ) queries in Rails projects more convenient.
|
6
6
|
Specifically it provides:
|
7
7
|
- **...a dedicated folder for queries**
|
8
8
|
e.g. `app/queries/reports/weekly.sql` is instantiated via `AppQuery["reports/weekly"]`.
|
@@ -13,6 +13,31 @@ Specifically it provides:
|
|
13
13
|
invoke rspec
|
14
14
|
create spec/queries/reports/weekly_query_spec.rb
|
15
15
|
```
|
16
|
+
- **...ERB templating**
|
17
|
+
Simple ERB templating with helper-functions:
|
18
|
+
```sql
|
19
|
+
-- app/queries/contracts.sql.erb
|
20
|
+
SELECT * FROM contracts
|
21
|
+
<%= order_by(order) %>
|
22
|
+
```
|
23
|
+
```ruby
|
24
|
+
AppQuery["contracts.sql.erb"].render(order: {year: :desc, month: :desc}).select_all
|
25
|
+
```
|
26
|
+
- **...positional and named binds**
|
27
|
+
Intuitive binds:
|
28
|
+
```ruby
|
29
|
+
AppQuery(%{select now() - (:interval)::interval as some_date}).select_value(binds: {interval: '1 day'})
|
30
|
+
AppQuery(<<~SQL).select_all(binds: [2.day.ago, Time.now, '5 minutes']).column("series")
|
31
|
+
select generate_series($1::timestamp, $2::timestamp, $3::interval) as series
|
32
|
+
SQL
|
33
|
+
```
|
34
|
+
- **...casting**
|
35
|
+
Automatic and custom casting:
|
36
|
+
```ruby
|
37
|
+
AppQuery(%{select array[1,2]}).select_value #=> [1,2]
|
38
|
+
cast = {"data" => ActiveRecord::Type::Json.new}
|
39
|
+
AppQuery(%{select '{"a": 1}' as data}).select_value(cast:)
|
40
|
+
```
|
16
41
|
- **...helpers to rewrite a query for introspection during development and testing**
|
17
42
|
See what a CTE yields: `query.select_all(select: "SELECT * FROM some_cte")`.
|
18
43
|
Query the end result: `query.select_one(select: "SELECT COUNT(*) FROM _ WHERE ...")`.
|
@@ -38,7 +63,12 @@ Specifically it provides:
|
|
38
63
|
|
39
64
|
> [!IMPORTANT]
|
40
65
|
> **Status**: alpha. API might change. See the CHANGELOG for breaking changes when upgrading.
|
41
|
-
>
|
66
|
+
>
|
67
|
+
|
68
|
+
## Rationale
|
69
|
+
|
70
|
+
Sometimes ActiveRecord doesn't cut it, and you'd rather use raw SQL to get the right data out. 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.
|
71
|
+
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.
|
42
72
|
|
43
73
|
## Installation
|
44
74
|
|
@@ -53,7 +83,83 @@ bundle add appquery
|
|
53
83
|
> [!NOTE]
|
54
84
|
> The following (trivial) examples are not meant to convince you to ditch your ORM, but just to show how this gem handles raw SQL queries.
|
55
85
|
|
56
|
-
###
|
86
|
+
### ...from console
|
87
|
+
|
88
|
+
Testdriving can be easily done from the console. Either by cloning this repository (recommended, see `Development`-section) or installing the gem in an existing Rails project.
|
89
|
+
<details>
|
90
|
+
<summary>Database setup (the `bin/console`-script does this for your)</summary>
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
94
|
+
ActiveRecord::Base.establish_connection(url: 'postgres://localhost:5432/some_db')
|
95
|
+
```
|
96
|
+
</details>
|
97
|
+
|
98
|
+
The following examples assume PostgreSQL (SQLite where stated):
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
# showing select_(all|one|value)
|
102
|
+
> AppQuery(%{select date('now') as today}).select_all.to_a
|
103
|
+
=> [{"today" => "2025-05-10"}]
|
104
|
+
> AppQuery(%{select date('now') as today}).select_one
|
105
|
+
=> {"today" => "2025-05-10"}
|
106
|
+
> AppQuery(%{select date('now') as today}).select_value
|
107
|
+
=> "2025-05-10"
|
108
|
+
|
109
|
+
# binds
|
110
|
+
# positional binds
|
111
|
+
> AppQuery(%{select now() - ($1)::interval as date}).select_value(binds: ['2 days'])
|
112
|
+
# named binds
|
113
|
+
> AppQuery(%{select now() - (:interval)::interval as date}).select_value(binds: {interval: '2 days'})
|
114
|
+
|
115
|
+
# casting
|
116
|
+
> AppQuery(%{select date('now') as today}).select_all(cast: true).to_a
|
117
|
+
=> [{"today" => Sat, 10 May 2025}]
|
118
|
+
|
119
|
+
## SQLite doesn't have a notion of dates or timestamp's so casting won't do anything:
|
120
|
+
sqlite> AppQuery(%{select date('now') as today}).select_one(cast: true)
|
121
|
+
=> {"today" => "2025-05-12"}
|
122
|
+
## Providing per-column-casts fixes this:
|
123
|
+
casts = {"today" => ActiveRecord::Type::Date.new}
|
124
|
+
sqlite> AppQuery(%{select date('now') as today}).select_one(cast: casts)
|
125
|
+
=> {"today" => Mon, 12 May 2025}
|
126
|
+
|
127
|
+
# rewriting queries (using CTEs)
|
128
|
+
q = AppQuery(<<~SQL)
|
129
|
+
WITH articles(id,title,published_on) AS (
|
130
|
+
values(1, 'Some title', '2024-3-31'),
|
131
|
+
(2, 'Other title', '2024-10-31'),
|
132
|
+
(3, 'Same title?', '2024-3-31'))
|
133
|
+
select * from articles order by id DESC
|
134
|
+
SQL
|
135
|
+
|
136
|
+
## query the articles-CTE
|
137
|
+
q.select_all(select: %{select * from articles where id < 2}).to_a
|
138
|
+
|
139
|
+
## query the end-result (available as the CTE named '_')
|
140
|
+
q.select_one(select: %{select * from _ limit 1})
|
141
|
+
|
142
|
+
## ERB templating
|
143
|
+
# Extract a query from q that can be sorted dynamically:
|
144
|
+
q2 = q.with_select("select id,title,published_on::date from articles <%= order_by(order) %>")
|
145
|
+
q2.render(order: {"published_on::date": :desc, 'lower(title)': "asc"}).select_all.entries
|
146
|
+
# shows latest articles first, and titles sorted alphabetically
|
147
|
+
# for articles published on the same date.
|
148
|
+
# order_by raises when it's passed something that would result in just `ORDER BY`:
|
149
|
+
q2.render(order: {})
|
150
|
+
# doing a select using a query that should be rendered, a `AppQuery::UnrenderedQueryError` will be raised:
|
151
|
+
q2.select_all.entries
|
152
|
+
|
153
|
+
# NOTE you can use both `order` and `@order`: local variables like `order` are required,
|
154
|
+
# while instance variables like `@order` are optional.
|
155
|
+
# To skip the order-part when provided:
|
156
|
+
<%= @order.presence && order_by(order) %>
|
157
|
+
# or use a default when order-part is always wanted but not always provided:
|
158
|
+
<%= order_by(@order || {id: :desc}) %>
|
159
|
+
```
|
160
|
+
|
161
|
+
|
162
|
+
### ...in a Rails project
|
57
163
|
|
58
164
|
> [!NOTE]
|
59
165
|
> The included [example Rails app](./examples/ror) contains all data and queries described below.
|
@@ -462,17 +568,29 @@ query.replace_cte("recent_articles as (select values(1, 'Some article'))")
|
|
462
568
|
|
463
569
|
- 💾 tested with **SQLite** and **PostgreSQL**
|
464
570
|
- 🚆 tested with Rails **v6.1**, **v7** and **v8.0**
|
465
|
-
- 💎 requires Ruby **>v3.
|
571
|
+
- 💎 requires Ruby **>v3.2**
|
466
572
|
Goal is to support [maintained Ruby versions](https://www.ruby-lang.org/en/downloads/branches/).
|
467
573
|
|
468
574
|
## Development
|
469
575
|
|
470
576
|
After checking out the repo, run `bin/setup` to install dependencies. **Make sure to check it exits with status code 0.**
|
471
577
|
|
472
|
-
Using [
|
578
|
+
Using [mise](https://mise.jdx.dev/) for env-vars recommended.
|
579
|
+
|
580
|
+
### console
|
581
|
+
|
582
|
+
The [console-script](./bin/console) is setup such that it's easy to connect with a database and experiment with the library:
|
583
|
+
```bash
|
584
|
+
$ ./bin/console sqlite3::memory:
|
585
|
+
$ ./bin/console postgres://localhost:5432/some_db
|
586
|
+
|
587
|
+
# more details
|
588
|
+
$ ./bin/console -h
|
589
|
+
```
|
473
590
|
|
591
|
+
### various
|
474
592
|
|
475
|
-
|
593
|
+
Run `rake spec` to run the tests.
|
476
594
|
|
477
595
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
478
596
|
|
@@ -483,4 +601,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/eval/a
|
|
483
601
|
## License
|
484
602
|
|
485
603
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
486
|
-
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module AppQuery
|
2
|
+
class Base
|
3
|
+
class_attribute :_cast, default: true, instance_predicate: false
|
4
|
+
class_attribute :_default_binds, default: {}, instance_predicate: false
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def run(build_only: false, binds: {}, vars: {}, cast: self.cast, select: nil, **)
|
8
|
+
_build(binds:, vars:, cast:, select:).then do
|
9
|
+
build_only ? _1 : _1.select_all
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def build(**opts)
|
14
|
+
run(build_only: true, **opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def default_binds(v = nil)
|
18
|
+
return _default_binds if v.nil?
|
19
|
+
self._default_binds = v
|
20
|
+
end
|
21
|
+
|
22
|
+
def cast(v = nil)
|
23
|
+
return _cast if v.nil?
|
24
|
+
self._cast = v
|
25
|
+
end
|
26
|
+
|
27
|
+
def query_name
|
28
|
+
derive_query_name unless defined?(@query_name)
|
29
|
+
@query_name
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_writer :query_name
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def _build(cast:, binds: {}, select: nil, vars: {})
|
37
|
+
AppQuery[query_name, binds:, cast:].render(vars).with_select(select)
|
38
|
+
end
|
39
|
+
|
40
|
+
def derive_query_name
|
41
|
+
self.query_name = name.underscore.sub(/_query$/, "")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/app_query/version.rb
CHANGED
data/lib/app_query.rb
CHANGED
@@ -7,6 +7,8 @@ require "active_record"
|
|
7
7
|
module AppQuery
|
8
8
|
class Error < StandardError; end
|
9
9
|
|
10
|
+
class UnrenderedQueryError < StandardError; end
|
11
|
+
|
10
12
|
Configuration = Struct.new(:query_path)
|
11
13
|
|
12
14
|
def self.configuration
|
@@ -24,10 +26,14 @@ module AppQuery
|
|
24
26
|
end
|
25
27
|
reset_configuration!
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
# Examples:
|
30
|
+
# AppQuery[:invoices] # looks for invoices.sql
|
31
|
+
# AppQuery["reports/weekly"]
|
32
|
+
# AppQuery["invoices.sql.erb"]
|
33
|
+
def self.[](query_name, **opts)
|
34
|
+
filename = File.extname(query_name.to_s).empty? ? "#{query_name}.sql" : query_name.to_s
|
35
|
+
full_path = (Pathname.new(configuration.query_path) / filename).expand_path
|
36
|
+
Q.new(full_path.read, name: "AppQuery #{query_name}", filename: full_path.to_s, **opts)
|
31
37
|
end
|
32
38
|
|
33
39
|
class Result < ActiveRecord::Result
|
@@ -50,6 +56,10 @@ module AppQuery
|
|
50
56
|
rows.map { _1[ix] }
|
51
57
|
end
|
52
58
|
|
59
|
+
def size
|
60
|
+
count
|
61
|
+
end
|
62
|
+
|
53
63
|
def self.from_ar_result(r, cast = nil)
|
54
64
|
if r.empty?
|
55
65
|
EMPTY
|
@@ -73,7 +83,7 @@ module AppQuery
|
|
73
83
|
# => [["{1,2}"]]
|
74
84
|
# > ActiveRecord::Base.connection.select_all("select array[1,2]").cast_values
|
75
85
|
# => [[1, 2]]
|
76
|
-
rows = rows.
|
86
|
+
rows = rows.zip if r.columns.one?
|
77
87
|
new(r.columns, rows, overrides, cast: true)
|
78
88
|
end
|
79
89
|
end
|
@@ -88,27 +98,102 @@ module AppQuery
|
|
88
98
|
end
|
89
99
|
|
90
100
|
class Q
|
91
|
-
attr_reader :name, :sql
|
101
|
+
attr_reader :name, :sql, :binds, :cast
|
92
102
|
|
93
|
-
def initialize(sql, name: nil)
|
103
|
+
def initialize(sql, name: nil, filename: nil, binds: [], cast: true)
|
94
104
|
@sql = sql
|
95
105
|
@name = name
|
106
|
+
@filename = filename
|
107
|
+
@binds = binds
|
108
|
+
@cast = cast
|
109
|
+
end
|
110
|
+
|
111
|
+
def deep_dup
|
112
|
+
super.send(:reset!)
|
96
113
|
end
|
97
114
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
|
115
|
+
def reset!
|
116
|
+
(instance_variables - %i[@sql @filename @name @binds @cast]).each do
|
117
|
+
instance_variable_set(_1, nil)
|
118
|
+
end
|
119
|
+
self
|
120
|
+
end
|
121
|
+
private :reset!
|
122
|
+
|
123
|
+
def render(vars)
|
124
|
+
vars ||= {}
|
125
|
+
with_sql(to_erb.result(render_helper(vars).get_binding))
|
126
|
+
end
|
127
|
+
|
128
|
+
def to_erb
|
129
|
+
ERB.new(sql, trim_mode: "-").tap { _1.location = [@filename, 0] if @filename }
|
130
|
+
end
|
131
|
+
private :to_erb
|
132
|
+
|
133
|
+
def render_helper(vars)
|
134
|
+
Module.new do
|
135
|
+
extend self
|
136
|
+
|
137
|
+
vars.each do |k, v|
|
138
|
+
define_method(k) { v }
|
139
|
+
instance_variable_set(:"@#{k}", v)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Examples
|
143
|
+
# <%= order_by({year: :desc, month: :desc}) %>
|
144
|
+
# #=> ORDER BY year DESC, month DESC
|
145
|
+
#
|
146
|
+
# Using variable:
|
147
|
+
# <%= order_by(ordering) %>
|
148
|
+
# NOTE Raises when ordering not provided or when blank.
|
149
|
+
#
|
150
|
+
# Make it optional:
|
151
|
+
# <%= @ordering.presence && order_by(ordering) %>
|
152
|
+
#
|
153
|
+
def order_by(hash)
|
154
|
+
raise ArgumentError, "Provide columns to sort by, e.g. order_by(id: :asc) (got #{hash.inspect})." unless hash.present?
|
155
|
+
"ORDER BY " + hash.map do |k, v|
|
156
|
+
v.nil? ? k : [k, v.upcase].join(" ")
|
157
|
+
end.join(", ")
|
158
|
+
end
|
159
|
+
|
160
|
+
def get_binding
|
161
|
+
binding
|
102
162
|
end
|
103
163
|
end
|
104
164
|
end
|
165
|
+
private :render_helper
|
166
|
+
|
167
|
+
def select_all(binds: [], select: nil, cast: self.cast)
|
168
|
+
binds = binds.presence || @binds
|
169
|
+
with_select(select).render({}).then do |aq|
|
170
|
+
if binds.is_a?(Hash)
|
171
|
+
sql = if ActiveRecord::VERSION::STRING.to_f >= 7.1
|
172
|
+
Arel.sql(aq.to_s, **binds)
|
173
|
+
else
|
174
|
+
ActiveRecord::Base.sanitize_sql_array([aq.to_s, **binds])
|
175
|
+
end
|
176
|
+
ActiveRecord::Base.connection.select_all(sql, name).then do |result|
|
177
|
+
Result.from_ar_result(result, cast)
|
178
|
+
end
|
179
|
+
else
|
180
|
+
ActiveRecord::Base.connection.select_all(aq.to_s, name, binds).then do |result|
|
181
|
+
Result.from_ar_result(result, cast)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
rescue NameError => e
|
186
|
+
# Prevent any subclasses, e.g. NoMethodError
|
187
|
+
raise e unless e.instance_of?(NameError)
|
188
|
+
raise UnrenderedQueryError, "Query is ERB. Use #render before select-ing."
|
189
|
+
end
|
105
190
|
|
106
|
-
def select_one(binds: [], select: nil, cast:
|
107
|
-
select_all(binds:, select:, cast:).first
|
191
|
+
def select_one(binds: [], select: nil, cast: self.cast)
|
192
|
+
select_all(binds:, select:, cast:).first
|
108
193
|
end
|
109
194
|
|
110
|
-
def select_value(binds: [], select: nil, cast:
|
111
|
-
select_one(binds:, select:, cast:)
|
195
|
+
def select_value(binds: [], select: nil, cast: self.cast)
|
196
|
+
select_one(binds:, select:, cast:)&.values&.first
|
112
197
|
end
|
113
198
|
|
114
199
|
def tokens
|
@@ -123,13 +208,31 @@ module AppQuery
|
|
123
208
|
tokens.filter { _1[:t] == "CTE_IDENTIFIER" }.map { _1[:v] }
|
124
209
|
end
|
125
210
|
|
211
|
+
def with_binds(binds)
|
212
|
+
deep_dup.tap do
|
213
|
+
_1.instance_variable_set(:@binds, binds)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def with_cast(cast)
|
218
|
+
deep_dup.tap do
|
219
|
+
_1.instance_variable_set(:@cast, cast)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def with_sql(sql)
|
224
|
+
deep_dup.tap do
|
225
|
+
_1.instance_variable_set(:@sql, sql)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
126
229
|
def with_select(sql)
|
127
|
-
return self
|
230
|
+
return self if sql.nil?
|
128
231
|
if cte_names.include?("_")
|
129
|
-
|
232
|
+
with_sql(tokens.each_with_object([]) do |token, acc|
|
130
233
|
v = (token[:t] == "SELECT") ? sql : token[:v]
|
131
234
|
acc << v
|
132
|
-
end.join
|
235
|
+
end.join)
|
133
236
|
else
|
134
237
|
append_cte("_ as (\n #{select}\n)").with_select(sql)
|
135
238
|
end
|
@@ -152,10 +255,10 @@ module AppQuery
|
|
152
255
|
end
|
153
256
|
|
154
257
|
if cte_names.none?
|
155
|
-
|
258
|
+
with_sql("WITH #{cte}\n#{self}")
|
156
259
|
else
|
157
260
|
split_at_type = recursive? ? "RECURSIVE" : "WITH"
|
158
|
-
|
261
|
+
with_sql(tokens.map do |token|
|
159
262
|
if token[:t] == split_at_type
|
160
263
|
token[:v] + to_append.map { _1[:v] }.join
|
161
264
|
else
|
@@ -175,11 +278,11 @@ module AppQuery
|
|
175
278
|
end
|
176
279
|
|
177
280
|
if cte_names.none?
|
178
|
-
|
281
|
+
with_sql("WITH #{cte}\n#{self}")
|
179
282
|
else
|
180
283
|
nof_ctes = cte_names.size
|
181
284
|
|
182
|
-
|
285
|
+
with_sql(tokens.map do |token|
|
183
286
|
nof_ctes -= 1 if token[:t] == "CTE_SELECT"
|
184
287
|
|
185
288
|
if nof_ctes.zero?
|
@@ -212,7 +315,7 @@ module AppQuery
|
|
212
315
|
|
213
316
|
cte_found = false
|
214
317
|
|
215
|
-
|
318
|
+
with_sql(tokens.map do |token|
|
216
319
|
if cte_found ||= token[:t] == "CTE_IDENTIFIER" && token[:v] == cte_name
|
217
320
|
unless (cte_found = (token[:t] != "CTE_SELECT"))
|
218
321
|
next to_append.map { _1[:v] }.join
|
@@ -243,3 +346,5 @@ rescue LoadError
|
|
243
346
|
end
|
244
347
|
|
245
348
|
require_relative "app_query/rspec" if Object.const_defined? :RSpec
|
349
|
+
|
350
|
+
require "app_query/base" if defined?(ActiveRecord::Base)
|
data/mise.toml
ADDED
metadata
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appquery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gert Goet
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: appraisal
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :development
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
13
26
|
description: "Improving introspection and testability of raw SQL queries in Rails\nThis
|
14
27
|
gem improves introspection and testability of raw SQL queries in Rails by:\n- ...providing
|
15
28
|
a separate query-folder and easy instantiation \n A query like `AppQuery[:some_query]`
|
@@ -27,15 +40,15 @@ executables: []
|
|
27
40
|
extensions: []
|
28
41
|
extra_rdoc_files: []
|
29
42
|
files:
|
30
|
-
- ".envrc"
|
31
|
-
- ".envrc.private.example"
|
32
43
|
- ".rspec"
|
33
44
|
- ".standard.yml"
|
45
|
+
- Appraisals
|
34
46
|
- CHANGELOG.md
|
35
47
|
- LICENSE.txt
|
36
48
|
- README.md
|
37
49
|
- Rakefile
|
38
50
|
- lib/app_query.rb
|
51
|
+
- lib/app_query/base.rb
|
39
52
|
- lib/app_query/rspec.rb
|
40
53
|
- lib/app_query/rspec/helpers.rb
|
41
54
|
- lib/app_query/tokenizer.rb
|
@@ -46,8 +59,9 @@ files:
|
|
46
59
|
- lib/rails/generators/query/templates/query.sql.tt
|
47
60
|
- lib/rails/generators/rspec/query_generator.rb
|
48
61
|
- lib/rails/generators/rspec/templates/query_spec.rb.tt
|
62
|
+
- mise.local.toml.example
|
63
|
+
- mise.toml
|
49
64
|
- sig/appquery.rbs
|
50
|
-
- tmp/.gitkeep
|
51
65
|
homepage: https://github.com/eval/appquery
|
52
66
|
licenses:
|
53
67
|
- MIT
|
@@ -55,7 +69,6 @@ metadata:
|
|
55
69
|
homepage_uri: https://github.com/eval/appquery
|
56
70
|
source_code_uri: https://github.com/eval/appquery
|
57
71
|
changelog_uri: https://github.com/eval/gem-try/blob/main/CHANGELOG.md
|
58
|
-
post_install_message:
|
59
72
|
rdoc_options: []
|
60
73
|
require_paths:
|
61
74
|
- lib
|
@@ -63,15 +76,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
63
76
|
requirements:
|
64
77
|
- - ">="
|
65
78
|
- !ruby/object:Gem::Version
|
66
|
-
version: 3.
|
79
|
+
version: 3.2.0
|
67
80
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
81
|
requirements:
|
69
82
|
- - ">="
|
70
83
|
- !ruby/object:Gem::Version
|
71
84
|
version: '0'
|
72
85
|
requirements: []
|
73
|
-
rubygems_version: 3.
|
74
|
-
signing_key:
|
86
|
+
rubygems_version: 3.6.7
|
75
87
|
specification_version: 4
|
76
88
|
summary: "raw SQL \U0001F966, cooked \U0001F372 or: make working with raw SQL queries
|
77
89
|
in Rails convenient by improving their introspection and testability."
|
data/.envrc
DELETED
data/.envrc.private.example
DELETED
data/tmp/.gitkeep
DELETED
File without changes
|