ashikawa-core 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/.rspec +0 -1
  2. data/.travis.yml +2 -0
  3. data/CONTRIBUTING.md +14 -29
  4. data/Gemfile.devtools +9 -10
  5. data/README.md +30 -9
  6. data/Rakefile +1 -1
  7. data/ashikawa-core.gemspec +9 -8
  8. data/config/flay.yml +2 -2
  9. data/config/flog.yml +1 -1
  10. data/config/roodi.yml +4 -5
  11. data/config/site.reek +40 -16
  12. data/config/yardstick.yml +1 -1
  13. data/lib/ashikawa-core/collection.rb +109 -22
  14. data/lib/ashikawa-core/connection.rb +42 -110
  15. data/lib/ashikawa-core/cursor.rb +13 -6
  16. data/lib/ashikawa-core/database.rb +67 -17
  17. data/lib/ashikawa-core/document.rb +41 -10
  18. data/lib/ashikawa-core/edge.rb +50 -0
  19. data/lib/ashikawa-core/exceptions/client_error/bad_syntax.rb +24 -0
  20. data/lib/ashikawa-core/exceptions/{collection_not_found.rb → client_error/resource_not_found/collection_not_found.rb} +3 -1
  21. data/lib/ashikawa-core/exceptions/{document_not_found.rb → client_error/resource_not_found/document_not_found.rb} +3 -1
  22. data/lib/ashikawa-core/exceptions/{index_not_found.rb → client_error/resource_not_found/index_not_found.rb} +3 -1
  23. data/lib/ashikawa-core/exceptions/client_error/resource_not_found.rb +25 -0
  24. data/lib/ashikawa-core/exceptions/client_error.rb +23 -0
  25. data/lib/ashikawa-core/exceptions/server_error/json_error.rb +25 -0
  26. data/lib/ashikawa-core/exceptions/server_error.rb +23 -0
  27. data/lib/ashikawa-core/figure.rb +59 -2
  28. data/lib/ashikawa-core/index.rb +23 -7
  29. data/lib/ashikawa-core/query.rb +10 -10
  30. data/lib/ashikawa-core/request_preprocessor.rb +49 -0
  31. data/lib/ashikawa-core/response_preprocessor.rb +111 -0
  32. data/lib/ashikawa-core/version.rb +1 -1
  33. data/lib/ashikawa-core.rb +0 -1
  34. data/spec/acceptance/basic_spec.rb +61 -22
  35. data/spec/acceptance/index_spec.rb +11 -4
  36. data/spec/acceptance/query_spec.rb +4 -1
  37. data/spec/acceptance/spec_helper.rb +0 -2
  38. data/spec/acceptance_auth/spec_helper.rb +0 -2
  39. data/spec/fixtures/collections/60768679-count.json +13 -0
  40. data/spec/fixtures/collections/60768679-figures.json +35 -0
  41. data/spec/fixtures/collections/60768679-properties-volatile.json +12 -0
  42. data/spec/fixtures/collections/60768679-properties.json +12 -0
  43. data/spec/fixtures/collections/{4588.json → 60768679.json} +2 -2
  44. data/spec/fixtures/collections/all.json +5 -5
  45. data/spec/fixtures/cursor/26011191-2.json +1 -1
  46. data/spec/fixtures/cursor/26011191.json +1 -1
  47. data/spec/fixtures/documents/example_1-137249191.json +6 -0
  48. data/spec/fixtures/documents/new-example_1-137249191.json +6 -0
  49. data/spec/setup/arangodb.sh +1 -1
  50. data/spec/unit/collection_spec.rb +117 -42
  51. data/spec/unit/connection_spec.rb +161 -61
  52. data/spec/unit/cursor_spec.rb +39 -12
  53. data/spec/unit/database_spec.rb +119 -19
  54. data/spec/unit/document_spec.rb +4 -2
  55. data/spec/unit/edge_spec.rb +54 -0
  56. data/spec/unit/exception_spec.rb +36 -8
  57. data/spec/unit/figure_spec.rb +37 -11
  58. data/spec/unit/index_spec.rb +1 -1
  59. data/spec/unit/query_spec.rb +18 -18
  60. data/spec/unit/spec_helper.rb +4 -13
  61. data/tasks/adjustments.rake +3 -2
  62. metadata +59 -32
  63. data/lib/ashikawa-core/exceptions/unknown_path.rb +0 -15
  64. data/spec/fixtures/collections/4590-properties.json +0 -9
  65. data/spec/fixtures/collections/4590.json +0 -8
  66. data/spec/fixtures/collections/73482-figures.json +0 -23
  67. data/spec/fixtures/documents/4590-333.json +0 -5
  68. data/spec/fixtures/documents/new-4590-333.json +0 -5
