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
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copying Grendel-Ruby
|
2
|
+
===============
|
3
|
+
|
4
|
+
Grendel-Ruby is MIT licensed.
|
5
|
+
|
6
|
+
Copyright © 2010 Wesabe, Inc.
|
7
|
+
-----------------------------
|
8
|
+
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
a copy of this software and associated documentation files (the
|
11
|
+
"Software"), to deal in the Software without restriction, including
|
12
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included
|
18
|
+
in all copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
23
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
24
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
25
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
26
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
grendel-ruby
|
2
|
+
============
|
3
|
+
|
4
|
+
Ruby interface to the Grendel secure document storage service (http://github.com/wesabe/grendel). See the Grendel API documentation (http://github.com/wesabe/grendel/blob/master/API.md) for more information.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
sudo gem install grendel-ruby
|
10
|
+
|
11
|
+
Examples
|
12
|
+
--------
|
13
|
+
|
14
|
+
The following examples assumes that you have the Grendel server running locally on port 8080.
|
15
|
+
|
16
|
+
|
17
|
+
### Establishing a Connection
|
18
|
+
|
19
|
+
client = Grendel::Client.new("http://localhost:8080")
|
20
|
+
|
21
|
+
|
22
|
+
### Listing Registered Users
|
23
|
+
|
24
|
+
client.users.list # returns an array of Grendel::Users
|
25
|
+
|
26
|
+
|
27
|
+
### Creating A New User
|
28
|
+
|
29
|
+
user = client.users.create("alice", "s3kret") # returns a Grendel::User with id "alice" and password "s3kret"
|
30
|
+
|
31
|
+
If the user `id` is taken, a `Grendel::HTTPException` will be thrown with a message containing `422 Unprocessable Entity` and an explanation.
|
32
|
+
|
33
|
+
|
34
|
+
### Viewing A User
|
35
|
+
|
36
|
+
user = client.users.find("alice") # returns a Grendel::User
|
37
|
+
|
38
|
+
# return a Grendel::User with the password set to "s3kret". Note that this is merely a convenience
|
39
|
+
# method for future authenticated calls--it does not actually check that the password is correct.
|
40
|
+
user = client.users.find("alice", "s3kret")
|
41
|
+
|
42
|
+
The returned `Grendel::User` will contain the following attributes:
|
43
|
+
|
44
|
+
id - user id
|
45
|
+
modified_at - DateTime
|
46
|
+
created_at - DateTime
|
47
|
+
keys - array of key fingerprints
|
48
|
+
|
49
|
+
If the user is not found, a `Grendel::HTTPException` will be thrown with a message containing `404 Not Found`
|
50
|
+
|
51
|
+
|
52
|
+
### Changing A User's Password
|
53
|
+
|
54
|
+
user = client.users.find("alice", "s3kret")
|
55
|
+
user.change_password("new-pass")
|
56
|
+
|
57
|
+
|
58
|
+
### Deleting A User
|
59
|
+
|
60
|
+
user = client.users.find("alice", "s3kret")
|
61
|
+
user.delete
|
62
|
+
|
63
|
+
|
64
|
+
### Listing A User's Documents
|
65
|
+
|
66
|
+
user = client.users.find("alice", "s3kret")
|
67
|
+
docs = user.documents.list # returns an array of Grendel::Documents
|
68
|
+
|
69
|
+
A `Grendel::Document` contains the following attributes:
|
70
|
+
|
71
|
+
- name
|
72
|
+
- data
|
73
|
+
- content_type
|
74
|
+
- uri
|
75
|
+
|
76
|
+
|
77
|
+
### Viewing A User's Document
|
78
|
+
|
79
|
+
user = client.users.find("alice", "s3kret")
|
80
|
+
doc = user.documents.find("document1.txt") # returns a Grendel::Document
|
81
|
+
|
82
|
+
|
83
|
+
### Storing A User's Document
|
84
|
+
|
85
|
+
user = client.users.find("alice", "s3kret")
|
86
|
+
doc = user.documents.store("document1.txt", "i am a super secret")
|
87
|
+
|
88
|
+
The content type can be specified as an optional third parameter. If not provided,
|
89
|
+
it will be guessed from the file extension of the document name.
|
90
|
+
|
91
|
+
doc = user.documents.store("document1.txt", "i am a super secret", "text/plain")
|
92
|
+
|
93
|
+
Note that this method will overwrite the document if it already exists in Grendel.
|
94
|
+
|
95
|
+
|
96
|
+
### Deleting A User's Document
|
97
|
+
|
98
|
+
user = client.users.find("alice", "s3kret")
|
99
|
+
doc = user.documents.delete("document1.txt")
|
100
|
+
|
101
|
+
# or
|
102
|
+
|
103
|
+
doc = user.documents.find("document1.txt")
|
104
|
+
doc.delete
|
105
|
+
|
106
|
+
|
107
|
+
## Linking Documents
|
108
|
+
|
109
|
+
A Grendel document can be linked by its owner with other users. Doing so
|
110
|
+
provides other users *read-only* access to the document.
|
111
|
+
|
112
|
+
|
113
|
+
### Viewing A Document's Linked Users
|
114
|
+
|
115
|
+
user = client.users.find("alice", "s3kret")
|
116
|
+
doc = user.documents.find("document1.txt")
|
117
|
+
links = doc.links.list # returns an array of Grendel::Links
|
118
|
+
|
119
|
+
A `Grendel::Link` contains the following attributes:
|
120
|
+
|
121
|
+
- document
|
122
|
+
- user # the user the document is linked to
|
123
|
+
- uri # the uri of the linked document
|
124
|
+
|
125
|
+
|
126
|
+
### Linking Another User To A Document
|
127
|
+
|
128
|
+
user = client.users.find("alice", "s3kret")
|
129
|
+
doc = user.documents.find("document1.txt")
|
130
|
+
doc.links.add("bob") # returns a Grendel::Link
|
131
|
+
|
132
|
+
User `bob` will now have read-only access to the document.
|
133
|
+
|
134
|
+
|
135
|
+
### Unlinking A User From A Document
|
136
|
+
|
137
|
+
user = client.users.find("alice", "s3kret")
|
138
|
+
doc = user.documents.find("document1.txt")
|
139
|
+
doc.links.remove("bob")
|
140
|
+
|
141
|
+
User `bob` will no longer have access to the document.
|
142
|
+
|
143
|
+
|
144
|
+
## Managing Linked Documents
|
145
|
+
|
146
|
+
The documents shared with a user are stored in their own namespace to avoid
|
147
|
+
document name collisions. If the document's owner modifies the document, the
|
148
|
+
linked users will see the changes. Likewise, if the document's owner deletes the
|
149
|
+
document (or the owner is deleted), the documents will be removed from the
|
150
|
+
user's linked documents.
|
151
|
+
|
152
|
+
|
153
|
+
### Listing A User's Linked Documents
|
154
|
+
|
155
|
+
|
156
|
+
user = client.users.find("alice", "s3kret")
|
157
|
+
linked_docs = user.linked_documents # returns an array of Grende::LinkedDocuments
|
158
|
+
|
159
|
+
A `Grendel::LinkedDocument` is a subclass of `Grendel::Document` with the following additional attribute:
|
160
|
+
|
161
|
+
- owner # the document owner as a Grendel::User
|
162
|
+
|
163
|
+
|
164
|
+
### Viewing A Linked Document
|
165
|
+
|
166
|
+
|
167
|
+
user = client.users.find("alice", "s3kret")
|
168
|
+
doc = user.linked_documents.find("bob", "bobs_secrets.txt")
|
169
|
+
|
170
|
+
|
171
|
+
### Deleting A Linked Document
|
172
|
+
|
173
|
+
user = client.users.find("alice", "s3kret")
|
174
|
+
user.linked_documents.delete("bob", "bobs_secrets.txt")
|
175
|
+
|
176
|
+
# or
|
177
|
+
|
178
|
+
doc = user.linked_documents.find("bob", "bobs_secrets.txt")
|
179
|
+
doc.delete
|
180
|
+
|
181
|
+
**This will *not* delete the document itself**, it will simply remove the
|
182
|
+
document from the user's list of linked documents. It will also **not**
|
183
|
+
re-encrypt the document; the next time the document is written to, however, the
|
184
|
+
user will be excluded from the recipients.
|
185
|
+
|
186
|
+
## Bugs and Issues
|
187
|
+
|
188
|
+
Please submit them here [http://github.com/wesabe/grendel-ruby/issues](http://github.com/wesabe/grendel-ruby/issues)
|
189
|
+
|
190
|
+
## Copyright
|
191
|
+
|
192
|
+
Copyright 2010 Wesabe, Inc. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "grendel-ruby"
|
8
|
+
gem.summary = %Q{Ruby interface to Wesabe's Grendel}
|
9
|
+
gem.description = %Q{Grendel is a RESTful web service which allows for the secure storage of users'
|
10
|
+
documents. Grendel-Ruby provides a Ruby API for Grendel.}
|
11
|
+
gem.email = "brad@wesabe.com"
|
12
|
+
gem.homepage = "http://github.com/wesabe/grendel-ruby"
|
13
|
+
gem.authors = ["Brad Greenlee"]
|
14
|
+
gem.add_dependency "json"
|
15
|
+
gem.add_dependency "httparty"
|
16
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'spec/rake/spectask'
|
25
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
28
|
+
end
|
29
|
+
|
30
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
31
|
+
spec.libs << 'lib' << 'spec'
|
32
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
33
|
+
spec.rcov = true
|
34
|
+
end
|
35
|
+
|
36
|
+
task :spec => :check_dependencies
|
37
|
+
|
38
|
+
task :default => :spec
|
39
|
+
|
40
|
+
require 'rake/rdoctask'
|
41
|
+
Rake::RDocTask.new do |rdoc|
|
42
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
43
|
+
|
44
|
+
rdoc.rdoc_dir = 'rdoc'
|
45
|
+
rdoc.title = "grendel-ruby #{version}"
|
46
|
+
rdoc.rdoc_files.include('README*')
|
47
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
48
|
+
end
|
data/TODO.md
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{grendel-ruby}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Brad Greenlee"]
|
12
|
+
s.date = %q{2010-02-24}
|
13
|
+
s.description = %q{Grendel is a RESTful web service which allows for the secure storage of users'
|
14
|
+
documents. Grendel-Ruby provides a Ruby API for Grendel.}
|
15
|
+
s.email = %q{brad@wesabe.com}
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.md",
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
".gitignore",
|
23
|
+
"LICENSE.md",
|
24
|
+
"README.md",
|
25
|
+
"Rakefile",
|
26
|
+
"TODO.md",
|
27
|
+
"VERSION",
|
28
|
+
"grendel-ruby.gemspec",
|
29
|
+
"lib/core_ext/hash.rb",
|
30
|
+
"lib/grendel.rb",
|
31
|
+
"lib/grendel/client.rb",
|
32
|
+
"lib/grendel/document.rb",
|
33
|
+
"lib/grendel/document_manager.rb",
|
34
|
+
"lib/grendel/link.rb",
|
35
|
+
"lib/grendel/link_manager.rb",
|
36
|
+
"lib/grendel/linked_document.rb",
|
37
|
+
"lib/grendel/linked_document_manager.rb",
|
38
|
+
"lib/grendel/user.rb",
|
39
|
+
"lib/grendel/user_manager.rb",
|
40
|
+
"spec/grendel/client_spec.rb",
|
41
|
+
"spec/grendel/document_manager_spec.rb",
|
42
|
+
"spec/grendel/document_spec.rb",
|
43
|
+
"spec/grendel/link_manager_spec.rb",
|
44
|
+
"spec/grendel/link_spec.rb",
|
45
|
+
"spec/grendel/linked_document_manager_spec.rb",
|
46
|
+
"spec/grendel/linked_document_spec.rb",
|
47
|
+
"spec/grendel/user_manager_spec.rb",
|
48
|
+
"spec/grendel/user_spec.rb",
|
49
|
+
"spec/spec.opts",
|
50
|
+
"spec/spec_helper.rb"
|
51
|
+
]
|
52
|
+
s.homepage = %q{http://github.com/wesabe/grendel-ruby}
|
53
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
54
|
+
s.require_paths = ["lib"]
|
55
|
+
s.rubygems_version = %q{1.3.6}
|
56
|
+
s.summary = %q{Ruby interface to Wesabe's Grendel}
|
57
|
+
s.test_files = [
|
58
|
+
"spec/grendel/client_spec.rb",
|
59
|
+
"spec/grendel/document_manager_spec.rb",
|
60
|
+
"spec/grendel/document_spec.rb",
|
61
|
+
"spec/grendel/link_manager_spec.rb",
|
62
|
+
"spec/grendel/link_spec.rb",
|
63
|
+
"spec/grendel/linked_document_manager_spec.rb",
|
64
|
+
"spec/grendel/linked_document_spec.rb",
|
65
|
+
"spec/grendel/user_manager_spec.rb",
|
66
|
+
"spec/grendel/user_spec.rb",
|
67
|
+
"spec/spec_helper.rb"
|
68
|
+
]
|
69
|
+
|
70
|
+
if s.respond_to? :specification_version then
|
71
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
72
|
+
s.specification_version = 3
|
73
|
+
|
74
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
75
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
76
|
+
s.add_runtime_dependency(%q<httparty>, [">= 0"])
|
77
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<json>, [">= 0"])
|
80
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
81
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
82
|
+
end
|
83
|
+
else
|
84
|
+
s.add_dependency(%q<json>, [">= 0"])
|
85
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
86
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# stolen^H^H^H^H^H^Hborrowed from Rails' ActiveSupport
|
2
|
+
class Hash
|
3
|
+
# Return a new hash with all keys converted to symbols, as long as
|
4
|
+
# they respond to +to_sym+.
|
5
|
+
def symbolize_keys
|
6
|
+
dup.symbolize_keys!
|
7
|
+
end
|
8
|
+
|
9
|
+
# Destructively convert all keys to symbols, as long as they respond
|
10
|
+
# to +to_sym+.
|
11
|
+
def symbolize_keys!
|
12
|
+
keys.each do |key|
|
13
|
+
self[(key.to_sym rescue key) || key] = delete(key)
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
data/lib/grendel.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'httparty'
|
5
|
+
require 'json'
|
6
|
+
require 'core_ext/hash'
|
7
|
+
require 'mime/types'
|
8
|
+
require 'grendel/client'
|
9
|
+
require 'grendel/user_manager'
|
10
|
+
require 'grendel/user'
|
11
|
+
require 'grendel/document_manager'
|
12
|
+
require 'grendel/document'
|
13
|
+
require 'grendel/link_manager'
|
14
|
+
require 'grendel/link'
|
15
|
+
require 'grendel/linked_document_manager'
|
16
|
+
require 'grendel/linked_document'
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Ruby interface to Wesabe's Grendel (http://github.com/wesabe/grendel)
|
2
|
+
module Grendel
|
3
|
+
class Client
|
4
|
+
attr_accessor :debug, :debug_output
|
5
|
+
attr_reader :base_uri
|
6
|
+
|
7
|
+
# Create a new Grendel client instance
|
8
|
+
def initialize(base_uri, options = {})
|
9
|
+
@base_uri = base_uri
|
10
|
+
@debug = options[:debug]
|
11
|
+
@debug_output = options[:debug_output] || $stderr
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(uri, options = {})
|
15
|
+
options.merge!(:debug_output => @debug_output) if @debug
|
16
|
+
response = HTTParty.get(@base_uri + uri, options)
|
17
|
+
raise HTTPException.new(response) if response.code >= 400
|
18
|
+
return response
|
19
|
+
end
|
20
|
+
|
21
|
+
def post(uri, data = {}, options = {})
|
22
|
+
data = data.to_json unless options.delete(:raw_data)
|
23
|
+
options.merge!(
|
24
|
+
:body => data,
|
25
|
+
:headers => {'Content-Type' => 'application/json'}
|
26
|
+
)
|
27
|
+
options.merge!(:debug_output => @debug_output) if @debug
|
28
|
+
response = HTTParty.post(@base_uri + uri, options)
|
29
|
+
raise HTTPException.new(response) if response.code >= 400
|
30
|
+
return response
|
31
|
+
end
|
32
|
+
|
33
|
+
def put(uri, data = {}, options = {})
|
34
|
+
data = data.to_json unless options.delete(:raw_data)
|
35
|
+
options = {
|
36
|
+
:body => data,
|
37
|
+
:headers => {'Content-Type' => 'application/json'}
|
38
|
+
}.update(options)
|
39
|
+
options.merge!(:debug_output => @debug_output) if @debug
|
40
|
+
response = HTTParty.put(@base_uri + uri, options)
|
41
|
+
raise HTTPException.new(response) if response.code >= 400
|
42
|
+
return response
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete(uri, options = {})
|
46
|
+
options.merge!(:debug_output => @debug_output) if @debug
|
47
|
+
response = HTTParty.delete(@base_uri + uri, options)
|
48
|
+
raise HTTPException.new(response) if response.code >= 400
|
49
|
+
end
|
50
|
+
|
51
|
+
def users
|
52
|
+
UserManager.new(self)
|
53
|
+
end
|
54
|
+
|
55
|
+
class HTTPException < Exception
|
56
|
+
def initialize(response)
|
57
|
+
msg = "#{response.code} #{response.message}"
|
58
|
+
msg << "\n#{response.body}" unless response.body.blank?
|
59
|
+
super(msg)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|