grendel-ruby 0.1.1

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