ashikawa-core 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +3 -3
  4. data/CHANGELOG.md +49 -0
  5. data/Gemfile +3 -2
  6. data/Gemfile.devtools +14 -22
  7. data/Guardfile +3 -2
  8. data/README.md +37 -6
  9. data/Rakefile +2 -1
  10. data/ashikawa-core.gemspec +2 -2
  11. data/cache/Mac_applications +1 -0
  12. data/config/devtools.yml +5 -0
  13. data/config/flay.yml +1 -1
  14. data/config/flog.yml +1 -2
  15. data/config/reek.yml +1 -1
  16. data/config/rubocop.yml +23 -25
  17. data/lib/ashikawa-core.rb +6 -5
  18. data/lib/ashikawa-core/collection.rb +142 -165
  19. data/lib/ashikawa-core/configuration.rb +41 -2
  20. data/lib/ashikawa-core/connection.rb +17 -16
  21. data/lib/ashikawa-core/cursor.rb +18 -12
  22. data/lib/ashikawa-core/database.rb +69 -59
  23. data/lib/ashikawa-core/document.rb +22 -20
  24. data/lib/ashikawa-core/edge.rb +8 -6
  25. data/lib/ashikawa-core/exceptions/client_error.rb +1 -0
  26. data/lib/ashikawa-core/exceptions/client_error/authentication_failed.rb +25 -0
  27. data/lib/ashikawa-core/exceptions/client_error/bad_syntax.rb +3 -2
  28. data/lib/ashikawa-core/exceptions/client_error/resource_not_found.rb +3 -2
  29. data/lib/ashikawa-core/exceptions/client_error/resource_not_found/collection_not_found.rb +3 -2
  30. data/lib/ashikawa-core/exceptions/client_error/resource_not_found/document_not_found.rb +3 -2
  31. data/lib/ashikawa-core/exceptions/client_error/resource_not_found/index_not_found.rb +3 -2
  32. data/lib/ashikawa-core/exceptions/no_collection_provided.rb +1 -0
  33. data/lib/ashikawa-core/exceptions/server_error.rb +1 -0
  34. data/lib/ashikawa-core/exceptions/server_error/json_error.rb +3 -2
  35. data/lib/ashikawa-core/figure.rb +18 -17
  36. data/lib/ashikawa-core/index.rb +15 -5
  37. data/lib/ashikawa-core/key_options.rb +5 -4
  38. data/lib/ashikawa-core/query.rb +38 -27
  39. data/lib/ashikawa-core/request_preprocessor.rb +4 -3
  40. data/lib/ashikawa-core/response_preprocessor.rb +64 -24
  41. data/lib/ashikawa-core/status.rb +1 -0
  42. data/lib/ashikawa-core/transaction.rb +12 -11
  43. data/lib/ashikawa-core/version.rb +2 -1
  44. data/spec/acceptance/basic_spec.rb +117 -116
  45. data/spec/acceptance/index_spec.rb +18 -15
  46. data/spec/acceptance/query_spec.rb +61 -64
  47. data/spec/acceptance/spec_helper.rb +26 -3
  48. data/spec/acceptance/transactions_spec.rb +12 -16
  49. data/spec/setup/arangodb.sh +2 -2
  50. data/spec/unit/collection_spec.rb +224 -242
  51. data/spec/unit/configuration_spec.rb +64 -0
  52. data/spec/unit/connection_spec.rb +121 -111
  53. data/spec/unit/cursor_spec.rb +78 -65
  54. data/spec/unit/database_spec.rb +112 -163
  55. data/spec/unit/document_spec.rb +74 -70
  56. data/spec/unit/edge_spec.rb +45 -33
  57. data/spec/unit/exception_spec.rb +28 -38
  58. data/spec/unit/figure_spec.rb +44 -47
  59. data/spec/unit/index_spec.rb +27 -24
  60. data/spec/unit/key_options_spec.rb +19 -17
  61. data/spec/unit/query_spec.rb +186 -135
  62. data/spec/unit/spec_helper.rb +14 -3
  63. data/spec/unit/status_spec.rb +37 -37
  64. data/spec/unit/transaction_spec.rb +71 -74
  65. data/tasks/adjustments.rake +10 -34
  66. metadata +11 -13
  67. data/spec/acceptance/arango_helper.rb +0 -27
  68. data/spec/acceptance_auth/arango_helper.rb +0 -30
  69. data/spec/acceptance_auth/auth_spec.rb +0 -40
  70. data/spec/acceptance_auth/spec_helper.rb +0 -6
