grendel-ruby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
@@ -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.
@@ -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.
@@ -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
@@ -0,0 +1,8 @@
1
+ To Do
2
+ ========================
3
+
4
+ * Possible escaping issues with user and document names
5
+ * ETag support
6
+ * Yard/RDocs
7
+ * Full example code
8
+ * Support for adding documents directly from the file system?
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
@@ -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