activerecord-query 0.1.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/CHANGELOG.md +3 -0
- data/Gemfile +10 -0
- data/README.md +425 -0
- data/Rakefile +8 -0
- data/activerecord-query.gemspec +42 -0
- data/lib/active_record_query/arel/nodes.rb +15 -0
- data/lib/active_record_query/argument_stacker.rb +42 -0
- data/lib/active_record_query/column.rb +15 -0
- data/lib/active_record_query/concerns/conditionable.rb +16 -0
- data/lib/active_record_query/concerns/featureable.rb +16 -0
- data/lib/active_record_query/concerns/groupable.rb +22 -0
- data/lib/active_record_query/concerns/havingable.rb +24 -0
- data/lib/active_record_query/concerns/identifiable.rb +20 -0
- data/lib/active_record_query/concerns/joinable.rb +43 -0
- data/lib/active_record_query/concerns/limitable.rb +21 -0
- data/lib/active_record_query/concerns/offsetable.rb +21 -0
- data/lib/active_record_query/concerns/orderable.rb +22 -0
- data/lib/active_record_query/concerns/selectable.rb +22 -0
- data/lib/active_record_query/conditions/builder.rb +36 -0
- data/lib/active_record_query/conditions/chain_link.rb +5 -0
- data/lib/active_record_query/conditions/condition_group.rb +10 -0
- data/lib/active_record_query/conditions/conditionable.rb +29 -0
- data/lib/active_record_query/conditions/where_group.rb +9 -0
- data/lib/active_record_query/conditions/wor_group.rb +9 -0
- data/lib/active_record_query/expression_parser.rb +40 -0
- data/lib/active_record_query/version.rb +5 -0
- data/lib/activerecord-query.rb +48 -0
- metadata +210 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fdf9b2ff6e244407f97068227e4db1514e85e4a6912178845a12123ad4f952af
|
4
|
+
data.tar.gz: d0b9acdfb57e77f20e82fe34d36b7158c9ae3c667af80807c1659d78269b5669
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '06893476dc21302c35ca1db93973abaf91dc8e1bcc58a27d4ae83a66a68e9ac7f87d2054f0d9ec31a9d76ec4a638a5b01054877d3d911561916a477d1c010740'
|
7
|
+
data.tar.gz: 83bd2598980986b424f694b06cf49acf609764030dbb226f326fc34f389143f59d77497d1fde0b7c497881b9cd5c6bcf49bc36c2976c5727bdd7da762cd19b90
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,425 @@
|
|
1
|
+
# ActiveRecordQuery
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
|
6
|
+
ActiveRecordQuery is a DSL buit on top of [ActiveRecord](https://github.com/rails/rails/tree/main/activerecord)
|
7
|
+
to help you write complex SQL queries
|
8
|
+
in the cleanest way possible. The lib provides a base class to
|
9
|
+
build a query pattern in your object oriented project.
|
10
|
+
|
11
|
+
Quick usage sample:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
# verbose
|
15
|
+
query = Post.joins(:author).where(Post.arel_table[:created_at].gt(Date.new(2000, 1, 2))).order(title: :asc)
|
16
|
+
|
17
|
+
# clean ;)
|
18
|
+
class Query < ActiveRecordQuery::Base
|
19
|
+
from Post
|
20
|
+
join :author
|
21
|
+
where created_at > Date.new(2000, 1, 2)
|
22
|
+
order_by title.asc, author.name.asc
|
23
|
+
end
|
24
|
+
query = Query.execute
|
25
|
+
```
|
26
|
+
|
27
|
+
The main goal is to turn your queries (or scopes) into classes.
|
28
|
+
These classes will be written naturally like a SQL query using a ruby DSL.
|
29
|
+
The common problem with the actual design of activerecord is that
|
30
|
+
the users tend to write the query features in chain, like this:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
Post.select(:title)
|
34
|
+
.where(title: 'A title')
|
35
|
+
.where('created_at > ?', Date.today)
|
36
|
+
.order(:title)
|
37
|
+
```
|
38
|
+
|
39
|
+
You can refactor with scopes, I guess..
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
class Post < ActiveRecord::Base
|
43
|
+
scope :titled, -> { where(title: 'A title') }
|
44
|
+
scope :created, -> { where('created_at < ?', Date.today) }
|
45
|
+
scope :titled_created, -> { titled.created }
|
46
|
+
|
47
|
+
def self.a_query
|
48
|
+
select(:title).titled_created.order(:title)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Very messy...
|
54
|
+
When arel table features comes in, it becomes even worst.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
is_fixed = Post[:fixed].eq(true)
|
58
|
+
is_coming = Post[:coming].eq(true).and(Post[:activated_at].not_eq(nil))
|
59
|
+
Post.where(is_fixed.or(is_coming))
|
60
|
+
```
|
61
|
+
|
62
|
+
Now, let's try the ActiveRecordQuery:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
class PostQuery < ActiveRecordQuery::Base
|
66
|
+
from Post
|
67
|
+
where fixed == true
|
68
|
+
wor do |other|
|
69
|
+
other.where coming == true
|
70
|
+
other.where actived_at != nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
## Installation
|
76
|
+
|
77
|
+
Add this line to your application's Gemfile:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
gem 'activerecord-query'
|
81
|
+
```
|
82
|
+
|
83
|
+
And then execute:
|
84
|
+
|
85
|
+
$ bundle install
|
86
|
+
|
87
|
+
Or install it yourself as:
|
88
|
+
|
89
|
+
$ gem install activerecord-query
|
90
|
+
|
91
|
+
## Usage
|
92
|
+
|
93
|
+
ActiveRecordQuery adds the ActiveRecord::Base class to your project,
|
94
|
+
so we can easily create query classes by extending it.
|
95
|
+
|
96
|
+
### Queries
|
97
|
+
The concept is given by the notion of query pattern classes.
|
98
|
+
The suggestion is that you put these classes in app/queries folder.
|
99
|
+
|
100
|
+
### The query setup
|
101
|
+
|
102
|
+
Consider to have a Post model:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
class Post < ActiveRecord::Base
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
Now, you have to create a classe extending `ActiveRecordQuery::Base` class,
|
110
|
+
and then add the reference for the `Post` activerecord type
|
111
|
+
on `from` method.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
class PostQuery < ActiveRecordQuery::Base
|
115
|
+
from Post
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
Once you have defined the from resource, the columns from Post will be available
|
120
|
+
as methods in the class scope.
|
121
|
+
E.g. the method `title` will be defined as a `Arel::Attributes::Attribute` object.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
class PostQuery < ActiveRecordQuery::Base
|
125
|
+
from Post
|
126
|
+
select title # the 'title' method was defined from 'from'
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
The public method `execute` will return the `ActiveRecord_Relation` object.
|
131
|
+
```ruby
|
132
|
+
PostQuery.execute # => ActiveRecord_Relation
|
133
|
+
```
|
134
|
+
|
135
|
+
Or you can instantiate it also:
|
136
|
+
```ruby
|
137
|
+
PostQuery.new.execute # => ActiveRecord_Relation
|
138
|
+
```
|
139
|
+
|
140
|
+
### Query Features
|
141
|
+
|
142
|
+
#### Conditions
|
143
|
+
The methods `where` and `wor` are available to set the query conditions.
|
144
|
+
The argument must be a `Arel::Node` object.
|
145
|
+
To a cleaner experience, you can use the generated methods for columns
|
146
|
+
generated from the `from` method.
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
class PostQuery < ActiveRecordQuery::Base
|
150
|
+
from Post
|
151
|
+
|
152
|
+
# and operation
|
153
|
+
where title == 'something' # using title helper
|
154
|
+
where Post.arel_table[:title].eq('something') # using arel
|
155
|
+
|
156
|
+
# or operation
|
157
|
+
wor title == 'something else'
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
The Arel predications are available:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
# between
|
165
|
+
where column.between 1..10
|
166
|
+
|
167
|
+
# matches
|
168
|
+
where column.matches '%something%'
|
169
|
+
|
170
|
+
# not in all
|
171
|
+
where column.not_in_all %w[something]
|
172
|
+
```
|
173
|
+
|
174
|
+
You can nest the conditions:
|
175
|
+
```ruby
|
176
|
+
where column == 'c1'
|
177
|
+
wor do |nested|
|
178
|
+
nested.where column == 'c2'
|
179
|
+
nested.where other == 'c3'
|
180
|
+
nested.wor do |deep_nested|
|
181
|
+
deep_nested.where other == 'c1'
|
182
|
+
deep_nested.where column == 'c4'
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
It Generates:
|
187
|
+
```sql
|
188
|
+
column = "c1" or (column = "c2" and other = "c3" or (other = 'c1' and column = 'c4'))
|
189
|
+
```
|
190
|
+
|
191
|
+
The dynamic values for conditions can be add as a symbol to reference
|
192
|
+
a method or can be a proc. The values will be evaluate when the `execute` is called.
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
class PostQuery < ActiveRecordQuery::Base
|
196
|
+
from Post
|
197
|
+
where title == :a_dynamic_method # references a method
|
198
|
+
where title == proc { 'a title' } # the value will be evaluate on execute
|
199
|
+
|
200
|
+
def a_dynamic_method
|
201
|
+
'a title'
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
#### Conditional where
|
207
|
+
The `where/wor` state can be conditioned by passing the option `if:`.
|
208
|
+
The value must be a symbol referencing a method.
|
209
|
+
```ruby
|
210
|
+
class PostQuery < ActiveRecordQuery::Base
|
211
|
+
from Post
|
212
|
+
where title == 'test', if: :a_method?
|
213
|
+
|
214
|
+
def a_method?
|
215
|
+
false
|
216
|
+
end
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
#### Selects
|
221
|
+
Selects can be done by passing a list of columns to the `select` method.
|
222
|
+
The args must be a list of `Arel::Attributes::Attribute`.
|
223
|
+
If no select is defined in the query, then the `*` selection will be taken.
|
224
|
+
Every call of `select` the attrs will be added to the selection.
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
class PostQuery < ActiveRecordQuery::Base
|
228
|
+
from Post
|
229
|
+
|
230
|
+
# simple select
|
231
|
+
select title, created_at
|
232
|
+
|
233
|
+
# can do a math op (it's just a arel attr)
|
234
|
+
select id + id
|
235
|
+
|
236
|
+
# plain arel attr
|
237
|
+
select Post.arel_table[:id]
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
241
|
+
#### Order by
|
242
|
+
Much like the select method, you shall pass a list of attributes to the method `order_by`.
|
243
|
+
Every call of `order_by` the attrs will be added to the selection.
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
class PostQuery < ActiveRecordQuery::Base
|
247
|
+
from Post
|
248
|
+
|
249
|
+
# list of arel attrs
|
250
|
+
order_by title.asc, created_at.desc
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
#### Limits
|
255
|
+
The `limit` method is available to define a query limit.
|
256
|
+
An integer value is the only arg acceptable.
|
257
|
+
Every time the limit method is called, the limit will be redefined.
|
258
|
+
```ruby
|
259
|
+
class PostQuery < ActiveRecordQuery::Base
|
260
|
+
from Post
|
261
|
+
limit 10
|
262
|
+
end
|
263
|
+
```
|
264
|
+
|
265
|
+
#### Offsets
|
266
|
+
The `offset` method is available to define a query offset.
|
267
|
+
An integer value is the only arg acceptable.
|
268
|
+
Every time the offset method is called, the offset will be redefined.
|
269
|
+
```ruby
|
270
|
+
class PostQuery < ActiveRecordQuery::Base
|
271
|
+
from Post
|
272
|
+
offset 10
|
273
|
+
end
|
274
|
+
```
|
275
|
+
|
276
|
+
#### Joins
|
277
|
+
The `join` method defines one/many relationships with the current resource (`from` state).
|
278
|
+
The following example has a Post and Author models, the way we define a join is the same as
|
279
|
+
defining a `joins` on activerecord (check the active record querying doc.).
|
280
|
+
Right after defined the join a new method will be available for retrieve the columns
|
281
|
+
from the new resource, the `author` method on this example. Every relationship listed in
|
282
|
+
the args will be converted to a method with the same name.
|
283
|
+
```ruby
|
284
|
+
# models
|
285
|
+
class Post < ActiveRecord::Base
|
286
|
+
belongs_to :author
|
287
|
+
end
|
288
|
+
|
289
|
+
class Author < ActiveRecord::Base
|
290
|
+
has_many :posts
|
291
|
+
end
|
292
|
+
|
293
|
+
# query
|
294
|
+
class PostQuery < ActiveRecordQuery::Base
|
295
|
+
from Post
|
296
|
+
join :author
|
297
|
+
|
298
|
+
# the author helper will be available
|
299
|
+
where author.name == 'John'
|
300
|
+
end
|
301
|
+
```
|
302
|
+
|
303
|
+
|
304
|
+
#### Group by
|
305
|
+
To apply a GROUP BY clause to the query, you can use the `group_by` method.
|
306
|
+
The method accepts a list of columns.
|
307
|
+
```ruby
|
308
|
+
class PostQuery < ActiveRecordQuery::Base
|
309
|
+
from Post
|
310
|
+
group_by title
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
314
|
+
#### Having
|
315
|
+
You can add the HAVING clause to the query by defining a `having` method.
|
316
|
+
A column condition can be done by calling the Arel predication methods like `gt`.
|
317
|
+
```ruby
|
318
|
+
class PostQuery < ActiveRecordQuery::Base
|
319
|
+
from Post
|
320
|
+
group_by title
|
321
|
+
having id.gt 5
|
322
|
+
end
|
323
|
+
```
|
324
|
+
|
325
|
+
|
326
|
+
### Scopes
|
327
|
+
There are at least to ways to scope your query class. The first one is the use of
|
328
|
+
class inheritance. The second one is extract features to modules.
|
329
|
+
|
330
|
+
#### Class inheritance
|
331
|
+
You can merge queries by extending the class.
|
332
|
+
Let's say that you have a base query definition `AScopeQuery`.
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
class AScopeQuery < ApplicationQuery
|
336
|
+
from Post
|
337
|
+
where title != nil
|
338
|
+
end
|
339
|
+
```
|
340
|
+
And then you extend this query:
|
341
|
+
```ruby
|
342
|
+
class AQuery < AScopeQuery
|
343
|
+
where id > 5
|
344
|
+
order_by title
|
345
|
+
end
|
346
|
+
```
|
347
|
+
The result will be the merge of the two queries:
|
348
|
+
```sql
|
349
|
+
SELECT * FROM posts WHERE title NOT NULL AND id > 5 ORDER BY title
|
350
|
+
```
|
351
|
+
|
352
|
+
#### Modules
|
353
|
+
Another way to scope a query, would be including modules into yours
|
354
|
+
query class. Let's define a module with activesupport concern.
|
355
|
+
|
356
|
+
```ruby
|
357
|
+
module AScope
|
358
|
+
extend ActiveSupport::Concern
|
359
|
+
|
360
|
+
included do
|
361
|
+
where title == 'a scope'
|
362
|
+
end
|
363
|
+
end
|
364
|
+
```
|
365
|
+
Note that we called the `where` macro inside the included method just like the
|
366
|
+
"activemodel concerns style". And then we can simply include the scope module into
|
367
|
+
the query class:
|
368
|
+
```ruby
|
369
|
+
class AQuery < ApplicationQuery
|
370
|
+
from Post
|
371
|
+
include AScope
|
372
|
+
end
|
373
|
+
```
|
374
|
+
It is important to notice that the module must be included after the `from` definition
|
375
|
+
due to the scope dependency on the `from` builds.
|
376
|
+
|
377
|
+
### Query Parameters
|
378
|
+
The query class can be instantiate/execute with user parameters.
|
379
|
+
The `options` method will be available on the instance context of the class.
|
380
|
+
This data can be part of the query dynamic solutions in their features.
|
381
|
+
```ruby
|
382
|
+
class PostQuery < ApplicationQuery
|
383
|
+
from Post
|
384
|
+
where title == :title_value
|
385
|
+
|
386
|
+
def title_value
|
387
|
+
options[:title]
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# execute with option :title
|
392
|
+
PostQuery.execute(title: 'A Title') # => select * from posts where title = "A Title"
|
393
|
+
```
|
394
|
+
On this example, the value for title condition is dynamic set by
|
395
|
+
the `options` parameter. A proc can be used also:
|
396
|
+
```ruby
|
397
|
+
where title == proc { options[:title] }
|
398
|
+
```
|
399
|
+
|
400
|
+
## Development
|
401
|
+
|
402
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
403
|
+
|
404
|
+
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).
|
405
|
+
|
406
|
+
## Contributing
|
407
|
+
|
408
|
+
Bug reports and pull requests are welcome on GitHub at
|
409
|
+
https://github.com/marcosfelipe/activerecord-query.
|
410
|
+
This project is intended to be a safe,
|
411
|
+
welcoming space for collaboration, and contributors are
|
412
|
+
expected to adhere to the [code of conduct](https://github.com/rubygems/rubygems/blob/master/CODE_OF_CONDUCT.md).
|
413
|
+
|
414
|
+
## License
|
415
|
+
|
416
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
417
|
+
|
418
|
+
## Code of Conduct
|
419
|
+
|
420
|
+
Everyone interacting in the ActiveRecordQuery project's codebases, issue trackers, chat rooms and mailing lists is
|
421
|
+
expected to follow the [code of conduct](https://github.com/rubygems/rubygems/blob/master/CODE_OF_CONDUCT.md).
|
422
|
+
|
423
|
+
## Author
|
424
|
+
|
425
|
+
Marcos Felipe (marcosfelipesilva54@gmail.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/active_record_query/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "activerecord-query"
|
7
|
+
spec.version = ActiveRecordQuery::VERSION
|
8
|
+
spec.authors = ["Marcos Felipe"]
|
9
|
+
spec.email = ["marcosfelipesilva54@gmail.com"]
|
10
|
+
spec.summary = "Small DSL for query build with Active Record."
|
11
|
+
spec.description = "The DSL provides a nice and clean way to write ActiveRecord queries better than model scopes."
|
12
|
+
spec.homepage = "https://github.com/marcosfelipe/activerecord-query"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.required_ruby_version = ">= 2.6.0"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
18
|
+
spec.metadata["changelog_uri"] = spec.homepage + '/CHANGELOG.md'
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
spec.files = Dir['README.md', 'LICENSE',
|
22
|
+
'CHANGELOG.md', 'lib/**/*.rb',
|
23
|
+
'lib/**/*.rake',
|
24
|
+
'*.gemspec', '.github/*.md',
|
25
|
+
'Gemfile', 'Rakefile']
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_dependency "activerecord", ">= 4.0", "< 8.0"
|
31
|
+
spec.add_dependency "activesupport", ">= 4.0", "< 8.0"
|
32
|
+
|
33
|
+
spec.add_development_dependency "bundler", '>= 1.3', '< 3.0'
|
34
|
+
spec.add_development_dependency "rake", '>= 1.0', '< 14.0'
|
35
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
36
|
+
spec.add_development_dependency "pry", '~> 0.14.0'
|
37
|
+
spec.add_development_dependency "sqlite3", '~> 1.0'
|
38
|
+
spec.add_development_dependency "simplecov", '~> 0.21.0'
|
39
|
+
|
40
|
+
# For more information and examples about making a new gem, check out our
|
41
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
42
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Arel # :nodoc: all
|
2
|
+
module Nodes
|
3
|
+
# The setter for value is necessary to evaluate
|
4
|
+
# when a symbol or proc is passed to it
|
5
|
+
class Casted
|
6
|
+
attr_writer :value
|
7
|
+
end
|
8
|
+
|
9
|
+
# The setter for expr is necessary to evaluate
|
10
|
+
# when a symbol or proc is passed to it
|
11
|
+
class Unary
|
12
|
+
alias :value= :expr=
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
# The stacker class receives a class as context
|
3
|
+
# to define instance methods ordered to collect
|
4
|
+
# a list of data in the class instance
|
5
|
+
class ArgumentStacker
|
6
|
+
def initialize(context, stack_name)
|
7
|
+
@context = context
|
8
|
+
@stack_name = stack_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(args)
|
12
|
+
context.define_method(next_method_name) do
|
13
|
+
args
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def list
|
18
|
+
stacked_methods.map { |method| context.send(method) }.flatten
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :context, :stack_name
|
24
|
+
|
25
|
+
def next_method_name
|
26
|
+
current_number = last_stacked_method.to_s.scan(/\d+$/).first.to_i
|
27
|
+
"_#{stack_name}_#{current_number + 1}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def last_stacked_method
|
31
|
+
stacked_methods.last
|
32
|
+
end
|
33
|
+
|
34
|
+
def stacked_methods
|
35
|
+
context_methods.grep(/^_#{stack_name}_/).sort
|
36
|
+
end
|
37
|
+
|
38
|
+
def context_methods
|
39
|
+
context.send(context.respond_to?(:instance_methods) ? :instance_methods : :methods)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
# Extends the Arel attribute class.
|
3
|
+
# It sets some aliases for syntactic sugar.
|
4
|
+
class Column < Arel::Attributes::Attribute
|
5
|
+
alias_method :==, :eq
|
6
|
+
alias_method :!=, :not_eq
|
7
|
+
alias_method :=~, :matches
|
8
|
+
alias_method :>=, :gteq
|
9
|
+
alias_method :>, :gt
|
10
|
+
alias_method :<, :lt
|
11
|
+
alias_method :<=, :lteq
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Conditionable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
include Conditions::Conditionable
|
7
|
+
|
8
|
+
add_feature :build_conditions
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_conditions(scope)
|
12
|
+
conditions = Conditions::Builder.new(resource, self).build(self)
|
13
|
+
conditions ? scope.where(conditions) : scope
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Featureable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def _query_features
|
7
|
+
@@_query_features
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_feature(feature)
|
11
|
+
@@_query_features ||= []
|
12
|
+
@@_query_features << feature
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Groupable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def group_by(*args)
|
7
|
+
arg_stacker = ArgumentStacker.new(self, :group_by)
|
8
|
+
arg_stacker.add(args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
included do
|
13
|
+
add_feature :build_group_by
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_group_by(scope)
|
17
|
+
arg_stacker = ArgumentStacker.new(self, :group_by)
|
18
|
+
args = arg_stacker.list
|
19
|
+
args.present? ? scope.group(*args.flatten) : scope
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Havingable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
attr_reader :_having
|
7
|
+
|
8
|
+
def having(*args)
|
9
|
+
arg_stacker = ArgumentStacker.new(self, :having)
|
10
|
+
arg_stacker.add(args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
included do
|
15
|
+
add_feature :build_having
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_having(scope)
|
19
|
+
arg_stacker = ArgumentStacker.new(self, :having)
|
20
|
+
args = ExpressionParser.new(self).parse(arg_stacker.list)
|
21
|
+
args.present? ? scope.having(*args) : scope
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Identifiable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def from(model_class)
|
7
|
+
raise(ArgumentError, 'Resource must be a ActiveRecord::Base object.') unless model_class.new.is_a?(ActiveRecord::Base)
|
8
|
+
define_method(:resource) do
|
9
|
+
model_class
|
10
|
+
end
|
11
|
+
|
12
|
+
model_class.column_names.each do |column|
|
13
|
+
define_singleton_method(column) do
|
14
|
+
Column.new(model_class.arel_table, column)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Joinable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class JoinedResource
|
6
|
+
def initialize(resource)
|
7
|
+
@resource = resource
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(m, *args, &block)
|
11
|
+
Column.new(Arel::Table.new(resource), m)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :resource
|
17
|
+
end
|
18
|
+
|
19
|
+
class_methods do
|
20
|
+
def join(*args)
|
21
|
+
arg_stacker = ArgumentStacker.new(self, :join)
|
22
|
+
arg_stacker.add(args)
|
23
|
+
args.to_s.split(/\W+/).compact.each do |resource_name|
|
24
|
+
define_singleton_method(resource_name) do
|
25
|
+
JoinedResource.new(resource_name.pluralize)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
included do
|
32
|
+
add_feature :build_joins
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_joins(scope)
|
36
|
+
arg_stacker = ArgumentStacker.new(self, :join)
|
37
|
+
arg_stacker.list.each do |join_params|
|
38
|
+
scope = scope.joins(join_params)
|
39
|
+
end
|
40
|
+
scope
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Limitable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def limit(value)
|
7
|
+
define_method('limit') do
|
8
|
+
value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
included do
|
14
|
+
add_feature :build_limit
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_limit(scope)
|
18
|
+
respond_to?(:limit) ? scope.limit(limit) : scope
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Offsetable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def offset(value)
|
7
|
+
define_method('offset') do
|
8
|
+
value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
included do
|
14
|
+
add_feature :build_offset
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_offset(scope)
|
18
|
+
respond_to?(:offset) ? scope.offset(offset) : scope
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Orderable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def order_by(*args)
|
7
|
+
arg_stacker = ArgumentStacker.new(self, :order_by)
|
8
|
+
arg_stacker.add(args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
included do
|
13
|
+
add_feature :build_order
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_order(scope)
|
17
|
+
arg_stacker = ArgumentStacker.new(self, :order_by)
|
18
|
+
args = arg_stacker.list
|
19
|
+
args.present? ? scope.order(args) : scope
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Selectable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def select(*args)
|
7
|
+
arg_stacker = ArgumentStacker.new(self, :select)
|
8
|
+
arg_stacker.add(args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
included do
|
13
|
+
add_feature :build_select
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_select(scope)
|
17
|
+
arg_stacker = ArgumentStacker.new(self, :select)
|
18
|
+
args = arg_stacker.list
|
19
|
+
args.present? ? scope.select(*args) : scope
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Conditions
|
3
|
+
class Builder
|
4
|
+
def initialize(resource, context)
|
5
|
+
@resource = resource
|
6
|
+
@context = context
|
7
|
+
end
|
8
|
+
|
9
|
+
def build(group)
|
10
|
+
chain_conditions = nil
|
11
|
+
arg_stacker = ArgumentStacker.new(group, :condition)
|
12
|
+
arg_stacker.list.each do |chain_link|
|
13
|
+
next unless executable?(chain_link)
|
14
|
+
if chain_link.respond_to?(:where)
|
15
|
+
group = chain_link.new
|
16
|
+
chain_conditions = chain_conditions.present? ? chain_conditions.send(chain_link::glue, build(group)) : build(group)
|
17
|
+
else
|
18
|
+
arel_condition = ExpressionParser.new(context).parse(chain_link.condition)
|
19
|
+
chain_conditions = chain_conditions.present? ? chain_conditions.send(chain_link.type, arel_condition) : arel_condition
|
20
|
+
end
|
21
|
+
end
|
22
|
+
resource.arel_table.grouping(chain_conditions) if chain_conditions
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :resource, :context
|
28
|
+
|
29
|
+
def executable?(chain_link)
|
30
|
+
condition_options = chain_link.options.to_h
|
31
|
+
return true unless condition_options[:if].present?
|
32
|
+
context.send(condition_options[:if])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative 'conditionable'
|
2
|
+
|
3
|
+
module ActiveRecordQuery
|
4
|
+
# ConditionGroup stacks ChainLink, WhereGroup and WorGroup when
|
5
|
+
# 'where' and 'wor' methods are called
|
6
|
+
class ConditionGroup
|
7
|
+
include Conditions::Conditionable
|
8
|
+
cattr_reader :options
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
module Conditions
|
3
|
+
module Conditionable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
class_methods do
|
7
|
+
def where(*args, &block)
|
8
|
+
chain_link_definer(Class.new(WhereGroup), args, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def wor(*args, &block)
|
12
|
+
chain_link_definer(Class.new(WorGroup), args, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def chain_link_definer(group_type, args)
|
16
|
+
arg_stacker = ArgumentStacker.new(self, :condition)
|
17
|
+
if block_given?
|
18
|
+
yield group_type
|
19
|
+
arg_stacker.add(group_type)
|
20
|
+
else
|
21
|
+
condition = args[0]
|
22
|
+
options = args[1]
|
23
|
+
arg_stacker.add(ChainLink.new(group_type.glue, condition, options))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ActiveRecordQuery
|
2
|
+
# It evaluates an expression to execute on the context
|
3
|
+
# whenever a symbol or a proc is passed as value on the expression.
|
4
|
+
class ExpressionParser
|
5
|
+
def initialize(context)
|
6
|
+
@context = context
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse(expression)
|
10
|
+
if expression.respond_to?(:each)
|
11
|
+
expression = expression.map(&:clone)
|
12
|
+
expression.each { |arg| parse_arg(arg) }
|
13
|
+
else
|
14
|
+
expression = expression.clone
|
15
|
+
parse_arg(expression)
|
16
|
+
end
|
17
|
+
expression
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :context
|
23
|
+
|
24
|
+
def parse_arg(arg)
|
25
|
+
if arg.respond_to?(:right) && arg.right.respond_to?(:value)
|
26
|
+
arg.right.value = parse_dynamic_values(arg.right.value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_dynamic_values(value)
|
31
|
+
if value.is_a?(Symbol)
|
32
|
+
context.send(value)
|
33
|
+
elsif value.is_a?(Proc)
|
34
|
+
context.instance_exec(&value)
|
35
|
+
else
|
36
|
+
value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'arel'
|
5
|
+
Dir.glob(File.join(__dir__, 'active_record_query/**/*.rb')) do |f|
|
6
|
+
require f
|
7
|
+
end
|
8
|
+
|
9
|
+
module ActiveRecordQuery
|
10
|
+
# The base class collects all the activerecord query features
|
11
|
+
# and the execute method processes all the features.
|
12
|
+
# The api user must inherit from this class in order to build
|
13
|
+
# a new query definition.
|
14
|
+
class Base
|
15
|
+
include Identifiable
|
16
|
+
include Featureable
|
17
|
+
include Selectable
|
18
|
+
include Orderable
|
19
|
+
include Limitable
|
20
|
+
include Joinable
|
21
|
+
include Groupable
|
22
|
+
include Offsetable
|
23
|
+
include Havingable
|
24
|
+
include Conditionable
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def execute(options = {})
|
28
|
+
new(options).execute
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(options = {})
|
33
|
+
@options = options
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute
|
37
|
+
query = resource.all
|
38
|
+
self.class._query_features.each do |query_feature|
|
39
|
+
query = send(query_feature, query)
|
40
|
+
end
|
41
|
+
query
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :options
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-query
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marcos Felipe
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-11-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '8.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '8.0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: activesupport
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '4.0'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '8.0'
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '4.0'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '8.0'
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: bundler
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '1.3'
|
60
|
+
- - "<"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '3.0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.3'
|
70
|
+
- - "<"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '3.0'
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rake
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '1.0'
|
80
|
+
- - "<"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '14.0'
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
- - "<"
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '14.0'
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: rspec
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '3.0'
|
100
|
+
type: :development
|
101
|
+
prerelease: false
|
102
|
+
version_requirements: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - "~>"
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '3.0'
|
107
|
+
- !ruby/object:Gem::Dependency
|
108
|
+
name: pry
|
109
|
+
requirement: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - "~>"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 0.14.0
|
114
|
+
type: :development
|
115
|
+
prerelease: false
|
116
|
+
version_requirements: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - "~>"
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 0.14.0
|
121
|
+
- !ruby/object:Gem::Dependency
|
122
|
+
name: sqlite3
|
123
|
+
requirement: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - "~>"
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '1.0'
|
128
|
+
type: :development
|
129
|
+
prerelease: false
|
130
|
+
version_requirements: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - "~>"
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '1.0'
|
135
|
+
- !ruby/object:Gem::Dependency
|
136
|
+
name: simplecov
|
137
|
+
requirement: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - "~>"
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 0.21.0
|
142
|
+
type: :development
|
143
|
+
prerelease: false
|
144
|
+
version_requirements: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - "~>"
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: 0.21.0
|
149
|
+
description: The DSL provides a nice and clean way to write ActiveRecord queries better
|
150
|
+
than model scopes.
|
151
|
+
email:
|
152
|
+
- marcosfelipesilva54@gmail.com
|
153
|
+
executables: []
|
154
|
+
extensions: []
|
155
|
+
extra_rdoc_files: []
|
156
|
+
files:
|
157
|
+
- CHANGELOG.md
|
158
|
+
- Gemfile
|
159
|
+
- README.md
|
160
|
+
- Rakefile
|
161
|
+
- activerecord-query.gemspec
|
162
|
+
- lib/active_record_query/arel/nodes.rb
|
163
|
+
- lib/active_record_query/argument_stacker.rb
|
164
|
+
- lib/active_record_query/column.rb
|
165
|
+
- lib/active_record_query/concerns/conditionable.rb
|
166
|
+
- lib/active_record_query/concerns/featureable.rb
|
167
|
+
- lib/active_record_query/concerns/groupable.rb
|
168
|
+
- lib/active_record_query/concerns/havingable.rb
|
169
|
+
- lib/active_record_query/concerns/identifiable.rb
|
170
|
+
- lib/active_record_query/concerns/joinable.rb
|
171
|
+
- lib/active_record_query/concerns/limitable.rb
|
172
|
+
- lib/active_record_query/concerns/offsetable.rb
|
173
|
+
- lib/active_record_query/concerns/orderable.rb
|
174
|
+
- lib/active_record_query/concerns/selectable.rb
|
175
|
+
- lib/active_record_query/conditions/builder.rb
|
176
|
+
- lib/active_record_query/conditions/chain_link.rb
|
177
|
+
- lib/active_record_query/conditions/condition_group.rb
|
178
|
+
- lib/active_record_query/conditions/conditionable.rb
|
179
|
+
- lib/active_record_query/conditions/where_group.rb
|
180
|
+
- lib/active_record_query/conditions/wor_group.rb
|
181
|
+
- lib/active_record_query/expression_parser.rb
|
182
|
+
- lib/active_record_query/version.rb
|
183
|
+
- lib/activerecord-query.rb
|
184
|
+
homepage: https://github.com/marcosfelipe/activerecord-query
|
185
|
+
licenses:
|
186
|
+
- MIT
|
187
|
+
metadata:
|
188
|
+
homepage_uri: https://github.com/marcosfelipe/activerecord-query
|
189
|
+
source_code_uri: https://github.com/marcosfelipe/activerecord-query
|
190
|
+
changelog_uri: https://github.com/marcosfelipe/activerecord-query/CHANGELOG.md
|
191
|
+
post_install_message:
|
192
|
+
rdoc_options: []
|
193
|
+
require_paths:
|
194
|
+
- lib
|
195
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - ">="
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: 2.6.0
|
200
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
201
|
+
requirements:
|
202
|
+
- - ">="
|
203
|
+
- !ruby/object:Gem::Version
|
204
|
+
version: '0'
|
205
|
+
requirements: []
|
206
|
+
rubygems_version: 3.3.3
|
207
|
+
signing_key:
|
208
|
+
specification_version: 4
|
209
|
+
summary: Small DSL for query build with Active Record.
|
210
|
+
test_files: []
|