cleversafe 1.0.7 → 1.1.10

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 18677c6a6c521554741f3af8d8d1cb1b758e14c8
4
+ data.tar.gz: 13fc121e700597895df533e32cbc72235332ff6e
5
+ SHA512:
6
+ metadata.gz: d50d22d53a7d2d1876d0a9827d34c5f7a6b2bf5d5106b35c52c7961cd5cd6c1ca383160946af8f602971e536156800cdefa7c7b6c4a586c563730ee53160329a
7
+ data.tar.gz: c94b1bdce16240613f90d1d2668fccd99f311650192c1aaf0ff75d9861a88eb8fce820a9e496e39d702e6c701936b26174445267de75ae71b835c6aa946931ad
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cleversafe (1.1.10)
5
+ json
6
+ rest-client (~> 1.6)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.3.7)
12
+ crack (0.4.2)
13
+ safe_yaml (~> 1.0.0)
14
+ json (1.8.2)
15
+ mime-types (2.4.3)
16
+ minitest (5.5.1)
17
+ netrc (0.10.2)
18
+ rake (10.4.2)
19
+ rest-client (1.7.3)
20
+ mime-types (>= 1.16, < 3.0)
21
+ netrc (~> 0.7)
22
+ safe_yaml (1.0.4)
23
+ webmock (1.20.4)
24
+ addressable (>= 2.3.6)
25
+ crack (>= 0.3.2)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ cleversafe!
32
+ minitest (~> 5)
33
+ rake
34
+ webmock (~> 1.7)
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Cleversafe SOH
2
+
3
+ ## Description
4
+ This is a Ruby interface into the Cleversafe's SOH API.
5
+
6
+ ## Notes
7
+ * Multipart uploads are not supported by Cleversafe's API. Coming soon.
8
+ * If you upload a file with "X-Digest" you will get back a hash that includes :id and :x_content_digest.
9
+
10
+ ## Todo
11
+ * Support range requests for getting an object
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ # Connect to Cleversafe
17
+ connection = Cleversafe::Connection.new('http://127.0.0.1:1234')
18
+
19
+ # Open a vault
20
+ vault = connection.vault('pictures')
21
+
22
+ # Upload, download, delete
23
+ key = vault.create_object(open('~/cat.jpg'))
24
+
25
+ vault[key].exists?
26
+ vault[key].size
27
+ vault[key].open { |io| ... }
28
+ vault[key].delete
29
+
30
+ ```
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+
6
+ task :default => :test
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs << 'test/lib'
10
+ t.pattern = 'test/*_test.rb'
11
+ end
Binary file
Binary file
Binary file
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "cleversafe"
3
+ s.summary = "A Ruby API into Cleversafe's REST interface."
4
+ s.author = "John Williams"
5
+ s.email = "john@37signals.com"
6
+ s.version = "1.1.10"
7
+
8
+ s.add_dependency 'json'
9
+ s.add_dependency 'rest-client', '~> 1.6'
10
+
11
+ s.add_development_dependency 'rake'
12
+ s.add_development_dependency 'minitest', '~> 5'
13
+ s.add_development_dependency 'webmock', '~> 1.7'
14
+
15
+ s.files = Dir["#{File.dirname(__FILE__)}/**/*"]
16
+ end
@@ -1,83 +1,33 @@
1
+ require 'json'
2
+ require 'forwardable'
3
+
1
4
  module Cleversafe
2
5
  class Connection
3
- attr_reader :username
4
- attr_accessor :password
5
- attr_accessor :host
6
- attr_accessor :protocol
7
- attr_accessor :vault
8
- attr_accessor :method
6
+ extend Forwardable
9
7
 
10
- def initialize(*args)
11
- if args[0].is_a?(Hash)
12
- options = args[0]
13
- @username = options[:username]
14
- @password = options[:password]
15
- @host = options[:host]
16
- @protocol = options[:protocol] || "http"
17
- @open_timeout = options[:open_timeout] || 10
18
- else
19
- @username = args[0]
20
- @password = args[1]
21
- @host = args[2]
22
- @protocol = args[3] || "http"
23
- @open_timeout = args[4] || 10
24
- end
8
+ def_delegators :@http, :url, :url_for, :get, :head, :put, :delete
25
9
 
26
- @connection ||= begin
27
- build_connection
28
- end
29
- return @connection
10
+ def initialize(url, options = {})
11
+ @http = Cleversafe::HttpClient.new(url, options)
30
12
  end
31
13
 
32
- def build_connection
33
- RestClient::Resource.new(base_url, :user => @username, :password => @password,
34
- :raw_response => true, :open_timeout => @open_timeout)
14
+ def ping
15
+ head '/'
16
+ true
17
+ rescue Exception
18
+ false
35
19
  end
36
20
 
37
- def base_url
38
- "#{@protocol}://#{@host}/"
21
+ def status
22
+ JSON.parse(get('/').to_s)
39
23
  end
40
24
 
41
- def url_for(vault, objectname, options={})
42
- protocol = options.fetch(:protocol, @protocol)
43
- host = options.fetch(:host, @host)
44
-
45
- "#{protocol}://#{host}/#{vault}/#{objectname}"
25
+ def vaults
26
+ status['vaults'].map { |v| v['vault_name'] }
46
27
  end
47
28
 
48
29
  def vault(name)
49
30
  Cleversafe::Vault.new(self, name)
50
31
  end
51
-
52
- def vaults
53
- vaults = JSON.parse(get(nil))['vaults']
54
- vaults.collect{|v| v['vault_name']}
55
- end
56
-
57
- def vault_exists?(vault_name)
58
- begin
59
- response = get(vault_name)
60
- true
61
- rescue => e
62
- false
63
- end
64
- end
65
-
66
- def get(path, options = {})
67
- @connection[path].get options
68
- end
69
-
70
- def head(path, options = {})
71
- @connection[path].head options
72
- end
73
-
74
- def put(path, payload, options = {})
75
- @connection[path].put payload, options
76
- end
77
-
78
- def delete(path)
79
- @connection[path].delete
80
- end
81
-
82
32
  end
83
33
  end
@@ -1,6 +1,7 @@
1
1
  module Cleversafe
2
- module Error
2
+ module Errors
3
3
  class Base < RuntimeError; end
4
4
  class NotFound < Base; end
5
+ class VaultMisconfigured < Base; end
5
6
  end
6
7
  end
@@ -0,0 +1,42 @@
1
+ module Cleversafe
2
+ class HttpClient
3
+ DEFAULTS = { :timeout => nil, :open_timeout => nil }
4
+
5
+ attr_reader :url
6
+
7
+ def initialize(url, options = {})
8
+ @url = url
9
+ @defaults = DEFAULTS.merge(options)
10
+ end
11
+
12
+ def url_for(*path)
13
+ File.join(url, *path)
14
+ end
15
+
16
+ def head(path, options = {})
17
+ request :head, path, options
18
+ end
19
+
20
+ def get(path, options = {})
21
+ request :get, path, options
22
+ end
23
+
24
+ def post(path, payload, options = {})
25
+ request :post, path, options.merge(:payload => payload)
26
+ end
27
+
28
+ def put(path, payload, options = {})
29
+ request :put, path, options.merge(:payload => payload)
30
+ end
31
+
32
+ def delete(path, options = {})
33
+ request :delete, path, options
34
+ end
35
+
36
+ private
37
+ def request(method, path, options = {})
38
+ options = @defaults.merge(options).merge(:method => method, :url => url_for(path))
39
+ RestClient::Request.execute(options)
40
+ end
41
+ end
42
+ end
@@ -1,61 +1,61 @@
1
1
  require 'fileutils'
2
+ require 'cgi'
2
3
 
3
4
  module Cleversafe
4
5
  class Object
5
6
 
6
- attr_reader :name
7
- attr_reader :vault
7
+ attr_reader :vault, :key, :connection
8
8
 
9
- def initialize(vault, objectname = {})
10
- @vault = vault.name
11
- @name = objectname
9
+ def initialize(vault, key)
10
+ raise ArgumentError, "key is required" unless key =~ /\S/
11
+ @vault = vault
12
+ @key = key
12
13
  @connection = vault.connection
13
14
  end
14
15
 
15
- def url(options={})
16
- @connection.url_for(@vault, @name, options)
16
+ def path
17
+ "#{vault.path}/#{CGI.escape(key)}"
18
+ end
19
+
20
+ def url
21
+ connection.url_for(path)
17
22
  end
18
23
 
19
24
  def delete
20
25
  handle_errors do
21
- @connection.delete("#{@vault}/#{@name}")
26
+ connection.delete(path)
22
27
  end
23
28
  end
24
29
 
25
30
  def exists?
26
31
  metadata
27
32
  true
28
- rescue Error::NotFound
33
+ rescue Errors::NotFound
29
34
  false
30
35
  end
31
36
 
32
- def data(options={})
33
- handle_errors do
34
- @connection.get("#{@vault}/#{@name}", options).to_s
35
- end
37
+ def data(options = {})
38
+ open(options) { |io| io.read }
36
39
  end
37
40
 
38
- def open(options={})
41
+ def open(options = {})
39
42
  handle_errors do
40
- response = @connection.get("#{@vault}/#{@name}", options)
43
+ response = connection.get(path, options.merge(:raw_response => true))
41
44
  begin
42
- yield response.file.open
45
+ file = response.file
46
+ file.open
47
+ file.binmode
48
+ yield file
43
49
  ensure
44
- response.file.unlink
50
+ file.unlink
45
51
  end
46
52
  end
47
53
  end
48
54
 
49
- def write_to(filename, options={})
50
- handle_errors do
51
- response = @connection.get("#{@vault}/#{@name}", options)
52
- FileUtils.cp(response.file.path, filename)
53
- FileUtils.rm(response.file.path)
54
- end
55
- end
56
-
57
55
  def metadata
58
- @metadata ||= handle_errors { @connection.head("#{@vault}/#{@name}").headers }
56
+ @metadata ||= handle_errors do
57
+ connection.head(path).headers
58
+ end
59
59
  end
60
60
 
61
61
  def etag
@@ -67,15 +67,10 @@ module Cleversafe
67
67
  end
68
68
 
69
69
  private
70
-
71
- def handle_errors
72
- yield
73
- rescue RestClient::Exception => e
74
- if (e.http_code.to_s == "404")
75
- raise Error::NotFound, "object `#{@name}' does not exist", caller[0..-2]
76
- else
77
- raise
70
+ def handle_errors
71
+ yield
72
+ rescue RestClient::ResourceNotFound
73
+ raise Cleversafe::Errors::NotFound, "object `#{key}' does not exist", caller[0..-2]
78
74
  end
