ashikawa-core 0.5.1 → 0.6.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.
Files changed (50) hide show
  1. data/.gitignore +11 -9
  2. data/.rspec +4 -0
  3. data/.travis.yml +8 -3
  4. data/CONTRIBUTING.md +5 -5
  5. data/Gemfile +5 -1
  6. data/Gemfile.devtools +65 -0
  7. data/Guardfile +1 -11
  8. data/README.md +14 -8
  9. data/Rakefile +5 -103
  10. data/ashikawa-core.gemspec +3 -29
  11. data/config/flay.yml +3 -0
  12. data/config/flog.yml +2 -0
  13. data/config/mutant.yml +3 -0
  14. data/config/roodi.yml +18 -0
  15. data/config/site.reek +95 -0
  16. data/config/yardstick.yml +2 -0
  17. data/lib/ashikawa-core/collection.rb +138 -178
  18. data/lib/ashikawa-core/connection.rb +74 -26
  19. data/lib/ashikawa-core/cursor.rb +30 -9
  20. data/lib/ashikawa-core/database.rb +23 -19
  21. data/lib/ashikawa-core/document.rb +33 -8
  22. data/lib/ashikawa-core/exceptions/collection_not_found.rb +15 -0
  23. data/lib/ashikawa-core/exceptions/document_not_found.rb +4 -0
  24. data/lib/ashikawa-core/exceptions/index_not_found.rb +15 -0
  25. data/lib/ashikawa-core/exceptions/no_collection_provided.rb +4 -0
  26. data/lib/ashikawa-core/exceptions/unknown_path.rb +15 -0
  27. data/lib/ashikawa-core/figure.rb +73 -0
  28. data/lib/ashikawa-core/index.rb +25 -7
  29. data/lib/ashikawa-core/query.rb +68 -55
  30. data/lib/ashikawa-core/status.rb +77 -0
  31. data/lib/ashikawa-core/version.rb +1 -1
  32. data/spec/acceptance/basic_spec.rb +14 -18
  33. data/spec/acceptance/index_spec.rb +4 -2
  34. data/spec/acceptance/query_spec.rb +18 -19
  35. data/spec/acceptance_auth/auth_spec.rb +2 -2
  36. data/spec/setup/arangodb.sh +34 -39
  37. data/spec/spec_helper.rb +27 -0
  38. data/spec/unit/collection_spec.rb +25 -73
  39. data/spec/unit/connection_spec.rb +46 -15
  40. data/spec/unit/cursor_spec.rb +3 -3
  41. data/spec/unit/database_spec.rb +8 -7
  42. data/spec/unit/document_spec.rb +2 -2
  43. data/spec/unit/exception_spec.rb +21 -0
  44. data/spec/unit/figure_spec.rb +28 -0
  45. data/spec/unit/index_spec.rb +1 -1
  46. data/spec/unit/query_spec.rb +25 -25
  47. data/spec/unit/spec_helper.rb +6 -4
  48. data/spec/unit/status_spec.rb +51 -0
  49. data/tasks/adjustments.rake +46 -0
  50. metadata +31 -203
@@ -0,0 +1,15 @@
1
+ module Ashikawa
2
+ module Core
3
+ # This Exception is thrown when a document was requested from
4
+ # the server that does not exist.
5
+ class CollectionNotFoundException < RuntimeError
6
+ # String representation of the exception
7
+ #
8
+ # @return String
9
+ # @api private
10
+ def to_s
11
+ "You requested a collection from the server that does not exist"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,6 +3,10 @@ module Ashikawa
3
3
  # This Exception is thrown when a document was requested from
4
4
  # the server that does not exist.
5
5
  class DocumentNotFoundException < RuntimeError
6
+ # String representation of the exception
7
+ #
8
+ # @return String
9
+ # @api private
6
10
  def to_s
7
11
  "You requested a document from the server that does not exist"
8
12
  end
@@ -0,0 +1,15 @@
1
+ module Ashikawa
2
+ module Core
3
+ # This Exception is thrown when an index was requested from
4
+ # the server that does not exist.
5
+ class IndexNotFoundException < RuntimeError
6
+ # String representation of the exception
7
+ #
8
+ # @return String
9
+ # @api private
10
+ def to_s
11
+ "You requested an index from the server that does not exist"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,6 +3,10 @@ module Ashikawa
3
3
  # This Exception is thrown when a Query object should execute a simple query
4
4
  # but no collection was provided upon creation
5
5
  class NoCollectionProvidedException < RuntimeError
