architect4r 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +53 -0
  4. data/Guardfile +10 -0
  5. data/License +20 -0
  6. data/README.md +62 -0
  7. data/Rakefile +40 -0
  8. data/ReleaseNotes.md +33 -0
  9. data/Roadmap.md +31 -0
  10. data/Specs.md +21 -0
  11. data/architect4r.gemspec +31 -0
  12. data/lib/architect4r.rb +66 -0
  13. data/lib/architect4r/adapters/carrier_wave.rb +64 -0
  14. data/lib/architect4r/core/configuration.rb +148 -0
  15. data/lib/architect4r/core/cypher_methods.rb +47 -0
  16. data/lib/architect4r/core/management_methods.rb +129 -0
  17. data/lib/architect4r/core/node_methods.rb +73 -0
  18. data/lib/architect4r/core/relationship_methods.rb +82 -0
  19. data/lib/architect4r/generic_node.rb +7 -0
  20. data/lib/architect4r/has_node.rb +80 -0
  21. data/lib/architect4r/instance_manager.rb +47 -0
  22. data/lib/architect4r/model/callbacks.rb +19 -0
  23. data/lib/architect4r/model/connection.rb +29 -0
  24. data/lib/architect4r/model/links_query_interface.rb +23 -0
  25. data/lib/architect4r/model/node.rb +117 -0
  26. data/lib/architect4r/model/persistency.rb +95 -0
  27. data/lib/architect4r/model/properties.rb +166 -0
  28. data/lib/architect4r/model/queries.rb +38 -0
  29. data/lib/architect4r/model/relationship.rb +105 -0
  30. data/lib/architect4r/model/relationships.rb +16 -0
  31. data/lib/architect4r/model/validations.rb +11 -0
  32. data/lib/architect4r/server.rb +96 -0
  33. data/lib/architect4r/version.rb +3 -0
  34. data/spec/architect4r_spec.rb +9 -0
  35. data/spec/core/configuration_spec.rb +54 -0
  36. data/spec/core/cypher_methods_spec.rb +29 -0
  37. data/spec/core/node_methods_spec.rb +47 -0
  38. data/spec/core/relationship_methods_spec.rb +92 -0
  39. data/spec/fixtures/architect4r.yml +21 -0
  40. data/spec/fixtures/graph.db.default/active_tx_log +1 -0
  41. data/spec/fixtures/graph.db.default/index/lucene-store.db +0 -0
  42. data/spec/fixtures/graph.db.default/index/lucene.log.1 +0 -0
  43. data/spec/fixtures/graph.db.default/index/lucene.log.active +0 -0
  44. data/spec/fixtures/graph.db.default/index/lucene.log.v0 +0 -0
  45. data/spec/fixtures/graph.db.default/index/lucene.log.v1 +0 -0
  46. data/spec/fixtures/graph.db.default/index/lucene.log.v2 +0 -0
  47. data/spec/fixtures/graph.db.default/lock +0 -0
  48. data/spec/fixtures/graph.db.default/messages.log +183 -0
  49. data/spec/fixtures/graph.db.default/neostore +0 -0
  50. data/spec/fixtures/graph.db.default/neostore.id +0 -0
  51. data/spec/fixtures/graph.db.default/neostore.nodestore.db +0 -0
  52. data/spec/fixtures/graph.db.default/neostore.nodestore.db.id +0 -0
  53. data/spec/fixtures/graph.db.default/neostore.propertystore.db +0 -0
  54. data/spec/fixtures/graph.db.default/neostore.propertystore.db.arrays +0 -0
  55. data/spec/fixtures/graph.db.default/neostore.propertystore.db.arrays.id +0 -0
  56. data/spec/fixtures/graph.db.default/neostore.propertystore.db.id +0 -0
  57. data/spec/fixtures/graph.db.default/neostore.propertystore.db.index +0 -0
  58. data/spec/fixtures/graph.db.default/neostore.propertystore.db.index.id +0 -0
  59. data/spec/fixtures/graph.db.default/neostore.propertystore.db.index.keys +0 -0
  60. data/spec/fixtures/graph.db.default/neostore.propertystore.db.index.keys.id +0 -0
  61. data/spec/fixtures/graph.db.default/neostore.propertystore.db.strings +0 -0
  62. data/spec/fixtures/graph.db.default/neostore.propertystore.db.strings.id +0 -0
  63. data/spec/fixtures/graph.db.default/neostore.relationshipstore.db +0 -0
  64. data/spec/fixtures/graph.db.default/neostore.relationshipstore.db.id +0 -0
  65. data/spec/fixtures/graph.db.default/neostore.relationshiptypestore.db +0 -0
  66. data/spec/fixtures/graph.db.default/neostore.relationshiptypestore.db.id +0 -0
  67. data/spec/fixtures/graph.db.default/neostore.relationshiptypestore.db.names +0 -0
  68. data/spec/fixtures/graph.db.default/neostore.relationshiptypestore.db.names.id +0 -0
  69. data/spec/fixtures/graph.db.default/nioneo_logical.log.1 +0 -0
  70. data/spec/fixtures/graph.db.default/nioneo_logical.log.active +0 -0
  71. data/spec/fixtures/graph.db.default/tm_tx_log.1 +0 -0
  72. data/spec/fixtures/graph.db.default/upgrade_backup/active_tx_log +1 -0
  73. data/spec/fixtures/graph.db.default/upgrade_backup/messages.log +142 -0
  74. data/spec/fixtures/graph.db.default/upgrade_backup/neostore +0 -0
  75. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.id +0 -0
  76. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.nodestore.db +0 -0
  77. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.nodestore.db.id +0 -0
  78. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db +0 -0
  79. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.arrays +0 -0
  80. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.arrays.id +0 -0
  81. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.id +0 -0
  82. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.index +0 -0
  83. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.index.id +0 -0
  84. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.index.keys +0 -0
  85. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.index.keys.id +0 -0
  86. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.strings +0 -0
  87. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.propertystore.db.strings.id +0 -0
  88. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshipstore.db +0 -0
  89. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshipstore.db.id +0 -0
  90. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshiptypestore.db +0 -0
  91. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshiptypestore.db.id +0 -0
  92. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshiptypestore.db.names +0 -0
  93. data/spec/fixtures/graph.db.default/upgrade_backup/neostore.relationshiptypestore.db.names.id +0 -0
  94. data/spec/fixtures/graph.db.default/upgrade_backup/nioneo_logical.log.active +0 -0
  95. data/spec/fixtures/graph.db.default/upgrade_backup/nioneo_logical.log.v0 +0 -0
  96. data/spec/fixtures/graph.db.default/upgrade_backup/nioneo_logical.log.v1 +0 -0
  97. data/spec/fixtures/graph.db.default/upgrade_backup/nioneo_logical.log.v2 +0 -0
  98. data/spec/fixtures/graph.db.default/upgrade_backup/tm_tx_log.1 +0 -0
  99. data/spec/has_node_spec.rb +87 -0
  100. data/spec/model/links_query_interface_spec.rb +22 -0
  101. data/spec/model/links_spec.rb +26 -0
  102. data/spec/model/node_spec.rb +48 -0
  103. data/spec/model/persistency_spec.rb +98 -0
  104. data/spec/model/properties_spec.rb +165 -0
  105. data/spec/model/queries_spec.rb +50 -0
  106. data/spec/model/relationship_spec.rb +63 -0
  107. data/spec/model/validations_spec.rb +31 -0
  108. data/spec/server_spec.rb +33 -0
  109. data/spec/spec_helper.rb +115 -0
  110. metadata +377 -0