@@ -0,0 +1,49 @@
1
+ require "faraday"
2
+ require "multi_json"
3
+
4
+ module Ashikawa
5
+ module Core
6
+ # Preprocessor for Faraday Requests
7
+ class RequestPreprocessor < Faraday::Middleware
8
+ # Create a new Request Preprocessor
9
+ #
10
+ # @param [Object] app Faraday internal
11
+ # @param [Object] logger The object you want to log to
12
+ # @return [RequestPreprocessor]
13
+ # @api private
14
+ def initialize(app, logger)
15
+ @app = app
16
+ @logger = logger
17
+ end
18
+
19
+ # Process a Request
20
+ #
21
+ # @param [Hash] env Environment info
22
+ # @return [Object]
23
+ # @api private
24
+ def call(env)
25
+ body = env[:body]
26
+ env[:body] = MultiJson.dump(body) if body
27
+ log(env[:method], env[:url], body)
28
+ @app.call(env)
29
+ end
30
+
31
+ private
32
+
33
+ # Log a Request
34
+ #
35
+ # @param [Symbol] method
36
+ # @param [String] url
37
+ # @param [String] body
38
+ # @return [nil]
39
+ # @api private
40
+ def log(method, url, body)
41
+ @logger.info("#{method.upcase} #{url} #{body}")
42
+ nil
43
+ end
44
+ end
45
+
46
+ Faraday.register_middleware :request,
47
+ :ashikawa_request => lambda { RequestPreprocessor}
48
+ end
49
+ end
@@ -0,0 +1,111 @@
1
+ require "faraday"
2
+ require "multi_json"
3
+ require "ashikawa-core/exceptions/client_error"
4
+ require "ashikawa-core/exceptions/client_error/resource_not_found"
5
+ require "ashikawa-core/exceptions/client_error/resource_not_found/index_not_found"
6
+ require "ashikawa-core/exceptions/client_error/resource_not_found/document_not_found"
7
+ require "ashikawa-core/exceptions/client_error/resource_not_found/collection_not_found"
8
+ require "ashikawa-core/exceptions/client_error/bad_syntax"
9
+ require "ashikawa-core/exceptions/server_error"
10
+ require "ashikawa-core/exceptions/server_error/json_error"
11
+
12
+ module Ashikawa
13
+ module Core
14
+ # Preprocessor for Faraday Requests
15
+ class ResponsePreprocessor < Faraday::Middleware
16
+ ClientErrorStatuses = 400...499
17
+ ServerErrorStatuses = 500...599
18
+ BadSyntaxStatus = 400
19
+ ResourceNotFoundErrorError = 404
20
+
21
+ # Create a new Response Preprocessor
22
+ #
23
+ # @param [Object] app Faraday internal
24
+ # @param [Object] logger The object you want to log to
25
+ # @return [ResponsePreprocessor]
26
+ # @api private
27
+ def initialize(app, logger)
28
+ @app = app
29
+ @logger = logger
30
+ end
31
+
32
+ # Process a Response
33
+ #
34
+ # @param [Hash] env Environment info
35
+ # @return [Object]
36
+ # @api private
37
+ def call(env)
38
+ @app.call(env).on_complete do
39
+ log(env)
40
+ handle_status(env)
41
+ env[:body] = parse_json(env)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ # Raise the fitting ResourceNotFoundException
48
+ #
49
+ # @raise [DocumentNotFoundException, CollectionNotFoundException, IndexNotFoundException]
50
+ # @return nil
51
+ # @api private
52
+ def resource_not_found_for(env)
53
+ raise case env[:url].path
54
+ when /\A\/_api\/document/ then Ashikawa::Core::DocumentNotFoundException
55
+ when /\A\/_api\/collection/ then Ashikawa::Core::CollectionNotFoundException
56
+ when /\A\/_api\/index/ then Ashikawa::Core::IndexNotFoundException
57
+ else Ashikawa::Core::ResourceNotFound
58
+ end
59
+ end
60
+
61
+ # Parse the JSON
62
+ #
63
+ # @param [Hash] env Environment info
64
+ # @return [Hash] The parsed body
65
+ # @api private
66
+ def parse_json(env)
67
+ raise MultiJson::LoadError unless json_content_type?(env[:response_headers]["content-type"])
68
+ MultiJson.load(env[:body])
69
+ rescue MultiJson::LoadError
70
+ raise Ashikawa::Core::JsonError
71
+ end
72
+
73
+ # Check if the Content Type is JSON
74
+ #
75
+ # @param [String] content_type
76
+ # @return [Boolean]
77
+ # @api private
78
+ def json_content_type?(content_type)
79
+ content_type == "application/json; charset=utf-8"
80
+ end
81
+
82
+ # Handle the status code
83
+ #
84
+ # @param [Hash] env Environment info
85
+ # @return [nil]
86
+ # @api private
87
+ def handle_status(env)
88
+ status = env[:status]
89
+ case status
90
+ when BadSyntaxStatus then raise Ashikawa::Core::BadSyntax
91
+ when ResourceNotFoundErrorError then raise resource_not_found_for(env)
92
+ when ClientErrorStatuses then raise Ashikawa::Core::ClientError, status
93
+ when ServerErrorStatuses then raise Ashikawa::Core::ServerError, status
94
+ end
95
+ end
96
+
97
+ # Log a Request
98
+ #
99
+ # @param [Hash] env Environment info
100
+ # @return [nil]
101
+ # @api private
102
+ def log(env)
103
+ @logger.info("#{env[:status]} #{env[:body]}")
104
+ nil
105
+ end
106
+ end
107
+
108
+ Faraday.register_middleware :response,
109
+ :ashikawa_response => lambda { ResponsePreprocessor}
110
+ end
111
+ end
@@ -1,6 +1,6 @@
1
1
  module Ashikawa
