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