queryable 2.0.1 → 2.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/README.md +46 -24
- data/lib/queryable.rb +3 -40
- data/lib/queryable/chainable.rb +59 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 137ea28c0dc8498a7dcb8044dfe22916e852e058
|
4
|
+
data.tar.gz: 50a904195d630da9e2286397ac90ebd8d9ff0171
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a71dee62ef6250e7e285935baa14b8bcfb336e5c862dab8a7528e12bbe486fd69a5f34e22abfb9bb6db2689710077d242d60e810b45f2b7c95400ce2cf6af33
|
7
|
+
data.tar.gz: d2ae50700d887071e93623387906cc62c36087918d45d64c1091999bea13621da380f9c1f047cb24e4fdf02f1d436ba62cb8ed94686dfe439ef0de2f7c1f1d1b
|
data/README.md
CHANGED
@@ -14,16 +14,6 @@ Queryable is a mixin that allows you to easily define query objects with chainab
|
|
14
14
|
Scopes serve to encapsulate reusable business rules, a method is defined with
|
15
15
|
the selected name and block (or proc)
|
16
16
|
|
17
|
-
### Scopeable Methods
|
18
|
-
|
19
|
-
While scopes are great because of their terseness, they can be limiting because
|
20
|
-
the block executes in the context of the internal query, so methods, constants,
|
21
|
-
and variables of the Queryable are not accessible.
|
22
|
-
|
23
|
-
For those cases, you can use a normal method, and then `scope` it. Queryable
|
24
|
-
will take care of setting the return value of the method as the internal query,
|
25
|
-
and return `self` at the end to make the method chainable.
|
26
|
-
|
27
17
|
### Delegation
|
28
18
|
|
29
19
|
By default most Array methods are delegated to the internal query. It's possible
|
@@ -54,22 +44,10 @@ class CustomersQuery
|
|
54
44
|
def miller_fans
|
55
45
|
favourite_brand(:beer, :Miller)
|
56
46
|
end
|
57
|
-
|
58
|
-
scope def search(field_values)
|
59
|
-
field_values.inject(customers) { |customers, (field, value)|
|
60
|
-
customers.where(field => /#{value}/i)
|
61
|
-
}
|
62
|
-
end
|
63
|
-
|
64
|
-
def search_in_current(field_values)
|
65
|
-
search(field_values).current
|
66
|
-
end
|
67
|
-
|
68
|
-
scope :search_in_current
|
69
47
|
end
|
70
48
|
|
71
49
|
|
72
|
-
CustomerQuery.new(shop.customers).miller_fans
|
50
|
+
CustomerQuery.new(shop.customers).miller_fans
|
73
51
|
```
|
74
52
|
|
75
53
|
## Advantages
|
@@ -85,7 +63,7 @@ to check how to test query objects without even having to require the ORM/ODM, o
|
|
85
63
|
you can test by requiring your ORM/ODM and executing queries as usual.
|
86
64
|
|
87
65
|
## Optional Modules
|
88
|
-
Besides Queryable, there are
|
66
|
+
Besides Queryable, there are three opt-in modules that can help you when creating
|
89
67
|
query objects. These modules would need to be manually required during app
|
90
68
|
initialization or wherever necessary (in Rails, config/initializers).
|
91
69
|
|
@@ -152,6 +130,46 @@ BigCustomersQuery.new.queryable ==
|
|
152
130
|
Customer.where(:last_purchase.gt => 7.days.ago, :total_expense.gt => 9999999)
|
153
131
|
```
|
154
132
|
|
133
|
+
### Chainable Methods
|
134
|
+
|
135
|
+
While scopes are great because of their terseness, they can be limiting because
|
136
|
+
the block executes in the context of the internal query, so methods, constants,
|
137
|
+
and variables of the Queryable are not accessible.
|
138
|
+
|
139
|
+
For those cases, you can use a normal method, and then `chain` it. Chainable
|
140
|
+
will take care of setting the return value of the method as the internal query,
|
141
|
+
and return `self` at the end to make the method chainable.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
class CustomersQuery
|
145
|
+
include Queryable
|
146
|
+
include Queryable::Chainable
|
147
|
+
|
148
|
+
chain :active, :recent
|
149
|
+
|
150
|
+
def active
|
151
|
+
where(status: 'active')
|
152
|
+
end
|
153
|
+
|
154
|
+
def recent
|
155
|
+
queryable.desc(:logged_in_at)
|
156
|
+
end
|
157
|
+
|
158
|
+
chain def search(field_values)
|
159
|
+
field_values.inject(queryable) { |query, (field, value)|
|
160
|
+
query.where(field => /#{value}/i)
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
def search_in_active(field_values)
|
165
|
+
search(field_values).active
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
CustomerQuery.new(shop.customers).miller_fans.search_in_current(last_name: 'M')
|
171
|
+
```
|
172
|
+
|
155
173
|
### Notes
|
156
174
|
To avoid repetition, it's a good idea to create a `BaseQuery` object
|
157
175
|
to contain both the modules inclusion, and common scopes you may reuse.
|
@@ -162,9 +180,13 @@ require 'queryable/default_scope'
|
|
162
180
|
|
163
181
|
def BaseQuery
|
164
182
|
include Queryable
|
183
|
+
include Queryable::Chainable
|
165
184
|
include Queryable::DefaultScope
|
166
185
|
include Queryable::DefaultQuery
|
167
186
|
|
187
|
+
# If you want to be concise:
|
188
|
+
include Queryable::DefaultQuery, Queryable::DefaultScope, Queryable::Chainable, Queryable
|
189
|
+
|
168
190
|
queryable false
|
169
191
|
|
170
192
|
scope :recent, ->{ where(:created_at.gt => 1.week.ago) }
|
data/lib/queryable.rb
CHANGED
@@ -41,7 +41,8 @@ module Queryable
|
|
41
41
|
methods.last.is_a?(Hash) ? super : def_delegators(:queryable, *methods)
|
42
42
|
end
|
43
43
|
|
44
|
-
# Public: Defines a new
|
44
|
+
# Public: Defines a new method that executes the passed proc or block in
|
45
|
+
# the context of the internal query object, and returns self.
|
45
46
|
#
|
46
47
|
# name - Name of the scope to define for this Queryable.
|
47
48
|
#
|
@@ -63,51 +64,13 @@ module Queryable
|
|
63
64
|
# where(_type: "#{brand}ExtremelyFastRacingCar")
|
64
65
|
# end
|
65
66
|
#
|
66
|
-
# scope def search(field_values)
|
67
|
-
# field_values.inject(query) { |query, (field, value)|
|
68
|
-
# query.where(field => /#{value}/i)
|
69
|
-
# }
|
70
|
-
# end
|
71
|
-
#
|
72
67
|
# Returns nothing.
|
73
68
|
def scope(name, proc=nil, &block)
|
74
|
-
if method_defined?(name)
|
75
|
-
scope_method(name)
|
76
|
-
else
|
77
|
-
define_scope(name, proc || block)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
# Internal: Defines a new method that executes the passed proc or block in
|
84
|
-
# the context of the internal query object, and returns self.
|
85
|
-
def define_scope(name, proc)
|
86
69
|
define_method(name) do |*args|
|
87
|
-
@queryable = queryable.instance_exec *args, &proc
|
70
|
+
@queryable = queryable.instance_exec *args, &(proc || block)
|
88
71
|
self
|
89
72
|
end
|
90
73
|
end
|
91
|
-
|
92
|
-
# Public: Makes an existing method chainable by intercepting the call, and
|
93
|
-
# storing the result as the internal query, and returning self.
|
94
|
-
def scope_method(name)
|
95
|
-
prepend Module.new.tap { |s| s.module_eval Queryable.scope_method(name) }
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Internal: Generates the scope interceptor method.
|
100
|
-
#
|
101
|
-
# name - Name of the method to convert to a scope.
|
102
|
-
#
|
103
|
-
# Returns a String with the code of the scope method.
|
104
|
-
def self.scope_method(name)
|
105
|
-
<<-SCOPE
|
106
|
-
def #{name}(*args)
|
107
|
-
@queryable = super
|
108
|
-
self
|
109
|
-
end
|
110
|
-
SCOPE
|
111
74
|
end
|
112
75
|
|
113
76
|
# Internal: Default methods to be delegated to the internal query.
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Public: Provides a class method that allows a method to be chained.
|
2
|
+
module Queryable
|
3
|
+
module Chainable
|
4
|
+
|
5
|
+
# Internal: Adds class methods, and default initialization.
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
# Internal: Contains the Chainable class methods.
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
# Public: Makes an existing method chainable by storing its return value
|
14
|
+
# as the internal query, and returning the query object itself.
|
15
|
+
#
|
16
|
+
# Examples:
|
17
|
+
#
|
18
|
+
# chain :order_by_name
|
19
|
+
#
|
20
|
+
# chain def search(field_values)
|
21
|
+
# field_values.inject(query) { |query, (field, value)|
|
22
|
+
# query.where(field => /#{value}/i)
|
23
|
+
# }
|
24
|
+
# end
|
25
|
+
def chain(*names)
|
26
|
+
prepend Module.new.tap { |m| Chainable.add_scope_methods(m, names) }
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Internal: Defines a scope method in the module per each name in the Array.
|
34
|
+
#
|
35
|
+
# mod - The Module where the scope methods will be added.
|
36
|
+
# names - Names of the methods to chain.
|
37
|
+
#
|
38
|
+
# Returns nothing.
|
39
|
+
def self.add_scope_methods(mod, names)
|
40
|
+
names.each do |name|
|
41
|
+
mod.module_eval Chainable.scope_method(name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Internal: Generates the scope interceptor method.
|
46
|
+
#
|
47
|
+
# name - Name of the method to convert to a scope.
|
48
|
+
#
|
49
|
+
# Returns a String with the code of the scope method.
|
50
|
+
def self.scope_method(name)
|
51
|
+
<<-SCOPE
|
52
|
+
def #{name}(*args)
|
53
|
+
@queryable = super
|
54
|
+
self
|
55
|
+
end
|
56
|
+
SCOPE
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: queryable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Máximo Mussini
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Queryable is a module that encapsulates query building so you don't have
|
14
14
|
to tuck scopes inside your models.
|
@@ -21,6 +21,7 @@ extra_rdoc_files:
|
|
21
21
|
files:
|
22
22
|
- README.md
|
23
23
|
- lib/queryable.rb
|
24
|
+
- lib/queryable/chainable.rb
|
24
25
|
- lib/queryable/default_query.rb
|
25
26
|
- lib/queryable/default_scope.rb
|
26
27
|
homepage: https://github.com/ElMassimo/queryable
|