qo 0.1.9 → 0.1.10

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
- SHA256:
3
- metadata.gz: 19be813c32add1fbe376c08a27533e43ca87254aca1b5bd73f146df70408d833
4
- data.tar.gz: 7876f56688b5a11d0821f368bfbd612c56ecb5e08fdf2dccca6e030d56449a2c
2
+ SHA1:
3
+ metadata.gz: 1af9159c107b7761e1b30c7062b108e692403067
4
+ data.tar.gz: 483b4e35c8c9bba6a7e84c7516ea3e8a1de6e5b2
5
5
  SHA512:
6
- metadata.gz: a13e4be4641782a63e1208053c7e35ee77c665ee5eff80b78af54a43f7e4254bd808a4dc6420c9c7e8bdb8c1070c36134f7192934c8c43c7169be0db81c051c8
7
- data.tar.gz: 2098317ea5c317a86107902504787a4d47aec8e69e204490bbf72438d2f210a9bd86377b0895672e33bdf4ab8700fc5f26fade3e871d31bd329e0e86050f6711
6
+ metadata.gz: 3db647525ff5685025108dacbe62b7945f135399d56c8a68eb9f39eb63dda3704614da143608a97d66251754c7d57c97d69d5e1a0d73f0c56e5d256cf813acdf
7
+ data.tar.gz: 72582ba2f1d0f81d149e2f0b9e3d35d10dcb929418c2cde4f88d3773a734f93c7e78594dc59689058f8de0d2cf3edfbc9dc7bb448ec7cfe9bda237fe101a07d0
data/.travis.yml CHANGED
@@ -1,5 +1,7 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.3.3
4
+ - 2.3.7
5
+ - 2.4.4
6
+ - 2.5.1
5
7
  before_install: gem install bundler -v 1.15.4
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)
@@ -9,8 +9,8 @@ module Qo
9
9
  end
10
10
 
11
11
  def to_proc
12
- -> match_target {
13
- return [false, false] unless super[match_target]
12
+ Proc.new { |match_target|
13
+ next [false, false] unless super[match_target]
14
14
 
15
15
  [true, @fn.call(match_target)]
16
16
  }
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
- match_with(matchers.each_with_index, array_against_array_matcher(match_target)) :
50
- match_with(matchers, array_against_object_matcher(match_target))
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
- match_with(matchers, hash_against_hash_matcher(match_target)) :
69
- match_with(matchers, hash_against_object_matcher(match_target))
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 false unless match_target.key?(match_key)
131
+ next true if hash_wildcard_match?(match_target, match_key, match_value)
124
132
 
125
- # If both the match value and target are hashes, descend if the key exists
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
- wildcard_match(match_value) ||
131
- case_match(match_target[match_key], match_value) ||
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
- next false unless match_target.respond_to?(match_key)
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 target [Any] Target to match against
208
- # @param value [respond_to?(:===)]
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(target, value)
213
- value === target
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
@@ -1,3 +1,3 @@
1
1
  module Qo
2
- VERSION = '0.1.9'
2
+ VERSION = '0.1.10'
3
3
  end
@@ -3,58 +3,58 @@ Array * Array - Literal
3
3
  =======================
4
4
 
5
5
  Warming up --------------------------------------
6
- Vanilla 255.131k i/100ms
7
- Qo.and 36.805k i/100ms
6
+ Vanilla 290.029k i/100ms
7
+ Qo.and 37.778k i/100ms
8
8
  Calculating -------------------------------------
9
- Vanilla 8.462M (± 2.6%) i/s - 42.352M in 5.008310s
10
- Qo.and 454.565k1.8%) i/s - 2.282M in 5.021661s
9
+ Vanilla 9.559M (± 2.3%) i/s - 47.855M in 5.009272s
10
+ Qo.and 468.514k2.5%) i/s - 2.342M in 5.002419s
11
11
 
12
12
  Comparison:
13
- Vanilla: 8462330.9 i/s
14
- Qo.and: 454565.2 i/s - 18.62x slower
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 46.834k i/100ms
22
- Qo.and 13.827k i/100ms
21
+ Vanilla 47.088k i/100ms
22
+ Qo.and 14.227k i/100ms
23
23
  Calculating -------------------------------------
24
- Vanilla 551.426k1.6%) i/s - 2.763M in 5.012355s
25
- Qo.and 146.762k (± 2.3%) i/s - 746.658k in 5.090187s
24
+ Vanilla 540.415k3.3%) i/s - 2.731M in 5.059509s
25
+ Qo.and 149.040k4.2%) i/s - 754.031k in 5.068772s
26
26
 
27
27
  Comparison:
28
- Vanilla: 551426.2 i/s
29
- Qo.and: 146762.1 i/s - 3.76x slower
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 133.668k i/100ms
37
- Qo.and 18.746k i/100ms
36
+ Vanilla 139.244k i/100ms
37
+ Qo.and 20.096k i/100ms
38
38
  Calculating -------------------------------------
39
- Vanilla 2.198M (± 3.1%) i/s - 11.094M in 5.053049s
40
- Qo.and 208.117k5.4%) i/s - 1.050M in 5.059694s
39
+ Vanilla 2.356M (± 3.4%) i/s - 11.836M in 5.030228s
40
+ Qo.and 218.039k2.9%) i/s - 1.105M in 5.073725s
41
41
 
42
42
  Comparison:
43
- Vanilla: 2197719.6 i/s
44
- Qo.and: 208116.8 i/s - 10.56x slower
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 13.222k i/100ms
52
- Qo.and 20.029k i/100ms
51
+ Vanilla 14.015k i/100ms
52
+ Qo.and 20.325k i/100ms
53
53
  Calculating -------------------------------------
54
- Vanilla 140.225k (± 3.0%) i/s - 700.766k in 5.002204s
55
- Qo.and 217.401k (± 8.2%) i/s - 1.082M in 5.036013s
54
+ Vanilla 140.673k (± 3.6%) i/s - 714.765k in 5.087715s
55
+ Qo.and 219.533k3.8%) i/s - 1.098M in 5.006844s
56
56
 
57
57
  Comparison:
58
- Qo.and: 217401.0 i/s
59
- Vanilla: 140224.8 i/s - 1.55x slower
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.9
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.7.6
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