inquery 0.0.1
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 +7 -0
- data/.gitignore +16 -0
- data/.releaser_config +4 -0
- data/.rubocop.yml +42 -0
- data/.travis.yml +9 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +288 -0
- data/RUBY_VERSION +1 -0
- data/Rakefile +36 -0
- data/VERSION +1 -0
- data/doc/Inquery.html +119 -0
- data/doc/Inquery/Exceptions.html +115 -0
- data/doc/Inquery/Exceptions/Base.html +127 -0
- data/doc/Inquery/Exceptions/InvalidRelation.html +131 -0
- data/doc/Inquery/Exceptions/UnknownCallSignature.html +131 -0
- data/doc/Inquery/Mixins.html +117 -0
- data/doc/Inquery/Mixins/RelationValidation.html +334 -0
- data/doc/Inquery/Mixins/RelationValidation/ClassMethods.html +190 -0
- data/doc/Inquery/Mixins/SchemaValidation.html +124 -0
- data/doc/Inquery/Mixins/SchemaValidation/ClassMethods.html +192 -0
- data/doc/Inquery/Query.html +736 -0
- data/doc/Inquery/Query/Chainable.html +476 -0
- data/doc/_index.html +254 -0
- data/doc/class_list.html +58 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file.README.html +365 -0
- data/doc/file_list.html +60 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +365 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +181 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +147 -0
- data/doc/top-level-namespace.html +112 -0
- data/inquery.gemspec +58 -0
- data/lib/inquery.rb +10 -0
- data/lib/inquery/exceptions.rb +7 -0
- data/lib/inquery/mixins/relation_validation.rb +100 -0
- data/lib/inquery/mixins/schema_validation.rb +27 -0
- data/lib/inquery/query.rb +50 -0
- data/lib/inquery/query/chainable.rb +53 -0
- data/test/db/models.rb +20 -0
- data/test/db/schema.rb +20 -0
- data/test/inquery/query/chainable_test.rb +67 -0
- data/test/inquery/query_test.rb +47 -0
- data/test/queries/group/fetch_as_json.rb +13 -0
- data/test/queries/group/fetch_green.rb +11 -0
- data/test/queries/group/fetch_red.rb +11 -0
- data/test/queries/group/filter_with_color.rb +12 -0
- data/test/queries/user/fetch_all.rb +9 -0
- data/test/queries/user/fetch_in_group.rb +13 -0
- data/test/queries/user/fetch_in_group_rel.rb +17 -0
- data/test/test_helper.rb +26 -0
- metadata +265 -0
data/doc/index.html
ADDED
@@ -0,0 +1,365 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
6
|
+
<title>
|
7
|
+
File: README
|
8
|
+
|
9
|
+
— Documentation by YARD 0.8.7.6
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
hasFrames = window.top.frames.main ? true : false;
|
19
|
+
relpath = '';
|
20
|
+
framesUrl = "frames.html#!file.README.html";
|
21
|
+
</script>
|
22
|
+
|
23
|
+
|
24
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
25
|
+
|
26
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
27
|
+
|
28
|
+
|
29
|
+
</head>
|
30
|
+
<body>
|
31
|
+
<div id="header">
|
32
|
+
<div id="menu">
|
33
|
+
|
34
|
+
<a href="_index.html">Index</a> »
|
35
|
+
<span class="title">File: README</span>
|
36
|
+
|
37
|
+
|
38
|
+
<div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div id="search">
|
42
|
+
|
43
|
+
<a class="full_list_link" id="class_list_link"
|
44
|
+
href="class_list.html">
|
45
|
+
Class List
|
46
|
+
</a>
|
47
|
+
|
48
|
+
<a class="full_list_link" id="method_list_link"
|
49
|
+
href="method_list.html">
|
50
|
+
Method List
|
51
|
+
</a>
|
52
|
+
|
53
|
+
<a class="full_list_link" id="file_list_link"
|
54
|
+
href="file_list.html">
|
55
|
+
File List
|
56
|
+
</a>
|
57
|
+
|
58
|
+
</div>
|
59
|
+
<div class="clear"></div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<iframe id="search_frame"></iframe>
|
63
|
+
|
64
|
+
<div id="content"><div id='filecontents'>
|
65
|
+
<h1 id="label-Inquery">Inquery</h1>
|
66
|
+
|
67
|
+
<p>A skeleton that allows extracting queries into atomic, reusable classes.</p>
|
68
|
+
|
69
|
+
<h2 id="label-Installation">Installation</h2>
|
70
|
+
|
71
|
+
<p>To install the <strong>Inquery</strong> gem:</p>
|
72
|
+
|
73
|
+
<pre class="code ruby"><code class="ruby">$ gem install inquery</code></pre>
|
74
|
+
|
75
|
+
<p>To install it using <code>bundler</code> (recommended for any application),
|
76
|
+
add it to your <code>Gemfile</code>:</p>
|
77
|
+
|
78
|
+
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_gem'>gem</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>inquery</span><span class='tstring_end'>'</span></span>
|
79
|
+
</code></pre>
|
80
|
+
|
81
|
+
<h2 id="label-Basic+usage">Basic usage</h2>
|
82
|
+
|
83
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>FetchUsersWithACar</span> <span class='op'><</span> <span class='const'>Inquery</span><span class='op'>::</span><span class='const'>Query</span>
|
84
|
+
<span class='id identifier rubyid_schema'>schema</span><span class='lparen'>(</span>
|
85
|
+
<span class='label'>color:</span> <span class='symbol'>:symbol</span>
|
86
|
+
<span class='rparen'>)</span>
|
87
|
+
|
88
|
+
<span class='kw'>def</span> <span class='id identifier rubyid_call'>call</span>
|
89
|
+
<span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_joins'>joins</span><span class='lparen'>(</span><span class='symbol'>:cars</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='label'>cars:</span> <span class='lbrace'>{</span> <span class='label'>color:</span> <span class='id identifier rubyid_osparams'>osparams</span><span class='period'>.</span><span class='id identifier rubyid_color'>color</span> <span class='rbrace'>}</span><span class='rparen'>)</span>
|
90
|
+
<span class='kw'>end</span>
|
91
|
+
<span class='kw'>end</span>
|
92
|
+
|
93
|
+
<span class='const'>FetchUsersWithACar</span><span class='period'>.</span><span class='id identifier rubyid_run'>run</span>
|
94
|
+
<span class='comment'># => [<User id: 1 ...]
|
95
|
+
</span></code></pre>
|
96
|
+
|
97
|
+
<p>Inquery offers its functionality trough two query base classes:
|
98
|
+
<span class='object_link'><a href="Inquery/Query.html" title="Inquery::Query (class)">Inquery::Query</a></span> and <span class='object_link'><a href="Inquery/Query/Chainable.html" title="Inquery::Query::Chainable (class)">Inquery::Query::Chainable</a></span>. See the following
|
99
|
+
sections for detailed explanations.</p>
|
100
|
+
|
101
|
+
<h2 id="label-Basic+queries">Basic queries</h2>
|
102
|
+
|
103
|
+
<p>Basic queries inherit from <span class='object_link'><a href="Inquery/Query.html" title="Inquery::Query (class)">Inquery::Query</a></span>. They receive an optional set
|
104
|
+
of parameters and commonly return a relation / AR result. An optional
|
105
|
+
<code>process</code> method lets you perform additional result processing
|
106
|
+
steps if needed (i.e. converting the result to a hash or similar).</p>
|
107
|
+
|
108
|
+
<p>For this basic functionality, inherit from <span class='object_link'><a href="Inquery/Query.html" title="Inquery::Query (class)">Inquery::Query</a></span> and overwrite
|
109
|
+
the <code>call</code> and optionally the <code>process</code> method:</p>
|
110
|
+
|
111
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>FetchRedCarsAsJson</span>
|
112
|
+
<span class='comment'># The `call` method must be overwritten for every query. It is usually called
|
113
|
+
</span> <span class='comment'># via `run`.
|
114
|
+
</span> <span class='kw'>def</span> <span class='id identifier rubyid_call'>call</span>
|
115
|
+
<span class='const'>Car</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='label'>color:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>red</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span>
|
116
|
+
<span class='kw'>end</span>
|
117
|
+
|
118
|
+
<span class='comment'># The `process` method can optionally be overwritten. The base implementation
|
119
|
+
</span> <span class='comment'># just returns the unprocessed `results` argument.
|
120
|
+
</span> <span class='kw'>def</span> <span class='id identifier rubyid_process'>process</span><span class='lparen'>(</span><span class='id identifier rubyid_results'>results</span><span class='rparen'>)</span>
|
121
|
+
<span class='id identifier rubyid_results'>results</span><span class='period'>.</span><span class='id identifier rubyid_to_json'>to_json</span>
|
122
|
+
<span class='kw'>end</span>
|
123
|
+
<span class='kw'>end</span>
|
124
|
+
</code></pre>
|
125
|
+
|
126
|
+
<p>Queries can be called in various ways:</p>
|
127
|
+
|
128
|
+
<pre class="code ruby"><code class="ruby"><span class='comment'># Instantiates the query class and runs `call` and `process`.
|
129
|
+
</span><span class='const'>FetchRedCarsAsJson</span><span class='period'>.</span><span class='id identifier rubyid_run'>run</span><span class='lparen'>(</span><span class='id identifier rubyid_params'>params</span> <span class='op'>=</span> <span class='lbrace'>{</span><span class='rbrace'>}</span><span class='rparen'>)</span>
|
130
|
+
|
131
|
+
<span class='comment'># Instantiates the query class and runs `call`. No result processing
|
132
|
+
</span><span class='comment'># is done.
|
133
|
+
</span><span class='const'>FetchRedCarsAsJson</span><span class='period'>.</span><span class='id identifier rubyid_call'>call</span><span class='lparen'>(</span><span class='id identifier rubyid_params'>params</span> <span class='op'>=</span> <span class='lbrace'>{</span><span class='rbrace'>}</span><span class='rparen'>)</span>
|
134
|
+
|
135
|
+
<span class='comment'># You can also instantiate the query class manually.
|
136
|
+
</span><span class='const'>FetchRedCarsAsJson</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='id identifier rubyid_params'>params</span> <span class='op'>=</span> <span class='lbrace'>{</span><span class='rbrace'>}</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_run'>run</span>
|
137
|
+
|
138
|
+
<span class='comment'># Or just run the `call` method without `process`.
|
139
|
+
</span><span class='const'>FetchRedCarsAsJson</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='id identifier rubyid_params'>params</span> <span class='op'>=</span> <span class='lbrace'>{</span><span class='rbrace'>}</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_call'>call</span>
|
140
|
+
</code></pre>
|
141
|
+
|
142
|
+
<p>Note that it's perfectly fine for some queries to return
|
143
|
+
<code>nil</code>, i.e. if they're writing queries that don't fetch
|
144
|
+
any results.</p>
|
145
|
+
|
146
|
+
<h2 id="label-Chainable+queries">Chainable queries</h2>
|
147
|
+
|
148
|
+
<p>Chainable queries are queries that input and output an Active Record
|
149
|
+
relation. You can access the given relation using the method
|
150
|
+
<code>relation</code>:</p>
|
151
|
+
|
152
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>Queries</span><span class='op'>::</span><span class='const'>User</span><span class='op'>::</span><span class='const'>FetchActive</span> <span class='op'><</span> <span class='const'>Inquery</span><span class='op'>::</span><span class='const'>Query</span><span class='op'>::</span><span class='const'>Chainable</span>
|
153
|
+
<span class='kw'>def</span> <span class='id identifier rubyid_call'>call</span>
|
154
|
+
<span class='id identifier rubyid_relation'>relation</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='label'>active:</span> <span class='int'>1</span><span class='rparen'>)</span>
|
155
|
+
<span class='kw'>end</span>
|
156
|
+
<span class='kw'>end</span>
|
157
|
+
</code></pre>
|
158
|
+
|
159
|
+
<p>Input and output relations may or may not be of the same AR class (i.e. you
|
160
|
+
could pass a relation of <code>Group</code>s and receive back a relation of
|
161
|
+
corresponding <code>User</code>s).</p>
|
162
|
+
|
163
|
+
<h3 id="label-Relation+validation">Relation validation</h3>
|
164
|
+
|
165
|
+
<p>Chainable queries allow you to further specify and validate the relation it
|
166
|
+
receives. This is done using the static <code>relation</code> method:</p>
|
167
|
+
|
168
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>Queries</span><span class='op'>::</span><span class='const'>User</span><span class='op'>::</span><span class='const'>FetchActive</span> <span class='op'><</span> <span class='const'>Inquery</span><span class='op'>::</span><span class='const'>Query</span><span class='op'>::</span><span class='const'>Chainable</span>
|
169
|
+
<span class='comment'># This will raise an exception when passing a relation which does not
|
170
|
+
</span> <span class='comment'># correspond to the `User` model.
|
171
|
+
</span> <span class='id identifier rubyid_relation'>relation</span> <span class='label'>class:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>User</span><span class='tstring_end'>'</span></span>
|
172
|
+
|
173
|
+
<span class='comment'># ....
|
174
|
+
</span><span class='kw'>end</span>
|
175
|
+
</code></pre>
|
176
|
+
|
177
|
+
<p>The <code>relation</code> method accepts the following options:</p>
|
178
|
+
<ul><li>
|
179
|
+
<p><code>class</code></p>
|
180
|
+
|
181
|
+
<p>Allows to restrict the class (attribute <code>klass</code>) of the
|
182
|
+
relation. Use <code>nil</code> to not perform any checks. The
|
183
|
+
<code>class</code> attribute will also be taken to infer a default if no
|
184
|
+
relation is given and you didn't specify any <code>default</code>.</p>
|
185
|
+
</li><li>
|
186
|
+
<p><code>default</code></p>
|
187
|
+
|
188
|
+
<p>This allows to specify a default relation that will be taken if no relation
|
189
|
+
is given. This must be specified as a Proc returning the relation. Set this
|
190
|
+
to <code>false</code> for no default. If this is set to <code>nil</code>,
|
191
|
+
it will try to infer the default from the option <code>class</code> (if
|
192
|
+
given).</p>
|
193
|
+
</li><li>
|
194
|
+
<p><code>fields</code></p>
|
195
|
+
|
196
|
+
<p>Allows to restrict the number of fields / values the relation must select.
|
197
|
+
This is particularly useful if you're using the query as a subquery and
|
198
|
+
need it to return exactly one field. Use <code>nil</code> to not perform
|
199
|
+
any checks.</p>
|
200
|
+
</li><li>
|
201
|
+
<p><code>default_select</code></p>
|
202
|
+
|
203
|
+
<p>If this is set to a symbol, the relation does not have any select fields
|
204
|
+
specified (<code>select_values</code> is empty) and <code>fields</code> is
|
205
|
+
> 0, it will automatically select the given field. This option defaults
|
206
|
+
to <code>:id</code>. Use <code>nil</code> to disable this behavior.</p>
|
207
|
+
</li></ul>
|
208
|
+
|
209
|
+
<h3 id="label-Using+query+classes+as+regular+scopes">Using query classes as regular scopes</h3>
|
210
|
+
|
211
|
+
<p>Chainable queries can also be used as regular AR model scopes:</p>
|
212
|
+
|
213
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>User</span> <span class='op'><</span> <span class='const'>ActiveRecord</span><span class='op'>::</span><span class='const'>Base</span>
|
214
|
+
<span class='id identifier rubyid_scope'>scope</span> <span class='symbol'>:active</span><span class='comma'>,</span> <span class='const'>Queries</span><span class='op'>::</span><span class='const'>User</span><span class='op'>::</span><span class='const'>FetchActive</span>
|
215
|
+
<span class='kw'>end</span>
|
216
|
+
|
217
|
+
<span class='kw'>class</span> <span class='const'>Queries</span><span class='op'>::</span><span class='const'>User</span><span class='op'>::</span><span class='const'>FetchActive</span> <span class='op'><</span> <span class='const'>Inquery</span><span class='op'>::</span><span class='const'>Query</span><span class='op'>::</span><span class='const'>Chainable</span>
|
218
|
+
<span class='comment'># Note that specifying either `class` or `default` is mandatory when using
|
219
|
+
</span> <span class='comment'># this query class as a scope. The reason for this is that, if the scope is
|
220
|
+
</span> <span class='comment'># otherwise empty, the class will receive `nil` from AR and therefore has no
|
221
|
+
</span> <span class='comment'># way of knowing which default class to take.
|
222
|
+
</span> <span class='id identifier rubyid_relation'>relation</span> <span class='label'>class:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>User</span><span class='tstring_end'>'</span></span>
|
223
|
+
|
224
|
+
<span class='kw'>def</span> <span class='id identifier rubyid_call'>call</span>
|
225
|
+
<span class='id identifier rubyid_relation'>relation</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='label'>active:</span> <span class='int'>1</span><span class='rparen'>)</span>
|
226
|
+
<span class='kw'>end</span>
|
227
|
+
<span class='kw'>end</span>
|
228
|
+
</code></pre>
|
229
|
+
|
230
|
+
<p>This approach allows to you use short and descriptive code like
|
231
|
+
<code>User.active</code> but have the possibly complex query code hidden in
|
232
|
+
a separate, reusable class.</p>
|
233
|
+
|
234
|
+
<p>Note that when using classes as scopes, the <code>process</code> method
|
235
|
+
will be ignored.</p>
|
236
|
+
|
237
|
+
<h3 id="label-Using+the+given+relation+as+subquery">Using the given relation as subquery</h3>
|
238
|
+
|
239
|
+
<p>In simple cases and all the examples above, we just extend the given
|
240
|
+
relation and return it again. It is also possible however to just use the
|
241
|
+
given relation as a subquery and return a completely new relation:</p>
|
242
|
+
|
243
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>FetchUsersInGroup</span> <span class='op'><</span> <span class='const'>Inquery</span><span class='op'>::</span><span class='const'>Query</span><span class='op'>::</span><span class='const'>Chainable</span>
|
244
|
+
<span class='comment'># Here we do not specify any specific class, as we don't care for it as long
|
245
|
+
</span> <span class='comment'># as the relation returns exactly one field.
|
246
|
+
</span> <span class='id identifier rubyid_relation'>relation</span> <span class='label'>fields:</span> <span class='int'>1</span>
|
247
|
+
|
248
|
+
<span class='kw'>def</span> <span class='id identifier rubyid_call'>call</span>
|
249
|
+
<span class='kw'>return</span> <span class='op'>::</span><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>%(</span><span class='tstring_content'>
|
250
|
+
id IN (
|
251
|
+
SELECT user_id FROM GROUPS_USERS WHERE group_id IN (
|
252
|
+
</span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_relation'>relation</span><span class='period'>.</span><span class='id identifier rubyid_to_sql'>to_sql</span><span class='embexpr_end'>}</span><span class='tstring_content'>
|
253
|
+
)
|
254
|
+
)
|
255
|
+
</span><span class='tstring_end'>)</span></span><span class='rparen'>)</span>
|
256
|
+
<span class='kw'>end</span>
|
257
|
+
<span class='kw'>end</span>
|
258
|
+
</code></pre>
|
259
|
+
|
260
|
+
<p>This query could then be called in the following ways:</p>
|
261
|
+
|
262
|
+
<pre class="code ruby"><code class="ruby"><span class='const'>FetchUsersInGroup</span><span class='period'>.</span><span class='id identifier rubyid_run'>run</span><span class='lparen'>(</span>
|
263
|
+
<span class='const'>GroupsUser</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='label'>user_id:</span> <span class='int'>1</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_select'>select</span><span class='lparen'>(</span><span class='symbol'>:group_id</span><span class='rparen'>)</span>
|
264
|
+
<span class='rparen'>)</span>
|
265
|
+
|
266
|
+
<span class='comment'># In this example, we're not specifying any select for the relation we pass to
|
267
|
+
</span><span class='comment'># the query class. This is fine because the query automatically defaults to
|
268
|
+
</span><span class='comment'># selecting `id` if exactly one field is required (`fields: 1`) and no select is
|
269
|
+
</span><span class='comment'># specifyed. You can control this further with the option `default_select`.
|
270
|
+
</span><span class='const'>FetchUsersInGroup</span><span class='period'>.</span><span class='id identifier rubyid_run'>run</span><span class='lparen'>(</span><span class='const'>Group</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='label'>color:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>red</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='rparen'>)</span>
|
271
|
+
</code></pre>
|
272
|
+
|
273
|
+
<h2 id="label-Parameters">Parameters</h2>
|
274
|
+
|
275
|
+
<p>Both query classes can be parameterized using a hash called
|
276
|
+
<code>params</code>. It is recommended to specify and validate input
|
277
|
+
parameters in every query. For this purpose, Inquery provides the
|
278
|
+
<code>schema</code> method witch integrates the <a
|
279
|
+
href="https://github.com/sitrox/schemacop">Schemacop</a> validation Gem:</p>
|
280
|
+
|
281
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>SomeQueryClass</span> <span class='op'><</span> <span class='const'>Inquery</span><span class='op'>::</span><span class='const'>Query</span>
|
282
|
+
<span class='id identifier rubyid_schema'>schema</span><span class='lparen'>(</span>
|
283
|
+
<span class='label'>some_param:</span> <span class='symbol'>:integer</span><span class='comma'>,</span>
|
284
|
+
<span class='label'>some_other_param:</span> <span class='lbrace'>{</span>
|
285
|
+
<span class='label'>hash:</span> <span class='lbrace'>{</span>
|
286
|
+
<span class='label'>some_field:</span> <span class='symbol'>:string</span>
|
287
|
+
<span class='rbrace'>}</span>
|
288
|
+
<span class='rbrace'>}</span>
|
289
|
+
<span class='rparen'>)</span>
|
290
|
+
|
291
|
+
<span class='comment'># ...
|
292
|
+
</span><span class='kw'>end</span>
|
293
|
+
</code></pre>
|
294
|
+
|
295
|
+
<p>The schema is validated at query class instantiation. An exception will be
|
296
|
+
raised if the given params do not match the schema specified. See
|
297
|
+
documentation of the Schemacop Gem for more information on how to specify
|
298
|
+
schemas.</p>
|
299
|
+
|
300
|
+
<p>Parameters can be accessed using either <code>params</code> or
|
301
|
+
<code>osparams</code>. The method <code>osparams</code> automatically wraps
|
302
|
+
<code>params</code> in an <code>OpenStruct</code> for more convenient
|
303
|
+
access.</p>
|
304
|
+
|
305
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>SomeQueryClass</span> <span class='op'><</span> <span class='const'>Inquery</span><span class='op'>::</span><span class='const'>Query</span>
|
306
|
+
<span class='kw'>def</span> <span class='id identifier rubyid_run'>run</span>
|
307
|
+
<span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span>
|
308
|
+
<span class='label'>active:</span> <span class='id identifier rubyid_params'>params</span><span class='lbracket'>[</span><span class='symbol'>:active</span><span class='rbracket'>]</span><span class='comma'>,</span>
|
309
|
+
<span class='label'>username:</span> <span class='id identifier rubyid_osparams'>osparams</span><span class='period'>.</span><span class='id identifier rubyid_search'>search</span>
|
310
|
+
<span class='rparen'>)</span>
|
311
|
+
<span class='kw'>end</span>
|
312
|
+
<span class='kw'>end</span>
|
313
|
+
</code></pre>
|
314
|
+
|
315
|
+
<h2 id="label-Rails+integration">Rails integration</h2>
|
316
|
+
|
317
|
+
<p>While it is optional, Inquery has been written from the ground up to be
|
318
|
+
perfectly integrated into any Rails application. It has proven to be a
|
319
|
+
winning concept to extract all complex queries into separate classes that
|
320
|
+
are independently executable and testable.</p>
|
321
|
+
|
322
|
+
<h3 id="label-Directory+structure">Directory structure</h3>
|
323
|
+
|
324
|
+
<p>While not enforced, it is encouraged to use the following structure for
|
325
|
+
storing your query classes:</p>
|
326
|
+
<ul><li>
|
327
|
+
<p>All domain-specific query classes reside in <code>app/queries</code>.</p>
|
328
|
+
</li><li>
|
329
|
+
<p>They're in the module <code>Queries</code>.</p>
|
330
|
+
</li><li>
|
331
|
+
<p>Queries are further grouped by the model they return (and not the model
|
332
|
+
they receive). For instance, a class fetching all active users could be
|
333
|
+
located at <code>Queries::User::FetchActive</code> and would reside under
|
334
|
+
<code>app/queries/user/fetch_active.rb</code>.</p>
|
335
|
+
</li></ul>
|
336
|
+
|
337
|
+
<p>There are some key benefits to this approach:</p>
|
338
|
+
<ul><li>
|
339
|
+
<p>As it should, domain-specific code is located within <code>app/</code>.</p>
|
340
|
+
</li><li>
|
341
|
+
<p>As queries are grouped by the model they return and consistently named,
|
342
|
+
they're easy to locate and it does not take much thought where to put
|
343
|
+
and how to name new query classes.</p>
|
344
|
+
</li><li>
|
345
|
+
<p>As there is a single file per query class, it's a breeze to list all
|
346
|
+
queries, i.e. to check their naming for consistency.</p>
|
347
|
+
</li><li>
|
348
|
+
<p>If you're using the same layout for your unit tests, it is absolutely
|
349
|
+
clear where to find the corresponding unit tests for each one of your
|
350
|
+
query classes.</p>
|
351
|
+
</li></ul>
|
352
|
+
|
353
|
+
<h2 id="label-Copyright">Copyright</h2>
|
354
|
+
|
355
|
+
<p>Copyright © 2016 Sitrox. See <code>LICENSE</code> for further details.</p>
|
356
|
+
</div></div>
|
357
|
+
|
358
|
+
<div id="footer">
|
359
|
+
Generated on Thu Jun 9 10:26:30 2016 by
|
360
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
361
|
+
0.8.7.6 (ruby-2.2.3).
|
362
|
+
</div>
|
363
|
+
|
364
|
+
</body>
|
365
|
+
</html>
|
data/doc/js/app.js
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
function createSourceLinks() {
|
2
|
+
$('.method_details_list .source_code').
|
3
|
+
before("<span class='showSource'>[<a href='#' class='toggleSource'>View source</a>]</span>");
|
4
|
+
$('.toggleSource').toggle(function() {
|
5
|
+
$(this).parent().nextAll('.source_code').slideDown(100);
|
6
|
+
$(this).text("Hide source");
|
7
|
+
},
|
8
|
+
function() {
|
9
|
+
$(this).parent().nextAll('.source_code').slideUp(100);
|
10
|
+
$(this).text("View source");
|
11
|
+
});
|
12
|
+
}
|
13
|
+
|
14
|
+
function createDefineLinks() {
|
15
|
+
var tHeight = 0;
|
16
|
+
$('.defines').after(" <a href='#' class='toggleDefines'>more...</a>");
|
17
|
+
$('.toggleDefines').toggle(function() {
|
18
|
+
tHeight = $(this).parent().prev().height();
|
19
|
+
$(this).prev().show();
|
20
|
+
$(this).parent().prev().height($(this).parent().height());
|
21
|
+
$(this).text("(less)");
|
22
|
+
},
|
23
|
+
function() {
|
24
|
+
$(this).prev().hide();
|
25
|
+
$(this).parent().prev().height(tHeight);
|
26
|
+
$(this).text("more...");
|
27
|
+
});
|
28
|
+
}
|
29
|
+
|
30
|
+
function createFullTreeLinks() {
|
31
|
+
var tHeight = 0;
|
32
|
+
$('.inheritanceTree').toggle(function() {
|
33
|
+
tHeight = $(this).parent().prev().height();
|
34
|
+
$(this).parent().toggleClass('showAll');
|
35
|
+
$(this).text("(hide)");
|
36
|
+
$(this).parent().prev().height($(this).parent().height());
|
37
|
+
},
|
38
|
+
function() {
|
39
|
+
$(this).parent().toggleClass('showAll');
|
40
|
+
$(this).parent().prev().height(tHeight);
|
41
|
+
$(this).text("show all");
|
42
|
+
});
|
43
|
+
}
|
44
|
+
|
45
|
+
function fixBoxInfoHeights() {
|
46
|
+
$('dl.box dd.r1, dl.box dd.r2').each(function() {
|
47
|
+
$(this).prev().height($(this).height());
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
function searchFrameLinks() {
|
52
|
+
$('.full_list_link').click(function() {
|
53
|
+
toggleSearchFrame(this, $(this).attr('href'));
|
54
|
+
return false;
|
55
|
+
});
|
56
|
+
}
|
57
|
+
|
58
|
+
function toggleSearchFrame(id, link) {
|
59
|
+
var frame = $('#search_frame');
|
60
|
+
$('#search a').removeClass('active').addClass('inactive');
|
61
|
+
if (frame.attr('src') == link && frame.css('display') != "none") {
|
62
|
+
frame.slideUp(100);
|
63
|
+
$('#search a').removeClass('active inactive');
|
64
|
+
}
|
65
|
+
else {
|
66
|
+
$(id).addClass('active').removeClass('inactive');
|
67
|
+
frame.attr('src', link).slideDown(100);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
function linkSummaries() {
|
72
|
+
$('.summary_signature').click(function() {
|
73
|
+
document.location = $(this).find('a').attr('href');
|
74
|
+
});
|
75
|
+
}
|
76
|
+
|
77
|
+
function framesInit() {
|
78
|
+
if (hasFrames) {
|
79
|
+
document.body.className = 'frames';
|
80
|
+
$('#menu .noframes a').attr('href', document.location);
|
81
|
+
try {
|
82
|
+
window.top.document.title = $('html head title').text();
|
83
|
+
} catch(error) {
|
84
|
+
// some browsers will not allow this when serving from file://
|
85
|
+
// but we don't want to stop the world.
|
86
|
+
}
|
87
|
+
}
|
88
|
+
else {
|
89
|
+
$('#menu .noframes a').text('frames').attr('href', framesUrl);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
function keyboardShortcuts() {
|
94
|
+
if (window.top.frames.main) return;
|
95
|
+
$(document).keypress(function(evt) {
|
96
|
+
if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) return;
|
97
|
+
if (typeof evt.target !== "undefined" &&
|
98
|
+
(evt.target.nodeName == "INPUT" ||
|
99
|
+
evt.target.nodeName == "TEXTAREA")) return;
|
100
|
+
switch (evt.charCode) {
|
101
|
+
case 67: case 99: $('#class_list_link').click(); break; // 'c'
|
102
|
+
case 77: case 109: $('#method_list_link').click(); break; // 'm'
|
103
|
+
case 70: case 102: $('#file_list_link').click(); break; // 'f'
|
104
|
+
default: break;
|
105
|
+
}
|
106
|
+
});
|
107
|
+
}
|
108
|
+
|
109
|
+
function summaryToggle() {
|
110
|
+
$('.summary_toggle').click(function() {
|
111
|
+
if (localStorage) {
|
112
|
+
localStorage.summaryCollapsed = $(this).text();
|
113
|
+
}
|
114
|
+
$('.summary_toggle').each(function() {
|
115
|
+
$(this).text($(this).text() == "collapse" ? "expand" : "collapse");
|
116
|
+
var next = $(this).parent().parent().nextAll('ul.summary').first();
|
117
|
+
if (next.hasClass('compact')) {
|
118
|
+
next.toggle();
|
119
|
+
next.nextAll('ul.summary').first().toggle();
|
120
|
+
}
|
121
|
+
else if (next.hasClass('summary')) {
|
122
|
+
var list = $('<ul class="summary compact" />');
|
123
|
+
list.html(next.html());
|
124
|
+
list.find('.summary_desc, .note').remove();
|
125
|
+
list.find('a').each(function() {
|
126
|
+
$(this).html($(this).find('strong').html());
|
127
|
+
$(this).parent().html($(this)[0].outerHTML);
|
128
|
+
});
|
129
|
+
next.before(list);
|
130
|
+
next.toggle();
|
131
|
+
}
|
132
|
+
});
|
133
|
+
return false;
|
134
|
+
});
|
135
|
+
if (localStorage) {
|
136
|
+
if (localStorage.summaryCollapsed == "collapse") {
|
137
|
+
$('.summary_toggle').first().click();
|
138
|
+
}
|
139
|
+
else localStorage.summaryCollapsed = "expand";
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
function fixOutsideWorldLinks() {
|
144
|
+
$('a').each(function() {
|
145
|
+
if (window.location.host != this.host) this.target = '_parent';
|
146
|
+
});
|
147
|
+
}
|
148
|
+
|
149
|
+
function generateTOC() {
|
150
|
+
if ($('#filecontents').length === 0) return;
|
151
|
+
var _toc = $('<ol class="top"></ol>');
|
152
|
+
var show = false;
|
153
|
+
var toc = _toc;
|
154
|
+
var counter = 0;
|
155
|
+
var tags = ['h2', 'h3', 'h4', 'h5', 'h6'];
|
156
|
+
var i;
|
157
|
+
if ($('#filecontents h1').length > 1) tags.unshift('h1');
|
158
|
+
for (i = 0; i < tags.length; i++) { tags[i] = '#filecontents ' + tags[i]; }
|
159
|
+
var lastTag = parseInt(tags[0][1], 10);
|
160
|
+
$(tags.join(', ')).each(function() {
|
161
|
+
if ($(this).parents('.method_details .docstring').length != 0) return;
|
162
|
+
if (this.id == "filecontents") return;
|
163
|
+
show = true;
|
164
|
+
var thisTag = parseInt(this.tagName[1], 10);
|
165
|
+
if (this.id.length === 0) {
|
166
|
+
var proposedId = $(this).attr('toc-id');
|
167
|
+
if (typeof(proposedId) != "undefined") this.id = proposedId;
|
168
|
+
else {
|
169
|
+
var proposedId = $(this).text().replace(/[^a-z0-9-]/ig, '_');
|
170
|
+
if ($('#' + proposedId).length > 0) { proposedId += counter; counter++; }
|
171
|
+
this.id = proposedId;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
if (thisTag > lastTag) {
|
175
|
+
for (i = 0; i < thisTag - lastTag; i++) {
|
176
|
+
var tmp = $('<ol/>'); toc.append(tmp); toc = tmp;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
if (thisTag < lastTag) {
|
180
|
+
for (i = 0; i < lastTag - thisTag; i++) toc = toc.parent();
|
181
|
+
}
|
182
|
+
var title = $(this).attr('toc-title');
|
183
|
+
if (typeof(title) == "undefined") title = $(this).text();
|
184
|
+
toc.append('<li><a href="#' + this.id + '">' + title + '</a></li>');
|
185
|
+
lastTag = thisTag;
|
186
|
+
});
|
187
|
+
if (!show) return;
|
188
|
+
html = '<div id="toc"><p class="title"><a class="hide_toc" href="#"><strong>Table of Contents</strong></a> <small>(<a href="#" class="float_toc">left</a>)</small></p></div>';
|
189
|
+
$('#content').prepend(html);
|
190
|
+
$('#toc').append(_toc);
|
191
|
+
$('#toc .hide_toc').toggle(function() {
|
192
|
+
$('#toc .top').slideUp('fast');
|
193
|
+
$('#toc').toggleClass('hidden');
|
194
|
+
$('#toc .title small').toggle();
|
195
|
+
}, function() {
|
196
|
+
$('#toc .top').slideDown('fast');
|
197
|
+
$('#toc').toggleClass('hidden');
|
198
|
+
$('#toc .title small').toggle();
|
199
|
+
});
|
200
|
+
$('#toc .float_toc').toggle(function() {
|
201
|
+
$(this).text('float');
|
202
|
+
$('#toc').toggleClass('nofloat');
|
203
|
+
}, function() {
|
204
|
+
$(this).text('left');
|
205
|
+
$('#toc').toggleClass('nofloat');
|
206
|
+
});
|
207
|
+
}
|
208
|
+
|
209
|
+
$(framesInit);
|
210
|
+
$(createSourceLinks);
|
211
|
+
$(createDefineLinks);
|
212
|
+
$(createFullTreeLinks);
|
213
|
+
$(fixBoxInfoHeights);
|
214
|
+
$(searchFrameLinks);
|
215
|
+
$(linkSummaries);
|
216
|
+
$(keyboardShortcuts);
|
217
|
+
$(summaryToggle);
|
218
|
+
$(fixOutsideWorldLinks);
|
219
|
+
$(generateTOC);
|