2
2
  module Core
3
3
  # Current version of Ashikawa::Core
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
6
6
  end
data/lib/ashikawa-core.rb CHANGED
@@ -3,7 +3,6 @@ require "ashikawa-core/connection"
3
3
  require "ashikawa-core/database"
4
4
  require "ashikawa-core/document"
5
5
  require "ashikawa-core/version"
6
- require "ashikawa-core/exceptions/document_not_found"
7
6
 
8
7
  # Ashikawa is a project dedicated to connect Ruby and ArangoDB
9
8
  module Ashikawa
@@ -1,10 +1,12 @@
1
1
  require 'acceptance/spec_helper'
2
2
 
3
3
  describe "Basics" do
4
- subject { ARANGO_HOST }
5
-
6
- describe "initialized database" do
7
- subject { Ashikawa::Core::Database.new ARANGO_HOST }
4
+ describe "for an initialized database" do
5
+ subject {
6
+ Ashikawa::Core::Database.new do |config|
7
+ config.url = ARANGO_HOST
8
+ end
9
+ }
8
10
 
9
11
  after :each do
10
12
  subject.collections.each { |collection| collection.delete }
@@ -17,7 +19,7 @@ describe "Basics" do
17
19
  end
18
20
 
19
21
  it "should create and delete collections" do
20
- subject.collections.should == []
22
+ subject.collections.each { |collection| collection.delete }
21
23
  subject["collection_1"]
22
24
  subject["collection_2"]
23
25
  subject["collection_3"]
@@ -26,6 +28,21 @@ describe "Basics" do
26
28
  subject.collections.length.should == 2
27
29
  end
28
30
 
31
+ it "should create a non-volatile collection by default" do
32
+ subject.create_collection("nonvolatile_collection")
33
+ subject["nonvolatile_collection"].volatile?.should be_false
34
+ end
35
+
36
+ it "should create a volatile collection" do
37
+ subject.create_collection("volatile_collection", :is_volatile => true)
38
+ subject["volatile_collection"].volatile?.should be_true
39
+ end
40
+
41
+ it "should be possible to create an edge collection" do
42
+ subject.create_collection("edge_collection", :content_type => :edge)
43
+ subject["edge_collection"].content_type.should == :edge
44
+ end
45
+
29
46
  it "should be possible to change the name of a collection" do
