dragonfly-fog_data_store 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a23ec2299440686776fa108cbc70825fa8c68e8a
4
+ data.tar.gz: 89bc14ed227cf097b8164b4ceebf856ec4f9fcae
5
+ SHA512:
6
+ metadata.gz: 36dabbedf624890cf291d9645c1a2f3fb2123284a23eda4b7df0174a03a1ce9008d8604b991674db8866864936926f6c8854224180dc154094295a6573125ea8
7
+ data.tar.gz: cd68c161eb8db2427c039c5bed98bec3479d4cbcbc5b59d1b231f263e2e678514077e77a33d4243356aa2da091a0199928f7587f3cc2bf994fe63fce6503890f
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .fog_spec.yml
19
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jimmy Hsu
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Dragonfly::FogDataStore
2
+
3
+ This is shamelessly stolen from https://github.com/markevans/dragonfly-s3_data_store.
4
+
5
+ There are failures in the specs arround mocking access to rackspace cloud
6
+ storage.
7
+ As such it's not meant for general consumption yet.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ require 'bundler'
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dragonfly/fog_data_store/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dragonfly-fog_data_store"
8
+ spec.version = Dragonfly::FogDataStore::VERSION
9
+ spec.authors = ["jimmy"]
10
+ spec.email = ["irregular.profit@gmail.com"]
11
+ spec.description = %q{fog data store for Dragonfly}
12
+ spec.summary = %q{Data store for storing Dragonfly content (e.g. images) on fog}
13
+ spec.homepage = "https://github.com/irregularprofit/dragonfly-fog_data_store"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "dragonfly", "~> 1.0"
22
+ spec.add_runtime_dependency "fog", '~> 0'
23
+ spec.add_development_dependency "rspec", "~> 2.0"
24
+ end
@@ -0,0 +1,5 @@
1
+ module Dragonfly
2
+ class FogDataStore
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,115 @@
1
+ require 'fog'
2
+ require 'dragonfly'
3
+
4
+ Dragonfly::App.register_datastore(:fog){ Dragonfly::FogDataStore }
5
+
6
+ module Dragonfly
7
+ class FogDataStore
8
+
9
+ # Exceptions
10
+ class NotConfigured < RuntimeError; end
11
+
12
+ def initialize(opts={})
13
+ @bucket_name = opts[:bucket_name]
14
+ @rackspace_region = opts[:rackspace_region]
15
+ @rackspace_api_key = opts[:rackspace_api_key]
16
+ @rackspace_username = opts[:rackspace_username]
17
+
18
+ @url_scheme = opts[:url_scheme] || 'http'
19
+ @url_host = opts[:url_host]
20
+ end
21
+
22
+ attr_accessor :bucket_name, :rackspace_api_key, :rackspace_username, :url_scheme, :url_host, :rackspace_region
23
+
24
+ def write(content, opts={})
25
+ ensure_configured
26
+ ensure_bucket_initialized
27
+
28
+ headers = {'Content-Type' => content.mime_type}
29
+ headers.merge!(opts[:headers]) if opts[:headers]
30
+ uid = opts[:path] || generate_uid(content.name || 'file')
31
+
32
+ rescuing_socket_errors do
33
+ content.file do |f|
34
+ storage.put_object(bucket_name, uid, f, headers)
35
+ end
36
+ end
37
+
38
+ uid
39
+ end
40
+
41
+ def read(uid)
42
+ ensure_configured
43
+ response = rescuing_socket_errors{ storage.get_object(bucket_name, uid) }
44
+ [response.body, response.headers]
45
+ rescue Excon::Errors::NotFound => e
46
+ nil
47
+ end
48
+
49
+ def destroy(uid)
50
+ rescuing_socket_errors{ storage.delete_object(bucket_name, uid) }
51
+ rescue Excon::Errors::NotFound, Excon::Errors::Conflict => e
52
+ Dragonfly.warn("#{self.class.name} destroy error: #{e}")
53
+ end
54
+
55
+ def url_for(uid, opts={})
56
+ if opts && opts[:expires]
57
+ storage.get_object_https_url(bucket_name, uid, opts[:expires])
58
+ else
59
+ scheme = opts[:scheme] || url_scheme
60
+ host = opts[:host] || url_host
61
+ "#{scheme}://#{host}/#{uid}"
62
+ end
63
+ end
64
+
65
+ def storage
66
+ @storage ||= begin
67
+ storage = Fog::Storage.new({
68
+ provider: 'Rackspace',
69
+ rackspace_region: rackspace_region,
70
+ rackspace_api_key: rackspace_api_key,
71
+ rackspace_username: rackspace_username
72
+ }.reject {|name, option| option.nil?})
73
+ storage
74
+ end
75
+ end
76
+
77
+ def bucket_exists?
78
+ #rescuing_socket_errors{ storage.get_bucket_location(bucket_name) }
79
+ true
80
+ rescue Excon::Errors::NotFound => e
81
+ false
82
+ end
83
+
84
+ private
85
+
86
+ def ensure_configured
87
+ unless @configured
88
+ [:bucket_name, :rackspace_api_key, :rackspace_username, :rackspace_region].each do |attr|
89
+ raise NotConfigured, "You need to configure #{self.class.name} with #{attr}" if send(attr).nil?
90
+ end
91
+ @configured = true
92
+ end
93
+ end
94
+
95
+ def ensure_bucket_initialized
96
+ unless @bucket_initialized
97
+ rescuing_socket_errors{ storage.put_bucket(bucket_name, 'LocationConstraint' => rackspace_region) } unless bucket_exists?
98
+ @bucket_initialized = true
99
+ end
100
+ end
101
+
102
+ def generate_uid(name)
103
+ "#{Time.now.strftime '%Y/%m/%d/%H/%M/%S'}/#{rand(1000)}/#{name.gsub(/[^\w.]+/, '_')}"
104
+ end
105
+
106
+ def rescuing_socket_errors(&block)
107
+ yield
108
+ rescue Excon::Errors::SocketError => e
109
+ storage.reload
110
+ yield
111
+ end
112
+
113
+ end
114
+ end
115
+
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+ require 'dragonfly/spec/data_store_examples'
3
+ require 'yaml'
4
+ require 'dragonfly/fog_data_store'
5
+
6
+ describe Dragonfly::FogDataStore do
7
+
8
+ # To run these tests, put a file ".fog_spec.yml" in the dragonfly root dir, like this:
9
+ # key: XXXXXXXXXX
10
+ # secret: XXXXXXXXXX
11
+ # enabled: true
12
+ if File.exist?(file = File.expand_path('../../.fog_spec.yml', __FILE__))
13
+ config = YAML.load_file(file)
14
+ KEY = config['key']
15
+ SECRET = config['secret']
16
+ enabled = config['enabled']
17
+ else
18
+ enabled = false
19
+ end
20
+
21
+ if enabled
22
+
23
+ # Make sure it's a new bucket name
24
+ BUCKET_NAME = "dragonfly-test-#{Time.now.to_i.to_s(36)}"
25
+ REGION = :ord
26
+
27
+ before(:each) do
28
+ @data_store = Dragonfly::FogDataStore.new(
29
+ bucket_name: BUCKET_NAME,
30
+ provider: 'Rackspace',
31
+ rackspace_api_key: KEY,
32
+ rackspace_username: SECRET,
33
+ rackspace_region: REGION
34
+ )
35
+ end
36
+
37
+ else
38
+
39
+ BUCKET_NAME = 'test-bucket'
40
+ REGION = :ord
41
+
42
+ before(:each) do
43
+ Fog.mock!
44
+ @data_store = Dragonfly::FogDataStore.new(
45
+ bucket_name: BUCKET_NAME,
46
+ provider: 'Rackspace',
47
+ rackspace_api_key: 'XXXXXXXXX',
48
+ rackspace_username: 'XXXXXXXXX',
49
+ rackspace_region: REGION
50
+ )
51
+ end
52
+
53
+ end
54
+
55
+ it_should_behave_like 'data_store'
56
+
57
+ let (:app) { Dragonfly.app }
58
+ let (:content) { Dragonfly::Content.new(app, "eggheads") }
59
+ let (:new_content) { Dragonfly::Content.new(app) }
60
+
61
+ describe "registering with a symbol" do
62
+ it "registers a symbol for configuring" do
63
+ app.configure do
64
+ datastore :fog
65
+ end
66
+ app.datastore.should be_a(Dragonfly::FogDataStore)
67
+ end
68
+ end
69
+
70
+ describe "write" do
71
+ it "should use the name from the content if set" do
72
+ content.name = 'doobie.doo'
73
+ uid = @data_store.write(content)
74
+ uid.should =~ /doobie\.doo$/
75
+ new_content.update(*@data_store.read(uid))
76
+ new_content.data.should == 'eggheads'
77
+ end
78
+
79
+ it "should work ok with files with funny names" do
80
+ content.name = "A Picture with many spaces in its name (at 20:00 pm).png"
81
+ uid = @data_store.write(content)
82
+ uid.should =~ /A_Picture_with_many_spaces_in_its_name_at_20_00_pm_\.png$/
83
+ new_content.update(*@data_store.read(uid))
84
+ new_content.data.should == 'eggheads'
85
+ end
86
+
87
+ it "should allow for setting the path manually" do
88
+ uid = @data_store.write(content, path: 'hello/there')
89
+ uid.should == 'hello/there'
90
+ new_content.update(*@data_store.read(uid))
91
+ new_content.data.should == 'eggheads'
92
+ end
93
+
94
+ if enabled # Fog.mock! doesn't act consistently here
95
+ it "should reset the connection and try again if Fog throws a socket EOFError" do
96
+ @data_store.storage.should_receive(:put_object).exactly(:once).and_raise(Excon::Errors::SocketError.new(EOFError.new))
97
+ @data_store.storage.should_receive(:put_object).with(BUCKET_NAME, anything, anything, hash_including)
98
+ @data_store.write(content)
99
+ end
100
+
101
+ it "should just let it raise if Fog throws a socket EOFError again" do
102
+ @data_store.storage.should_receive(:put_object).and_raise(Excon::Errors::SocketError.new(EOFError.new))
103
+ @data_store.storage.should_receive(:put_object).and_raise(Excon::Errors::SocketError.new(EOFError.new))
104
+ expect{
105
+ @data_store.write(content)
106
+ }.to raise_error(Excon::Errors::SocketError)
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "not configuring stuff properly" do
112
+ it "should require a bucket name on write" do
113
+ @data_store.bucket_name = nil
114
+ proc{ @data_store.write(content) }.should raise_error(Dragonfly::FogDataStore::NotConfigured)
115
+ end
116
+
117
+ it "should require an rackspace_api_key on write" do
118
+ @data_store.rackspace_api_key = nil
119
+ proc{ @data_store.write(content) }.should raise_error(Dragonfly::FogDataStore::NotConfigured)
120
+ end
121
+
122
+ it "should require a secret access key on write" do
123
+ @data_store.rackspace_username = nil
124
+ proc{ @data_store.write(content) }.should raise_error(Dragonfly::FogDataStore::NotConfigured)
125
+ end
126
+
127
+ it "should require a bucket name on read" do
128
+ @data_store.bucket_name = nil
129
+ proc{ @data_store.read('asdf') }.should raise_error(Dragonfly::FogDataStore::NotConfigured)
130
+ end
131
+
132
+ it "should require an rackspace_api_key on read" do
133
+ @data_store.rackspace_api_key = nil
134
+ proc{ @data_store.read('asdf') }.should raise_error(Dragonfly::FogDataStore::NotConfigured)
135
+ end
136
+
137
+ it "should require a secret access key on read" do
138
+ @data_store.rackspace_username = nil
139
+ proc{ @data_store.read('asdf') }.should raise_error(Dragonfly::FogDataStore::NotConfigured)
140
+ end
141
+
142
+ if !enabled #this will fail since the specs are not running on an ec2 instance with an iam role defined
143
+ it 'should allow missing secret key and access key on write if iam profiles are allowed' do
144
+ # This is slightly brittle but it's annoying waiting for fog doing stuff
145
+ @data_store.storage.stub(get_bucket_location: nil, put_object: nil)
146
+
147
+ @data_store.rackspace_username = nil
148
+ @data_store.rackspace_api_key = nil
149
+ expect{ @data_store.write(content) }.not_to raise_error
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ describe "autocreating the bucket" do
156
+ it "should create the bucket on write if it doesn't exist" do
157
+ @data_store.bucket_name = "dragonfly-test-blah-blah-#{rand(100000000)}"
158
+ @data_store.write(content)
159
+ end
160
+
161
+ it "should not try to create the bucket on read if it doesn't exist" do
162
+ @data_store.bucket_name = "dragonfly-test-blah-blah-#{rand(100000000)}"
163
+ @data_store.send(:storage).should_not_receive(:put_bucket)
164
+ @data_store.read("gungle").should be_nil
165
+ end
166
+ end
167
+
168
+ end
@@ -0,0 +1,7 @@
1
+ RSpec.configure do |config|
2
+ config.treat_symbols_as_metadata_keys_with_true_values = true
3
+ config.run_all_when_everything_filtered = true
4
+ config.filter_run :focus
5
+ config.order = 'random'
6
+ end
7
+
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dragonfly-fog_data_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - jimmy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dragonfly
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fog
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ description: fog data store for Dragonfly
56
+ email:
57
+ - irregular.profit@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - dragonfly-fog_data_store.gemspec
68
+ - lib/dragonfly/fog_data_store.rb
69
+ - lib/dragonfly/fog_data_store/version.rb
70
+ - spec/fog_data_store_spec.rb
71
+ - spec/spec_helper.rb
72
+ homepage: https://github.com/irregularprofit/dragonfly-fog_data_store
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.4.6
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Data store for storing Dragonfly content (e.g. images) on fog
96
+ test_files:
97
+ - spec/fog_data_store_spec.rb
98
+ - spec/spec_helper.rb