ashikawa-core 0.1

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.
@@ -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