neography-down 1.6.4

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.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.project +12 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +4 -0
  6. data/CHANGELOG.md +939 -0
  7. data/CONTRIBUTORS +18 -0
  8. data/Gemfile +4 -0
  9. data/Guardfile +14 -0
  10. data/LICENSE +19 -0
  11. data/README.md +281 -0
  12. data/Rakefile +14 -0
  13. data/examples/facebook.rb +40 -0
  14. data/examples/facebook_v2.rb +25 -0
  15. data/examples/greatest.rb +43 -0
  16. data/examples/linkedin.rb +39 -0
  17. data/examples/linkedin_v2.rb +22 -0
  18. data/examples/traversal_example1.rb +65 -0
  19. data/examples/traversal_example2.rb +54 -0
  20. data/lib/neography.rb +45 -0
  21. data/lib/neography/config.rb +64 -0
  22. data/lib/neography/connection.rb +263 -0
  23. data/lib/neography/equal.rb +21 -0
  24. data/lib/neography/errors.rb +60 -0
  25. data/lib/neography/index.rb +52 -0
  26. data/lib/neography/multi_json_parser.rb +28 -0
  27. data/lib/neography/neography.rb +10 -0
  28. data/lib/neography/node.rb +63 -0
  29. data/lib/neography/node_path.rb +29 -0
  30. data/lib/neography/node_relationship.rb +37 -0
  31. data/lib/neography/node_traverser.rb +146 -0
  32. data/lib/neography/path_traverser.rb +100 -0
  33. data/lib/neography/property.rb +110 -0
  34. data/lib/neography/property_container.rb +28 -0
  35. data/lib/neography/railtie.rb +19 -0
  36. data/lib/neography/relationship.rb +78 -0
  37. data/lib/neography/relationship_traverser.rb +80 -0
  38. data/lib/neography/rest.rb +99 -0
  39. data/lib/neography/rest/batch.rb +414 -0
  40. data/lib/neography/rest/clean.rb +17 -0
  41. data/lib/neography/rest/constraints.rb +38 -0
  42. data/lib/neography/rest/cypher.rb +29 -0
  43. data/lib/neography/rest/extensions.rb +21 -0
  44. data/lib/neography/rest/gremlin.rb +20 -0
  45. data/lib/neography/rest/helpers.rb +96 -0
  46. data/lib/neography/rest/node_auto_indexes.rb +60 -0
  47. data/lib/neography/rest/node_indexes.rb +139 -0
  48. data/lib/neography/rest/node_labels.rb +49 -0
  49. data/lib/neography/rest/node_paths.rb +49 -0
  50. data/lib/neography/rest/node_properties.rb +52 -0
  51. data/lib/neography/rest/node_relationships.rb +33 -0
  52. data/lib/neography/rest/node_traversal.rb +25 -0
  53. data/lib/neography/rest/nodes.rb +94 -0
  54. data/lib/neography/rest/other_node_relationships.rb +38 -0
  55. data/lib/neography/rest/relationship_auto_indexes.rb +60 -0
  56. data/lib/neography/rest/relationship_indexes.rb +142 -0
  57. data/lib/neography/rest/relationship_properties.rb +52 -0
  58. data/lib/neography/rest/relationship_types.rb +11 -0
  59. data/lib/neography/rest/relationships.rb +16 -0
  60. data/lib/neography/rest/schema_indexes.rb +26 -0
  61. data/lib/neography/rest/spatial.rb +137 -0
  62. data/lib/neography/rest/transactions.rb +101 -0
  63. data/lib/neography/tasks.rb +207 -0
  64. data/lib/neography/version.rb +3 -0
  65. data/neography.gemspec +39 -0
  66. data/spec/integration/authorization_spec.rb +40 -0
  67. data/spec/integration/broken_spatial_spec.rb +28 -0
  68. data/spec/integration/index_spec.rb +71 -0
  69. data/spec/integration/neography_spec.rb +10 -0
  70. data/spec/integration/node_encoding_spec.rb +71 -0
  71. data/spec/integration/node_path_spec.rb +222 -0
  72. data/spec/integration/node_relationship_spec.rb +381 -0
  73. data/spec/integration/node_spec.rb +260 -0
  74. data/spec/integration/parsing_spec.rb +13 -0
  75. data/spec/integration/performance_spec.rb +17 -0
  76. data/spec/integration/relationship_spec.rb +37 -0
  77. data/spec/integration/rest_batch_no_streaming_spec.rb +41 -0
  78. data/spec/integration/rest_batch_spec.rb +604 -0
  79. data/spec/integration/rest_batch_streaming_spec.rb +51 -0
  80. data/spec/integration/rest_bulk_spec.rb +106 -0
  81. data/spec/integration/rest_constraints_spec.rb +72 -0
  82. data/spec/integration/rest_experimental_spec.rb +22 -0
  83. data/spec/integration/rest_gremlin_fail_spec.rb +46 -0
  84. data/spec/integration/rest_header_spec.rb +15 -0
  85. data/spec/integration/rest_index_spec.rb +481 -0
  86. data/spec/integration/rest_labels_spec.rb +128 -0
  87. data/spec/integration/rest_node_spec.rb +274 -0
  88. data/spec/integration/rest_other_node_relationship_spec.rb +137 -0
  89. data/spec/integration/rest_path_spec.rb +231 -0
  90. data/spec/integration/rest_plugin_spec.rb +177 -0
  91. data/spec/integration/rest_relationship_spec.rb +354 -0
  92. data/spec/integration/rest_relationship_types_spec.rb +18 -0
  93. data/spec/integration/rest_schema_index_spec.rb +32 -0
  94. data/spec/integration/rest_spatial_spec.rb +166 -0
  95. data/spec/integration/rest_transaction_spec.rb +166 -0
  96. data/spec/integration/rest_traverse_spec.rb +149 -0
  97. data/spec/integration/unmanaged_spec.rb +27 -0
  98. data/spec/matchers.rb +33 -0
  99. data/spec/neography_spec.rb +23 -0
  100. data/spec/spec_helper.rb +44 -0
  101. data/spec/unit/config_spec.rb +135 -0
  102. data/spec/unit/connection_spec.rb +284 -0
  103. data/spec/unit/node_spec.rb +100 -0
  104. data/spec/unit/properties_spec.rb +285 -0
  105. data/spec/unit/relationship_spec.rb +118 -0
  106. data/spec/unit/rest/batch_spec.rb +262 -0
  107. data/spec/unit/rest/clean_spec.rb +16 -0
  108. data/spec/unit/rest/constraints_spec.rb +45 -0
  109. data/spec/unit/rest/cypher_spec.rb +20 -0
  110. data/spec/unit/rest/extensions_spec.rb +28 -0
  111. data/spec/unit/rest/gremlin_spec.rb +25 -0
  112. data/spec/unit/rest/helpers_spec.rb +124 -0
  113. data/spec/unit/rest/labels_spec.rb +77 -0
  114. data/spec/unit/rest/node_auto_indexes_spec.rb +70 -0
  115. data/spec/unit/rest/node_indexes_spec.rb +140 -0
  116. data/spec/unit/rest/node_paths_spec.rb +77 -0
  117. data/spec/unit/rest/node_properties_spec.rb +79 -0
  118. data/spec/unit/rest/node_relationships_spec.rb +57 -0
  119. data/spec/unit/rest/node_traversal_spec.rb +35 -0
  120. data/spec/unit/rest/nodes_spec.rb +187 -0
  121. data/spec/unit/rest/relationship_auto_indexes_spec.rb +66 -0
  122. data/spec/unit/rest/relationship_indexes_spec.rb +132 -0
  123. data/spec/unit/rest/relationship_properties_spec.rb +79 -0
  124. data/spec/unit/rest/relationship_types_spec.rb +15 -0
  125. data/spec/unit/rest/relationships_spec.rb +21 -0
  126. data/spec/unit/rest/schema_index_spec.rb +30 -0
  127. data/spec/unit/rest/transactions_spec.rb +43 -0
  128. metadata +372 -0
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'neography'
3
+
4
+ @neo = Neography::Rest.new
5
+
6
+ def create_person(name)
7
+ @neo.create_node("name" => name)
8
+ end
9
+
10
+ def make_mutual_friends(node1, node2)
11
+ @neo.create_relationship("friends", node1, node2)
12
+ @neo.create_relationship("friends", node2, node1)
13
+ end
14
+
15
+ def degrees_of_separation(start_node, destination_node)
16
+ paths = @neo.get_paths(start_node, destination_node, {"type"=> "friends", "direction" => "in"}, depth=4, algorithm="allSimplePaths")
17
+ paths.each do |p|
18
+ p["names"] = p["nodes"].collect {|node| @neo.get_node_properties(node, "name")["name"] }
19
+ end
20
+
21
+ end
22
+
23
+ johnathan = create_person('Johnathan')
24
+ mark = create_person('Mark')
25
+ phill = create_person('Phill')
26
+ mary = create_person('Mary')
27
+
28
+ make_mutual_friends(johnathan, mark)
29
+ make_mutual_friends(mark, phill)
30
+ make_mutual_friends(phill, mary)
31
+ make_mutual_friends(mark, mary)
32
+
33
+ degrees_of_separation(johnathan, mary).each do |path|
34
+ puts path["names"].join(' => friends => ')
35
+ end
36
+
37
+ # RESULT
38
+ # Johnathan => friends => Mark => friends => Phill => friends => Mary
39
+ # Johnathan => friends => Mark => friends => Mary
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'neography'
3
+
4
+ @neo = Neography::Rest.new
5
+
6
+ johnathan = Neography::Node.create("name" =>'Johnathan')
7
+ mark = Neography::Node.create("name" =>'Mark')
8
+ phill = Neography::Node.create("name" =>'Phill')
9
+ mary = Neography::Node.create("name" =>'Mary')
10
+
11
+ johnathan.both(:friends) << mark
12
+ mark.both(:friends) << phill
13
+ phill.both(:friends) << mary
14
+ mark.both(:friends) << mary
15
+
16
+ johnathan.all_simple_paths_to(mary).incoming(:friends).depth(4).nodes.each do |node|
17
+ puts node.map{|n| n.name }.join(' => friends => ')
18
+ end
19
+
20
+ # RESULT
21
+ # Johnathan => friends => Mark => friends => Phill => friends => Mary
22
+ # Johnathan => friends => Mark => friends => Mary
@@ -0,0 +1,65 @@
1
+ require 'rubygems'
2
+ require 'neography'
3
+
4
+ @neo = Neography::Rest.new
5
+
6
+ def create_node(level, type)
7
+ @neo.create_node("NODE_LEVEL" => level, "TYPE" => type)
8
+ end
9
+
10
+ def create_other_node(type)
11
+ @neo.create_node("TYPE" => type)
12
+ end
13
+
14
+
15
+ def get_nodes_by_level(level, node)
16
+ starting_id = node["self"].split('/').last
17
+
18
+ @neo.traverse(node,"nodes", {"order" => "breadth first",
19
+ "uniqueness" => "node global",
20
+ "relationships" => {"type"=> "linked", "direction" => "out"},
21
+ "prune evaluator" => {
22
+ "language" => "javascript",
23
+ "body" => "position.startNode().hasProperty('NODE_LEVEL')
24
+ && position.startNode().getProperty('NODE_LEVEL')==5
25
+ && position.startNode().getId()!=#{starting_id};"},
26
+ "return filter" => {
27
+ "language" => "javascript",
28
+ "body" => "position.endNode().hasProperty('NODE_LEVEL') && position.endNode().getProperty('NODE_LEVEL')==5;"}})
29
+ end
30
+
31
+ node1 = create_node(5, "N")
32
+ node2 = create_node(5, "N")
33
+ node3 = create_node(5, "N")
34
+ node4 = create_node(5, "N")
35
+ node5 = create_node(5, "N")
36
+ node6 = create_node(5, "N")
37
+ node7 = create_node(5, "N")
38
+
39
+ node8 = create_other_node("Y")
40
+ node9 = create_other_node("Y")
41
+ node10 = create_other_node("Y")
42
+
43
+ node11 = create_node(6, "N")
44
+ node12 = create_node(7, "N")
45
+ node13 = create_node(8, "N")
46
+
47
+
48
+ @neo.create_relationship("linked", node1, node2)
49
+ @neo.create_relationship("linked", node2, node3)
50
+ @neo.create_relationship("linked", node3, node4)
51
+ @neo.create_relationship("linked", node4, node5)
52
+ @neo.create_relationship("linked", node5, node6)
53
+ @neo.create_relationship("linked", node6, node7)
54
+
55
+ @neo.create_relationship("linked", node2, node8)
56
+ @neo.create_relationship("linked", node3, node9)
57
+ @neo.create_relationship("linked", node4, node10)
58
+
59
+ @neo.create_relationship("linked", node5, node11)
60
+ @neo.create_relationship("linked", node6, node12)
61
+ @neo.create_relationship("linked", node7, node13)
62
+
63
+ puts "The node levels returned are #{get_nodes_by_level(5, node1).map{|n| n["data"]["NODE_LEVEL"]}.join(', ')}"
64
+
65
+ # The node levels returned are 5, 5, 5, 5, 5, 5, 5
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'neography'
3
+
4
+ @neo = Neography::Rest.new
5
+
6
+ def create_node(name, mysql_id)
7
+ @neo.create_node("name" => name, "mysql_id" => mysql_id)
8
+ end
9
+
10
+ def attended(student, school, degree, graduated)
11
+ @neo.create_relationship("attended", student, school, {"degree" => degree, "graduated" => graduated})
12
+ end
13
+
14
+
15
+ def graduated_with_me(student)
16
+ student = student["self"].split('/').last
17
+ student_attended = @neo.get_node_relationships(student)[0]
18
+ graduated = student_attended["data"]["graduated"]
19
+ school = student_attended["end"].split('/').last
20
+
21
+ @neo.traverse(school,"nodes", {"order" => "breadth first",
22
+ "uniqueness" => "node global",
23
+ "relationships" => {"type"=> "attended", "direction" => "in"},
24
+ "return filter" => {
25
+ "language" => "javascript",
26
+ "body" => "position.length() == 1
27
+ && position.endNode().getId() != #{student}
28
+ && position.lastRelationship().getProperty(\"graduated\") == #{graduated};"}})
29
+ end
30
+
31
+ charlie = create_node("Charlie", 1)
32
+ max = create_node("Max", 2)
33
+ peter = create_node("Peter", 3)
34
+ carol = create_node("Carol", 3)
35
+ tom = create_node("Tom", 4)
36
+ jerry = create_node("Jerry", 5)
37
+ larry = create_node("Larry", 6)
38
+
39
+ yale = create_node("Yale", 7)
40
+ harvard = create_node("Harvard", 8)
41
+ rutgers = create_node("Rutgers", 9)
42
+
43
+ attended(charlie,yale,"engineering", 2010)
44
+ attended(max,yale,"mathematics", 2005)
45
+ attended(peter,yale,"biology", 2010)
46
+ attended(carol,yale,"engineering", 2010)
47
+ attended(tom,harvard,"biology", 2008)
48
+ attended(jerry,rutgers,"physics", 2007)
49
+ attended(larry,rutgers,"mathematics", 2010)
50
+
51
+
52
+ puts "Charlie graduated with #{graduated_with_me(charlie).map{|n| n["data"]["name"]}.join(', ')}"
53
+
54
+ # The node levels returned are Peter, Carol
data/lib/neography.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'cgi'
2
+ require 'excon'
3
+ require 'json'
4
+ require 'multi_json'
5
+ require 'logger'
6
+ require 'ostruct'
7
+ require 'os'
8
+ # require 'zip/filesystem'
9
+
10
+ require 'neography/multi_json_parser'
11
+
12
+ require 'neography/version'
13
+
14
+ require 'neography/config'
15
+
16
+ require 'neography/rest'
17
+
18
+ require 'neography/neography'
19
+
20
+ require 'neography/property_container'
21
+ require 'neography/property'
22
+ require 'neography/node_relationship'
23
+ require 'neography/node_path'
24
+ require 'neography/relationship_traverser'
25
+ require 'neography/node_traverser'
26
+ require 'neography/path_traverser'
27
+ require 'neography/equal'
28
+ require 'neography/index'
29
+
30
+ require 'neography/node'
31
+ require 'neography/relationship'
32
+
33
+ require 'neography/railtie' if defined? Rails::Railtie
34
+
35
+ module Neography
36
+
37
+ def self.configure
38
+ yield configuration
39
+ end
40
+
41
+ def self.configuration
42
+ @configuration ||= Config.new
43
+ end
44
+
45
+ end
@@ -0,0 +1,64 @@
1
+ module Neography
2
+ class Config
3
+
4
+ attr_accessor :protocol, :server, :port, :directory,
5
+ :cypher_path, :gremlin_path,
6
+ :log_file, :log_enabled, :logger, :slow_log_threshold,
7
+ :max_threads,
8
+ :authentication, :username, :password,
9
+ :parser, :max_execution_time,
10
+ :proxy, :http_send_timeout, :http_receive_timeout
11
+
12
+ def initialize
13
+ set_defaults
14
+ end
15
+
16
+ def to_hash
17
+ {
18
+ :protocol => @protocol,
19
+ :server => @server,
20
+ :port => @port,
21
+ :directory => @directory,
22
+ :cypher_path => @cypher_path,
23
+ :gremlin_path => @gremlin_path,
24
+ :log_file => @log_file,
25
+ :log_enabled => @log_enabled,
26
+ :logger => @logger,
27
+ :slow_log_threshold => @slow_log_threshold,
28
+ :max_threads => @max_threads,
29
+ :authentication => @authentication,
30
+ :username => @username,
31
+ :password => @password,
32
+ :parser => @parser,
33
+ :max_execution_time => @max_execution_time,
34
+ :proxy => @proxy,
35
+ :http_send_timeout => @http_send_timeout,
36
+ :http_receive_timeout => @http_receive_timeout
37
+ }
38
+ end
39
+
40
+ private
41
+
42
+ def set_defaults
43
+ @protocol = "http://"
44
+ @server = "localhost"
45
+ @port = 7474
46
+ @directory = ""
47
+ @cypher_path = "/cypher"
48
+ @gremlin_path = "/ext/GremlinPlugin/graphdb/execute_script"
49
+ @log_file = "neography.log"
50
+ @log_enabled = false
51
+ @slow_log_threshold = 0
52
+ @max_threads = 20
53
+ @authentication = nil
54
+ @username = nil
55
+ @password = nil
56
+ @parser = MultiJsonParser
57
+ @max_execution_time = 6000
58
+ @proxy = nil
59
+ @http_send_timeout = 1200
60
+ @http_receive_timeout = 1200
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,263 @@
1
+ module Neography
2
+ module WasCreated
3
+ end
4
+ class Connection
5
+ USER_AGENT = "Neography/#{Neography::VERSION}"
6
+ ACTIONS = ["get", "post", "put", "delete"]
7
+
8
+ attr_reader :protocol, :server, :port, :directory,
9
+ :cypher_path, :gremlin_path,
10
+ :log_file, :log_enabled, :logger, :slow_log_threshold,
11
+ :max_threads,
12
+ :authentication, :username, :password,
13
+ :parser, :client,
14
+ :proxy, :http_send_timeout, :http_receive_timeout
15
+
16
+ def initialize(options = ENV['NEO4J_URL'] || {})
17
+ config = merge_configuration(options)
18
+ save_local_configuration(config)
19
+ @client ||= Excon.new(config[:proxy] || "#{@protocol}#{@server}:#{@port}",
20
+ :read_timeout => config[:http_receive_timeout],
21
+ :write_timeout => config[:http_send_timeout],
22
+ :persistent => true,
23
+ :user => config[:username],
24
+ :password => config[:password])
25
+ #authenticate
26
+ end
27
+
28
+ def configure(protocol, server, port, directory)
29
+ @protocol = protocol
30
+ @server = server
31
+ @port = port
32
+ @directory = directory
33
+ end
34
+
35
+ def configuration
36
+ @configuration ||= "#{@protocol}#{@server}:#{@port}#{@directory}"
37
+ end
38
+
39
+ def merge_options(options)
40
+ merged_options = options.merge!(@authentication)
41
+ if merged_options[:headers]
42
+ merged_options[:headers].merge!(@user_agent)
43
+ merged_options[:headers].merge!('X-Stream' => true) unless merged_options[:headers].key?('X-Stream')
44
+ merged_options[:headers].merge!(@max_execution_time)
45
+ end
46
+ merged_options
47
+ end
48
+
49
+ ACTIONS.each do |action|
50
+ define_method(action) do |path, options = {}|
51
+ # This ugly hack is required because internal Batch paths do not start with "/db/data"
52
+ # if somebody has a cleaner solution... pull request please!
53
+ partial_path = path.split("/")
54
+ if partial_path.size > 0
55
+ partial_path = partial_path[1].split("?").first
56
+ end
57
+ reserved = ["node", "relationship", "transaction", "cypher", "propertykeys", "schema", "label", "labels", "batch", "index", "ext"]
58
+ path = "/db/data" + path if reserved.include?(partial_path)
59
+ query_body = options[:body]
60
+ stream = ""
61
+ headers = merge_options(options)[:headers]
62
+ log path, query_body do
63
+ req_params = {:method => action.to_sym, :path => path, :body => query_body, :headers => headers}
64
+ is_streaming = headers && (headers['X-Stream'] == true)
65
+ if is_streaming
66
+ streamer = lambda { |chunk, _, _| stream += chunk }
67
+ req_params.merge!({:persistent => false, :response_block => streamer,
68
+ :read_timeout => 100000000, :write_timeout => 100000000})
69
+ end
70
+ response = @client.request(req_params)
71
+ evaluate_response(response, path, query_body, is_streaming, (partial_path == "batch"), stream)
72
+ end
73
+ end
74
+ end
75
+
76
+ def log(path, body)
77
+ if @log_enabled
78
+ start_time = Time.now
79
+ response = yield
80
+ time = ((Time.now - start_time) * 1000).round(2)
81
+ @logger.info "[Neography::Query] #{path} #{body} [#{time}ms]" if time >= slow_log_threshold
82
+ response
83
+ else
84
+ yield
85
+ end
86
+ end
87
+
88
+ def authenticate(path = nil)
89
+ unless @authentication.empty?
90
+ auth_type = @authentication.keys.first
91
+ @client.set_auth(
92
+ path,
93
+ @authentication[auth_type][:username],
94
+ @authentication[auth_type][:password]
95
+ )
96
+ # Force http client to always send auth credentials without
97
+ # waiting for WWW-Authenticate headers, thus saving one request
98
+ # roundtrip for each URI. Only works for HTTP basic auth.
99
+ @client.www_auth.basic_auth.challenge(configuration) if auth_type == 'basic_auth'
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def merge_configuration(options)
106
+ options = parse_string_options(options) unless options.is_a? Hash
107
+ config = Neography.configuration.to_hash
108
+ config.merge(options)
109
+ end
110
+
111
+ def save_local_configuration(config)
112
+ @protocol = config[:protocol]
113
+ @server = config[:server]
114
+ @port = config[:port]
115
+ @directory = config[:directory]
116
+ @cypher_path = config[:cypher_path]
117
+ @gremlin_path = config[:gremlin_path]
118
+ @log_file = config[:log_file]
119
+ @log_enabled = config[:log_enabled]
120
+ @slow_log_threshold = config[:slow_log_threshold]
121
+ @max_threads = config[:max_threads]
122
+ @parser = config[:parser]
123
+ @logger = config[:logger]
124
+ @proxy = config[:proxy]
125
+
126
+ @max_execution_time = { 'max-execution-time' => config[:max_execution_time] }
127
+ @user_agent = { "User-Agent" => USER_AGENT }
128
+
129
+ @authentication = {}
130
+ if config[:authentication]
131
+ @authentication = {
132
+ "#{config[:authentication]}_auth".to_sym => {
133
+ :username => config[:username],
134
+ :password => config[:password]
135
+ }
136
+ }
137
+ end
138
+
139
+ if @log_enabled
140
+ @logger ||= Logger.new(@log_file)
141
+ end
142
+ end
143
+
144
+ def evaluate_chunk_response(response, result)
145
+ code = response.code
146
+ return_result(code, result)
147
+ end
148
+
149
+ def evaluate_response(response, path, query_body, streaming, batching, stream = nil)
150
+ code = response.status
151
+ parsed = false
152
+ if streaming && batching
153
+ code, body, parsed = handle_batch(stream)
154
+ elsif streaming
155
+ body = (stream || '').force_encoding("UTF-8")
156
+ else
157
+ body = response.body.force_encoding("UTF-8")
158
+ end
159
+ return_result(response, code, body, parsed, path, query_body)
160
+ end
161
+
162
+ def handle_batch(stream)
163
+ code = 200
164
+ body = @parser.json(stream.force_encoding("UTF-8"))
165
+ body.each do |result|
166
+ if result["status"] >= 400
167
+ code = result["status"]
168
+ break
169
+ end
170
+ end
171
+ return code, body, true
172
+ end
173
+
174
+ def return_result(response, code, body, parsed, path, query_body)
175
+ case code
176
+ when 200
177
+ @logger.debug "OK, created #{body}" if @log_enabled
178
+ parsed ? body : @parser.json(body)
179
+ when 201
180
+ @logger.debug "OK, created #{body}" if @log_enabled
181
+ r = parsed ? body : @parser.json(body)
182
+ r.extend(WasCreated)
183
+ r
184
+ when 204
185
+ @logger.debug "OK, no content returned" if @log_enabled
186
+ nil
187
+ when 400..500
188
+ handle_4xx_500_response(response, code, body, path, query_body)
189
+ nil
190
+ end
191
+ end
192
+
193
+ def handle_4xx_500_response(response, code, body, path, query_body)
194
+ index = 0
195
+ request = {:path => path, :body => query_body}
196
+ if body.nil? or body == ""
197
+ parsed_body = {"message" => "No error message returned from server.",
198
+ "stacktrace" => "No stacktrace returned from server." }
199
+ elsif body.is_a? Hash
200
+ parsed_body = body
201
+ elsif body.is_a? Array
202
+ body.each_with_index do |result, idx|
203
+ if result["status"] >= 400
204
+ index = idx
205
+ parsed_body = result["body"] || result
206
+ break
207
+ end
208
+ end
209
+ else
210
+ parsed_body = @parser.json(body)
211
+ end
212
+
213
+ message = parsed_body["message"]
214
+ stacktrace = parsed_body["stacktrace"]
215
+
216
+ @logger.error "#{response.dump} error: #{body}" if @log_enabled
217
+ raise_errors(code, parsed_body["exception"], message, stacktrace, request, index)
218
+ end
219
+
220
+ def raise_errors(code, exception, message, stacktrace, request, index)
221
+ error = nil
222
+ case code
223
+ when 401
224
+ error = UnauthorizedError
225
+ when 409
226
+ error = OperationFailureException
227
+ end
228
+
229
+ error ||= case exception
230
+ when /SyntaxException/ ; SyntaxException
231
+ when /this is not a query/ ; SyntaxException
232
+ when /PropertyValueException/ ; PropertyValueException
233
+ when /BadInputException/ ; BadInputException
234
+ when /NodeNotFoundException/ ; NodeNotFoundException
235
+ when /NoSuchPropertyException/ ; NoSuchPropertyException
236
+ when /RelationshipNotFoundException/ ; RelationshipNotFoundException
237
+ when /ParameterNotFoundException/ ; ParameterNotFoundException
238
+ when /NotFoundException/ ; NotFoundException
239
+ when /UniquePathNotUniqueException/ ; UniquePathNotUniqueException
240
+ when /DeadlockDetectedException/ ; DeadlockDetectedException
241
+ else
242
+ NeographyError
243
+ end
244
+
245
+ raise error.new(message, code, stacktrace, request, index)
246
+ end
247
+
248
+ def parse_string_options(options)
249
+ url = URI.parse(options)
250
+ options = {
251
+ :protocol => url.scheme + "://",
252
+ :server => url.host,
253
+ :port => url.port,
254
+ :directory => url.path,
255
+ :username => url.user,
256
+ :password => url.password
257
+ }
258
+ options[:authentication] = 'basic' unless url.user.nil?
259
+ options
260
+ end
261
+
262
+ end
263
+ end