defog 0.3.2 → 0.4.0
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/lib/defog/file.rb +38 -20
- data/lib/defog/fog_wrapper.rb +4 -1
- data/lib/defog/handle.rb +14 -6
- data/lib/defog/proxy.rb +22 -3
- data/lib/defog/version.rb +1 -1
- data/spec/file_spec.rb +24 -1
- data/spec/proxy_spec.rb +33 -0
- metadata +16 -16
data/lib/defog/file.rb
CHANGED
@@ -43,9 +43,14 @@ module Defog
|
|
43
43
|
class File < ::File
|
44
44
|
|
45
45
|
def initialize(opts={}, &block) #:nodoc:
|
46
|
-
opts = opts.keyword_args(:handle => :required,
|
46
|
+
opts = opts.keyword_args(:handle => :required,
|
47
|
+
:mode => :required,
|
48
|
+
:persist => :optional,
|
49
|
+
:synchronize => { :valid => [:async, true, false], :default => true},
|
50
|
+
:size_hint => :optional)
|
47
51
|
@handle = opts.handle
|
48
52
|
@persist = opts.persist
|
53
|
+
@synchronize = opts.synchronize
|
49
54
|
|
50
55
|
key = @handle.key
|
51
56
|
proxy_path = @handle.proxy_path
|
@@ -75,38 +80,51 @@ module Defog
|
|
75
80
|
super(proxy_path, opts.mode, &block)
|
76
81
|
end
|
77
82
|
|
78
|
-
def download_proxy
|
79
|
-
@handle.proxy.fog_wrapper.get_file(@handle.key, @handle.proxy_path, @encoding)
|
80
|
-
end
|
81
|
-
|
82
|
-
def upload_proxy
|
83
|
-
@handle.proxy.fog_wrapper.put_file(@handle.key, @handle.proxy_path, @encoding)
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
83
|
# Closes the proxy file and synchronizes the cloud storage (if it was
|
88
84
|
# opened as writeable) then deletes the proxy file.
|
89
85
|
#
|
90
|
-
# Synchronization can be
|
91
|
-
# :synchronize =>
|
86
|
+
# Synchronization (i.e. upload of a proxy) can be controlled by passing the option
|
87
|
+
# :synchronize => :async # upload asynchronously in a separate thread
|
88
|
+
# :synchronize => true # upload synchronously
|
89
|
+
# :synchronize => false # don't upload
|
92
90
|
# Synchronization will also be implicitly suppressed if the proxy file
|
93
91
|
# was deleted before this call, e.g., via <code>::File.unlink(file.path)</code>.
|
94
92
|
#
|
95
|
-
#
|
96
93
|
# Whether the proxy file gets deleted vs persisted after the close can
|
97
94
|
# be set by passing the option
|
98
95
|
# :persist => true or false
|
99
|
-
#
|
96
|
+
#
|
97
|
+
# The :persist and :synchronize values override the settings passed to
|
98
|
+
# Handle#open, which in turn overrides the settings passed to Proxy.new
|
100
99
|
#
|
101
100
|
def close(opts={})
|
102
|
-
opts = opts.keyword_args(:persist => @persist,
|
101
|
+
opts = opts.keyword_args(:persist => @persist,
|
102
|
+
:synchronize => { :valid => [true, false, :async], :default => @synchronize })
|
103
|
+
@persist = opts.persist
|
104
|
+
@synchronize = opts.synchronize
|
103
105
|
super()
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
106
|
+
if @handle.proxy_path.exist?
|
107
|
+
if @upload and @synchronize == :async
|
108
|
+
Thread.new { wrap_proxy }
|
109
|
+
else
|
110
|
+
wrap_proxy
|
111
|
+
end
|
108
112
|
end
|
109
|
-
@handle.proxy.release_proxy_path(proxy_path)
|
113
|
+
@handle.proxy.release_proxy_path(@handle.proxy_path)
|
114
|
+
end
|
115
|
+
|
116
|
+
def download_proxy #:nodoc:
|
117
|
+
@handle.proxy.fog_wrapper.get_file(@handle.key, @handle.proxy_path, @encoding)
|
118
|
+
end
|
119
|
+
|
120
|
+
def upload_proxy #:nodoc:
|
121
|
+
@handle.proxy.fog_wrapper.put_file(@handle.key, @handle.proxy_path, @encoding)
|
110
122
|
end
|
123
|
+
|
124
|
+
def wrap_proxy #:nodoc:
|
125
|
+
upload_proxy if @upload and @synchronize
|
126
|
+
@handle.proxy_path.unlink unless @persist
|
127
|
+
end
|
128
|
+
|
111
129
|
end
|
112
130
|
end
|
data/lib/defog/fog_wrapper.rb
CHANGED
@@ -29,6 +29,7 @@ module Defog #:nodoc: all
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def put_file(key, path, encoding)
|
32
|
+
return if path.exist? and fog_head(key) and Digest::MD5.hexdigest(path.read) == get_md5(key)
|
32
33
|
path.open("r#{encoding}") do |file|
|
33
34
|
fog_directory.files.create(:key => key, :body => file)
|
34
35
|
end
|
@@ -38,6 +39,8 @@ module Defog #:nodoc: all
|
|
38
39
|
fog_directory.files.head(key)
|
39
40
|
end
|
40
41
|
|
42
|
+
private
|
43
|
+
|
41
44
|
class Local < FogWrapper
|
42
45
|
def provider ; :local ; end
|
43
46
|
|
@@ -70,7 +73,7 @@ module Defog #:nodoc: all
|
|
70
73
|
def initialize(opts={})
|
71
74
|
opts = opts.keyword_args(:aws_access_key_id => :required, :aws_secret_access_key => :required, :region => :optional, :bucket => :required)
|
72
75
|
@location = opts.delete(:bucket)
|
73
|
-
@fog_connection = Fog::Storage.new(opts.merge(:provider => provider))
|
76
|
+
@fog_connection = (@@aws_connection_cache||={})[opts] ||= Fog::Storage.new(opts.merge(:provider => provider))
|
74
77
|
@fog_connection.directories.create :key => @location unless @fog_connection.directories.map(&:key).include? @location
|
75
78
|
@fog_directory = @fog_connection.directories.get(@location)
|
76
79
|
end
|
data/lib/defog/handle.rb
CHANGED
@@ -88,19 +88,27 @@ module Defog
|
|
88
88
|
# Like ::File.open, if called with a block yields the file object to
|
89
89
|
# the block and ensures the file will be closed when leaving the block.
|
90
90
|
#
|
91
|
-
# Normally
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
91
|
+
# Normally the proxy file gets deleted upon close (after synchronized
|
92
|
+
# as needed) rather than persisted, although the default behavior can
|
93
|
+
# be controlled by Defog::Proxy.new. To specify persistence behavior
|
94
|
+
# on a per-file basis, use
|
95
|
+
# :persist => true-or-false
|
96
|
+
# See File#close for more details.
|
96
97
|
#
|
97
98
|
# If you are managing your cache size, when opening a proxy for writing
|
98
99
|
# you may want to provide a hint as to the expected size of the data:
|
99
100
|
# :size_hint => 500.kilobytes
|
100
101
|
# See README for more details.
|
101
102
|
#
|
103
|
+
# Normally upon close of a writeable proxy file, the synchronization
|
104
|
+
# happens synchronously and the close will wait, althrough the behavior
|
105
|
+
# can be controlled by Defog::Proxy.new. To specify synchronization
|
106
|
+
# behavior on a per-file basis, use
|
107
|
+
# :synchronize => true-or-false-or-async
|
108
|
+
# See File#close for more details.
|
109
|
+
#
|
102
110
|
def open(mode, opts={}, &block)
|
103
|
-
opts = opts.keyword_args(:persist => @proxy.persist, :size_hint => :optional)
|
111
|
+
opts = opts.keyword_args(:persist => @proxy.persist, :synchronize => @proxy.synchronize, :size_hint => :optional)
|
104
112
|
File.open(opts.merge(:handle => self, :mode => mode), &block)
|
105
113
|
end
|
106
114
|
|
data/lib/defog/proxy.rb
CHANGED
@@ -8,6 +8,7 @@ module Defog
|
|
8
8
|
|
9
9
|
attr_reader :proxy_root
|
10
10
|
attr_reader :persist
|
11
|
+
attr_reader :synchronize
|
11
12
|
attr_reader :max_cache_size
|
12
13
|
attr_reader :fog_wrapper # :nodoc:
|
13
14
|
|
@@ -21,7 +22,9 @@ module Defog
|
|
21
22
|
# <code>:local</code> and <code>:AWS</code> are supported. When using
|
22
23
|
# <code>:AWS</code>, an additional option <code>:bucket</code> must be
|
23
24
|
# specified; all files proxied by this instance must be in a single
|
24
|
-
# bucket.
|
25
|
+
# bucket. (It's OK to create multiple Defog::Proxy instances with
|
26
|
+
# the same access info but different buckets; they will internally
|
27
|
+
# share a single Fog::Storage isntance hence AWS connection.)
|
25
28
|
#
|
26
29
|
# By default, each proxy's root directory is placed in a reasonable
|
27
30
|
# safe place, under <code>Rails.root/tmp</code> if Rails is defined
|
@@ -34,25 +37,41 @@ module Defog
|
|
34
37
|
# to worry about it. But if you do care, you can specify the option:
|
35
38
|
# :proxy_root => "/root/for/this/proxy/files"
|
36
39
|
#
|
37
|
-
# You can
|
40
|
+
# You can specify that by default local proxy files will be persisted,
|
41
|
+
# by specifying
|
38
42
|
# :persist => true
|
39
43
|
# The persistence behavior can be overriden on a per-file basis when
|
40
|
-
# opening a proxy (see Defog::Handle#open)
|
44
|
+
# opening or closing a proxy (see Defog::Handle#open, Defog::File#close)
|
41
45
|
#
|
42
46
|
# You can enable cache management by specifying a max cache size in
|
43
47
|
# bytes, e.g.
|
44
48
|
# :max_cache_size => 3.gigabytes
|
45
49
|
# See the README for discussion. [Number#gigabytes is defined in
|
46
50
|
# Rails' ActiveSupport core extensions]
|
51
|
+
#
|
52
|
+
# Normally synchronization (i.e. upload) of changes to local proxy
|
53
|
+
# files happens synchronously on close; i.e. Defog::File#close waits
|
54
|
+
# until the upload completes. However, you can control synchronization
|
55
|
+
# by specifying
|
56
|
+
# :synchronize => :async # Synchronize in a separate thread, don't wait
|
57
|
+
# :synchronize => false # Don't synchronize at all. Defeats the purpose of Defog
|
58
|
+
# :synchronize => true # This is the default behavior
|
59
|
+
# The synchronization behavior can be overridden on a per-file basis
|
60
|
+
# when opening or closing a proxy (see Defog::Handle#open,
|
61
|
+
# Defog::File#close). Note that this applies only to upload of changes to
|
62
|
+
# proxy files that are opened as writeable; the download of data to
|
63
|
+
# readable proxy files always happens synchronously.
|
47
64
|
def initialize(opts={})
|
48
65
|
opts = opts.keyword_args(:provider => :required,
|
49
66
|
:proxy_root => :optional,
|
50
67
|
:persist => :optional,
|
68
|
+
:synchronize => {:valid => [:async, true, false], :default => true},
|
51
69
|
:max_cache_size => :optional,
|
52
70
|
:OTHERS => :optional)
|
53
71
|
|
54
72
|
@proxy_root = Pathname.new(opts.delete(:proxy_root)) if opts.proxy_root
|
55
73
|
@persist = opts.delete(:persist)
|
74
|
+
@synchronize = opts.delete(:synchronize)
|
56
75
|
@max_cache_size = opts.delete(:max_cache_size)
|
57
76
|
@reserved_proxy_paths = Set.new
|
58
77
|
|
data/lib/defog/version.rb
CHANGED
data/spec/file_spec.rb
CHANGED
@@ -114,6 +114,17 @@ shared_examples "create" do
|
|
114
114
|
expect {remote_body}.should raise_error
|
115
115
|
end
|
116
116
|
|
117
|
+
it "should create remote asynchronously if :synchronize => async" do
|
118
|
+
file = @proxy.file(key, @mode)
|
119
|
+
create_proxy("upload me in thread")
|
120
|
+
Thread.should_receive(:new) { |&block|
|
121
|
+
expect {remote_body}.should raise_error
|
122
|
+
block.call
|
123
|
+
}
|
124
|
+
file.close(:synchronize => :async)
|
125
|
+
remote_body.should == "upload me in thread"
|
126
|
+
end
|
127
|
+
|
117
128
|
end
|
118
129
|
|
119
130
|
shared_examples "update" do
|
@@ -127,6 +138,18 @@ shared_examples "update" do
|
|
127
138
|
remote_body.should == "upload me"
|
128
139
|
end
|
129
140
|
|
141
|
+
it "should overwrite remote asynchronously if :synchronize => :async" do
|
142
|
+
create_remote("overwrite me")
|
143
|
+
file = @proxy.file(key, @mode)
|
144
|
+
create_proxy("upload me")
|
145
|
+
Thread.should_receive(:new) { |&block|
|
146
|
+
remote_body.should == "overwrite me"
|
147
|
+
block.call
|
148
|
+
}
|
149
|
+
file.close(:synchronize => :async)
|
150
|
+
remote_body.should == "upload me"
|
151
|
+
end
|
152
|
+
|
130
153
|
it "should not overwrite remote if proxy is deleted" do
|
131
154
|
create_remote("keep me")
|
132
155
|
@proxy.file(key, @mode) do |file|
|
@@ -206,8 +229,8 @@ shared_examples "a proxy file" do |proxyargs|
|
|
206
229
|
it_should_behave_like "read" if mode == "r" or mode == "a+"
|
207
230
|
it_should_behave_like "write" if mode =~ %r{[wa+]}
|
208
231
|
it_should_behave_like "read after write" if mode == "w+"
|
232
|
+
it_should_behave_like "create" if mode =~ %r{w}
|
209
233
|
it_should_behave_like "append" if mode =~ %r{a}
|
210
|
-
it_should_behave_like "create" if mode =~ %r{wa}
|
211
234
|
it_should_behave_like "update" if mode =~ %r{[wa+]}
|
212
235
|
it_should_behave_like "persistence"
|
213
236
|
end
|
data/spec/proxy_spec.rb
CHANGED
@@ -39,6 +39,20 @@ shared_examples "a proxy" do |args|
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
context "settings" do
|
43
|
+
it "should set default for :persist => true" do
|
44
|
+
@proxy = Defog::Proxy.new(args.merge(:persist => true))
|
45
|
+
Defog::File.should_receive(:open).with(hash_including :persist => true)
|
46
|
+
@proxy.file(key, "w") do end
|
47
|
+
end
|
48
|
+
it "should set default for :synchronize => :async" do
|
49
|
+
@proxy = Defog::Proxy.new(args.merge(:synchronize => :async))
|
50
|
+
Defog::File.should_receive(:open).with(hash_including :synchronize => :async)
|
51
|
+
@proxy.file(key, "w") do end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
42
56
|
context "iteration" do
|
43
57
|
|
44
58
|
before(:each) do
|
@@ -232,6 +246,25 @@ describe Defog::Proxy do
|
|
232
246
|
it "should use the bucket name as the location" do
|
233
247
|
Defog::Proxy.new(args).location.should == args[:bucket]
|
234
248
|
end
|
249
|
+
|
250
|
+
it "should share fog connection with same bucket" do
|
251
|
+
proxy1 = Defog::Proxy.new(args)
|
252
|
+
proxy2 = Defog::Proxy.new(args)
|
253
|
+
proxy1.fog_connection.should be_equal proxy2.fog_connection
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should share fog connection with different bucket" do
|
257
|
+
proxy1 = Defog::Proxy.new(args)
|
258
|
+
proxy2 = Defog::Proxy.new(args.merge(:bucket => "other"))
|
259
|
+
proxy1.fog_connection.should be_equal proxy2.fog_connection
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should not share fog connection with different connection args" do
|
263
|
+
proxy1 = Defog::Proxy.new(args)
|
264
|
+
proxy2 = Defog::Proxy.new(args.merge(:aws_access_key_id => "other"))
|
265
|
+
proxy1.fog_connection.should_not be_equal proxy2.fog_connection
|
266
|
+
end
|
267
|
+
|
235
268
|
end
|
236
269
|
|
237
270
|
it "should raise error on bad provider" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: defog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fog
|
16
|
-
requirement: &
|
16
|
+
requirement: &70345231086320 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70345231086320
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: hash_keyword_args
|
27
|
-
requirement: &
|
27
|
+
requirement: &70345231085600 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70345231085600
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: fastandand
|
38
|
-
requirement: &
|
38
|
+
requirement: &70345231084580 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70345231084580
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rake
|
49
|
-
requirement: &
|
49
|
+
requirement: &70345231082860 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70345231082860
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rspec
|
60
|
-
requirement: &
|
60
|
+
requirement: &70345231005400 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70345231005400
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: simplecov
|
71
|
-
requirement: &
|
71
|
+
requirement: &70345231001760 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70345231001760
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: simplecov-gem-adapter
|
82
|
-
requirement: &
|
82
|
+
requirement: &70345231000720 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,7 +87,7 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70345231000720
|
91
91
|
description: Wrapper to fog gem, proxying access to cloud files as local files.
|
92
92
|
email:
|
93
93
|
- ronen@barzel.org
|