@@ -0,0 +1,38 @@
1
+ module Architect4r
2
+ module Model
3
+ module Queries
4
+ extend ActiveSupport::Concern
5
+
6
+ module InstanceMethods
7
+
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ def count(opts = {}, &block)
13
+ data = connection.execute_cypher("start s=node(#{self.model_root.id}) match (s)<-[:model_type]-(d) return count(d)")
14
+ data.first['count(d)']
15
+ end
16
+
17
+ def find_by_id(id)
18
+ data = connection.execute_cypher("start s=node(#{self.model_root.id}), d=node(#{id.to_i}) match s<-[r:model_type]-d return d")
19
+ data &&= data.first && data.first['d']
20
+ self.build_from_database(data)
21
+ end
22
+
23
+ def find_by_id!(id)
24
+ raise 'not implemented'
25
+ end
26
+
27
+ def find_by_cypher(query, identifier)
28
+ if data = connection.execute_cypher(query)
29
+ data.map { |item| build_from_database(item[identifier]) }
30
+ else
31
+ nil
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,105 @@
1
+ module Architect4r
2
+ module Model
3
+ class Relationship
4
+
5
+ #
6
+ # Architect4r extensions
7
+ #
8
+ include Architect4r::Model::Connection
9
+ include Architect4r::Model::Callbacks
10
+ include Architect4r::Model::Persistency
11
+
12
+
13
+ def self.inherited(subklass)
14
+ super
15
+ subklass.send(:include, ActiveModel::Conversion)
16
+ subklass.extend ActiveModel::Naming
17
+ subklass.send(:include, Architect4r::Model::Properties)
18
+ subklass.send(:include, Architect4r::Model::Validations)
19
+
20
+ subklass.class_exec do
21
+ # Validations
22
+ validates :source, :presence => true
23
+ validates :destination, :presence => true
24
+ end
25
+ end
26
+
27
+ #
28
+ # Virtual attributes
29
+ #
30
+ attr_accessor :source, :destination, :raw_data
31
+
32
+ #
33
+ # Instance methods
34
+ #
35
+
36
+ def initialize(*args)
37
+ # Detect source and destination
38
+ if s = args[0].is_a?(Architect4r::Model::Node) && args.shift
39
+ self.source = s
40
+
41
+ if d = args[0].is_a?(Architect4r::Model::Node) && args.shift
42
+ self.destination = d
43
+ end
44
+ end
45
+
46
+ # Detect properties
47
+ properties = args[0].is_a?(Hash) && args.shift
48
+ properties ||= {}
49
+ parse_properties(properties)
50
+ end
51
+
52
+ # Create the document. Validation is enabled by default and will return
53
+ # false if the document is not valid. If all goes well, the document will
54
+ # be returned.
55
+ def create(options = {})
56
+ run_callbacks :create do
57
+ run_callbacks :save do
58
+ # only create valid records
59
+ return false unless perform_validations(options)
60
+
61
+ # perform creation
62
+ if result = connection.create_relationship(self.source.id, self.destination.id, self.class.name, self._to_database_hash)
63
+ self.raw_data = result
64
+ end
65
+
66
+ # if something goes wrong we receive a nil value and return false
67
+ !result.nil?
68
+ end
69
+ end
70
+ end
71
+
72
+ # Trigger the callbacks (before, after, around)
73
+ # only if the document isn't new
74
+ def update(options = {})
75
+ run_callbacks :update do
76
+ run_callbacks :save do
77
+ # Check if record can be updated
78
+ raise "Cannot save a destroyed document!" if destroyed?
79
+ raise "Calling #{self.class.name}#update on document that has not been created!" if new?
80
+
81
+ # Check if we can continue
82
+ return false unless perform_validations(options)
83
+
84
+ # perform update
85
+ result = connection.update_relationship(self.id, self._to_database_hash)
86
+
87
+ # if something goes wrong we receive a nil value and return false
88
+ !result.nil?
89
+ end
90
+ end
91
+ end
92
+
93
+ def destroy
94
+ run_callbacks :destroy do
95
+ if result = connection.delete_relationship(self.id)
96
+ @_destroyed = true
97
+ self.freeze
98
+ end
99
+ result
100
+ end
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,16 @@
1
+ module Architect4r
2
+ module Model
3
+ module Relationships
4
+ extend ActiveSupport::Concern
5
+
6
+ module InstanceMethods
7
+
8
+ def links
9
+ @links_query_interface = LinksQueryInterface.new(self)
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Architect4r
2
+ module Model
3
+ module Validations
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::Validations
6
+
7
+ # just in case that we want to add some more sugar
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,96 @@
1
+ require 'json'
2
+ require 'typhoeus'
3
+
4
+ module Architect4r
5
+
6
+ class Server
7
+
8
+ include Architect4r::Core::CypherMethods
9
+ include Architect4r::Core::NodeMethods
10
+ include Architect4r::Core::RelationshipMethods
11
+
12
+ def initialize(config=nil)
13
+ @server_config = config
14
+ end
15
+
16
+ def configuration
17
+ @configuration ||= Core::Configuration.new(@server_config)
18
+ end
19
+
20
+ # Basic rest actions
21
+
22
+ def get(url, options = {})
23
+ response = Typhoeus::Request.get(prepend_base_url(url), :headers => { 'Accept' => 'application/json' })
24
+ response.success? ? JSON.parse(response.body) : nil
25
+ end
26
+
27
+ def post(url, params = {})
28
+ response = Typhoeus::Request.post(prepend_base_url(url), :params => params)
29
+ response.success? ? JSON.parse(response.body) : nil
30
+ end
31
+
32
+ def put(url, params = {})
33
+ response = Typhoeus::Request.put(prepend_base_url(url), :params => params)
34
+ response.success? ? JSON.parse(response.body) : nil
35
+ end
36
+
37
+ def delete
38
+ response = Typhoeus::Request.delete(prepend_base_url(url), :params => params)
39
+ response.success? ? JSON.parse(response.body) : nil
40
+ end
41
+
42
+ protected
43
+
44
+ def prepend_base_url(url)
45
+ if url[0,4] == "http"
46
+ url
47
+ else
48
+ "http://#{configuration.host}:#{configuration.port}#{configuration.path}/db/data#{url}"
49
+ end
50
+ end
51
+
52
+ def node_url(url_or_id)
53
+ if url_or_id.is_a?(Hash)
54
+ url_or_id['self'].to_s
55
+ elsif url_or_id.to_s != '0' and url_or_id.to_i == 0
56
+ url_or_id.to_s
57
+ else
58
+ prepend_base_url("/node/#{url_or_id.to_i}")
59
+ end
60
+ end
61
+
62
+ def relationship_url(url_or_id)
63
+ if url_or_id.is_a?(Hash)
64
+ url_or_id['self'].to_s
65
+ elsif url_or_id.to_s != '0' and url_or_id.to_i == 0
66
+ url_or_id.to_s
67
+ else
68
+ prepend_base_url("/relationship/#{url_or_id.to_i}")
69
+ end
70
+ end
71
+
72
+ def convert_if_possible(data)
73
+ data
74
+ end
75
+
76
+ end
77
+
78
+
79
+ end
80
+
81
+
82
+ =begin
83
+
84
+ Example service root response
85
+ extensions:
86
+ CypherPlugin:
87
+ execute_query: http://localhost:7475/db/data/ext/CypherPlugin/graphdb/execute_query
88
+ GremlinPlugin:
89
+ execute_script: http://localhost:7475/db/data/ext/GremlinPlugin/graphdb/execute_script
90
+ relationship_types: http://localhost:7475/db/data/relationship/types
91
+ relationship_index: http://localhost:7475/db/data/index/relationship
92
+ reference_node: http://localhost:7475/db/data/node/0
93
+ node: http://localhost:7475/db/data/node
94
+ extensions_info: http://localhost:7475/db/data/ext
95
+ node_index: http://localhost:7475/db/data/index/node
96
+ =end
@@ -0,0 +1,3 @@
1
+ module Architect4r
2
+ VERSION = "0.3.2"
3
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Architect4r do
4
+
5
+ it 'should return correct version string' do
6
+ Architect4r.version.should == "Architect4r version #{Architect4r::VERSION}"
7
+ end
8
+
9
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Architect4r::Core::Configuration do
4
+
5
+ describe "default configuration" do
6
+ subject { Architect4r::Core::Configuration.new }
7
+
8
+ its(:host) { should == 'localhost' }
9
+ its(:port) { should == 7474 }
10
+ its(:path) { should == '' }
11
+ its(:log_level) { should == 'INFO' }
12
+
13
+ context "in test environment" do
14
+ subject { Architect4r::Core::Configuration.new(:environment => :test) }
15
+
16
+ its(:host) { should == 'localhost' }
17
+ its(:port) { should == 7475 }
18
+ its(:path) { should == '' }
19
+ its(:log_level) { should == 'OFF' }
20
+ end
21
+
22
+ context "in production environment" do
23
+ subject { Architect4r::Core::Configuration.new(:environment => :production) }
24
+
25
+ its(:host) { should == 'localhost' }
26
+ its(:port) { should == 7474 }
27
+ its(:path) { should == '' }
28
+ its(:log_level) { should == 'WARNING' }
29
+ end
30
+
31
+ end
32
+
33
+ describe "provided by hash" do
34
+ subject { Architect4r::Core::Configuration.new(:environment => :development, :config => { :host => 'neo4j.local', :port => '80', :path => 'dev', :log_level => 'ERROR', :log_file => '/tmp/neo.log' }) }
35
+
36
+ its(:host) { should == 'neo4j.local' }
37
+ its(:port) { should == 80 }
38
+ its(:path) { should == 'dev' }
39
+ its(:log_level) { should == 'ERROR' }
40
+ its(:log_file) { should == '/tmp/neo.log' }
41
+
42
+ end
43
+
44
+ describe "provided by a custom config file" do
45
+ subject { Architect4r::Core::Configuration.new(:environment => :staging, :config => File.join(Dir.pwd, 'spec', 'fixtures', 'architect4r.yml')) }
46
+
47
+ its(:host) { should == 'staging.dev' }
48
+ its(:port) { should == 8080 }
49
+ its(:path) { should == 'my_neo_instance' }
50
+ its(:log_level) { should == 'WARNING' }
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Architect4r::Server do
4
+
5
+ subject { TEST_SERVER }
6
+
7
+ describe :execute_cypher do
8
+
9
+ it "should return an array of nodes" do
10
+ # nodes
11
+ #results = subject.execute_cypher("start root = (#{subject.root_node.id}) match (root)-[r:model_root]->(x) return r")
12
+ pending
13
+ end
14
+
15
+ it "should return an array of relationships" do
16
+ # relations
17
+ #results = subject.find_by_cypher("start root = (#{subject.root_node.id}) match (root)-[r:model_root]->(x) return r")
18
+ pending
19
+ end
20
+
21
+ it "should the data unprocessed" do
22
+ #results = subject.execute_cypher("start node = (0) return node,node.name?")
23
+ #results.should_not be_empty
24
+ pending
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe Architect4r::Server do
4
+
5
+ subject { TEST_SERVER }
6
+
7
+ it "should create a node" do
8
+ result = subject.create_node({ 'name' => 'My test node', 'friends' => 13 })
9
+ result.should be_a(Hash)
10
+ result['self'].should be_a(String)
11
+ end
12
+
13
+ it "should get a node" do
14
+ result = subject.get_node(0)
15
+ result.should be_a(Hash)
16
+ result['self'].should be_a(String)
17
+ end
18
+
19
+ it "should delete a node" do
20
+ # Create a node which can be deleted
21
+ node = subject.create_node({ 'test' => 'test' })
22
+ node.should be_a(Hash)
23
+
24
+ # Delete the node
25
+ subject.delete_node(node['self'])
26
+
27
+ # Check if it still exists
28
+ subject.get_node(node['self']).should be_nil
29
+ end
30
+
31
+ it "should update a nodes properties" do
32
+ # Create a node which can be deleted
33
+ original_node = subject.create_node({ 'test1' => 'test', 'test2' => 'hello' })
34
+ original_node.should be_a(Hash)
35
+
36
+ # Update some attributes
37
+ result = subject.update_node(original_node['self'], { 'test1' => 'world', 'test3' => 'word'})
38
+ result.should be_true
39
+
40
+ # check result
41
+ changed_node = subject.get_node(original_node['self'])
42
+ changed_node['data']['test1'].should == 'world'
43
+ changed_node['data'].has_key?('test2').should be_false
44
+ changed_node['data']['test3'].should == 'word'
45
+ end
46
+
47
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe Architect4r::Server do
4
+
5
+ subject { TEST_SERVER }
6
+
7
+ before(:all) do
8
+ # Lets create two nodes we can work with
9
+ @node1 = subject.create_node({ 'name' => 'first test node' })
10
+ @node2 = subject.create_node({ 'name' => 'second test node' })
11
+ end
12
+
13
+ after(:all) do
14
+ # Lets create two nodes we can work with
15
+ subject.delete_node(@node1)
16
+ subject.delete_node(@node2)
17
+ end
18
+
19
+ it "should create and delete a new relationship" do
20
+ result = subject.create_relationship(@node1, @node2, 'friendship', { 'reason' => 'Because I really like you!' })
21
+ result.should be_a(Hash)
22
+ subject.delete_relationship(result)
23
+ end
24
+
25
+ it "should allow creating relationships without properties" do
26
+ result = subject.create_relationship(@node1, @node2, 'friendship')
27
+ result.should be_a(Hash)
28
+ subject.delete_relationship(result)
29
+ end
30
+
31
+ it "should allow filtering relationships by direction" do
32
+ # create test nodes
33
+ n1 = subject.create_node({ 'name' => 'first test node' })
34
+ n2 = subject.create_node({ 'name' => 'second test node' })
35
+
36
+ # create relationships
37
+ subject.create_relationship(n1, n2, 'relation')
38
+ subject.create_relationship(n1, n2, 'relation2')
39
+ subject.create_relationship(n2, n1, 'relation3')
40
+
41
+ # test node direction
42
+ subject.get_node_relationships(n1).size.should == 3
43
+ subject.get_node_relationships(n1, :all).size.should == 3
44
+ subject.get_node_relationships(n1, :outgoing).size.should == 2
45
+ subject.get_node_relationships(n1, :incoming).size.should == 1
46
+
47
+ # test relation type
48
+ subject.get_node_relationships(n1, :all, 'relation').size.should == 1
49
+ subject.get_node_relationships(n1, :outgoing, 'relation').size.should == 1
50
+ subject.get_node_relationships(n1, :incoming, 'relation').size.should == 0
51
+ subject.get_node_relationships(n1, :all, 'relation', 'relation2').size.should == 2
52
+
53
+ # Clean up
54
+ subject.delete_node(n1)
55
+ subject.delete_node(n2)
56
+ end
57
+
58
+ it "should retrieve all relationships" do
59
+ subject.get_node_relationships(0).should be_a(Array)
60
+ end
61
+
62
+ it "should load a relationship" do
63
+ node = subject.create_node({ 'name' => 'A test node' })
64
+ rel = subject.create_relationship(node, node, 'test', { 'some' => 'data' })
65
+ data = subject.get_relationship(rel)
66
+ data.should be_a(Hash)
67
+ data['data']['some'].should == 'data'
68
+ end
69
+
70
+ it "should update a nodes properties" do
71
+ node = subject.create_node({ 'name' => 'A test node' })
72
+ original_rel = subject.create_relationship(node, node, 'self-reference', { 'note' => 'Some random note', 'obsolete' => '1' })
73
+ original_rel.should be_a(Hash)
74
+
75
+ # Update some attributes
76
+ result = subject.update_relationship(original_rel, { 'note' => 'Some changed note', 'highlight' => 'note'})
77
+ result.should be_true
78
+
79
+ # check result
80
+ changed_rel = subject.get_relationship(original_rel)
81
+ changed_rel['data']['note'].should == 'Some changed note'
82
+ changed_rel['data'].has_key?('obsolete').should be_false
83
+ changed_rel['data']['highlight'].should == 'note'
84
+ end
85
+
86
+ it "should know about all relationship types" do
87
+ node = subject.create_node({ 'name' => 'A test node' })
88
+ subject.create_relationship(node, node, 'self-reference')
89
+ subject.get_relationship_types.should include('self-reference')
90
+ end
91
+
92
+ end