persistable 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +6 -0
- data/History.txt +34 -0
- data/README.rdoc +64 -0
- data/Rakefile +11 -0
- data/lib/persistable.rb +13 -0
- data/lib/persistable/cloud_storage_adapter.rb +89 -0
- data/lib/persistable/factory.rb +52 -0
- data/lib/persistable/fs_adapter.rb +29 -0
- data/lib/persistable/memory_adapter.rb +23 -0
- data/lib/persistable/mogile_fs_adapter.rb +53 -0
- data/lib/persistable/version.rb +3 -0
- data/lib/persistable/xt/hash.rb +11 -0
- data/persistable.gemspec +24 -0
- data/test/cloud_storage_test.rb +91 -0
- data/test/factory_test.rb +76 -0
- data/test/fs_adapter_test.rb +73 -0
- data/test/memory_adapter_test.rb +46 -0
- data/test/mogile_fs_adapter_test.rb +98 -0
- data/test/test_helper.rb +11 -0
- metadata +146 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/History.txt
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
== 0.5.9 2009-03-06
|
2
|
+
|
3
|
+
* 1 major enhancement:
|
4
|
+
* mogile-fs-adapter now opens and closes the connection for every action
|
5
|
+
|
6
|
+
== 0.5.7 2009-02-18
|
7
|
+
|
8
|
+
* 1 major enhancement:
|
9
|
+
* mogile-fs-adapter can now be given a timeout option
|
10
|
+
|
11
|
+
== 0.5.6 2009-01-29
|
12
|
+
|
13
|
+
* 1 major enhancement:
|
14
|
+
* mogile-fs-adapter now has the same handling for non-existant keys as the other adapters: returning nil
|
15
|
+
|
16
|
+
== 0.5.4 2009-01-29
|
17
|
+
|
18
|
+
* 1 minor enhancement:
|
19
|
+
* bumped version for great rebuild
|
20
|
+
|
21
|
+
== 0.5.3 2009-01-28
|
22
|
+
|
23
|
+
* 1 minor enhancement:
|
24
|
+
* Separated dependencies in dev and runtime dependencies
|
25
|
+
|
26
|
+
== 0.5.2 2009-01-28
|
27
|
+
|
28
|
+
* 1 major enhancement:
|
29
|
+
* Added missing gem dependencies
|
30
|
+
|
31
|
+
== 0.0.1 2009-01-06
|
32
|
+
|
33
|
+
* 1 major enhancement:
|
34
|
+
* Initial release
|
data/README.rdoc
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
= Persistable
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
Persistable is a library for persisting IO-Data into any storage you like. It provides adapters for in-memory storage, file storage, mogile-fs storage and a { fog gem }[http://fog.io] powered cloud storage but can be easily extended with your own adapters.
|
6
|
+
|
7
|
+
Persistable has been harvested from project pkw.de[http://pkw.de] of { Caroo GmbH }[http://caroo.com].
|
8
|
+
|
9
|
+
== Download and Installation
|
10
|
+
|
11
|
+
Install the gem with the following command...
|
12
|
+
|
13
|
+
$ gem install persistable --source http://github.com/pkwde/persistable
|
14
|
+
|
15
|
+
== Examples
|
16
|
+
|
17
|
+
class Image
|
18
|
+
|
19
|
+
attr_accessor :name, :persistence_adapter
|
20
|
+
|
21
|
+
def initialize(attributes = {})
|
22
|
+
@name = attributes[:name]
|
23
|
+
@persistence_data = attributes[:persistence_data]
|
24
|
+
end
|
25
|
+
|
26
|
+
def persistence_key
|
27
|
+
self.name
|
28
|
+
end
|
29
|
+
|
30
|
+
def persistence_data
|
31
|
+
@persistence_data
|
32
|
+
end
|
33
|
+
|
34
|
+
def persistence_data=(persistence_data)
|
35
|
+
@persistence_data = persistence_data
|
36
|
+
end
|
37
|
+
|
38
|
+
def save_image_data
|
39
|
+
persistence_adapter.write(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_image_data
|
43
|
+
persistence_adapter.load(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
image = Image.new(:name => "foo", :persistence_data => File.open("/path/to/foo.jpg"))
|
50
|
+
image.persistence_adapter = Persistable::FSAdapter.new
|
51
|
+
image.save_image_data
|
52
|
+
|
53
|
+
new_but_same_image = Image.new(:name => "foo")
|
54
|
+
new_but_same_image.persistence_adapter = Persistable::FSAdapter.new
|
55
|
+
new_but_same_image.load_image_data
|
56
|
+
|
57
|
+
image.persistence_data == new_but_same_image.persistence_data
|
58
|
+
|
59
|
+
|
60
|
+
== License
|
61
|
+
|
62
|
+
Copyright Caroo GmbH 2009
|
63
|
+
|
64
|
+
You may use, copy and redistribute this library under the same terms as { Ruby itself }[http://www.ruby-lang.org/en/LICENSE.txt] or under the { MIT license }[http://mocha.rubyforge.org/files/MIT-LICENSE.html].
|
data/Rakefile
ADDED
data/lib/persistable.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "persistable/version"
|
2
|
+
require "persistable/xt/hash"
|
3
|
+
|
4
|
+
require "persistable/factory"
|
5
|
+
|
6
|
+
##
|
7
|
+
# Persistable defines an abstract interface to save, retrieve and delete data from a storage.
|
8
|
+
# Currently memory, mogile_fs, file and cloud adapters are supported.
|
9
|
+
# The latter is powered by the {http://fog.io/storage/ fog gem}, so any fog storage provider can be used.
|
10
|
+
# Persistable has also a {Persistable::Factory factory class}, so you can use it to build your adapter using yml files.
|
11
|
+
module Persistable
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "fog"
|
2
|
+
|
3
|
+
module Persistable
|
4
|
+
##
|
5
|
+
# CloudStorageAdapter powered by the fog gem
|
6
|
+
class CloudStorageAdapter
|
7
|
+
|
8
|
+
##
|
9
|
+
# @!attribute [r] options
|
10
|
+
# @return [Hash] Options used during the object initialization
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
##
|
14
|
+
# Creates a new `CloudStorageAdapter`
|
15
|
+
# @param options [Hash] `hash` with options for this adapter
|
16
|
+
# @option options [Symbol] :provider One of the providers given
|
17
|
+
# by {http://http://fog.io/storage/ fog gem}
|
18
|
+
# @option options [String] :directory The directory to save the files to,
|
19
|
+
# e.g. file system directory, amazon s3 bucket, ...
|
20
|
+
# @option options [Hash] :provider_options Provider options needed
|
21
|
+
# for {http://http://fog.io/storage/ fog gem} storage providers
|
22
|
+
def initialize(options = {})
|
23
|
+
@options = options
|
24
|
+
available_providers = Fog::Storage.providers
|
25
|
+
available_providers.include?(provider) or raise ArgumentError, "Unknown Fog::Storage provider. Should be on of \
|
26
|
+
#{available_providers.inspect}, given provider"
|
27
|
+
directory_name or raise ArgumentError, "Directory to save file should be given."
|
28
|
+
check_connection
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Write the data given by `#persisence_data` method to the cloud
|
33
|
+
# @param persistable [#persistance_key, #persistance_data] Any object responding to the methods needed
|
34
|
+
# @return [Boolean] Returns the success status of this operation
|
35
|
+
def write(persistable)
|
36
|
+
if persistable.persistence_data
|
37
|
+
return directory.files.create(:key => persistable.persistence_key, :body => persistable.persistence_data)
|
38
|
+
end
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Reads the data on the cloud and write it on the `persistance_data` property of `persistable` object
|
44
|
+
# @param persistable (@see #write)
|
45
|
+
# @return (@see #write)
|
46
|
+
def read(persistable)
|
47
|
+
if file = directory.files.get(persistable.persistence_key)
|
48
|
+
persistable.persistence_data = StringIO.new(file.body)
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Deletes file named `persistable#persistance_key` from storage
|
56
|
+
# @param persistable (@see #write)
|
57
|
+
# @return (@see #write)
|
58
|
+
def delete(persistable)
|
59
|
+
if file = directory.files.head(persistable.persistence_key)
|
60
|
+
return file.destroy
|
61
|
+
end
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def connection
|
68
|
+
@connection ||= Fog::Storage.new provider_options
|
69
|
+
end
|
70
|
+
alias check_connection connection
|
71
|
+
|
72
|
+
def directory
|
73
|
+
@directory ||= connection.directories.get(directory_name) || connection.directories.create(:key => directory_name)
|
74
|
+
end
|
75
|
+
|
76
|
+
def directory_name
|
77
|
+
@directory_name ||= options[:directory]
|
78
|
+
end
|
79
|
+
|
80
|
+
def provider
|
81
|
+
@provider ||= options[:provider].to_sym
|
82
|
+
end
|
83
|
+
|
84
|
+
def provider_options
|
85
|
+
options[:provider_options].merge({:provider => options[:provider]}).symbolize_keys
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Persistable
|
2
|
+
##
|
3
|
+
# Factory class to help to define particular adapters.
|
4
|
+
# @deprecated This class has a strange API, so in the future a new API will be provided
|
5
|
+
class Factory
|
6
|
+
|
7
|
+
##
|
8
|
+
# Builds a new adapter
|
9
|
+
# @param yml_file_path [String] the path to the config path in yml format.
|
10
|
+
# This file should include a `Hash` with a key `:adapter`, which is a `Hash` itself.
|
11
|
+
# The latter hash should contain a field named `type` specifying the adapter type.
|
12
|
+
# The adapter type is one of ['filesystem', 'mogilefs', 'memory', 'cloud']
|
13
|
+
# @param defaults [Hash={}] Hash with default values for given adapter. Those values will be reverse merged
|
14
|
+
# with values taken from yml file. The whole method is still useful just because of this param.
|
15
|
+
# Usually you will specify all values using `defaults` and leaving `yml_file_path` empty.
|
16
|
+
# @return [Adapter] Returns one of the adapter instances specified by the `type` option
|
17
|
+
def self.build(yml_file_path, defaults = {})
|
18
|
+
adapter_config = load_yml(yml_file_path, defaults)
|
19
|
+
raise ArgumentError.new("Invalid Parameter: There should be a Hash with :adapter key.") unless adapter_config[:adapter]
|
20
|
+
adapter_config = adapter_config[:adapter].symbolize_keys
|
21
|
+
raise ArgumentError.new("Invalid Parameter: There should be a Hash with :type key.") unless adapter_config[:type]
|
22
|
+
|
23
|
+
adapter = case adapter_config.delete(:type)
|
24
|
+
when 'filesystem'
|
25
|
+
require "persistable/fs_adapter"
|
26
|
+
Persistable::FSAdapter.new(adapter_config)
|
27
|
+
when 'mogilefs'
|
28
|
+
require "persistable/mogile_fs_adapter"
|
29
|
+
Persistable::MogileFSAdapter.new(adapter_config)
|
30
|
+
when 'memory'
|
31
|
+
require "persistable/memory_adapter"
|
32
|
+
Persistable::MemoryAdapter.new(adapter_config)
|
33
|
+
when 'cloud'
|
34
|
+
require "persistable/cloud_storage_adapter"
|
35
|
+
Persistable::CloudStorageAdapter.new(adapter_config)
|
36
|
+
end
|
37
|
+
|
38
|
+
return adapter
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Loads the config from a yml file and returns the values as a `Hash`
|
43
|
+
# @param yml_file_path (@see #build)
|
44
|
+
# @param defaults (@see #build)
|
45
|
+
# @return [Hash] options needed to build an adapter
|
46
|
+
def self.load_yml(yml_file_path, defaults = {})
|
47
|
+
defaults.merge!(YAML.load_file(yml_file_path).symbolize_keys) if yml_file_path && File.exists?(yml_file_path)
|
48
|
+
return defaults
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Persistable
|
2
|
+
class FSAdapter
|
3
|
+
|
4
|
+
attr_reader :storage_location
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@storage_location = options[:storage_location] || "/tmp"
|
8
|
+
end
|
9
|
+
|
10
|
+
def write(persistable)
|
11
|
+
File.open("#{storage_location}/#{persistable.persistence_key}", "w") do |file|
|
12
|
+
file.write persistable.persistence_data.read
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def read(persistable)
|
17
|
+
if File.exists?(path = "#{storage_location}/#{persistable.persistence_key}")
|
18
|
+
persistable.persistence_data = File.open(path)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete(persistable)
|
23
|
+
if File.exists?(path = "#{storage_location}/#{persistable.persistence_key}")
|
24
|
+
File.delete("#{storage_location}/#{persistable.persistence_key}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Persistable
|
2
|
+
class MemoryAdapter
|
3
|
+
|
4
|
+
# Options are not used in this case, but we implement the constructor
|
5
|
+
# header in the same way the other adapters implement it.
|
6
|
+
def initialize(options = {})
|
7
|
+
@storage = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def write(persistable)
|
11
|
+
@storage[persistable.persistence_key] = persistable.persistence_data
|
12
|
+
end
|
13
|
+
|
14
|
+
def read(persistable)
|
15
|
+
persistable.persistence_data = @storage[persistable.persistence_key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(persistable)
|
19
|
+
@storage.delete(persistable.persistence_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'mogilefs'
|
2
|
+
|
3
|
+
module Persistable
|
4
|
+
class MogileFSAdapter
|
5
|
+
|
6
|
+
attr_reader :mogile_fs_class, :options
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@options = options
|
11
|
+
@mogile_fs_class = options[:class]
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(persistable)
|
15
|
+
hosts = options[:tracker].is_a?(String) ? [options[:tracker]] : options[:tracker]
|
16
|
+
connection = MogileFS::MogileFS.new(:domain => options[:domain], :hosts => hosts, :timeout => options[:timeout] ? options[:timeout] : 10)
|
17
|
+
status = connection.store_file(persistable.persistence_key, mogile_fs_class, persistable.persistence_data)
|
18
|
+
connection.backend.shutdown
|
19
|
+
status
|
20
|
+
end
|
21
|
+
|
22
|
+
def read(persistable)
|
23
|
+
hosts = options[:tracker].is_a?(String) ? [options[:tracker]] : options[:tracker]
|
24
|
+
connection = MogileFS::MogileFS.new(:domain => options[:domain], :hosts => hosts, :timeout => options[:timeout] ? options[:timeout] : 10)
|
25
|
+
begin
|
26
|
+
if data = connection.get_file_data(persistable.persistence_key)
|
27
|
+
persistable.persistence_data = StringIO.new(data)
|
28
|
+
else
|
29
|
+
persistable.persistence_data = nil
|
30
|
+
end
|
31
|
+
rescue MogileFS::Backend::UnknownKeyError, MogileFS::Backend::NoKeyError => e
|
32
|
+
persistable.persistence_data = nil
|
33
|
+
ensure
|
34
|
+
connection.backend.shutdown
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete(persistable)
|
39
|
+
hosts = options[:tracker].is_a?(String) ? [options[:tracker]] : options[:tracker]
|
40
|
+
connection = MogileFS::MogileFS.new(:domain => options[:domain], :hosts => hosts, :timeout => options[:timeout] ? options[:timeout] : 10)
|
41
|
+
begin
|
42
|
+
status = connection.delete(persistable.persistence_key)
|
43
|
+
rescue MogileFS::Backend::UnknownKeyError => e
|
44
|
+
status = false
|
45
|
+
ensure
|
46
|
+
connection.backend.shutdown
|
47
|
+
end
|
48
|
+
|
49
|
+
status
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/persistable.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "persistable/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "persistable"
|
7
|
+
s.version = Persistable::VERSION
|
8
|
+
s.authors = ["pkw.de development team"]
|
9
|
+
s.email = ["dev@pkw.de"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Persistence gem for pkw.de images}
|
12
|
+
s.description = %q{Defines different ways to store images: file, memory, mogile_fs}
|
13
|
+
|
14
|
+
# s.add_dependency "mogilefs-client", "2.2.0"
|
15
|
+
s.add_development_dependency "rake"
|
16
|
+
s.add_development_dependency "test-unit"
|
17
|
+
s.add_development_dependency "mocha"
|
18
|
+
s.add_development_dependency "yard"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'persistable/cloud_storage_adapter'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
class CloudStorageTest < Test::Unit::TestCase
|
8
|
+
def test_write_file
|
9
|
+
persistable = get_persistable
|
10
|
+
persistable.persistence_data = File.open(__FILE__)
|
11
|
+
assert adapter.write persistable
|
12
|
+
assert_true File.exist?(file_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_read_file
|
16
|
+
prepare_file
|
17
|
+
persistable = get_persistable
|
18
|
+
assert adapter.read persistable
|
19
|
+
assert persistable.persistence_data
|
20
|
+
assert_equal persistable.persistence_data.read, File.read(file_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_read_not_existent_path
|
24
|
+
assert_nothing_raised do
|
25
|
+
delete_file
|
26
|
+
persistable = get_persistable
|
27
|
+
assert_false adapter.read persistable
|
28
|
+
assert_nil persistable.persistence_data
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert_delete
|
33
|
+
prepare_file
|
34
|
+
persistable = get_persistable
|
35
|
+
assert_true adapter.delete persistable
|
36
|
+
assert_false File.exist?(file_path)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_initializer_should_check_provider
|
40
|
+
assert_raise ArgumentError do
|
41
|
+
Persistable::CloudStorageAdapter.new(:provider => :now_known_provider)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_initializer_should_check_directory_name
|
46
|
+
assert_raise ArgumentError do
|
47
|
+
Persistable::CloudStorageAdapter.new(:provider => :aws)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_initializer_should_not_raise_errors_if_all_required_options_given
|
52
|
+
assert_nothing_raised do
|
53
|
+
Persistable::CloudStorageAdapter.new(valid_options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def get_persistable
|
60
|
+
persistable = PersistableClass.new
|
61
|
+
persistable.persistence_key = "my_file_name"
|
62
|
+
persistable
|
63
|
+
end
|
64
|
+
|
65
|
+
def file_path
|
66
|
+
'/tmp/my_storage_dir/my_file_name'
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete_file
|
70
|
+
FileUtils.rm_f file_path
|
71
|
+
end
|
72
|
+
|
73
|
+
def prepare_file
|
74
|
+
FileUtils.cp __FILE__, file_path
|
75
|
+
assert_true File.exist?(file_path)
|
76
|
+
end
|
77
|
+
|
78
|
+
def adapter
|
79
|
+
Persistable::CloudStorageAdapter.new valid_options
|
80
|
+
end
|
81
|
+
|
82
|
+
def valid_options
|
83
|
+
{
|
84
|
+
:provider => :local,
|
85
|
+
:directory => 'my_storage_dir',
|
86
|
+
:provider_options => {
|
87
|
+
:local_root => '/tmp'
|
88
|
+
}
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'persistable/factory'
|
3
|
+
|
4
|
+
class FactoryTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_should_build_filesystem_adapter_from_yml
|
7
|
+
yml_file_name = "adapter.yml"
|
8
|
+
Persistable::Factory.expects(:load_yml).with(yml_file_name, {}).returns({:adapter => {:type => "filesystem"}})
|
9
|
+
persistable = Persistable::Factory.build(yml_file_name, {})
|
10
|
+
assert_kind_of Persistable::FSAdapter, persistable
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_should_build_memory_adapter_from_yml
|
14
|
+
yml_file_name = "adapter.yml"
|
15
|
+
Persistable::Factory.expects(:load_yml).with(yml_file_name, {}).returns({:adapter => {:type => "memory"}})
|
16
|
+
persistable = Persistable::Factory.build(yml_file_name, {})
|
17
|
+
assert_kind_of Persistable::MemoryAdapter, persistable
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_build_mogilefs_adapter_from_yml
|
21
|
+
yml_file_name = "adapter.yml"
|
22
|
+
Persistable::Factory.expects(:load_yml).with(yml_file_name, {}).returns({:adapter => {:type => "mogilefs", :tracker => "tracker1:6001", :class => "foo", :domain => "bar"}})
|
23
|
+
persistable_adapter = mock("persistable_adapter")
|
24
|
+
Persistable::MogileFSAdapter.expects(:new).with({:tracker => "tracker1:6001", :class => "foo", :domain => "bar"}).returns(persistable_adapter)
|
25
|
+
persistable = Persistable::Factory.build(yml_file_name, {})
|
26
|
+
assert_equal persistable_adapter, persistable
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_should_load_yml_from_filename
|
30
|
+
File.expects(:exists?).returns(true)
|
31
|
+
mock_hash = mock("MockHash")
|
32
|
+
mock_hash.expects(:symbolize_keys).returns({})
|
33
|
+
YAML.expects(:load_file).with("adapter.yml").returns(mock_hash)
|
34
|
+
Persistable::Factory.load_yml("adapter.yml")
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_should_just_return_defaults_in_load_yml_if_file_does_not_exist
|
38
|
+
file_mock = mock("File_mock")
|
39
|
+
File.expects(:exists?).returns(false)
|
40
|
+
defaults = mock("Defaults")
|
41
|
+
loaded_config = Persistable::Factory.load_yml("adapter.yml", defaults)
|
42
|
+
assert_equal defaults, loaded_config
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_should_build_adapter_from_defaults
|
46
|
+
defaults = mock("defaults")
|
47
|
+
Persistable::Factory.expects(:load_yml).with(nil, defaults).returns({:adapter => {:type => "memory"}})
|
48
|
+
Persistable::Factory.build(nil, defaults)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_build_should_raise_exception_if_wrong_parameter
|
52
|
+
Persistable::Factory.expects(:load_yml).with("file_path", {}).returns(:wrong => :hash)
|
53
|
+
assert_raise(ArgumentError) { Persistable::Factory.build("file_path") }
|
54
|
+
Persistable::Factory.expects(:load_yml).with("file_path", {}).returns(:adapter => {:no_type => "but_adapter"})
|
55
|
+
assert_raise(ArgumentError) { Persistable::Factory.build("file_path") }
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_should_build_cloud_storage_adapter_using_yml
|
59
|
+
yml_file_name = "adapter.yml"
|
60
|
+
Persistable::Factory.expects(:load_yml).with(yml_file_name, {}).returns(
|
61
|
+
{:adapter =>
|
62
|
+
{
|
63
|
+
:type => "cloud",
|
64
|
+
:directory => "store_directory",
|
65
|
+
:provider => "local",
|
66
|
+
:provider_options => {
|
67
|
+
:local_root => "/tmp"
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
)
|
72
|
+
|
73
|
+
persistable = Persistable::Factory.build(yml_file_name, {})
|
74
|
+
assert_kind_of Persistable::CloudStorageAdapter, persistable
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'persistable/fs_adapter'
|
3
|
+
|
4
|
+
class FSAdapterTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_should_write_persistable_objects
|
7
|
+
adapter = Persistable::FSAdapter.new
|
8
|
+
adapter.expects(:storage_location).returns("/tmp")
|
9
|
+
|
10
|
+
persistable_object = mock("Persistable")
|
11
|
+
persistable_object.expects(:persistence_key).returns("42")
|
12
|
+
persistable_object.expects(:persistence_data).returns(StringIO.new("The answer to all questions."))
|
13
|
+
|
14
|
+
adapter.write(persistable_object)
|
15
|
+
|
16
|
+
assert File.exists?("/tmp/42")
|
17
|
+
assert_equal "The answer to all questions.", File.open("/tmp/42").read
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_read_persistable_objects
|
21
|
+
adapter = Persistable::FSAdapter.new
|
22
|
+
adapter.expects(:storage_location).returns("/tmp")
|
23
|
+
|
24
|
+
File.open("/tmp/just_a_file", "wb") { |file| file.puts "test" }
|
25
|
+
just_a_file = File.open("/tmp/just_a_file")
|
26
|
+
File.expects(:open).with("/tmp/just_a_file").returns(just_a_file)
|
27
|
+
|
28
|
+
persistable_object = mock("Persistable")
|
29
|
+
persistable_object.expects(:persistence_key).returns("just_a_file")
|
30
|
+
persistable_object.expects(:persistence_data=).with(just_a_file)
|
31
|
+
|
32
|
+
adapter.read(persistable_object)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_should_not_read_not_existing_persistable_objects
|
36
|
+
adapter = Persistable::FSAdapter.new
|
37
|
+
adapter.expects(:storage_location).at_least_once.returns("/tmp")
|
38
|
+
|
39
|
+
persistable_object = mock("Persistable")
|
40
|
+
persistable_object.expects(:persistence_key).returns("just_a_not_existing_key")
|
41
|
+
persistable_object.expects(:persistence_data=).never
|
42
|
+
|
43
|
+
adapter.read(persistable_object)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_should_delete_persistable_objects
|
47
|
+
adapter = Persistable::FSAdapter.new
|
48
|
+
adapter.expects(:storage_location).times(3).returns("/tmp")
|
49
|
+
|
50
|
+
persistable_object = mock("Persistable")
|
51
|
+
persistable_object.expects(:persistence_key).times(3).returns("42")
|
52
|
+
persistable_object.expects(:persistence_data).returns(StringIO.new("The answer to all questions."))
|
53
|
+
|
54
|
+
adapter.write(persistable_object)
|
55
|
+
|
56
|
+
assert File.exists?("/tmp/42")
|
57
|
+
assert_equal "The answer to all questions.", File.open("/tmp/42").read
|
58
|
+
|
59
|
+
adapter.delete(persistable_object)
|
60
|
+
assert !File.exists?("/tmp/42")
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_should_set_location_for_persisting_data
|
64
|
+
adapter = Persistable::FSAdapter.new(:storage_location => "/custom/location")
|
65
|
+
assert_equal "/custom/location", adapter.storage_location
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_should_have_default_storage_location_set_to_global_tmp_dir
|
69
|
+
adapter = Persistable::FSAdapter.new
|
70
|
+
assert_equal "/tmp", adapter.storage_location
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'persistable/memory_adapter'
|
3
|
+
|
4
|
+
class MemoryAdapterTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_should_write_persistable_objects
|
7
|
+
adapter = Persistable::MemoryAdapter.new
|
8
|
+
|
9
|
+
persistable_object = mock("Persistable")
|
10
|
+
persistable_object.expects(:persistence_key).returns("42")
|
11
|
+
persistable_object.expects(:persistence_data).returns(StringIO.new("The answer to all questions."))
|
12
|
+
|
13
|
+
adapter.write(persistable_object)
|
14
|
+
|
15
|
+
assert_equal "The answer to all questions.", adapter.instance_variable_get("@storage")["42"].read
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_read_persistable_objects
|
19
|
+
adapter = Persistable::MemoryAdapter.new
|
20
|
+
|
21
|
+
test_io_object = StringIO.new("The answer to all questions.")
|
22
|
+
adapter.instance_variable_set("@storage", {"42" => test_io_object})
|
23
|
+
|
24
|
+
persistable_object = mock("Persistable")
|
25
|
+
persistable_object.expects(:persistence_key).returns("42")
|
26
|
+
persistable_object.expects(:persistence_data=).with(test_io_object)
|
27
|
+
|
28
|
+
adapter.read(persistable_object)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_should_delete_persistable_objects
|
32
|
+
adapter = Persistable::MemoryAdapter.new
|
33
|
+
|
34
|
+
persistable_object = mock("Persistable")
|
35
|
+
persistable_object.expects(:persistence_key).times(2).returns("42")
|
36
|
+
persistable_object.expects(:persistence_data).returns(StringIO.new("The answer to all questions."))
|
37
|
+
|
38
|
+
adapter.write(persistable_object)
|
39
|
+
|
40
|
+
assert_equal "The answer to all questions.", adapter.instance_variable_get("@storage")["42"].read
|
41
|
+
|
42
|
+
adapter.delete(persistable_object)
|
43
|
+
assert !adapter.instance_variable_get("@storage").has_key?("42")
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'persistable/mogile_fs_adapter'
|
3
|
+
|
4
|
+
class MogileFSAdapterTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_should_write_and_read_and_delete_persistable_objects
|
7
|
+
adapter = Persistable::MogileFSAdapter.new(:domain => "mydomain", :tracker => "tracker.test.de:6001", :class => 'devel')
|
8
|
+
|
9
|
+
data_to_persist = StringIO.new("The answer to all questions.")
|
10
|
+
persistable_object_in = mock("PersistableIn")
|
11
|
+
persistable_object_in.expects(:persistence_key).returns("42")
|
12
|
+
persistable_object_in.expects(:persistence_data).returns(data_to_persist)
|
13
|
+
|
14
|
+
connection_in = mock("MogileFS-Connection")
|
15
|
+
connection_in.expects(:store_file).with("42", "devel", data_to_persist)
|
16
|
+
connection_in.expects(:backend).returns(mock("MockBackend", :shutdown => true))
|
17
|
+
MogileFS::MogileFS.expects(:new).at_least_once.with(:domain => "mydomain", :hosts => ["tracker.test.de:6001"], :timeout => 10).returns(connection_in)
|
18
|
+
|
19
|
+
adapter.write(persistable_object_in)
|
20
|
+
|
21
|
+
persistable_object_out = mock("PersistableOut")
|
22
|
+
persistable_object_out.expects(:persistence_key).returns("42")
|
23
|
+
persistable_object_out.expects(:persistence_data=).with() { |data| data.kind_of? StringIO }
|
24
|
+
|
25
|
+
connection_out = mock("MogileFS-Connection")
|
26
|
+
connection_out.expects(:get_file_data).with("42").returns(data_to_persist.read)
|
27
|
+
connection_out.expects(:backend).returns(mock("MockBackend", :shutdown => true))
|
28
|
+
MogileFS::MogileFS.expects(:new).at_least_once.with(:domain => "mydomain", :hosts => ["tracker.test.de:6001"], :timeout => 10).returns(connection_out)
|
29
|
+
|
30
|
+
adapter.read(persistable_object_out)
|
31
|
+
|
32
|
+
persistable_object_delete = mock("PersistableOut")
|
33
|
+
persistable_object_delete.expects(:persistence_key).returns("42")
|
34
|
+
|
35
|
+
connection_delete = mock("MogileFS-Connection")
|
36
|
+
connection_delete.expects(:delete).with("42")
|
37
|
+
connection_delete.expects(:backend).returns(mock("MockBackend", :shutdown => true))
|
38
|
+
MogileFS::MogileFS.expects(:new).at_least_once.with(:domain => "mydomain", :hosts => ["tracker.test.de:6001"], :timeout => 10).returns(connection_delete)
|
39
|
+
|
40
|
+
adapter.delete(persistable_object_delete)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_should_set_domain_tracker_and_class
|
44
|
+
adapter = Persistable::MogileFSAdapter.new(:domain => "mydomain", :tracker => "tracker.test.de:6001", :class => 'devel')
|
45
|
+
assert_equal "devel", adapter.mogile_fs_class
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_should_set_domain_multiple_tracker_and_class
|
49
|
+
adapter = Persistable::MogileFSAdapter.new(:domain => "mydomain", :tracker => ["tracker1.test.de:6001", "tracker2.test.com:6001"], :class => 'devel')
|
50
|
+
assert_equal "devel", adapter.mogile_fs_class
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_should_set_timeout
|
54
|
+
adapter = Persistable::MogileFSAdapter.new(:domain => "mydomain", :tracker => "tracker.test.de:6001", :class => 'devel', :timeout => 10)
|
55
|
+
assert_equal "devel", adapter.mogile_fs_class
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_should_handle_unknown_key_on_delete_gracefully
|
59
|
+
adapter = Persistable::MogileFSAdapter.new(:domain => "mydomain", :tracker => "tracker.test.de:6001", :class => 'devel')
|
60
|
+
connection_delete = mock("MogileFS-Connection")
|
61
|
+
persistable_object_delete = mock("PersistableOut")
|
62
|
+
persistable_object_delete.expects(:persistence_key).returns("42")
|
63
|
+
connection_delete.expects(:delete).raises(MogileFS::Backend::UnknownKeyError)
|
64
|
+
connection_delete.expects(:backend).returns(mock("MockBackend", :shutdown => true))
|
65
|
+
MogileFS::MogileFS.expects(:new).at_least_once.with(:domain => "mydomain", :hosts => ["tracker.test.de:6001"], :timeout => 10).returns(connection_delete)
|
66
|
+
|
67
|
+
assert_equal(false, adapter.delete(persistable_object_delete))
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_should_handle_unknown_key_on_read_gracefully
|
71
|
+
adapter = Persistable::MogileFSAdapter.new(:domain => "mydomain", :tracker => "tracker.test.de:6001", :class => 'devel')
|
72
|
+
connection_delete = mock("MogileFS-Connection")
|
73
|
+
persistable_object_read = mock("PersistableOut")
|
74
|
+
persistable_object_read.expects(:persistence_key).returns("42")
|
75
|
+
persistable_object_read.expects(:persistence_data=).with(nil)
|
76
|
+
connection_delete.expects(:get_file_data).raises(MogileFS::Backend::UnknownKeyError)
|
77
|
+
connection_delete.expects(:backend).returns(mock("MockBackend", :shutdown => true))
|
78
|
+
MogileFS::MogileFS.expects(:new).at_least_once.with(:domain => "mydomain", :hosts => ["tracker.test.de:6001"], :timeout => 10).returns(connection_delete)
|
79
|
+
|
80
|
+
assert_nothing_raised(MogileFS::Backend::UnknownKeyError) { adapter.read(persistable_object_read) }
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_should_return_nil_if_file_was_not_found
|
84
|
+
adapter = Persistable::MogileFSAdapter.new(:domain => "mydomain", :tracker => "tracker.test.de:6001", :class => 'devel')
|
85
|
+
|
86
|
+
|
87
|
+
persistable_object_out = mock("PersistableOut")
|
88
|
+
persistable_object_out.expects(:persistence_key).returns("42")
|
89
|
+
persistable_object_out.expects(:persistence_data=).with(nil)
|
90
|
+
|
91
|
+
connection_out = mock("MogileFS-Connection")
|
92
|
+
connection_out.expects(:get_file_data).with("42").returns(nil)
|
93
|
+
connection_out.expects(:backend).returns(mock("MockBackend", :shutdown => true))
|
94
|
+
MogileFS::MogileFS.expects(:new).with(:domain => "mydomain", :hosts => ["tracker.test.de:6001"], :timeout => 10).returns(connection_out)
|
95
|
+
adapter.read(persistable_object_out)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: persistable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 3
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 7
|
9
|
+
- 0
|
10
|
+
version: 0.7.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- pkw.de development team
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-02-07 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rake
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
requirement: *id001
|
33
|
+
prerelease: false
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: test-unit
|
36
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :development
|
46
|
+
requirement: *id002
|
47
|
+
prerelease: false
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: mocha
|
50
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
type: :development
|
60
|
+
requirement: *id003
|
61
|
+
prerelease: false
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: yard
|
64
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
type: :development
|
74
|
+
requirement: *id004
|
75
|
+
prerelease: false
|
76
|
+
description: "Defines different ways to store images: file, memory, mogile_fs"
|
77
|
+
email:
|
78
|
+
- dev@pkw.de
|
79
|
+
executables: []
|
80
|
+
|
81
|
+
extensions: []
|
82
|
+
|
83
|
+
extra_rdoc_files: []
|
84
|
+
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- Gemfile
|
88
|
+
- History.txt
|
89
|
+
- README.rdoc
|
90
|
+
- Rakefile
|
91
|
+
- lib/persistable.rb
|
92
|
+
- lib/persistable/cloud_storage_adapter.rb
|
93
|
+
- lib/persistable/factory.rb
|
94
|
+
- lib/persistable/fs_adapter.rb
|
95
|
+
- lib/persistable/memory_adapter.rb
|
96
|
+
- lib/persistable/mogile_fs_adapter.rb
|
97
|
+
- lib/persistable/version.rb
|
98
|
+
- lib/persistable/xt/hash.rb
|
99
|
+
- persistable.gemspec
|
100
|
+
- test/cloud_storage_test.rb
|
101
|
+
- test/factory_test.rb
|
102
|
+
- test/fs_adapter_test.rb
|
103
|
+
- test/memory_adapter_test.rb
|
104
|
+
- test/mogile_fs_adapter_test.rb
|
105
|
+
- test/test_helper.rb
|
106
|
+
homepage: ""
|
107
|
+
licenses: []
|
108
|
+
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 3
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
hash: 3
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
version: "0"
|
132
|
+
requirements: []
|
133
|
+
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 1.8.24
|
136
|
+
signing_key:
|
137
|
+
specification_version: 3
|
138
|
+
summary: Persistence gem for pkw.de images
|
139
|
+
test_files:
|
140
|
+
- test/cloud_storage_test.rb
|
141
|
+
- test/factory_test.rb
|
142
|
+
- test/fs_adapter_test.rb
|
143
|
+
- test/memory_adapter_test.rb
|
144
|
+
- test/mogile_fs_adapter_test.rb
|
145
|
+
- test/test_helper.rb
|
146
|
+
has_rdoc:
|