79
- end
80
75
  end
81
76
  end
@@ -1,45 +1,56 @@
1
+ require 'cgi'
2
+
1
3
  module Cleversafe
2
4
  class Vault
3
-
4
- attr_reader :name
5
- attr_reader :connection
5
+ attr_reader :connection, :name, :path
6
6
 
7
7
  def initialize(connection, name)
8
8
  @connection = connection
9
9
  @name = name
10
+ @path = CGI.escape name
10
11
  end
11
12
 
12
13
  def metadata
13
- @metadata ||= JSON.parse(@connection.get(@name))
14
+ @metadata ||= JSON.parse(connection.get(path).to_s)
14
15
  end
15
16
 
16
- def bytes_used
17
- metadata['vault_usage']['used_size']
17
+ def usage
18
+ metadata['vault_usage']
18
19
  end
19
20
 
20
- def object(objectname)
21
- Cleversafe::Object.new(self, objectname)
21
+ def object(key)
22
+ Cleversafe::Object.new(self, key)
22
23
  end
23
24
  alias [] object
24
25
 
25
- def objects(params = {})
26
- options = {}
27
- options['X-Operation'] = "list"
28
- options['X-List-Length-Limit'] = params[:limit] if params[:limit]
29
- options['X-Start-Id'] = params[:start_id] if params[:start_id]
30
- @connection.get(@name, options).to_s.split("\n")
26
+ def objects(options = {})
27
+ headers = {}
28
+
29
+ headers['X-Operation'] = 'list'
30
+ headers['X-Start-Id'] = options[:start_id] if options[:start_id]
31
+ headers['X-List-Length-Limit'] = options[:limit] if options[:limit]
32
+
33
+ connection.get(path, :headers => headers).to_s.split("\n")
31
34
  end
