ashikawa-core 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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