queryable 2.0.0 → 2.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 +4 -4
- data/README.md +129 -23
- data/lib/queryable.rb +6 -5
- data/lib/queryable/default_query.rb +5 -3
- 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: d8c4dc189756585db85f2f5b335744291f4df89c
|
4
|
+
data.tar.gz: b23cb30e1e3030e1c3690110c44bb43372c97495
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d9965e1bdeb3f9b542813a7f87c50cd45651c27ee0317e39940fddf780c4c355eba91bab4c69171c3e174564b538b4fbc55978ebcb4ca52536cabfae656e3fd
|
7
|
+
data.tar.gz: 69bc201f009943a2b2864a62bdfb37d30be70e64385b1f4e118d5a09b096e0920ef71a4ef6f3c05cbef817813896b12d81e2ac94cbc349fa69885c25e24f8870
|
data/README.md
CHANGED
@@ -2,15 +2,41 @@ Queryable
|
|
2
2
|
=====================
|
3
3
|
[](http://badge.fury.io/rb/queryable)
|
4
4
|
[](https://travis-ci.org/ElMassimo/queryable)
|
5
|
-
[](https://codeclimate.com/github/ElMassimo/queryable)
|
6
6
|
[](http://inch-ci.org/github/ElMassimo/queryable)
|
7
|
-
|
7
|
+
[](https://github.com/ElMassimo/queryable/blob/master/LICENSE.txt)
|
8
|
+
<!-- [](https://coveralls.io/r/ElMassimo/queryable) -->
|
8
9
|
|
9
10
|
Queryable is a mixin that allows you to easily define query objects with chainable scopes.
|
10
11
|
|
12
|
+
### Scopes
|
13
|
+
|
14
|
+
Scopes serve to encapsulate reusable business rules, a method is defined with
|
15
|
+
the selected name and block (or proc)
|
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
|
+
### Delegation
|
28
|
+
|
29
|
+
By default most Array methods are delegated to the internal query. It's possible
|
30
|
+
to delegate extra methods to the query by calling `delegate`.
|
31
|
+
```ruby
|
32
|
+
def CustomersQuery
|
33
|
+
delegate :update_all, :destroy_all
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
11
37
|
## Usage
|
12
38
|
```ruby
|
13
|
-
class
|
39
|
+
class CustomersQuery
|
14
40
|
include Queryable
|
15
41
|
|
16
42
|
scope(:recent) { desc(:logged_in_at) }
|
@@ -29,34 +55,23 @@ class CustomerQuery
|
|
29
55
|
favourite_brand(:beer, :Miller)
|
30
56
|
end
|
31
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
|
+
|
32
64
|
def search_in_current(field_values)
|
33
|
-
|
34
|
-
field_values.inject(customers) { |customers, (field, value)|
|
35
|
-
customers.where(field => /#{value}/i)
|
36
|
-
}
|
37
|
-
end.current
|
65
|
+
search(field_values).current
|
38
66
|
end
|
67
|
+
|
68
|
+
scope :search_in_current
|
39
69
|
end
|
40
70
|
|
41
71
|
|
42
72
|
CustomerQuery.new(shop.customers).miller_fans.search_in_current(last_name: 'M')
|
43
73
|
```
|
44
74
|
|
45
|
-
### Scopes
|
46
|
-
|
47
|
-
Scopes serve to encapsulate reusable business rules, a method is defined with
|
48
|
-
the selected name and block (or proc)
|
49
|
-
|
50
|
-
### Define Query
|
51
|
-
|
52
|
-
While scopes are great because of their terseness, they can be limiting because
|
53
|
-
the block executes in the context of the internal query, so methods, constants,
|
54
|
-
and variables of the Queryable are not accessible.
|
55
|
-
|
56
|
-
For those cases, you can use `define_query`, which is a convenience setter for
|
57
|
-
the internal query, that also takes care of returning `self` at the end of the
|
58
|
-
call to make the methods chainable.
|
59
|
-
|
60
75
|
## Advantages
|
61
76
|
|
62
77
|
* Query objects are easy to understand.
|
@@ -69,6 +84,97 @@ You can check the [specs](https://github.com/ElMassimo/queryable/tree/master/spe
|
|
69
84
|
to check how to test query objects without even having to require the ORM/ODM, or
|
70
85
|
you can test by requiring your ORM/ODM and executing queries as usual.
|
71
86
|
|
87
|
+
## Optional Modules
|
88
|
+
Besides Queryable, there are two opt-in modules that can help you when creating
|
89
|
+
query objects. These modules would need to be manually required during app
|
90
|
+
initialization or wherever necessary (in Rails, config/initializers).
|
91
|
+
|
92
|
+
### DefaultQuery
|
93
|
+
Provides default initialization for query objects, by attempting to infer the
|
94
|
+
class name of the default collection for the query, and it also provides a
|
95
|
+
`queryable` method to specify it.
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
require 'queryable/default_query'
|
99
|
+
|
100
|
+
def CustomersQuery
|
101
|
+
include Queryable
|
102
|
+
include Queryable::DefaultQuery
|
103
|
+
end
|
104
|
+
|
105
|
+
def OldCustomersQuery < CustomersQuery
|
106
|
+
queryable ArchivedCustomers
|
107
|
+
end
|
108
|
+
|
109
|
+
CustomersQuery.new.queryable == Customer.all
|
110
|
+
OldCustomersQuery.new.queryable == ArchivedCustomers.all
|
111
|
+
```
|
112
|
+
If you want to use common base objects for your queries, you may want want to
|
113
|
+
delay the automatic inference:
|
114
|
+
```ruby
|
115
|
+
class BaseQuery
|
116
|
+
include Queryable
|
117
|
+
include Queryable::DefaultQuery
|
118
|
+
|
119
|
+
queryable false
|
120
|
+
end
|
121
|
+
|
122
|
+
class CustomersQuery < BaseQuery
|
123
|
+
end
|
124
|
+
|
125
|
+
CustomersQuery.new.queryable == Customer.all
|
126
|
+
```
|
127
|
+
|
128
|
+
### DefaultScope
|
129
|
+
Allows to define default scopes in query objects, and inherit them in query
|
130
|
+
object subclasses.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
require 'queryable/default_scope'
|
134
|
+
|
135
|
+
def CustomersQuery
|
136
|
+
include Queryable
|
137
|
+
include Queryable::DefaultScope
|
138
|
+
include Queryable::DefaultQuery
|
139
|
+
|
140
|
+
default_scope :active
|
141
|
+
scope :active, -> { where(:last_purchase.gt => 7.days.ago) }
|
142
|
+
end
|
143
|
+
|
144
|
+
def BigCustomersQuery < CustomersQuery
|
145
|
+
default_scope :big_spender
|
146
|
+
scope :big_spender, -> { where(:total_expense.gt => 9999999) }
|
147
|
+
end
|
148
|
+
|
149
|
+
CustomersQuery.new.queryable == Customer.where(:last_purchase.gt => 7.days.ago)
|
150
|
+
|
151
|
+
BigCustomersQuery.new.queryable ==
|
152
|
+
Customer.where(:last_purchase.gt => 7.days.ago, :total_expense.gt => 9999999)
|
153
|
+
```
|
154
|
+
|
155
|
+
### Notes
|
156
|
+
To avoid repetition, it's a good idea to create a `BaseQuery` object
|
157
|
+
to contain both the modules inclusion, and common scopes you may reuse.
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
require 'queryable/default_query'
|
161
|
+
require 'queryable/default_scope'
|
162
|
+
|
163
|
+
def BaseQuery
|
164
|
+
include Queryable
|
165
|
+
include Queryable::DefaultScope
|
166
|
+
include Queryable::DefaultQuery
|
167
|
+
|
168
|
+
queryable false
|
169
|
+
|
170
|
+
scope :recent, ->{ where(:created_at.gt => 1.week.ago) }
|
171
|
+
end
|
172
|
+
|
173
|
+
def CustomersQuery < BaseQuery
|
174
|
+
...
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
72
178
|
## RDocs
|
73
179
|
|
74
180
|
You can view the **Queryable** documentation in RDoc format here:
|
data/lib/queryable.rb
CHANGED
@@ -18,7 +18,8 @@ module Queryable
|
|
18
18
|
base.extend ClassMethods
|
19
19
|
base.class_eval do
|
20
20
|
# Public: Gets/Sets the internal query.
|
21
|
-
attr_accessor :
|
21
|
+
attr_accessor :queryable
|
22
|
+
alias_method :query, :queryable
|
22
23
|
|
23
24
|
# Internal: Delegates Array and Criteria methods to the internal query.
|
24
25
|
delegate *Queryable.default_delegated_methods
|
@@ -29,7 +30,7 @@ module Queryable
|
|
29
30
|
#
|
30
31
|
# query - The internal query to build upon.
|
31
32
|
def initialize(query)
|
32
|
-
@
|
33
|
+
@queryable = query.all
|
33
34
|
end
|
34
35
|
|
35
36
|
# Internal: Contains the Queryable class methods.
|
@@ -37,7 +38,7 @@ module Queryable
|
|
37
38
|
|
38
39
|
# Public: Delegates the specified methods to the internal query.
|
39
40
|
def delegate(*methods)
|
40
|
-
methods.last.is_a?(Hash) ? super : def_delegators(:
|
41
|
+
methods.last.is_a?(Hash) ? super : def_delegators(:queryable, *methods)
|
41
42
|
end
|
42
43
|
|
43
44
|
# Public: Defines a new scope method, or makes an existing method chainable.
|
@@ -83,7 +84,7 @@ module Queryable
|
|
83
84
|
# the context of the internal query object, and returns self.
|
84
85
|
def define_scope(name, proc)
|
85
86
|
define_method(name) do |*args|
|
86
|
-
@
|
87
|
+
@queryable = queryable.instance_exec *args, &proc
|
87
88
|
self
|
88
89
|
end
|
89
90
|
end
|
@@ -103,7 +104,7 @@ module Queryable
|
|
103
104
|
def self.scope_method(name)
|
104
105
|
<<-SCOPE
|
105
106
|
def #{name}(*args)
|
106
|
-
@
|
107
|
+
@queryable = super
|
107
108
|
self
|
108
109
|
end
|
109
110
|
SCOPE
|
@@ -28,12 +28,14 @@ module Queryable
|
|
28
28
|
queryable_class.all
|
29
29
|
end
|
30
30
|
|
31
|
-
# Internal:
|
32
|
-
#
|
31
|
+
# Internal: Attempts to use the parent query collection (if any), and
|
32
|
+
# provides a default based on a convention of the query object name.
|
33
33
|
def queryable_class
|
34
|
-
|
34
|
+
unless defined?(@queryable_class)
|
35
|
+
@queryable_class = superclass.respond_to?(:queryable_class) &&
|
35
36
|
superclass.queryable_class || Object.const_get(name.gsub('sQuery', ''))
|
36
37
|
end
|
38
|
+
@queryable_class
|
37
39
|
end
|
38
40
|
end
|
39
41
|
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.0.1
|
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-08-
|
11
|
+
date: 2014-08-16 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.
|
@@ -49,3 +49,4 @@ signing_key:
|
|
49
49
|
specification_version: 4
|
50
50
|
summary: Keep your scopes and queries flexible by using Ruby
|
51
51
|
test_files: []
|
52
|
+
has_rdoc:
|