32
35
 
33
36
  def create_object(payload, options = {})
34
- response = @connection.put("#{@name}", payload, options)
35
- id = response.to_s.strip
36
-
37
- if response.headers[:x_content_digest]
38
- {:id => id, :x_content_digest => response.headers[:x_content_digest] }
39
- else
40
- id
37
+ handle_errors do
38
+ response = connection.put(path, payload, options)
39
+ id = response.to_s.strip
40
+
41
+ if response.headers[:x_content_digest]
42
+ { :id => id, :x_content_digest => response.headers[:x_content_digest] }
43
+ else
44
+ id
45
+ end
41
46
  end
42
47
  end
43
48
 
49
+ private
50
+ def handle_errors
51
+ yield
52
+ rescue RestClient::MethodNotAllowed
53
+ raise Cleversafe::Errors::VaultMisconfigured, "Vault has not been added to accessers.", caller[0..-2]
54
+ end
44
55
  end
45
56
  end
data/lib/cleversafe.rb CHANGED
@@ -1,10 +1,14 @@
1
- #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'rest-client'
4
- require 'json'
1
+ require 'cleversafe/connection'
2
+ require 'cleversafe/http_client'
3
+ require 'cleversafe/vault'
4
+ require 'cleversafe/object'
5
+ require 'cleversafe/errors'
5
6
 