@@ -1,7 +1,10 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'ashikawa-core/connection'
3
+
1
4
  module Ashikawa
2
5
  module Core
3
6
  # Configuration of Ashikawa::Core
4
- class Configuration < Struct.new(:url, :connection, :logger, :adapter)
7
+ class Configuration
5
8
  # The URL of the database instance
6
9
  # @api private
7
10
  # @return String
@@ -10,7 +13,7 @@ module Ashikawa
10
13
  # The Connection object
11
14
  # @api private
12
15
  # @return Connection
13
- attr_accessor :connection
16
+ attr_writer :connection
14
17
 
15
18
  # The logger instance
16
19
  # @api private
@@ -21,6 +24,42 @@ module Ashikawa
21
24
  # @api private
22
25
  # @return Object
23
26
  attr_accessor :adapter
27
+
28
+ # The username for authentication
29
+ # @api private
30
+ # @return String
31
+ attr_accessor :username
32
+
33
+ # The password for authentication
34
+ # @api private
35
+ # @return String
36
+ attr_accessor :password
37
+
38
+ # The Connection object
39
+ # @api private
40
+ # @return Connection
41
+ def connection
42
+ @connection = @connection || setup_new_connection
43
+ @connection.authenticate_with(username: username, password: password) if username && password
44
+ @connection
45
+ end
46
+
47
+ private
48
+
49
+ # Setup the connection object
50
+ #
51
+ # @param [String] url
52
+ # @param [Logger] logger
53
+ # @param [Adapter] adapter
54
+ # @return [Connection]
55
+ # @api private
56
+ def setup_new_connection
57
+ raise(ArgumentError, 'Please provide either an url or a connection to setup the database') if url.nil?
58
+ Ashikawa::Core::Connection.new(url, {
59
+ logger: logger,
60
+ adapter: adapter
61
+ })
62
+ end
24
63
  end
25
64
  end
26
65
  end
@@ -1,10 +1,11 @@
1
- require "forwardable"
2
- require "faraday"
3
- require "null_logger"
4
- require "uri"
5
- require "equalizer"
6
- require "ashikawa-core/request_preprocessor"
7
- require "ashikawa-core/response_preprocessor"
1
+ # -*- encoding : utf-8 -*-
2
+ require 'forwardable'
3
+ require 'faraday'
4
+ require 'null_logger'
5
+ require 'uri'
6
+ require 'equalizer'
7
+ require 'ashikawa-core/request_preprocessor'
8
+ require 'ashikawa-core/response_preprocessor'
8
9
 
9
10
  module Ashikawa
10
11
  module Core
@@ -20,8 +21,8 @@ module Ashikawa
20
21
  # @return [String]
21
22
  # @api public
22
23
  # @example Get the host part of the connection
23
- # connection = Connection.new("http://localhost:8529")
24
- # connection.host # => "localhost"
24
+ # connection = Connection.new('http://localhost:8529')
25
+ # connection.host # => 'localhost'
25
26
  def_delegator :@connection, :host
26
27
 
27
28
  # The scheme of the connection
@@ -30,8 +31,8 @@ module Ashikawa
30
31
  # @return [String]
31
32
  # @api public
32
33
  # @example Get the scheme of the connection
33
- # connection = Connection.new("http://localhost:8529")
34
- # connection.scheme # => "http"
34
+ # connection = Connection.new('http://localhost:8529')
35
+ # connection.scheme # => 'http'
35
36
  def_delegator :@connection, :scheme
36
37
 
37
38
  # The port of the connection
@@ -40,7 +41,7 @@ module Ashikawa
40
41
  # @return [Fixnum]
41
42
  # @api public
42
43
  # @example Get the port of the connection
