hoodoo 1.11.0 → 1.12.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 +5 -13
- data/lib/hoodoo/active/active_record/finder.rb +16 -17
- data/lib/hoodoo/active/active_record/search_helper.rb +118 -29
- data/lib/hoodoo/active/active_record/support.rb +45 -0
- data/lib/hoodoo/client/paginated_enumeration.rb +1 -1
- data/lib/hoodoo/services/middleware/middleware.rb +85 -12
- data/lib/hoodoo/services/services/interface.rb +66 -8
- data/lib/hoodoo/version.rb +1 -1
- data/spec/active/active_record/finder_spec.rb +131 -11
- data/spec/active/active_record/search_helper_spec.rb +500 -273
- data/spec/active/active_record/support_spec.rb +22 -0
- data/spec/client/paginated_enumeration_spec.rb +41 -42
- data/spec/monkey/patch/newrelic_middleware_analytics_spec.rb +60 -0
- data/spec/monkey/patch/newrelic_traced_amqp_spec.rb +2 -2
- data/spec/new_relic/agent/method_tracer.rb +35 -0
- data/spec/services/middleware/middleware_spec.rb +74 -0
- data/spec/services/services/interface_spec.rb +26 -0
- metadata +43 -39
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
YWNkMjFhNmMwODQ2OGYxYjZmNDI1ZGIxZGJmNzg3YjQyY2I5ZDMxYw==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 30fd454d84a35502e8c324645a1c8c981521aa7e
|
4
|
+
data.tar.gz: fae766337e9034cf46e9c030c64d5d6ea0609827
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ZGE3N2RhNWZlOGMwMDQ2MWVmNzNhMzQ3MWEwZTRiZDhjMjBkMDNjNDdhZjdh
|
11
|
-
ZjYxOGUwYTk1NDFlYTJmY2VhNGYxYWI5ODk5YzdmNDNkOThjYTE=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MmQzNzQxZWI0OTQ2YjI2Nzg1ODAxMzA0ODVlNmM0ODRmMTE2YTY0YmEwYzA2
|
14
|
-
YTdjMzZkMTY4NWRlNjlmMzdhNTc1NDdiYWM4OTQ1YjNkNzQzNjYzZTlmNDFl
|
15
|
-
Nzc3Mzk5ZmFhMDIxOTk0NmY2MzIyMTc2MGQ2YjFlYWQ5MDQxMGQ=
|
6
|
+
metadata.gz: be4607dfff0f5f798b48eb83abbd3985562df696570137f1c7c9fa7fbaad5df227659c274c758c323fb12901365d665c9a1642d743c17103708cb1509b23db3f
|
7
|
+
data.tar.gz: 0c35e38a6740d1e689c361fcf2f0ab07bac09540eb46a9bb1d7ac2f3ab40319c3711b26741fe85c0836e5b1d277208c9c06152f3aa5369745c936714de7a4bf7
|
@@ -20,8 +20,8 @@ module Hoodoo
|
|
20
20
|
#
|
21
21
|
# It is _STRONGLY_ _RECOMMENDED_ that you use the likes of:
|
22
22
|
#
|
23
|
-
# * Hoodoo::ActiveRecord::Finder::ClassMethods
|
24
|
-
# * Hoodoo::ActiveRecord::Finder::ClassMethods
|
23
|
+
# * Hoodoo::ActiveRecord::Finder::ClassMethods#acquire_in
|
24
|
+
# * Hoodoo::ActiveRecord::Finder::ClassMethods#list_in
|
25
25
|
#
|
26
26
|
# ...to retrieve model data related to resource instances and participate
|
27
27
|
# "for free" in whatever plug-in ActiveRecord modules are mixed into the
|
@@ -428,20 +428,14 @@ module Hoodoo
|
|
428
428
|
finder = all.offset( list_parameters.offset ).limit( list_parameters.limit )
|
429
429
|
finder = finder.order( list_parameters.sort_data )
|
430
430
|
|
431
|
-
# DRY up the 'each' loops below. Use a Proc not a method because any
|
432
|
-
# methods we define will end up being defined on the including Model,
|
433
|
-
# increasing the chance of a name collision.
|
434
|
-
#
|
435
|
-
dry_proc = Proc.new do | data, attr, proc |
|
436
|
-
value = data[ attr ]
|
437
|
-
proc.call( attr, value ) unless value.nil?
|
438
|
-
end
|
439
|
-
|
440
431
|
search_map = self.nz_co_loyalty_hoodoo_search_with
|
441
432
|
|
442
433
|
unless search_map.nil?
|
443
|
-
search_map.each do | attr,
|
444
|
-
|
434
|
+
search_map.each do | attr, finder_args_proc |
|
435
|
+
value = list_parameters.search_data[ attr ]
|
436
|
+
next if value.nil?
|
437
|
+
|
438
|
+
args = finder_args_proc.call( attr, value )
|
445
439
|
finder = finder.where( *args ) unless args.nil?
|
446
440
|
end
|
447
441
|
end
|
@@ -449,8 +443,11 @@ module Hoodoo
|
|
449
443
|
filter_map = self.nz_co_loyalty_hoodoo_filter_with
|
450
444
|
|
451
445
|
unless filter_map.nil?
|
452
|
-
filter_map.each do | attr,
|
453
|
-
|
446
|
+
filter_map.each do | attr, finder_args_proc |
|
447
|
+
value = list_parameters.filter_data[ attr ]
|
448
|
+
next if value.nil?
|
449
|
+
|
450
|
+
args = finder_args_proc.call( attr, value )
|
454
451
|
finder = finder.where.not( *args ) unless args.nil?
|
455
452
|
end
|
456
453
|
end
|
@@ -714,7 +711,8 @@ module Hoodoo
|
|
714
711
|
# which assist with filling in non-nil values for this Hash.
|
715
712
|
#
|
716
713
|
def search_with( hash )
|
717
|
-
self.nz_co_loyalty_hoodoo_search_with
|
714
|
+
self.nz_co_loyalty_hoodoo_search_with ||= Hoodoo::ActiveRecord::Support.framework_search_and_filter_data()
|
715
|
+
self.nz_co_loyalty_hoodoo_search_with.merge!( Hoodoo::ActiveRecord::Support.process_to_map( hash ) )
|
718
716
|
end
|
719
717
|
|
720
718
|
# As #search_with, but used in +where.not+ queries.
|
@@ -735,7 +733,8 @@ module Hoodoo
|
|
735
733
|
# +map+:: As #search_with.
|
736
734
|
#
|
737
735
|
def filter_with( hash )
|
738
|
-
self.nz_co_loyalty_hoodoo_filter_with
|
736
|
+
self.nz_co_loyalty_hoodoo_filter_with ||= Hoodoo::ActiveRecord::Support.framework_search_and_filter_data()
|
737
|
+
self.nz_co_loyalty_hoodoo_filter_with.merge!( Hoodoo::ActiveRecord::Support.process_to_map( hash ) )
|
739
738
|
end
|
740
739
|
|
741
740
|
# Deprecated interface replaced by #acquire. Instead of:
|
@@ -14,8 +14,9 @@ module Hoodoo
|
|
14
14
|
module Finder
|
15
15
|
|
16
16
|
# Help build up Hash maps to pass into Hoodoo::ActiveRecord::Finder
|
17
|
-
# methods Hoodoo::ActiveRecord::Finder#search_with and
|
18
|
-
# Hoodoo::ActiveRecord::Finder#filter_with.
|
17
|
+
# methods Hoodoo::ActiveRecord::Finder::ClassMethods#search_with and
|
18
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#filter_with. Used also
|
19
|
+
# by the default framework search scopes.
|
19
20
|
#
|
20
21
|
# The usage pattern is as follows, using "sh" as a local variable
|
21
22
|
# just for brevity - it isn't required:
|
@@ -31,7 +32,7 @@ module Hoodoo
|
|
31
32
|
# end
|
32
33
|
#
|
33
34
|
# The helper methods just provide values to pass into the Hash used
|
34
|
-
# with the search/
|
35
|
+
# with the search/filter Hoodoo::ActiveRecord::Finder methods, so
|
35
36
|
# they're optional and compatible with calls that write it out "by
|
36
37
|
# hand".
|
37
38
|
#
|
@@ -48,15 +49,16 @@ module Hoodoo
|
|
48
49
|
# will be case sensitive only if your database is configured for
|
49
50
|
# case sensitive matching by default.
|
50
51
|
#
|
51
|
-
# Results in a <tt>foo = bar</tt> query.
|
52
|
+
# Results in a <tt>foo = bar AND foo IS NOT NULL</tt> query.
|
52
53
|
#
|
53
54
|
# +model_field_name+:: If the model attribute name differs from the
|
54
55
|
# search key you want to use in the URI, give
|
55
56
|
# the model attribute name here, else omit.
|
56
57
|
#
|
57
58
|
# Returns a value that can be asssigned to a URI query string key in
|
58
|
-
# the Hash given to
|
59
|
-
# Hoodoo::ActiveRecord::Finder#
|
59
|
+
# the Hash given to
|
60
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#search_with or
|
61
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#filter_with.
|
60
62
|
#
|
61
63
|
def self.cs_match( model_field_name = nil )
|
62
64
|
Proc.new { | attr, value |
|
@@ -70,15 +72,17 @@ module Hoodoo
|
|
70
72
|
# which are split into an array then processed by AREL back to
|
71
73
|
# something SQL-safe.
|
72
74
|
#
|
73
|
-
# Results in a <tt>foo IN bar,baz,boo</tt>
|
75
|
+
# Results in a <tt>foo IN (bar,baz,boo) AND foo IS NOT NULL</tt>
|
76
|
+
# query.
|
74
77
|
#
|
75
78
|
# +model_field_name+:: If the model attribute name differs from the
|
76
79
|
# search key you want to use in the URI, give
|
77
80
|
# the model attribute name here, else omit.
|
78
81
|
#
|
79
82
|
# Returns a value that can be asssigned to a URI query string key in
|
80
|
-
# the Hash given to
|
81
|
-
# Hoodoo::ActiveRecord::Finder#
|
83
|
+
# the Hash given to
|
84
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#search_with or
|
85
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#filter_with.
|
82
86
|
#
|
83
87
|
def self.cs_match_csv( model_field_name = nil )
|
84
88
|
Proc.new { | attr, value |
|
@@ -92,17 +96,20 @@ module Hoodoo
|
|
92
96
|
# Case-sensitive match of a series of values given as an Array.
|
93
97
|
# Normally, query string information comes in as a String so the
|
94
98
|
# use cases for this are quite unusual; you probably want to use
|
95
|
-
#
|
99
|
+
# Hoodoo::ActiveRecord::Finder::SearchHelper::cs_match_csv most of
|
100
|
+
# the time.
|
96
101
|
#
|
97
|
-
# Results in a <tt>foo IN bar,baz,boo</tt>
|
102
|
+
# Results in a <tt>foo IN (bar,baz,boo) AND foo IS NOT NULL</tt>
|
103
|
+
# query.
|
98
104
|
#
|
99
105
|
# +model_field_name+:: If the model attribute name differs from the
|
100
106
|
# search key you want to use in the URI, give
|
101
107
|
# the model attribute name here, else omit.
|
102
108
|
#
|
103
109
|
# Returns a value that can be asssigned to a URI query string key in
|
104
|
-
# the Hash given to
|
105
|
-
# Hoodoo::ActiveRecord::Finder#
|
110
|
+
# the Hash given to
|
111
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#search_with or
|
112
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#filter_with.
|
106
113
|
#
|
107
114
|
def self.cs_match_array( model_field_name = nil )
|
108
115
|
Proc.new { | attr, value |
|
@@ -113,8 +120,11 @@ module Hoodoo
|
|
113
120
|
}
|
114
121
|
end
|
115
122
|
|
116
|
-
# As
|
117
|
-
# for a
|
123
|
+
# As Hoodoo::ActiveRecord::Finder::SearchHelper::cs_match, but
|
124
|
+
# adds wildcards at the front and end of the string for a
|
125
|
+
# case-sensitive-all-wildcard match.
|
126
|
+
#
|
127
|
+
# Results in a <tt>foo LIKE bar AND foo IS NOT NULL</tt> query.
|
118
128
|
#
|
119
129
|
def self.csaw_match( model_field_name = nil )
|
120
130
|
Proc.new { | attr, value |
|
@@ -127,19 +137,22 @@ module Hoodoo
|
|
127
137
|
|
128
138
|
# Case-insensitive match which should be fairly database independent
|
129
139
|
# but will run relatively slowly as a result. If you are using
|
130
|
-
# PostgreSQL, consider using the faster
|
140
|
+
# PostgreSQL, consider using the faster
|
141
|
+
# Hoodoo::ActiveRecord::Finder::SearchHelper::ci_match_postgres method
|
131
142
|
# instead.
|
132
143
|
#
|
133
|
-
# Results in a <tt>lower(foo) = bar</tt> query
|
134
|
-
# a String and converted to lower case by Ruby
|
144
|
+
# Results in a <tt>lower(foo) = bar AND foo IS NOT NULL</tt> query
|
145
|
+
# with +bar+ coerced to a String and converted to lower case by Ruby
|
146
|
+
# first.
|
135
147
|
#
|
136
148
|
# +model_field_name+:: If the model attribute name differs from the
|
137
149
|
# search key you want to use in the URI, give
|
138
150
|
# the model attribute name here, else omit.
|
139
151
|
#
|
140
152
|
# Returns a value that can be asssigned to a URI query string key in
|
141
|
-
# the Hash given to
|
142
|
-
# Hoodoo::ActiveRecord::Finder#
|
153
|
+
# the Hash given to
|
154
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#search_with or
|
155
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#filter_with.
|
143
156
|
#
|
144
157
|
def self.ci_match_generic( model_field_name = nil )
|
145
158
|
Proc.new { | attr, value |
|
@@ -150,8 +163,11 @@ module Hoodoo
|
|
150
163
|
}
|
151
164
|
end
|
152
165
|
|
153
|
-
# As
|
154
|
-
# the string for a
|
166
|
+
# As Hoodoo::ActiveRecord::Finder::SearchHelper::ci_match_generic,
|
167
|
+
# but adds wildcards at the front and end of the string for a
|
168
|
+
# case-insensitive-all-wildcard match.
|
169
|
+
#
|
170
|
+
# Results in a <tt>foo LIKE %bar% AND foo IS NOT NULL</tt> query.
|
155
171
|
#
|
156
172
|
def self.ciaw_match_generic( model_field_name = nil )
|
157
173
|
Proc.new { | attr, value |
|
@@ -164,17 +180,20 @@ module Hoodoo
|
|
164
180
|
|
165
181
|
# Case-insensitive match which requires PostgreSQL but should run
|
166
182
|
# quickly. If you need a database agnostic solution, consider using
|
167
|
-
# the slower
|
183
|
+
# the slower
|
184
|
+
# Hoodoo::ActiveRecord::Finder::SearchHelper::ci_match_generic method
|
185
|
+
# instead.
|
168
186
|
#
|
169
|
-
# Results in a <tt>foo ILIKE bar</tt> query.
|
187
|
+
# Results in a <tt>foo ILIKE bar AND foo IS NOT NULL</tt> query.
|
170
188
|
#
|
171
189
|
# +model_field_name+:: If the model attribute name differs from the
|
172
190
|
# search key you want to use in the URI, give
|
173
191
|
# the model attribute name here, else omit.
|
174
192
|
#
|
175
193
|
# Returns a value that can be asssigned to a URI query string key in
|
176
|
-
# the Hash given to
|
177
|
-
# Hoodoo::ActiveRecord::Finder#
|
194
|
+
# the Hash given to
|
195
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#search_with or
|
196
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#filter_with.
|
178
197
|
#
|
179
198
|
def self.ci_match_postgres( model_field_name = nil )
|
180
199
|
Proc.new { | attr, value |
|
@@ -184,8 +203,11 @@ module Hoodoo
|
|
184
203
|
}
|
185
204
|
end
|
186
205
|
|
187
|
-
# As
|
188
|
-
# the string for a
|
206
|
+
# As Hoodoo::ActiveRecord::Finder::SearchHelper::ci_match_postgres,
|
207
|
+
# but adds wildcards at the front and end of the string for a
|
208
|
+
# case-insensitive-all-wildcard match.
|
209
|
+
#
|
210
|
+
# Results in a <tt>foo ILIKE %bar% AND foo IS NOT NULL</tt> query.
|
189
211
|
#
|
190
212
|
def self.ciaw_match_postgres( model_field_name = nil )
|
191
213
|
Proc.new { | attr, value |
|
@@ -194,8 +216,75 @@ module Hoodoo
|
|
194
216
|
[ "#{ column } ILIKE ? AND #{ column } IS NOT NULL", "%#{ value }%" ]
|
195
217
|
}
|
196
218
|
end
|
197
|
-
end
|
198
219
|
|
220
|
+
# Case-sensitive less-than (default-style comparison). *WARNING:* This
|
221
|
+
# will be case sensitive only if your database is configured for
|
222
|
+
# case sensitive matching by default.
|
223
|
+
#
|
224
|
+
# If comparing non-string column types be sure to pass in a value of an
|
225
|
+
# appropriate matching type (e.g. compare dates with DateTimes), else
|
226
|
+
# returned results will be incorrect but errors may not arise depending
|
227
|
+
# on database engine in use.
|
228
|
+
#
|
229
|
+
# Results in a <tt>foo < bar AND foo IS NOT NULL</tt> query.
|
230
|
+
#
|
231
|
+
# +model_field_name+:: If the model attribute name differs from the
|
232
|
+
# search key you want to use in the URI, give
|
233
|
+
# the model attribute name here, else omit.
|
234
|
+
#
|
235
|
+
# Returns a value that can be asssigned to a URI query string key in
|
236
|
+
# the Hash given to
|
237
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#search_with or
|
238
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#filter_with.
|
239
|
+
#
|
240
|
+
def self.cs_lt( model_field_name = nil )
|
241
|
+
Proc.new { | attr, value |
|
242
|
+
column = model_field_name || attr
|
243
|
+
|
244
|
+
[ "#{ column } < ? AND #{ column } IS NOT NULL", value ]
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
# As Hoodoo::ActiveRecord::Finder::SearchHelper::cs_lt, but
|
249
|
+
# compares with less-than-or-equal-to.
|
250
|
+
#
|
251
|
+
# Results in a <tt>foo <= bar AND foo IS NOT NULL</tt> query.
|
252
|
+
#
|
253
|
+
def self.cs_lte( model_field_name = nil )
|
254
|
+
Proc.new { | attr, value |
|
255
|
+
column = model_field_name || attr
|
256
|
+
|
257
|
+
[ "#{ column } <= ? AND #{ column } IS NOT NULL", value ]
|
258
|
+
}
|
259
|
+
end
|
260
|
+
|
261
|
+
# As Hoodoo::ActiveRecord::Finder::SearchHelper::cs_lt, but
|
262
|
+
# compares with greater-than.
|
263
|
+
#
|
264
|
+
# Results in a <tt>foo > bar AND foo IS NOT NULL</tt> query.
|
265
|
+
#
|
266
|
+
def self.cs_gt( model_field_name = nil )
|
267
|
+
Proc.new { | attr, value |
|
268
|
+
column = model_field_name || attr
|
269
|
+
|
270
|
+
[ "#{ column } > ? AND #{ column } IS NOT NULL", value ]
|
271
|
+
}
|
272
|
+
end
|
273
|
+
|
274
|
+
# As Hoodoo::ActiveRecord::Finder::SearchHelper::cs_lt, but
|
275
|
+
# compares with greater-than-or-equal-to.
|
276
|
+
#
|
277
|
+
# Results in a <tt>foo >= bar AND foo IS NOT NULL</tt> query.
|
278
|
+
#
|
279
|
+
def self.cs_gte( model_field_name = nil )
|
280
|
+
Proc.new { | attr, value |
|
281
|
+
column = model_field_name || attr
|
282
|
+
|
283
|
+
[ "#{ column } >= ? AND #{ column } IS NOT NULL", value ]
|
284
|
+
}
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
199
288
|
end
|
200
289
|
end
|
201
290
|
end
|
@@ -35,6 +35,51 @@ module Hoodoo
|
|
35
35
|
#
|
36
36
|
class Support
|
37
37
|
|
38
|
+
# Returns a (newly generated) Hash of search keys mapping to helper Procs
|
39
|
+
# which are in the same format as would be passed to
|
40
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#search_with or
|
41
|
+
# Hoodoo::ActiveRecord::Finder::ClassMethods#filter_with, describing the
|
42
|
+
# default framework search parameters. The middleware defines keys, but
|
43
|
+
# each ORM adapter module must specify how those keys actually get used
|
44
|
+
# to search inside supported database engines.
|
45
|
+
#
|
46
|
+
def self.framework_search_and_filter_data
|
47
|
+
|
48
|
+
# The middleware includes framework-level mappings between URI query
|
49
|
+
# string search keys and data validators and processors which convert
|
50
|
+
# types where necessary. For example, 'created_at' must be given a
|
51
|
+
# valid ISO 8601 subset string and a parsed DateTime will end up in
|
52
|
+
# the parsed search hash.
|
53
|
+
#
|
54
|
+
# Services opt out of framework-level searching at an interface level
|
55
|
+
# which means the Finder code herein, under normal flow, will never
|
56
|
+
# be asked to process something the interface omits. There is thus no
|
57
|
+
# need to try and break encapsulation and come up with a way to read
|
58
|
+
# the service interface's omissions. Instead, map everything.
|
59
|
+
#
|
60
|
+
# This could actually be useful if someone manually drives the #list
|
61
|
+
# mechanism with hand-constructed search or filter data that quite
|
62
|
+
# intentionally includes framework level parameters even if their own
|
63
|
+
# service interface for some reason opts out of allowing them to be
|
64
|
+
# exposed to API callers.
|
65
|
+
#
|
66
|
+
# Note that the #search_with / #filter_with DSL declaration in an
|
67
|
+
# appropriately extended model can be used to override the default
|
68
|
+
# values wired in below, because the defaults are established by
|
69
|
+
# design _before_ the model declarations are processed.
|
70
|
+
#
|
71
|
+
mapping = {
|
72
|
+
'created_after' => Hoodoo::ActiveRecord::Finder::SearchHelper.cs_gt( :created_at ),
|
73
|
+
'created_before' => Hoodoo::ActiveRecord::Finder::SearchHelper.cs_lt( :created_at )
|
74
|
+
}
|
75
|
+
|
76
|
+
if mapping.keys.length != ( mapping.keys | Hoodoo::Services::Middleware::FRAMEWORK_QUERY_DATA.keys ).length
|
77
|
+
raise 'Hoodoo::ActiveRecord::Support#framework_search_and_filter_data: Mismatch between internal mapping and Hoodoo::Services::Middleware::FRAMEWORK_QUERY_DATA'
|
78
|
+
end
|
79
|
+
|
80
|
+
return mapping
|
81
|
+
end
|
82
|
+
|
38
83
|
# Takes a Hash of possibly-non-String keys and with +nil+ values or
|
39
84
|
# Proc instances appropriate for Hoodoo::ActiveRecord::Finder#search_with
|
40
85
|
# / #filter_with. Returns a similar Hash with all-String keys and a Proc
|
@@ -52,7 +52,7 @@ module Hoodoo
|
|
52
52
|
if results.size > 0
|
53
53
|
|
54
54
|
if results.platform_errors.has_errors?
|
55
|
-
raise
|
55
|
+
raise 'Hoodoo::Client::PaginatedEnumeration#enumerate_all: Unexpected internal state combination of results set and results error indication'
|
56
56
|
end
|
57
57
|
|
58
58
|
# Yield a resource at a time to the caller
|
@@ -175,6 +175,48 @@ module Hoodoo; module Services
|
|
175
175
|
}
|
176
176
|
} )
|
177
177
|
|
178
|
+
# A validation Proc for FRAMEWORK_QUERY_DATA - see that for details. This
|
179
|
+
# one ensures that the value is a valid ISO 8601 subset date/time string
|
180
|
+
# and evaluates to the parsed version of that string if so.
|
181
|
+
#
|
182
|
+
FRAMEWORK_QUERY_VALUE_DATE_PROC = -> ( value ) {
|
183
|
+
Hoodoo::Utilities.valid_iso8601_subset_datetime?( value ) ?
|
184
|
+
Hoodoo::Utilities.rationalise_datetime( value ) :
|
185
|
+
nil
|
186
|
+
}
|
187
|
+
|
188
|
+
# Out-of-box search and filter query keys. Interfaces can override the
|
189
|
+
# support for these inside the Hoodoo::Services::Interface.to_list block
|
190
|
+
# using Hoodoo::Services::Interface::ToListDSL.do_not_search and
|
191
|
+
# Hoodoo::Services::Interface::ToListDSL.do_not_filter.
|
192
|
+
#
|
193
|
+
# Keys, in order, are:
|
194
|
+
#
|
195
|
+
# * Query key to detect records with a +created_at+ date that is after the
|
196
|
+
# given value, in supporting resource; if used as a filter instead of a
|
197
|
+
# search string, would find records on-or-before the date.
|
198
|
+
#
|
199
|
+
# * Query key to detect records with a +created_at+ date that is before
|
200
|
+
# the given value, in supporting resource; if used as a filter instead
|
201
|
+
# of a search string, would find records on-or-after the date.
|
202
|
+
#
|
203
|
+
# Values are either a validation Proc or +nil+ for no validation. The
|
204
|
+
# Proc takes the search query value as its sole input paraeter and must
|
205
|
+
# evaluate to the input value either unmodified or in some canonicalised
|
206
|
+
# form if it is valid, else to +nil+ if the input value is invalid. The
|
207
|
+
# canonicalisation is typically used to coerce a URI query string based
|
208
|
+
# String type into a more useful comparable entity such as an Integer or
|
209
|
+
# DateTime.
|
210
|
+
#
|
211
|
+
# *IMPORTANT* - if this list is changed, any database support modules -
|
212
|
+
# e.g. in Hoodoo::ActiveRecord::Support - will need any internal mapping
|
213
|
+
# of "framework query keys to module-appropriate query code" updating.
|
214
|
+
#
|
215
|
+
FRAMEWORK_QUERY_DATA = {
|
216
|
+
'created_after' => FRAMEWORK_QUERY_VALUE_DATE_PROC,
|
217
|
+
'created_before' => FRAMEWORK_QUERY_VALUE_DATE_PROC,
|
218
|
+
}
|
219
|
+
|
178
220
|
# Utility - returns the execution environment as a Rails-like environment
|
179
221
|
# object which answers queries like +production?+ or +staging?+ with +true+
|
180
222
|
# or +false+ according to the +RACK_ENV+ environment variable setting.
|
@@ -2770,21 +2812,52 @@ module Hoodoo; module Services
|
|
2770
2812
|
end
|
2771
2813
|
end
|
2772
2814
|
|
2773
|
-
search
|
2774
|
-
|
2775
|
-
|
2815
|
+
search = query_hash[ 'search' ] || {}
|
2816
|
+
framework_search = FRAMEWORK_QUERY_DATA.keys - interface.to_list.do_not_search
|
2817
|
+
bad_search_keys = search.keys - framework_search - interface.to_list.search
|
2818
|
+
|
2819
|
+
framework_search.each do | search_key |
|
2820
|
+
next unless search.has_key?( search_key )
|
2821
|
+
|
2822
|
+
search_value = search[ search_key ]
|
2823
|
+
validator = FRAMEWORK_QUERY_DATA[ search_key ]
|
2824
|
+
canonical = validator.call( search_value )
|
2825
|
+
|
2826
|
+
if canonical.nil?
|
2827
|
+
bad_search_keys << search_key
|
2828
|
+
else
|
2829
|
+
search[ search_key ] = canonical
|
2830
|
+
end
|
2831
|
+
end
|
2832
|
+
|
2833
|
+
filter = query_hash[ 'filter' ] || {}
|
2834
|
+
framework_filter = FRAMEWORK_QUERY_DATA.keys - interface.to_list.do_not_filter
|
2835
|
+
bad_filter_keys = filter.keys - framework_filter - interface.to_list.filter
|
2836
|
+
|
2837
|
+
framework_filter.each do | filter_key |
|
2838
|
+
next unless filter.has_key?( filter_key )
|
2839
|
+
|
2840
|
+
filter_value = filter[ filter_key ]
|
2841
|
+
validator = FRAMEWORK_QUERY_DATA[ filter_key ]
|
2842
|
+
canonical = validator.call( filter_value )
|
2843
|
+
|
2844
|
+
if canonical.nil?
|
2845
|
+
bad_filter_keys << filter_key
|
2846
|
+
else
|
2847
|
+
filter[ filter_key ] = canonical
|
2848
|
+
end
|
2849
|
+
end
|
2776
2850
|
|
2777
|
-
|
2778
|
-
|
2779
|
-
malformed << "filter: #{ unrecognised_filter_keys.join(', ') }" unless unrecognised_filter_keys.empty?
|
2851
|
+
embeds = query_hash[ '_embed' ] || []
|
2852
|
+
bad_embeds = embeds - interface.embeds
|
2780
2853
|
|
2781
|
-
|
2782
|
-
|
2783
|
-
malformed << "_embed: #{ unrecognised_embeds.join(', ') }" unless unrecognised_embeds.empty?
|
2854
|
+
references = query_hash[ '_reference' ] || []
|
2855
|
+
bad_references = references - interface.embeds # (sic.)
|
2784
2856
|
|
2785
|
-
|
2786
|
-
|
2787
|
-
malformed <<
|
2857
|
+
malformed << "search: #{ bad_search_keys.join( ', ' ) }" unless bad_search_keys.empty?
|
2858
|
+
malformed << "filter: #{ bad_filter_keys.join( ', ' ) }" unless bad_filter_keys.empty?
|
2859
|
+
malformed << "_embed: #{ bad_embeds.join( ', ' ) }" unless bad_embeds.empty?
|
2860
|
+
malformed << "_reference: #{ bad_references.join( ', ' ) }" unless bad_references.empty?
|
2788
2861
|
|
2789
2862
|
return response.add_error(
|
2790
2863
|
'platform.malformed',
|