ashikawa-core 0.6.0 → 0.7.0

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 (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
+ }