30
47
  my_collection = subject["test_collection"]
31
48
  my_collection.name.should == "test_collection"
@@ -49,11 +66,16 @@ describe "Basics" do
49
66
 
50
67
  it "should be possible to get figures" do
51
68
  my_collection = subject["test_collection"]
52
- my_collection.figure.datafiles_count.class.should == Fixnum
53
69
  my_collection.figure.alive_size.class.should == Fixnum
54
70
  my_collection.figure.alive_count.class.should == Fixnum
55
71
  my_collection.figure.dead_size.class.should == Fixnum
56
72
  my_collection.figure.dead_count.class.should == Fixnum
73
+ my_collection.figure.dead_deletion.class.should == Fixnum
74
+ my_collection.figure.datafiles_count.class.should == Fixnum
75
+ my_collection.figure.datafiles_file_size.class.should == Fixnum
76
+ my_collection.figure.journals_count.class.should == Fixnum
77
+ my_collection.figure.journals_file_size.class.should == Fixnum
78
+ my_collection.figure.shapes_count.class.should == Fixnum
57
79
  end
58
80
 
59
81
  it "should change and receive information about waiting for sync" do
@@ -77,44 +99,61 @@ describe "Basics" do
77
99
  it "should be possible to update the attributes of a document" do
78
100
  collection = subject["documenttests"]
79
101
 
80
- document = collection.create :name => "The Dude", :bowling => true
81
- document_id = document.id
102
+ document = collection.create_document(:name => "The Dude", :bowling => true)
103
+ document_key = document.key
82
104
  document["name"] = "Other Dude"
83
105
  document.save
84
106
 
85
- collection[document_id]["name"].should == "Other Dude"
107
+ collection[document_key]["name"].should == "Other Dude"
86
108
  end
87
109
 
88
110
  it "should be possible to access and create documents from a collection" do
89
111
  collection = subject["documenttests"]
90
112
 
91
- document = collection.create :name => "The Dude", :bowling => true
92
- document_id = document.id
93
- collection[document_id]["name"].should == "The Dude"
113
+ document = collection.create_document(:name => "The Dude", :bowling => true)
114
+ document_key = document.key
115
+ collection[document_key]["name"].should == "The Dude"
116
+
117
+ collection[document_key] = { :name => "Other Dude", :bowling => true }
118
+ collection[document_key]["name"].should == "Other Dude"
119
+ end
120
+
121
+ it "should be possible to create an edge between two documents" do
122
+ nodes = subject.create_collection("nodecollection")
123
+ edges = subject.create_collection("edgecollection", :content_type => :edge)
124
+
125
+ a = nodes.create_document({:name => "a"})
126
+ b = nodes.create_document({:name => "b"})
127
+ e = edges.create_edge(a, b, {:name => "fance_edge"})
94
128
 
95
- collection[document_id] = { :name => "Other Dude", :bowling => true }
96
- collection[document_id]["name"].should == "Other Dude"
129
+ e = edges[e.key]
130
+ e.from_id.should == a.id
131
+ e.to_id.should == b.id
97
132
  end
98
133
  end
99
134
 
100
- describe "created document" do
101
- let(:database) { Ashikawa::Core::Database.new ARANGO_HOST }
135
+ describe "for a created document" do
136
+ let(:database) {
137
+ Ashikawa::Core::Database.new do |config|
138
+ config.url = ARANGO_HOST
139
+ end
140
+ }
102
141
  let(:collection) { database["documenttests"] }
103
- subject { collection.create :name => "The Dude" }
104
- let(:document_id) { subject.id }
142
+ subject { collection.create_document(:name => "The Dude") }
143
+ let(:document_key) { subject.key }
105
144
 
106
145
  it "should be possible to manipulate documents and save them" do
107
146
  subject["name"] = "Jeffrey Lebowski"
108
147
  subject["name"].should == "Jeffrey Lebowski"
109
- collection[document_id]["name"].should == "The Dude"
148
+ collection[document_key]["name"].should == "The Dude"
110
149
  subject.save
111
- collection[document_id]["name"].should == "Jeffrey Lebowski"
150
+ collection[document_key]["name"].should == "Jeffrey Lebowski"
112
151
  end
