ashikawa-core 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +18 -0
- data/Gemfile +1 -3
- data/README.md +7 -3
- data/Rakefile +1 -0
- data/ashikawa-core.gemspec +9 -11
- data/config/reek.yml +5 -5
- data/lib/ashikawa-core/collection.rb +4 -0
- data/lib/ashikawa-core/configuration.rb +13 -1
- data/lib/ashikawa-core/connection.rb +34 -52
- data/lib/ashikawa-core/database.rb +9 -5
- data/lib/ashikawa-core/document.rb +6 -6
- data/lib/ashikawa-core/faraday_factory.rb +89 -0
- data/lib/ashikawa-core/minimal_logger.rb +64 -0
- data/lib/ashikawa-core/version.rb +1 -1
- data/log/.gitkeep +0 -0
- data/spec/acceptance/basic_spec.rb +2 -2
- data/spec/acceptance/index_spec.rb +5 -0
- data/spec/acceptance/spec_helper.rb +37 -22
- data/spec/unit/collection_spec.rb +34 -33
- data/spec/unit/configuration_spec.rb +8 -8
- data/spec/unit/connection_spec.rb +28 -48
- data/spec/unit/cursor_spec.rb +5 -4
- data/spec/unit/database_spec.rb +8 -16
- data/spec/unit/document_spec.rb +11 -10
- data/spec/unit/edge_spec.rb +12 -11
- data/spec/unit/exception_spec.rb +2 -2
- data/spec/unit/faraday_factory_spec.rb +51 -0
- data/spec/unit/figure_spec.rb +11 -11
- data/spec/unit/index_spec.rb +7 -6
- data/spec/unit/key_options_spec.rb +12 -15
- data/spec/unit/minimal_logger_spec.rb +55 -0
- data/spec/unit/query_spec.rb +53 -53
- data/spec/unit/spec_helper.rb +1 -0
- data/spec/unit/transaction_spec.rb +12 -14
- metadata +44 -53
- data/spec/fixtures/collections/60768679-figures.json +0 -35
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'ashikawa-core/minimal_logger'
|
4
|
+
|
5
|
+
module Ashikawa
|
6
|
+
module Core
|
7
|
+
# Create Faraday objects
|
8
|
+
class FaradayFactory
|
9
|
+
# Defaults for the options of create connection
|
10
|
+
DEFAULTS = {
|
11
|
+
additional_request_middlewares: [],
|
12
|
+
additional_response_middlewares: [],
|
13
|
+
additional_middlewares: [],
|
14
|
+
adapter: Faraday.default_adapter
|
15
|
+
}
|
16
|
+
|
17
|
+
# Request middlewares that will be prepended
|
18
|
+
DEFAULT_REQUEST_MIDDLEWARES = [:json]
|
19
|
+
|
20
|
+
# Response middlewares that will be prepended
|
21
|
+
DEFAULT_RESPONSE_MIDDLEWARES = [:error_response, :json]
|
22
|
+
|
23
|
+
# Create a Faraday object
|
24
|
+
#
|
25
|
+
# @param [String] url The complete URL of the ArangoDB instance
|
26
|
+
# @option options [Object] adapter The Faraday adapter you want to use. Defaults to Default Adapter
|
27
|
+
# @option options [Object] logger The logger you want to use. Defaults to no logger.
|
28
|
+
# @option options [Array] additional_request_middlewares Additional request middlewares
|
29
|
+
# @option options [Array] additional_response_middlewares Additional response middlewares
|
30
|
+
# @option options [Array] additional_middlewares Additional middlewares
|
31
|
+
# @api private
|
32
|
+
# @example Create a FaradayObject with the given configuration
|
33
|
+
# faraday = FaradayFactory.new('http://localhost:8529/_db/mydb/_api', logger: my_logger)
|
34
|
+
def self.create_connection(url, options)
|
35
|
+
options = DEFAULTS.merge(options)
|
36
|
+
faraday = new(
|
37
|
+
options.fetch(:adapter),
|
38
|
+
options.fetch(:additional_request_middlewares),
|
39
|
+
options.fetch(:additional_response_middlewares),
|
40
|
+
options.fetch(:additional_middlewares)
|
41
|
+
)
|
42
|
+
faraday.debug_headers = options.fetch(:debug_headers) { false }
|
43
|
+
faraday.logger = options.fetch(:logger) if options.key?(:logger)
|
44
|
+
faraday.faraday_for(url)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Debug headers to be used by Faraday
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
attr_accessor :debug_headers
|
51
|
+
|
52
|
+
# Create a new Faraday Factory with additional middlewares
|
53
|
+
#
|
54
|
+
# @param [Adapter] adapter The adapter to use
|
55
|
+
# @param [Array] additional_request_middlewares Additional request middlewares
|
56
|
+
# @param [Array] additional_response_middlewares Additional response middlewares
|
57
|
+
# @param [Array] additional_middlewares Additional middlewares
|
58
|
+
# @api private
|
59
|
+
def initialize(adapter, additional_request_middlewares, additional_response_middlewares, additional_middlewares)
|
60
|
+
@adapter = adapter
|
61
|
+
@request_middlewares = DEFAULT_REQUEST_MIDDLEWARES + additional_request_middlewares
|
62
|
+
@response_middlewares = DEFAULT_RESPONSE_MIDDLEWARES + additional_response_middlewares
|
63
|
+
@additional_middlewares = additional_middlewares
|
64
|
+
end
|
65
|
+
|
66
|
+
# Logger to be used by Faraday
|
67
|
+
#
|
68
|
+
# @param [Logger] logger The logger you want to use
|
69
|
+
# @api private
|
70
|
+
def logger=(logger)
|
71
|
+
@response_middlewares << [:minimal_logger, logger, debug_headers: debug_headers]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Create the Faraday for the given URL
|
75
|
+
#
|
76
|
+
# @param [String] url
|
77
|
+
# @return [Faraday]
|
78
|
+
# @api private
|
79
|
+
def faraday_for(url)
|
80
|
+
Faraday.new(url) do |connection|
|
81
|
+
@request_middlewares.each { |middleware| connection.request(*middleware) }
|
82
|
+
@response_middlewares.each { |middleware| connection.response(*middleware) }
|
83
|
+
@additional_middlewares.each { |middleware| connection.use(*middleware) }
|
84
|
+
connection.adapter(*@adapter)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday_middleware'
|
5
|
+
|
6
|
+
module Ashikawa
|
7
|
+
module Core
|
8
|
+
# A more minimal logger as replacement for the very chatty Faraday logger
|
9
|
+
class MinimalLogger < ::Faraday::Response::Middleware
|
10
|
+
# The logger to be used
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
# @return [Logger] The configured logger
|
14
|
+
attr_reader :logger
|
15
|
+
|
16
|
+
# Should HTTP headers be logged
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
# @return [Boolean] If the headers will be logged or not. Defaults to `false`
|
20
|
+
attr_reader :debug_headers
|
21
|
+
|
22
|
+
# Initialize the middleware
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
# @param [Faraday::Middleware] app The middleware to nest this one in
|
26
|
+
# @param [Logger] logger The logger to be used
|
27
|
+
# @option options [Boolean] :debug_headers Should the headers be logged. Defaults to `false`
|
28
|
+
def initialize(app, logger, options = {})
|
29
|
+
super(app)
|
30
|
+
@logger = logger
|
31
|
+
@debug_headers = options.fetch(:debug_headers) { false }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Calls the this middleware and passes on to `super`
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
# @param [Faraday::Env] env The current env object
|
38
|
+
def call(env)
|
39
|
+
logger.debug('request') { "#{env.method.upcase} #{env.url}#{dump_headers(env.request_headers)}" }
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
# The callback when the request was completed
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
# @param [Faraday::Env] env The current env object
|
47
|
+
def on_complete(env)
|
48
|
+
logger.debug('response') { "#{env.method.upcase} #{env.url} #{env.status}#{dump_headers(env.response_headers)}" }
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Creates a one-liner out of headers
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
# @params [Hash] headers A headers hash
|
57
|
+
def dump_headers(headers)
|
58
|
+
" #{headers.map { |field_name, field_value| "#{field_name}: #{field_value.inspect}" }.join(' ')}" if debug_headers
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
::Faraday::Response.register_middleware minimal_logger: -> { MinimalLogger }
|
63
|
+
end
|
64
|
+
end
|
data/log/.gitkeep
ADDED
File without changes
|
@@ -18,7 +18,7 @@ describe 'Basics' do
|
|
18
18
|
it 'should have one more database if a database with random name is created' do
|
19
19
|
expect {
|
20
20
|
database_with_random_name.create
|
21
|
-
}.to change {
|
21
|
+
}.to change { SYSTEM_DATABASE.all_databases.length }.by(1)
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'should create and delete collections' do
|
@@ -197,7 +197,7 @@ describe 'Basics' do
|
|
197
197
|
changed_document.save
|
198
198
|
|
199
199
|
expect(subject['name']).to eq('The Dude')
|
200
|
-
subject.refresh
|
200
|
+
subject.refresh
|
201
201
|
expect(subject['name']).to eq('New Name')
|
202
202
|
end
|
203
203
|
end
|
@@ -6,6 +6,11 @@ describe 'Indices' do
|
|
6
6
|
subject { database['documenttest'] }
|
7
7
|
let(:index) { subject.add_index(:skiplist, on: [:identifier]) }
|
8
8
|
|
9
|
+
it 'should accept a single attribute' do
|
10
|
+
single_attr_index = subject.add_index(:hash, on: :identifier)
|
11
|
+
expect(single_attr_index.on).to eq [:identifier]
|
12
|
+
end
|
13
|
+
|
9
14
|
it 'should be possible to set indices' do
|
10
15
|
index.delete
|
11
16
|
|
@@ -2,44 +2,59 @@
|
|
2
2
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
3
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
4
|
|
5
|
-
RSpec.configure do |config|
|
6
|
-
config.expect_with :rspec do |c|
|
7
|
-
c.syntax = :expect
|
8
|
-
end
|
9
|
-
|
10
|
-
config.mock_with :rspec do |c|
|
11
|
-
c.syntax = :expect
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
5
|
require 'ashikawa-core'
|
6
|
+
require 'logging'
|
16
7
|
|
17
8
|
PORT = ENV.fetch('ARANGODB_PORT', 8529)
|
18
9
|
USERNAME = ENV.fetch('ARANGODB_USERNAME', 'root')
|
19
10
|
PASSWORD = ENV.fetch('ARANGODB_PASSWORD', '')
|
20
11
|
AUTHENTIFICATION_ENABLED = ENV['ARANGODB_DISABLE_AUTHENTIFICATION'] == 'false'
|
21
12
|
|
22
|
-
|
23
|
-
|
24
|
-
|
13
|
+
def database_with_name(database_name = "_system")
|
14
|
+
Ashikawa::Core::Database.new do |config|
|
15
|
+
config.url = "http://localhost:#{PORT}"
|
16
|
+
# Log to a file
|
17
|
+
logger = Logging.logger['ashikawa-logger']
|
18
|
+
logger.add_appenders(
|
19
|
+
Logging.appenders.file('log/acceptance.log')
|
20
|
+
)
|
21
|
+
logger.level = :debug
|
22
|
+
|
23
|
+
config.logger = logger
|
24
|
+
config.database_name = database_name
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
if AUTHENTIFICATION_ENABLED
|
27
|
+
config.username = USERNAME
|
28
|
+
config.password = PASSWORD
|
29
|
+
end
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
33
|
def database_with_random_name
|
33
34
|
# This results in a database that has a valid name according to:
|
34
35
|
# https://www.arangodb.org/manuals/2/NamingConventions.html#DatabaseNames
|
35
|
-
|
36
|
+
database_with_name("a#{rand.to_s[2, 10]}")
|
37
|
+
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
+
# The database for the general specs
|
40
|
+
DATABASE = database_with_name('ashikawa-acceptance-specs')
|
41
|
+
# Some specs require access to the _system database
|
42
|
+
SYSTEM_DATABASE = database_with_name('_system')
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
RSpec.configure do |config|
|
45
|
+
config.expect_with :rspec do |c|
|
46
|
+
c.syntax = :expect
|
47
|
+
end
|
48
|
+
|
49
|
+
config.mock_with :rspec do |c|
|
50
|
+
c.syntax = :expect
|
51
|
+
end
|
52
|
+
|
53
|
+
config.before(:each) do
|
54
|
+
begin
|
55
|
+
DATABASE.create
|
56
|
+
rescue Ashikawa::Core::ClientError
|
43
57
|
end
|
58
|
+
DATABASE.truncate
|
44
59
|
end
|
45
60
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
require 'unit/spec_helper'
|
3
3
|
require 'ashikawa-core/collection'
|
4
|
+
require 'ashikawa-core/database'
|
4
5
|
|
5
6
|
describe Ashikawa::Core::Collection do
|
6
|
-
let(:database) {
|
7
|
+
let(:database) { instance_double('Ashikawa::Core::Database') }
|
7
8
|
let(:raw_document_collection) do
|
8
9
|
{
|
9
10
|
'id' => '60768679',
|
@@ -25,11 +26,10 @@ describe Ashikawa::Core::Collection do
|
|
25
26
|
describe 'an initialized collection' do
|
26
27
|
subject { Ashikawa::Core::Collection.new(database, raw_document_collection) }
|
27
28
|
|
28
|
-
let(:
|
29
|
-
let(:
|
30
|
-
let(:
|
31
|
-
let(:
|
32
|
-
let(:figure) { double }
|
29
|
+
let(:key_options) { instance_double('Ashikawa::Core::KeyOptions') }
|
30
|
+
let(:response) { instance_double('Hash') }
|
31
|
+
let(:value) { double('Value') }
|
32
|
+
let(:figure) { instance_double('Ashikawa::Core::Figure') }
|
33
33
|
|
34
34
|
its(:name) { should eq('example_1') }
|
35
35
|
its(:id) { should eq('60768679') }
|
@@ -164,7 +164,7 @@ describe Ashikawa::Core::Collection do
|
|
164
164
|
end
|
165
165
|
|
166
166
|
describe 'indexes' do
|
167
|
-
let(:index_response) {
|
167
|
+
let(:index_response) { instance_double('Hash') }
|
168
168
|
|
169
169
|
it 'should add a new index' do
|
170
170
|
expect(database).to receive(:send_request)
|
@@ -190,6 +190,18 @@ describe Ashikawa::Core::Collection do
|
|
190
190
|
subject.add_index(:hash, on: [:a, :b], unique: true)
|
191
191
|
end
|
192
192
|
|
193
|
+
it 'should accept a single attribute for indexing' do
|
194
|
+
expect(database).to receive(:send_request)
|
195
|
+
.with('index?collection=60768679', post: {
|
196
|
+
'type' => 'hash', 'fields' => %w(a), 'unique' => true
|
197
|
+
})
|
198
|
+
.and_return(index_response)
|
199
|
+
expect(Ashikawa::Core::Index).to receive(:new)
|
200
|
+
.with(subject, index_response)
|
201
|
+
|
202
|
+
subject.add_index(:hash, on: :a, unique: true)
|
203
|
+
end
|
204
|
+
|
193
205
|
it 'should get an index by ID' do
|
194
206
|
allow(database).to receive(:send_request)
|
195
207
|
.with('index/example_1/168054969')
|
@@ -201,18 +213,9 @@ describe Ashikawa::Core::Collection do
|
|
201
213
|
end
|
202
214
|
|
203
215
|
it 'should get all indexes' do
|
204
|
-
multi_index_response = double
|
205
|
-
allow(multi_index_response).to receive(:map)
|
206
|
-
.and_yield(index_response)
|
207
|
-
|
208
|
-
cursor = double
|
209
|
-
allow(cursor).to receive(:[])
|
210
|
-
.with('indexes')
|
211
|
-
.and_return(multi_index_response)
|
212
|
-
|
213
216
|
allow(database).to receive(:send_request)
|
214
217
|
.with('index?collection=60768679')
|
215
|
-
.and_return(
|
218
|
+
.and_return('indexes' => [index_response])
|
216
219
|
|
217
220
|
expect(Ashikawa::Core::Index).to receive(:new)
|
218
221
|
.with(subject, index_response)
|
@@ -223,12 +226,12 @@ describe Ashikawa::Core::Collection do
|
|
223
226
|
end
|
224
227
|
|
225
228
|
describe 'an initialized document collection' do
|
226
|
-
subject { Ashikawa::Core::Collection.new
|
229
|
+
subject { Ashikawa::Core::Collection.new(database, raw_document_collection) }
|
227
230
|
|
228
|
-
let(:document) {
|
229
|
-
let(:response) { double }
|
230
|
-
let(:raw_document) { double }
|
231
|
-
let(:value) { double }
|
231
|
+
let(:document) { instance_double('Ashikawa::Core::Document') }
|
232
|
+
let(:response) { double('Response') }
|
233
|
+
let(:raw_document) { double('RawDocument') }
|
234
|
+
let(:value) { double('Value') }
|
232
235
|
|
233
236
|
its(:content_type) { should be(:document) }
|
234
237
|
|
@@ -238,7 +241,6 @@ describe Ashikawa::Core::Collection do
|
|
238
241
|
it 'should receive a document by ID via fetch' do
|
239
242
|
expect(database).to receive(:send_request)
|
240
243
|
.with('document/60768679/333', {})
|
241
|
-
.and_return(double)
|
242
244
|
expect(Ashikawa::Core::Document).to receive(:new)
|
243
245
|
|
244
246
|
subject.fetch(key)
|
@@ -247,7 +249,6 @@ describe Ashikawa::Core::Collection do
|
|
247
249
|
it 'should receive a document by ID via []' do
|
248
250
|
expect(database).to receive(:send_request)
|
249
251
|
.with('document/60768679/333', {})
|
250
|
-
.and_return(double)
|
251
252
|
expect(Ashikawa::Core::Document).to receive(:new)
|
252
253
|
|
253
254
|
subject[key]
|
@@ -267,7 +268,6 @@ describe Ashikawa::Core::Collection do
|
|
267
268
|
it 'should receive a document by ID via fetch' do
|
268
269
|
expect(database).to receive(:send_request)
|
269
270
|
.with('document/60768679/333', {})
|
270
|
-
.and_return(double)
|
271
271
|
expect(Ashikawa::Core::Document).to receive(:new)
|
272
272
|
|
273
273
|
subject.fetch(id)
|
@@ -276,7 +276,6 @@ describe Ashikawa::Core::Collection do
|
|
276
276
|
it 'should receive a document by ID via []' do
|
277
277
|
expect(database).to receive(:send_request)
|
278
278
|
.with('document/60768679/333', {})
|
279
|
-
.and_return(double)
|
280
279
|
expect(Ashikawa::Core::Document).to receive(:new)
|
281
280
|
|
282
281
|
subject[id]
|
@@ -302,8 +301,10 @@ describe Ashikawa::Core::Collection do
|
|
302
301
|
end
|
303
302
|
|
304
303
|
it 'should not create a new edge' do
|
304
|
+
from = instance_double('Ashikawa::Core::Document')
|
305
|
+
to = instance_double('Ashikawa::Core::Document')
|
305
306
|
expect do
|
306
|
-
subject.create_edge(
|
307
|
+
subject.create_edge(from, to, { 'quote' => "D'ya have to use s'many cuss words?" })
|
307
308
|
end.to raise_exception(RuntimeError, "Can't create an edge in a document collection")
|
308
309
|
end
|
309
310
|
end
|
@@ -311,16 +312,15 @@ describe Ashikawa::Core::Collection do
|
|
311
312
|
describe 'an initialized edge collection' do
|
312
313
|
subject { Ashikawa::Core::Collection.new database, raw_edge_collection }
|
313
314
|
|
314
|
-
let(:document) {
|
315
|
-
let(:response) { double }
|
316
|
-
let(:raw_document) { double }
|
315
|
+
let(:document) { instance_double('Ashikawa::Core::Document') }
|
316
|
+
let(:response) { double('Response') }
|
317
|
+
let(:raw_document) { double('RawDocument') }
|
317
318
|
|
318
319
|
its(:content_type) { should be(:edge) }
|
319
320
|
|
320
321
|
it 'should receive an edge by ID via fetch' do
|
321
322
|
expect(database).to receive(:send_request)
|
322
323
|
.with('edge/60768679/333', {})
|
323
|
-
.and_return(double)
|
324
324
|
expect(Ashikawa::Core::Edge).to receive(:new)
|
325
325
|
|
326
326
|
subject.fetch(333)
|
@@ -329,7 +329,6 @@ describe Ashikawa::Core::Collection do
|
|
329
329
|
it 'should receive an edge by ID via []' do
|
330
330
|
expect(database).to receive(:send_request)
|
331
331
|
.with('edge/60768679/333', {})
|
332
|
-
.and_return(double)
|
333
332
|
expect(Ashikawa::Core::Edge).to receive(:new)
|
334
333
|
|
335
334
|
subject[333]
|
@@ -350,7 +349,9 @@ describe Ashikawa::Core::Collection do
|
|
350
349
|
.with(database, response, raw_document)
|
351
350
|
.and_return(document)
|
352
351
|
|
353
|
-
|
352
|
+
from = instance_double('Ashikawa::Core::Document', id: 1)
|
353
|
+
to = instance_double('Ashikawa::Core::Document', id: 2)
|
354
|
+
subject.create_edge(from, to, raw_document)
|
354
355
|
end
|
355
356
|
|
356
357
|
it 'should not create a new document' do
|