6
- $:.unshift(File.dirname(__FILE__))
7
- require 'cleversafe/errors'
8
- require 'cleversafe/vault'
9
- require 'cleversafe/object'
10
- require 'cleversafe/connection'
7
+ require 'rest-client'
8
+
9
+ # Monkeypatch RestClient to prevent it from closing IO objects after they're
10
+ # uploaded, which it really shouldn't do.
11
+ class RestClient::Payload::Streamed
12
+ def close
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ require 'cleversafe/test_helper'
2
+
3
+ class ConnectionTest < Minitest::Test
4
+ def test_truth
5
+ assert true
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ require 'cleversafe/test_helper'
2
+
3
+ class HttpClientTest < Minitest::Test
4
+ def test_url_for
5
+ client = Cleversafe::HttpClient.new('http://example.com')
6
+
7
+ assert_equal 'http://example.com/', client.url_for('')
8
+ assert_equal 'http://example.com/', client.url_for('/')
9
+ assert_equal 'http://example.com/a/b', client.url_for('/a/b')
10
+ assert_equal 'http://example.com/a/b', client.url_for('a/b')
11
+ assert_equal 'http://example.com/a/b', client.url_for('a', 'b')
12
+ assert_equal 'http://example.com/a%2Fb', client.url_for('a%2Fb')
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'cleversafe'
4
+
5
+ require 'minitest/autorun'
6
+ require 'webmock/minitest'
@@ -0,0 +1,19 @@
1
+ require 'cleversafe/test_helper'
2
+
3
+ class ObjectTest < Minitest::Test
4
+ def setup
5
+ connection = Cleversafe::Connection.new('http://test.host')
6
+ @vault = Cleversafe::Vault.new(connection, 'test_vault')
7
+ end
8
+
9
+ def test_name_is_required
10
+ assert_raises ArgumentError do
11
+ Cleversafe::Object.new(@vault, '')
12
+ end
13
+ end
14
+
15
+ def test_path_is_escaped
16
+ object = Cleversafe::Object.new(@vault, 'foo/bar[1].png')
17
+ assert_equal 'test_vault/foo%2Fbar%5B1%5D.png', object.path
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ require 'cleversafe/test_helper'
2
+
3
+ class VaultTest < Minitest::Test
4
+ def setup
5
+ @connection = Cleversafe::Connection.new('http://test.host')
6
+ end
7
+
8
+ def test_path_is_escaped
9
+ assert_equal 'test%2Fvault', Cleversafe::Vault.new(@connection, 'test/vault').path
10
+ end
11
+
12
+ def test_vault_is_configured_correctly
13
+ stub_request(:put, "http://test.host/assets").with(:body => "foo").to_return(:status => 405, :body => "", :headers => {})
14
+ vault = Cleversafe::Vault.new(@connection, 'assets')
15
+ error = assert_raises(Cleversafe::Errors::VaultMisconfigured) { vault.create_object('foo') }
16
+ assert_equal 'Vault has not been added to accessers.', error.message
17
+ end
18
+ end
metadata CHANGED
@@ -1,77 +1,131 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: cleversafe
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 1
7
- - 0
8
- - 7
9
- version: 1.0.7
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.10
10
5
  platform: ruby