6
+ # String representation of the exception
7
+ #
8
+ # @return String
9
+ # @api private
6
10
  def to_s
7
11
  "A simple query can't be executed by a Query object without a collection"
8
12
  end
@@ -0,0 +1,15 @@
1
+ module Ashikawa
2
+ module Core
3
+ # This Exception is thrown when you request
4
+ # a path from the server which is not known
5
+ class UnknownPath < RuntimeError
6
+ # String representation of the exception
7
+ #
8
+ # @return String
9
+ # @api private
10
+ def to_s
11
+ "The path is unknown"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,73 @@
1
+ module Ashikawa
2
+ module Core
3
+ # Wrapper around figures of a collection
4
+ class Figure
5
+ # Create a wrapper around given figures
6
+ #
7
+ # @param [Hash] raw_figure
8
+ # @api public
9
+ # @example Create a new figure from a raw figure
10
+ # figure = Ashikawa::Core::Figure.new(raw_figure)
11
+ def initialize(raw_figure)
12
+ @datafiles = raw_figure["datafiles"]
13
+ @alive = raw_figure["alive"]
14
+ @dead = raw_figure["dead"]
15
+ end
16
+
17
+ # The number of active datafiles
18
+ #
19
+ # @return Fixnum
20
+ # @api public
21
+ # @example Get the number of datafiles
22
+ # figure = Ashikawa::Core::Figure.new(raw_figure)
23
+ # figure.datafiles_count #=> 1337
24
+ def datafiles_count
25
+ @datafiles["count"]
26
+ end
27
+
28
+ # The total size in bytes used by all living documents
29
+ #
30
+ # @return Fixnum
31
+ # @api public
32
+ # @example Get the size of all living documents in bytes
33
+ # figure = Ashikawa::Core::Figure.new(raw_figure)
34
+ # figure.alive_size #=> 1337
35
+ def alive_size
36
+ @alive["size"]
37
+ end
38
+
39
+ # The number of living documents
40
+ #
41
+ # @return Fixnum
42
+ # @api public
43
+ # @example Get the number of living documents
44
+ # figure = Ashikawa::Core::Figure.new(raw_figure)
45
+ # figure.alive_count #=> 1337
46
+ def alive_count
47
+ @alive["count"]
48
+ end
49
+
50
+ # The total size in bytes used by all dead documents
51
+ #
52
+ # @return Fixnum
53
+ # @api public
54
+ # @example Get the size of all dead documents in bytes
55
+ # figure = Ashikawa::Core::Figure.new(raw_figure)
56
+ # figure.dead_size #=> 1337
57
+ def dead_size
58
+ @dead["size"]
59
+ end
60
+
61
+ # The number of dead documents
62
+ #
63
+ # @return Fixnum
64
+ # @api public
65
+ # @example Get the number of dead documents
66
+ # figure = Ashikawa::Core::Figure.new(raw_figure)
67
+ # figure.dead_count #=> 1337
68
+ def dead_count
69
+ @dead["count"]
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,25 +1,37 @@
1
1
  module Ashikawa
2
2
  module Core
3
- # Represents an index on a certain collection
3
+ # An index on a certain collection
4
4
  class Index
5
5
  # The fields the index is defined on as symbols
6
6
  # @return [Array<Symbol>]
7
7
  # @api public
8
+ # @example Get the fields the index is set on
9
+ # index = Ashikawa::Core::Index.new(collection, raw_index)
10
+ # index.fields #=> [:name]
8
11
  attr_reader :on
9
12
 
10
13
  # The type of index as a symbol
11
14
  # @return [Symbol]
12
15
  # @api public
16
+ # @example Get the type of the index
17
+ # index = Ashikawa::Core::Index.new(collection, raw_index)
18
+ # index.type #=> :skiplist
13
19
  attr_reader :type
14
20
 
15
21
  # Is the unique constraint set?
16
22
  # @return [Boolean]
17
23
  # @api public
24
+ # @example Get the fields the index is set on
25
+ # index = Ashikawa::Core::Index.new(collection, raw_index)
26
+ # index.unique #=> false
18
27
  attr_reader :unique
19
28
 
20
29
  # The id of the index
21
30
  # @return [Int]
22
31
  # @api public
32
+ # @example Get the id of this index
33
+ # index = Ashikawa::Core::Index.new(collection, raw_index)
34
+ # index.id #=> 4567
23
35
  attr_reader :id
24
36
 
25
37
  # Create a new Index
@@ -27,20 +39,26 @@ module Ashikawa
27
39
  # @param [Collection] collection The collection the index is defined on
