queryable 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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