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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d8c4dc189756585db85f2f5b335744291f4df89c
4
- data.tar.gz: b23cb30e1e3030e1c3690110c44bb43372c97495
3
+ metadata.gz: 137ea28c0dc8498a7dcb8044dfe22916e852e058
4
+ data.tar.gz: 50a904195d630da9e2286397ac90ebd8d9ff0171
5
5
  SHA512:
6
- metadata.gz: 2d9965e1bdeb3f9b542813a7f87c50cd45651c27ee0317e39940fddf780c4c355eba91bab4c69171c3e174564b538b4fbc55978ebcb4ca52536cabfae656e3fd
7
- data.tar.gz: 69bc201f009943a2b2864a62bdfb37d30be70e64385b1f4e118d5a09b096e0920ef71a4ef6f3c05cbef817813896b12d81e2ac94cbc349fa69885c25e24f8870
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.search_in_current(last_name: 'M')
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 two opt-in modules that can help you when creating
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) }
@@ -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 scope method, or makes an existing method chainable.
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.1
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-08-16 00:00:00.000000000 Z
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