qo 0.1.2 → 0.1.3

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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -1
  3. data/lib/qo/matcher.rb +77 -22
  4. data/lib/qo/version.rb +1 -1
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4df3208a180d13d368677f8fe821106fe073a992
4
- data.tar.gz: 5cb7a3d0157be3e78ef5b3eec297886fbf914188
3
+ metadata.gz: a106b00677e65b0d1bc7f87678d234aaba4a7580
4
+ data.tar.gz: 56745cdd7c92b3504434209bf269851e95bf6a6a
5
5
  SHA512:
6
- metadata.gz: 8ab4868303500073f978e621d268f74537eb4d8e48139bd8ab9aab82effe3fe7c835c431058bf214ce5ddd4878337a4c937303e66ad13f6433fa72640039fb05
7
- data.tar.gz: 28ab8ae79d276a5ce96a8c05eb4fd2edc8febe460bd7b65d0ed4acbea96b1b647ddf9b21eea5bcd41a2be1417cb9ae032b6a4f54d5b6e4edd77876cf63059a71
6
+ metadata.gz: c74eee58565ad19a3cd268e3f3a704d8c861f8c978f314ab830d011a46b67bd8a4031730a6a178514b0cf749e03b10643d129bc6fac7c12644520c6d3edacfd9
7
+ data.tar.gz: 7f976433977c10a5655313e4aacf155cfb5c6c4a4517ea9abfa8f6ce1c048381656f593661047e9d30524918f616c59bcc72c84449d8c55edadd7de057cd0425
data/README.md CHANGED
@@ -61,6 +61,31 @@ else 'who knows'
61
61
  end
62
62
  ```
63
63
 
64
+ It's even nice enough to work on types, as they respond to `===` too:
65
+
66
+ ```ruby
67
+ case ['Robert', 22]
68
+ when Qo[String, Integer] then 'normal person'
69
+ when Qo[String, Float] then 'probably a kid'
70
+ else 'who knows'
71
+ end
72
+ ```
73
+
74
+ Predicate methods given as symbols will also call through:
75
+
76
+ ```ruby
77
+ case [1, 2]
78
+ when Qo[:even?, :odd?], Qo[:odd?, :even?]
79
+ 'balanced'
80
+ when Qo[:even?, :even?]
81
+ 'even better'
82
+ when Qo[:odd?, :odd?]
83
+ 'odd pair, them'
84
+ else
85
+ 'what did ya do!?'
86
+ end
87
+ ```
88
+
64
89
  Though chances are you don't have tuple-like code, you have objects. How about we play with those:
65
90
 
66
91
  ```ruby
