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.
@@ -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