cleversafe 1.0.7 → 1.1.10

Sign up to get free protection for your applications and to get access to all the features.
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
-