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 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