inquery 1.0.10 → 1.1.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/.github/workflows/rubocop.yml +1 -1
- data/.github/workflows/ruby.yml +24 -8
- data/.gitignore +2 -2
- data/.rubocop.yml +4 -0
- data/Appraisals +24 -4
- data/CHANGELOG.md +47 -1
- data/Gemfile +17 -1
- data/Gemfile.lock +125 -0
- data/LICENSE +1 -1
- data/MIGRATION.md +260 -0
- data/README.md +31 -9
- data/RUBY_VERSION +1 -1
- data/Rakefile +4 -11
- data/VERSION +1 -1
- data/doc/Inquery/Exceptions/Base.html +4 -4
- data/doc/Inquery/Exceptions/InvalidRelation.html +4 -4
- data/doc/Inquery/Exceptions/UnknownCallSignature.html +4 -4
- data/doc/Inquery/Exceptions.html +4 -4
- data/doc/Inquery/MethodAccessibleHash.html +431 -0
- data/doc/Inquery/Mixins/RawSqlUtils.html +17 -4
- data/doc/Inquery/Mixins/RelationValidation/ClassMethods.html +8 -7
- data/doc/Inquery/Mixins/RelationValidation.html +9 -8
- data/doc/Inquery/Mixins/SchemaValidation/ClassMethods.html +20 -6
- data/doc/Inquery/Mixins/SchemaValidation.html +4 -4
- data/doc/Inquery/Mixins.html +4 -4
- data/doc/Inquery/Query/Chainable.html +207 -90
- data/doc/Inquery/Query.html +401 -73
- data/doc/Inquery.html +121 -6
- data/doc/_index.html +12 -5
- data/doc/class_list.html +6 -3
- data/doc/css/full_list.css +3 -3
- data/doc/css/style.css +6 -0
- data/doc/file.README.html +118 -17
- data/doc/file_list.html +5 -2
- data/doc/frames.html +10 -5
- data/doc/index.html +118 -17
- data/doc/js/app.js +294 -264
- data/doc/js/full_list.js +30 -4
- data/doc/method_list.html +52 -17
- data/doc/top-level-namespace.html +4 -4
- data/gemfiles/rails_5.2.gemfile +1 -0
- data/gemfiles/rails_6.0.gemfile +1 -0
- data/gemfiles/rails_6.1.gemfile +1 -0
- data/gemfiles/rails_7.0.gemfile +1 -0
- data/gemfiles/{rails_5.1.gemfile → rails_7.1.gemfile} +2 -1
- data/gemfiles/rails_7.2.gemfile +8 -0
- data/gemfiles/rails_8.0.gemfile +8 -0
- data/gemfiles/rails_8.1.gemfile +8 -0
- data/inquery.gemspec +11 -34
- data/lib/inquery/method_accessible_hash.rb +45 -0
- data/lib/inquery/mixins/raw_sql_utils.rb +32 -6
- data/lib/inquery/mixins/relation_validation.rb +1 -1
- data/lib/inquery/mixins/schema_validation.rb +8 -1
- data/lib/inquery/query/chainable.rb +69 -27
- data/lib/inquery/query.rb +67 -14
- data/lib/inquery.rb +9 -0
- data/test/inquery/error_handling_test.rb +117 -0
- data/test/inquery/method_accessible_hash_test.rb +85 -0
- data/test/inquery/mixins/raw_sql_utils_test.rb +67 -0
- data/test/inquery/query/chainable_test.rb +78 -0
- data/test/inquery/query_test.rb +86 -0
- data/test/test_helper.rb +11 -0
- metadata +30 -129
- data/.yardopts +0 -1
data/doc/js/full_list.js
CHANGED
|
@@ -62,8 +62,25 @@ function enableToggles() {
|
|
|
62
62
|
evt.stopPropagation();
|
|
63
63
|
evt.preventDefault();
|
|
64
64
|
$(this).parent().parent().toggleClass('collapsed');
|
|
65
|
+
$(this).attr('aria-expanded', function (i, attr) {
|
|
66
|
+
return attr == 'true' ? 'false' : 'true'
|
|
67
|
+
});
|
|
65
68
|
highlight();
|
|
66
69
|
});
|
|
70
|
+
|
|
71
|
+
// navigation of nested classes using keyboard
|
|
72
|
+
$('#full_list a.toggle').on('keypress',function(evt) {
|
|
73
|
+
// enter key is pressed
|
|
74
|
+
if (evt.which == 13) {
|
|
75
|
+
evt.stopPropagation();
|
|
76
|
+
evt.preventDefault();
|
|
77
|
+
$(this).parent().parent().toggleClass('collapsed');
|
|
78
|
+
$(this).attr('aria-expanded', function (i, attr) {
|
|
79
|
+
return attr == 'true' ? 'false' : 'true'
|
|
80
|
+
});
|
|
81
|
+
highlight();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
67
84
|
}
|
|
68
85
|
|
|
69
86
|
function populateSearchCache() {
|
|
@@ -91,7 +108,7 @@ function enableSearch() {
|
|
|
91
108
|
}
|
|
92
109
|
});
|
|
93
110
|
|
|
94
|
-
$('#full_list').after("<div id='noresults' style='display:none'></div>");
|
|
111
|
+
$('#full_list').after("<div id='noresults' role='status' style='display: none'></div>");
|
|
95
112
|
}
|
|
96
113
|
|
|
97
114
|
function ignoredKeyPress(event) {
|
|
@@ -154,11 +171,14 @@ function partialSearch(searchString, offset) {
|
|
|
154
171
|
function searchDone() {
|
|
155
172
|
searchTimeout = null;
|
|
156
173
|
highlight();
|
|
157
|
-
|
|
158
|
-
|
|
174
|
+
var found = $('#full_list li:visible').size();
|
|
175
|
+
if (found === 0) {
|
|
176
|
+
$('#noresults').text('No results were found.');
|
|
159
177
|
} else {
|
|
160
|
-
|
|
178
|
+
// This is read out to screen readers
|
|
179
|
+
$('#noresults').text('There are ' + found + ' results.');
|
|
161
180
|
}
|
|
181
|
+
$('#noresults').show();
|
|
162
182
|
$('#content').removeClass('insearch');
|
|
163
183
|
}
|
|
164
184
|
|
|
@@ -188,6 +208,12 @@ function expandTo(path) {
|
|
|
188
208
|
$target.addClass('clicked');
|
|
189
209
|
$target.removeClass('collapsed');
|
|
190
210
|
$target.parentsUntil('#full_list', 'li').removeClass('collapsed');
|
|
211
|
+
|
|
212
|
+
$target.find('a.toggle').attr('aria-expanded', 'true')
|
|
213
|
+
$target.parentsUntil('#full_list', 'li').each(function(i, el) {
|
|
214
|
+
$(el).find('> div > a.toggle').attr('aria-expanded', 'true');
|
|
215
|
+
});
|
|
216
|
+
|
|
191
217
|
if($target[0]) {
|
|
192
218
|
window.scrollTo(window.scrollX, $target.offset().top - 250);
|
|
193
219
|
highlight();
|
data/doc/method_list.html
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
2
|
+
<html >
|
|
3
3
|
<head>
|
|
4
4
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5
5
|
<meta charset="utf-8" />
|
|
@@ -38,7 +38,10 @@
|
|
|
38
38
|
|
|
39
39
|
</div>
|
|
40
40
|
|
|
41
|
-
<div id="search">
|
|
41
|
+
<div id="search">
|
|
42
|
+
<label for="search-class">Search:</label>
|
|
43
|
+
<input id="search-class" type="text" />
|
|
44
|
+
</div>
|
|
42
45
|
</div>
|
|
43
46
|
|
|
44
47
|
<ul id="full_list" class="method">
|
|
@@ -46,8 +49,8 @@
|
|
|
46
49
|
|
|
47
50
|
<li class="odd ">
|
|
48
51
|
<div class="item">
|
|
49
|
-
<span class='object_link'><a href="Inquery/Query
|
|
50
|
-
<small>Inquery::Query
|
|
52
|
+
<span class='object_link'><a href="Inquery/Query.html#call-class_method" title="Inquery::Query.call (method)">call</a></span>
|
|
53
|
+
<small>Inquery::Query</small>
|
|
51
54
|
</div>
|
|
52
55
|
</li>
|
|
53
56
|
|
|
@@ -62,31 +65,31 @@
|
|
|
62
65
|
|
|
63
66
|
<li class="odd ">
|
|
64
67
|
<div class="item">
|
|
65
|
-
<span class='object_link'><a href="Inquery/Query.html#call-class_method" title="Inquery::Query.call (method)">call</a></span>
|
|
66
|
-
<small>Inquery::Query</small>
|
|
68
|
+
<span class='object_link'><a href="Inquery/Query/Chainable.html#call-class_method" title="Inquery::Query::Chainable.call (method)">call</a></span>
|
|
69
|
+
<small>Inquery::Query::Chainable</small>
|
|
67
70
|
</div>
|
|
68
71
|
</li>
|
|
69
72
|
|
|
70
73
|
|
|
71
74
|
<li class="even ">
|
|
72
75
|
<div class="item">
|
|
73
|
-
<span class='object_link'><a href="Inquery/Query
|
|
74
|
-
<small>Inquery::Query
|
|
76
|
+
<span class='object_link'><a href="Inquery/Query.html#connection-instance_method" title="Inquery::Query#connection (method)">#connection</a></span>
|
|
77
|
+
<small>Inquery::Query</small>
|
|
75
78
|
</div>
|
|
76
79
|
</li>
|
|
77
80
|
|
|
78
81
|
|
|
79
82
|
<li class="odd ">
|
|
80
83
|
<div class="item">
|
|
81
|
-
<span class='object_link'><a href="Inquery/Query.html#connection-instance_method" title="Inquery::Query#connection (method)">#connection</a></span>
|
|
82
|
-
<small>Inquery::Query</small>
|
|
84
|
+
<span class='object_link'><a href="Inquery/Query/Chainable.html#connection-instance_method" title="Inquery::Query::Chainable#connection (method)">#connection</a></span>
|
|
85
|
+
<small>Inquery::Query::Chainable</small>
|
|
83
86
|
</div>
|
|
84
87
|
</li>
|
|
85
88
|
|
|
86
89
|
|
|
87
90
|
<li class="even ">
|
|
88
91
|
<div class="item">
|
|
89
|
-
<span class='object_link'><a href="Inquery/Query/Chainable.html#
|
|
92
|
+
<span class='object_link'><a href="Inquery/Query/Chainable.html#initialize-instance_method" title="Inquery::Query::Chainable#initialize (method)">#initialize</a></span>
|
|
90
93
|
<small>Inquery::Query::Chainable</small>
|
|
91
94
|
</div>
|
|
92
95
|
</li>
|
|
@@ -94,8 +97,8 @@
|
|
|
94
97
|
|
|
95
98
|
<li class="odd ">
|
|
96
99
|
<div class="item">
|
|
97
|
-
<span class='object_link'><a href="Inquery/
|
|
98
|
-
<small>Inquery::
|
|
100
|
+
<span class='object_link'><a href="Inquery/MethodAccessibleHash.html#initialize-instance_method" title="Inquery::MethodAccessibleHash#initialize (method)">#initialize</a></span>
|
|
101
|
+
<small>Inquery::MethodAccessibleHash</small>
|
|
99
102
|
</div>
|
|
100
103
|
</li>
|
|
101
104
|
|
|
@@ -108,6 +111,22 @@
|
|
|
108
111
|
</li>
|
|
109
112
|
|
|
110
113
|
|
|
114
|
+
<li class="odd ">
|
|
115
|
+
<div class="item">
|
|
116
|
+
<span class='object_link'><a href="Inquery/MethodAccessibleHash.html#merge-instance_method" title="Inquery::MethodAccessibleHash#merge (method)">#merge</a></span>
|
|
117
|
+
<small>Inquery::MethodAccessibleHash</small>
|
|
118
|
+
</div>
|
|
119
|
+
</li>
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
<li class="even ">
|
|
123
|
+
<div class="item">
|
|
124
|
+
<span class='object_link'><a href="Inquery/MethodAccessibleHash.html#method_missing-instance_method" title="Inquery::MethodAccessibleHash#method_missing (method)">#method_missing</a></span>
|
|
125
|
+
<small>Inquery::MethodAccessibleHash</small>
|
|
126
|
+
</div>
|
|
127
|
+
</li>
|
|
128
|
+
|
|
129
|
+
|
|
111
130
|
<li class="odd ">
|
|
112
131
|
<div class="item">
|
|
113
132
|
<span class='object_link'><a href="Inquery/Query.html#osparams-instance_method" title="Inquery::Query#osparams (method)">#osparams</a></span>
|
|
@@ -149,6 +168,14 @@
|
|
|
149
168
|
|
|
150
169
|
|
|
151
170
|
<li class="even ">
|
|
171
|
+
<div class="item">
|
|
172
|
+
<span class='object_link'><a href="Inquery/MethodAccessibleHash.html#respond_to_missing%3F-instance_method" title="Inquery::MethodAccessibleHash#respond_to_missing? (method)">#respond_to_missing?</a></span>
|
|
173
|
+
<small>Inquery::MethodAccessibleHash</small>
|
|
174
|
+
</div>
|
|
175
|
+
</li>
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
<li class="odd ">
|
|
152
179
|
<div class="item">
|
|
153
180
|
<span class='object_link'><a href="Inquery/Query.html#run-instance_method" title="Inquery::Query#run (method)">#run</a></span>
|
|
154
181
|
<small>Inquery::Query</small>
|
|
@@ -156,7 +183,7 @@
|
|
|
156
183
|
</li>
|
|
157
184
|
|
|
158
185
|
|
|
159
|
-
<li class="
|
|
186
|
+
<li class="even ">
|
|
160
187
|
<div class="item">
|
|
161
188
|
<span class='object_link'><a href="Inquery/Query.html#run-class_method" title="Inquery::Query.run (method)">run</a></span>
|
|
162
189
|
<small>Inquery::Query</small>
|
|
@@ -164,7 +191,7 @@
|
|
|
164
191
|
</li>
|
|
165
192
|
|
|
166
193
|
|
|
167
|
-
<li class="
|
|
194
|
+
<li class="odd ">
|
|
168
195
|
<div class="item">
|
|
169
196
|
<span class='object_link'><a href="Inquery/Mixins/SchemaValidation/ClassMethods.html#schema-instance_method" title="Inquery::Mixins::SchemaValidation::ClassMethods#schema (method)">#schema</a></span>
|
|
170
197
|
<small>Inquery::Mixins::SchemaValidation::ClassMethods</small>
|
|
@@ -172,7 +199,7 @@
|
|
|
172
199
|
</li>
|
|
173
200
|
|
|
174
201
|
|
|
175
|
-
<li class="
|
|
202
|
+
<li class="even ">
|
|
176
203
|
<div class="item">
|
|
177
204
|
<span class='object_link'><a href="Inquery/Mixins/SchemaValidation/ClassMethods.html#schema2-instance_method" title="Inquery::Mixins::SchemaValidation::ClassMethods#schema2 (method)">#schema2</a></span>
|
|
178
205
|
<small>Inquery::Mixins::SchemaValidation::ClassMethods</small>
|
|
@@ -180,7 +207,7 @@
|
|
|
180
207
|
</li>
|
|
181
208
|
|
|
182
209
|
|
|
183
|
-
<li class="
|
|
210
|
+
<li class="odd ">
|
|
184
211
|
<div class="item">
|
|
185
212
|
<span class='object_link'><a href="Inquery/Mixins/SchemaValidation/ClassMethods.html#schema3-instance_method" title="Inquery::Mixins::SchemaValidation::ClassMethods#schema3 (method)">#schema3</a></span>
|
|
186
213
|
<small>Inquery::Mixins::SchemaValidation::ClassMethods</small>
|
|
@@ -188,6 +215,14 @@
|
|
|
188
215
|
</li>
|
|
189
216
|
|
|
190
217
|
|
|
218
|
+
<li class="even ">
|
|
219
|
+
<div class="item">
|
|
220
|
+
<span class='object_link'><a href="Inquery.html#setup-class_method" title="Inquery.setup (method)">setup</a></span>
|
|
221
|
+
<small>Inquery</small>
|
|
222
|
+
</div>
|
|
223
|
+
</li>
|
|
224
|
+
|
|
225
|
+
|
|
191
226
|
<li class="odd ">
|
|
192
227
|
<div class="item">
|
|
193
228
|
<span class='object_link'><a href="Inquery/Mixins/RelationValidation.html#validate_relation!-instance_method" title="Inquery::Mixins::RelationValidation#validate_relation! (method)">#validate_relation!</a></span>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>
|
|
7
7
|
Top Level Namespace
|
|
8
8
|
|
|
9
|
-
— Documentation by YARD 0.9.
|
|
9
|
+
— Documentation by YARD 0.9.37
|
|
10
10
|
|
|
11
11
|
</title>
|
|
12
12
|
|
|
@@ -100,9 +100,9 @@
|
|
|
100
100
|
</div>
|
|
101
101
|
|
|
102
102
|
<div id="footer">
|
|
103
|
-
Generated on
|
|
104
|
-
<a href="
|
|
105
|
-
0.9.
|
|
103
|
+
Generated on Mon Jan 5 14:06:48 2026 by
|
|
104
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
105
|
+
0.9.37 (ruby-3.3.5).
|
|
106
106
|
</div>
|
|
107
107
|
|
|
108
108
|
</div>
|
data/gemfiles/rails_5.2.gemfile
CHANGED
data/gemfiles/rails_6.0.gemfile
CHANGED
data/gemfiles/rails_6.1.gemfile
CHANGED
data/gemfiles/rails_7.0.gemfile
CHANGED
data/inquery.gemspec
CHANGED
|
@@ -1,46 +1,23 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: inquery 1.0
|
|
2
|
+
# stub: inquery 1.1.0 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "inquery".freeze
|
|
6
|
-
s.version = "1.0.
|
|
6
|
+
s.version = "1.1.0".freeze
|
|
7
7
|
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
|
9
9
|
s.require_paths = ["lib".freeze]
|
|
10
10
|
s.authors = ["Sitrox".freeze]
|
|
11
|
-
s.date = "
|
|
12
|
-
s.files = [".github/workflows/rubocop.yml".freeze, ".github/workflows/ruby.yml".freeze, ".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, "
|
|
13
|
-
s.
|
|
11
|
+
s.date = "2026-01-05"
|
|
12
|
+
s.files = [".github/workflows/rubocop.yml".freeze, ".github/workflows/ruby.yml".freeze, ".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, "Appraisals".freeze, "CHANGELOG.md".freeze, "Gemfile".freeze, "Gemfile.lock".freeze, "LICENSE".freeze, "MIGRATION.md".freeze, "README.md".freeze, "RUBY_VERSION".freeze, "Rakefile".freeze, "VERSION".freeze, "doc/Inquery.html".freeze, "doc/Inquery/Exceptions.html".freeze, "doc/Inquery/Exceptions/Base.html".freeze, "doc/Inquery/Exceptions/InvalidRelation.html".freeze, "doc/Inquery/Exceptions/UnknownCallSignature.html".freeze, "doc/Inquery/MethodAccessibleHash.html".freeze, "doc/Inquery/Mixins.html".freeze, "doc/Inquery/Mixins/RawSqlUtils.html".freeze, "doc/Inquery/Mixins/RelationValidation.html".freeze, "doc/Inquery/Mixins/RelationValidation/ClassMethods.html".freeze, "doc/Inquery/Mixins/SchemaValidation.html".freeze, "doc/Inquery/Mixins/SchemaValidation/ClassMethods.html".freeze, "doc/Inquery/Query.html".freeze, "doc/Inquery/Query/Chainable.html".freeze, "doc/_index.html".freeze, "doc/class_list.html".freeze, "doc/css/common.css".freeze, "doc/css/full_list.css".freeze, "doc/css/style.css".freeze, "doc/file.README.html".freeze, "doc/file_list.html".freeze, "doc/frames.html".freeze, "doc/index.html".freeze, "doc/js/app.js".freeze, "doc/js/full_list.js".freeze, "doc/js/jquery.js".freeze, "doc/method_list.html".freeze, "doc/top-level-namespace.html".freeze, "gemfiles/rails_5.2.gemfile".freeze, "gemfiles/rails_6.0.gemfile".freeze, "gemfiles/rails_6.1.gemfile".freeze, "gemfiles/rails_7.0.gemfile".freeze, "gemfiles/rails_7.1.gemfile".freeze, "gemfiles/rails_7.2.gemfile".freeze, "gemfiles/rails_8.0.gemfile".freeze, "gemfiles/rails_8.1.gemfile".freeze, "inquery.gemspec".freeze, "lib/inquery.rb".freeze, "lib/inquery/exceptions.rb".freeze, "lib/inquery/method_accessible_hash.rb".freeze, "lib/inquery/mixins/raw_sql_utils.rb".freeze, "lib/inquery/mixins/relation_validation.rb".freeze, "lib/inquery/mixins/schema_validation.rb".freeze, "lib/inquery/query.rb".freeze, "lib/inquery/query/chainable.rb".freeze, "test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/inquery/error_handling_test.rb".freeze, "test/inquery/method_accessible_hash_test.rb".freeze, "test/inquery/mixins/raw_sql_utils_test.rb".freeze, "test/inquery/query/chainable_test.rb".freeze, "test/inquery/query_test.rb".freeze, "test/queries/group/fetch_as_json.rb".freeze, "test/queries/group/fetch_green.rb".freeze, "test/queries/group/fetch_red.rb".freeze, "test/queries/group/filter_with_color.rb".freeze, "test/queries/user/fetch_all.rb".freeze, "test/queries/user/fetch_in_group.rb".freeze, "test/queries/user/fetch_in_group_rel.rb".freeze, "test/test_helper.rb".freeze]
|
|
13
|
+
s.homepage = "https://github.com/sitrox/inquery".freeze
|
|
14
|
+
s.rubygems_version = "3.5.18".freeze
|
|
14
15
|
s.summary = "A skeleton that allows extracting queries into atomic, reusable classes.".freeze
|
|
15
|
-
s.test_files = ["test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/inquery/query/chainable_test.rb".freeze, "test/inquery/query_test.rb".freeze, "test/queries/group/fetch_as_json.rb".freeze, "test/queries/group/fetch_green.rb".freeze, "test/queries/group/fetch_red.rb".freeze, "test/queries/group/filter_with_color.rb".freeze, "test/queries/user/fetch_all.rb".freeze, "test/queries/user/fetch_in_group.rb".freeze, "test/queries/user/fetch_in_group_rel.rb".freeze, "test/test_helper.rb".freeze]
|
|
16
|
+
s.test_files = ["test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/inquery/error_handling_test.rb".freeze, "test/inquery/method_accessible_hash_test.rb".freeze, "test/inquery/mixins/raw_sql_utils_test.rb".freeze, "test/inquery/query/chainable_test.rb".freeze, "test/inquery/query_test.rb".freeze, "test/queries/group/fetch_as_json.rb".freeze, "test/queries/group/fetch_green.rb".freeze, "test/queries/group/fetch_red.rb".freeze, "test/queries/group/filter_with_color.rb".freeze, "test/queries/user/fetch_all.rb".freeze, "test/queries/user/fetch_in_group.rb".freeze, "test/queries/user/fetch_in_group_rel.rb".freeze, "test/test_helper.rb".freeze]
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
s.specification_version = 4
|
|
19
|
-
end
|
|
18
|
+
s.specification_version = 4
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
s.add_development_dependency(%q<sqlite3>.freeze, [">= 0"])
|
|
25
|
-
s.add_development_dependency(%q<haml>.freeze, [">= 0"])
|
|
26
|
-
s.add_development_dependency(%q<yard>.freeze, [">= 0"])
|
|
27
|
-
s.add_development_dependency(%q<rubocop>.freeze, ["= 1.25"])
|
|
28
|
-
s.add_development_dependency(%q<redcarpet>.freeze, [">= 0"])
|
|
29
|
-
s.add_runtime_dependency(%q<minitest>.freeze, [">= 0"])
|
|
30
|
-
s.add_runtime_dependency(%q<activesupport>.freeze, [">= 0"])
|
|
31
|
-
s.add_runtime_dependency(%q<activerecord>.freeze, [">= 0"])
|
|
32
|
-
s.add_runtime_dependency(%q<schemacop>.freeze, ["~> 3.0.8"])
|
|
33
|
-
else
|
|
34
|
-
s.add_dependency(%q<appraisal>.freeze, [">= 0"])
|
|
35
|
-
s.add_dependency(%q<rake>.freeze, [">= 0"])
|
|
36
|
-
s.add_dependency(%q<sqlite3>.freeze, [">= 0"])
|
|
37
|
-
s.add_dependency(%q<haml>.freeze, [">= 0"])
|
|
38
|
-
s.add_dependency(%q<yard>.freeze, [">= 0"])
|
|
39
|
-
s.add_dependency(%q<rubocop>.freeze, ["= 1.25"])
|
|
40
|
-
s.add_dependency(%q<redcarpet>.freeze, [">= 0"])
|
|
41
|
-
s.add_dependency(%q<minitest>.freeze, [">= 0"])
|
|
42
|
-
s.add_dependency(%q<activesupport>.freeze, [">= 0"])
|
|
43
|
-
s.add_dependency(%q<activerecord>.freeze, [">= 0"])
|
|
44
|
-
s.add_dependency(%q<schemacop>.freeze, ["~> 3.0.8"])
|
|
45
|
-
end
|
|
20
|
+
s.add_runtime_dependency(%q<activesupport>.freeze, [">= 5.1".freeze])
|
|
21
|
+
s.add_runtime_dependency(%q<activerecord>.freeze, [">= 5.1".freeze])
|
|
22
|
+
s.add_runtime_dependency(%q<schemacop>.freeze, [">= 3.0.8".freeze, "< 4.0".freeze])
|
|
46
23
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Inquery
|
|
2
|
+
# A safe alternative for OpenStruct in Ruby. It behaves exactly the same, but
|
|
3
|
+
# does not define methods on-the-fly but uses `method_missing` instead.
|
|
4
|
+
#
|
|
5
|
+
# Usage example:
|
|
6
|
+
#
|
|
7
|
+
# ```ruby
|
|
8
|
+
# default_options = { foo: :bar }
|
|
9
|
+
# options = MethodAccessibleHash.new(default_options)
|
|
10
|
+
# options[:color] = :green
|
|
11
|
+
# options.foo # => :bar
|
|
12
|
+
# options.color # => green
|
|
13
|
+
# ```
|
|
14
|
+
class MethodAccessibleHash < ::Hash
|
|
15
|
+
# Takes an optional hash as argument and constructs a new
|
|
16
|
+
# MethodAccessibleHash.
|
|
17
|
+
def initialize(hash = {})
|
|
18
|
+
super()
|
|
19
|
+
|
|
20
|
+
hash.each do |key, value|
|
|
21
|
+
self[key.to_sym] = value
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @private
|
|
26
|
+
def merge(**hash)
|
|
27
|
+
super(hash.symbolize_keys)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @private
|
|
31
|
+
def method_missing(method, *args, &_block)
|
|
32
|
+
if method.to_s.end_with?('=')
|
|
33
|
+
name = method.to_s.gsub(/=$/, '')
|
|
34
|
+
self[name.to_sym] = args.first
|
|
35
|
+
else
|
|
36
|
+
self[method.to_sym]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @private
|
|
41
|
+
def respond_to_missing?(_method, _include_private = false)
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -1,19 +1,45 @@
|
|
|
1
1
|
module Inquery
|
|
2
2
|
module Mixins
|
|
3
|
+
# Provides utilities for working with raw SQL queries in a safe way.
|
|
4
|
+
#
|
|
5
|
+
# This mixin adds two helper methods for executing raw SQL queries:
|
|
6
|
+
# 'san' for sanitizing SQL with parameter substitution, and 'exec_query'
|
|
7
|
+
# for executing sanitized SQL and returning results.
|
|
3
8
|
module RawSqlUtils
|
|
4
9
|
extend ActiveSupport::Concern
|
|
5
10
|
|
|
6
11
|
included do
|
|
7
|
-
# Sanitizes
|
|
8
|
-
#
|
|
12
|
+
# Sanitizes SQL and substitutes variables using ActiveRecord's
|
|
13
|
+
# parameterized query mechanism.
|
|
14
|
+
#
|
|
15
|
+
# This method uses ActiveRecord's 'sanitize_sql_array' to safely
|
|
16
|
+
# escape values and prevent SQL injection. Always use this instead of
|
|
17
|
+
# string interpolation when building SQL queries.
|
|
18
|
+
#
|
|
19
|
+
# Example:
|
|
20
|
+
# sql = san("SELECT * FROM users WHERE age > ? AND city = ?", 18, 'NYC')
|
|
21
|
+
# # => "SELECT * FROM users WHERE age > 18 AND city = 'NYC'"
|
|
22
|
+
#
|
|
23
|
+
# @param sql [String] SQL query with '?' placeholders
|
|
24
|
+
# @param variables [Array] Values to substitute for placeholders
|
|
25
|
+
# @return [String] Sanitized SQL with values substituted
|
|
9
26
|
def san(sql, *variables)
|
|
10
27
|
ActiveRecord::Base.send(:sanitize_sql_array, [sql, *variables])
|
|
11
28
|
end
|
|
12
29
|
|
|
13
|
-
# Executes
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
30
|
+
# Executes sanitized SQL and returns the results.
|
|
31
|
+
#
|
|
32
|
+
# The SQL should already be sanitized using the 'san' method or
|
|
33
|
+
# ActiveRecord's query methods. Uses the connection from the 'connection'
|
|
34
|
+
# method, which must be defined in the including class.
|
|
35
|
+
#
|
|
36
|
+
# Example:
|
|
37
|
+
# sql = san("SELECT COUNT(*) as total FROM users WHERE active = ?", true)
|
|
38
|
+
# result = exec_query(sql)
|
|
39
|
+
# result.first['total'] # => 42
|
|
40
|
+
#
|
|
41
|
+
# @param sql [String] Sanitized SQL query to execute
|
|
42
|
+
# @return [ActiveRecord::Result] Query results
|
|
17
43
|
def exec_query(sql)
|
|
18
44
|
connection.exec_query(sql, self.class.to_s)
|
|
19
45
|
end
|
|
@@ -91,7 +91,7 @@ module Inquery
|
|
|
91
91
|
# ---------------------------------------------------------------
|
|
92
92
|
fields_count = relation.select_values.size
|
|
93
93
|
|
|
94
|
-
if fields_count.zero? && options[:default_select] && options[:fields]
|
|
94
|
+
if fields_count.zero? && options[:default_select] && options[:fields]&.positive?
|
|
95
95
|
relation = relation.select(options[:default_select])
|
|
96
96
|
fields_count = 1
|
|
97
97
|
end
|
|
@@ -23,7 +23,14 @@ module Inquery
|
|
|
23
23
|
|
|
24
24
|
# @see schema2
|
|
25
25
|
def schema(*args, &block)
|
|
26
|
-
|
|
26
|
+
case Inquery.default_schema_version
|
|
27
|
+
when 2
|
|
28
|
+
schema2(*args, &block)
|
|
29
|
+
when 3
|
|
30
|
+
schema3(*args, &block)
|
|
31
|
+
else
|
|
32
|
+
fail 'Schemacop schema versions supported are 2 and 3.'
|
|
33
|
+
end
|
|
27
34
|
end
|
|
28
35
|
end
|
|
29
36
|
end
|
|
@@ -1,25 +1,66 @@
|
|
|
1
1
|
module Inquery
|
|
2
|
+
# Chainable query class for queries that input and output ActiveRecord relations.
|
|
3
|
+
#
|
|
4
|
+
# Use this class when you want to build queries that can be chained together
|
|
5
|
+
# or used as ActiveRecord scopes. The query receives a relation, transforms
|
|
6
|
+
# it, and returns a new relation.
|
|
7
|
+
#
|
|
8
|
+
# Example:
|
|
9
|
+
# class FetchActive < Inquery::Query::Chainable
|
|
10
|
+
# relation class: 'User'
|
|
11
|
+
#
|
|
12
|
+
# def call
|
|
13
|
+
# relation.where(active: true)
|
|
14
|
+
# end
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# User.all.then { |rel| FetchActive.run(rel) }
|
|
18
|
+
# # Or as a scope:
|
|
19
|
+
# class User < ActiveRecord::Base
|
|
20
|
+
# scope :active, FetchActive
|
|
21
|
+
# end
|
|
2
22
|
class Query::Chainable < Query
|
|
3
23
|
include Inquery::Mixins::RelationValidation
|
|
4
24
|
|
|
5
|
-
#
|
|
25
|
+
# Instantiates the query and executes it, allowing use as an AR scope.
|
|
26
|
+
#
|
|
27
|
+
# This enables chainable queries to be used directly as ActiveRecord scopes:
|
|
28
|
+
# scope :active, FetchActive
|
|
29
|
+
#
|
|
30
|
+
# @param args [Array] Arguments passed to initialize (relation and/or params)
|
|
31
|
+
# @return [ActiveRecord::Relation] The transformed relation
|
|
6
32
|
def self.call(*args)
|
|
7
33
|
return new(*args).call
|
|
8
34
|
end
|
|
9
35
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
36
|
+
# The input ActiveRecord relation that will be transformed by this query.
|
|
37
|
+
#
|
|
38
|
+
# @return [ActiveRecord::Relation]
|
|
14
39
|
attr_reader :relation
|
|
15
40
|
|
|
41
|
+
# Initializes a chainable query with a relation and optional parameters.
|
|
42
|
+
#
|
|
43
|
+
# Supports multiple call signatures:
|
|
44
|
+
# new() - Uses default relation from 'relation' DSL
|
|
45
|
+
# new(relation) - Uses provided relation
|
|
46
|
+
# new(params) - Uses default relation with params
|
|
47
|
+
# new(relation, params) - Uses provided relation and params
|
|
48
|
+
#
|
|
49
|
+
# @param args [Array] Variable arguments for relation and/or params
|
|
50
|
+
# @raise [Inquery::Exceptions::UnknownCallSignature] for invalid arguments
|
|
51
|
+
# @raise [Inquery::Exceptions::InvalidRelation] if relation validation fails
|
|
16
52
|
def initialize(*args)
|
|
17
53
|
relation, params = parse_init_args(*args)
|
|
18
54
|
@relation = validate_relation!(relation)
|
|
19
55
|
super(params)
|
|
20
56
|
end
|
|
21
57
|
|
|
22
|
-
#
|
|
58
|
+
# Returns the database connection from the relation.
|
|
59
|
+
#
|
|
60
|
+
# This ensures that the query uses the same connection as the input
|
|
61
|
+
# relation, which is important for connection pooling and transactions.
|
|
62
|
+
#
|
|
63
|
+
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
|
23
64
|
def connection
|
|
24
65
|
@relation.connection
|
|
25
66
|
end
|
|
@@ -27,32 +68,33 @@ module Inquery
|
|
|
27
68
|
private
|
|
28
69
|
|
|
29
70
|
def parse_init_args(*args)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
71
|
+
first_arg = args[0]
|
|
72
|
+
second_arg = args[1]
|
|
73
|
+
|
|
74
|
+
# new() - no arguments
|
|
75
|
+
return [nil, {}] if args.empty?
|
|
34
76
|
|
|
35
|
-
# new(params)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
77
|
+
# new(relation) or new(params)
|
|
78
|
+
if second_arg.nil?
|
|
79
|
+
if relation_like?(first_arg)
|
|
80
|
+
return [first_arg, {}]
|
|
81
|
+
elsif first_arg.is_a?(Hash)
|
|
82
|
+
return [nil, first_arg]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
39
85
|
|
|
40
86
|
# new(relation, params)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
params = args[1]
|
|
44
|
-
|
|
45
|
-
# new()
|
|
46
|
-
elsif args.empty?
|
|
47
|
-
relation = nil
|
|
48
|
-
params = {}
|
|
49
|
-
|
|
50
|
-
# Unknown
|
|
51
|
-
else
|
|
52
|
-
fail Inquery::Exceptions::UnknownCallSignature, "Unknown call signature for the query constructor: #{args.collect(&:class)}."
|
|
87
|
+
if relation_like?(first_arg) && second_arg.is_a?(Hash)
|
|
88
|
+
return [first_arg, second_arg]
|
|
53
89
|
end
|
|
54
90
|
|
|
55
|
-
|
|
91
|
+
# Unknown signature
|
|
92
|
+
fail Inquery::Exceptions::UnknownCallSignature,
|
|
93
|
+
"Unknown call signature for the query constructor: #{args.collect(&:class)}."
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def relation_like?(obj)
|
|
97
|
+
obj.is_a?(ActiveRecord::Relation) || (obj.class < ActiveRecord::Base)
|
|
56
98
|
end
|
|
57
99
|
end
|
|
58
100
|
end
|