ashikawa-core 0.1 → 0.2

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 (38) hide show
  1. data/Guardfile +18 -0
  2. data/README.md +10 -10
  3. data/Rakefile +5 -4
  4. data/ashikawa-core.gemspec +14 -6
  5. data/lib/ashikawa-core.rb +1 -0
  6. data/lib/ashikawa-core/collection.rb +189 -98
  7. data/lib/ashikawa-core/connection.rb +18 -20
  8. data/lib/ashikawa-core/cursor.rb +65 -0
  9. data/lib/ashikawa-core/database.rb +33 -46
  10. data/lib/ashikawa-core/document.rb +63 -22
  11. data/lib/ashikawa-core/exceptions/document_not_found.rb +8 -0
  12. data/lib/ashikawa-core/index.rb +47 -0
  13. data/lib/ashikawa-core/version.rb +1 -1
  14. data/spec/fixtures/cursor/26011191-2.json +19 -0
  15. data/spec/fixtures/cursor/26011191-3.json +13 -0
  16. data/spec/fixtures/cursor/26011191.json +19 -0
  17. data/spec/fixtures/cursor/query.json +18 -0
  18. data/spec/fixtures/documents/new-4590-333.json +5 -0
  19. data/spec/fixtures/indices/all.json +22 -0
  20. data/spec/fixtures/indices/hash-index.json +12 -0
  21. data/spec/fixtures/indices/new-hash-index.json +12 -0
  22. data/spec/fixtures/simple-queries/all.json +10 -4
  23. data/spec/fixtures/simple-queries/all_limit.json +9 -3
  24. data/spec/fixtures/simple-queries/all_skip.json +9 -3
  25. data/spec/fixtures/simple-queries/example.json +9 -3
  26. data/spec/fixtures/simple-queries/near.json +11 -5
  27. data/spec/fixtures/simple-queries/range.json +10 -0
  28. data/spec/fixtures/simple-queries/within.json +9 -3
  29. data/spec/integration/arango_helper.rb +27 -0
  30. data/spec/integration/basic_spec.rb +107 -28
  31. data/spec/integration/spec_helper.rb +0 -28
  32. data/spec/unit/collection_spec.rb +190 -83
  33. data/spec/unit/connection_spec.rb +17 -17
  34. data/spec/unit/cursor_spec.rb +75 -0
  35. data/spec/unit/database_spec.rb +34 -19
  36. data/spec/unit/document_spec.rb +77 -6
  37. data/spec/unit/index_spec.rb +39 -0
  38. metadata +98 -6
