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