43
- # connection = Connection.new("http://localhost:8529")
44
+ # connection = Connection.new('http://localhost:8529')
44
45
  # connection.port # => 8529
45
46
  def_delegator :@connection, :port
46
47
 
@@ -51,7 +52,7 @@ module Ashikawa
51
52
  # @option opts [Object] logger The logger you want to use. Defaults to Null Logger.
52
53
  # @api public
53
54
  # @example Create a new Connection
54
- # connection = Connection.new("http://localhost:8529")
55
+ # connection = Connection.new('http://localhost:8529')
55
56
  def initialize(api_string, opts = {})
56
57
  logger = opts[:logger] || NullLogger.instance
57
58
  adapter = opts[:adapter] || Faraday.default_adapter
@@ -84,7 +85,7 @@ module Ashikawa
84
85
  # @return [Boolean]
85
86
  # @api public
86
87
  # @example Is authentication activated for this connection?
87
- # connection = Connection.new("http://localhost:8529")
88
+ # connection = Connection.new('http://localhost:8529')
88
89
  # connection.authentication? #=> false
89
90
  # connection.authenticate_with(:username => 'james', :password => 'bond')
90
91
  # connection.authentication? #=> true
@@ -100,7 +101,7 @@ module Ashikawa
100
101
  # @raise [ArgumentError] if username or password are missing
101
102
  # @api public
102
103
  # @example Authenticate with the database for all future requests
103
- # connection = Connection.new("http://localhost:8529")
104
+ # connection = Connection.new('http://localhost:8529')
104
105
  # connection.authenticate_with(:username => 'james', :password => 'bond')
105
106
  def authenticate_with(options = {})
106
107
  raise ArgumentError, 'missing username or password' unless options.key? :username and options.key? :password
@@ -117,7 +118,7 @@ module Ashikawa
117
118
  # @api private
118
119
  def http_verb(params)
119
120
  [:post, :put, :delete].detect { |method_name|
120
- params.has_key?(method_name)
121
+ params.key?(method_name)
121
122
  } || :get
122
123
  end
123
124
  end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'ashikawa-core/document'
2
3
  require 'ashikawa-core/edge'
3
4
  require 'equalizer'
@@ -16,7 +17,7 @@ module Ashikawa
16
17
  # @api public
17
18
  # @example Get the id of the cursor
18
19
  # cursor = Ashikawa::Core::Cursor.new(database, raw_cursor)
19
- # cursor.id #=> "1337"
20
+ # cursor.id #=> '1337'
20
21
  attr_reader :id
21
22
 
22
23
  # The number of documents
@@ -39,9 +40,9 @@ module Ashikawa
39
40
  parse_raw_cursor(raw_cursor)
40
41
  end
41
42
 
42
- # Iterate over the documents found by the cursor
43
+ # Iterate over the result
43
44
  #
44
- # @yield [document]
45
+ # @yield [Object] A Document, An Edge or a Raw Object
45
46
  # @return [nil, Enumerator] If no block is given, an Enumerator is returned
46
47
  # @api public
47
48
  # @example Print all documents
@@ -56,11 +57,12 @@ module Ashikawa
56
57
  def each
57
58
  return to_enum(__callee__) unless block_given?
58
59
 
59
- begin
60
+ loop do
60
61
  @current.each do |raw_document|
61
62
  yield parse_raw_document(raw_document)
62
63
  end
63
- end while next_batch
64
+ break unless next_batch
65
+ end
64
66
  nil
65
67
  end
66
68
 
@@ -71,18 +73,22 @@ module Ashikawa
71
73
  # cursor = Ashikawa::Core::Cursor.new(database, raw_cursor)
72
74
  # cursor.delete
73
75
  def delete
74
- @database.send_request("cursor/#{@id}", :delete => {})
76
+ @database.send_request("cursor/#{@id}", delete: {})
75
77
  end
76
78
 
77
79
  private
78
80
 
79
- # Parse a raw document and return a Document or Edge for it
81
+ # Parse a raw document and return a Document, an Edge or a raw object
80
82
  #
81
83
  # @param [Hash] raw_document
82
- # @return Document | Edge
84
+ # @return Document | Edge | Object
83
85
  # @api private
