grendel-ruby 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE.md +26 -0
- data/README.md +192 -0
- data/Rakefile +48 -0
- data/TODO.md +8 -0
- data/VERSION +1 -0
- data/grendel-ruby.gemspec +89 -0
- data/lib/core_ext/hash.rb +17 -0
- data/lib/grendel.rb +16 -0
- data/lib/grendel/client.rb +63 -0
- data/lib/grendel/document.rb +27 -0
- data/lib/grendel/document_manager.rb +47 -0
- data/lib/grendel/link.rb +15 -0
- data/lib/grendel/link_manager.rb +30 -0
- data/lib/grendel/linked_document.rb +30 -0
- data/lib/grendel/linked_document_manager.rb +31 -0
- data/lib/grendel/user.rb +68 -0
- data/lib/grendel/user_manager.rb +31 -0
- data/spec/grendel/client_spec.rb +19 -0
- data/spec/grendel/document_manager_spec.rb +104 -0
- data/spec/grendel/document_spec.rb +23 -0
- data/spec/grendel/link_manager_spec.rb +80 -0
- data/spec/grendel/link_spec.rb +12 -0
- data/spec/grendel/linked_document_manager_spec.rb +91 -0
- data/spec/grendel/linked_document_spec.rb +23 -0
- data/spec/grendel/user_manager_spec.rb +89 -0
- data/spec/grendel/user_spec.rb +49 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +21 -0
- metadata +140 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module Grendel
|
2
|
+
class Document
|
3
|
+
attr_accessor :user, :name, :uri, :data, :content_type
|
4
|
+
|
5
|
+
def initialize(user, params)
|
6
|
+
params.symbolize_keys!
|
7
|
+
@user = user
|
8
|
+
@client = user.client
|
9
|
+
@name = params[:name]
|
10
|
+
@data = params[:data]
|
11
|
+
@content_type = params[:content_type]
|
12
|
+
@uri = params[:uri] ?
|
13
|
+
URI.parse(params[:uri]).path :
|
14
|
+
"/documents/" + @name # escape this?
|
15
|
+
end
|
16
|
+
|
17
|
+
# delete this document from Grendel
|
18
|
+
def delete
|
19
|
+
@user.delete(@uri)
|
20
|
+
end
|
21
|
+
|
22
|
+
# send link operations to the Link class
|
23
|
+
def links
|
24
|
+
LinkManager.new(self)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Grendel
|
2
|
+
class DocumentManager
|
3
|
+
|
4
|
+
def initialize(user)
|
5
|
+
@user = user
|
6
|
+
@base_uri = "/documents"
|
7
|
+
end
|
8
|
+
|
9
|
+
# list all documents
|
10
|
+
def list
|
11
|
+
response = @user.get(@base_uri)
|
12
|
+
response["documents"].map {|d| Document.new(@user, d) }
|
13
|
+
end
|
14
|
+
|
15
|
+
# retreive a document
|
16
|
+
def find(name)
|
17
|
+
response = @user.get(@base_uri + "/" + name)
|
18
|
+
params = {
|
19
|
+
:name => name,
|
20
|
+
:data => response.body,
|
21
|
+
:content_type => response.headers['content-type'].first
|
22
|
+
}
|
23
|
+
Document.new(@user, params)
|
24
|
+
end
|
25
|
+
|
26
|
+
# store a document, creating a new one if it doesn't exist, or replacing the existing one if it does
|
27
|
+
def store(name, data, content_type = nil)
|
28
|
+
# if the content type isn't provided, guess it or set it to a default
|
29
|
+
unless content_type
|
30
|
+
if mime_type = MIME::Types.type_for(name).first
|
31
|
+
content_type = mime_type.content_type
|
32
|
+
else
|
33
|
+
content_type = 'application/octet-stream'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
response = @user.put(@base_uri + "/" + name, data, :raw_data => true, :headers => {'Content-Type' => content_type})
|
38
|
+
Document.new(@user, :name => name, :data => data, :content_type => content_type)
|
39
|
+
end
|
40
|
+
|
41
|
+
# delete the specified document from Grendel
|
42
|
+
def delete(name)
|
43
|
+
@user.delete(@base_uri + "/" + name)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/grendel/link.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Grendel
|
2
|
+
class Link
|
3
|
+
attr_accessor :document, :user, :uri
|
4
|
+
|
5
|
+
def initialize(document, user, params = {})
|
6
|
+
params.symbolize_keys!
|
7
|
+
@document = document
|
8
|
+
@user = user
|
9
|
+
@uri = params[:uri] ?
|
10
|
+
URI.parse(params[:uri]).path :
|
11
|
+
"/links/" + @user.id # escape this?
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Grendel
|
2
|
+
class LinkManager
|
3
|
+
|
4
|
+
def initialize(document)
|
5
|
+
@document = document
|
6
|
+
@base_uri = @document.uri + "/links"
|
7
|
+
end
|
8
|
+
|
9
|
+
# return links to this document
|
10
|
+
def list
|
11
|
+
response = @document.user.get(@base_uri)
|
12
|
+
response["links"].map do |link|
|
13
|
+
Link.new(@document, User.new(@document.user.client, link["user"]), :uri => link["uri"])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# add a link to a user and return a Link object
|
18
|
+
def add(user_id)
|
19
|
+
# REVIEW: 2010-02-23 <brad@wesabe.com> -- what does Grendel return if the link already exists?
|
20
|
+
@document.user.put(@base_uri + "/" + user_id)
|
21
|
+
Link.new(@document, User.new(@document.user.client, :id => user_id))
|
22
|
+
end
|
23
|
+
|
24
|
+
# remove a link to a user
|
25
|
+
def remove(user_id)
|
26
|
+
# REVIEW: 2010-02-23 <brad@wesabe.com> -- what does Grendel return if the link didn't exist?
|
27
|
+
@document.user.delete(@base_uri + "/" + user_id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Grendel
|
2
|
+
class LinkedDocument < Document
|
3
|
+
attr_accessor :linked_user, :owner
|
4
|
+
|
5
|
+
# create a new linked document
|
6
|
+
# user - linked user
|
7
|
+
# params:
|
8
|
+
# :name => document name
|
9
|
+
# :uri => linked document uri
|
10
|
+
# :owner => {
|
11
|
+
# :id => owner id
|
12
|
+
# :uri => owner uri
|
13
|
+
# }
|
14
|
+
def initialize(linked_user, params)
|
15
|
+
params.symbolize_keys!
|
16
|
+
@owner = User.new(linked_user.client, params[:owner])
|
17
|
+
super(@owner, params)
|
18
|
+
@linked_user = linked_user
|
19
|
+
@name = params[:name]
|
20
|
+
@uri = params[:uri] ?
|
21
|
+
URI.parse(params[:uri]).path :
|
22
|
+
["/linked-documents", @owner.id, name].join("/")
|
23
|
+
end
|
24
|
+
|
25
|
+
# delete this linked document
|
26
|
+
def delete
|
27
|
+
@linked_user.delete(@uri)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Grendel
|
2
|
+
class LinkedDocumentManager
|
3
|
+
def initialize(user)
|
4
|
+
@user = user
|
5
|
+
@base_uri = "/linked-documents"
|
6
|
+
end
|
7
|
+
|
8
|
+
# list this user's linked documents. Returns an array of LinkedDocument objects
|
9
|
+
def list
|
10
|
+
response = @user.get(@base_uri)
|
11
|
+
response["linked-documents"].map {|ld| LinkedDocument.new(@user, ld) }
|
12
|
+
end
|
13
|
+
|
14
|
+
# retreive a linked document
|
15
|
+
def find(owner_id, name)
|
16
|
+
response = @user.get([@base_uri, owner_id, name].join("/"))
|
17
|
+
params = {
|
18
|
+
:name => name,
|
19
|
+
:data => response.body,
|
20
|
+
:content_type => response.headers['content-type'].first,
|
21
|
+
:owner => { :id => owner_id }
|
22
|
+
}
|
23
|
+
LinkedDocument.new(@user, params)
|
24
|
+
end
|
25
|
+
|
26
|
+
# delete the linked document
|
27
|
+
def delete(owner_id, name)
|
28
|
+
@user.delete([@base_uri, owner_id, name].join("/"))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/grendel/user.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Grendel
|
2
|
+
class User
|
3
|
+
attr_accessor :id, :password, :uri
|
4
|
+
attr_reader :client, :modified_at, :created_at, :keys
|
5
|
+
|
6
|
+
# create a new Grendel::User object
|
7
|
+
# params:
|
8
|
+
# id
|
9
|
+
# uri
|
10
|
+
# password
|
11
|
+
def initialize(client, params)
|
12
|
+
params.symbolize_keys!
|
13
|
+
@client = client
|
14
|
+
@id = params[:id]
|
15
|
+
@uri = params[:uri] ?
|
16
|
+
URI.parse(params[:uri]).path :
|
17
|
+
"/users/" + @id # escape this?
|
18
|
+
@password = params[:password]
|
19
|
+
@modified_at = DateTime.parse(params[:"modified-at"]) if params[:"modified-at"]
|
20
|
+
@created_at = DateTime.parse(params[:"created-at"]) if params[:"created-at"]
|
21
|
+
@keys = params[:keys]
|
22
|
+
end
|
23
|
+
|
24
|
+
# return user's creds in the form required by HTTParty
|
25
|
+
def auth
|
26
|
+
{:basic_auth => {:username => id, :password => password}}
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# methods to do authenticated client calls with the user's base_uri
|
31
|
+
#
|
32
|
+
def get(uri = "", options = {})
|
33
|
+
options.merge!(auth)
|
34
|
+
@client.get(@uri + uri, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def post(uri = "", data = {}, options = {})
|
38
|
+
options.merge!(auth)
|
39
|
+
@client.post(@uri + uri, data, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def put(uri = "", data = {}, options = {})
|
43
|
+
options.merge!(auth)
|
44
|
+
@client.put(@uri + uri, data, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(uri = "", options = {})
|
48
|
+
options.merge!(auth)
|
49
|
+
@client.delete(@uri + uri, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
# change the user's password
|
53
|
+
def change_password(new_password)
|
54
|
+
put("", {:password => new_password})
|
55
|
+
@password = new_password
|
56
|
+
end
|
57
|
+
|
58
|
+
# send documents calls to the DocumentManager
|
59
|
+
def documents
|
60
|
+
DocumentManager.new(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
# send linked documents calls to the LinkedDocumentManager
|
64
|
+
def linked_documents
|
65
|
+
LinkedDocumentManager.new(self)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Grendel
|
2
|
+
class UserManager
|
3
|
+
|
4
|
+
def initialize(client)
|
5
|
+
@client = client
|
6
|
+
end
|
7
|
+
|
8
|
+
# return all Grendel users as Grendel::User objects
|
9
|
+
def list
|
10
|
+
response = @client.get("/users")
|
11
|
+
response["users"].map {|u| User.new(@client, u) }
|
12
|
+
end
|
13
|
+
|
14
|
+
# retrieve a user, optionally setting the password
|
15
|
+
def find(id, password = nil)
|
16
|
+
response = @client.get("/users/#{id}") # need to escape this
|
17
|
+
user = User.new(@client, response)
|
18
|
+
user.password = password
|
19
|
+
return user
|
20
|
+
end
|
21
|
+
|
22
|
+
# create a new user
|
23
|
+
def create(id, password)
|
24
|
+
params = {:id => id, :password => password}
|
25
|
+
response = @client.post("/users", params)
|
26
|
+
# TODO: strip protocol and host from uri
|
27
|
+
User.new(@client, params.merge(:uri => response.headers['location'].first))
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Grendel::Client" do
|
4
|
+
before do
|
5
|
+
@client = Grendel::Client.new("http://example.com")
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "new method" do
|
9
|
+
it "should set the base_uri" do
|
10
|
+
@client.base_uri.should == "http://example.com"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "users method" do
|
15
|
+
it "should return a Grendel::UserManager" do
|
16
|
+
@client.users.class.should be(Grendel::UserManager)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Grendel::DocumentManager" do
|
4
|
+
before do
|
5
|
+
@client = Grendel::Client.new("http://grendel")
|
6
|
+
@user_id = "alice"
|
7
|
+
@password = "s3kret"
|
8
|
+
@user = Grendel::User.new(@client, :id => @user_id, :password => @password)
|
9
|
+
@base_uri = "#{@user_id}:#{@password}@grendel/users/#{@user_id}/documents"
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "list" do
|
13
|
+
before do
|
14
|
+
stub_json_request(:get, @base_uri, %{{
|
15
|
+
"documents":[
|
16
|
+
{"name":"document1.txt",
|
17
|
+
"uri":"http://grendel/users/#{@user_id}/documents/document1.txt"},
|
18
|
+
{"name":"document2.txt",
|
19
|
+
"uri":"http://grendel/users/#{@user_id}/documents/document2.txt"}
|
20
|
+
]}})
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return an array of all documents" do
|
24
|
+
docs = @user.documents.list
|
25
|
+
docs.length.should == 2
|
26
|
+
docs[0].name.should == "document1.txt"
|
27
|
+
docs[0].uri.should == "/users/#{@user_id}/documents/document1.txt"
|
28
|
+
docs[1].name.should == "document2.txt"
|
29
|
+
docs[1].uri.should == "/users/#{@user_id}/documents/document2.txt"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "find" do
|
34
|
+
before do
|
35
|
+
stub_json_request(:get, @base_uri + "/document1.txt", "yay for me", :content_type => "text/plain")
|
36
|
+
stub_json_request(:get, @base_uri + "/notfound.txt", "", :status => "404 Not Found")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return the document" do
|
40
|
+
doc = @user.documents.find("document1.txt")
|
41
|
+
doc.name.should == "document1.txt"
|
42
|
+
doc.content_type.should == "text/plain"
|
43
|
+
doc.data.should == "yay for me"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should raise an exception if the document is not found" do
|
47
|
+
lambda {
|
48
|
+
@user.documents.find("notfound.txt")
|
49
|
+
}.should raise_error(Grendel::Client::HTTPException) {|error| error.message.should match("404")} # change to should == "404 Not Found" once WebMock supports status messages
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "store" do
|
54
|
+
describe "a successful request" do
|
55
|
+
before do
|
56
|
+
stub_json_request(:put, @base_uri + "/new_document.txt", "", :status => "204 No Content")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should send a properly-formatted request" do
|
60
|
+
@user.documents.store("new_document.txt", "top secret stuff", "text/plain")
|
61
|
+
params = { "id" => @user_id, "password" => @password }
|
62
|
+
request(:put, @base_uri + "/new_document.txt").
|
63
|
+
with(:body => "top secret stuff", :headers => {"Content-Type" => "text/plain"}).
|
64
|
+
should have_been_made.once
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should guess the content type if not provided" do
|
68
|
+
@user.documents.store("new_document.txt", "top secret stuff")
|
69
|
+
params = { "id" => @user_id, "password" => @password }
|
70
|
+
request(:put, @base_uri + "/new_document.txt").
|
71
|
+
with(:body => "top secret stuff", :headers => {"Content-Type" => "text/plain"}).
|
72
|
+
should have_been_made.once
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should default the content type to 'application/octet-stream' if unknown" do
|
76
|
+
stub_json_request(:put, @base_uri + "/new_document.-wtf-", "", :status => "204 No Content")
|
77
|
+
@user.documents.store("new_document.-wtf-", "top secret stuff")
|
78
|
+
params = { "id" => @user_id, "password" => @password }
|
79
|
+
request(:put, @base_uri + "/new_document.-wtf-").
|
80
|
+
with(:body => "top secret stuff", :headers => {"Content-Type" => "application/octet-stream"}).
|
81
|
+
should have_been_made.once
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should return a document" do
|
85
|
+
doc = @user.documents.store("new_document.txt", "top secret stuff")
|
86
|
+
doc.name.should == "new_document.txt"
|
87
|
+
doc.data.should == "top secret stuff"
|
88
|
+
doc.content_type.should == "text/plain"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "delete" do
|
94
|
+
before do
|
95
|
+
stub_json_request(:delete, @base_uri + "/document.txt", "", :status => "204 No Content")
|
96
|
+
@document = Grendel::Document.new(@user, :name => "document.txt")
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should send a properly-formatted request" do
|
100
|
+
@user.documents.delete("document.txt")
|
101
|
+
request(:delete, @base_uri + "/document.txt").should have_been_made.once
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Grendel::Document" do
|
4
|
+
before do
|
5
|
+
@client = Grendel::Client.new("http://grendel")
|
6
|
+
@user_id = "alice"
|
7
|
+
@password = "s3kret"
|
8
|
+
@user = Grendel::User.new(@client, :id => @user_id, :password => @password)
|
9
|
+
@base_uri = "#{@user_id}:#{@password}@grendel/users/#{@user_id}/documents"
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "delete" do
|
13
|
+
before do
|
14
|
+
stub_json_request(:delete, @base_uri + "/document.txt", "", :status => "204 No Content")
|
15
|
+
@document = Grendel::Document.new(@user, :name => "document.txt")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should send a properly-formatted request" do
|
19
|
+
@document.delete
|
20
|
+
request(:delete, @base_uri + "/document.txt").should have_been_made.once
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
# - get a list of documents a user has linked to them
|
4
|
+
# docs = user.linked_documents.list
|
5
|
+
# - retrieve a linked document
|
6
|
+
# doc = user.linked_documents.find("owner_user_id", "document.txt")
|
7
|
+
# - delete a linked document
|
8
|
+
# user.linked_documents.delete("owner_user_id", "document.txt")
|
9
|
+
|
10
|
+
describe "Grendel::LinkManager" do
|
11
|
+
before do
|
12
|
+
@client = Grendel::Client.new("http://grendel")
|
13
|
+
@user_id = "alice"
|
14
|
+
@password = "s3kret"
|
15
|
+
@user = Grendel::User.new(@client, :id => @user_id, :password => @password)
|
16
|
+
@document = Grendel::Document.new(@user, :name => "document1.txt")
|
17
|
+
@uri = "#{@user_id}:#{@password}@grendel/users/#{@user_id}/documents/#{@document.name}/links"
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "list" do
|
21
|
+
before do
|
22
|
+
stub_json_request(:get, @uri, %{{
|
23
|
+
"links":[
|
24
|
+
{
|
25
|
+
"user":{
|
26
|
+
"id":"bob",
|
27
|
+
"uri":"http://grendel/users/bob"
|
28
|
+
},
|
29
|
+
"uri":"http://grendel/users/alice/documents/document1.txt/links/bob"
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"user":{
|
33
|
+
"id":"carol",
|
34
|
+
"uri":"http://grendel/users/carol"
|
35
|
+
},
|
36
|
+
"uri":"http://grendel/users/alice/documents/document1.txt/links/carol"
|
37
|
+
}]
|
38
|
+
}})
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should list users with links to this document" do
|
42
|
+
links = @document.links.list
|
43
|
+
links.length.should == 2
|
44
|
+
links[0].user.id.should == "bob"
|
45
|
+
links[0].uri.should == "/users/alice/documents/document1.txt/links/bob"
|
46
|
+
links[1].user.id.should == "carol"
|
47
|
+
links[1].uri.should == "/users/alice/documents/document1.txt/links/carol"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "add" do
|
52
|
+
before do
|
53
|
+
@other_user_id = "bob"
|
54
|
+
stub_json_request(:put, @uri + "/" + @other_user_id, "")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should send a properly-formatted request" do
|
58
|
+
@document.links.add(@other_user_id)
|
59
|
+
request(:put, @uri + "/" + @other_user_id).should have_been_made.once
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return a Link object" do
|
63
|
+
link = @document.links.add(@other_user_id)
|
64
|
+
link.document.should == @document
|
65
|
+
link.user.id.should == @other_user_id
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "remove" do
|
70
|
+
before do
|
71
|
+
@other_user_id = "bob"
|
72
|
+
stub_json_request(:delete, @uri + "/" + @other_user_id, "")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should send a properly-formatted request" do
|
76
|
+
@document.links.remove(@other_user_id)
|
77
|
+
request(:delete, @uri + "/" + @other_user_id).should have_been_made.once
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|