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