remote_files 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/remote_files/configuration.rb +123 -0
- data/lib/remote_files/file.rb +19 -20
- data/lib/remote_files/fog_store.rb +2 -2
- data/lib/remote_files/memory_store.rb +4 -2
- data/lib/remote_files/version.rb +1 -1
- data/lib/remote_files.rb +18 -82
- data/test/configuration_test.rb +178 -0
- data/test/remote_files_test.rb +0 -173
- data/test/resque_job_test.rb +2 -1
- data/test/test_helper.rb +3 -2
- metadata +11 -8
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'remote_files/fog_store'
|
2
|
+
|
3
|
+
module RemoteFiles
|
4
|
+
class Configuration
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(name, config = {})
|
8
|
+
@name = name
|
9
|
+
@stores = []
|
10
|
+
@stores_map = {}
|
11
|
+
from_hash(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear
|
15
|
+
@stores.clear
|
16
|
+
@stores_map.clear
|
17
|
+
end
|
18
|
+
|
19
|
+
def from_hash(hash)
|
20
|
+
hash.each do |store_identifier, config|
|
21
|
+
#symbolize_keys!
|
22
|
+
cfg = {}
|
23
|
+
config.each { |name, value| cfg[name.to_sym] = config[name] }
|
24
|
+
config = cfg
|
25
|
+
|
26
|
+
#camelize
|
27
|
+
type = config[:type].gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } + 'Store'
|
28
|
+
|
29
|
+
klass = RemoteFiles.const_get(type) rescue nil
|
30
|
+
unless klass
|
31
|
+
require "remote_files/#{config[:type]}_store"
|
32
|
+
klass = RemoteFiles.const_get(type)
|
33
|
+
end
|
34
|
+
|
35
|
+
config.delete(:type)
|
36
|
+
|
37
|
+
add_store(store_identifier.to_sym, :class => klass, :primary => !!config.delete(:primary)) do |store|
|
38
|
+
config.each do |name, value|
|
39
|
+
store[name] = value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_store(store_identifier, options = {}, &block)
|
48
|
+
store = (options[:class] || FogStore).new(store_identifier)
|
49
|
+
block.call(store) if block_given?
|
50
|
+
|
51
|
+
if options[:primary]
|
52
|
+
@stores.unshift(store)
|
53
|
+
else
|
54
|
+
@stores << store
|
55
|
+
end
|
56
|
+
|
57
|
+
@stores_map[store_identifier] = store
|
58
|
+
end
|
59
|
+
|
60
|
+
def stores
|
61
|
+
raise "You need to configure add stores to the #{name} RemoteFiles configuration" if @stores.empty?
|
62
|
+
@stores
|
63
|
+
end
|
64
|
+
|
65
|
+
def lookup_store(store_identifier)
|
66
|
+
@stores_map[store_identifier]
|
67
|
+
end
|
68
|
+
|
69
|
+
def primary_store
|
70
|
+
stores.first
|
71
|
+
end
|
72
|
+
|
73
|
+
def store_once!(file)
|
74
|
+
return file.stored_in.first if file.stored?
|
75
|
+
|
76
|
+
exception = nil
|
77
|
+
|
78
|
+
stores.each do |store|
|
79
|
+
begin
|
80
|
+
stored = store.store!(file)
|
81
|
+
file.stored_in << store.identifier
|
82
|
+
break
|
83
|
+
rescue ::RemoteFiles::Error => e
|
84
|
+
exception = e
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
raise exception unless file.stored?
|
89
|
+
|
90
|
+
file.stored_in.first
|
91
|
+
end
|
92
|
+
|
93
|
+
def store!(file)
|
94
|
+
store_once!(file) unless file.stored?
|
95
|
+
|
96
|
+
RemoteFiles.synchronize_stores(file) unless file.stored_everywhere?
|
97
|
+
|
98
|
+
true
|
99
|
+
end
|
100
|
+
|
101
|
+
def delete!(file)
|
102
|
+
file.stored_in.each do |store_identifier|
|
103
|
+
store = lookup_store(store_identifier)
|
104
|
+
store.delete!(file.identifier)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def synchronize!(file)
|
109
|
+
file.missing_stores.each do |store_identifier|
|
110
|
+
store = lookup_store(store_identifier)
|
111
|
+
store.store!(file)
|
112
|
+
file.stored_in << store.identifier
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def file_from_url(url)
|
117
|
+
stores.each do |store|
|
118
|
+
file = store.file_from_url(url)
|
119
|
+
return file if file
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/remote_files/file.rb
CHANGED
@@ -1,27 +1,26 @@
|
|
1
1
|
module RemoteFiles
|
2
2
|
class File
|
3
|
-
attr_reader :content, :content_type, :identifier, :stored_in
|
3
|
+
attr_reader :content, :content_type, :identifier, :stored_in, :configuration
|
4
4
|
|
5
5
|
def initialize(identifier, options = {})
|
6
|
-
@identifier
|
7
|
-
@stored_in
|
8
|
-
@content
|
9
|
-
@content_type
|
10
|
-
@
|
6
|
+
@identifier = identifier
|
7
|
+
@stored_in = options[:stored_in] || []
|
8
|
+
@content = options.delete(:content)
|
9
|
+
@content_type = options[:content_type]
|
10
|
+
@configuration = RemoteFiles::CONFIGURATIONS[options[:configuration] || :default]
|
11
|
+
@options = options
|
11
12
|
end
|
12
13
|
|
13
14
|
def self.from_url(url)
|
14
|
-
RemoteFiles.
|
15
|
-
file = store.file_from_url(url)
|
16
|
-
return file if file
|
17
|
-
end
|
15
|
+
RemoteFiles.default_configuration.file_from_url(url)
|
18
16
|
end
|
19
17
|
|
20
18
|
def options
|
21
19
|
@options.merge(
|
22
|
-
:identifier
|
23
|
-
:stored_in
|
24
|
-
:content_type
|
20
|
+
:identifier => identifier,
|
21
|
+
:stored_in => stored_in,
|
22
|
+
:content_type => content_type,
|
23
|
+
:configuration => configuration.name
|
25
24
|
)
|
26
25
|
end
|
27
26
|
|
@@ -34,17 +33,17 @@ module RemoteFiles
|
|
34
33
|
end
|
35
34
|
|
36
35
|
def missing_stores
|
37
|
-
|
36
|
+
configuration.stores.map(&:identifier) - @stored_in
|
38
37
|
end
|
39
38
|
|
40
39
|
def url(store_identifier = nil)
|
41
|
-
store = store_identifier ?
|
40
|
+
store = store_identifier ? configuration.lookup_store(store_identifier) : configuration.primary_store
|
42
41
|
return nil unless store
|
43
42
|
store.url(identifier)
|
44
43
|
end
|
45
44
|
|
46
45
|
def current_url
|
47
|
-
prioritized_stores =
|
46
|
+
prioritized_stores = configuration.stores.map(&:identifier) & @stored_in
|
48
47
|
|
49
48
|
return nil if prioritized_stores.empty?
|
50
49
|
|
@@ -52,19 +51,19 @@ module RemoteFiles
|
|
52
51
|
end
|
53
52
|
|
54
53
|
def store!
|
55
|
-
|
54
|
+
configuration.store!(self)
|
56
55
|
end
|
57
56
|
|
58
57
|
def store_once!
|
59
|
-
|
58
|
+
configuration.store_once!(self)
|
60
59
|
end
|
61
60
|
|
62
61
|
def synchronize!
|
63
|
-
|
62
|
+
configuration.synchronize!(self)
|
64
63
|
end
|
65
64
|
|
66
65
|
def delete!
|
67
|
-
|
66
|
+
configuration.delete!(self)
|
68
67
|
end
|
69
68
|
|
70
69
|
def delete
|
@@ -4,14 +4,14 @@ require 'fog'
|
|
4
4
|
module RemoteFiles
|
5
5
|
class FogStore < AbstractStore
|
6
6
|
def store!(file)
|
7
|
-
|
7
|
+
success = directory.files.create(
|
8
8
|
:body => file.content,
|
9
9
|
:content_type => file.content_type,
|
10
10
|
:key => file.identifier,
|
11
11
|
:public => options[:public]
|
12
12
|
)
|
13
13
|
|
14
|
-
raise RemoteFiles::Error unless
|
14
|
+
raise RemoteFiles::Error unless success
|
15
15
|
|
16
16
|
true
|
17
17
|
rescue Fog::Errors::Error, Excon::Errors::Error
|
@@ -12,8 +12,10 @@ module RemoteFiles
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.clear!
|
15
|
-
RemoteFiles.
|
16
|
-
|
15
|
+
RemoteFiles::CONFIGURATIONS.values.each do |config|
|
16
|
+
config.stores.each do |store|
|
17
|
+
store.clear! if store.is_a?(RemoteFiles::MemoryStore)
|
18
|
+
end
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
data/lib/remote_files/version.rb
CHANGED
data/lib/remote_files.rb
CHANGED
@@ -1,107 +1,43 @@
|
|
1
1
|
require 'remote_files/version'
|
2
|
-
require 'remote_files/
|
2
|
+
require 'remote_files/configuration'
|
3
3
|
require 'remote_files/file'
|
4
4
|
|
5
5
|
module RemoteFiles
|
6
6
|
class Error < StandardError; end
|
7
7
|
class NotFoundError < Error; end
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
CONFIGURATIONS = Hash.new do |configs, name|
|
10
|
+
name = name.to_sym
|
11
|
+
configs[name] = Configuration.new(name)
|
12
|
+
end
|
11
13
|
|
12
|
-
def self.
|
13
|
-
|
14
|
-
|
14
|
+
def self.default_configuration
|
15
|
+
CONFIGURATIONS[:default]
|
16
|
+
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def self.configure(name, hash = {})
|
19
|
+
if name.is_a?(Hash)
|
20
|
+
hash = name
|
21
|
+
name = :default
|
20
22
|
end
|
21
23
|
|
22
|
-
|
24
|
+
CONFIGURATIONS[name].from_hash(hash)
|
23
25
|
end
|
24
26
|
|
25
|
-
def self.
|
26
|
-
|
27
|
-
#symbolize_keys!
|
28
|
-
cfg = {}
|
29
|
-
config.each { |name, value| cfg[name.to_sym] = config[name] }
|
30
|
-
config = cfg
|
31
|
-
|
32
|
-
#camelize
|
33
|
-
type = config[:type].gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } + 'Store'
|
34
|
-
|
35
|
-
klass = RemoteFiles.const_get(type) rescue nil
|
36
|
-
unless klass
|
37
|
-
require "remote_files/#{config[:type]}_store"
|
38
|
-
klass = RemoteFiles.const_get(type)
|
39
|
-
end
|
40
|
-
|
41
|
-
config.delete(:type)
|
42
|
-
|
43
|
-
add_store(store_identifier.to_sym, :class => klass, :primary => !!config.delete(:primary)) do |store|
|
44
|
-
config.each do |name, value|
|
45
|
-
store[name] = value
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
27
|
+
def self.add_store(store_identifier, options = {}, &block)
|
28
|
+
default_configuration.add_store(store_identifier, options, &block)
|
49
29
|
end
|
50
30
|
|
51
31
|
def self.stores
|
52
|
-
|
53
|
-
STORES
|
32
|
+
default_configuration.stores
|
54
33
|
end
|
55
34
|
|
56
35
|
def self.lookup_store(store_identifier)
|
57
|
-
|
36
|
+
default_configuration.lookup_store(store_identifier)
|
58
37
|
end
|
59
38
|
|
60
39
|
def self.primary_store
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.store_once!(file)
|
65
|
-
return file.stored_in.first if file.stored?
|
66
|
-
|
67
|
-
exception = nil
|
68
|
-
|
69
|
-
stores.each do |store|
|
70
|
-
begin
|
71
|
-
stored = store.store!(file)
|
72
|
-
file.stored_in << store.identifier
|
73
|
-
break
|
74
|
-
rescue ::RemoteFiles::Error => e
|
75
|
-
exception = e
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
raise exception unless file.stored?
|
80
|
-
|
81
|
-
file.stored_in.first
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.store!(file)
|
85
|
-
store_once!(file) unless file.stored?
|
86
|
-
|
87
|
-
synchronize_stores(file) unless file.stored_everywhere?
|
88
|
-
|
89
|
-
true
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.delete!(file)
|
93
|
-
file.stored_in.each do |store_identifier|
|
94
|
-
store = lookup_store(store_identifier)
|
95
|
-
store.delete!(file.identifier)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def self.synchronize!(file)
|
100
|
-
file.missing_stores.each do |store_identifier|
|
101
|
-
store = lookup_store(store_identifier)
|
102
|
-
store.store!(file)
|
103
|
-
file.stored_in << store.identifier
|
104
|
-
end
|
40
|
+
default_configuration.primary_store
|
105
41
|
end
|
106
42
|
|
107
43
|
def self.synchronize_stores(file = nil, &block)
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'remote_files/mock_store'
|
3
|
+
|
4
|
+
describe RemoteFiles::Configuration do
|
5
|
+
before do
|
6
|
+
@configuration = RemoteFiles.configure(:test)
|
7
|
+
@file = RemoteFiles::File.new('file', :configuration => :test, :content => 'content', :content_type => 'text/plain')
|
8
|
+
@mock_store1 = @configuration.add_store(:mock1, :class => RemoteFiles::MockStore)
|
9
|
+
@mock_store2 = @configuration.add_store(:mock2, :class => RemoteFiles::MockStore)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '::add_store' do
|
13
|
+
describe 'when adding a non-primary store' do
|
14
|
+
before { @non_primary_store = @configuration.add_store(:primary) }
|
15
|
+
|
16
|
+
it 'should add it to the tail of the list of stores' do
|
17
|
+
@configuration.stores.must_equal([@mock_store1, @mock_store2, @non_primary_store])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'when adding a promary store' do
|
22
|
+
before { @primary_store = @configuration.add_store(:primary, :primary => true) }
|
23
|
+
|
24
|
+
it 'should add it to the head of the list of stores' do
|
25
|
+
@configuration.stores.must_equal([@primary_store, @mock_store1, @mock_store2])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '::primary_store' do
|
31
|
+
before do
|
32
|
+
@primary_store1 = @configuration.add_store(:primary1, :primary => true)
|
33
|
+
@primary_store2 = @configuration.add_store(:primary2, :primary => true)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should return the head of the list of stores' do
|
37
|
+
@configuration.primary_store.must_equal(@primary_store2)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '::lookup_store' do
|
42
|
+
before do
|
43
|
+
@primary_store = @configuration.add_store(:primary, :primary => true)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should find the store my identifier' do
|
47
|
+
@configuration.lookup_store(:mock1).must_equal(@mock_store1)
|
48
|
+
@configuration.lookup_store(:mock2).must_equal(@mock_store2)
|
49
|
+
@configuration.lookup_store(:primary).must_equal(@primary_store)
|
50
|
+
@configuration.lookup_store(:unknown).must_be_nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '::store_once!' do
|
55
|
+
|
56
|
+
describe 'when the first store succeeds' do
|
57
|
+
before { @configuration.store_once!(@file) }
|
58
|
+
|
59
|
+
it 'should only store the file in the first store' do
|
60
|
+
@mock_store1.data['file'].must_equal(:content => 'content', :content_type => 'text/plain')
|
61
|
+
@mock_store2.data['file'].must_be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'when the first store fails' do
|
66
|
+
before do
|
67
|
+
@mock_store1.expects(:store!).with(@file).raises(RemoteFiles::Error)
|
68
|
+
@configuration.store_once!(@file)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should only store the file in the second store' do
|
72
|
+
@mock_store1.data['file'].must_be_nil
|
73
|
+
@mock_store2.data['file'].must_equal(:content => 'content', :content_type => 'text/plain')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'when alls stores fail' do
|
78
|
+
before do
|
79
|
+
@mock_store1.expects(:store!).with(@file).raises(RemoteFiles::Error)
|
80
|
+
@mock_store2.expects(:store!).with(@file).raises(RemoteFiles::Error)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should raise a RemoteFiles::Error' do
|
84
|
+
proc { @configuration.store_once!(@file) }.must_raise(RemoteFiles::Error)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '::store!' do
|
90
|
+
describe 'when the file is already stored in some stores' do
|
91
|
+
before { @file.stored_in.replace([@mock_store1.identifier]) }
|
92
|
+
|
93
|
+
it 'should not store the file' do
|
94
|
+
@configuration.expects(:store_once!).never
|
95
|
+
@configuration.store!(@file)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should synchronize the stores' do
|
99
|
+
RemoteFiles.expects(:synchronize_stores).with(@file)
|
100
|
+
@configuration.store!(@file)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'when the file is stored in all stores' do
|
105
|
+
before { @file.stored_in.replace([@mock_store1.identifier, @mock_store2.identifier]) }
|
106
|
+
|
107
|
+
it 'should not store the file' do
|
108
|
+
@configuration.expects(:store_once!).never
|
109
|
+
@configuration.store!(@file)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should not synchronize the stores' do
|
113
|
+
RemoteFiles.expects(:synchronize_stores).never
|
114
|
+
@configuration.store!(@file)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'when the file is not stored anywhere' do
|
120
|
+
before { @file.stored_in.replace([]) }
|
121
|
+
|
122
|
+
it 'should store the file once' do
|
123
|
+
@file.configuration.expects(:store_once!).with(@file)
|
124
|
+
@configuration.store!(@file)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should synchronize the stores' do
|
128
|
+
RemoteFiles.expects(:synchronize_stores).with(@file)
|
129
|
+
@configuration.store!(@file)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '::delete!' do
|
135
|
+
it 'should delete the file from all the stores' do
|
136
|
+
@file.stored_in.replace([:mock1, :mock2])
|
137
|
+
@mock_store1.expects(:delete!).with(@file.identifier)
|
138
|
+
@mock_store2.expects(:delete!).with(@file.identifier)
|
139
|
+
@configuration.delete!(@file)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe '::synchronize!' do
|
144
|
+
describe 'when the file is not stored anywhere' do
|
145
|
+
before { @file.stored_in.replace([]) }
|
146
|
+
|
147
|
+
it 'should store the file on all stores' do
|
148
|
+
@mock_store1.expects(:store!).returns(true)
|
149
|
+
@mock_store2.expects(:store!).returns(true)
|
150
|
+
|
151
|
+
@configuration.synchronize!(@file)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe 'when the file is stored in some stores' do
|
156
|
+
before { @file.stored_in.replace([@mock_store1.identifier]) }
|
157
|
+
|
158
|
+
it 'should store the file in the remaining stores' do
|
159
|
+
@mock_store1.expects(:store!).never
|
160
|
+
@mock_store2.expects(:store!).with(@file).returns(true)
|
161
|
+
|
162
|
+
@configuration.synchronize!(@file)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'when the file is stored everywhere' do
|
167
|
+
before { @file.stored_in.replace([@mock_store1.identifier, @mock_store2.identifier]) }
|
168
|
+
|
169
|
+
it 'should not do anything' do
|
170
|
+
@mock_store1.expects(:store!).never
|
171
|
+
@mock_store2.expects(:store!).never
|
172
|
+
|
173
|
+
@configuration.synchronize!(@file)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
data/test/remote_files_test.rb
CHANGED
@@ -1,90 +1,6 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
|
-
require 'remote_files/mock_store'
|
3
2
|
|
4
3
|
describe RemoteFiles do
|
5
|
-
before do
|
6
|
-
@file = RemoteFiles::File.new('file', :content => 'content', :content_type => 'text/plain')
|
7
|
-
@mock_store1 = RemoteFiles.add_store(:mock1, :class => RemoteFiles::MockStore)
|
8
|
-
@mock_store2 = RemoteFiles.add_store(:mock2, :class => RemoteFiles::MockStore)
|
9
|
-
end
|
10
|
-
|
11
|
-
describe '::add_store' do
|
12
|
-
describe 'when adding a non-primary store' do
|
13
|
-
before { @non_primary_store = RemoteFiles.add_store(:primary) }
|
14
|
-
|
15
|
-
it 'should add it to the tail of the list of stores' do
|
16
|
-
RemoteFiles.stores.must_equal([@mock_store1, @mock_store2, @non_primary_store])
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe 'when adding a promary store' do
|
21
|
-
before { @primary_store = RemoteFiles.add_store(:primary, :primary => true) }
|
22
|
-
|
23
|
-
it 'should add it to the head of the list of stores' do
|
24
|
-
RemoteFiles.stores.must_equal([@primary_store, @mock_store1, @mock_store2])
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe '::primary_store' do
|
30
|
-
before do
|
31
|
-
@primary_store1 = RemoteFiles.add_store(:primary1, :primary => true)
|
32
|
-
@primary_store2 = RemoteFiles.add_store(:primary2, :primary => true)
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'should return the head of the list of stores' do
|
36
|
-
RemoteFiles.primary_store.must_equal(@primary_store2)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe '::lookup_store' do
|
41
|
-
before do
|
42
|
-
@primary_store = RemoteFiles.add_store(:primary, :primary => true)
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'should find the store my identifier' do
|
46
|
-
RemoteFiles.lookup_store(:mock1).must_equal(@mock_store1)
|
47
|
-
RemoteFiles.lookup_store(:mock2).must_equal(@mock_store2)
|
48
|
-
RemoteFiles.lookup_store(:primary).must_equal(@primary_store)
|
49
|
-
RemoteFiles.lookup_store(:unknown).must_be_nil
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
describe '::store_once!' do
|
54
|
-
|
55
|
-
describe 'when the first store succeeds' do
|
56
|
-
before { RemoteFiles.store_once!(@file) }
|
57
|
-
|
58
|
-
it 'should only store the file in the first store' do
|
59
|
-
@mock_store1.data['file'].must_equal(:content => 'content', :content_type => 'text/plain')
|
60
|
-
@mock_store2.data['file'].must_be_nil
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe 'when the first store fails' do
|
65
|
-
before do
|
66
|
-
@mock_store1.expects(:store!).with(@file).raises(RemoteFiles::Error)
|
67
|
-
RemoteFiles.store_once!(@file)
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'should only store the file in the second store' do
|
71
|
-
@mock_store1.data['file'].must_be_nil
|
72
|
-
@mock_store2.data['file'].must_equal(:content => 'content', :content_type => 'text/plain')
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
describe 'when alls stores fail' do
|
77
|
-
before do
|
78
|
-
@mock_store1.expects(:store!).with(@file).raises(RemoteFiles::Error)
|
79
|
-
@mock_store2.expects(:store!).with(@file).raises(RemoteFiles::Error)
|
80
|
-
end
|
81
|
-
|
82
|
-
it 'should raise a RemoteFiles::Error' do
|
83
|
-
proc { RemoteFiles.store_once!(@file) }.must_raise(RemoteFiles::Error)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
4
|
describe '::synchronize_stores' do
|
89
5
|
before do
|
90
6
|
@files = []
|
@@ -101,93 +17,4 @@ describe RemoteFiles do
|
|
101
17
|
end
|
102
18
|
end
|
103
19
|
|
104
|
-
describe '::store!' do
|
105
|
-
describe 'when the file is already stored in some stores' do
|
106
|
-
before { @file.stored_in.replace([@mock_store1.identifier]) }
|
107
|
-
|
108
|
-
it 'should not store the file' do
|
109
|
-
RemoteFiles.expects(:store_once!).never
|
110
|
-
RemoteFiles.store!(@file)
|
111
|
-
end
|
112
|
-
|
113
|
-
it 'should synchronize the stores' do
|
114
|
-
RemoteFiles.expects(:synchronize_stores).with(@file)
|
115
|
-
RemoteFiles.store!(@file)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
describe 'when the file is stored in all stores' do
|
120
|
-
before { @file.stored_in.replace([@mock_store1.identifier, @mock_store2.identifier]) }
|
121
|
-
|
122
|
-
it 'should not store the file' do
|
123
|
-
RemoteFiles.expects(:store_once!).never
|
124
|
-
RemoteFiles.store!(@file)
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'should not synchronize the stores' do
|
128
|
-
RemoteFiles.expects(:synchronize_stores).never
|
129
|
-
RemoteFiles.store!(@file)
|
130
|
-
end
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
describe 'when the file is not stored anywhere' do
|
135
|
-
before { @file.stored_in.replace([]) }
|
136
|
-
|
137
|
-
it 'should store the file once' do
|
138
|
-
RemoteFiles.expects(:store_once!).with(@file)
|
139
|
-
RemoteFiles.store!(@file)
|
140
|
-
end
|
141
|
-
|
142
|
-
it 'should synchronize the stores' do
|
143
|
-
RemoteFiles.expects(:synchronize_stores).with(@file)
|
144
|
-
RemoteFiles.store!(@file)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
describe '::delete!' do
|
150
|
-
it 'should delete the file from all the stores' do
|
151
|
-
@file.stored_in.replace([:mock1, :mock2])
|
152
|
-
@mock_store1.expects(:delete!).with(@file.identifier)
|
153
|
-
@mock_store2.expects(:delete!).with(@file.identifier)
|
154
|
-
RemoteFiles.delete!(@file)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
describe '::synchronize!' do
|
159
|
-
describe 'when the file is not stored anywhere' do
|
160
|
-
before { @file.stored_in.replace([]) }
|
161
|
-
|
162
|
-
it 'should store the file on all stores' do
|
163
|
-
@mock_store1.expects(:store!).returns(true)
|
164
|
-
@mock_store2.expects(:store!).returns(true)
|
165
|
-
|
166
|
-
RemoteFiles.synchronize!(@file)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
describe 'when the file is stored in some stores' do
|
171
|
-
before { @file.stored_in.replace([@mock_store1.identifier]) }
|
172
|
-
|
173
|
-
it 'should store the file in the remaining stores' do
|
174
|
-
@mock_store1.expects(:store!).never
|
175
|
-
@mock_store2.expects(:store!).with(@file).returns(true)
|
176
|
-
|
177
|
-
RemoteFiles.synchronize!(@file)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
describe 'when the file is stored everywhere' do
|
182
|
-
before { @file.stored_in.replace([@mock_store1.identifier, @mock_store2.identifier]) }
|
183
|
-
|
184
|
-
it 'should not do anything' do
|
185
|
-
@mock_store1.expects(:store!).never
|
186
|
-
@mock_store2.expects(:store!).never
|
187
|
-
|
188
|
-
RemoteFiles.synchronize!(@file)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
20
|
end
|
data/test/resque_job_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -15,8 +15,9 @@ MiniTest::Spec.class_eval do
|
|
15
15
|
before do
|
16
16
|
Fog::Mock.reset
|
17
17
|
|
18
|
-
RemoteFiles::
|
19
|
-
|
18
|
+
RemoteFiles::CONFIGURATIONS.values.each do |conf|
|
19
|
+
conf.clear
|
20
|
+
end
|
20
21
|
|
21
22
|
$syncs = []
|
22
23
|
RemoteFiles.synchronize_stores do |file|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: remote_files
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fog
|
@@ -48,17 +48,17 @@ dependencies:
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 3.4.0
|
54
54
|
type: :development
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
none: false
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 3.4.0
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: debugger
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,6 +116,7 @@ extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
118
|
- lib/remote_files/abstract_store.rb
|
119
|
+
- lib/remote_files/configuration.rb
|
119
120
|
- lib/remote_files/file.rb
|
120
121
|
- lib/remote_files/file_store.rb
|
121
122
|
- lib/remote_files/fog_store.rb
|
@@ -124,6 +125,7 @@ files:
|
|
124
125
|
- lib/remote_files/resque_job.rb
|
125
126
|
- lib/remote_files/version.rb
|
126
127
|
- lib/remote_files.rb
|
128
|
+
- test/configuration_test.rb
|
127
129
|
- test/file_store_test.rb
|
128
130
|
- test/file_test.rb
|
129
131
|
- test/fog_store_test.rb
|
@@ -146,7 +148,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
146
148
|
version: '0'
|
147
149
|
segments:
|
148
150
|
- 0
|
149
|
-
hash:
|
151
|
+
hash: -1536087519966001179
|
150
152
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
153
|
none: false
|
152
154
|
requirements:
|
@@ -155,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
157
|
version: '0'
|
156
158
|
segments:
|
157
159
|
- 0
|
158
|
-
hash:
|
160
|
+
hash: -1536087519966001179
|
159
161
|
requirements: []
|
160
162
|
rubyforge_project:
|
161
163
|
rubygems_version: 1.8.24
|
@@ -165,6 +167,7 @@ summary: The purpose of the library is to implement a simple interface for uploa
|
|
165
167
|
files to multiple backends and to keep the backends in sync, so that your app will
|
166
168
|
keep working when one backend is down.
|
167
169
|
test_files:
|
170
|
+
- test/configuration_test.rb
|
168
171
|
- test/file_store_test.rb
|
169
172
|
- test/file_test.rb
|
170
173
|
- test/fog_store_test.rb
|