84
86
  def parse_raw_document(raw_document)
85
- detect_document_class_for(raw_document).new(@database, raw_document)
87
+ if raw_document.class == Hash
88
+ detect_document_class_for(raw_document).new(@database, raw_document)
89
+ else
90
+ raw_document
91
+ end
86
92
  end
87
93
 
88
94
  # Detect if a raw document is a document or edge and return the class
@@ -91,7 +97,7 @@ module Ashikawa
91
97
  # @return class
92
98
  # @api private
93
99
  def detect_document_class_for(raw_document)
94
- if raw_document.has_key?("_from") && raw_document.has_key?("_to")
100
+ if raw_document.key?('_from') && raw_document.key?('_to')
95
101
  Edge
96
102
  else
97
103
  Document
@@ -117,7 +123,7 @@ module Ashikawa
117
123
  # @api private
118
124
  def parse_documents_cursor(raw_cursor)
119
125
  @current = raw_cursor['result']
120
- @length = raw_cursor['count'].to_i if raw_cursor.has_key?('count')
126
+ @length = raw_cursor['count'].to_i if raw_cursor.key?('count')
121
127
  end
122
128
 
123
129
  # Get a new batch from the server
@@ -126,7 +132,7 @@ module Ashikawa
126
132
  # @api private
127
133
  def next_batch
128
134
  return false unless @has_more
129
- raw_cursor = @database.send_request("cursor/#{@id}", :put => {})
135
+ raw_cursor = @database.send_request("cursor/#{@id}", put: {})
130
136
  parse_raw_cursor(raw_cursor)
131
137
  end
132
138
  end
@@ -1,19 +1,20 @@
1
- require "ashikawa-core/exceptions/client_error/resource_not_found/collection_not_found"
2
- require "ashikawa-core/collection"
3
- require "ashikawa-core/connection"
4
- require "ashikawa-core/cursor"
5
- require "ashikawa-core/configuration"
6
- require "ashikawa-core/transaction"
7
- require "forwardable"
8
- require "equalizer"
1
+ # -*- encoding : utf-8 -*-
2
+ require 'ashikawa-core/exceptions/client_error/resource_not_found/collection_not_found'
3
+ require 'ashikawa-core/collection'
4
+ require 'ashikawa-core/connection'
5
+ require 'ashikawa-core/cursor'
6
+ require 'ashikawa-core/configuration'
7
+ require 'ashikawa-core/transaction'
8
+ require 'forwardable'
9
+ require 'equalizer'
9
10
 
10
11
  module Ashikawa
11
12
  module Core
12
13
  # An ArangoDB database
13
14
  class Database
14
15
  COLLECTION_TYPES = {
15
- :document => 2,
16
- :edge => 3
16
+ document: 2,
17
+ edge: 3
17
18
  }
18
19
 
19
20
  extend Forwardable
@@ -25,30 +26,36 @@ module Ashikawa
25
26
  def_delegator :@connection, :host
26
27
  def_delegator :@connection, :port
27
28
  def_delegator :@connection, :scheme
28
- def_delegator :@connection, :authenticate_with
29
29
 
30
30
  # Initializes the connection to the database
31
31
  #
32
32
  # @api public
33
33
  # @example Access a Database by providing the URL
34
34
  # database = Ashikawa::Core::Database.new do |config|
35
- # config.url = "http://localhost:8529"
35
+ # config.url = 'http://localhost:8529'
36
36
  # end
37
- # @example Access a Database by providing a Connection
38
- # connection = Connection.new("http://localhost:8529")
37
+ # @example Access a Database by providing a Connection and authentication
38
+ # connection = Connection.new('http://localhost:8529')
39
39
  # database = Ashikawa::Core::Database.new do |config|
40
40
  # config.connection = connection
41
+ # config.username = 'lebowski'
42
+ # config.password = 'i<3bowling'
43
+ # end
44
+ # @example Access a certain database from ArangoDB
45
+ # database = Ashikawa::Core::Database.new do |config|
46
+ # config.url = 'http://localhost:8529/_db/my_db'
47
+ # config.connection = connection
41
48
  # end
