storage 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,71 @@
1
+ = Storage
2
+
3
+ This gem provides a simple API for multiple storage backends. Supported storages: Amazon S3 and FileSystem.
4
+
5
+ The storage provides only 3 methods, one for each operation: <tt>Storage::get</tt>, <tt>Storage::store</tt> and <tt>Storage::remove</tt>.
6
+
7
+ == Installation
8
+
9
+ sudo gem install storage
10
+
11
+ You can get source code at http://github.com/fnando/storage
12
+
13
+ == Usage
14
+
15
+ require "rubygems"
16
+ require "storage"
17
+
18
+ You basically use the same method no matter what storage strategy you're using.
19
+
20
+ === Amazon S3
21
+
22
+ Storage.setup do |config|
23
+ config.strategy = :s3
24
+ config.access_key = "abcdef"
25
+ config.secret_key = "123456"
26
+ end
27
+
28
+ # Store a local file on S3.
29
+ # You can easily switch from S3 to FileSystem; keys that are not used by one strategy is
30
+ # simply ignored.
31
+ Storage.store "some/file.rb", :name => "file.rb", :bucket => "sample"
32
+ Storage.store File.open("some/file.rb"), :name => "file.rb", :bucket => "sample"
33
+
34
+ # Retrieve the public url for that file
35
+ Storage.get "file.rb"
36
+ #=> http://s3.amazon.com/sample-files/file.rb
37
+
38
+ # Remove a file.
39
+ Storage.remove "file.rb"
40
+
41
+ === FileSystem
42
+
43
+ Storage.setup do |config|
44
+ config.strategy = :file
45
+ config.path = "some/directory"
46
+ end
47
+
48
+ # Store a file.
49
+ Storage.store "some/file.rb", :name => "file.rb"
50
+ Storage.store File.open("some/file.rb"), :name => "file.rb"
51
+
52
+ # Retrieve that file's path.
53
+ Storage.get "file.rb"
54
+ #=> some/directory/file.rb
55
+
56
+ # Remove a file.
57
+ Storage.remove "file.rb"
58
+
59
+ == License
60
+
61
+ (The MIT License)
62
+
63
+ Copyright © 2010:
64
+
65
+ * Nando Vieira (http://simplesideias.com.br)
66
+
67
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
68
+
69
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
70
+
71
+ THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ module Storage
2
+ class Config
3
+ class << self
4
+ # Set a storage strategy based on its registered name.
5
+ #
6
+ # Storage::Config.strategy = :s3
7
+ #
8
+ attr_accessor :strategy
9
+
10
+ # Set a storage class.
11
+ #
12
+ # Storage::Config.strategy_class = Storage::Strategies::S3
13
+ #
14
+ attr_accessor :strategy_class
15
+
16
+ # Set the S3 default bucket.
17
+ attr_accessor :bucket
18
+
19
+ # Set the S3 access key.
20
+ attr_accessor :access_key
21
+
22
+ # Set the S3 secret key
23
+ attr_accessor :secret_key
24
+
25
+ # Set the FileSystem storage path.
26
+ attr_accessor :path
27
+ end
28
+
29
+ # Override setter so we can automatically define the strategy class
30
+ # based on its registered name.
31
+ def self.strategy=(strategy)
32
+ self.strategy_class = eval(Storage::Strategies::STRATEGIES[strategy])
33
+ @strategy = strategy
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ module Storage
2
+ class FileAlreadyExistsError < StandardError; end
3
+ class MissingFileError < StandardError; end
4
+ end
@@ -0,0 +1,39 @@
1
+ module Storage
2
+ module Strategies
3
+ module FileSystem
4
+ extend self
5
+
6
+ def self.prepare!
7
+ FileUtils.mkdir_p File.expand_path(Storage::Config.path)
8
+ end
9
+
10
+ def fullpath(file)
11
+ File.expand_path(File.join(Storage::Config.path, file))
12
+ end
13
+
14
+ def get(file)
15
+ path = fullpath(file)
16
+ raise Storage::MissingFileError unless File.file?(path)
17
+ path
18
+ end
19
+
20
+ def remove(file)
21
+ path = get(file)
22
+ File.unlink(path)
23
+ end
24
+
25
+ def store(file, options = {})
26
+ file = File.open(file, "rb") unless file.respond_to?(:read) && !file.kind_of?(Pathname)
27
+ path = fullpath(options[:name])
28
+
29
+ raise Storage::FileAlreadyExistsError if File.file?(path)
30
+
31
+ File.open(path, "wb") do |handler|
32
+ while line = file.gets
33
+ handler.write line
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,52 @@
1
+ module Storage
2
+ module Strategies
3
+ module S3
4
+ extend self
5
+
6
+ def prepare!
7
+ AWS::S3::Base.establish_connection!({
8
+ :access_key_id => Storage::Config.access_key,
9
+ :secret_access_key => Storage::Config.secret_key
10
+ }) unless AWS::S3::Base.connected?
11
+ end
12
+
13
+ def get(file, options = {})
14
+ object = find_object(file, options)
15
+ AWS::S3::S3Object.url_for(file, options[:bucket], :authenticated => false)
16
+ rescue AWS::S3::NoSuchKey, AWS::S3::NoSuchBucket
17
+ raise Storage::MissingFileError
18
+ end
19
+
20
+ def store(file, options = {})
21
+ object = find_object(file, options) rescue nil
22
+
23
+ raise Storage::FileAlreadyExistsError if object
24
+
25
+ bucket = find_bucket_or_create(options[:bucket])
26
+ file = File.open(file, "rb") unless file.respond_to?(:read) && !file.kind_of?(Pathname)
27
+ AWS::S3::S3Object.store(options[:name], file, bucket.name, :access => :public_read)
28
+ end
29
+
30
+ def remove(file, options = {})
31
+ object = find_object(file, options)
32
+ object.delete
33
+ rescue AWS::S3::NoSuchKey, AWS::S3::NoSuchBucket
34
+ raise Storage::MissingFileError
35
+ end
36
+
37
+ def find_bucket(name)
38
+ AWS::S3::Bucket.find(name)
39
+ end
40
+
41
+ def find_object(file, options = {})
42
+ AWS::S3::S3Object.find(file, options[:bucket])
43
+ end
44
+
45
+ def find_bucket_or_create(name)
46
+ bucket = find_bucket(name)
47
+ bucket ||= AWS::S3::Bucket.create(name)
48
+ bucket
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module Storage
2
+ module Strategies
3
+ autoload :S3, "storage/strategies/s3"
4
+ autoload :FileSystem, "storage/strategies/file_system"
5
+
6
+ STRATEGIES = {}
7
+
8
+ # Register a new strategy.
9
+ def self.register(name, klass)
10
+ STRATEGIES[name] = klass.to_s
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module Storage
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 0
6
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
+ end
8
+ end
data/lib/storage.rb ADDED
@@ -0,0 +1,48 @@
1
+ require "ostruct"
2
+ require "fileutils" unless defined?(FileUtils)
3
+ require "storage/errors"
4
+ require "aws/s3"
5
+
6
+ module Storage
7
+ autoload :Config, "storage/config"
8
+ autoload :Strategies, "storage/strategies"
9
+ autoload :Version, "storage/version"
10
+
11
+ class << self
12
+ # Set up the storage options.
13
+ #
14
+ # Storage.setup do |config|
15
+ # config.strategy = :s3
16
+ # end
17
+ #
18
+ # Check Storage::Config for available options.
19
+ #
20
+ def setup(&block)
21
+ yield Config
22
+ strategy.prepare!
23
+ end
24
+
25
+ # A shortcut to the current strategy.
26
+ def strategy
27
+ Config.strategy_class
28
+ end
29
+
30
+ # Save a file.
31
+ def store(*args)
32
+ strategy.store(*args)
33
+ end
34
+
35
+ # Destroy a file.
36
+ def remove(*args)
37
+ strategy.remove(*args)
38
+ end
39
+
40
+ # Retrieve a file.
41
+ def get(*args)
42
+ strategy.get(*args)
43
+ end
44
+ end
45
+ end
46
+
47
+ Storage::Strategies.register :s3, Storage::Strategies::S3
48
+ Storage::Strategies.register :file, Storage::Strategies::FileSystem
@@ -0,0 +1,9 @@
1
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin sed velit turpis. Etiam purus est, lacinia at suscipit et, tempor id orci. Sed pulvinar orci non lacus pharetra quis vestibulum elit posuere. Morbi vestibulum neque vitae ligula lobortis dignissim dictum lorem venenatis. Praesent scelerisque placerat metus, cursus eleifend ligula luctus non. Sed sodales pharetra massa sit amet venenatis. Proin adipiscing ligula vel urna bibendum condimentum. Maecenas tellus justo, suscipit id vehicula a, mattis sit amet ipsum. Praesent tempus fermentum enim, non fringilla sapien tincidunt et. Ut auctor bibendum purus, vitae fermentum neque viverra non. Etiam vel lacinia libero. Nulla vel sagittis neque. Pellentesque tristique est porta diam accumsan dignissim.
2
+
3
+ Nulla non adipiscing lorem. Sed tortor nisl, gravida gravida dictum fermentum, pulvinar iaculis metus. Morbi id eros arcu. Duis vitae magna nec leo semper porta. Nulla aliquam nibh sit amet ligula egestas nec viverra dui fringilla. Vivamus vestibulum dolor non dui dapibus posuere. Proin sem arcu, molestie a volutpat non, faucibus quis dui. Etiam non nunc ac libero posuere venenatis. Sed sed tellus non diam bibendum feugiat ut eu turpis. Integer consectetur turpis sed tortor fringilla sagittis. Praesent quis est nec dui auctor adipiscing a eu ipsum. Phasellus eros nisi, pharetra ut accumsan a, vehicula vel odio. Aenean mattis congue bibendum. Aliquam laoreet, felis ac vehicula dictum, velit lorem convallis urna, at eleifend odio magna nec enim. Sed quis dui a quam facilisis dapibus et sed nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Morbi imperdiet bibendum metus ut iaculis.
4
+
5
+ Sed adipiscing, enim sit amet lacinia vulputate, leo nulla ultricies sapien, at posuere justo sapien quis metus. Aliquam erat volutpat. Mauris eros nisl, euismod imperdiet rutrum ut, tempor aliquam magna. Maecenas ac nisi et nisi pellentesque euismod vitae non nisl. Aliquam non ullamcorper nunc. Donec sed metus sit amet purus laoreet commodo sit amet ac enim. Quisque vitae sem vitae lacus elementum consequat. Aliquam nec erat at sapien venenatis hendrerit mollis vel enim. Curabitur id dui enim, nec facilisis purus. Etiam massa velit, facilisis ac pellentesque et, sagittis at nisl. Phasellus facilisis mauris quis dolor euismod eget tincidunt libero elementum. Maecenas quis ornare tortor. Nullam eget tellus nisl. Fusce augue neque, sagittis a blandit condimentum, vehicula at sem. Praesent nec magna nibh. Maecenas enim nunc, interdum non condimentum et, pellentesque sit amet dui. Aliquam vel massa justo, vitae lobortis dui.
6
+
7
+ Curabitur purus risus, dapibus sit amet aliquet vitae, tristique et sem. Curabitur ac lacus ut elit pretium hendrerit vitae sit amet lorem. Vestibulum quis tortor lobortis tortor convallis porttitor. Etiam volutpat imperdiet rhoncus. Suspendisse pulvinar tempor sapien non consectetur. Phasellus vehicula ultricies scelerisque. Quisque porta enim eleifend dolor sollicitudin in pellentesque lorem semper. Vestibulum sed tortor risus, vel malesuada magna. Maecenas id libero non neque scelerisque gravida ac ut lectus. Nam risus urna, tincidunt ut sagittis eget, hendrerit in odio. Vestibulum nec posuere nisl. Sed facilisis risus eget purus interdum vel gravida justo mattis. Aliquam et venenatis justo. Morbi pulvinar, erat quis imperdiet porta, risus ipsum imperdiet ipsum, non euismod nulla dui vel dui.
8
+
9
+ Pellentesque sit amet lectus id tortor porta congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis ipsum massa, iaculis vel commodo venenatis, congue eu neque. Morbi eleifend vestibulum ipsum, sit amet elementum ipsum tristique et. Praesent faucibus vestibulum massa sit amet pulvinar. Nulla metus leo, feugiat ac bibendum et, pellentesque scelerisque libero. Proin posuere placerat ultricies. Vivamus turpis turpis, semper pellentesque tincidunt eget, rutrum vitae urna. Etiam id metus orci, a sollicitudin purus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed volutpat, nisl sit amet placerat pharetra, mi risus vestibulum est, euismod pulvinar ligula leo id metus. Curabitur dictum ullamcorper nisi, quis rutrum nunc aliquam ac. Nunc scelerisque, purus in ullamcorper interdum, nulla metus mollis elit, id laoreet elit dolor nec enim. Nullam dolor dui, suscipit sit amet facilisis ut, sagittis eu ante. Aliquam aliquam mattis ullamcorper. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque pharetra dapibus turpis eu porta. Mauris nec iaculis est. In hac habitasse platea dictumst. Nam eleifend magna vitae erat malesuada tempus.
@@ -0,0 +1,21 @@
1
+ require "rspec"
2
+ require "storage"
3
+ require "fileutils"
4
+ require "pathname"
5
+
6
+ TMP = Pathname.new(File.expand_path(File.dirname(__FILE__) + "/tmp"))
7
+ RESOURCES = Pathname.new(File.expand_path(File.dirname(__FILE__) + "/resources"))
8
+
9
+ RSpec.configure do |config|
10
+ config.formatter = "documentation"
11
+ config.color_enabled = true
12
+
13
+ config.before :each do
14
+ FileUtils.rm_rf(TMP) rescue nil
15
+ FileUtils.mkdir_p(TMP) rescue nil
16
+ end
17
+
18
+ config.after :each do
19
+ FileUtils.rm_rf(TMP) rescue nil
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ require "spec_helper"
2
+
3
+ describe Storage::Config do
4
+ it "should set strategy class based on its name" do
5
+ Storage::Config.strategy_class = nil
6
+ Storage::Config.strategy = :s3
7
+
8
+ Storage::Config.strategy_class.should == Storage::Strategies::S3
9
+ end
10
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ describe Storage::Strategies::FileSystem do
4
+ before do
5
+ @source = RESOURCES.join("file.txt")
6
+ @destiny = TMP.join("lorem.txt")
7
+
8
+ Storage.setup do |c|
9
+ c.strategy = :file
10
+ c.path = TMP
11
+ end
12
+ end
13
+
14
+ it "should save a file using file handler" do
15
+ handler = File.open(@source)
16
+ Storage.store(handler, :name => "lorem.txt")
17
+
18
+ File.should be_file(@destiny)
19
+ File.read(@destiny).should == File.read(@source)
20
+ end
21
+
22
+ it "should save a file using a path" do
23
+ Storage.store(@source, :name => "lorem.txt")
24
+
25
+ File.should be_file(@destiny)
26
+ File.read(@destiny).should == File.read(@source)
27
+ end
28
+
29
+ it "should remove an existing file" do
30
+ Storage.store(@source, :name => "lorem.txt")
31
+ Storage.remove("lorem.txt").should be_true
32
+ File.should_not be_file(@destiny)
33
+ end
34
+
35
+ it "should raise when trying to removing an unexesting file" do
36
+ lambda {
37
+ Storage.remove("invalid")
38
+ }.should raise_error(Storage::MissingFileError)
39
+ end
40
+
41
+ it "should retrieve an existing file" do
42
+ Storage.store(@source, :name => "lorem.txt")
43
+ Storage.get("lorem.txt").should == File.expand_path(TMP.join("lorem.txt"))
44
+ end
45
+
46
+ it "should raise when trying to retrieve an unexesting file" do
47
+ lambda {
48
+ Storage.get("invalid")
49
+ }.should raise_error(Storage::MissingFileError)
50
+ end
51
+
52
+ it "should raise when saving a file that already exists" do
53
+ Storage.store(@source, :name => "lorem.txt")
54
+
55
+ lambda {
56
+ Storage.store(@source, :name => "lorem.txt")
57
+ }.should raise_error(Storage::FileAlreadyExistsError)
58
+ end
59
+ end
@@ -0,0 +1,92 @@
1
+ require "spec_helper"
2
+
3
+ describe Storage::Strategies::S3 do
4
+ before do
5
+ @source = RESOURCES.join("file.txt")
6
+ @destiny = TMP.join("lorem.txt")
7
+ @bucket = mock("bucket", :name => "files")
8
+
9
+ AWS::S3::Base.stub!(:establish_connection!)
10
+ AWS::S3::Bucket.stub!(:find).and_return(@bucket)
11
+ AWS::S3::S3Object.stub!(:store)
12
+
13
+ Storage.setup do |c|
14
+ c.strategy = :s3
15
+ c.access_key = "abc"
16
+ c.secret_key = "123"
17
+ end
18
+ end
19
+
20
+ it "should establish connection" do
21
+ options = {:access_key_id => "abc", :secret_access_key => "123"}
22
+ AWS::S3::Base.should_receive(:establish_connection!).with(options)
23
+
24
+ Storage::Strategies::S3.prepare!
25
+ end
26
+
27
+ it "should not reconnect when a connection is already established" do
28
+ AWS::S3::Base.should_receive(:connected?).and_return(true)
29
+ AWS::S3::Base.should_not_receive(:establish_connection!)
30
+
31
+ Storage::Strategies::S3.prepare!
32
+ end
33
+
34
+ it "should save a file using file handler" do
35
+ handler = File.open(@source)
36
+ AWS::S3::S3Object.should_receive(:store).with("lorem.txt", handler, "files", :access => :public_read)
37
+ Storage.store(handler, :name => "lorem.txt", :bucket => "files")
38
+ end
39
+
40
+ it "should save a file using a path" do
41
+ AWS::S3::S3Object.should_receive(:store).with("lorem.txt", kind_of(File), "files", :access => :public_read)
42
+ Storage.store(@source, :name => "lorem.txt", :bucket => "files")
43
+ end
44
+
45
+ it "should remove an existing file" do
46
+ object = mock("object")
47
+ object.should_receive(:delete).and_return(true)
48
+ Storage::Strategies::S3.should_receive(:find_object).with("lorem.txt", :bucket => "files").and_return(object)
49
+
50
+ Storage.remove("lorem.txt", :bucket => "files").should be_true
51
+ end
52
+
53
+ it "should raise when trying to removing an unexesting file" do
54
+ pending "spec is failing but real code works"
55
+
56
+ Storage::Strategies::S3.should_receive(:find_object).and_raise(AWS::S3::NoSuchKey)
57
+
58
+ lambda {
59
+ Storage.remove("lorem.txt", :bucket => "files")
60
+ }.should raise_error(Storage::MissingFileError)
61
+ end
62
+
63
+ it "should retrieve an existing file" do
64
+ object = mock("object")
65
+
66
+ AWS::S3::S3Object.should_receive(:find).with("lorem.txt", "files").and_return(object)
67
+ AWS::S3::S3Object.should_receive(:url_for).with("lorem.txt", "files", :authenticated => false)
68
+
69
+ Storage.get("lorem.txt", :bucket => "files")
70
+ end
71
+
72
+ it "should raise when trying to retrieve an unexesting file" do
73
+ pending "spec is failing but real code works"
74
+
75
+ AWS::S3::S3Object.should_receive(:find).with("lorem.txt", "files").and_raise(AWS::S3::NoSuchKey)
76
+ AWS::S3::S3Object.should_not_receive(:url_for)
77
+
78
+ lambda {
79
+ Storage.get("lorem.txt", :bucket => "files")
80
+ }.should raise_error(Storage::MissingFileError)
81
+ end
82
+
83
+ it "should raise when saving a file that already exists" do
84
+ object = mock("object")
85
+ options = {:name => "lorem.txt", :bucket => "files"}
86
+ Storage::Strategies::S3.should_receive(:find_object).with(@source, options).and_return(object)
87
+
88
+ lambda {
89
+ Storage.store(@source, :name => "lorem.txt", :bucket => "files")
90
+ }.should raise_error(Storage::FileAlreadyExistsError)
91
+ end
92
+ end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ describe Storage do
4
+ it "should return the strategy" do
5
+ @strategy = mock("strategy")
6
+ Storage::Config.strategy_class = @strategy
7
+
8
+ Storage.strategy.should be(@strategy)
9
+ end
10
+
11
+ it "should return the config" do
12
+ Storage::Strategies::S3.stub!(:prepare!)
13
+
14
+ Storage.setup do |config|
15
+ config.strategy = :s3
16
+ config.should be(Storage::Config)
17
+ end
18
+ end
19
+
20
+ it "prepare strategy after setting its configuration" do
21
+ Storage::Strategies::S3.should_receive(:prepare!).once
22
+ Storage.setup {|config| config.strategy = :s3}
23
+ end
24
+
25
+ context "delegation" do
26
+ before do
27
+ @strategy = mock("strategy")
28
+ Storage.should_receive(:strategy).and_return(@strategy)
29
+ end
30
+
31
+ it "should delegate save method" do
32
+ @strategy.should_receive(:store).with("some/file")
33
+ Storage.store "some/file"
34
+ end
35
+
36
+ it "should delegate destroy method" do
37
+ @strategy.should_receive(:remove).with("some/file")
38
+ Storage.remove "some/file"
39
+ end
40
+
41
+ it "should delegate get method" do
42
+ @strategy.should_receive(:get).with("some/file")
43
+ Storage.get "some/file"
44
+ end
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: storage
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Nando Vieira
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-17 00:00:00 -03:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: aws-s3
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ description:
33
+ email: fnando.vieira@gmail.com
34
+ executables: []
35
+
36
+ extensions: []
37
+
38
+ extra_rdoc_files:
39
+ - README.rdoc
40
+ files:
41
+ - README.rdoc
42
+ - lib/storage.rb
43
+ - lib/storage/config.rb
44
+ - lib/storage/errors.rb
45
+ - lib/storage/strategies.rb
46
+ - lib/storage/strategies/file_system.rb
47
+ - lib/storage/strategies/s3.rb
48
+ - lib/storage/version.rb
49
+ - spec/resources/file.txt
50
+ - spec/spec_helper.rb
51
+ - spec/storage/config_spec.rb
52
+ - spec/storage/strategies/file_system_spec.rb
53
+ - spec/storage/strategies/s3_spec.rb
54
+ - spec/storage_spec.rb
55
+ has_rdoc: true
56
+ homepage: http://github.com/fnando/storage
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --charset=UTF-8
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.6
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: "This gem provides a simple API for multiple storage backends. Supported storages: Amazon S3 and FileSystem."
85
+ test_files:
86
+ - spec/spec_helper.rb
87
+ - spec/storage/config_spec.rb
88
+ - spec/storage/strategies/file_system_spec.rb
89
+ - spec/storage/strategies/s3_spec.rb
90
+ - spec/storage_spec.rb