qo 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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