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