ashikawa-core 0.11.0 → 0.12.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.
@@ -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
@@ -2,6 +2,6 @@
2
2
  module Ashikawa
3
3
  module Core
4
4
  # Current version of Ashikawa::Core
5
- VERSION = '0.11.0'
5
+ VERSION = '0.12.0'
6
6
  end
7
7
  end
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 { DATABASE.all_databases.length }.by(1)
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
- # System Database for general use in specs
23
- DATABASE = Ashikawa::Core::Database.new do |config|
24
- config.url = "http://localhost:#{PORT}"
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
- if AUTHENTIFICATION_ENABLED
27
- config.username = USERNAME
28
- config.password = PASSWORD
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
- name = "a#{rand.to_s[2, 10]}"
36
+ database_with_name("a#{rand.to_s[2, 10]}")
37
+ end
36
38
 
37
- Ashikawa::Core::Database.new do |config|
38
- config.url = "http://localhost:#{PORT}/_db/#{name}"
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
- if AUTHENTIFICATION_ENABLED
41
- config.username = USERNAME
42
- config.password = PASSWORD
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) { double }
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(:raw_key_options) { double }
29
- let(:key_options) { double }
30
- let(:response) { double }
31
- let(:value) { double }
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) { double }
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(cursor)
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 database, raw_document_collection }
229
+ subject { Ashikawa::Core::Collection.new(database, raw_document_collection) }
227
230
 
228
- let(:document) { double }
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(double, double, { 'quote' => "D'ya have to use s'many cuss words?" })
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) { double }
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
- subject.create_edge(double(id: '1'), double(id: '2'), raw_document)
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