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 CHANGED
@@ -1,2 +1,7 @@
1
+ ## 0.2.0
2
+ * Added EDN support
3
+ * 2 pending specs fixed
4
+ * Introduced our own Response object
5
+
1
6
  ## 0.1.0
2
7
  * Initial release that works with datomic 0.8.3488!
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/README.md CHANGED
@@ -16,21 +16,35 @@ and then `bundle`.
16
16
 
17
17
  ## Usage
18
18
 
19
- ```sh
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, "TODO")
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
@@ -1 +1,5 @@
1
1
  require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+ task :default => :spec
@@ -1,6 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
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
@@ -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
- VERSION = '0.1.0'
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
- RestClient.get db_url(dbname)
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
- RestClient.post db_url(dbname), data
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
- RestClient.get db_url(dbname, "datoms/#{index}"), :params => params
34
+ get db_url(dbname, "datoms/#{index}"), :params => params
26
35
  end
27
36
 
28
37
  def range(dbname, params = {})
29
- RestClient.get db_url(dbname, 'range'), :params => params
38
+ get db_url(dbname, 'range'), :params => params
30
39
  end
31
40
 
32
41
  def entity(dbname, id, params = {})
33
- RestClient.get db_url(dbname, 'entity', id), :params => params
42
+ get db_url(dbname, 'entity', id), :params => params
34
43
  end
35
44
 
36
- def query(query, params = {})
37
- RestClient.get root_url("api/query"), :params => params.merge(:q => query)
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
- RestClient.get root_url('monitor', @storage, dbname)
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
@@ -0,0 +1,5 @@
1
+ module Datomic
2
+ class Client
3
+ VERSION = '0.2.0'
4
+ end
5
+ end
@@ -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.body.should include(':basis-t')
35
- resp.body.should include(':db/alias')
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.body.should include(':basis-t')
41
- resp.body.should include(':db/alias')
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
- pending "til valid transaction data given"
56
- resp = client.transact('test-transact', "[:db/add 1 :some :value]")
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.body.should match MAP
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.body.should match VEC
75
+ resp.data.should be_a(Array)
71
76
  end
72
77
  end
73
78
 
74
- it "raises 500 error for invalid index" do
75
- expect { client.datoms('test-datoms', 'blarg') }.
76
- to raise_error(RestClient::InternalServerError, /500 Internal Server Error/)
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.body.should == "[]"
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.body.should match VEC
97
+ resp.data.should be_a(Array)
93
98
  end
94
99
 
95
- it "raises 400 without required attribute" do
96
- expect { client.range('test-range') }.
97
- to raise_error(RestClient::BadRequest, /400 Bad Request/)
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.body.should match MAP
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.body.should match MAP
118
+ resp.data.should be_a(Hash)
114
119
  end
115
120
  end
116
121
 
117
122
  describe "#query" do
118
- let(:client) { Datomic::Client.new datomic_uri }
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
- pending "til valid query given"
122
- resp = client.query("[:find ?e :where [?e :id 1]]")
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.body.should match VEC
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
- expect { client.events('zzzz') }.
154
- to raise_error(RestClient::ServiceUnavailable, /503 Service Unavailable/)
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
@@ -0,0 +1,9 @@
1
+ [
2
+ {:db/id #db/id[:db.part/db]
3
+ :db/ident :community/name
4
+ :db/valueType :db.type/string
5
+ :db/cardinality :db.cardinality/one
6
+ :db/fulltext true
7
+ :db/doc "A community's name"
8
+ :db.install/_attribute :db.part/db}
9
+ ]
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.1.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-11 00:00:00.000000000 Z
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: '0'
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: '0'
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: '0'
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: '0'
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