113
152
 
114
153
  it "should be possible to delete a document" do
115
- collection[document_id].delete
154
+ collection[document_key].delete
116
155
  expect {
117
- collection[document_id]
156
+ collection[document_key]
118
157
  }.to raise_exception Ashikawa::Core::DocumentNotFoundException
119
158
  end
120
159
 
@@ -1,9 +1,12 @@
1
1
  require 'acceptance/spec_helper'
2
2
 
3
3
  describe "Indices" do
4
- let(:database) { Ashikawa::Core::Database.new ARANGO_HOST }
4
+ let(:database) { Ashikawa::Core::Database.new do |config|
5
+ config.url = ARANGO_HOST
6
+ end
7
+ }
5
8
  subject { database["documenttest"] }
6
- let(:index) { subject.add_index :skiplist, :on => [:identifier] }
9
+ let(:index) { subject.add_index(:skiplist, :on => [:identifier]) }
7
10
 
8
11
  it "should be possible to set indices" do
9
12
  index.delete
@@ -14,15 +17,19 @@ describe "Indices" do
14
17
  end
15
18
 
16
19
  it "should be possible to get an index by ID" do
17
- subject.index(index.id).id.should == index.id
20
+ # This is temporary until Index has a key
21
+ index_key = index.id.split('/')[1]
22
+
23
+ subject.index(index_key).id.should == index.id
18
24
  subject.indices[0].class.should == Ashikawa::Core::Index
19
25
  end
20
26
 
21
27
  it "should be possible to remove indices" do
22
- pending "Delete doesn't work at random"
28
+ pending "See Bug #34"
23
29
 
24
30
  expect {
25
31
  index.delete
32
+ sleep(1) # from time to time it may fail because of threading
26
33
  }.to change { subject.indices.length }.by(-1)
27
34
  end
28
35
  end
@@ -1,7 +1,10 @@
1
1
  require 'acceptance/spec_helper'
2
2
 
3
3
  describe "Queries" do
4
- let(:database) { Ashikawa::Core::Database.new ARANGO_HOST }
4
+ let(:database) { Ashikawa::Core::Database.new do |config|
5
+ config.url = ARANGO_HOST
6
+ end
7
+ }
5
8
  let(:collection) { database["my_collection"] }
6
9
 
7
10
  describe "AQL query via the database" do
@@ -1,8 +1,6 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
3
 
4
- require "rest-client"
5
- require "json"
6
4
  require "ashikawa-core"
7
5
 
8
6
  ARANGO_HOST = "http://localhost:8529"
@@ -1,8 +1,6 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
3
 
4
- require "rest-client"
5
- require "json"
6
4
  require "ashikawa-core"
7
5
 
8
6
  ARANGO_HOST = "http://localhost:8529"
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "6076879",
3
+ "name": "example_1",
4
+ "waitForSync": false,
5
+ "journalSize": 33554432,
6
+ "isVolatile": false,
7
+ "isSystem": false,
8
+ "count": 54,
9
+ "status": 3,
10
+ "type": 2,
11
+ "error": false,
12
+ "code": 200
13
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "id": "60768679",
3
+ "name": "example_1",
4
+ "waitForSync": true,
5
+ "journalSize": 134217728,
6
+ "isVolatile": false,
7
+ "isSystem": false,
8
+ "count": 0,
9
+ "figures": {
10
+ "alive": {
11
+ "count": 0,
12
+ "size": 0
13
+ },
14
+ "dead": {
15
+ "count": 149,
16
+ "size": 2384,
17
+ "deletion": 0
18
+ },
19
+ "datafiles": {
20
+ "count": 1,
21
+ "fileSize": 0
22
+ },
23
+ "journals": {
24
+ "count": 1,
25
+ "fileSize": 33554432
26
+ },
27
+ "shapes": {
28
+ "count": 6
29
+ }
30
+ },
31
+ "status": 3,
32
+ "type": 2,
33
+ "error": false,
34
+ "code": 200
35
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "60768679",
3
+ "name": "example_1",
4
+ "waitForSync": true,
5
+ "journalSize": 134217728,
6
+ "isVolatile": true,
7
+ "isSystem": false,
8
+ "status": 3,
9
+ "type": 2,
10
+ "error": false,
11
+ "code": 200
12
+ }