ashikawa-core 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ require "rest-client"
2
+ require "json"
3
+
4
+ module Ashikawa
5
+ module Core
6
+ # Represents a Connection via HTTP to a certain host
7
+ class Connection
8
+ # The IP of the connection
9
+ #
10
+ # @return [String]
11
+ # @api public
12
+ # @example Get the IP of the connection
13
+ # connection = Connection.new "http://localhost:8529"
14
+ # connection.ip # => "http://localhost"
15
+ attr_reader :ip
16
+
17
+ # The port of the connection
18
+ #
19
+ # @return [Fixnum]
20
+ # @api public
21
+ # @example Get the port of the connection
22
+ # connection = Connection.new "http://localhost:8529"
23
+ # connection.port # => 8529
24
+ attr_reader :port
25
+
26
+ # Initialize a Connection with a given API String
27
+ #
28
+ # @param [String] api_string IP and Port as a String
29
+ # @api public
30
+ # @example Create a new Connection
31
+ # connection = Connection.new "http://localhost:8529"
32
+ def initialize(api_string)
33
+ @api_string = api_string
34
+ @ip, @port = @api_string.scan(/(\S+):(\d+)/).first
35
+ @port = @port.to_i
36
+ end
37
+
38
+ # Sends a request to a given path (Prepends the api_string automatically)
39
+ #
40
+ # @example get request
41
+ # connection.send_request('/collection/new_collection')
42
+ # @example post request
43
+ # connection.send_request('/collection/new_collection', :post => { :name => 'new_collection' })
44
+ # @param [String] path the path you wish to send a request to.
45
+ # @param [Hash] method_params additional parameters for your request. Only needed if you want to send something other than a GET request.
46
+ # @option method_params [Hash] :post POST data in case you want to send a POST request.
47
+ # @return [Hash] parsed JSON response from the server
48
+ # @api semipublic
49
+ def send_request(path, method_params = {})
50
+ path.gsub! /^\//, ''
51
+
52
+ if method_params.has_key? :post
53
+ JSON.parse RestClient.post("#{@api_string}/_api/#{path}", method_params[:post].to_json )
54
+ elsif method_params.has_key? :put
55
+ JSON.parse RestClient.put("#{@api_string}/_api/#{path}", method_params[:put].to_json )
56
+ elsif method_params.has_key? :delete
57
+ if method_params[:delete] = {}
58
+ JSON.parse RestClient.delete("#{@api_string}/_api/#{path}" )
59
+ else
60
+ JSON.parse RestClient.delete("#{@api_string}/_api/#{path}", method_params[:delete].to_json )
61
+ end
62
+ else
63
+ JSON.parse RestClient.get("#{@api_string}/_api/#{path}")
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,98 @@
1
+ require "ashikawa-core/collection"
2
+ require "ashikawa-core/connection"
3
+
4
+ module Ashikawa
5
+ module Core
6
+ # Represents an ArangoDB database in Ruby
7
+ class Database
8
+ # Initializes the connection to the database
9
+ #
10
+ # @param [Connection, String] connection A Connection object or a String to create a Connection object.
11
+ # @api public
12
+ # @example Access a Database by providing the URL
13
+ # database = Ashikawa::Core::Database.new "http://localhost:8529"
14
+ # @example Access a Database by providing a Connection
15
+ # connection = Connection.new "http://localhost:8529"
16
+ # database = Ashikawa::Core::Database.new connection
17
+ def initialize(connection)
18
+ if connection.class == String
19
+ @connection = Ashikawa::Core::Connection.new connection
20
+ else
21
+ @connection = connection
22
+ end
23
+ end
24
+
25
+ # The IP of the database
26
+ #
27
+ # @return [String]
28
+ # @api public
29
+ # @example Get the IP of the connection
30
+ # database = Ashikawa::Core::Database.new "http://localhost:8529"
31
+ # database.ip # => http://localhost
32
+ def ip
33
+ @connection.ip
34
+ end
35
+
36
+ # The Port of the database
37
+ #
38
+ # @return [Fixnum]
39
+ # @api public
40
+ # @example Get the port for the connection
41
+ # database = Ashikawa::Core::Database.new "http://localhost:8529"
42
+ # database.port # => 8529
43
+ def port
44
+ @connection.port
45
+ end
46
+
47
+ # Returns a list of all collections defined in the database
48
+ #
49
+ # @return [Array<Collection>]
50
+ # @api public
51
+ # @example Get an Array containing the Collections in the database
52
+ # database = Ashikawa::Core::Database.new "http://localhost:8529"
53
+ # database["a"]
54
+ # database["b"]
55
+ # database.collections # => [ #<Collection name="a">, #<Collection name="b">]
56
+ def collections
57
+ server_response = @connection.send_request "/collection"
58
+ server_response["collections"].map { |collection| Ashikawa::Core::Collection.new self, collection }
59
+ end
60
+
61
+ # Get or create a Collection based on name or ID
62
+ #
63
+ # @param [String, Fixnum] collection_identifier The name or ID of the collection
64
+ # @return [Collection]
65
+ # @api public
66
+ # @example Get a Collection from the database by name
67
+ # database = Ashikawa::Core::Database.new "http://localhost:8529"
68
+ # database["a"] # => #<Collection name="a">
69
+ # @example Get a Collection from the database by ID
70
+ # database = Ashikawa::Core::Database.new "http://localhost:8529"
71
+ # database["7254820"] # => #<Collection id=7254820>
72
+ def [](collection_identifier)
73
+ begin
74
+ server_response = @connection.send_request "/collection/#{collection_identifier}"
75
+ rescue RestClient::ResourceNotFound
76
+ server_response = @connection.send_request "/collection", post: { name: collection_identifier }
77
+ end
78
+
79
+ Ashikawa::Core::Collection.new self, server_response
80
+ end
81
+
82
+ # Sends a request to a given path (Prepends the api_string automatically)
83
+ #
84
+ # @example Send a get request to the database
85
+ # connection.send_request('/collection/new_collection')
86
+ # @example Send a post request to the database
87
+ # connection.send_request('/collection/new_collection', :post => { :name => 'new_collection' })
88
+ # @param [String] path the path you wish to send a request to.
89
+ # @param [Hash] method_params additional parameters for your request. Only needed if you want to send something other than a GET request.
90
+ # @option method_params [Hash] :post POST data in case you want to send a POST request.
91
+ # @return [Hash] parsed JSON response from the server
92
+ # @api semipublic
93
+ def send_request(path, method_params = {})
94
+ @connection.send_request path, method_params
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,37 @@
1
+ module Ashikawa
2
+ module Core
3
+ # Represents a certain Document within a certain Collection
4
+ class Document
5
+ # The ID of the document including the Collection prefix
6
+ #
7
+ # @return [String]
8
+ # @api public
9
+ # @example Get the ID for a Document
10
+ # document = Ashikawa::Core::Document.new "1234567/2345678", "3456789"
11
+ # document.id # => "1234567/2345678"
12
+ attr_reader :id
13
+
14
+ # The current revision of the document
15
+ #
16
+ # @return [String]
17
+ # @api public
18
+ # @example Get the Revision for a Document
19
+ # document = Ashikawa::Core::Document.new "1234567/2345678", "3456789"
20
+ # document.revision # => "3456789"
21
+ attr_reader :revision
22
+
23
+ # Initialize a Document with an ID and revision
24
+ #
25
+ # @param [String] id The complete ID including the Collection prefix
26
+ # @param [String] revision
27
+ # @api public
28
+ # @example The Document 2345678 in the Collection 1234567 in revision 3456789
29
+ # document = Ashikawa::Core::Document.new "1234567/2345678", "3456789"
30
+ def initialize(id, revision)
31
+ @id = id
32
+ @revision = revision
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ module Ashikawa
2
+ module Core
3
+ # Current version of Ashikawa::Core
4
+ VERSION = "0.1"
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "example_1",
3
+ "waitForSync": true,
4
+ "id": 4588,
5
+ "status": 3,
6
+ "error": false,
7
+ "code": 200
8
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "new_collection",
3
+ "waitForSync": true,
4
+ "id": 4590,
5
+ "status": 3,
6
+ "error": false,
7
+ "code": 200,
8
+ "count": 54
9
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "new_collection",
3
+ "waitForSync": true,
4
+ "id": 4590,
5
+ "status": 3,
6
+ "error": false,
7
+ "code": 200
8
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "UnitTestsCollectionBasics",
3
+ "code": 200,
4
+ "figures": {
5
+ "datafiles": {
6
+ "count": 1
7
+ },
8
+ "alive": {
9
+ "size": 0,
10
+ "count": 0
11
+ },
12
+ "dead": {
13
+ "size": 2384,
14
+ "count": 149
15
+ }
16
+ },
17
+ "waitForSync": true,
18
+ "id": 73482,
19
+ "journalSize": 134217728,
20
+ "count": 0,
21
+ "status": 3,
22
+ "error": false
23
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "collections": [
3
+ {
4
+ "name": "example_1",
5
+ "waitForSync": true,
6
+ "id": 4588,
7
+ "status": 3
8
+ },
9
+ {
10
+ "name": "example_2",
11
+ "waitForSync": true,
12
+ "id": 4589,
13
+ "status": 3
14
+ }
15
+ ],
16
+ "code": 200,
17
+ "error": false,
18
+ "names":{
19
+ "example_1": {
20
+ "name": "example_1",
21
+ "waitForSync": true,
22
+ "id": 4588,
23
+ "status": 3
24
+ },
25
+ "example_2": {
26
+ "name": "example_2",
27
+ "waitForSync": true,
28
+ "id": 4589,
29
+ "status": 3
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "error": true,
3
+ "code": 404,
4
+ "errorNum": 1203,
5
+ "errorMessage": "unknown collection 'new_collection'"
6
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "_rev": 28411694,
3
+ "_id": "4590/333",
4
+ "Hello": "World"
5
+ }
@@ -0,0 +1,4 @@
1
+ [
2
+ { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" },
3
+ { "_id" : "12346/3872", "_rev" : 3872, "key" : "value" }
4
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" }
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ { "_id" : "12346/3872", "_rev" : 3872, "key" : "value" }
3
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ { "_id" : "12345/57463", "_rev" : 57463, "hello" : "world" }
3
+ ]
@@ -0,0 +1,5 @@
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
+ ]
@@ -0,0 +1,3 @@
1
+ [
2
+ { "_id" : "12345/57463", "_rev" : 57463, "name" : "world/0/0", "loc" : [0, 0] }
3
+ ]
@@ -0,0 +1,167 @@
1
+ require 'integration/spec_helper'
2
+
3
+ describe "Basics" do
4
+ subject { "http://localhost:8529" }
5
+
6
+ it "should have booted up an ArangoDB instance" do
7
+ expect { JSON.parse RestClient.get(subject) }.to raise_error(RestClient::NotImplemented)
8
+ end
9
+
10
+ describe "initialized database" do
11
+ subject { Ashikawa::Core::Database.new "http://localhost:8529" }
12
+
13
+ it "should do what the README describes" do
14
+ subject["my_collection"]
15
+ subject["my_collection"].name = "new_name"
16
+ subject["new_name"].delete
17
+ end
18
+
19
+ it "should create and delete collections" do
20
+ subject.collections.should ==[]
21
+ subject["collection_1"]
22
+ subject["collection_2"]
23
+ subject["collection_3"]
24
+ subject.collections.length.should == 3
25
+ subject["collection_3"].delete
26
+ subject.collections.length.should == 2
27
+ end
28
+
29
+ it "should be possible to change the name of a collection" do
30
+ my_collection = subject["test_collection"]
31
+ my_collection.name.should == "test_collection"
32
+ my_collection.name = "my_new_name"
33
+ my_collection.name.should == "my_new_name"
34
+ end
35
+
36
+ it "should be possible to find a collection by ID" do
37
+ my_collection = subject["test_collection"]
38
+ subject[my_collection.id].name.should == "test_collection"
39
+ end
40
+
41
+ it "should be possible to load and unload collections" do
42
+ my_collection = subject["test_collection"]
43
+ my_collection.loaded?.should be_true
44
+ my_collection.unload
45
+ my_id = my_collection.id
46
+ my_collection = subject[my_id]
47
+ subject[my_id].loaded?.should be_false
48
+ end
49
+
50
+ it "should be possible to get figures" do
51
+ my_collection = subject["test_collection"]
52
+ my_collection.figure(:datafiles_count).class.should == Fixnum
53
+ my_collection.figure(:alive_size).class.should == Fixnum
54
+ my_collection.figure(:alive_count).class.should == Fixnum
55
+ my_collection.figure(:dead_size).class.should == Fixnum
56
+ my_collection.figure(:dead_count).class.should == Fixnum
57
+ end
58
+
59
+ it "should change and receive information about waiting for sync" do
60
+ my_collection = subject["my_collection"]
61
+ my_collection.wait_for_sync?.should be_false
62
+ my_collection.wait_for_sync = true
63
+ my_collection.wait_for_sync?.should be_true
64
+ end
65
+
66
+ it "should be possible to get information about the number of documents" do
67
+ pending("`<<` not implemented yet")
68
+ empty_collection = subject["empty_collection"]
69
+ empty_collection.length.should == 0
70
+ empty_collection << { name: "testname", age: 27}
71
+ empty_collection << { name: "anderer name", age: 28}
72
+ empty_collection.length.should == 2
73
+ empty_collection.truncate!
74
+ empty_collection.length.should == 0
75
+ end
76
+
77
+ it "should return all documents of a collection" do
78
+ pending("`<<` not implemented yet")
79
+ empty_collection = subject["empty_collection"]
80
+ empty_collection << { name: "testname", age: 27}
81
+ empty_collection.all.first["name"].should == "testname"
82
+ end
83
+
84
+ it "should be possible to limit and skip results" do
85
+ pending("`<<` not implemented yet")
86
+ empty_collection = subject["empty_collection"]
87
+ empty_collection.truncate!
88
+
89
+ empty_collection << { name: "test1"}
90
+ empty_collection << { name: "test2"}
91
+ empty_collection << { name: "test3"}
92
+
93
+ empty_collection.all(limit: 2).length.should == 2
94
+ empty_collection.all(skip: 2).length.should == 1
95
+ end
96
+
97
+
98
+ it "should be possible to access and create documents from a collection" do
99
+ collection = subject["documenttests"]
100
+
101
+ pending("`create` not implemented yet")
102
+ document = collection.create name: "The Dude", bowling: true
103
+ document_id = document.id
104
+ collection[document_id]["name"].should == "The Dude"
105
+
106
+ collection[document_id] = { name: "Other Dude", bowling: true }
107
+ collection[document_id]["name"].should == "Other Dude"
108
+ end
109
+
110
+ describe "geolocation" do
111
+ before :each do
112
+ @places = subject['geo_collection']
113
+ @places.truncate!
114
+
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 }
118
+ end
119
+
120
+ 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"
124
+ end
125
+
126
+ 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"
130
+ end
131
+ end
132
+
133
+ describe "created document" do
134
+ before :each do
135
+ pending("`create` not implemented yet")
136
+ @collection = subject["documenttests"]
137
+ @document = @collection.create name: "The Dude"
138
+ @document_id = @document.id
139
+ end
140
+
141
+ it "should be possible to manipulate documents and save them" do
142
+ @document = @collection[document_id]
143
+ @document["name"] = "Jeffrey Lebowski"
144
+ @document["name"].should == "Jeffrey Lebowski"
145
+ @collection[@document_id].should == "The Dude"
146
+ @document.save
147
+ @collection[@document_id]["name"].should == "Jeffrey Lebowski"
148
+ end
149
+
150
+ it "should be possible to delete a document" do
151
+ @collection = subject["documenttests"]
152
+ @document = @collection.create name: "The Dude"
153
+ @document_id = @document.id
154
+ @collection[@document_id].delete
155
+ expect { @collection[@document_id] }.to raise_exception Ashikawa::DocumentNotFoundException
156
+ end
157
+ end
158
+
159
+ it "should be possible to query documents by example" do
160
+ collection = subject["documenttests"]
161
+
162
+ pending("`<<` not implemented yet")
163
+ collection << { name: "Random Collection" }
164
+ collection.by_example("name" => "Random Collection").length.should == 1
165
+ end
166
+ end
167
+ end