datomic-client 0.1.0 → 0.2.0
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.
- 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
|