jsl-hashpipe 0.0.2

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/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require 'rake'
5
+ require 'spec/rake/spectask'
6
+ require 'rake/rdoctask'
7
+
8
+ desc 'Test the plugin.'
9
+ Spec::Rake::SpecTask.new(:spec) do |t|
10
+ t.spec_opts = ["--format", "specdoc", "--colour"]
11
+ t.libs << 'lib'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc "Run all the tests"
16
+ task :default => :spec
17
+
18
+ desc 'Generate documentation for the hashpipe plugin.'
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'HashPipe'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
@@ -0,0 +1,23 @@
1
+ test:
2
+ storage: filesystem
3
+ s3:
4
+ bucket: test_archived_attributes
5
+ access_key: your access key
6
+ secret_key: your secret key
7
+ protocol: https
8
+
9
+ development:
10
+ storage: s3
11
+ s3:
12
+ bucket: development_archived_attributes
13
+ access_key: your access key
14
+ secret_key: your secret key
15
+
16
+ anotherenv:
17
+ storage: filesystem
18
+ filesystem:
19
+ archive_root: /tmp/archived_attributes
20
+
21
+ qa:
22
+ storage: memcache
23
+ namespace: hashit
data/hashpipe.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "hashpipe"
3
+ s.version = "0.0.2"
4
+ s.date = "2009-04-12"
5
+ s.summary = "Rails plugin to save content to a pluggable, hash-style backend"
6
+ s.email = "justin@phq.org"
7
+ s.homepage = "http://github.com/jsl/hashpipe"
8
+ s.description = "HashPipe allows for data to be saved to a backend other than the rdbms"
9
+ s.has_rdoc = true
10
+ s.authors = ["Justin Leitgeb"]
11
+ s.files = [
12
+ "Rakefile",
13
+ "hashpipe.gemspec",
14
+ "init.rb",
15
+ "config/hashpipe.yml",
16
+ "lib/hashpipe.rb",
17
+ "lib/hashpipe/archived_attribute.rb",
18
+ "lib/hashpipe/global_configuration.rb",
19
+ "lib/hashpipe/backends/s3.rb",
20
+ "lib/hashpipe/backends/filesystem.rb"
21
+ ]
22
+ s.test_files = [
23
+ "spec/hashpipe/global_configuration_spec.rb",
24
+ "spec/hashpipe/archived_attribute_spec.rb",
25
+ "spec/hashpipe/backends/filesystem_spec.rb",
26
+ "spec/hashpipe/backends/s3_spec.rb"
27
+ ]
28
+
29
+ s.add_dependency("right_aws", ["> 0.0.0"])
30
+ s.add_dependency("assaf-uuid", ["> 0.0.0"])
31
+ end
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[lib hashpipe]))
2
+
3
+ ActiveRecord::Base.send(:include, HashPipe)
4
+
@@ -0,0 +1,71 @@
1
+ require 'activesupport'
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[ backends filesystem ]))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[ backends s3 ]))
5
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[ backends memcache ]))
6
+
7
+ module HashPipe
8
+
9
+ class ArchivedAttribute
10
+ attr_reader :name, :instance
11
+
12
+ def initialize(name, instance, opts = {})
13
+ @name = name
14
+ @instance = instance
15
+ @dirty = false
16
+
17
+ @_options = HashPipe::GlobalConfiguration.instance.to_hash.
18
+ merge(opts)
19
+
20
+ @backend = instantiate_backend_from(options)
21
+ end
22
+
23
+ def value
24
+ val = defined?(@stashed_value) ? @stashed_value : @backend.load
25
+ val = compress? ? Zlib::Inflate.inflate(val) : val
26
+ val = marshal? ? Marshal.load(val) : val
27
+ end
28
+
29
+ def value=(other)
30
+ other = marshal? ? Marshal.dump(other) : other
31
+ other = compress? && !other.nil? ? Zlib::Deflate.deflate(other) : other
32
+ @stashed_value = other
33
+ @dirty = true
34
+ end
35
+
36
+ def dirty?
37
+ @dirty
38
+ end
39
+
40
+ # First saves this record to the back-end. If backend storage raises an
41
+ # error, we capture it and add it to the AR validation errors.
42
+ def save
43
+ @backend.save(@stashed_value) if self.dirty?
44
+ end
45
+
46
+ def destroy
47
+ @backend.destroy
48
+ end
49
+
50
+ # Returns a backend object based on the options given (e.g., filesystem,
51
+ # s3).
52
+ def instantiate_backend_from(options)
53
+ "HashPipe::Backends::#{options[:storage].to_s.camelize}".
54
+ constantize.new(self)
55
+ end
56
+
57
+ def options
58
+ @_options
59
+ end
60
+
61
+ private
62
+
63
+ [:marshal, :compress].each do |sym|
64
+ define_method("#{sym}?") do # def marshal?
65
+ options[sym].nil? ? false : options[sym] # options[:marshal].nil? ? false : options[:marshal]
66
+ end # end
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,60 @@
1
+ module HashPipe
2
+ module Backends
3
+
4
+ # The filesystem backend is mostly useful for development or test work. It's not currently sharded
5
+ # in any way, so would probably have serious issues for production use. You have been warned.
6
+ class Filesystem
7
+
8
+ def initialize(archived_attribute)
9
+ @archived_attribute = archived_attribute
10
+ end
11
+
12
+ def save(content)
13
+ create_filesystem_path unless File.exist?(filepath)
14
+ write_to_disk(content)
15
+ end
16
+
17
+ def destroy
18
+ FileUtils.rm_f(filename)
19
+ end
20
+
21
+ def load
22
+ File.read( filename ) if File.exist?( filename )
23
+ end
24
+
25
+ # Returns the full file path + name of this archived attribute
26
+ def filename
27
+ File.join(filepath, @archived_attribute.name.to_s)
28
+ end
29
+
30
+ private
31
+
32
+ # Writes content to disk, or raises an error if the content is unable to
33
+ # be saved
34
+ def write_to_disk(content)
35
+ File.open(filename, 'w') { |f| f.write(content) }
36
+ end
37
+
38
+ def create_filesystem_path
39
+ FileUtils.mkdir_p(filepath)
40
+ end
41
+
42
+ # The file path used for archiving this attribute. Includes either the
43
+ # options[:filesystem][:archive_root] attribute, if available, or the
44
+ # RAILS_ROOT/tmp/archived_attribute_archive/attribute_name.
45
+ def filepath
46
+ config_path = @archived_attribute.options[:filesystem][:archive_root]
47
+
48
+ base_path = config_path || File.join(
49
+ %W[#{RAILS_ROOT} tmp hashpipe_stash]
50
+ )
51
+
52
+ File.expand_path( File.join(base_path,
53
+ @archived_attribute.name.to_s,
54
+ @archived_attribute.instance.uuid ))
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,58 @@
1
+ require 'right_aws'
2
+
3
+ module HashPipe
4
+ module Backends
5
+
6
+ class S3
7
+
8
+ def initialize(archived_attribute)
9
+ @archived_attribute = archived_attribute
10
+ @config = HashPipe::GlobalConfiguration.instance[:s3]
11
+ end
12
+
13
+ def save(content)
14
+ bucket.put( key_name, StringIO.new( content ) ) unless content.nil?
15
+ end
16
+
17
+ def destroy
18
+ bucket.key(key_name).delete
19
+ end
20
+
21
+ def load
22
+ bucket.get(key_name)
23
+ end
24
+
25
+ private
26
+
27
+ def bucket
28
+ @bucket ||= right_aws_s3.bucket(bucket_name)
29
+ end
30
+
31
+ def right_aws_s3
32
+ @s3 ||= RightAws::S3.new(
33
+ @config[:access_key],
34
+ @config[:secret_key]
35
+ )
36
+ end
37
+
38
+ # Returns the bucket name to be used based on attributes of the archived
39
+ # attribute.
40
+ def bucket_name
41
+ @config[:bucket]
42
+ end
43
+
44
+ def key_name
45
+ [ @archived_attribute.instance.class.table_name,
46
+ @archived_attribute.name,
47
+ @archived_attribute.instance.uuid ].join('/')
48
+ end
49
+
50
+ # Returns a string representing whether we use http or https for this
51
+ # connection.
52
+ def protocol
53
+ @config[:protocol] || 'https'
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ module HashPipe
2
+
3
+ # Singleton class for reading the defaults archived attribute configuration
4
+ # for this environment.
5
+ class GlobalConfiguration
6
+ include Singleton
7
+
8
+ DEFAULTS = HashWithIndifferentAccess.new({
9
+ :storage => 'filesystem',
10
+ :marshal => false,
11
+ :compress => false,
12
+ :s3 => {
13
+ :protocol => 'https'
14
+ },
15
+ :filesystem => {
16
+ :archive_root => nil
17
+ },
18
+ :memcache => {
19
+ :port => 1978
20
+ }
21
+ })
22
+
23
+ def [](val)
24
+ config[val]
25
+ end
26
+
27
+ def to_hash
28
+ config
29
+ end
30
+
31
+ def to_s
32
+ config.inspect
33
+ end
34
+
35
+ private
36
+
37
+ def config
38
+ @config ||= HashWithIndifferentAccess.new(
39
+ DEFAULTS.merge(load_yaml_configuration)
40
+ )
41
+
42
+ @config.dup
43
+ end
44
+
45
+ def load_yaml_configuration
46
+ YAML.load_file(
47
+ File.join( RAILS_ROOT, 'config', 'hashpipe.yml' )
48
+ )[RAILS_ENV]
49
+ end
50
+ end
51
+
52
+ end
data/lib/hashpipe.rb ADDED
@@ -0,0 +1,80 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[hashpipe archived_attribute] ))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[hashpipe global_configuration] ))
3
+
4
+ require 'uuid'
5
+
6
+ module HashPipe
7
+
8
+ def self.included(base)
9
+ base.extend(SingletonMethods)
10
+ end
11
+
12
+ module SingletonMethods
13
+
14
+ def hattr(*args)
15
+ attribute = args.first
16
+
17
+ options = args.extract_options!
18
+ options.reverse_merge! :marshalled => false
19
+
20
+ if archived_attribute_definitions.nil?
21
+ write_inheritable_attribute(:archived_attribute_definitions, {})
22
+
23
+ before_save :generate_uuid
24
+ before_save :save_archived_attributes
25
+ before_destroy :destroy_archived_attributes
26
+ end
27
+
28
+ archived_attribute_definitions[attribute] = options
29
+
30
+ self.__send__(:include, InstanceMethods)
31
+
32
+ define_method attribute do
33
+ archive_stash_for(attribute).value
34
+ end
35
+
36
+ define_method "#{attribute}=" do |value|
37
+ archive_stash_for(attribute).value = value
38
+ end
39
+ end
40
+
41
+ # Returns the attachment definitions defined by each call to
42
+ # has_attached_file.
43
+ def archived_attribute_definitions
44
+ read_inheritable_attribute(:archived_attribute_definitions)
45
+ end
46
+
47
+ end
48
+
49
+ module InstanceMethods
50
+ def archive_stash_for(model)
51
+ @_archived_attribute_stashes ||= {}
52
+ @_archived_attribute_stashes[model] ||= ArchivedAttribute.new(
53
+ model, self, self.class.archived_attribute_definitions[model]
54
+ )
55
+ end
56
+
57
+ def each_archived_stash
58
+ self.class.archived_attribute_definitions.each do |name, definition|
59
+ yield(name, archive_stash_for(name))
60
+ end
61
+ end
62
+
63
+ def save_archived_attributes
64
+ each_archived_stash do |name, stash|
65
+ stash.__send__(:save)
66
+ end
67
+ end
68
+
69
+ def destroy_archived_attributes
70
+ each_archived_stash do |name, stash|
71
+ stash.__send__(:destroy)
72
+ end
73
+ end
74
+
75
+ def generate_uuid
76
+ self.uuid = UUID.new.generate if self.new_record?
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,120 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. spec_helper ])
2
+
3
+ describe HashPipe::ArchivedAttribute do
4
+ before do
5
+ options = { :storage => 'filesystem' }
6
+ stub_model = stub(:uuid => '43')
7
+ @aa = HashPipe::ArchivedAttribute.new(:glorp, stub_model, options)
8
+ end
9
+
10
+ describe "#dirty?" do
11
+ it "should return false when a value has not been set" do
12
+ @aa.should_not be_dirty
13
+ end
14
+
15
+ it "should return when the value has been set" do
16
+ @aa.value = 'stuff'
17
+ @aa.should be_dirty
18
+ end
19
+ end
20
+
21
+ describe "#marshal?" do
22
+ it "should return false if configuration option is nil" do
23
+ @aa.expects(:options).returns({:marshal => nil})
24
+ @aa.__send__(:marshal?).should be_false
25
+ end
26
+
27
+ it "should return value from configuration if configuration option is not nil" do
28
+ @aa.expects(:options).returns({:marshal => false}).times(2)
29
+ @aa.__send__(:marshal?).should be_false
30
+ end
31
+ end
32
+
33
+ describe "#compress?" do
34
+ it "should return false if configuration option is nil" do
35
+ @aa.expects(:options).returns({:compress => nil})
36
+ @aa.__send__(:compress?).should be_false
37
+ end
38
+
39
+ it "should return value from configuration if configuration option is not nil" do
40
+ @aa.expects(:options).returns({:compress => false}).times(2)
41
+ @aa.__send__(:compress?).should be_false
42
+ end
43
+ end
44
+
45
+ describe "#destroy" do
46
+ it "should call destroy on the backend object" do
47
+ backend = mock('backend', :destroy => true)
48
+ @aa.instance_variable_set(:'@backend', backend)
49
+ @aa.destroy
50
+ end
51
+ end
52
+
53
+ describe "when marshal is on" do
54
+ before do
55
+ @content = 'mary had a little lamb'
56
+ stub_backend = stub('backend', :load => Marshal.dump(@content))
57
+ @aa.stubs(:options).
58
+ returns({ :storage => 'filesystem', :marshal => true })
59
+ @aa.__send__(:instance_variable_set, :'@backend', stub_backend)
60
+ end
61
+
62
+ it "should call Marshal.load to restore value" do
63
+ Marshal.expects(:load)
64
+ @aa.value
65
+ end
66
+
67
+ it "should call Marshal.dump to save value" do
68
+ Marshal.expects(:dump)
69
+ @aa.value = 'foo'
70
+ end
71
+
72
+ it "should retrieve same content string stored in Marshalled form" do
73
+ @aa.value.should == @content
74
+ end
75
+ end
76
+
77
+ describe "when gzip is on" do
78
+ before do
79
+ @content = "The band formed in 1988 in Oxford"
80
+ stub_backend = stub('backend', :load => Zlib::Deflate.deflate(@content))
81
+ @aa.stubs(:options).
82
+ returns({ :storage => 'filesystem', :compress => true })
83
+ @aa.__send__(:instance_variable_set, :'@backend', stub_backend)
84
+ end
85
+
86
+ it "should call Zlib::Inflate.inflate to restore value" do
87
+ Zlib::Inflate.expects(:inflate)
88
+ @aa.value
89
+ end
90
+
91
+ it "should call Zlib::Deflate.deflate to save value" do
92
+ Zlib::Deflate.expects(:deflate)
93
+ @aa.value = 'foo'
94
+ end
95
+
96
+ it "should retrieve same content string stored in gzip form" do
97
+ @aa.value.should == @content
98
+ end
99
+
100
+ it "should not raise an error with nil content" do
101
+ lambda {
102
+ @aa.value = nil
103
+ }.should_not raise_error
104
+ end
105
+ end
106
+
107
+ describe "when compress and marshal are on" do
108
+ before do
109
+ @content = "The band formed in 1988 in Oxford"
110
+ stub_backend = stub('backend', :load => Zlib::Deflate.deflate(Marshal.dump(@content)))
111
+ @aa.stubs(:options).
112
+ returns({ :storage => 'filesystem', :compress => true, :marshal => true })
113
+ @aa.__send__(:instance_variable_set, :'@backend', stub_backend)
114
+ end
115
+
116
+ it "should retrieve the same content given" do
117
+ @aa.value.should == @content
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,82 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. .. spec_helper ])
2
+
3
+ require 'tmpdir'
4
+
5
+ describe HashPipe::Backends::Filesystem do
6
+
7
+ before do
8
+ @unique_path_part = 'archived_attributes_test'
9
+ @path = File.join(Dir.tmpdir, @unique_path_part)
10
+
11
+
12
+ @instance = stub('http_retrieval',
13
+ :uuid => '63d3a120-caca-012b-d468-002332d4f91e',
14
+ :name => :foo
15
+ )
16
+
17
+ aa = HashPipe::ArchivedAttribute.new(:content, @instance,
18
+ :filesystem => { :archive_root => @path } )
19
+
20
+ @fs = HashPipe::Backends::Filesystem.new(aa)
21
+ end
22
+
23
+ it "should write to the correct path" do
24
+ @fs.__send__(:filepath).should ==
25
+ File.expand_path(File.join(@path, 'content', @instance.uuid))
26
+ end
27
+
28
+ describe "#save" do
29
+ before do
30
+ if File.exist?(@path)
31
+ err = <<-EOS
32
+ Test directory #{@path} already exists. Please remove it and run the
33
+ test suite again.
34
+ EOS
35
+ raise RuntimeError, err
36
+ else
37
+ @remove_path = true
38
+ FileUtils.mkdir(@path)
39
+ end
40
+ end
41
+
42
+ it "should call methods to create path and save file to disk" do
43
+ @fs.save('test')
44
+ File.exist?(@fs.filename).should be_true
45
+ end
46
+
47
+ after(:all) do
48
+ FileUtils.rm_rf(@path) if @remove_path
49
+ end
50
+
51
+ end
52
+
53
+ describe "#destroy" do
54
+ before do
55
+ if File.exist?(@path)
56
+ err = <<-EOS
57
+ Test directory #{@path} already exists. Please remove it and run the
58
+ test suite again.
59
+ EOS
60
+ raise RuntimeError, err
61
+ else
62
+ @remove_path = true
63
+ FileUtils.mkdir(@path)
64
+ end
65
+ end
66
+
67
+ it "should call methods to remove path and file" do
68
+ @fs.save('test')
69
+ File.exist?(@fs.filename).should be_true
70
+
71
+ @fs.destroy
72
+ File.exist?(@fs.filename).should be_false
73
+ end
74
+
75
+ after(:all) do
76
+ FileUtils.rm_rf(@path) if @remove_path
77
+ end
78
+
79
+ end
80
+
81
+
82
+ end
@@ -0,0 +1,79 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. .. spec_helper ])
2
+
3
+ describe HashPipe::Backends::S3 do
4
+
5
+ before do
6
+ @instance = stub('ar_instance',
7
+ :uuid => '63d3a120-caca-012b-d468-002332d4f91e',
8
+ :table_name => 'glorps'
9
+ )
10
+
11
+ @aa = HashPipe::ArchivedAttribute.new(:stuff, @instance)
12
+ @s3 = HashPipe::Backends::S3.new(@aa)
13
+ @config = HashPipe::GlobalConfiguration.instance
14
+ end
15
+
16
+ describe "#initialize" do
17
+ it "should initialize an s3 object without error" do
18
+ lambda { HashPipe::Backends::S3.new(@aa) }.should_not raise_error
19
+ end
20
+ end
21
+
22
+ describe "#protocol" do
23
+ it "should be https by default" do
24
+ @s3.__send__(:protocol).should == "https"
25
+ end
26
+
27
+ it "should use http if specified in the configuration object" do
28
+ config = { :protocol => 'http' }
29
+ @s3.instance_variable_set(:"@config", config)
30
+
31
+ @s3.__send__(:protocol).should == "http"
32
+ end
33
+
34
+ end
35
+
36
+ describe "#load" do
37
+ it "should call method to load data from s3" do
38
+ bucket = mock('bucket')
39
+ bucket.expects(:get).once
40
+ aws_s3 = mock('aws_s3')
41
+ aws_s3.expects(:bucket).returns(bucket)
42
+ @s3.expects(:key_name).returns('some-key')
43
+ @s3.expects(:right_aws_s3).returns(aws_s3)
44
+ @s3.load
45
+ end
46
+ end
47
+
48
+ describe "#save" do
49
+ it "should call method to store data in s3" do
50
+ bucket = mock('bucket')
51
+ content = 'hey'
52
+ key = 'fookey'
53
+ @s3.expects(:key_name).returns(key)
54
+ bucket.expects(:put).once
55
+ aws_s3 = mock('aws_s3')
56
+ aws_s3.expects(:bucket).returns(bucket)
57
+ @s3.expects(:right_aws_s3).returns(aws_s3)
58
+ @s3.save(content)
59
+ end
60
+ end
61
+
62
+ describe "#destroy" do
63
+ it "should call method to delete key" do
64
+ key = mock('key', :delete => true)
65
+ bucket = mock('bucket', :key => key)
66
+ aws_s3 = mock('aws_s3')
67
+ aws_s3.expects(:bucket).returns(bucket)
68
+ @s3.expects(:right_aws_s3).returns(aws_s3)
69
+ @s3.expects(:key_name).returns('foo-key')
70
+ @s3.destroy
71
+ end
72
+ end
73
+
74
+ describe "#bucket_name" do
75
+ it "should be based on the attribute class and model" do
76
+ @s3.__send__(:bucket_name).should == @config[:s3]['bucket']
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,72 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. spec_helper ])
2
+
3
+ describe HashPipe::GlobalConfiguration do
4
+ before do
5
+ @conf = HashPipe::GlobalConfiguration.instance
6
+ end
7
+
8
+ describe "defaults" do
9
+ it "should set default marshal value to false" do
10
+ HashPipe::GlobalConfiguration::DEFAULTS[:marshal].should == false
11
+ end
12
+
13
+ it "should set gzip value to false" do
14
+ HashPipe::GlobalConfiguration::DEFAULTS[:marshal].should == false
15
+ end
16
+ end
17
+
18
+ it "should read default access key from the configuration file" do
19
+ @conf[:s3]['access_key'].should == 'your access key'
20
+ end
21
+
22
+ it "should the default secret key from configuration" do
23
+ @conf[:s3]['secret_key'].should == 'your secret key'
24
+ end
25
+
26
+ it "should read the default bucket from the configuration file" do
27
+ @conf[:s3]['bucket'].should == 'test_archived_attributes'
28
+ end
29
+
30
+ describe "#to_s" do
31
+ it "should return a string" do
32
+ @conf.to_s.should be_an_instance_of(String)
33
+ end
34
+ end
35
+
36
+ describe "hash cloning" do
37
+ it "should be able to alter a Hash without affecting the original object" do
38
+ conf = @conf.to_hash
39
+ previous = @conf[:storage]
40
+ conf[:storage] = 'foo'
41
+ @conf[:storage].should == previous
42
+ end
43
+
44
+ it "should not affect deeply nested attributes when values are changed" do
45
+ conf = @conf.to_hash
46
+ previous = @conf[:s3][:protocol]
47
+ conf[:s3][:protocol] = 'puddle'
48
+ @conf[:s3][:protocol].should == previous
49
+ end
50
+ end
51
+
52
+ describe "#to_hash" do
53
+ it "should return an instance of Hash" do
54
+ @conf.to_hash.should be_an_instance_of(HashWithIndifferentAccess)
55
+ end
56
+ end
57
+
58
+ describe "when an option is not specified in the yaml config file" do
59
+ it "should have a section for s3 options in hash" do
60
+ @conf[:s3].should_not be_nil
61
+ end
62
+
63
+ it "should default to https for protocol" do
64
+ @conf[:s3][:protocol].should == 'https'
65
+ end
66
+
67
+ it "should set default storage attribute to :filesystem" do
68
+ @conf[:storage].should == 'filesystem'
69
+ end
70
+ end
71
+
72
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jsl-hashpipe
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Justin Leitgeb
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-12 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: right_aws
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: assaf-uuid
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.0
34
+ version:
35
+ description: HashPipe allows for data to be saved to a backend other than the rdbms
36
+ email: justin@phq.org
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - Rakefile
45
+ - hashpipe.gemspec
46
+ - init.rb
47
+ - config/hashpipe.yml
48
+ - lib/hashpipe.rb
49
+ - lib/hashpipe/archived_attribute.rb
50
+ - lib/hashpipe/global_configuration.rb
51
+ - lib/hashpipe/backends/s3.rb
52
+ - lib/hashpipe/backends/filesystem.rb
53
+ has_rdoc: true
54
+ homepage: http://github.com/jsl/hashpipe
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.2.0
76
+ signing_key:
77
+ specification_version: 2
78
+ summary: Rails plugin to save content to a pluggable, hash-style backend
79
+ test_files:
80
+ - spec/hashpipe/global_configuration_spec.rb
81
+ - spec/hashpipe/archived_attribute_spec.rb
82
+ - spec/hashpipe/backends/filesystem_spec.rb
83
+ - spec/hashpipe/backends/s3_spec.rb