28
40
  # @param [Hash] raw_index The JSON representation of the index
29
41
  # @return [Index]
30
- # @api
42
+ # @api public
43
+ # @example Create a new index from the raw representation
44
+ # index = Ashikawa::Core::Index.new(collection, raw_index)
31
45
  def initialize(collection, raw_index)
32
46
  @collection = collection
33
- @id = raw_index["id"].split("/")[1].to_i if raw_index.has_key? "id"
34
- @on = raw_index["fields"].map { |field| field.to_sym } if raw_index.has_key? "fields"
35
- @type = raw_index["type"].to_sym if raw_index.has_key? "type"
36
- @unique = raw_index["unique"] if raw_index.has_key? "unique"
47
+ @id = raw_index["id"].split("/")[1].to_i if raw_index.has_key?("id")
48
+ @on = raw_index["fields"].map { |field| field.to_sym } if raw_index.has_key?("fields")
49
+ @type = raw_index["type"].to_sym if raw_index.has_key?("type")
50
+ @unique = raw_index["unique"] if raw_index.has_key?("unique")
37
51
  end
38
52
 
39
53
  # Remove the index from the collection
40
54
  #
55
+ # @return [Hash] parsed JSON response from the server
41
56
  # @api public
57
+ # @example Remove this index from the collection
58
+ # index = Ashikawa::Core::Index.new(collection, raw_index)
59
+ # index.delete
42
60
  def delete
43
- @collection.send_request("index/#{@collection.id}/#{@id}", delete: {})
61
+ @collection.send_request("index/#{@collection.id}/#{@id}", :delete => {})
44
62
  end
45
63
  end
46
64
  end
@@ -2,6 +2,8 @@ require 'ashikawa-core/cursor'
2
2
  require 'ashikawa-core/document'
3
3
  require 'ashikawa-core/exceptions/no_collection_provided'
4
4
  require 'forwardable'
5
+ require 'backports'
6
+ require 'rest-client'
5
7
 
6
8
  module Ashikawa
7
9
  module Core
@@ -10,12 +12,16 @@ module Ashikawa
10
12
  extend Forwardable
11
13
 
12
14
  # Delegate sending requests to the connection
13
- delegate send_request: :@connection
15
+ def_delegator :@connection, :send_request
14
16
 
15
17
  # Initializes a Query
16
18
  #
17
19
  # @param [Collection, Database] connection
18
20
  # @return [Query]
21
+ # @api public
22
+ # @example Create a new query object
23
+ # collection = Ashikawa::Core::Collection.new(database, raw_collection)
24
+ # query = Ashikawa::Core::Query.new(collection)
19
25
  def initialize(connection)
20
26
  @connection = connection
21
27
  end
@@ -29,12 +35,12 @@ module Ashikawa
29
35
  # @raise [NoCollectionProvidedException] If you provided a database, no collection
30
36
  # @api public
31
37
  # @example Get an array with all documents
32
- # query = Ashikawa::Core::Query.new collection
38
+ # query = Ashikawa::Core::Query.new(collection)
33
39
  # query.all # => #<Cursor id=33>
34
40
  def all(options={})
35
- simple_query_request "/simple/all",
41
+ simple_query_request("/simple/all",
36
42
  options,
37
- [:limit, :skip]
43
+ [:limit, :skip])
38
44
  end
39
45
 
40
46
  # Looks for documents in a collection which match the given criteria
@@ -47,12 +53,12 @@ module Ashikawa
47
53
  # @raise [NoCollectionProvidedException] If you provided a database, no collection
48
54
  # @api public
49
55
  # @example Find all documents in a collection that are red
50
- # query = Ashikawa::Core::Query.new collection
51
- # query.by_example { "color" => "red" }, :options => { :limit => 1 } # => #<Cursor id=2444>
56
+ # query = Ashikawa::Core::Query.new(collection)
57
+ # query.by_example({ "color" => "red" }, :options => { :limit => 1 }) #=> #<Cursor id=2444>
52
58
  def by_example(example={}, options={})
53
- simple_query_request "/simple/by-example",
54
- { example: example }.merge(options),
55
- [:limit, :skip, :example]
59
+ simple_query_request("/simple/by-example",
60
+ { :example => example }.merge(options),
61
+ [:limit, :skip, :example])
56
62
  end
57
63
 
58
64
  # Looks for one document in a collection which matches the given criteria
@@ -62,12 +68,12 @@ module Ashikawa
62
68
  # @raise [NoCollectionProvidedException] If you provided a database, no collection
