qo 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +3 -1
- data/README.md +4 -0
- data/lib/qo/guard_block_matcher.rb +2 -2
- data/lib/qo/matcher.rb +104 -34
- data/lib/qo/version.rb +1 -1
- data/performance_report.txt +24 -24
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1af9159c107b7761e1b30c7062b108e692403067
|
4
|
+
data.tar.gz: 483b4e35c8c9bba6a7e84c7516ea3e8a1de6e5b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3db647525ff5685025108dacbe62b7945f135399d56c8a68eb9f39eb63dda3704614da143608a97d66251754c7d57c97d69d5e1a0d73f0c56e5d256cf813acdf
|
7
|
+
data.tar.gz: 72582ba2f1d0f81d149e2f0b9e3d35d10dcb929418c2cde4f88d3773a734f93c7e78594dc59689058f8de0d2cf3edfbc9dc7bb448ec7cfe9bda237fe101a07d0
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Qo
|
2
2
|
|
3
|
+
[![Build Status](https://travis-ci.org/baweaver/qo.svg?branch=master)](https://travis-ci.org/baweaver/qo)
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/186e9cbb7003842acaf0/maintainability)](https://codeclimate.com/github/baweaver/qo/maintainability)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/qo.svg)](https://badge.fury.io/rb/qo)
|
6
|
+
|
3
7
|
Short for Query Object, my play at Ruby pattern matching and fluent querying
|
4
8
|
|
5
9
|
![Qo Lemur logo](img/qo_logo.png)
|
data/lib/qo/matcher.rb
CHANGED
@@ -45,9 +45,15 @@ module Qo
|
|
45
45
|
Proc.new { |match_target|
|
46
46
|
next true if matchers == match_target
|
47
47
|
|
48
|
-
match_target.is_a?(::Array)
|
49
|
-
|
50
|
-
|
48
|
+
if match_target.is_a?(::Array)
|
49
|
+
match_collection = matchers.each_with_index
|
50
|
+
match_fn = array_against_array_matcher(match_target)
|
51
|
+
else
|
52
|
+
match_collection = matchers
|
53
|
+
match_fn = array_against_object_matcher(match_target)
|
54
|
+
end
|
55
|
+
|
56
|
+
match_with(match_collection, match_fn)
|
51
57
|
}
|
52
58
|
end
|
53
59
|
|
@@ -64,9 +70,11 @@ module Qo
|
|
64
70
|
Proc.new { |match_target|
|
65
71
|
next true if matchers == match_target
|
66
72
|
|
67
|
-
match_target.is_a?(::Hash) ?
|
68
|
-
|
69
|
-
|
73
|
+
match_fn = match_target.is_a?(::Hash) ?
|
74
|
+
hash_against_hash_matcher(match_target) :
|
75
|
+
hash_against_object_matcher(match_target)
|
76
|
+
|
77
|
+
match_with(matchers, match_fn)
|
70
78
|
}
|
71
79
|
end
|
72
80
|
|
@@ -92,8 +100,8 @@ module Qo
|
|
92
100
|
# Any -> Int -> Bool # Match against wildcard or same position in target array
|
93
101
|
private def array_against_array_matcher(match_target)
|
94
102
|
Proc.new { |matcher, i|
|
95
|
-
wildcard_match(matcher) ||
|
96
|
-
case_match(match_target[i], matcher) ||
|
103
|
+
wildcard_match?(matcher) ||
|
104
|
+
case_match?(match_target[i], matcher) ||
|
97
105
|
method_matches?(match_target[i], matcher)
|
98
106
|
}
|
99
107
|
end
|
@@ -106,8 +114,8 @@ module Qo
|
|
106
114
|
# String | Symbol -> Bool # Match against wildcard or boolean return of a predicate method
|
107
115
|
private def array_against_object_matcher(match_target)
|
108
116
|
Proc.new { |matcher|
|
109
|
-
wildcard_match(matcher) ||
|
110
|
-
case_match(match_target, matcher) ||
|
117
|
+
wildcard_match?(matcher) ||
|
118
|
+
case_match?(match_target, matcher) ||
|
111
119
|
method_matches?(match_target, matcher)
|
112
120
|
}
|
113
121
|
end
|
@@ -120,22 +128,12 @@ module Qo
|
|
120
128
|
# Any -> Any -> Bool # Matches against wildcard or a key and value. Coerces key to_s if no matches for JSON.
|
121
129
|
private def hash_against_hash_matcher(match_target)
|
122
130
|
Proc.new { |(match_key, match_value)|
|
123
|
-
next
|
131
|
+
next true if hash_wildcard_match?(match_target, match_key, match_value)
|
124
132
|
|
125
|
-
|
126
|
-
if match_value.is_a?(Hash) && match_target.is_a?(Hash)
|
127
|
-
next match_against_hash(match_value)[match_target[match_key]]
|
128
|
-
end
|
133
|
+
next hash_recurse(match_target[match_key], match_value) if hash_should_recurse?(match_target, match_value)
|
129
134
|
|
130
|
-
|
131
|
-
|
132
|
-
method_matches?(match_target[match_key], match_value) || (
|
133
|
-
# This is done for JSON responses, but as key can be `Any` we don't want to assume it knows how
|
134
|
-
# to coerce `to_s` either. It's more of a nicety function.
|
135
|
-
match_key.respond_to?(:to_s) &&
|
136
|
-
match_target.key?(match_key.to_s) &&
|
137
|
-
case_match(match_target[match_key.to_s], match_value)
|
138
|
-
)
|
135
|
+
hash_case_match?(match_target, match_key, match_value) ||
|
136
|
+
hash_method_case_match?(match_target, match_key, match_value)
|
139
137
|
}
|
140
138
|
end
|
141
139
|
|
@@ -147,11 +145,8 @@ module Qo
|
|
147
145
|
# Any -> Any -> Bool # Matches against wildcard or match value versus the public send return of the target
|
148
146
|
private def hash_against_object_matcher(match_target)
|
149
147
|
Proc.new { |(match_key, match_value)|
|
150
|
-
|
151
|
-
|
152
|
-
wildcard_match(match_value) ||
|
153
|
-
case_match(method_send(match_target, match_key), match_value) ||
|
154
|
-
method_matches?(method_send(match_target, match_key), match_value)
|
148
|
+
object_wildcard_match?(match_target, match_key, match_value) ||
|
149
|
+
hash_method_case_match?(match_target, match_key, match_value)
|
155
150
|
}
|
156
151
|
end
|
157
152
|
|
@@ -196,21 +191,96 @@ module Qo
|
|
196
191
|
# like IPAddr, and I kinda want to use that.
|
197
192
|
#
|
198
193
|
# @return [Boolean]
|
199
|
-
private def wildcard_match(value)
|
194
|
+
private def wildcard_match?(value)
|
200
195
|
value == WILDCARD_MATCH rescue false
|
201
196
|
end
|
202
197
|
|
198
|
+
# Wraps strict checks on keys with a wildcard match
|
199
|
+
#
|
200
|
+
# @param match_target [Hash]
|
201
|
+
# @param match_key [Symbol]
|
202
|
+
# @param match_value [Any]
|
203
|
+
#
|
204
|
+
# @return [Boolean]
|
205
|
+
private def hash_wildcard_match?(match_target, match_key, match_value)
|
206
|
+
return false unless match_target.key?(match_key)
|
207
|
+
|
208
|
+
wildcard_match?(match_value)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Wraps strict checks for methods existing on objects with a wildcard match
|
212
|
+
#
|
213
|
+
# @param match_target [Hash]
|
214
|
+
# @param match_key [Symbol]
|
215
|
+
# @param match_value [Any]
|
216
|
+
#
|
217
|
+
# @return [Boolean]
|
218
|
+
private def object_wildcard_match?(match_target, match_key, match_value)
|
219
|
+
return false unless match_target.respond_to?(match_key)
|
220
|
+
|
221
|
+
wildcard_match?(match_value)
|
222
|
+
end
|
223
|
+
|
203
224
|
# Wraps a case equality statement to make it a bit easier to read. The
|
204
225
|
# typical left bias of `===` can be confusing reading down a page, so
|
205
226
|
# more of a clarity thing than anything. Also makes for nicer stack traces.
|
206
227
|
#
|
207
|
-
# @param
|
208
|
-
# @param
|
228
|
+
# @param match_target [Any] Target to match against
|
229
|
+
# @param match_value [respond_to?(:===)]
|
209
230
|
# Anything that responds to ===, preferably in a unique and entertaining way.
|
210
231
|
#
|
211
232
|
# @return [Boolean]
|
212
|
-
private def case_match(
|
213
|
-
|
233
|
+
private def case_match?(match_target, match_value)
|
234
|
+
match_value === match_target
|
235
|
+
end
|
236
|
+
|
237
|
+
# Double wraps case match in order to ensure that we try against both Symbol
|
238
|
+
# and String variants of the keys, as this is a very common mixup in Ruby.
|
239
|
+
#
|
240
|
+
# @param match_target [Hash] Target of the match
|
241
|
+
# @param match_key [Symbol] Key to match against
|
242
|
+
# @param match_value [respond_to?(:===)] Matcher
|
243
|
+
#
|
244
|
+
# @return [Boolean]
|
245
|
+
private def hash_case_match?(match_target, match_key, match_value)
|
246
|
+
return true if case_match?(match_target[match_key], match_value)
|
247
|
+
|
248
|
+
match_key.respond_to?(:to_s) &&
|
249
|
+
match_target.key?(match_key.to_s) &&
|
250
|
+
case_match?(match_target[match_key.to_s], match_value)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Attempts to run a case match against a method call derived from a hash
|
254
|
+
# key, and checks the result.
|
255
|
+
#
|
256
|
+
# @param match_target [Hash] Target of the match
|
257
|
+
# @param match_key [Symbol] Method to call
|
258
|
+
# @param match_value [respond_to?(:===)] Matcher
|
259
|
+
#
|
260
|
+
# @return [Boolean]
|
261
|
+
private def hash_method_case_match?(match_target, match_key, match_value)
|
262
|
+
case_match?(method_send(match_target, match_key), match_value)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Defines preconditions for Hash recursion in matching. Currently it's
|
266
|
+
# only Hash and Hash, but may expand later to Arrays and other Enums.
|
267
|
+
#
|
268
|
+
# @param match_target [Any]
|
269
|
+
# @param match_value [Any]
|
270
|
+
#
|
271
|
+
# @return [Boolean]
|
272
|
+
private def hash_should_recurse?(match_target, match_value)
|
273
|
+
match_target.is_a?(Hash) && match_value.is_a?(Hash)
|
274
|
+
end
|
275
|
+
|
276
|
+
# Recurses on nested hashes.
|
277
|
+
#
|
278
|
+
# @param match_target [Hash]
|
279
|
+
# @param match_value [Hash]
|
280
|
+
#
|
281
|
+
# @return [Boolean]
|
282
|
+
private def hash_recurse(match_target, match_value)
|
283
|
+
match_against_hash(match_value).call(match_target)
|
214
284
|
end
|
215
285
|
end
|
216
286
|
end
|
data/lib/qo/version.rb
CHANGED
data/performance_report.txt
CHANGED
@@ -3,58 +3,58 @@ Array * Array - Literal
|
|
3
3
|
=======================
|
4
4
|
|
5
5
|
Warming up --------------------------------------
|
6
|
-
Vanilla
|
7
|
-
Qo.and
|
6
|
+
Vanilla 290.029k i/100ms
|
7
|
+
Qo.and 37.778k i/100ms
|
8
8
|
Calculating -------------------------------------
|
9
|
-
Vanilla
|
10
|
-
Qo.and
|
9
|
+
Vanilla 9.559M (± 2.3%) i/s - 47.855M in 5.009272s
|
10
|
+
Qo.and 468.514k (± 2.5%) i/s - 2.342M in 5.002419s
|
11
11
|
|
12
12
|
Comparison:
|
13
|
-
Vanilla:
|
14
|
-
Qo.and:
|
13
|
+
Vanilla: 9558516.6 i/s
|
14
|
+
Qo.and: 468514.2 i/s - 20.40x slower
|
15
15
|
|
16
16
|
|
17
17
|
Array * Array - Index pattern match
|
18
18
|
===================================
|
19
19
|
|
20
20
|
Warming up --------------------------------------
|
21
|
-
Vanilla
|
22
|
-
Qo.and
|
21
|
+
Vanilla 47.088k i/100ms
|
22
|
+
Qo.and 14.227k i/100ms
|
23
23
|
Calculating -------------------------------------
|
24
|
-
Vanilla
|
25
|
-
Qo.and
|
24
|
+
Vanilla 540.415k (± 3.3%) i/s - 2.731M in 5.059509s
|
25
|
+
Qo.and 149.040k (± 4.2%) i/s - 754.031k in 5.068772s
|
26
26
|
|
27
27
|
Comparison:
|
28
|
-
Vanilla:
|
29
|
-
Qo.and:
|
28
|
+
Vanilla: 540414.9 i/s
|
29
|
+
Qo.and: 149040.0 i/s - 3.63x slower
|
30
30
|
|
31
31
|
|
32
32
|
Array * Object - Predicate match
|
33
33
|
================================
|
34
34
|
|
35
35
|
Warming up --------------------------------------
|
36
|
-
Vanilla
|
37
|
-
Qo.and
|
36
|
+
Vanilla 139.244k i/100ms
|
37
|
+
Qo.and 20.096k i/100ms
|
38
38
|
Calculating -------------------------------------
|
39
|
-
Vanilla 2.
|
40
|
-
Qo.and
|
39
|
+
Vanilla 2.356M (± 3.4%) i/s - 11.836M in 5.030228s
|
40
|
+
Qo.and 218.039k (± 2.9%) i/s - 1.105M in 5.073725s
|
41
41
|
|
42
42
|
Comparison:
|
43
|
-
Vanilla:
|
44
|
-
Qo.and:
|
43
|
+
Vanilla: 2355717.5 i/s
|
44
|
+
Qo.and: 218038.9 i/s - 10.80x slower
|
45
45
|
|
46
46
|
|
47
47
|
Array * Array - Select index pattern match
|
48
48
|
==========================================
|
49
49
|
|
50
50
|
Warming up --------------------------------------
|
51
|
-
Vanilla
|
52
|
-
Qo.and 20.
|
51
|
+
Vanilla 14.015k i/100ms
|
52
|
+
Qo.and 20.325k i/100ms
|
53
53
|
Calculating -------------------------------------
|
54
|
-
Vanilla 140.
|
55
|
-
Qo.and
|
54
|
+
Vanilla 140.673k (± 3.6%) i/s - 714.765k in 5.087715s
|
55
|
+
Qo.and 219.533k (± 3.8%) i/s - 1.098M in 5.006844s
|
56
56
|
|
57
57
|
Comparison:
|
58
|
-
Qo.and:
|
59
|
-
Vanilla:
|
58
|
+
Qo.and: 219533.2 i/s
|
59
|
+
Vanilla: 140672.7 i/s - 1.56x slower
|
60
60
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Weaver
|
@@ -142,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
142
|
version: '0'
|
143
143
|
requirements: []
|
144
144
|
rubyforge_project:
|
145
|
-
rubygems_version: 2.
|
145
|
+
rubygems_version: 2.6.14.1
|
146
146
|
signing_key:
|
147
147
|
specification_version: 4
|
148
148
|
summary: Qo is a querying library for Ruby pattern matching
|