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.
- checksums.yaml +4 -4
- data/README.md +37 -1
- data/lib/qo/matcher.rb +77 -22
- data/lib/qo/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a106b00677e65b0d1bc7f87678d234aaba4a7580
|
4
|
+
data.tar.gz: 56745cdd7c92b3504434209bf269851e95bf6a6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
data/lib/qo/matcher.rb
CHANGED
@@ -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
|
48
|
-
matchers
|
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
|
65
|
-
matchers
|
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
|
89
|
+
private def array_against_array_matcher(match_target)
|
90
90
|
-> matcher, i {
|
91
|
-
matcher
|
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
|
103
|
+
private def array_against_object_matcher(match_target)
|
102
104
|
-> matcher {
|
103
|
-
matcher
|
104
|
-
matcher
|
105
|
-
|
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
|
117
|
+
private def hash_against_hash_matcher(match_target)
|
122
118
|
-> match_key, match_value {
|
123
|
-
match_value
|
124
|
-
|
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
|
-
|
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
|
135
|
+
private def hash_against_object_matcher(match_target)
|
140
136
|
-> match_key, match_value {
|
141
|
-
match_value
|
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
|
data/lib/qo/version.rb
CHANGED
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.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.
|
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
|