11
- authors:
6
+ authors:
12
7
  - John Williams
13
8
  autorequire:
14
9
  bindir: bin
15
10
  cert_chain: []
16
-
17
- date: 2013-04-16 00:00:00 -05:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: rest-client
11
+ date: 2022-07-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
22
21
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- requirements:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
25
24
  - - ">="
26
- - !ruby/object:Gem::Version
27
- segments:
28
- - 0
29
- version: "0"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
30
34
  type: :runtime
31
- version_requirements: *id001
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
32
83
  description:
33
84
  email: john@37signals.com
34
85
  executables: []
35
-
36
86
  extensions: []
37
-
38
87
  extra_rdoc_files: []
39
-
40
- files:
41
- - lib/cleversafe/connection.rb
42
- - lib/cleversafe/errors.rb
43
- - lib/cleversafe/object.rb
44
- - lib/cleversafe/vault.rb
45
- - lib/cleversafe.rb
46
- has_rdoc: true
88
+ files:
89
+ - "./Gemfile"
90
+ - "./Gemfile.lock"
91
+ - "./README.md"
92
+ - "./Rakefile"
93
+ - "./cleversafe-1.1.10.gem"
94
+ - "./cleversafe-1.1.11.gem"
95
+ - "./cleversafe-1.1.12.gem"
96
+ - "./cleversafe.gemspec"
97
+ - "./lib/cleversafe.rb"
98
+ - "./lib/cleversafe/connection.rb"
99
+ - "./lib/cleversafe/errors.rb"
100
+ - "./lib/cleversafe/http_client.rb"
101
+ - "./lib/cleversafe/object.rb"
102
+ - "./lib/cleversafe/vault.rb"
103
+ - "./test/connection_test.rb"
104
+ - "./test/http_client_test.rb"
105
+ - "./test/lib/cleversafe/test_helper.rb"
106
+ - "./test/object_test.rb"
107
+ - "./test/vault_test.rb"
47
108
  homepage:
48
109
  licenses: []
49
-
110
+ metadata: {}
50
111
  post_install_message:
51
112
  rdoc_options: []
52
-
53
- require_paths:
113
+ require_paths:
54
114
  - lib
55
- required_ruby_version: !ruby/object:Gem::Requirement
56
- requirements:
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
57
117
  - - ">="
58
- - !ruby/object:Gem::Version
59
- segments:
60
- - 0
61
- version: "0"
62
- required_rubygems_version: !ruby/object:Gem::Requirement
63
- requirements:
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
64
122
  - - ">="
65
- - !ruby/object:Gem::Version
66
- segments:
67
- - 0
68
- version: "0"
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
69
125
  requirements: []
70
-
71
126
  rubyforge_project:
72
- rubygems_version: 1.3.6
127
+ rubygems_version: 2.5.2
73
128
  signing_key:
74
- specification_version: 3
129
+ specification_version: 4
75
130
  summary: A Ruby API into Cleversafe's REST interface.
76
131
  test_files: []
77
-