ashikawa-core 0.9.0 → 0.10.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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +7 -7
- data/CHANGELOG.md +25 -0
- data/CONTRIBUTING.md +4 -4
- data/Gemfile.devtools +32 -16
- data/README.md +9 -11
- data/Rakefile +3 -0
- data/ashikawa-core.gemspec +7 -5
- data/config/flay.yml +2 -2
- data/config/flog.yml +2 -1
- data/config/mutant.yml +14 -1
- data/config/reek.yml +9 -4
- data/config/rubocop.yml +14 -14
- data/lib/ashikawa-core/collection.rb +10 -6
- data/lib/ashikawa-core/configuration.rb +10 -5
- data/lib/ashikawa-core/connection.rb +30 -21
- data/lib/ashikawa-core/cursor.rb +2 -3
- data/lib/ashikawa-core/database.rb +12 -35
- data/lib/ashikawa-core/document.rb +3 -4
- data/lib/ashikawa-core/edge.rb +5 -5
- data/lib/ashikawa-core/error_response.rb +108 -0
- data/lib/ashikawa-core/index.rb +4 -14
- data/lib/ashikawa-core/query.rb +1 -1
- data/lib/ashikawa-core/transaction.rb +1 -16
- data/lib/ashikawa-core/version.rb +1 -1
- data/spec/acceptance/basic_spec.rb +9 -6
- data/spec/acceptance/spec_helper.rb +5 -4
- data/spec/setup/arangodb.sh +12 -17
- data/spec/unit/collection_spec.rb +60 -18
- data/spec/unit/configuration_spec.rb +35 -4
- data/spec/unit/connection_spec.rb +23 -65
- data/spec/unit/cursor_spec.rb +2 -2
- data/spec/unit/database_spec.rb +16 -28
- data/spec/unit/document_spec.rb +3 -3
- data/spec/unit/edge_spec.rb +1 -1
- data/spec/unit/index_spec.rb +1 -1
- data/spec/unit/query_spec.rb +1 -1
- data/spec/unit/spec_helper.rb +3 -2
- data/tasks/adjustments.rake +0 -14
- metadata +26 -13
- data/lib/ashikawa-core/request_preprocessor.rb +0 -50
- data/lib/ashikawa-core/response_preprocessor.rb +0 -160
@@ -4,8 +4,8 @@ require 'faraday'
|
|
4
4
|
require 'null_logger'
|
5
5
|
require 'uri'
|
6
6
|
require 'equalizer'
|
7
|
-
require '
|
8
|
-
require 'ashikawa-core/
|
7
|
+
require 'faraday_middleware'
|
8
|
+
require 'ashikawa-core/error_response'
|
9
9
|
|
10
10
|
module Ashikawa
|
11
11
|
module Core
|
@@ -45,20 +45,34 @@ module Ashikawa
|
|
45
45
|
# connection.port # => 8529
|
46
46
|
def_delegator :@connection, :port
|
47
47
|
|
48
|
+
# The Faraday connection object
|
49
|
+
#
|
50
|
+
# @return [Faraday]
|
51
|
+
# @api public
|
52
|
+
# @example Set additional response middleware
|
53
|
+
# connection = Connection.new('http://localhost:8529')
|
54
|
+
# connection.connection.response :caching
|
55
|
+
attr_reader :connection
|
56
|
+
|
48
57
|
# Initialize a Connection with a given API String
|
49
58
|
#
|
50
59
|
# @param [String] api_string scheme, hostname and port as a String
|
51
|
-
# @option
|
52
|
-
# @option
|
60
|
+
# @option options [Object] adapter The Faraday adapter you want to use. Defaults to Default Adapter
|
61
|
+
# @option options [Object] logger The logger you want to use. Defaults to Null Logger.
|
53
62
|
# @api public
|
54
63
|
# @example Create a new Connection
|
55
64
|
# connection = Connection.new('http://localhost:8529')
|
56
|
-
def initialize(api_string,
|
57
|
-
logger =
|
58
|
-
adapter =
|
65
|
+
def initialize(api_string, options = {})
|
66
|
+
logger = options.fetch(:logger) { NullLogger.instance }
|
67
|
+
adapter = options.fetch(:adapter) { Faraday.default_adapter }
|
68
|
+
|
59
69
|
@connection = Faraday.new("#{api_string}/_api") do |connection|
|
60
|
-
connection.request
|
61
|
-
|
70
|
+
connection.request :json
|
71
|
+
|
72
|
+
connection.response :logger, logger
|
73
|
+
connection.response :error_response
|
74
|
+
connection.response :json
|
75
|
+
|
62
76
|
connection.adapter(*adapter)
|
63
77
|
end
|
64
78
|
end
|
@@ -78,6 +92,8 @@ module Ashikawa
|
|
78
92
|
method = http_verb(params)
|
79
93
|
result = @connection.public_send(method, path, params[method])
|
80
94
|
result.body
|
95
|
+
rescue Faraday::Error::ParsingError
|
96
|
+
raise Ashikawa::Core::JsonError
|
81
97
|
end
|
82
98
|
|
83
99
|
# Checks if authentication for this Connection is active or not
|
@@ -95,18 +111,11 @@ module Ashikawa
|
|
95
111
|
|
96
112
|
# Authenticate with given username and password
|
97
113
|
#
|
98
|
-
# @
|
99
|
-
# @
|
100
|
-
# @
|
101
|
-
|
102
|
-
|
103
|
-
# @example Authenticate with the database for all future requests
|
104
|
-
# connection = Connection.new('http://localhost:8529')
|
105
|
-
# connection.authenticate_with(:username => 'james', :password => 'bond')
|
106
|
-
def authenticate_with(options = {})
|
107
|
-
raise ArgumentError, 'missing username or password' unless options.key? :username and options.key? :password
|
108
|
-
@authentication = @connection.basic_auth(options[:username], options[:password])
|
109
|
-
self
|
114
|
+
# @param [String] username
|
115
|
+
# @param [String] password
|
116
|
+
# @api private
|
117
|
+
def authenticate_with(username, password)
|
118
|
+
@authentication = @connection.basic_auth(username, password)
|
110
119
|
end
|
111
120
|
|
112
121
|
private
|
data/lib/ashikawa-core/cursor.rb
CHANGED
@@ -57,12 +57,11 @@ module Ashikawa
|
|
57
57
|
def each
|
58
58
|
return to_enum(__callee__) unless block_given?
|
59
59
|
|
60
|
-
|
60
|
+
begin
|
61
61
|
@current.each do |raw_document|
|
62
62
|
yield parse_raw_document(raw_document)
|
63
63
|
end
|
64
|
-
|
65
|
-
end
|
64
|
+
end while next_batch
|
66
65
|
nil
|
67
66
|
end
|
68
67
|
|
@@ -53,7 +53,7 @@ module Ashikawa
|
|
53
53
|
# config.logger = my_logger
|
54
54
|
# end
|
55
55
|
def initialize
|
56
|
-
configuration =
|
56
|
+
configuration = Configuration.new
|
57
57
|
yield(configuration)
|
58
58
|
@connection = configuration.connection
|
59
59
|
end
|
@@ -85,16 +85,16 @@ module Ashikawa
|
|
85
85
|
# Create a Collection based on name
|
86
86
|
#
|
87
87
|
# @param [String] collection_identifier The desired name of the collection
|
88
|
-
# @option
|
89
|
-
# @option
|
88
|
+
# @option options [Boolean] :is_volatile Should the collection be volatile? Default is false
|
89
|
+
# @option options [Boolean] :content_type What kind of content should the collection have? Default is :document
|
90
90
|
# @return [Collection]
|
91
91
|
# @api public
|
92
92
|
# @example Create a new, volatile collection
|
93
93
|
# database = Ashikawa::Core::Database.new('http://localhost:8529')
|
94
94
|
# database.create_collection('a', :isVolatile => true) # => #<Collection name="a">
|
95
|
-
def create_collection(collection_identifier,
|
96
|
-
response = send_request('collection', post: translate_params(collection_identifier,
|
97
|
-
|
95
|
+
def create_collection(collection_identifier, options = {})
|
96
|
+
response = send_request('collection', post: translate_params(collection_identifier, options))
|
97
|
+
Collection.new(self, response)
|
98
98
|
end
|
99
99
|
|
100
100
|
# Get or create a Collection based on name or ID
|
@@ -109,13 +109,10 @@ module Ashikawa
|
|
109
109
|
# database = Ashikawa::Core::Database.new('http://localhost:8529')
|
110
110
|
# database['7254820'] # => #<Collection id=7254820>
|
111
111
|
def collection(collection_identifier)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
117
|
-
|
118
|
-
Ashikawa::Core::Collection.new(self, response)
|
112
|
+
response = send_request("collection/#{collection_identifier}")
|
113
|
+
Collection.new(self, response)
|
114
|
+
rescue CollectionNotFoundException
|
115
|
+
create_collection(collection_identifier)
|
119
116
|
end
|
120
117
|
|
121
118
|
alias_method :[], :collection
|
@@ -142,25 +139,7 @@ module Ashikawa
|
|
142
139
|
# transaction = database.create_transaction('function () { return 5; }", :read => ["collection_1'])
|
143
140
|
# transaction.execute #=> 5
|
144
141
|
def create_transaction(action, collections)
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
# Authenticate with given username and password
|
149
|
-
#
|
150
|
-
# @option [String] username
|
151
|
-
# @option [String] password
|
152
|
-
# @return [self]
|
153
|
-
# @raise [ArgumentError] if username or password are missing
|
154
|
-
# @api public
|
155
|
-
# @deprecated Use the initialization block instead
|
156
|
-
# @example Authenticate with the database for all future requests
|
157
|
-
# database = Ashikawa::Core::Database.new do |config|
|
158
|
-
# config.url = 'http://localhost:8529'
|
159
|
-
# end
|
160
|
-
# database.authenticate_with(:username => 'james', :password => 'bond')
|
161
|
-
def authenticate_with(options = {})
|
162
|
-
warn 'authenticate_with is deprecated. Please use the config block instead.'
|
163
|
-
@connection.authenticate_with(options)
|
142
|
+
Transaction.new(self, action, collections)
|
164
143
|
end
|
165
144
|
|
166
145
|
private
|
@@ -171,9 +150,7 @@ module Ashikawa
|
|
171
150
|
# @return [Array]
|
172
151
|
# @api private
|
173
152
|
def parse_raw_collections(raw_collections)
|
174
|
-
raw_collections.map { |collection|
|
175
|
-
Ashikawa::Core::Collection.new(self, collection)
|
176
|
-
}
|
153
|
+
raw_collections.map { |collection| Collection.new(self, collection) }
|
177
154
|
end
|
178
155
|
|
179
156
|
# Translate the key options into the required format
|
@@ -95,7 +95,6 @@ module Ashikawa
|
|
95
95
|
# document = Ashikawa::Core::Document.new(database, raw_document)
|
96
96
|
# document['name'] = 'The dude'
|
97
97
|
def []=(attribute_name, value)
|
98
|
-
check_if_persisted!
|
99
98
|
@content[attribute_name] = value
|
100
99
|
end
|
101
100
|
|
@@ -105,8 +104,8 @@ module Ashikawa
|
|
105
104
|
# @api public
|
106
105
|
# @example Get the hash representation of a document
|
107
106
|
# document = Ashikawa::Core::Document.new(database, raw_document)
|
108
|
-
# document.
|
109
|
-
def
|
107
|
+
# document.to_h #=> { :name => 'Lebowski", :occupation => "Not occupied' }
|
108
|
+
def to_h
|
110
109
|
@content
|
111
110
|
end
|
112
111
|
|
@@ -145,7 +144,7 @@ module Ashikawa
|
|
145
144
|
@id = raw_document['_id'] || :not_persisted
|
146
145
|
@key = raw_document['_key']
|
147
146
|
@revision = raw_document['_rev'] || :not_persisted
|
148
|
-
@content = raw_document.delete_if { |key
|
147
|
+
@content = raw_document.delete_if { |key| key.start_with?('_') }
|
149
148
|
self
|
150
149
|
end
|
151
150
|
|
data/lib/ashikawa-core/edge.rb
CHANGED
@@ -28,16 +28,16 @@ module Ashikawa
|
|
28
28
|
|
29
29
|
# Initialize an Edge with the database and raw data
|
30
30
|
#
|
31
|
-
# @param [Database]
|
31
|
+
# @param [Database] _database
|
32
32
|
# @param [Hash] raw_edge
|
33
|
-
# @param [Hash]
|
33
|
+
# @param [Hash] _additional_data
|
34
34
|
# @api public
|
35
35
|
# @example Create an Edge
|
36
36
|
# document = Ashikawa::Core::Edge.new(database, raw_edge)
|
37
|
-
def initialize(
|
37
|
+
def initialize(_database, raw_edge, _additional_data = {})
|
38
38
|
@from_id = raw_edge['_from']
|
39
39
|
@to_id = raw_edge['_to']
|
40
|
-
super
|
40
|
+
super
|
41
41
|
end
|
42
42
|
|
43
43
|
protected
|
@@ -47,7 +47,7 @@ module Ashikawa
|
|
47
47
|
# @param [Hash] opts Options for this request
|
48
48
|
# @return [Hash] The parsed response from the server
|
49
49
|
# @api private
|
50
|
-
def send_request_for_document(opts
|
50
|
+
def send_request_for_document(opts)
|
51
51
|
@database.send_request("edge/#{@id}", opts)
|
52
52
|
end
|
53
53
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'ashikawa-core/exceptions/client_error'
|
3
|
+
require 'ashikawa-core/exceptions/client_error/resource_not_found'
|
4
|
+
require 'ashikawa-core/exceptions/client_error/resource_not_found/index_not_found'
|
5
|
+
require 'ashikawa-core/exceptions/client_error/resource_not_found/document_not_found'
|
6
|
+
require 'ashikawa-core/exceptions/client_error/resource_not_found/collection_not_found'
|
7
|
+
require 'ashikawa-core/exceptions/client_error/bad_syntax'
|
8
|
+
require 'ashikawa-core/exceptions/client_error/authentication_failed'
|
9
|
+
require 'ashikawa-core/exceptions/server_error'
|
10
|
+
require 'ashikawa-core/exceptions/server_error/json_error'
|
11
|
+
|
12
|
+
module Ashikawa
|
13
|
+
module Core
|
14
|
+
# A response from the server
|
15
|
+
class ErrorResponse < Faraday::Response::Middleware
|
16
|
+
# Status code for a [Bad Request](http://httpstatus.es/400)
|
17
|
+
BadSyntaxStatus = 400
|
18
|
+
|
19
|
+
# Status code for an [Unauthorized Request](http://httpstatus.es/401)
|
20
|
+
AuthenticationFailed = 401
|
21
|
+
|
22
|
+
# Status code for a [Not Found Resource](http://httpstatus.es/404)
|
23
|
+
ResourceNotFoundError = 404
|
24
|
+
|
25
|
+
# All other status codes for client errors
|
26
|
+
ClientErrorStatuses = 405...499
|
27
|
+
|
28
|
+
# All status codes for server errors
|
29
|
+
ServerErrorStatuses = 500...599
|
30
|
+
|
31
|
+
def on_complete(env)
|
32
|
+
@body = env[:body]
|
33
|
+
@url = env[:url]
|
34
|
+
|
35
|
+
case env[:status]
|
36
|
+
when BadSyntaxStatus then bad_syntax
|
37
|
+
when AuthenticationFailed then authentication_failed
|
38
|
+
when ResourceNotFoundError then resource_not_found
|
39
|
+
when ClientErrorStatuses then client_error
|
40
|
+
when ServerErrorStatuses then server_error
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Raise a Bad Syntax Error
|
47
|
+
#
|
48
|
+
# @raise [BadSyntax]
|
49
|
+
# @return nil
|
50
|
+
# @api private
|
51
|
+
def bad_syntax
|
52
|
+
raise BadSyntax
|
53
|
+
end
|
54
|
+
|
55
|
+
# Raise an Authentication Failed Error
|
56
|
+
#
|
57
|
+
# @raise [AuthenticationFailed]
|
58
|
+
# @return nil
|
59
|
+
# @api private
|
60
|
+
def authentication_failed
|
61
|
+
raise Core::AuthenticationFailed
|
62
|
+
end
|
63
|
+
|
64
|
+
# Raise a Client Error for a given body
|
65
|
+
#
|
66
|
+
# @raise [ClientError]
|
67
|
+
# @return nil
|
68
|
+
# @api private
|
69
|
+
def client_error
|
70
|
+
raise ClientError, error
|
71
|
+
end
|
72
|
+
|
73
|
+
# Raise a Server Error for a given body
|
74
|
+
#
|
75
|
+
# @raise [ServerError]
|
76
|
+
# @return nil
|
77
|
+
# @api private
|
78
|
+
def server_error
|
79
|
+
raise ServerError, error
|
80
|
+
end
|
81
|
+
|
82
|
+
# Raise the fitting ResourceNotFoundException
|
83
|
+
#
|
84
|
+
# @raise [DocumentNotFoundException, CollectionNotFoundException, IndexNotFoundException]
|
85
|
+
# @return nil
|
86
|
+
# @api private
|
87
|
+
def resource_not_found
|
88
|
+
raise case @url.path
|
89
|
+
when %r{\A(/_db/[^/]+)?/_api/document} then DocumentNotFoundException
|
90
|
+
when %r{\A(/_db/[^/]+)?/_api/collection} then CollectionNotFoundException
|
91
|
+
when %r{\A(/_db/[^/]+)?/_api/index} then IndexNotFoundException
|
92
|
+
else ResourceNotFound
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Read the error message for the request
|
97
|
+
#
|
98
|
+
# @param [String] The raw body of the request
|
99
|
+
# @return [String] The formatted error message
|
100
|
+
# @api private
|
101
|
+
def error
|
102
|
+
"#{@body['errorNum']}: #{@body["errorMessage"]}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
Faraday.register_middleware :response, error_response: -> { ErrorResponse }
|
107
|
+
end
|
108
|
+
end
|
data/lib/ashikawa-core/index.rb
CHANGED
@@ -53,7 +53,10 @@ module Ashikawa
|
|
53
53
|
# index = Ashikawa::Core::Index.new(collection, raw_index)
|
54
54
|
def initialize(collection, raw_index)
|
55
55
|
@collection = collection
|
56
|
-
|
56
|
+
@id = raw_index['id']
|
57
|
+
@on = convert_to_symbols(raw_index['fields'])
|
58
|
+
@type = raw_index['type'].to_sym
|
59
|
+
@unique = raw_index['unique']
|
57
60
|
end
|
58
61
|
|
59
62
|
# Remove the index from the collection
|
@@ -69,19 +72,6 @@ module Ashikawa
|
|
69
72
|
|
70
73
|
private
|
71
74
|
|
72
|
-
# Parse information returned from the server
|
73
|
-
#
|
74
|
-
# @param [Hash] raw_index
|
75
|
-
# @return self
|
76
|
-
# @api private
|
77
|
-
def parse_raw_index(raw_index)
|
78
|
-
@id = raw_index['id']
|
79
|
-
@on = convert_to_symbols(raw_index['fields']) if raw_index.key?('fields')
|
80
|
-
@type = raw_index['type'].to_sym if raw_index.key?('type')
|
81
|
-
@unique = raw_index['unique']
|
82
|
-
self
|
83
|
-
end
|
84
|
-
|
85
75
|
# Convert all elements of an array to symbols
|
86
76
|
#
|
87
77
|
# @param [Array] arr
|
data/lib/ashikawa-core/query.rb
CHANGED
@@ -63,7 +63,7 @@ module Ashikawa
|
|
63
63
|
# @api public
|
64
64
|
# @example Find all documents in a collection that are red
|
65
65
|
# query = Ashikawa::Core::Query.new(collection)
|
66
|
-
# query.by_example({ 'color' => 'red' },
|
66
|
+
# query.by_example({ 'color' => 'red' }, { :limit => 1 }) #=> #<Cursor id=2444>
|
67
67
|
def by_example(example = {}, options = {})
|
68
68
|
simple_query_request('simple/by-example', { example: example }.merge(options))
|
69
69
|
end
|
@@ -77,7 +77,7 @@ module Ashikawa
|
|
77
77
|
@database = database
|
78
78
|
@request_parameters = {
|
79
79
|
action: action,
|
80
|
-
collections:
|
80
|
+
collections: options,
|
81
81
|
waitForSync: false
|
82
82
|
}
|
83
83
|
end
|
@@ -94,21 +94,6 @@ module Ashikawa
|
|
94
94
|
response = @database.send_request('transaction', post: @request_parameters)
|
95
95
|
response['result']
|
96
96
|
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
# Parse the read and write collections from the options
|
101
|
-
#
|
102
|
-
# @option options [Array<String>] :write The collections you want to write to
|
103
|
-
# @option options [Array<String>] :read The collections you want to read from
|
104
|
-
# @return [Hash]
|
105
|
-
# @api private
|
106
|
-
def parse_options(options)
|
107
|
-
collections = {}
|
108
|
-
collections[:write] = options[:write] if options.key? :write
|
109
|
-
collections[:read] = options[:read] if options.key? :read
|
110
|
-
collections
|
111
|
-
end
|
112
97
|
end
|
113
98
|
end
|
114
99
|
end
|
@@ -146,6 +146,13 @@ describe 'Basics' do
|
|
146
146
|
expect(e.to_id).to eq(b.id)
|
147
147
|
end
|
148
148
|
|
149
|
+
it 'should be possible to get a document by either its key or its ID' do
|
150
|
+
collection = subject['documenttests']
|
151
|
+
document = collection.create_document(name: 'The Dude')
|
152
|
+
|
153
|
+
expect(collection.fetch(document.key)).to eq collection.fetch(document.id)
|
154
|
+
end
|
155
|
+
|
149
156
|
it 'should be possible to get a single attribute by AQL query' do
|
150
157
|
collection = subject['documenttests']
|
151
158
|
collection.truncate!
|
@@ -171,15 +178,11 @@ describe 'Basics' do
|
|
171
178
|
|
172
179
|
it 'should be possible to delete a document' do
|
173
180
|
collection.fetch(document_key).delete
|
174
|
-
expect {
|
175
|
-
collection.fetch(document_key)
|
176
|
-
}.to raise_exception Ashikawa::Core::DocumentNotFoundException
|
181
|
+
expect { collection.fetch(document_key) }.to raise_exception Ashikawa::Core::DocumentNotFoundException
|
177
182
|
end
|
178
183
|
|
179
184
|
it "should not be possible to delete a document that doesn't exist" do
|
180
|
-
expect {
|
181
|
-
collection.fetch(123).delete
|
182
|
-
}.to raise_exception Ashikawa::Core::DocumentNotFoundException
|
185
|
+
expect { collection.fetch(123).delete }.to raise_exception Ashikawa::Core::DocumentNotFoundException
|
183
186
|
end
|
184
187
|
|
185
188
|
it 'should be possible to refresh a document' do
|