63
69
  # @api public
64
70
  # @example Find one document in a collection that is red
65
- # query = Ashikawa::Core::Query.new collection
66
- # query.first_example { "color" => "red"} # => #<Document id=2444 color="red">
71
+ # query = Ashikawa::Core::Query.new(collection)
72
+ # query.first_example({ "color" => "red"}) # => #<Document id=2444 color="red">
67
73
  def first_example(example = {})
68
- response = simple_query_request "/simple/first-example",
69
- { example: example },
70
- [:example]
74
+ response = simple_query_request("/simple/first-example",
75
+ { :example => example },
76
+ [:example])
71
77
  response.first
72
78
  end
73
79
 
@@ -83,12 +89,12 @@ module Ashikawa
83
89
  # @raise [NoCollectionProvidedException] If you provided a database, no collection
84
90
  # @api public
85
91
  # @example Find all documents at Infinite Loop
86
- # query = Ashikawa::Core::Query.new collection
87
- # query.near latitude: 37.331693, longitude: -122.030468
92
+ # query = Ashikawa::Core::Query.new(collection)
93
+ # query.near(:latitude => 37.331693, :longitude => -122.030468)
88
94
  def near(options={})
89
- simple_query_request "/simple/near",
95
+ simple_query_request("/simple/near",
90
96
  options,
91
- [:latitude, :longitude, :distance, :skip, :limit, :geo]
97
+ [:latitude, :longitude, :distance, :skip, :limit, :geo])
92
98
  end
93
99
 
94
100
  # Looks for documents in a collection within a radius
@@ -104,12 +110,12 @@ module Ashikawa
104
110
  # @api public
105
111
  # @raise [NoCollectionProvidedException] If you provided a database, no collection
106
112
  # @example Find all documents within a radius of 100 to Infinite Loop
107
- # query = Ashikawa::Core::Query.new collection
108
- # query.within latitude: 37.331693, longitude: -122.030468, radius: 100
113
+ # query = Ashikawa::Core::Query.new(collection)
114
+ # query.within(:latitude => 37.331693, :longitude => -122.030468, :radius => 100)
109
115
  def within(options={})
110
- simple_query_request "/simple/within",
116
+ simple_query_request("/simple/within",
111
117
  options,
112
- [:latitude, :longitude, :radius, :distance, :skip, :limit, :geo]
118
+ [:latitude, :longitude, :radius, :distance, :skip, :limit, :geo])
113
119
  end
114
120
 
115
121
  # Looks for documents in a collection with an attribute between two values
@@ -124,12 +130,12 @@ module Ashikawa
124
130
  # @raise [NoCollectionProvidedException] If you provided a database, no collection
125
131
  # @api public
126
132
  # @example Find all documents within a radius of 100 to Infinite Loop
127
- # query = Ashikawa::Core::Query.new collection
128
- # query.within latitude: 37.331693, longitude: -122.030468, radius: 100
133
+ # query = Ashikawa::Core::Query.new(collection)
134
+ # query.within(:latitude => 37.331693, :longitude => -122.030468, :radius => 100)
129
135
  def in_range(options={})
130
- simple_query_request "/simple/range",
136
+ simple_query_request("/simple/range",
131
137
  options,
132
- [:attribute, :left, :right, :closed, :limit, :skip]
138
+ [:attribute, :left, :right, :closed, :limit, :skip])
133
139
  end
134
140
 
135
141
  # Send an AQL query to the database
@@ -140,12 +146,12 @@ module Ashikawa
140
146
  # @return [Cursor]
141
147
  # @api public
142
148
  # @example Send an AQL query to the database
143
- # query = Ashikawa::Core::Query.new collection
144
- # query.execute "FOR u IN users LIMIT 2" # => #<Cursor id=33>
149
+ # query = Ashikawa::Core::Query.new(collection)
150
+ # query.execute("FOR u IN users LIMIT 2") # => #<Cursor id=33>
145
151
  def execute(query, options = {})
146
- post_request "/cursor",
147
- options.merge({ query: query }),
148
- [:query, :count, :batch_size]
152
+ post_request("/cursor",
153
+ options.merge({ :query => query }),
154
+ [:query, :count, :batch_size])
149
155
  end
150
156
 
151
157
  # Test if an AQL query is valid
@@ -154,14 +160,12 @@ module Ashikawa
154
160
  # @return [Boolean]
155
161
  # @api public
156
162
  # @example Validate an AQL query
