wewoo 0.1.5
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 +7 -0
- data/.gitignore +21 -0
- data/Gemfile +6 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +176 -0
- data/Rakefile +1 -0
- data/bin/wewoo +15 -0
- data/lib/wewoo.rb +19 -0
- data/lib/wewoo/adapter.rb +58 -0
- data/lib/wewoo/configuration.rb +24 -0
- data/lib/wewoo/edge.rb +54 -0
- data/lib/wewoo/element.rb +28 -0
- data/lib/wewoo/graph.rb +233 -0
- data/lib/wewoo/result_set.rb +99 -0
- data/lib/wewoo/version.rb +3 -0
- data/lib/wewoo/vertex.rb +63 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/graph_sets.rb +33 -0
- data/spec/wewoo/edge_spec.rb +43 -0
- data/spec/wewoo/graph_spec.rb +265 -0
- data/spec/wewoo/gremlin_spec.rb +434 -0
- data/spec/wewoo/result_set_spec.rb +225 -0
- data/spec/wewoo/vertex_spec.rb +75 -0
- data/wewoo.gemspec +35 -0
- metadata +237 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b9405068406bfb2b2a289835503acf38b8c41a13
|
4
|
+
data.tar.gz: caddd5fad7db02dd7711fff21615bec5ae0ceb29
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 60c3603e885d9bab087e80053136bc37896b7a018a664da10e15b8f697f366e39e555f30e2da84e83a61606596504e14b88e65733b136fb67dbdc414c2de3703
|
7
|
+
data.tar.gz: b8f56a6f2ef9892eaefe802cd3ad81cdd6dcbc9b603c3c4b4bc5479173817bc5c632706f37244e9dcdb6aa8f42505c1fdbbc67d5f13d05f7b4e0aa22a3d5ade1
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
guard 'bundler' do
|
2
|
+
watch('Gemfile')
|
3
|
+
watch(/^.+\.gemspec/)
|
4
|
+
end
|
5
|
+
|
6
|
+
guard 'rspec', cli: "--color --format Fuubar --fail-fast --drb", all_after_pass: false, all_on_start: false do
|
7
|
+
watch(%r{^spec/.+_spec\.rb$})
|
8
|
+
watch(%r{^spec/support/.+\.rb$}) { "spec"}
|
9
|
+
watch(%r{^lib/wewoo/(.+)\.rb$}) { |m| "spec/wewoo/#{m[1]}_spec.rb" }
|
10
|
+
watch('spec/spec_helper.rb') { "spec" }
|
11
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 derailed
|
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,176 @@
|
|
1
|
+
# WeWoo
|
2
|
+
|
3
|
+
Wewoo is a small wrapper library that provides for graph database
|
4
|
+
management using Ruby. Any graph databases that supports the Rexster graph
|
5
|
+
server REST API can be integrated with Wewoo.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```
|
12
|
+
gem 'wewoo'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
```
|
18
|
+
$ bundle
|
19
|
+
```
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
```
|
24
|
+
$ gem install wewoo
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Wewoo comes bundled with a REPL console to make for experimentation with the
|
30
|
+
graph database using Ruby a joy.
|
31
|
+
|
32
|
+
```
|
33
|
+
$ wewoo
|
34
|
+
```
|
35
|
+
|
36
|
+
### Connecting to a graph
|
37
|
+
|
38
|
+
Dependending on your Rexster configuration, you will need to specify the
|
39
|
+
host and port to tell Wewoo how to connect. The default url is localhost:8182.
|
40
|
+
You may optionaly choose to set the configuration in a shell environment variable
|
41
|
+
WEWOO_URL ie WEWOO_URL='http://localhost:8185'
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
# List out all available graphs
|
45
|
+
Wewoo::Graph.available_graphs #=> ["test_graph", "my_awesome_graph"]
|
46
|
+
|
47
|
+
# Connect to an existing graph on localhost:8182
|
48
|
+
g = Wewoo::Graph.new( :my_awesome_graph )
|
49
|
+
# or...
|
50
|
+
g = Wewoo::Graph.new( :my_awesome_graph, 'localhost', 9000 )
|
51
|
+
|
52
|
+
# List out all vertices
|
53
|
+
g.V # => [v(95084), v(95072), v(95076), v(95088), v(95080)]
|
54
|
+
|
55
|
+
# List out all edges
|
56
|
+
g.E # => [e(1lIN-oJG-4K) [95088-created-95076], e(1lIJ-oJy-4K) [95080-created-95084]]
|
57
|
+
```
|
58
|
+
|
59
|
+
### Adding a vertex
|
60
|
+
|
61
|
+
Most graph database don't allow setting an id on a graph element. Wewoo errs on
|
62
|
+
letting the graph implementation assign an internal id. It is usually considered
|
63
|
+
good practice to keep vertex properties to a minimum. Wewoo provides a special
|
64
|
+
property :gid to integrate the graph element with another data source.
|
65
|
+
You can leverage :gid to represent a foreign key to another store. Hence :gid must
|
66
|
+
be unique. If not set the gid will refer to the underlying graph
|
67
|
+
implementation id.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
v = g.add_vertex( name: 'Fred', age: 20, active: true, gid: 'user_900' )
|
71
|
+
v.id #=> 1234
|
72
|
+
v.gid #=> 'user_900'
|
73
|
+
v.props #=> {name: 'Fred', age: 20, active: true}
|
74
|
+
v.props.age #=> 20
|
75
|
+
```
|
76
|
+
|
77
|
+
### Adding an edge
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
v1 = g.add_vertex( name: 'Fred' )
|
81
|
+
v2 = g.add_vertex( name: 'Blee' )
|
82
|
+
e = g.add_edge( v1, v2, :friend, timestamp: Time.now )
|
83
|
+
e.id #=> 1234
|
84
|
+
e.gid #=> 1234
|
85
|
+
e.props #=> {"timestamp"=>"2014-03-01 13:55:26 -0700"}
|
86
|
+
```
|
87
|
+
|
88
|
+
### Deleting elements
|
89
|
+
|
90
|
+
Wewoo provides two way to delete a graph element. If you have a handle on the
|
91
|
+
instance, you can call destroy on it directly. If not you can remove an element
|
92
|
+
by calling the associated remove method on a graph instance using the graph
|
93
|
+
element id.
|
94
|
+
NOTE: By virtue of a graph database, when deleting a vertex, all associated
|
95
|
+
edges will be deleted.
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
# Delete edge with id 1234
|
99
|
+
g.e( 1234 ).destroy
|
100
|
+
# or ...
|
101
|
+
g.remove_edge( 1234 )
|
102
|
+
|
103
|
+
# Delete a vertex with id 5678
|
104
|
+
g.v( 5678 ).destroy
|
105
|
+
# or...
|
106
|
+
g.remove_vertex( 5678 )
|
107
|
+
|
108
|
+
# Clear out the graph
|
109
|
+
g.clear
|
110
|
+
```
|
111
|
+
|
112
|
+
### Traversing
|
113
|
+
|
114
|
+
The power of graph database is in traversal. Traversal in Wewoo is made available
|
115
|
+
using the [Gremlin](http://gremlindocs.com/) graph api.
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
# Find all vertices outbound from vertex 1234
|
119
|
+
g.v( 1234 ).out #=> [v(4567)]
|
120
|
+
|
121
|
+
# Find all vertices inbound to vertex 1234
|
122
|
+
g.v( 1234 ).in #=> [v(7890), v(7891)]
|
123
|
+
|
124
|
+
# Find all vertices directly connected to vertex 1234
|
125
|
+
g.v( 1234 ).both #=> [v(7890), v(7891), v(4567)]
|
126
|
+
|
127
|
+
# Find all edges connected to vertex 1234 with labels fred or created
|
128
|
+
g.v(1234).bothE( :fred, :created) #=> [e(1lIJ-oJy-4K) [95080-created-95084]]
|
129
|
+
|
130
|
+
# Find all vertices with name property blee
|
131
|
+
g.find_vertices( :name, 'blee' ) #=> [v(1234)]
|
132
|
+
|
133
|
+
# Find vertex with gid 1234
|
134
|
+
g.find_first_vertex( :gid, 1234 ) #=> [v(567)]
|
135
|
+
|
136
|
+
# Find vertex with id 456
|
137
|
+
g.find_vertex( 456 ) #=> [v(456)]
|
138
|
+
# or...
|
139
|
+
g.v( 456 ) #=> [v(456)]
|
140
|
+
|
141
|
+
# Paging support...
|
142
|
+
g.find_vertices( 'enabled', true, 1, 2 ) #=> [v(7890), v(7891)]
|
143
|
+
```
|
144
|
+
|
145
|
+
You can access the full gremlin query api using the Wewoo::Graph#query
|
146
|
+
method. Wewoo does its best to construct graph elements objects with the returning
|
147
|
+
results.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
# Find all vertices who's name is fred
|
151
|
+
g.query( "g.V.out.groupCount.cap") #=> {v(113420)=>2, v(113416)=>2, v(113412)=>1}
|
152
|
+
|
153
|
+
# Find all edge with weight < 0.5
|
154
|
+
g.q( "g.E.has( 'weight', T.lt, 0.5f)" ) #=> [e(1xSj-tve-2W) [113412-friend-113416]]
|
155
|
+
```
|
156
|
+
|
157
|
+
## Releases
|
158
|
+
|
159
|
+
* 0.1.0 Initial drop
|
160
|
+
* 0.1.1 Disable user defined IDs as most graphs disallow
|
161
|
+
* 0.1.2 Bug fixes and spec updates
|
162
|
+
* 0.1.3 Spec'ed out Gremlin API
|
163
|
+
* 0.1.4 Refactor graph elements
|
164
|
+
* 0.1.5 Beef up specs + README
|
165
|
+
|
166
|
+
## Contributing
|
167
|
+
|
168
|
+
1. Fork it
|
169
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
170
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
171
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
172
|
+
5. Create new Pull Request
|
173
|
+
|
174
|
+
## License
|
175
|
+
|
176
|
+
Wewoo is released under the [MIT](http://opensource.org/licenses/MIT) license.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/wewoo
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
3
|
+
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require
|
6
|
+
require 'wewoo'
|
7
|
+
require 'pry'
|
8
|
+
|
9
|
+
Main {
|
10
|
+
def run
|
11
|
+
puts "WeWoo Console"
|
12
|
+
Pry.config.prompt_name = "WeWoo"
|
13
|
+
Pry.start
|
14
|
+
end
|
15
|
+
}
|
data/lib/wewoo.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "wewoo/version"
|
2
|
+
|
3
|
+
module Wewoo
|
4
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
5
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
6
|
+
|
7
|
+
def self.configure(&block)
|
8
|
+
Configuration.module_eval( &block )
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
12
|
+
dir ||= ::File.basename(fname, '.*')
|
13
|
+
search_me = ::File.expand_path(
|
14
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
15
|
+
Dir.glob(search_me).sort.each { |rb| require rb }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Wewoo.require_all_libs_relative_to File.expand_path( "wewoo", Wewoo::LIBPATH )
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'json'
|
3
|
+
require 'map'
|
4
|
+
|
5
|
+
module Wewoo
|
6
|
+
module Adapter
|
7
|
+
module_function
|
8
|
+
|
9
|
+
class NoDataError < RuntimeError; end
|
10
|
+
class InvalidRequestError < RuntimeError; end
|
11
|
+
|
12
|
+
def get( url, opts={} )
|
13
|
+
handle_response( Typhoeus.get( url, opts ) )
|
14
|
+
end
|
15
|
+
|
16
|
+
def post( url, opts={} )
|
17
|
+
handle_response( Typhoeus.post( url, opts ) )
|
18
|
+
end
|
19
|
+
|
20
|
+
def put( url, opts={} )
|
21
|
+
handle_response( Typhoeus.put( url, opts ) )
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete( url, opts={} )
|
25
|
+
handle_response( Typhoeus.delete( url, opts ) )
|
26
|
+
end
|
27
|
+
|
28
|
+
def log( title, message )
|
29
|
+
return unless Configuration.debug
|
30
|
+
|
31
|
+
msg = "[Wewoo] #{title} -- #{message}"
|
32
|
+
if Object.const_defined? :Rails
|
33
|
+
Rails.logger.info msg
|
34
|
+
else
|
35
|
+
puts msg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def handle_response( resp )
|
40
|
+
log "URL", resp.effective_url
|
41
|
+
|
42
|
+
unless resp.success?
|
43
|
+
error = "-- " + JSON.parse( resp.response_body )['message'] rescue ""
|
44
|
+
raise InvalidRequestError, "<#{resp.response_code}> " +
|
45
|
+
"Failed request:#{resp.effective_url} #{error}"
|
46
|
+
end
|
47
|
+
|
48
|
+
if resp.body.empty? or resp.body == "null"
|
49
|
+
raise NoDataError, "No data found at location #{url}"
|
50
|
+
end
|
51
|
+
|
52
|
+
body = JSON.parse( resp.body )
|
53
|
+
log "RESP", body
|
54
|
+
results = body['results'] || body
|
55
|
+
results.is_a?(Hash) ? Map[results] : results
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Wewoo
|
2
|
+
class Configuration
|
3
|
+
def self.debug(value=false)
|
4
|
+
@debug ||= value
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.url(host=nil, port=nil)
|
8
|
+
@url = nil if host
|
9
|
+
@url ||= begin
|
10
|
+
url = compute_url( host, port) || ENV['WEWOO_URL'] || default_url
|
11
|
+
url.to_s.gsub( %r(/*$), '' )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.compute_url( host, port )
|
18
|
+
return nil unless host and port
|
19
|
+
"http://#{host}:#{port}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.default_url; compute_url( :localhost, 8182 ); end
|
23
|
+
end
|
24
|
+
end
|
data/lib/wewoo/edge.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'wewoo/element'
|
2
|
+
|
3
|
+
module Wewoo
|
4
|
+
class Edge < Element
|
5
|
+
include Adapter
|
6
|
+
|
7
|
+
attr_accessor :label, :from_id, :to_id
|
8
|
+
|
9
|
+
def ==( other )
|
10
|
+
self.class == other.class and
|
11
|
+
self.id == other.id and
|
12
|
+
self.from_id == other.from_id and
|
13
|
+
self.to_id == other.to_id and
|
14
|
+
self.label == other.label and
|
15
|
+
self.props == other.props
|
16
|
+
end
|
17
|
+
alias :eql? :==
|
18
|
+
def hash; id; end
|
19
|
+
|
20
|
+
def get_vertex( direction )
|
21
|
+
id = (direction == :in ? to_id : from_id)
|
22
|
+
graph.find_vertex( id )
|
23
|
+
end
|
24
|
+
def in; @in || get_vertex(:in); end
|
25
|
+
def out; @out || get_vertex(:out); end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"e(#{self.id}) [#{from_id}-#{label}-#{to_id}]"
|
29
|
+
end
|
30
|
+
alias :inspect :to_s
|
31
|
+
|
32
|
+
def destroy
|
33
|
+
graph.remove_edge( id )
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def self.from_hash( graph, hash )
|
39
|
+
new( graph,
|
40
|
+
hash.delete( '_id' ),
|
41
|
+
hash.delete( '_outV' ),
|
42
|
+
hash.delete( '_inV' ),
|
43
|
+
hash.delete('_label'), hash )
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize( graph, id, from_id, to_id, label, properties )
|
47
|
+
super( graph, id, properties )
|
48
|
+
|
49
|
+
@from_id = from_id
|
50
|
+
@to_id = to_id
|
51
|
+
@label = label
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Wewoo
|
2
|
+
class Element
|
3
|
+
attr_reader :id, :graph
|
4
|
+
attr_accessor :properties
|
5
|
+
|
6
|
+
alias :props :properties
|
7
|
+
|
8
|
+
def gid
|
9
|
+
props.gid
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def initialize( graph, id, properties )
|
15
|
+
@graph = graph
|
16
|
+
@id = id
|
17
|
+
@properties = to_props( properties )
|
18
|
+
@properties[:gid] = id unless props[:gid]
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_props( properties )
|
22
|
+
properties.delete( '_type' )
|
23
|
+
Map[Hash[properties.map { |k,v|
|
24
|
+
[k, ((v.is_a? Hash and v.has_key? 'type') ? v['value'] : v)]
|
25
|
+
}]]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|