42
49
  # @example Access a Database with a logger and custom HTTP adapter
43
50
  # database = Ashikawa::Core::Database.new do |config|
44
- # config.url = "http://localhost:8529"
51
+ # config.url = 'http://localhost:8529'
45
52
  # config.adapter = my_adapter
46
53
  # config.logger = my_logger
47
54
  # end
48
55
  def initialize
49
56
  configuration = Ashikawa::Core::Configuration.new
50
57
  yield(configuration)
51
- @connection = configuration.connection || setup_new_connection(configuration.url, configuration.logger, configuration.adapter)
58
+ @connection = configuration.connection
52
59
  end
53
60
 
54
61
  # Returns a list of all non-system collections defined in the database
@@ -56,12 +63,12 @@ module Ashikawa
56
63
  # @return [Array<Collection>]
57
64
  # @api public
58
65
  # @example Get an Array containing the Collections in the database
59
- # database = Ashikawa::Core::Database.new("http://localhost:8529")
60
- # database["a"]
61
- # database["b"]
62
- # database.collections # => [ #<Collection name="a">, #<Collection name="b">]
66
+ # database = Ashikawa::Core::Database.new('http://localhost:8529')
67
+ # database['a']
68
+ # database['b']
69
+ # database.collections # => [ #<Collection name='a'>, #<Collection name="b">]
63
70
  def collections
64
- all_collections_where { |collection| !collection["name"].start_with?("_") }
71
+ all_collections_where { |collection| !collection['name'].start_with?('_') }
65
72
  end
66
73
 
67
74
  # Returns a list of all system collections defined in the database
@@ -69,10 +76,10 @@ module Ashikawa
69
76
  # @return [Array<Collection>]
70
77
  # @api public
71
78
  # @example Get an Array containing the Collections in the database
72
- # database = Ashikawa::Core::Database.new("http://localhost:8529")
73
- # database.system_collections # => [ #<Collection name="_a">, #<Collection name="_b">]
79
+ # database = Ashikawa::Core::Database.new('http://localhost:8529')
80
+ # database.system_collections # => [ #<Collection name='_a'>, #<Collection name="_b">]
74
81
  def system_collections
75
- all_collections_where { |collection| collection["name"].start_with?("_") }
82
+ all_collections_where { |collection| collection['name'].start_with?('_') }
76
83
  end
77
84
 
78
85
  # Create a Collection based on name
@@ -83,10 +90,10 @@ module Ashikawa
83
90
  # @return [Collection]
84
91
  # @api public
85
92
  # @example Create a new, volatile collection
86
- # database = Ashikawa::Core::Database.new("http://localhost:8529")
87
- # database.create_collection("a", :isVolatile => true) # => #<Collection name="a">
93
+ # database = Ashikawa::Core::Database.new('http://localhost:8529')
94
+ # database.create_collection('a', :isVolatile => true) # => #<Collection name="a">
88
95
  def create_collection(collection_identifier, opts = {})
89
- response = send_request("collection", :post => translate_params(collection_identifier, opts))
96
+ response = send_request('collection', post: translate_params(collection_identifier, opts))
90
97
  Ashikawa::Core::Collection.new(self, response)
91
98
  end
92
99
 
@@ -96,30 +103,30 @@ module Ashikawa
96
103
  # @return [Collection]
97
104
  # @api public
98
105
  # @example Get a Collection from the database by name
99
- # database = Ashikawa::Core::Database.new("http://localhost:8529")
100
- # database["a"] # => #<Collection name="a">
106
+ # database = Ashikawa::Core::Database.new('http://localhost:8529')
107
+ # database['a'] # => #<Collection name="a">
101
108
  # @example Get a Collection from the database by ID
102
- # database = Ashikawa::Core::Database.new("http://localhost:8529")
103
- # database["7254820"] # => #<Collection id=7254820>
109
+ # database = Ashikawa::Core::Database.new('http://localhost:8529')
110
+ # database['7254820'] # => #<Collection id=7254820>
104
111
  def collection(collection_identifier)
105
112
  begin
106
113
  response = send_request("collection/#{collection_identifier}")
