dragonfly-fog_data_store 0.0.1

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.
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