@@ -103,7 +128,7 @@ people = [
103
128
  people.select(&Qo[name: /Rob/]) # => [Person('Robert', 22), Person('Roberta', 22)]
104
129
  ```
105
130
 
106
- Qo tries to be clever though, it assumes Symbol keys first and then String keys:
131
+ Qo tries to be clever though, it assumes Symbol keys first and then String keys, so how about some JSON?:
107
132
 
108
133
  ```ruby
109
134
  require 'json'
@@ -117,6 +142,17 @@ posts.select(&Qo[userId: 1])
117
142
 
118
143
  Nifty!
119
144
 
145
+ What about NMap for our Opsy friends? Well, simulated, but still fun.
146
+
147
+ ```ruby
148
+ hosts = (`nmap -oG - -sP 192.168.1.* 10.0.0.* | grep Host`).lines.map { |v| v.split[1..2] }
149
+ => [["192.168.1.1", "(Linksys03384)"], ["192.168.1.2", "(My Computer)"], ["10.0.0.1", "(Gateway)"]]
150
+
151
+ hosts.select(&Qo[IPAddr.new('192.168.1.1/8')])
152
+ => [["192.168.1.1", "(Linksys03384)"], ["192.168.1.2", "(My Computer)"]]
153
+ [13] pry(main)>
154
+ ```
155
+
120
156
  ## Development
121
157
 
122
158
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -44,8 +44,8 @@ module Qo
44
44
  private def match_against_array(matchers)
45
45
  -> match_target {
46
46
  match_target.is_a?(::Array) ?
47
- matchers.each_with_index.public_send(@match_method, &array_matches_array_fn(match_target)) :
48
- matchers.public_send(@match_method, &array_matches_object_fn(match_target))
47
+ match_with(matchers.each_with_index, array_against_array_matcher(match_target)) :
48
+ match_with(matchers, array_against_object_matcher(match_target))
49
49
  }
50
50
  end
51
51
 
@@ -61,8 +61,8 @@ module Qo
61
61
  private def match_against_hash(matchers)
62
62
  -> match_target {
63
63
  match_target.is_a?(::Hash) ?
64
- matchers.public_send(@match_method, &hash_matches_hash_fn(match_target)) :
65
- matchers.public_send(@match_method, &hash_matches_object_fn(match_target))
64
+ match_with(matchers, hash_against_hash_matcher(match_target)) :
65
+ match_with(matchers, hash_against_object_matcher(match_target))
66
66
  }
67
67
  end
68
68
 
@@ -86,9 +86,11 @@ module Qo
86
86
  #
87
87
  # @return [Proc]
88
88
  # Any -> Int -> Bool # Match against wildcard or same position in target array
89
- private def array_matches_array_fn(match_target)
89
+ private def array_against_array_matcher(match_target)
90
90
  -> matcher, i {
91
- matcher == WILDCARD_MATCH || matcher === match_target[i]
91
+ wildcard_match(matcher) ||
92
+ case_match(match_target[i], matcher) ||
93
+ method_matches?(match_target[i], matcher)
92
94
  }
93
95
  end
94
96
 
@@ -98,17 +100,11 @@ module Qo
98
100
  #
99
101
  # @return [Proc]
100
102
  # String | Symbol -> Bool # Match against wildcard or boolean return of a predicate method
101
- private def array_matches_object_fn(match_target)
103
+ private def array_against_object_matcher(match_target)
102
104
  -> matcher {
103
- matcher == WILDCARD_MATCH ||
104
- matcher === match_target || (
105
- # Check if the matcher is a symbol / symbolizeable and
106
- # if it's on the public interface. This is to give
107
- # the ability to avoid using :sym.to_proc for method calls.
108
- matcher.respond_to?(:to_sym) &&
109
- match_target.respond_to?(matcher.to_sym) &&
110
- match_target.public_send(matcher)
111
- )
105
+ wildcard_match(matcher) ||
106
+ case_match(match_target, matcher) ||
107
+ method_matches?(match_target, matcher)
112
108
  }
113
109
  end
114
110
 
@@ -118,14 +114,14 @@ module Qo
118
114
  #
119
115
  # @return [Proc]
120
116
  # Any -> Any -> Bool # Matches against wildcard or a key and value. Coerces key to_s if no matches for JSON.
121
- private def hash_matches_hash_fn(match_target)
117
+ private def hash_against_hash_matcher(match_target)
122
118
  -> match_key, match_value {
123
- match_value == WILDCARD_MATCH ||
124
- match_value === match_target[match_key] || (
119
+ wildcard_match(match_value) ||
120
+ case_match(match_target[match_key], match_value) || (
125
121
  # This is done for JSON responses, but as key can be `Any` we don't want to assume it knows how
126
122
  # to coerce `to_s` either. It's more of a nicety function.
127
123
  match_key.respond_to?(:to_s) &&
128
- match_value === match_target[match_key.to_s]
124
+ case_match(match_target[match_key.to_s], match_value)
129
125
  )
130
126
  }
131
127
  end
@@ -136,10 +132,69 @@ module Qo
136
132
  #
137
133
  # @return [Proc]
138
134
  # Any -> Any -> Bool # Matches against wildcard or match value versus the public send return of the target
139
- private def hash_matches_object_fn(match_target)
135
+ private def hash_against_object_matcher(match_target)
140
136
  -> match_key, match_value {
141
- match_value == WILDCARD_MATCH || match_value === match_target.public_send(match_key)
137
+ wildcard_match(match_value) ||
138
+ case_match(method_send(match_target, match_key), match_value)
142
139
  }
143
140
  end
141
+
142
+ # Wrapper around public send to encapsulate the matching method (any, all, none)
143
+ #
144
+ # @param collection [Enumerable] Any collection that can be enumerated over
145
+ # @param fn [Proc] Function to match with
146
+ #
147
+ # @return [Enumerable] Resulting collection
148
+ private def match_with(collection, fn)
149
+ collection.public_send(@match_method, &fn)
150
+ end
151
+
152
+ # Predicate variant of `method_send` with the same guard concerns
153
+ #
154
+ # @param target [Any] Object to send to
155
+ # @param matcher [respond_to?(:to_sym)] Anything that can be coerced into a method name
156
+ #
157
+ # @return [Boolean] Success status of predicate
158
+ private def method_matches?(target, matcher)
159
+ !!method_send(target, matcher)
160
+ end
161
+
162
+ # Guarded version of `public_send` meant to stamp out more
163
+ # obscure errors when running against non-matching types.
164
+ #
165
+ # @param target [Any] Object to send to
166
+ # @param matcher [respond_to?(:to_sym)] Anything that can be coerced into a method name
167
+ #
168
+ # @return [Any] Response of sending to the method, or false if failed
169
+ private def method_send(target, matcher)
170
+ matcher.respond_to?(:to_sym) &&
171
+ target.respond_to?(matcher.to_sym) &&
172
+ target.public_send(matcher)
173
+ end
174
+
175
+ # Wraps wildcard in case we want to do anything fun with it later
176
+ #
177
+ # @param value [Any] Value to test against the wild card
178
+ #
179
+ # @note The rescue is because some classes override `==` to do silly things,
180
+ # like IPAddr, and I kinda want to use that.
181
+ #
182
+ # @return [Boolean]
183
+ private def wildcard_match(value)
184
+ value == WILDCARD_MATCH rescue false
185
+ end
186
+
187
+ # Wraps a case equality statement to make it a bit easier to read. The
188
+ # typical left bias of `===` can be confusing reading down a page, so
189
+ # more of a clarity thing than anything.
190
+ #
191
+ # @param target [Any] Target to match against
192
+ # @param value [respond_to?(:===)]
193
+ # Anything that responds to ===, preferably in a unique and entertaining way.
194
+ #
195
+ # @return [Boolean]
196
+ private def case_match(target, value)
197
+ value === target
198
+ end
144
199
  end
145
200
  end
@@ -1,3 +1,3 @@
1
1
  module Qo
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end
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.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Weaver
@@ -122,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
122
  version: '0'
123
123
  requirements: []
124
124
  rubyforge_project:
125
- rubygems_version: 2.6.12
125
+ rubygems_version: 2.5.2
126
126
  signing_key:
127
127
  specification_version: 4
128
128
  summary: Qo is a querying library for Ruby pattern matching