ashikawa-core 0.1 → 0.2

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