@@ -0,0 +1,13 @@
1
+ {
2
+ "hasMore": false,
3
+ "error": false,
4
+ "result": [
5
+ {
6
+ "n": 4,
7
+ "_rev": 25880119,
8
+ "_id": "23914039/25880119"
9
+ }
10
+ ],
11
+ "code": 200,
12
+ "count": 5
13
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "hasMore": true,
3
+ "error": false,
4
+ "id": 26011191,
5
+ "result": [
6
+ {
7
+ "n": 0,
8
+ "_rev": 25880119,
9
+ "_id": "23914039/25880119"
10
+ },
11
+ {
12
+ "n": 1,
13
+ "_rev": 25880119,
14
+ "_id": "23914039/25880119"
15
+ }
16
+ ],
17
+ "code": 201,
18
+ "count": 5
19
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "hasMore": false,
3
+ "error": false,
4
+ "result": [
5
+ {
6
+ "n": 0,
7
+ "_rev": 21030455,
8
+ "_id": "19588663/21030455"
9
+ },
10
+ {
11
+ "n": 1,
12
+ "_rev": 21030455,
13
+ "_id": "19588663/21030455"
14
+ }
15
+ ],
16
+ "code": 201,
17
+ "count": 2
18
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "_rev": 28411694,
3
+ "_id": "4590/333",
4
+ "error": false
5
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "code": 200,
3
+ "indexes": [
4
+ {
5
+ "fields": [
6
+ "_id"
7
+ ],
8
+ "id": "4590/0",
9
+ "type": "primary"
10
+ }
11
+ ],
12
+ "error": false,
13
+ "identifiers": {
14
+ "176836793/0": {
15
+ "fields": [
16
+ "_id"
17
+ ],
18
+ "id": "4590/0",
19
+ "type": "primary"
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "code": 201,
3
+ "fields": [
4
+ "a",
5
+ "b"
6
+ ],
7
+ "id": "4590/168054969",
8
+ "type": "hash",
9
+ "isNewlyCreated": false,
10
+ "unique": false,
11
+ "error": false
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "code": 201,
3
+ "fields": [
4
+ "a",
5
+ "b"
6
+ ],
7
+ "id": "4590/168054969",
8
+ "type": "hash",
9
+ "isNewlyCreated": true,
10
+ "unique": false,
11
+ "error": false
12
+ }
@@ -1,4 +1,10 @@
1
- [
2
- { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" },
3
- { "_id" : "12346/3872", "_rev" : 3872, "key" : "value" }
4
- ]
1
+ {
2
+ "code": 201,
3
+ "hasMore": false,
4
+ "count": 1,
5
+ "error": false,
6
+ "result": [
7
+ { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" },
8
+ { "_id" : "12346/3872", "_rev" : 3872, "key" : "value" }
9
+ ]
10
+ }
@@ -1,3 +1,9 @@
1
- [
2
- { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" }
3
- ]
1
+ {
2
+ "code": 201,
3
+ "hasMore": false,
4
+ "count": 1,
5
+ "error": false,
6
+ "result": [
7
+ { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" }
8
+ ]
9
+ }
@@ -1,3 +1,9 @@
1
- [
2
- { "_id" : "12346/3872", "_rev" : 3872, "key" : "value" }
3
- ]
1
+ {
2
+ "code": 201,
3
+ "hasMore": false,
4
+ "count": 1,
5
+ "error": false,
6
+ "result": [
7
+ { "_id" : "12346/3872", "_rev" : 3872, "key" : "value" }
8
+ ]
9
+ }
@@ -1,3 +1,9 @@
1
- [
2
- { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" }
3
- ]
1
+ {
2
+ "code": 201,
3
+ "hasMore": false,
4
+ "count": 1,
5
+ "error": false,
6
+ "result": [
7
+ { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" }
8
+ ]
9
+ }
@@ -1,5 +1,11 @@
1
- [
2
- { "_id" : "12345/57463", "_rev" : 57463, "name" : "world/0/0", "loc" : [0, 0] },
3
- { "_id" : "12346/2938", "_rev" : 2938, "name" : "world/0/10", "loc" : [0, 10] },
4
- { "_id" : "12347/23737", "_rev" : 23737, "name" : "world/-10/0", "loc" : [-10, 0] }
5
- ]
1
+ {
2
+ "code": 201,
3
+ "hasMore": false,
4
+ "count": 3,
5
+ "error": false,
6
+ "result": [
7
+ { "_id" : "12345/57463", "_rev" : 57463, "name" : "world/0/0", "loc" : [0, 0] },
8
+ { "_id" : "12346/2938", "_rev" : 2938, "name" : "world/0/10", "loc" : [0, 10] },
9
+ { "_id" : "12347/23737", "_rev" : 23737, "name" : "world/-10/0", "loc" : [-10, 0] }
10
+ ]
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "code": 201,
3
+ "result": [
4
+ { "_id": "6328369086/6329876414", "i": 2, "_rev": 6329876414 },
5
+ { "_id": "6328369086/6329941950", "i": 3, "_rev": 6329941950 }
6
+ ],
7
+ "count": 2,
8
+ "hasMore": false,
9
+ "error": false
10
+ }
@@ -1,3 +1,9 @@
1
- [
2
- { "_id" : "12345/57463", "_rev" : 57463, "name" : "world/0/0", "loc" : [0, 0] }
3
- ]
1
+ {
2
+ "code": 201,
3
+ "hasMore": false,
4
+ "count": 1,
5
+ "error": false,
6
+ "result": [
7
+ { "_id" : "12345/57463", "_rev" : 57463, "name" : "world/0/0", "loc" : [0, 0] }
8
+ ]
9
+ }
@@ -0,0 +1,27 @@
1
+ RSpec.configure do |config|
2
+ raise "Could not find arangod. Please install it or check if it is in your path." if `which arangod` == ""
3
+
4
+ database_directory = "/tmp/ashikawa-integration"
5
+ arango_process = false
6
+
7
+ config.before(:suite) do
8
+ puts "Starting ArangoDB"
9
+ process_id = $$
10
+
11
+ Dir.mkdir database_directory unless Dir.exists? database_directory
12
+ arango_process = IO.popen("arangod #{database_directory} --watch-process #{process_id}")
13
+
14
+ sleep 2 # Wait for Arango to start up
15
+ end
16
+
17
+ config.after(:suite) do
18
+ puts
19
+ puts "Shutting down ArangoDB"
20
+
21
+ Process.kill "INT", arango_process.pid
22
+ sleep 2 # Wait for Arango to shut down
23
+ arango_process.close
24
+
25
+ `rm -r #{database_directory}/*`
26
+ end
27
+ end
@@ -1,14 +1,20 @@
1
1
  require 'integration/spec_helper'
2
2
 
3
+ ARANGO_HOST = "http://localhost:8529"
4
+
3
5
  describe "Basics" do
4
- subject { "http://localhost:8529" }
6
+ subject { ARANGO_HOST }
5
7
 
6
8
  it "should have booted up an ArangoDB instance" do
7
- expect { JSON.parse RestClient.get(subject) }.to raise_error(RestClient::NotImplemented)
9
+ expect { RestClient.get(subject) }.to_not raise_error
8
10
  end
9
11
 
10
12
  describe "initialized database" do
11
- subject { Ashikawa::Core::Database.new "http://localhost:8529" }
13
+ subject { Ashikawa::Core::Database.new ARANGO_HOST }
14
+
15
+ after :each do
16
+ subject.collections.each { |collection| collection.delete }
17
+ end
12
18
 
13
19
  it "should do what the README describes" do
14
20
  subject["my_collection"]
@@ -33,12 +39,12 @@ describe "Basics" do
33
39
  my_collection.name.should == "my_new_name"
34
40
  end
35
41
 
36
- it "should be possible to find a collection by ID" do
42
+ it "should be possible to find a collection by ID" do
37
43
  my_collection = subject["test_collection"]
38
44
  subject[my_collection.id].name.should == "test_collection"
39
45
  end
40
46
 
41
- it "should be possible to load and unload collections" do
47
+ it "should be possible to load and unload collections" do
42
48
  my_collection = subject["test_collection"]
43
49
  my_collection.loaded?.should be_true
44
50
  my_collection.unload
@@ -47,7 +53,7 @@ describe "Basics" do
47
53
  subject[my_id].loaded?.should be_false
48
54
  end
49
55
 
50
- it "should be possible to get figures" do
56
+ it "should be possible to get figures" do
51
57
  my_collection = subject["test_collection"]
52
58
  my_collection.figure(:datafiles_count).class.should == Fixnum
53
59
  my_collection.figure(:alive_size).class.should == Fixnum
@@ -58,13 +64,13 @@ describe "Basics" do
58
64
 
59
65
  it "should change and receive information about waiting for sync" do
60
66
  my_collection = subject["my_collection"]
67
+ my_collection.wait_for_sync = false
61
68
  my_collection.wait_for_sync?.should be_false
62
69
  my_collection.wait_for_sync = true
63
70
  my_collection.wait_for_sync?.should be_true
64
71
  end
65
72
 
66
73
  it "should be possible to get information about the number of documents" do
67
- pending("`<<` not implemented yet")
68
74
  empty_collection = subject["empty_collection"]
69
75
  empty_collection.length.should == 0
70
76
  empty_collection << { name: "testname", age: 27}
@@ -75,14 +81,12 @@ describe "Basics" do
75
81
  end
76
82
 
77
83
  it "should return all documents of a collection" do
78
- pending("`<<` not implemented yet")
79
84
  empty_collection = subject["empty_collection"]
80
85
  empty_collection << { name: "testname", age: 27}
81
86
  empty_collection.all.first["name"].should == "testname"
82
87
  end
83
88
 
84
89
  it "should be possible to limit and skip results" do
85
- pending("`<<` not implemented yet")
86
90
  empty_collection = subject["empty_collection"]
87
91
  empty_collection.truncate!
88
92
 
@@ -94,11 +98,20 @@ describe "Basics" do
94
98
  empty_collection.all(skip: 2).length.should == 1
95
99
  end
96
100
 
101
+ it "should be possible to update the attributes of a document" do
102
+ collection = subject["documenttests"]
103
+
104
+ document = collection.create name: "The Dude", bowling: true
105
+ document_id = document.id
106
+ document["name"] = "Other Dude"
107
+ document.save
108
+
109
+ collection[document_id]["name"].should == "Other Dude"
110
+ end
97
111
 
98
112
  it "should be possible to access and create documents from a collection" do
99
113
  collection = subject["documenttests"]
100
114
 
101
- pending("`create` not implemented yet")
102
115
  document = collection.create name: "The Dude", bowling: true
103
116
  document_id = document.id
104
117
  collection[document_id]["name"].should == "The Dude"
@@ -112,37 +125,53 @@ describe "Basics" do
112
125
  @places = subject['geo_collection']
113
126
  @places.truncate!
114
127
 
115
- pending("`<<` not implemented yet")
116
- @places << { name: "cologne", latitude: 50.948045, longitude: 6.961212 }
117
- @places << { name: "san francisco", latitude: -122.395899, longitude: 37.793621 }
128
+ @places.add_index :geo, on: [:latitude, :longitude]
129
+ @places << { "name" => "cologne", "latitude" => 50.948045, "longitude" => 6.961212 }
130
+ @places << { "name" => "san francisco", "latitude" => -122.395899, "longitude" => 37.793621 }
118
131
  end
119
132
 
120
133
  it "should be possible to query documents near a certain location" do
121
- near_places = @places.near latitude: -122.395898, longitude: 37.793622
122
- near_places.length.should == 1
123
- near_places.first.name.should == "san francisco"
134
+ found_places = @places.near latitude: 50, longitude: 6
135
+ found_places.first["name"].should == "cologne"
124
136
  end
125
137
 
126
138
  it "should be possible to query documents within a certain range" do
127
- near_places = @places.within latitude: 50.948040, longitude: 6.961210, radius: 2
128
- near_places.length.should == 1
129
- near_places.first.name.should == "cologne"
139
+ found_places = @places.within latitude: 50.948040, longitude: 6.961210, radius: 2
140
+ found_places.length.should == 1
141
+ found_places.first["name"].should == "cologne"
142
+ end
143
+ end
144
+
145
+ describe "ranges" do
146
+ before :each do
147
+ @people = subject['range_collection']
148
+ @people.truncate!
149
+
150
+ @people.add_index :skiplist, on: [:age]
151
+ @people << { "name" => "Georg", "age" => 12 }
152
+ @people << { "name" => "Anne", "age" => 21 }
153
+ @people << { "name" => "Jens", "age" => 49 }
154
+ end
155
+
156
+ it "should be possible to query documents for numbers in a certain range" do
157
+ found_people = @people.in_range attribute: "age", left: 20, right: 30, closed: true
158
+ found_people.length.should == 1
159
+ found_people.first["name"].should == "Anne"
130
160
  end
131
161
  end
132
162
 
133
163
  describe "created document" do
134
164
  before :each do
135
- pending("`create` not implemented yet")
136
165
  @collection = subject["documenttests"]
137
166
  @document = @collection.create name: "The Dude"
138
167
  @document_id = @document.id
139
168
  end
140
169
 
141
170
  it "should be possible to manipulate documents and save them" do
142
- @document = @collection[document_id]
171
+ @document = @collection[@document_id]
143
172
  @document["name"] = "Jeffrey Lebowski"
144
173
  @document["name"].should == "Jeffrey Lebowski"
145
- @collection[@document_id].should == "The Dude"
174
+ @collection[@document_id]["name"].should == "The Dude"
146
175
  @document.save
147
176
  @collection[@document_id]["name"].should == "Jeffrey Lebowski"
148
177
  end
@@ -152,16 +181,66 @@ describe "Basics" do
152
181
  @document = @collection.create name: "The Dude"
153
182
  @document_id = @document.id
154
183
  @collection[@document_id].delete
155
- expect { @collection[@document_id] }.to raise_exception Ashikawa::DocumentNotFoundException
184
+ expect { @collection[@document_id] }.to raise_exception Ashikawa::Core::DocumentNotFoundException
185
+ end
186
+
187
+ it "should not be possible to delete a document that doesn't exist" do
188
+ @collection = subject["documenttests"]
189
+ expect { @collection[123].delete }.to raise_exception Ashikawa::Core::DocumentNotFoundException
156
190
  end
157
191
  end
158
192
 
159
- it "should be possible to query documents by example" do
160
- collection = subject["documenttests"]
193
+ describe "setting and deleting indices" do
194
+ before :each do
195
+ @collection = subject["documenttests"]
196
+ end
197
+
198
+ it "should be possible to set indices" do
199
+ @collection.add_index :geo, on: [:latitude, :longitude]
200
+ @collection.add_index :skiplist, on: [:identifier]
201
+ @collection.indices.length.should == 3 # primary index is always set
202
+ @collection.indices[0].class.should == Ashikawa::Core::Index
203
+ end
204
+
205
+ it "should be possible to get an index by ID" do
206
+ index = @collection.add_index :skiplist, on: [:identifier]
207
+ @collection.index(index.id).id.should == index.id
208
+ end
161
209
 
162
- pending("`<<` not implemented yet")
163
- collection << { name: "Random Collection" }
164
- collection.by_example("name" => "Random Collection").length.should == 1
210
+ it "should be possible to remove indices" do
211
+ index = @collection.add_index :skiplist, on: [:identifier]
212
+ index.delete
213
+ @collection.indices.length.should == 1 # primary index is always set
214
+ end
215
+ end
216
+
217
+ describe "querying for documents" do
218
+ it "should be possible to query documents by example" do
219
+ collection = subject["documenttests"]
220
+
221
+ collection << { "name" => "Random Collection" }
222
+ result = collection.by_example example: {"name" => "Random Collection"}
223
+ result.length.should == 1
224
+ end
225
+
226
+ it "should be possible to query documents with AQL" do
227
+ collection = subject["aqltest"]
228
+
229
+ collection << { "name" => "Jeff Lebowski", "bowling" => true }
230
+ collection << { "name" => "Walter Sobchak", "bowling" => true }
231
+ collection << { "name" => "Donny Kerabatsos", "bowling" => true }
232
+ collection << { "name" => "Jeffrey Lebowski", "bowling" => false }
233
+
234
+ results = subject.query "FOR u IN aqltest FILTER u.bowling == true RETURN u",
235
+ batch_size: 2,
236
+ count: true
237
+
238
+ results.length.should == 3
239
+
240
+ results = results.map { |person| person["name"] }
241
+ results.should include "Jeff Lebowski"
242
+ results.should_not include "Jeffrey Lebowski"
243
+ end
165
244
  end
166
245
  end
167
246
  end