157
- # query = Ashikawa::Core::Query.new collection
158
- # query.valid? "FOR u IN users LIMIT 2" # => true
163
+ # query = Ashikawa::Core::Query.new(collection)
164
+ # query.valid?("FOR u IN users LIMIT 2") # => true
159
165
  def valid?(query)
160
- begin
161
- !!post_request("/query", { query: query })
162
- rescue RestClient::BadRequest
163
- false
164
- end
166
+ !!post_request("/query", { :query => query })
167
+ rescue RestClient::BadRequest
168
+ false
165
169
  end
166
170
 
167
171
  private
@@ -179,7 +183,7 @@ module Ashikawa
179
183
  # @return [collection]
180
184
  # @api private
181
185
  def collection
182
- raise NoCollectionProvidedException unless @connection.respond_to? :database
186
+ raise NoCollectionProvidedException unless @connection.respond_to?(:database)
183
187
  @connection
184
188
  end
185
189
 
@@ -190,10 +194,10 @@ module Ashikawa
190
194
  # @return [Hash] The filtered Hash
191
195
  # @api private
192
196
  def allowed_options(options, allowed_keys)
193
- options.keep_if { |key, _| allowed_keys.include? key }
197
+ options.keep_if { |key, _| allowed_keys.include?(key) }
194
198
  end
195
199
 
196
- # Transforms the keys into strings, camelizes them and removes pairs without a value
200
+ # Transforms the keys into the required format
197
201
  #
198
202
  # @param [Hash] request_data
199
203
  # @return [Hash] Cleaned request data
@@ -208,18 +212,33 @@ module Ashikawa
208
212
  #
209
213
  # @param [String] path The path for the request
210
214
  # @param [Hash] request_data The data send to the database
211
- # @param [Array<Symbol>] keys The keys allowed for this request
215
+ # @param [Array<Symbol>] allowed_keys The keys allowed for this request
212
216
  # @return [String] Server response
213
217
  # @raise [NoCollectionProvidedException] If you provided a database, no collection
214
218
  # @api private
215
219
  def simple_query_request(path, request_data, allowed_keys)
216
- request_data = request_data.merge({ collection: collection.name })
217
- put_request path,
220
+ request_data = request_data.merge({ :collection => collection.name })
221
+ put_request(path,
218
222
  request_data,
219
- allowed_keys << :collection
223
+ allowed_keys << :collection)
224
+ end
225
+
226
+ # Perform a wrapped request
227
+ #
228
+ # @param [String] path The path for the request
229
+ # @param [Symbol] request_method The request method to perform
230
+ # @param [Hash] request_data The data send to the database
231
+ # @param [Array] allowed_keys Keys allowed in request_data, if nil: All keys are allowed
232
+ # @return [Cursor]
233
+ # @api private
234
+ def wrapped_request(path, request_method, request_data, allowed_keys)
235
+ request_data = allowed_options(request_data, allowed_keys) unless allowed_keys.nil?
236
+ request_data = prepare_request_data(request_data)
237
+ server_response = send_request(path, { request_method => request_data })
238
+ Cursor.new(database, server_response)
220
239
  end
221
240
 
222
- # Perform a put request
241
+ # Perform a wrapped put request
223
242
  #
224
243
  # @param [String] path The path for the request
225
244
  # @param [Hash] request_data The data send to the database
@@ -227,13 +246,10 @@ module Ashikawa
227
246
  # @return [Cursor]
228
247
  # @api private
229
248
  def put_request(path, request_data, allowed_keys = nil)
230
- request_data = allowed_options request_data, allowed_keys unless allowed_keys.nil?
231
- request_data = prepare_request_data request_data
232
- server_response = send_request path, :put => request_data
233
- Cursor.new database, server_response
249
+ wrapped_request(path, :put, request_data, allowed_keys)
234
250
  end
235
251
 
236
- # Perform a post request
252
+ # Perform a wrapped post request
237
253
  #
238
254
  # @param [String] path The path for the request
239
255
  # @param [Hash] request_data The data send to the database
@@ -241,10 +257,7 @@ module Ashikawa
241
257
  # @return [Cursor]
242
258
  # @api private
243
259
  def post_request(path, request_data, allowed_keys = nil)
244
- request_data = allowed_options request_data, allowed_keys unless allowed_keys.nil?
245
- request_data = prepare_request_data request_data
246
- server_response = send_request path, :post => request_data
247
- Cursor.new database, server_response
260
+ wrapped_request(path, :post, request_data, allowed_keys)
248
261
  end
249
262
  end
250
263
  end