107
114
  rescue CollectionNotFoundException
108
- response = send_request("collection", :post => { :name => collection_identifier })
115
+ response = send_request('collection', post: { name: collection_identifier })
109
116
  end
110
117
 
111
118
  Ashikawa::Core::Collection.new(self, response)
112
119
  end
113
120
 
114
- alias :[] :collection
121
+ alias_method :[], :collection
115
122
 
116
123
  # Return a Query initialized with this database
117
124
  #
118
125
  # @return [Query]
119
126
  # @api public
120
127
  # @example Send an AQL query to the database
121
- # database = Ashikawa::Core::Database.new("http://localhost:8529")
122
- # database.query.execute "FOR u IN users LIMIT 2" # => #<Cursor id=33>
128
+ # database = Ashikawa::Core::Database.new('http://localhost:8529')
129
+ # database.query.execute 'FOR u IN users LIMIT 2' # => #<Cursor id=33>
123
130
  def query
124
131
  Query.new(self)
125
132
  end
@@ -132,29 +139,32 @@ module Ashikawa
132
139
  # @return [Object] The result of the transaction
133
140
  # @api public
134
141
  # @example Create a new Transaction
135
- # transaction = database.create_transaction("function () { return 5; }", :read => ["collection_1"])
142
+ # transaction = database.create_transaction('function () { return 5; }", :read => ["collection_1'])
136
143
  # transaction.execute #=> 5
137
144
  def create_transaction(action, collections)
138
145
  Ashikawa::Core::Transaction.new(self, action, collections)
139
146
  end
140
147
 
141
- private
142
-
143
- # Setup the connection object
148
+ # Authenticate with given username and password
144
149
  #
145
- # @param [String] url
146
- # @param [Logger] logger
147
- # @param [Adapter] adapter
148
- # @return [Connection]
149
- # @api private
150
- def setup_new_connection(url, logger, adapter)
151
- raise(ArgumentError, "Please provide either an url or a connection to setup the database") if url.nil?
152
- Ashikawa::Core::Connection.new(url, {
153
- :logger => logger,
154
- :adapter => adapter
155
- })
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)
156
164
  end
157
165
 
166
+ private
167
+
158
168
  # Parse a raw collection
159
169
  #
160
170
  # @param [Array] raw_collections
@@ -173,10 +183,10 @@ module Ashikawa
173
183
  # @api private
174
184
  def translate_key_options(key_options)
175
185
  {
176
- :type => key_options[:type].to_s,
177
- :offset => key_options[:offset],
178
- :increment => key_options[:increment],
179
- :allowUserKeys => key_options[:allow_user_keys]
186
+ type: key_options[:type].to_s,
187
+ offset: key_options[:offset],
188
+ increment: key_options[:increment],
189
+ allowUserKeys: key_options[:allow_user_keys]
180
190
  }
181
191
  end
182
192
 
@@ -187,10 +197,10 @@ module Ashikawa
187
197
  # @return [Hash]
188
198
  # @api private
189
199
  def translate_params(collection_identifier, opts)
190
- params = { :name => collection_identifier }
191
- params[:isVolatile] = true if opts[:is_volatile] == true
192
- params[:type] = COLLECTION_TYPES[opts[:content_type]] if opts.has_key?(:content_type)
193
- params[:keyOptions] = translate_key_options(opts[:key_options]) if opts.has_key?(:key_options)
200
+ params = { name: collection_identifier }
201
+ params[:isVolatile] = true if opts[:is_volatile]
202
+ params[:type] = COLLECTION_TYPES[opts[:content_type]] if opts.key?(:content_type)
203
+ params[:keyOptions] = translate_key_options(opts[:key_options]) if opts.key?(:key_options)
194
204
  params
195
205
  end
196
206
 
@@ -202,7 +212,7 @@ module Ashikawa
202
212
  # @return [Array<Collection>]
203
213
  # @api private
204
214
  def all_collections_where(&block)
205
- raw_collections = send_request("collection")["collections"]
215
+ raw_collections = send_request('collection')['collections']
206
216
  raw_collections.keep_if(&block)
207
217
  parse_raw_collections(raw_collections)
208
218
  end