rpath 1.0.0
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 +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +263 -0
- data/lib/rpath.rb +101 -0
- data/lib/rpath/adapter.rb +64 -0
- data/lib/rpath/adapters.rb +7 -0
- data/lib/rpath/adapters/filesystem.rb +108 -0
- data/lib/rpath/adapters/nokogiri.rb +60 -0
- data/lib/rpath/adapters/rexml.rb +60 -0
- data/lib/rpath/expressions.rb +332 -0
- data/lib/rpath/registry.rb +52 -0
- data/lib/rpath/util.rb +21 -0
- data/lib/rpath/version.rb +3 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NDRkNDdiZDc2YTVmOTBlNmY4MzMzN2VmNDU2NThlOTk1OTY4YzBjZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MmE5NzkxYzg2MzdhNjJiYzNjN2U5NDhiMTAwNDBlYjBmYjg1YjZlNw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ODIxNmI2NWM4MTUyYzc0MDA1ZTlhNjlmNzc5YWY1YjkyNWE1Nzk1OTc0Y2Ex
|
10
|
+
NWY0ZDQ3ODI4ODQ4ODVkZmU4MzFmOTQ2YTNjMzMwNWNiNjMyYjk0OTRmNmE1
|
11
|
+
YTM0ZmQ1NGQ4ODJiZWE4MWI0NjIwNWE0ZGRhNTZiMjFmMmZkNGE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZTk2ODMyMTc4NTI1OWU1MWI0NjY5YmZkZDI4NDljNTIxNWJiN2Y4OGY0MTZj
|
14
|
+
MTI1MDA2MzhjNjU4MjdmMjJiYjMzYjc3MWFmMWY5NWUzYTdmZWI2YWIzZTVl
|
15
|
+
MDQ5NmVjZWM4YjI5OTNlYzNiMWI5MmEyZGUzMzRhMGQ3MTA0NWI=
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Jonah Burke
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
# RPath
|
2
|
+
|
3
|
+
"Don't do this." —[flavorjones](https://github.com/flavorjones) [[1]](http://www.nokogiri.org/tutorials/searching_a_xml_html_document.html)
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
RPath lets you traverse graphs, such as XML documents, with just Ruby.
|
8
|
+
|
9
|
+
RPath can operate on [Nokogiri](http://www.nokogiri.org) documents, [REXML](http://www.germane-software.com/software/rexml/) documents, and the filesystem. Building adapters for other graphs is simple.
|
10
|
+
|
11
|
+
Leading members of the Ruby community have [warned against](http://www.nokogiri.org/tutorials/searching_a_xml_html_document.html) RPath's approach. They're probably right! RPath is as much an experiment as a useful tool.
|
12
|
+
|
13
|
+
## Documentation
|
14
|
+
|
15
|
+
This README provides an overview of RPath. Full documentation is available at [rubydoc.info](http://www.rubydoc.info/gems/rpath).
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
```bash
|
20
|
+
gem install rpath
|
21
|
+
```
|
22
|
+
|
23
|
+
## Example
|
24
|
+
|
25
|
+
Suppose we want the value of the `name` attribute in the following XML document:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
xml = Nokogiri::XML <<end
|
29
|
+
<places>
|
30
|
+
<place name="Green-Wood"/>
|
31
|
+
</places>
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
First we tell RPath we'll be using Nokgiri:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
RPath.use :nokogiri
|
39
|
+
```
|
40
|
+
|
41
|
+
Then we create an RPath expression ...
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
exp = RPath { places.place[:name] }
|
45
|
+
```
|
46
|
+
|
47
|
+
... and evaluate it on the document:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
exp.eval(xml) # => "Green-Wood"
|
51
|
+
```
|
52
|
+
|
53
|
+
Or, if we only plan to use the expression once, we can combine the two lines above, passing the graph to `RPath`. RPath evaluates the expression and returns the result:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
RPath(xml) { places.place[:name] } # => "Green-Wood"
|
57
|
+
```
|
58
|
+
|
59
|
+
For even more succinct syntax, RPath adds to Nokogiri the `#rpath` method:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
xml.rpath { places.place[:name] } # => "Green-Wood"
|
63
|
+
```
|
64
|
+
|
65
|
+
## The Graph Model
|
66
|
+
|
67
|
+
In an RPath [graph](http://en.wikipedia.org/wiki/Graph_(mathematics)),
|
68
|
+
|
69
|
+
* There is an initial vertex (a "root"),
|
70
|
+
* Each vertex has a name,
|
71
|
+
* Each vertex has zero or more adjacent vertices,
|
72
|
+
* Each vertex has zero or more named attributes, and
|
73
|
+
* Each vertex may have associated data called "content."
|
74
|
+
|
75
|
+
RPath expressions assume only this abstract model. They can be applied to any graph for which there is an adapter.
|
76
|
+
|
77
|
+
## Expressions
|
78
|
+
|
79
|
+
An RPath expression, given a graph, produces a value—a vertex, a vertex array, the value of an attribute, or a vertex's content. RPath expressions are constructed by chaining methods inside the block passed to `RPath`.
|
80
|
+
|
81
|
+
### Selecting Vertices
|
82
|
+
|
83
|
+
All vertices named "foo" adjacent to the root:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
RPath { foo }
|
87
|
+
```
|
88
|
+
|
89
|
+
The first "foo" adjacent to the root:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
RPath { foo[0] }
|
93
|
+
```
|
94
|
+
|
95
|
+
All vertices named "bar" adjacent to the first "foo":
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
RPath { foo[0].bar }
|
99
|
+
```
|
100
|
+
|
101
|
+
Or, more succinctly (the first "foo" is assumed if the indexer is omitted):
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
RPath { foo.bar }
|
105
|
+
```
|
106
|
+
|
107
|
+
_All_ vertices adjacent to the first "foo":
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
RPath { foo.adjacent }
|
111
|
+
```
|
112
|
+
|
113
|
+
All vertices adjacent to the first "foo" named "adjacent" (`#named` lets us avoid collisions with built-in methods):
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
RPath { foo.adjacent.named("adjacent") }
|
117
|
+
```
|
118
|
+
|
119
|
+
All "foos" with attribute "baz" equal to "qux":
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
RPath { foo.where(baz: 'qux') }
|
123
|
+
```
|
124
|
+
|
125
|
+
Or simply:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
RPath { foo[baz: 'qux'] }
|
129
|
+
```
|
130
|
+
|
131
|
+
And finally, all "foos" meeting arbitrary criteria:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
RPath { foo.where { |vertex| some_predicate?(vertex) } }
|
135
|
+
```
|
136
|
+
|
137
|
+
### Selecting Attributes
|
138
|
+
|
139
|
+
Attribute values are selected by passing a string to `#[]`:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# The "baz" attribute of the first vertex named "foo" adjacent to the root
|
143
|
+
RPath { foo['baz'] }
|
144
|
+
```
|
145
|
+
|
146
|
+
### Selecting Content
|
147
|
+
|
148
|
+
A vertex's content is selected with `#content`:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
# The content of the first vertex named "foo" adjacent to the root
|
152
|
+
RPath { foo.content }
|
153
|
+
```
|
154
|
+
|
155
|
+
## Adapters
|
156
|
+
|
157
|
+
### Nokogiri
|
158
|
+
|
159
|
+
The Nokogiri adapter exposes XML elements as vertices and child elements as adjacent vertices:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
RPath.use :nokogiri
|
163
|
+
|
164
|
+
xml = Nokogiri::XML <<end
|
165
|
+
<foo>
|
166
|
+
<bar baz="qux">Hello, RPath</bar>
|
167
|
+
</foo>
|
168
|
+
end
|
169
|
+
|
170
|
+
RPath(xml) { foo.bar[0] } # => #<Nokogiri::XML::Element ... >
|
171
|
+
```
|
172
|
+
|
173
|
+
XML attributes become RPath attributes:
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
RPath(xml) { foo.bar['baz'] } # => "qux"
|
177
|
+
```
|
178
|
+
|
179
|
+
And text content is accessible with `#content`:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
RPath(xml) { foo.bar.content } # => "Hello, RPath"
|
183
|
+
```
|
184
|
+
|
185
|
+
An expression may be evaluated not just on an XML document but any `Nokogiri::XML::Node`. Non-element nodes such as processing instructions, alas, are not accessible.
|
186
|
+
|
187
|
+
Finally, the convenience method `#rpath`, added to `Nokogiri::XML::Node`, allows for more compact syntax:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
xml.rpath { foo.bar.content } # => "Hello, RPath"
|
191
|
+
```
|
192
|
+
|
193
|
+
### REXML
|
194
|
+
|
195
|
+
The REXML adapter is similar to the Nokogiri one. Expressions may be evaluated on any `REXML::Element`.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
RPath.use :rexml
|
199
|
+
xml = REXML::Document.new('<foo bar="baz"/>')
|
200
|
+
xml.rpath { foo['bar'] } # => "baz"
|
201
|
+
```
|
202
|
+
|
203
|
+
### Filesystem
|
204
|
+
|
205
|
+
The filesystem adapter exposes files and directories as vertices. Directory entries are adjacent to their directory. Expressions may be evaluated on any directory:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
RPath.use :filesystem
|
209
|
+
|
210
|
+
# Note that we must specify the adapter because RPath can't infer it from '~'
|
211
|
+
RPath('~', :filesystem) { where { |f| f =~ /bash/ } } # => ["~/.bash_history", "~/.bash_profile"]
|
212
|
+
```
|
213
|
+
|
214
|
+
Many file properties become RPath attributes:
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
RPath('/', :filesystem) { etc.hostname[:mtime] } # => 2014-12-17 14:43:24 -0500
|
218
|
+
```
|
219
|
+
|
220
|
+
And file contents are accessible with `#content`:
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
RPath('/', :filesystem) { etc.hostname.content } # => "jbook"
|
224
|
+
```
|
225
|
+
|
226
|
+
## Custom Adapters
|
227
|
+
|
228
|
+
Custom adapters are subclasses of `RPath::Adapter`. They implement three abstract methods: `#adjacent`, `#attribute`, and `#content`. See the implementations in `RPath::Adapters` for examples.
|
229
|
+
|
230
|
+
Register a custom adapter by passing an instance to `RPath.use`:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
RPath.use MapsAdapter.new
|
234
|
+
```
|
235
|
+
|
236
|
+
To use the adapter, pass the underscored, symbolized class name as the second argument to `RPath` or `RPath::Expression#eval`:
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
RPath(map, :maps_adapter) { ... }
|
240
|
+
```
|
241
|
+
|
242
|
+
You can eliminate the need to specify the adapter by implementing `RPath::Adapter#adapts?`:
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
class MapsAdapter < RPath::Adapter
|
246
|
+
def adapts?(graph)
|
247
|
+
graph.is_a? Map
|
248
|
+
end
|
249
|
+
...
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
Now RPath will select `MapsAdapter` when an expression is evaluated on a `Map`:
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
RPath.use MapsAdapter.new
|
257
|
+
RPath(Map.new) { ... }
|
258
|
+
```
|
259
|
+
|
260
|
+
## Contributing
|
261
|
+
|
262
|
+
Please submit issues and pull requests to [jonahb/rpath](http://github.com/jonahb/rpath) on GitHub.
|
263
|
+
|
data/lib/rpath.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
%w{
|
2
|
+
adapter
|
3
|
+
adapters
|
4
|
+
expressions
|
5
|
+
registry
|
6
|
+
util
|
7
|
+
version
|
8
|
+
}.each do |file|
|
9
|
+
require "rpath/#{file}"
|
10
|
+
end
|
11
|
+
|
12
|
+
module RPath
|
13
|
+
class << self
|
14
|
+
# Registers an adapter. Once an adapter is registered, RPath calls its
|
15
|
+
# {Adapter#adapts?} when trying to infer the adapter for an evaluation,
|
16
|
+
# and its id may be given to {#RPath}.
|
17
|
+
# @example Built-in adapter
|
18
|
+
# RPath.use :nokogiri
|
19
|
+
# @example Custom adapter
|
20
|
+
# RPath.use CustomAdapter.new
|
21
|
+
# RPath(graph, :custom_adapter) { foo.bar }
|
22
|
+
# @example Custom adapter with custom ID
|
23
|
+
# RPath.use CustomAdapter.new, :custom
|
24
|
+
# RPath(graph, :custom) { foo.bar }
|
25
|
+
# @param [Symbol, Adapter] adapter
|
26
|
+
# For built-in adapters, the underscored, symbolized class name (e.g.
|
27
|
+
# +:nokogiri+). For custom adapters, an instance of the adapter class.
|
28
|
+
# @param [Symbol, nil] id
|
29
|
+
# The identifier to be used in calls to {#RPath}. If +nil+, the
|
30
|
+
# underscored, symbolized name of the adapter class is assumed.
|
31
|
+
# @return [void]
|
32
|
+
#
|
33
|
+
def use(adapter, id = nil)
|
34
|
+
if adapter.is_a?(Symbol)
|
35
|
+
class_names = [Util.camelcase(adapter.to_s), adapter.to_s.upcase]
|
36
|
+
class_ = Util.first_defined_const(RPath::Adapters, *class_names)
|
37
|
+
|
38
|
+
unless class_
|
39
|
+
raise "No adapter in RPath::Adapters with class name in #{class_names}"
|
40
|
+
end
|
41
|
+
|
42
|
+
adapter = class_.new
|
43
|
+
end
|
44
|
+
|
45
|
+
Registry.register adapter, id
|
46
|
+
end
|
47
|
+
alias_method :register, :use
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Constructs an RPath expression and optionally evaluates it on a graph.
|
52
|
+
#
|
53
|
+
# @overload RPath
|
54
|
+
# Constructs an RPath expression
|
55
|
+
# @example Construct an expression
|
56
|
+
# exp = RPath { foo.bar }
|
57
|
+
# @example Construct an expression beginning with an uppercase letter
|
58
|
+
# exp = RPath { |root| root.Users.alice }
|
59
|
+
# @yieldparam [RPath::Root] root
|
60
|
+
# The {RPath::Root} of the RPath expression. You should almost
|
61
|
+
# always omit this yield paramter. Use it only to avoid an exception if the
|
62
|
+
# first letter of your expression is uppercase. See the example above.
|
63
|
+
# @return [RPath::Expression]
|
64
|
+
# @see file:README.md
|
65
|
+
#
|
66
|
+
# @overload RPath(graph, adapter = nil)
|
67
|
+
# Constructs an RPath expression, evaluates it, and returns the result
|
68
|
+
# @example Construct and expression and evaluate it on an XML document
|
69
|
+
# RPath.use :nokogiri
|
70
|
+
# xml = Nokogiri::XML('<foo bar="baz"/>')
|
71
|
+
# RPath(xml) { foo['bar'] } # => "baz"
|
72
|
+
# @example Construct an expression and evaluate it with a custom adapter
|
73
|
+
# RPath(graph, CustomAdapter.new) { foo.bar }
|
74
|
+
# @example Construct an expression and evaluate it with a custom adapter that has been registered
|
75
|
+
# RPath(graph, :custom) { foo.bar }
|
76
|
+
# @example Construct an expression and evaluate it, letting RPath infer the adapter
|
77
|
+
# RPath(graph) { foo.bar }
|
78
|
+
# @param [Object] graph
|
79
|
+
# The graph on which to evaluate the expression.
|
80
|
+
# @param [RPath::Adapter, Symbol, nil] adapter
|
81
|
+
# The adapter with which to evaluate the expression. If the adapter has been
|
82
|
+
# registered with {RPath.use}, its id (a symbol) may be given as a
|
83
|
+
# shortcut. If +nil+, RPath attempts to infer the adapter by calling
|
84
|
+
# {RPath::Adapter#adapts?} on registered adapters.
|
85
|
+
# @yieldparam [RPath::Root] root
|
86
|
+
# The {RPath::Root} of the RPath expression. You should almost
|
87
|
+
# always omit this yield parameter. Use it only to avoid an exception if the
|
88
|
+
# first letter of your expression is uppercase. See the example above.
|
89
|
+
# @return [Object]
|
90
|
+
# @see file:README.md
|
91
|
+
# @see RPath.use
|
92
|
+
#
|
93
|
+
def RPath(graph = nil, adapter = nil, &block)
|
94
|
+
exp = RPath::Root.new
|
95
|
+
|
96
|
+
if block_given?
|
97
|
+
exp = block.arity > 0 ? block.call(exp) : exp.instance_eval(&block)
|
98
|
+
end
|
99
|
+
|
100
|
+
graph ? exp.eval(graph, adapter) : exp
|
101
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module RPath
|
2
|
+
|
3
|
+
# An RPath adapter makes it possible to evaluate RPath expressions on some
|
4
|
+
# type of graph. There are built-in adapters for Nokogiri, REXML, and the
|
5
|
+
# filesystem. To build an adapter for another graph type, inherit from
|
6
|
+
# {Adapter} and implement the abstract methods.
|
7
|
+
# @abstract
|
8
|
+
#
|
9
|
+
class Adapter
|
10
|
+
# Used to infer the adapter when {#RPath} is called without an explicit
|
11
|
+
# adapter. The first registered adapter whose {#adapts?} returns +true+
|
12
|
+
# is chosen. The default implementation returns +false+.
|
13
|
+
# @param [Object] graph
|
14
|
+
# @return [Boolean]
|
15
|
+
# @see #RPath
|
16
|
+
# @see RPath.use
|
17
|
+
def adapts?(graph)
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the root of the given graph, the vertex where evaluation
|
22
|
+
# begins. The default implementation returns the given graph.
|
23
|
+
# the given graph.
|
24
|
+
# @param [Object] graph
|
25
|
+
# @return [Object]
|
26
|
+
def root(graph)
|
27
|
+
graph
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the name of the given vertex
|
31
|
+
# @abstract
|
32
|
+
# @param [Object] vertex
|
33
|
+
# @return [String]
|
34
|
+
def name(vertex)
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the vertices adjacent to the given vertex.
|
39
|
+
# @abstract
|
40
|
+
# @param [Object] vertex
|
41
|
+
# @return [Array]
|
42
|
+
def adjacent(vertex)
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the value of attribute +name+ of +vertex+ or +nil+ if no such
|
47
|
+
# attribute exists.
|
48
|
+
# @abstract
|
49
|
+
# @param [Object] vertex
|
50
|
+
# @param [String, Symbol] name
|
51
|
+
# @return [Object, nil]
|
52
|
+
def attribute(vertex, name)
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the content of +vertex+ or nil if no content exists.
|
57
|
+
# @abstract
|
58
|
+
# @param [Object] vertex
|
59
|
+
# @return [Object, nil]
|
60
|
+
def content(vertex)
|
61
|
+
raise NotImplementedError
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module RPath
|
4
|
+
module Adapters
|
5
|
+
|
6
|
+
class Filesystem < RPath::Adapter
|
7
|
+
|
8
|
+
# Always false. The filesystem adapter must be specified in calls to
|
9
|
+
# {#RPath}.
|
10
|
+
# @param [Object] graph
|
11
|
+
# @return [Boolean]
|
12
|
+
def adapts?(graph)
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [String] vertex
|
17
|
+
# A filesystem path
|
18
|
+
# @return [String]
|
19
|
+
# Returns the basename
|
20
|
+
def name(vertex)
|
21
|
+
File.basename vertex
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [String] vertex
|
25
|
+
# A filesystem path
|
26
|
+
# @return [Array<String>]
|
27
|
+
# Returns the expanded paths of the directory entries. An empty array
|
28
|
+
# if +vertex+ is a file.
|
29
|
+
def adjacent(vertex)
|
30
|
+
begin
|
31
|
+
entries = Dir.entries(File.expand_path(vertex))
|
32
|
+
rescue SystemCallError
|
33
|
+
return []
|
34
|
+
end
|
35
|
+
|
36
|
+
entries.collect { |entry| File.join(vertex, entry) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param [String] vertex
|
40
|
+
# A filesystem path
|
41
|
+
# @param [String, Symbol] name
|
42
|
+
# An attribute in {ATTRIBUTES}
|
43
|
+
# @return [Object, nil]
|
44
|
+
# Returns the value of the attribute; +nil+ if the attribute is
|
45
|
+
# invalid.
|
46
|
+
def attribute(vertex, name)
|
47
|
+
if ATTRIBUTES.include?(name.to_s)
|
48
|
+
begin
|
49
|
+
Pathname(File.expand_path(vertex)).send(name)
|
50
|
+
rescue SystemCallError
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param [String] vertex
|
59
|
+
# A filesystem path
|
60
|
+
# @return [String, nil]
|
61
|
+
# Returns the contents if +vertex+ is a file; otherwise +nil+.
|
62
|
+
def content(vertex)
|
63
|
+
begin
|
64
|
+
File.read File.expand_path(vertex)
|
65
|
+
rescue SystemCallError
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Attributes that may be passed as names to {#attribute}
|
71
|
+
ATTRIBUTES = %w{
|
72
|
+
blockdev?
|
73
|
+
chardev?
|
74
|
+
directory?
|
75
|
+
executable?
|
76
|
+
executable_real?
|
77
|
+
file?
|
78
|
+
grpowned?
|
79
|
+
owned?
|
80
|
+
pipe?
|
81
|
+
readable?
|
82
|
+
world_readable?
|
83
|
+
readable_real?
|
84
|
+
setgid?
|
85
|
+
setuid?
|
86
|
+
size
|
87
|
+
socket?
|
88
|
+
sticky?
|
89
|
+
symlink?
|
90
|
+
writable?
|
91
|
+
world_writable?
|
92
|
+
writable_real?
|
93
|
+
zero?
|
94
|
+
atime
|
95
|
+
birthtime
|
96
|
+
ctime
|
97
|
+
mtime
|
98
|
+
ftype
|
99
|
+
readlink
|
100
|
+
stat
|
101
|
+
lstat
|
102
|
+
dirname
|
103
|
+
extname
|
104
|
+
split }
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module RPath
|
4
|
+
module Adapters
|
5
|
+
|
6
|
+
class Nokogiri < RPath::Adapter
|
7
|
+
|
8
|
+
# Returns +true+ iff +graph+ is a +Nokogiri::XML::Node+.
|
9
|
+
# @param [Object] graph
|
10
|
+
# @return [Boolean]
|
11
|
+
def adapts?(graph)
|
12
|
+
graph.is_a? ::Nokogiri::XML::Node
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the name of the given node
|
16
|
+
# @param [Nokogiri::XML::Node] vertex
|
17
|
+
# @return [String]
|
18
|
+
def name(vertex)
|
19
|
+
vertex.name
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the child elements of the given node
|
23
|
+
# @param [Nokogiri::XML::Node] vertex
|
24
|
+
# @return [Array<Nokogiri::XML::Node>]
|
25
|
+
def adjacent(vertex)
|
26
|
+
vertex.children.to_a
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the value of the named attribute on the given node.
|
30
|
+
# @param [Nokogiri::XML::Node] vertex
|
31
|
+
# @param [String, Symbol] name
|
32
|
+
# @return [String, nil]
|
33
|
+
def attribute(vertex, name)
|
34
|
+
vertex[name.to_s]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the text content of the given node.
|
38
|
+
# @param [Nokogiri::XML::Node] vertex
|
39
|
+
# @return [String, nil]
|
40
|
+
def content(vertex)
|
41
|
+
vertex.text
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Nokogiri::XML::Node
|
49
|
+
# Evaluates an expression on the element
|
50
|
+
# @example
|
51
|
+
# RPath.use :nokogiri
|
52
|
+
# xml = Nokogiri::XML('<foo bar="baz"/>')
|
53
|
+
# xml.rpath { foo['bar'] } # => "baz"
|
54
|
+
# @see #RPath
|
55
|
+
# @return [Object]
|
56
|
+
#
|
57
|
+
def rpath(&block)
|
58
|
+
RPath self, :nokogiri, &block
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module RPath
|
4
|
+
module Adapters
|
5
|
+
|
6
|
+
class REXML < RPath::Adapter
|
7
|
+
|
8
|
+
# Returns +true+ iff +graph+ is an +REXML::Element+.
|
9
|
+
# @param [Object] graph
|
10
|
+
# @return [Boolean]
|
11
|
+
def adapts?(graph)
|
12
|
+
graph.is_a? ::REXML::Element
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the name of the given element
|
16
|
+
# @param [REXML::Element] vertex
|
17
|
+
# @return [String]
|
18
|
+
def name(vertex)
|
19
|
+
vertex.name
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the child elements of the given element
|
23
|
+
# @param [REXML::Element] vertex
|
24
|
+
# @return [Array<REXML::Element>]
|
25
|
+
def adjacent(vertex)
|
26
|
+
vertex.elements.to_a
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the value of the named attribute on the given element.
|
30
|
+
# @param [REXML::Element] vertex
|
31
|
+
# @param [String, Symbol] name
|
32
|
+
# @return [String, nil]
|
33
|
+
def attribute(vertex, name)
|
34
|
+
vertex.attributes[name.to_s]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the text content of the given element.
|
38
|
+
# @param [REXML::Element] vertex
|
39
|
+
# @return [String, nil]
|
40
|
+
def content(vertex)
|
41
|
+
vertex.text
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class REXML::Element
|
49
|
+
# Evaluates an expression on the element
|
50
|
+
# @example
|
51
|
+
# RPath.use :rexml
|
52
|
+
# xml = REXML::Document.new('<foo bar="baz"/>')
|
53
|
+
# xml.rpath { foo['bar'] } # => "baz"
|
54
|
+
# @see #RPath
|
55
|
+
# @return [Object]
|
56
|
+
#
|
57
|
+
def rpath(&block)
|
58
|
+
RPath self, :rexml, &block
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,332 @@
|
|
1
|
+
module RPath
|
2
|
+
|
3
|
+
# An RPath expression, given a graph, produces a value: a vertex, a vertex
|
4
|
+
# array, an attribute value, or a vertex's content.
|
5
|
+
# @abstract
|
6
|
+
class Expression
|
7
|
+
|
8
|
+
# Evaluates the expression on a graph
|
9
|
+
# @param [Object] graph
|
10
|
+
# @param [RPath::Adapter, Symbol, nil] adapter
|
11
|
+
# An {Adapter} instance, the id symbol given when the adapter was
|
12
|
+
# registered with {RPath.use}, or +nil+ if the adapter should be
|
13
|
+
# inferred.
|
14
|
+
# @return [Object]
|
15
|
+
# @raise [RuntimeError]
|
16
|
+
# The adapter can't be determined
|
17
|
+
# @ raise [ArgumentError]
|
18
|
+
# +adapter+ is not an {Adapter}, Symbol, or nil
|
19
|
+
# @see #RPath
|
20
|
+
# @see RPath.use
|
21
|
+
#
|
22
|
+
def eval(graph, adapter = nil)
|
23
|
+
adapter = case adapter
|
24
|
+
when RPath::Adapter
|
25
|
+
adapter
|
26
|
+
when Symbol
|
27
|
+
Registry.find adapter.to_sym
|
28
|
+
when nil
|
29
|
+
Registry.infer graph
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Adapter must be an RPath::Adapter, Symbol, or nil"
|
32
|
+
end
|
33
|
+
|
34
|
+
unless adapter
|
35
|
+
raise "Can't determine adapter"
|
36
|
+
end
|
37
|
+
|
38
|
+
do_eval graph, adapter
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def do_eval(graph, adapter)
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# An expression that evaluates to a vertex V
|
50
|
+
# @abstract
|
51
|
+
class VertexExpression < Expression
|
52
|
+
# Returns an expression that evaluates to V's adjacent vertices.
|
53
|
+
# @return [Adjacent]
|
54
|
+
def adjacent
|
55
|
+
Adjacent.new self
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns an expression that evaluates to V's content.
|
59
|
+
# @return [Content]
|
60
|
+
def content
|
61
|
+
Content.new self
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns an expression that evaluates to the value of an attribute of V
|
65
|
+
# @return [Attribute]
|
66
|
+
# @raise [ArgumentError]
|
67
|
+
# +subscript+ is not a String or Symbol
|
68
|
+
def [](subscript)
|
69
|
+
unless subscript.is_a?(String) || subscript.is_a?(Symbol)
|
70
|
+
raise ArgumentError, "Subscript for expression producing a vertex must by a String or Symbol"
|
71
|
+
end
|
72
|
+
Attribute.new self, subscript
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns an expression that evaluates to V's adjacent vertices named
|
76
|
+
# +name+. Enables the basic RPath expression +RPath { foo }+.
|
77
|
+
# @return [Named]
|
78
|
+
def method_missing(name, *args, &block)
|
79
|
+
Named.new adjacent, name.to_s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# An expression that evaluates to a vertex array A
|
85
|
+
# @abstract
|
86
|
+
class VertexArrayExpression < Expression
|
87
|
+
# Returns an expression that evaluates to the vertices in A meeting certain
|
88
|
+
# conditions.
|
89
|
+
# @return [Where]
|
90
|
+
# @see Where#initialize
|
91
|
+
def where(*args, &block)
|
92
|
+
Where.new self, *args, &block
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns an expression that evaluates to the vertices in A named +name+.
|
96
|
+
# @param [String] name
|
97
|
+
# @return [Named]
|
98
|
+
def named(name)
|
99
|
+
Named.new self, name
|
100
|
+
end
|
101
|
+
|
102
|
+
# @overload [](index)
|
103
|
+
# Returns an expression that evaluates to the vertex at index +index+ in
|
104
|
+
# A.
|
105
|
+
# @param [Integer] index
|
106
|
+
# @return [At]
|
107
|
+
# @overload [](conditions)
|
108
|
+
# Returns an expression that evaluates to the vertices in A meeting
|
109
|
+
# certain conditions.
|
110
|
+
# @param [Hash] conditions
|
111
|
+
# @return [Where]
|
112
|
+
# @see Where#initialize
|
113
|
+
# @overload [](attribute)
|
114
|
+
# Returns an expression that evaluates to the value of an attribute of
|
115
|
+
# the first vertex in A. Enables omitting the indexer in
|
116
|
+
# +RPath { foo['bar'] }+
|
117
|
+
# @param [String, Symbol] attribute
|
118
|
+
# @return [Attribute]
|
119
|
+
# @raise [ArgumentError]
|
120
|
+
# +subscript+ is not an Integer, Hash, String, or Symbol
|
121
|
+
def [](subscript)
|
122
|
+
case subscript
|
123
|
+
when Integer
|
124
|
+
At.new self, subscript
|
125
|
+
when Hash
|
126
|
+
Where.new self, subscript
|
127
|
+
when String, Symbol
|
128
|
+
self[0][subscript]
|
129
|
+
else
|
130
|
+
raise ArgumentError, "Subscript for expression producing a vertex must be an Integer, Hash, String, or Symbol"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Constructs an {At} that evaluates to the first vertex in A;
|
135
|
+
# forwards the method invocation to this {At}. Enables omitting
|
136
|
+
# the indexer in expressions like +RPath { foo.bar }+.
|
137
|
+
def method_missing(name, *args, &block)
|
138
|
+
self[0].send name, *args, &block
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
# Evaluates to the root of the graph.
|
144
|
+
class Root < VertexExpression
|
145
|
+
# @return [String]
|
146
|
+
def to_s
|
147
|
+
'root'
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def do_eval(graph, adapter)
|
153
|
+
adapter.root graph
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
# Given a prior expression producing vertex V, evaluates to an array
|
159
|
+
# containing V's adjacent vertices.
|
160
|
+
class Adjacent < VertexArrayExpression
|
161
|
+
# @param [Expression] prior
|
162
|
+
# An expression that evaluates to a vertex
|
163
|
+
def initialize(prior)
|
164
|
+
super()
|
165
|
+
@prior = prior
|
166
|
+
end
|
167
|
+
|
168
|
+
# @return [String]
|
169
|
+
def to_s
|
170
|
+
"#{@prior}."
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def do_eval(graph, adapter)
|
176
|
+
vertex = @prior.eval(graph, adapter)
|
177
|
+
vertex && adapter.adjacent(vertex)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
# Given a prior expression producing vertex array A, evaluates to an array
|
183
|
+
# containing the vertices in A with a certain name.
|
184
|
+
class Named < VertexArrayExpression
|
185
|
+
# @param [Expression] prior
|
186
|
+
# An expression that evaluates to a vertex array
|
187
|
+
# @param [String] name
|
188
|
+
def initialize(prior, name)
|
189
|
+
super()
|
190
|
+
@prior = prior
|
191
|
+
@name = name
|
192
|
+
end
|
193
|
+
|
194
|
+
# @return [String]
|
195
|
+
def to_s
|
196
|
+
"#{@prior}#{@name}"
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def do_eval(graph, adapter)
|
202
|
+
vertices = @prior.eval(graph, adapter)
|
203
|
+
vertices && vertices.select { |vertex| @name == adapter.name(vertex) }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
# Given a prior expression producing vertex array A, evaluates to an array
|
209
|
+
# containing the vertices in A that match certain conditions.
|
210
|
+
class Where < VertexArrayExpression
|
211
|
+
# @overload initialize(prior, conditions)
|
212
|
+
# @param [Expression] prior
|
213
|
+
# An expression that evaluates to a vertex array
|
214
|
+
# @param [Hash{Symbol => Object}] conditions
|
215
|
+
# A map of attribute keys to values.
|
216
|
+
# @overload initialize(prior)
|
217
|
+
# @param [Expression] prior
|
218
|
+
# An expression that evaluates to a vertex array
|
219
|
+
# @yieldparam vertex [Object]
|
220
|
+
# @yieldreturn [Boolean]
|
221
|
+
# Whether the vertex should be selected
|
222
|
+
def initialize(prior, conditions = {}, &selector)
|
223
|
+
super()
|
224
|
+
@prior = prior
|
225
|
+
@selector = block_given? ? selector : nil
|
226
|
+
@conditions = block_given? ? nil : conditions
|
227
|
+
end
|
228
|
+
|
229
|
+
# @return [String]
|
230
|
+
def to_s
|
231
|
+
conditions = @selector ?
|
232
|
+
'selector' :
|
233
|
+
@conditions.map { |k, v| "#{k}: #{v}" }.join(', ')
|
234
|
+
|
235
|
+
"#{@prior}[#{conditions}]"
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
def do_eval(graph, adapter)
|
241
|
+
vertices = @prior.eval(graph, adapter)
|
242
|
+
return nil unless vertices
|
243
|
+
|
244
|
+
if @selector
|
245
|
+
vertices.select(&@selector)
|
246
|
+
else
|
247
|
+
vertices.select do |vertex|
|
248
|
+
@conditions.all? do |name, value|
|
249
|
+
adapter.attribute(vertex, name) == value
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
# Given a prior expression producing vertex array A, evaluates to the vertex
|
258
|
+
# in A at a given index.
|
259
|
+
class At < VertexExpression
|
260
|
+
# @param [Expression] prior
|
261
|
+
# An expression that evaluates to a vertex array
|
262
|
+
# @param [Integer] index
|
263
|
+
# The index of the vertex to produce
|
264
|
+
def initialize(prior, index)
|
265
|
+
super()
|
266
|
+
@prior = prior
|
267
|
+
@index = index
|
268
|
+
end
|
269
|
+
|
270
|
+
# @return [String]
|
271
|
+
def to_s
|
272
|
+
"#{@prior}[#{@index}]"
|
273
|
+
end
|
274
|
+
|
275
|
+
private
|
276
|
+
|
277
|
+
def do_eval(graph, adapter)
|
278
|
+
vertices = @prior.eval(graph, adapter)
|
279
|
+
vertices && vertices[@index]
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
# Given a prior expression producing a vertex V, evaluates to the value of
|
285
|
+
# the attribute of V with the given name.
|
286
|
+
class Attribute < Expression
|
287
|
+
# @param [Expression] prior
|
288
|
+
# An expression that evaluates to a vertex
|
289
|
+
# @param [String] name
|
290
|
+
# The name of the attribute
|
291
|
+
def initialize(prior, name)
|
292
|
+
super()
|
293
|
+
@prior = prior
|
294
|
+
@name = name
|
295
|
+
end
|
296
|
+
|
297
|
+
# @return [String]
|
298
|
+
def to_s
|
299
|
+
"#{@prior}[#{@name}]"
|
300
|
+
end
|
301
|
+
|
302
|
+
private
|
303
|
+
|
304
|
+
def do_eval(graph, adapter)
|
305
|
+
vertex = @prior.eval(graph, adapter)
|
306
|
+
vertex && adapter.attribute(vertex, @name)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
# Given a prior expression producing vertex V, evaluates to V's content.
|
312
|
+
class Content < Expression
|
313
|
+
# @param [Expression] prior
|
314
|
+
# An expression producing a vertex
|
315
|
+
def initialize(prior)
|
316
|
+
@prior = prior
|
317
|
+
end
|
318
|
+
|
319
|
+
# @return [String]
|
320
|
+
def to_s
|
321
|
+
"#{@prior}:content"
|
322
|
+
end
|
323
|
+
|
324
|
+
private
|
325
|
+
|
326
|
+
def do_eval(graph, adapter)
|
327
|
+
vertex = @prior.eval(graph, adapter)
|
328
|
+
vertex && adapter.content(vertex)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module RPath
|
2
|
+
|
3
|
+
# @private
|
4
|
+
class Registry
|
5
|
+
class << self
|
6
|
+
# Registers an adapter. Once an adapter is registered, RPath calls its
|
7
|
+
# {#adapts?} when trying to infer the adapter for an evaluation, and its
|
8
|
+
# id, as opposed to an instance, may be given to {#RPath}.
|
9
|
+
# @param [Adapter] adapter
|
10
|
+
# @param [Symbol, nil] id
|
11
|
+
# An id that can later be passed to {#RPath}. If omitted, the
|
12
|
+
# symbolized, underscored name of the adapter class is assumed.
|
13
|
+
# @return [void]
|
14
|
+
def register(adapter, id = nil)
|
15
|
+
id ||= default_id(adapter)
|
16
|
+
id_to_adapter[id] = adapter
|
17
|
+
end
|
18
|
+
alias_method :use, :register
|
19
|
+
|
20
|
+
# Infers the adapter for a given graph. The first adapter whose
|
21
|
+
# {#adapts?} returns +true+ is chosen.
|
22
|
+
# @param [Object] graph
|
23
|
+
# @return [Adapter, nil]
|
24
|
+
def infer(graph)
|
25
|
+
id_to_adapter.each_value.find { |adapter| adapter.adapts?(graph) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Finds a registered adapter by id.
|
29
|
+
# @param [Symbol] id
|
30
|
+
# @return [Adapter, nil]
|
31
|
+
def find(id)
|
32
|
+
id_to_adapter[id]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Unregisters all adapters
|
36
|
+
def clear
|
37
|
+
id_to_adapter.clear
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def id_to_adapter
|
43
|
+
@id_to_adapter ||= {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_id(adapter)
|
47
|
+
Util.underscore(adapter.class.name.split('::').last).to_sym
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/rpath/util.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module RPath
|
2
|
+
|
3
|
+
# @private
|
4
|
+
module Util
|
5
|
+
class << self
|
6
|
+
def underscore(string)
|
7
|
+
string.gsub(/([^A-Z])([A-Z])/, '\1_\2').downcase
|
8
|
+
end
|
9
|
+
|
10
|
+
def camelcase(string)
|
11
|
+
string.gsub(/(?:^|_)([a-z])/) { $1.upcase }
|
12
|
+
end
|
13
|
+
|
14
|
+
def first_defined_const(module_, *consts)
|
15
|
+
const = consts.find { |c| module_.const_defined?(c) }
|
16
|
+
const && module_.const_get(const)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rpath
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonah Burke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.6.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.6.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.8.7
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.8.7
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- jonah@jonahb.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- LICENSE.txt
|
77
|
+
- README.md
|
78
|
+
- lib/rpath.rb
|
79
|
+
- lib/rpath/adapter.rb
|
80
|
+
- lib/rpath/adapters.rb
|
81
|
+
- lib/rpath/adapters/filesystem.rb
|
82
|
+
- lib/rpath/adapters/nokogiri.rb
|
83
|
+
- lib/rpath/adapters/rexml.rb
|
84
|
+
- lib/rpath/expressions.rb
|
85
|
+
- lib/rpath/registry.rb
|
86
|
+
- lib/rpath/util.rb
|
87
|
+
- lib/rpath/version.rb
|
88
|
+
homepage: http://github.com/jonahb/rpath
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 1.9.3
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.4.5
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: TBD
|
112
|
+
test_files: []
|
113
|
+
has_rdoc:
|