datomic-client 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/Gemfile +2 -0
- data/README.md +23 -6
- data/Rakefile +4 -0
- data/datomic-client.gemspec +4 -4
- data/lib/datomic/client.rb +32 -11
- data/lib/datomic/client/response.rb +29 -0
- data/lib/datomic/client/version.rb +5 -0
- data/spec/datomic_client_spec.rb +56 -31
- data/spec/datomic_response_spec.rb +32 -0
- data/spec/fixtures/seattle-schema.dtm +9 -0
- metadata +40 -10
data/CHANGELOG.md
CHANGED
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -16,21 +16,35 @@ and then `bundle`.
|
|
16
16
|
|
17
17
|
## Usage
|
18
18
|
|
19
|
-
```
|
19
|
+
```ruby
|
20
20
|
# In another shell in datomic's directory
|
21
21
|
$ bin/rest 9000 socrates datomic:mem://
|
22
22
|
|
23
|
+
# Assuming you have a schema with a :"community/name" attribute
|
23
24
|
# In project's directory
|
24
25
|
$ irb -rdatomic/client
|
25
26
|
>> dbname = 'cosas'
|
26
27
|
>> datomic = Datomic::Client.new 'http://localhost:9000', 'socrates'
|
27
|
-
>> datomic.create_database(dbname)
|
28
|
+
>> resp = datomic.create_database(dbname)
|
29
|
+
=> #<Datomic::Client::Response:0x0000010157bc58 @body="", @args={:method=>:put,
|
30
|
+
:url=>"http://localhost:9000/db/socrates/test-1347638297", :payload=>{}, :headers=>{}},
|
31
|
+
@net_http=#<Net::HTTPCreated 201 Created readbody=true>, @rest_client_response="">
|
32
|
+
>> resp.code
|
33
|
+
=> 201
|
34
|
+
>> resp.body
|
35
|
+
=> ''
|
36
|
+
|
37
|
+
# Most responses are in edn and thus can be accessed natively
|
38
|
+
>> resp = datomic.query(dbname, '[:find ?c :where [?c :community/name]]')
|
39
|
+
>> resp.data
|
40
|
+
=> [[1]]
|
41
|
+
|
42
|
+
# additional endpoints
|
28
43
|
>> datomic.database_info(dbname)
|
29
|
-
>> datomic.transact(dbname, "
|
44
|
+
>> datomic.transact(dbname, [[:"db/add", 1, :"community/name", "Some Community"]])
|
30
45
|
>> datomic.datoms(dbname, 'aevt')
|
31
46
|
>> datomic.range(dbname, :a => "db/ident")
|
32
|
-
>> datomic.entity(1)
|
33
|
-
>> datomic.query("TODO")
|
47
|
+
>> datomic.entity(dbname, 1)
|
34
48
|
>> datomic.monitor(dbname)
|
35
49
|
>> datomic.events(dbname) {|r| puts "Received: #{r.inspect}" }
|
36
50
|
```
|
@@ -43,13 +57,16 @@ Please report them [on github](http://github.com/cldwalker/datomic-client/issues
|
|
43
57
|
|
44
58
|
## Credits
|
45
59
|
|
60
|
+
* @crnixon for adding edn support
|
46
61
|
* @flyingmachine for starting this with me
|
47
62
|
|
48
63
|
##Todo
|
49
64
|
|
65
|
+
* Allow entity endpoint to take an e param
|
50
66
|
* Fix pending specs
|
51
67
|
|
52
68
|
## Links
|
53
69
|
|
54
|
-
* [API documentation](http://docs.datomic.com/rest.html)
|
70
|
+
* [API documentation](http://docs.datomic.com/rest.html) - Actual documentation now resides on root
|
71
|
+
url of datomic endpoint
|
55
72
|
* [Initial announcement](http://blog.datomic.com/2012/09/rest-api.html)
|
data/Rakefile
CHANGED
data/datomic-client.gemspec
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require "datomic/client"
|
2
|
+
require File.expand_path('../lib/datomic/client/version', __FILE__)
|
4
3
|
|
5
4
|
Gem::Specification.new do |s|
|
6
5
|
s.name = "datomic-client"
|
@@ -17,7 +16,8 @@ Gem::Specification.new do |s|
|
|
17
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
17
|
|
19
18
|
s.add_development_dependency 'bundler'
|
20
|
-
s.add_development_dependency 'rspec'
|
19
|
+
s.add_development_dependency 'rspec', '~> 2.11'
|
21
20
|
s.add_development_dependency 'rake', '~> 0.9.2.2'
|
22
|
-
s.add_dependency 'rest-client'
|
21
|
+
s.add_dependency 'rest-client', '~> 1.6.7'
|
22
|
+
s.add_dependency 'edn', '~> 0.9.1'
|
23
23
|
end
|
data/lib/datomic/client.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
+
require 'datomic/client/version'
|
2
|
+
require 'datomic/client/response'
|
1
3
|
require 'rest-client'
|
4
|
+
require 'set' # Remove when fixed upstream
|
5
|
+
require 'edn'
|
2
6
|
|
3
7
|
module Datomic
|
4
8
|
class Client
|
5
|
-
|
9
|
+
HANDLE_RESPONSE = lambda do |body, request, response|
|
10
|
+
Response.new body, response, request
|
11
|
+
end
|
6
12
|
|
7
13
|
def initialize(url, storage = nil)
|
8
14
|
@url = url
|
@@ -10,47 +16,58 @@ module Datomic
|
|
10
16
|
end
|
11
17
|
|
12
18
|
def create_database(dbname)
|
13
|
-
RestClient.put db_url(dbname), {}
|
19
|
+
RestClient.put db_url(dbname), {}, &HANDLE_RESPONSE
|
14
20
|
end
|
15
21
|
|
16
22
|
def database_info(dbname)
|
17
|
-
|
23
|
+
get db_url(dbname)
|
18
24
|
end
|
19
25
|
|
26
|
+
# Data can be a ruby data structure or a string representing clojure data
|
20
27
|
def transact(dbname, data)
|
21
|
-
|
28
|
+
data = transmute_data(data)
|
29
|
+
RestClient.post(db_url(dbname), data, :content_type => 'application/x-edn', &HANDLE_RESPONSE)
|
22
30
|
end
|
23
31
|
|
32
|
+
# Index only has certain valid types. See datomic's docs for details.
|
24
33
|
def datoms(dbname, index, params = {})
|
25
|
-
|
34
|
+
get db_url(dbname, "datoms/#{index}"), :params => params
|
26
35
|
end
|
27
36
|
|
28
37
|
def range(dbname, params = {})
|
29
|
-
|
38
|
+
get db_url(dbname, 'range'), :params => params
|
30
39
|
end
|
31
40
|
|
32
41
|
def entity(dbname, id, params = {})
|
33
|
-
|
42
|
+
get db_url(dbname, 'entity', id), :params => params
|
34
43
|
end
|
35
44
|
|
36
|
-
|
37
|
-
|
45
|
+
# Query can be a ruby data structure or a string representing clojure data
|
46
|
+
def query(dbname, query, params = {})
|
47
|
+
query = transmute_data(query)
|
48
|
+
args = [{:"db/alias" => [@storage, dbname].join('/')}].to_edn
|
49
|
+
get root_url("api/query"), :params => params.merge(:q => query, :args => args)
|
38
50
|
end
|
39
51
|
|
40
52
|
def monitor(dbname)
|
41
|
-
|
53
|
+
get root_url('monitor', @storage, dbname)
|
42
54
|
end
|
43
55
|
|
44
56
|
# Given block is called with Net::HTTPOK response from event
|
45
57
|
def events(dbname, &block)
|
58
|
+
# can't use RestClient.get b/c of :block_response
|
46
59
|
RestClient::Request.execute(:method => :get,
|
47
60
|
:url => root_url('events', @storage, dbname),
|
48
61
|
:headers => {:accept => "text/event-stream"},
|
49
|
-
:block_response => block)
|
62
|
+
:block_response => block, &HANDLE_RESPONSE)
|
50
63
|
end
|
51
64
|
|
52
65
|
private
|
53
66
|
|
67
|
+
def get(*args)
|
68
|
+
RestClient.get(*args, &HANDLE_RESPONSE)
|
69
|
+
end
|
70
|
+
|
54
71
|
def root_url(*parts)
|
55
72
|
[@url].concat(parts).join('/')
|
56
73
|
end
|
@@ -58,5 +75,9 @@ module Datomic
|
|
58
75
|
def db_url(dbname, *parts)
|
59
76
|
root_url 'db', @storage, dbname, *parts
|
60
77
|
end
|
78
|
+
|
79
|
+
def transmute_data(data)
|
80
|
+
data.is_a?(String) ? data : data.to_edn
|
81
|
+
end
|
61
82
|
end
|
62
83
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Datomic
|
2
|
+
class Client
|
3
|
+
class Response
|
4
|
+
# Response body as a string
|
5
|
+
attr_reader :body
|
6
|
+
# Underlying Net:HTTP response
|
7
|
+
attr_reader :net_http
|
8
|
+
|
9
|
+
def initialize(body, response, request)
|
10
|
+
@body = body
|
11
|
+
@args = request.args
|
12
|
+
@net_http = response
|
13
|
+
# used to parse response cookies and headers
|
14
|
+
@rest_client_response = RestClient::Response.create(body, response, @args)
|
15
|
+
end
|
16
|
+
|
17
|
+
# converts an EDN body to a data structure i.e. array, hash
|
18
|
+
def data
|
19
|
+
@data ||= EDN.read @body
|
20
|
+
end
|
21
|
+
|
22
|
+
[:code, :headers, :cookies, :raw_headers].each do |meth|
|
23
|
+
define_method(meth) do
|
24
|
+
@rest_client_response.public_send(meth)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/datomic_client_spec.rb
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
require 'datomic/client'
|
2
2
|
|
3
|
+
# datomic's `rest` needs to run for these tests to pass i.e.
|
4
|
+
# bin/rest 9000 socrates datomic:mem://
|
3
5
|
describe Datomic::Client do
|
4
6
|
let(:datomic_uri) { ENV['DATOMIC_URI'] || 'http://localhost:9000' }
|
5
|
-
# datomic's `rest` needs to run for these tests to pass i.e.
|
6
|
-
# bin/rest 9000 socrates datomic:mem://
|
7
7
|
let(:client) do
|
8
8
|
Datomic::Client.new datomic_uri, ENV['DATOMIC_STORAGE'] || 'socrates'
|
9
9
|
end
|
10
|
-
|
11
|
-
VEC = /^\[.*\]$/
|
12
|
-
MAP = /^\{.*\}$/
|
10
|
+
let(:schema) { File.read(File.expand_path('../fixtures/seattle-schema.dtm', __FILE__)) }
|
13
11
|
|
14
12
|
describe "#create_database" do
|
15
13
|
it "returns 201 when creating a new database" do
|
@@ -31,14 +29,14 @@ describe Datomic::Client do
|
|
31
29
|
it "returns 200 for existing database" do
|
32
30
|
resp = client.database_info('test-database_info')
|
33
31
|
resp.code.should == 200
|
34
|
-
resp.
|
35
|
-
resp.
|
32
|
+
resp.data.should have_key(:"basis-t")
|
33
|
+
resp.data.should have_key(:"db/alias")
|
36
34
|
end
|
37
35
|
|
38
36
|
it "returns database info for existing database" do
|
39
37
|
resp = client.database_info('test-database_info')
|
40
|
-
resp.
|
41
|
-
resp.
|
38
|
+
resp.data.should have_key(:"basis-t")
|
39
|
+
resp.data.should have_key(:"db/alias")
|
42
40
|
end
|
43
41
|
|
44
42
|
it "returns 404 for nonexistent database" do
|
@@ -51,11 +49,18 @@ describe Datomic::Client do
|
|
51
49
|
describe "#transact" do
|
52
50
|
before { client.create_database('test-transact') }
|
53
51
|
|
54
|
-
it "returns correct response" do
|
55
|
-
|
56
|
-
resp
|
52
|
+
it "returns correct response with string of data" do
|
53
|
+
resp = client.transact('test-transact', schema)
|
54
|
+
resp.code.should == 200
|
55
|
+
resp.data.should be_a(Hash)
|
56
|
+
resp.data.keys.sort.should == [:"db-after", :"db-before", :tempids, :"tx-data"]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns correct response with array of data" do
|
60
|
+
resp = client.transact('test-transact', [[:"db/add", 1, :"community/name", "Some Community"]])
|
57
61
|
resp.code.should == 200
|
58
|
-
resp.
|
62
|
+
resp.data.should be_a(Hash)
|
63
|
+
resp.data.keys.sort.should == [:"db-after", :"db-before", :tempids, :"tx-data"]
|
59
64
|
end
|
60
65
|
end
|
61
66
|
|
@@ -67,19 +72,19 @@ describe Datomic::Client do
|
|
67
72
|
pending "possible bug" if index == 'vaet'
|
68
73
|
resp = client.datoms('test-datoms', index)
|
69
74
|
resp.code.should == 200
|
70
|
-
resp.
|
75
|
+
resp.data.should be_a(Array)
|
71
76
|
end
|
72
77
|
end
|
73
78
|
|
74
|
-
it "
|
75
|
-
|
76
|
-
|
79
|
+
it "returns 500 for invalid index" do
|
80
|
+
resp = client.datoms('test-datoms', 'blarg')
|
81
|
+
resp.code.should == 500
|
77
82
|
end
|
78
83
|
|
79
84
|
it "returns correct response with limit param" do
|
80
85
|
resp = client.datoms('test-datoms', "eavt", :limit => 0)
|
81
86
|
resp.code.should == 200
|
82
|
-
resp.
|
87
|
+
resp.data.should == []
|
83
88
|
end
|
84
89
|
end
|
85
90
|
|
@@ -89,12 +94,12 @@ describe Datomic::Client do
|
|
89
94
|
it "returns correct response with required attribute" do
|
90
95
|
resp = client.range('test-range', :a => "db/ident")
|
91
96
|
resp.code.should == 200
|
92
|
-
resp.
|
97
|
+
resp.data.should be_a(Array)
|
93
98
|
end
|
94
99
|
|
95
|
-
it "
|
96
|
-
|
97
|
-
|
100
|
+
it "returns 400 without required attribute" do
|
101
|
+
resp = client.range('test-range')
|
102
|
+
resp.code.should == 400
|
98
103
|
end
|
99
104
|
end
|
100
105
|
|
@@ -104,25 +109,45 @@ describe Datomic::Client do
|
|
104
109
|
it "returns correct response" do
|
105
110
|
resp = client.entity('test-entity', 1)
|
106
111
|
resp.code.should == 200
|
107
|
-
resp.
|
112
|
+
resp.data.should be_a(Hash)
|
108
113
|
end
|
109
114
|
|
110
115
|
it "returns correct response with valid param" do
|
111
116
|
resp = client.entity('test-entity', 1, :since => 0)
|
112
117
|
resp.code.should == 200
|
113
|
-
resp.
|
118
|
+
resp.data.should be_a(Hash)
|
114
119
|
end
|
115
120
|
end
|
116
121
|
|
117
122
|
describe "#query" do
|
118
|
-
|
123
|
+
before {
|
124
|
+
client.create_database('test-query')
|
125
|
+
client.transact('test-query', schema)
|
126
|
+
client.transact('test-query', [[:"db/add", 1, :"community/name", "Some Community"]])
|
127
|
+
}
|
128
|
+
|
129
|
+
it "returns a correct response with a string query" do
|
130
|
+
resp = client.query('test-query', '[:find ?c :where [?c :community/name]]')
|
131
|
+
resp.code.should == 200
|
132
|
+
resp.data.should be_a(Array)
|
133
|
+
resp.data.should == [[1]]
|
134
|
+
end
|
119
135
|
|
120
|
-
it "returns a correct response" do
|
121
|
-
|
122
|
-
resp
|
136
|
+
it "returns a correct response with limit param" do
|
137
|
+
resp = client.query('test-query', '[:find ?c :where [?c :community/name]]', :limit => 0)
|
138
|
+
resp.code.should == 200
|
139
|
+
resp.data.should be_a(Array)
|
140
|
+
resp.data.should == []
|
141
|
+
end
|
142
|
+
|
143
|
+
it "returns a correct response with a data query" do
|
144
|
+
resp = client.query('test-query',
|
145
|
+
[:find, EDN::Type::Symbol.new('?c'), :where,
|
146
|
+
[EDN::Type::Symbol.new('?c'), :"community/name"]])
|
123
147
|
resp.code.should == 200
|
124
|
-
resp.
|
148
|
+
resp.data.should be_a(Array)
|
125
149
|
end
|
150
|
+
|
126
151
|
end
|
127
152
|
|
128
153
|
describe "#monitor" do
|
@@ -150,8 +175,8 @@ describe Datomic::Client do
|
|
150
175
|
end
|
151
176
|
|
152
177
|
it "returns a 503 for nonexistent db" do
|
153
|
-
|
154
|
-
|
178
|
+
resp = client.events('zzzz')
|
179
|
+
resp.code.should == 503
|
155
180
|
end
|
156
181
|
end
|
157
182
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'datomic/client'
|
2
|
+
|
3
|
+
describe Datomic::Client::Response do
|
4
|
+
let(:datomic_uri) { ENV['DATOMIC_URI'] || 'http://localhost:9000' }
|
5
|
+
let(:client) do
|
6
|
+
Datomic::Client.new datomic_uri, ENV['DATOMIC_STORAGE'] || 'socrates'
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.new' do
|
10
|
+
let(:resp) { client.create_database("test-#{Time.now.to_i}") }
|
11
|
+
|
12
|
+
it "returns a valid code" do
|
13
|
+
resp.code.should be_a Integer
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns a valid string" do
|
17
|
+
resp.body.should be_a String
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns valid headers" do
|
21
|
+
resp.headers.should be_a Hash
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns valid cookies" do
|
25
|
+
resp.cookies.should be_a Hash
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns valid raw headers" do
|
29
|
+
resp.raw_headers.should be_a Hash
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datomic-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -32,17 +32,17 @@ dependencies:
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
|
-
- -
|
35
|
+
- - ~>
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
37
|
+
version: '2.11'
|
38
38
|
type: :development
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
|
-
- -
|
43
|
+
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: '
|
45
|
+
version: '2.11'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: rake
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -64,30 +64,52 @@ dependencies:
|
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
65
65
|
none: false
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - ~>
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: 1.6.7
|
70
70
|
type: :runtime
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
|
-
- -
|
75
|
+
- - ~>
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
77
|
+
version: 1.6.7
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: edn
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.9.1
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.9.1
|
78
94
|
description: This gem provides a simple way to use datomic's http API - http://docs.datomic.com/rest.html.
|
79
95
|
email: gabriel.horner@gmail.com
|
80
96
|
executables: []
|
81
97
|
extensions: []
|
82
98
|
extra_rdoc_files: []
|
83
99
|
files:
|
100
|
+
- .gitignore
|
84
101
|
- CHANGELOG.md
|
102
|
+
- Gemfile
|
85
103
|
- LICENSE.txt
|
86
104
|
- README.md
|
87
105
|
- Rakefile
|
88
106
|
- datomic-client.gemspec
|
89
107
|
- lib/datomic/client.rb
|
108
|
+
- lib/datomic/client/response.rb
|
109
|
+
- lib/datomic/client/version.rb
|
90
110
|
- spec/datomic_client_spec.rb
|
111
|
+
- spec/datomic_response_spec.rb
|
112
|
+
- spec/fixtures/seattle-schema.dtm
|
91
113
|
homepage: http://github.com/cldwalker/datomic-client
|
92
114
|
licenses: []
|
93
115
|
post_install_message:
|
@@ -100,12 +122,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
122
|
- - ! '>='
|
101
123
|
- !ruby/object:Gem::Version
|
102
124
|
version: '0'
|
125
|
+
segments:
|
126
|
+
- 0
|
127
|
+
hash: -1359618081440399035
|
103
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
129
|
none: false
|
105
130
|
requirements:
|
106
131
|
- - ! '>='
|
107
132
|
- !ruby/object:Gem::Version
|
108
133
|
version: '0'
|
134
|
+
segments:
|
135
|
+
- 0
|
136
|
+
hash: -1359618081440399035
|
109
137
|
requirements: []
|
110
138
|
rubyforge_project:
|
111
139
|
rubygems_version: 1.8.24
|
@@ -114,3 +142,5 @@ specification_version: 3
|
|
114
142
|
summary: http client for datomic's API
|
115
143
|
test_files:
|
116
144
|
- spec/datomic_client_spec.rb
|
145
|
+
- spec/datomic_response_spec.rb
|
146
|
+
- spec/fixtures/seattle-schema.dtm
|