persistable 0.7.0
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.
- 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:
|