asset_cloud 2.7.0 → 2.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +42 -0
- data/.github/workflows/cla.yml +22 -0
- data/.rubocop.yml +3 -1
- data/Gemfile +5 -3
- data/History.md +8 -0
- data/README.rdoc +3 -4
- data/Rakefile +18 -16
- data/asset_cloud.gemspec +19 -18
- data/dev.yml +1 -1
- data/lib/asset_cloud/asset.rb +17 -13
- data/lib/asset_cloud/asset_extension.rb +27 -15
- data/lib/asset_cloud/base.rb +77 -72
- data/lib/asset_cloud/bucket.rb +5 -2
- data/lib/asset_cloud/buckets/active_record_bucket.rb +16 -14
- data/lib/asset_cloud/buckets/blackhole_bucket.rb +2 -0
- data/lib/asset_cloud/buckets/bucket_chain.rb +38 -31
- data/lib/asset_cloud/buckets/file_system_bucket.rb +14 -15
- data/lib/asset_cloud/buckets/gcs_bucket.rb +6 -8
- data/lib/asset_cloud/buckets/invalid_bucket.rb +9 -6
- data/lib/asset_cloud/buckets/memory_bucket.rb +7 -4
- data/lib/asset_cloud/buckets/s3_bucket.rb +11 -8
- data/lib/asset_cloud/buckets/versioned_memory_bucket.rb +4 -2
- data/lib/asset_cloud/callbacks.rb +9 -5
- data/lib/asset_cloud/free_key_locator.rb +6 -6
- data/lib/asset_cloud/metadata.rb +11 -7
- data/lib/asset_cloud/validations.rb +9 -5
- data/lib/asset_cloud.rb +23 -21
- data/spec/active_record_bucket_spec.rb +27 -26
- data/spec/asset_cloud/metadata_spec.rb +4 -2
- data/spec/asset_extension_spec.rb +17 -16
- data/spec/asset_spec.rb +27 -21
- data/spec/base_spec.rb +93 -92
- data/spec/blackhole_bucket_spec.rb +12 -11
- data/spec/bucket_chain_spec.rb +61 -56
- data/spec/bucket_spec.rb +6 -5
- data/spec/callbacks_spec.rb +52 -32
- data/spec/file_system_spec.rb +25 -24
- data/spec/find_free_key_spec.rb +16 -17
- data/spec/gcs_bucket_remote_spec.rb +23 -22
- data/spec/gcs_bucket_spec.rb +48 -60
- data/spec/memory_bucket_spec.rb +12 -11
- data/spec/mock_s3_interface.rb +17 -6
- data/spec/remote_s3_bucket_spec.rb +31 -28
- data/spec/s3_bucket_spec.rb +19 -17
- data/spec/spec_helper.rb +8 -7
- data/spec/validations_spec.rb +13 -12
- data/spec/versioned_memory_bucket_spec.rb +11 -10
- metadata +10 -33
- data/.github/probots.yml +0 -2
- data/.rubocop_todo.yml +0 -326
- data/.travis.yml +0 -12
data/lib/asset_cloud/base.rb
CHANGED
@@ -1,42 +1,44 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "uri/rfc2396_parser"
|
4
4
|
|
5
|
+
module AssetCloud
|
5
6
|
class IllegalPath < StandardError
|
6
7
|
end
|
7
8
|
|
8
9
|
class Base
|
9
10
|
cattr_accessor :logger
|
10
11
|
|
11
|
-
VALID_PATHS =
|
12
|
+
VALID_PATHS = %r{\A
|
12
13
|
(
|
13
|
-
(\w)
|
14
|
-
|
|
14
|
+
(\w) # Filename can be a single letter or underscore
|
15
|
+
| # OR it is many and follows the below rules
|
15
16
|
(
|
16
|
-
(\.?[\w\[\]\(\)\-\@])
|
17
|
+
(\.?[\w\[\]\(\)\-\@]) # It can start with a dot but it must have a following character
|
17
18
|
(
|
18
|
-
[\w\[\]\(\)\-\@]
|
19
|
+
[\w\[\]\(\)\-\@] # You can have a letter without any following conditions
|
19
20
|
|
|
20
|
-
[\ ][\w\[\]\(\)\-\@\.]
|
21
|
+
[\ ][\w\[\]\(\)\-\@\.] # If there is a space you need to have a normal letter afterward or a dot
|
21
22
|
|
|
22
|
-
[
|
23
|
+
[/][\w\[\]\(\)\-\@] # If there is a slash you need to have a normal letter afterward
|
23
24
|
|
|
24
|
-
[
|
25
|
+
[/][\.][\w\[\]\(\)\-\@] # Though a slash could be followed by a dot
|
26
|
+
# so long as there is a normal letter afterward
|
25
27
|
|
|
26
|
-
[\.]+[\w\[\]\(\)\-\@]+
|
27
|
-
)*
|
28
|
+
[\.]+[\w\[\]\(\)\-\@]+ # One or more dots must be followed by one (or more) normal letters
|
29
|
+
)* # Zero to many of these combinations.
|
28
30
|
)
|
29
|
-
)\z
|
30
|
-
MATCH_BUCKET =
|
31
|
+
)\z}x
|
32
|
+
MATCH_BUCKET = %r{^(\w+)(/|$)}
|
31
33
|
|
32
34
|
URI_PARSER = URI::RFC2396_Parser.new
|
33
35
|
|
34
36
|
attr_accessor :url, :root
|
35
37
|
|
36
38
|
class_attribute :root_bucket_class
|
37
|
-
self.root_bucket_class =
|
39
|
+
self.root_bucket_class = "AssetCloud::FileSystemBucket"
|
38
40
|
class_attribute :root_asset_class
|
39
|
-
self.root_asset_class
|
41
|
+
self.root_asset_class = "AssetCloud::Asset"
|
40
42
|
|
41
43
|
class_attribute :bucket_classes
|
42
44
|
self.bucket_classes = {}.freeze
|
@@ -45,56 +47,68 @@ module AssetCloud
|
|
45
47
|
class_attribute :asset_extension_classes
|
46
48
|
self.asset_extension_classes = {}.freeze
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
class << self
|
51
|
+
def bucket(*args)
|
52
|
+
asset_class = if args.last.is_a?(Hash)
|
53
|
+
convert_to_class_name_if_possible(args.pop[:asset_class])
|
54
|
+
end
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
bucket_class = if args.last.is_a?(Class)
|
57
|
+
convert_to_class_name_if_possible(args.pop)
|
58
|
+
else
|
59
|
+
raise ArgumentError, "requires a bucket class"
|
60
|
+
end
|
58
61
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
if (bucket_name = args.first)
|
63
|
+
self.bucket_classes = bucket_classes.merge(bucket_name.to_sym => bucket_class).freeze
|
64
|
+
self.asset_classes = asset_classes.merge(bucket_name.to_sym => asset_class).freeze if asset_class
|
65
|
+
else
|
66
|
+
self.root_bucket_class = bucket_class
|
67
|
+
if asset_class
|
68
|
+
raise ArgumentError, "asset_class on the root bucket cannot be a proc" if asset_class.is_a?(Proc)
|
69
|
+
|
70
|
+
self.root_asset_class = asset_class
|
71
|
+
end
|
67
72
|
end
|
68
73
|
end
|
69
|
-
end
|
70
74
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
def asset_extensions(*args)
|
76
|
+
opts = args.last.is_a?(Hash) ? args.pop.slice(:only, :except) : {}
|
77
|
+
opts.each do |k, v|
|
78
|
+
opts[k] = [v].flatten.map(&:to_sym)
|
79
|
+
end
|
80
|
+
|
81
|
+
args.each do |klass|
|
82
|
+
klass = convert_to_class_name_if_possible(klass)
|
83
|
+
self.asset_extension_classes = asset_extension_classes.merge(klass => opts).freeze
|
84
|
+
end
|
75
85
|
end
|
76
86
|
|
77
|
-
|
78
|
-
|
79
|
-
|
87
|
+
private
|
88
|
+
|
89
|
+
def convert_to_class_name_if_possible(klass)
|
90
|
+
if klass.is_a?(Class) && klass.name.present?
|
91
|
+
klass.name
|
92
|
+
else
|
93
|
+
klass
|
94
|
+
end
|
80
95
|
end
|
81
96
|
end
|
82
97
|
|
83
98
|
def buckets
|
84
99
|
@buckets ||= Hash.new do |hash, key|
|
85
|
-
if klass = self.class.bucket_classes[key]
|
86
|
-
|
87
|
-
else
|
88
|
-
hash[key] = nil
|
100
|
+
hash[key] = if (klass = self.class.bucket_classes[key])
|
101
|
+
constantize_if_necessary(klass).new(self, key)
|
89
102
|
end
|
90
103
|
end
|
91
104
|
end
|
92
105
|
|
93
|
-
def initialize(root, url =
|
94
|
-
@root
|
106
|
+
def initialize(root, url = "/")
|
107
|
+
@root = root
|
108
|
+
@url = url
|
95
109
|
end
|
96
110
|
|
97
|
-
def url_for(key, options={})
|
111
|
+
def url_for(key, options = {})
|
98
112
|
File.join(@url, URI_PARSER.escape(key))
|
99
113
|
end
|
100
114
|
|
@@ -140,13 +154,13 @@ module AssetCloud
|
|
140
154
|
end
|
141
155
|
|
142
156
|
def build(key, value = nil, &block)
|
143
|
-
logger
|
157
|
+
logger&.info { " [#{self.class.name}] Building asset #{key}" }
|
144
158
|
asset_class_for(key).new(self, key, value, Metadata.non_existing, &block)
|
145
159
|
end
|
146
160
|
|
147
161
|
def write(key, value)
|
148
162
|
check_key_for_errors(key)
|
149
|
-
logger
|
163
|
+
logger&.info { " [#{self.class.name}] Writing #{value.size} bytes to #{key}" }
|
150
164
|
|
151
165
|
bucket_for(key).write(key, value)
|
152
166
|
end
|
@@ -158,25 +172,25 @@ module AssetCloud
|
|
158
172
|
end
|
159
173
|
|
160
174
|
def read(key)
|
161
|
-
logger
|
175
|
+
logger&.info { " [#{self.class.name}] Reading from #{key}" }
|
162
176
|
|
163
177
|
bucket_for(key).read(key)
|
164
178
|
end
|
165
179
|
|
166
180
|
def stat(key)
|
167
|
-
logger
|
181
|
+
logger&.info { " [#{self.class.name}] Statting #{key}" }
|
168
182
|
|
169
183
|
bucket_for(key).stat(key)
|
170
184
|
end
|
171
185
|
|
172
186
|
def ls(key)
|
173
|
-
logger
|
187
|
+
logger&.info { " [#{self.class.name}] Listing objects in #{key}" }
|
174
188
|
|
175
189
|
bucket_for(key).ls(key)
|
176
190
|
end
|
177
191
|
|
178
192
|
def exist?(key)
|
179
|
-
if fp = stat(key)
|
193
|
+
if (fp = stat(key))
|
180
194
|
fp.exist?
|
181
195
|
else
|
182
196
|
false
|
@@ -188,7 +202,7 @@ module AssetCloud
|
|
188
202
|
end
|
189
203
|
|
190
204
|
def delete(key)
|
191
|
-
logger
|
205
|
+
logger&.info { " [#{self.class.name}] Deleting #{key}" }
|
192
206
|
|
193
207
|
bucket_for(key).delete(key)
|
194
208
|
end
|
@@ -211,17 +225,17 @@ module AssetCloud
|
|
211
225
|
# versioning
|
212
226
|
|
213
227
|
def read_version(key, version)
|
214
|
-
logger
|
228
|
+
logger&.info { " [#{self.class.name}] Reading from #{key} at version #{version}" }
|
215
229
|
bucket_for(key).read_version(key, version)
|
216
230
|
end
|
217
231
|
|
218
232
|
def versions(key)
|
219
|
-
logger
|
233
|
+
logger&.info { " [#{self.class.name}] Getting all versions for #{key}" }
|
220
234
|
bucket_for(key).versions(key)
|
221
235
|
end
|
222
236
|
|
223
237
|
def version_details(key)
|
224
|
-
logger
|
238
|
+
logger&.info { " [#{self.class.name}] Getting all version details for #{key}" }
|
225
239
|
bucket_for(key).version_details(key)
|
226
240
|
end
|
227
241
|
|
@@ -239,40 +253,31 @@ module AssetCloud
|
|
239
253
|
klasses = extensions.keys.select do |ext|
|
240
254
|
opts = extensions[ext]
|
241
255
|
(opts.key?(:only) ? opts[:only].include?(bucket) : true) &&
|
242
|
-
|
256
|
+
(opts.key?(:except) ? !opts[:except].include?(bucket) : true)
|
243
257
|
end
|
244
|
-
klasses.map {|klass| constantize_if_necessary(klass)}
|
258
|
+
klasses.map { |klass| constantize_if_necessary(klass) }
|
245
259
|
end
|
246
260
|
|
247
261
|
protected
|
248
262
|
|
249
263
|
def bucket_symbol_for_key(key)
|
250
|
-
|
264
|
+
Regexp.last_match(1).to_sym if key =~ MATCH_BUCKET
|
251
265
|
end
|
252
266
|
|
253
267
|
def root_bucket
|
254
|
-
@default_bucket ||= constantize_if_necessary(self.class.root_bucket_class).new(self,
|
268
|
+
@default_bucket ||= constantize_if_necessary(self.class.root_bucket_class).new(self, "")
|
255
269
|
end
|
256
270
|
|
257
271
|
def constantize_if_necessary(klass)
|
258
272
|
klass.is_a?(Class) ? klass : klass.constantize
|
259
273
|
end
|
260
274
|
|
261
|
-
def self.convert_to_class_name_if_possible(klass)
|
262
|
-
if klass.is_a?(Class) && klass.name.present?
|
263
|
-
klass.name
|
264
|
-
else
|
265
|
-
klass
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
275
|
def check_key_for_errors(key)
|
270
276
|
raise IllegalPath, "key cannot be empty" if key.blank?
|
271
277
|
raise IllegalPath, "#{key.inspect} contains illegal characters" unless supports?(key)
|
272
278
|
rescue => e
|
273
|
-
logger
|
279
|
+
logger&.info { " [#{self.class.name}] bad key #{e.message}" }
|
274
280
|
raise
|
275
281
|
end
|
276
|
-
|
277
282
|
end
|
278
283
|
end
|
data/lib/asset_cloud/bucket.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AssetCloud
|
2
4
|
class AssetNotFoundError < StandardError
|
3
|
-
def initialize(key, version=nil)
|
5
|
+
def initialize(key, version = nil)
|
4
6
|
super(version ? "Could not find version #{version} of asset #{key}" : "Could not find asset #{key}")
|
5
7
|
end
|
6
8
|
end
|
@@ -10,7 +12,8 @@ module AssetCloud
|
|
10
12
|
attr_accessor :cloud
|
11
13
|
|
12
14
|
def initialize(cloud, name)
|
13
|
-
@cloud
|
15
|
+
@cloud = cloud
|
16
|
+
@name = name
|
14
17
|
end
|
15
18
|
|
16
19
|
def ls(key = nil)
|
@@ -1,35 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AssetCloud
|
2
4
|
class ActiveRecordBucket < AssetCloud::Bucket
|
3
5
|
class_attribute :key_attribute, :value_attribute
|
4
|
-
self.key_attribute =
|
5
|
-
self.value_attribute =
|
6
|
+
self.key_attribute = "key"
|
7
|
+
self.value_attribute = "value"
|
6
8
|
|
7
|
-
def ls(key=name)
|
8
|
-
col = records.connection.quote_column_name(
|
9
|
-
records.all(:
|
10
|
-
cloud[r.send(
|
9
|
+
def ls(key = name)
|
10
|
+
col = records.connection.quote_column_name(key_attribute)
|
11
|
+
records.all(conditions: ["#{col} LIKE ?", "#{key}%"]).map do |r|
|
12
|
+
cloud[r.send(key_attribute)]
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
14
16
|
def read(key)
|
15
|
-
find_record!(key).send(
|
17
|
+
find_record!(key).send(value_attribute)
|
16
18
|
end
|
17
19
|
|
18
20
|
def write(key, value)
|
19
|
-
record = records.send("find_or_initialize_by_#{
|
20
|
-
record.send("#{
|
21
|
+
record = records.send("find_or_initialize_by_#{key_attribute}", key.to_s)
|
22
|
+
record.send("#{value_attribute}=", value)
|
21
23
|
record.save!
|
22
24
|
end
|
23
25
|
|
24
26
|
def delete(key)
|
25
|
-
if record = find_record(key)
|
27
|
+
if (record = find_record(key))
|
26
28
|
record.destroy
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
32
|
def stat(key)
|
31
|
-
if record = find_record(key)
|
32
|
-
AssetCloud::Metadata.new(true, record.send(
|
33
|
+
if (record = find_record(key))
|
34
|
+
AssetCloud::Metadata.new(true, record.send(value_attribute).size, record.created_at, record.updated_at)
|
33
35
|
else
|
34
36
|
AssetCloud::Metadata.new(false)
|
35
37
|
end
|
@@ -47,11 +49,11 @@ module AssetCloud
|
|
47
49
|
end
|
48
50
|
|
49
51
|
def find_record(key)
|
50
|
-
records.first(:
|
52
|
+
records.first(conditions: { key_attribute => key.to_s })
|
51
53
|
end
|
52
54
|
|
53
55
|
def find_record!(key)
|
54
|
-
find_record(key)
|
56
|
+
find_record(key) || raise(AssetCloud::AssetNotFoundError, key)
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -1,61 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AssetCloud
|
2
4
|
class BucketChain < Bucket
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
class << self
|
6
|
+
# returns a new Bucket class which writes to each given Bucket
|
7
|
+
# but only uses the first one for reading
|
8
|
+
def chain(*klasses)
|
9
|
+
Class.new(self) do
|
10
|
+
attr_reader :chained_buckets
|
11
|
+
|
12
|
+
define_method "initialize" do |cloud, name|
|
13
|
+
super(cloud, name)
|
14
|
+
@chained_buckets = klasses.map { |klass| klass.new(cloud, name) }
|
15
|
+
end
|
11
16
|
end
|
12
17
|
end
|
13
18
|
end
|
14
19
|
|
15
|
-
def ls(key=nil)
|
16
|
-
first_possible_bucket {|b| b.ls(key)}
|
20
|
+
def ls(key = nil)
|
21
|
+
first_possible_bucket { |b| b.ls(key) }
|
17
22
|
end
|
23
|
+
|
18
24
|
def read(key)
|
19
|
-
first_possible_bucket {|b| b.read(key)}
|
25
|
+
first_possible_bucket { |b| b.read(key) }
|
20
26
|
end
|
21
|
-
|
22
|
-
|
27
|
+
|
28
|
+
def stat(key = nil)
|
29
|
+
first_possible_bucket { |b| b.stat(key) }
|
23
30
|
end
|
31
|
+
|
24
32
|
def read_version(key, version)
|
25
|
-
first_possible_bucket {|b| b.read_version(key, version)}
|
33
|
+
first_possible_bucket { |b| b.read_version(key, version) }
|
26
34
|
end
|
35
|
+
|
27
36
|
def versions(key)
|
28
|
-
first_possible_bucket {|b| b.versions(key)}
|
37
|
+
first_possible_bucket { |b| b.versions(key) }
|
29
38
|
end
|
30
39
|
|
31
40
|
def write(key, data)
|
32
|
-
every_bucket_with_transaction_on_key(key) {|b| b.write(key, data)}
|
41
|
+
every_bucket_with_transaction_on_key(key) { |b| b.write(key, data) }
|
33
42
|
end
|
43
|
+
|
34
44
|
def delete(key)
|
35
|
-
every_bucket_with_transaction_on_key(key) {|b| b.delete(key)}
|
45
|
+
every_bucket_with_transaction_on_key(key) { |b| b.delete(key) }
|
36
46
|
end
|
37
47
|
|
38
|
-
def
|
39
|
-
@chained_buckets.any? {|b| b.respond_to?(sym)}
|
48
|
+
def respond_to_missing?(sym, *)
|
49
|
+
@chained_buckets.any? { |b| b.respond_to?(sym) }
|
40
50
|
end
|
51
|
+
|
41
52
|
def method_missing(sym, *args)
|
42
|
-
first_possible_bucket {|b| b.send(sym, *args)}
|
53
|
+
first_possible_bucket { |b| b.send(sym, *args) }
|
43
54
|
end
|
44
55
|
|
45
56
|
private
|
46
57
|
|
47
58
|
def first_possible_bucket(&block)
|
48
59
|
@chained_buckets.each do |bucket|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
nil
|
53
|
-
end
|
60
|
+
return yield(bucket)
|
61
|
+
rescue NoMethodError, NotImplementedError
|
62
|
+
nil
|
54
63
|
end
|
55
64
|
end
|
56
65
|
|
57
|
-
def every_bucket_with_transaction_on_key(key, i=0, &block)
|
58
|
-
return unless bucket = @chained_buckets[i]
|
66
|
+
def every_bucket_with_transaction_on_key(key, i = 0, &block)
|
67
|
+
return unless (bucket = @chained_buckets[i])
|
59
68
|
|
60
69
|
old_value = begin
|
61
70
|
bucket.read(key)
|
@@ -65,8 +74,8 @@ module AssetCloud
|
|
65
74
|
result = yield(bucket)
|
66
75
|
|
67
76
|
begin
|
68
|
-
every_bucket_with_transaction_on_key(key, i+1, &block)
|
69
|
-
|
77
|
+
every_bucket_with_transaction_on_key(key, i + 1, &block)
|
78
|
+
result
|
70
79
|
rescue StandardError => e
|
71
80
|
if old_value
|
72
81
|
bucket.write(key, old_value)
|
@@ -76,7 +85,5 @@ module AssetCloud
|
|
76
85
|
raise e
|
77
86
|
end
|
78
87
|
end
|
79
|
-
|
80
88
|
end
|
81
|
-
|
82
89
|
end
|
@@ -1,27 +1,29 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module AssetCloud
|
3
4
|
class FileSystemBucket < Bucket
|
4
|
-
|
5
5
|
def ls(key = nil)
|
6
6
|
objects = []
|
7
|
-
base_path = File.join(path_for(key),
|
7
|
+
base_path = File.join(path_for(key), "*")
|
8
8
|
|
9
9
|
Dir.glob(base_path).each do |f|
|
10
10
|
next unless File.file?(f)
|
11
|
-
|
11
|
+
|
12
|
+
objects.push(cloud[relative_path_for(f)])
|
12
13
|
end
|
13
14
|
objects
|
14
15
|
end
|
15
16
|
|
16
17
|
def read(key)
|
17
18
|
File.read(path_for(key))
|
18
|
-
rescue Errno::ENOENT
|
19
|
+
rescue Errno::ENOENT
|
19
20
|
raise AssetCloud::AssetNotFoundError, key
|
20
21
|
end
|
21
22
|
|
22
23
|
def delete(key)
|
23
24
|
File.delete(path_for(key))
|
24
25
|
rescue Errno::ENOENT
|
26
|
+
nil
|
25
27
|
end
|
26
28
|
|
27
29
|
def write(key, data)
|
@@ -32,12 +34,10 @@ module AssetCloud
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def stat(key)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
Metadata.new(false)
|
40
|
-
end
|
37
|
+
stat = File.stat(path_for(key))
|
38
|
+
Metadata.new(true, stat.size, stat.ctime, stat.mtime)
|
39
|
+
rescue Errno::ENOENT
|
40
|
+
Metadata.new(false)
|
41
41
|
end
|
42
42
|
|
43
43
|
protected
|
@@ -53,11 +53,11 @@ module AssetCloud
|
|
53
53
|
private
|
54
54
|
|
55
55
|
def remove_full_path_regexp
|
56
|
-
@regexp ||=
|
56
|
+
@regexp ||= %r{^#{path}/}
|
57
57
|
end
|
58
58
|
|
59
59
|
def relative_path_for(f)
|
60
|
-
f.sub(remove_full_path_regexp,
|
60
|
+
f.sub(remove_full_path_regexp, "")
|
61
61
|
end
|
62
62
|
|
63
63
|
def execute_in_full_path(key, &block)
|
@@ -71,10 +71,9 @@ module AssetCloud
|
|
71
71
|
|
72
72
|
begin
|
73
73
|
yield(path)
|
74
|
-
rescue Errno::ENOENT
|
74
|
+
rescue Errno::ENOENT
|
75
75
|
raise if retried
|
76
76
|
|
77
|
-
directory = File.dirname(path)
|
78
77
|
FileUtils.mkdir_p(File.dirname(path))
|
79
78
|
retried = true
|
80
79
|
retry
|
@@ -31,12 +31,10 @@ module AssetCloud
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def stat(key)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
Metadata.new(false)
|
39
|
-
end
|
34
|
+
file = find_by_key!(key)
|
35
|
+
Metadata.new(true, file.size, file.created_at, file.updated_at)
|
36
|
+
rescue AssetCloud::AssetNotFoundError
|
37
|
+
Metadata.new(false)
|
40
38
|
end
|
41
39
|
|
42
40
|
private
|
@@ -47,11 +45,11 @@ module AssetCloud
|
|
47
45
|
|
48
46
|
def absolute_key(key = nil)
|
49
47
|
if key.to_s.starts_with?(path_prefix)
|
50
|
-
|
48
|
+
key
|
51
49
|
else
|
52
50
|
args = [path_prefix]
|
53
51
|
args << key.to_s if key
|
54
|
-
args.join(
|
52
|
+
args.join("/")
|
55
53
|
end
|
56
54
|
end
|
57
55
|
|
@@ -1,28 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AssetCloud
|
2
4
|
class InvalidBucketError < StandardError
|
3
5
|
end
|
4
6
|
|
5
7
|
class InvalidBucket < Bucket
|
6
|
-
|
8
|
+
ERROR = "No such namespace: %s"
|
9
|
+
private_constant :ERROR
|
7
10
|
|
8
11
|
def ls(namespace)
|
9
|
-
raise InvalidBucketError,
|
12
|
+
raise InvalidBucketError, ERROR % namespace
|
10
13
|
end
|
11
14
|
|
12
15
|
def read(key)
|
13
|
-
raise InvalidBucketError,
|
16
|
+
raise InvalidBucketError, ERROR % key
|
14
17
|
end
|
15
18
|
|
16
19
|
def write(key, data)
|
17
|
-
raise InvalidBucketError,
|
20
|
+
raise InvalidBucketError, ERROR % key
|
18
21
|
end
|
19
22
|
|
20
23
|
def delete(key)
|
21
|
-
raise InvalidBucketError,
|
24
|
+
raise InvalidBucketError, ERROR % key
|
22
25
|
end
|
23
26
|
|
24
27
|
def stat(key)
|
25
|
-
raise InvalidBucketError,
|
28
|
+
raise InvalidBucketError, ERROR % key
|
26
29
|
end
|
27
30
|
end
|
28
31
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AssetCloud
|
2
4
|
class MemoryBucket < Bucket
|
3
5
|
def initialize(*args)
|
@@ -5,16 +7,17 @@ module AssetCloud
|
|
5
7
|
@memory = {}
|
6
8
|
end
|
7
9
|
|
8
|
-
def ls(prefix=nil)
|
10
|
+
def ls(prefix = nil)
|
9
11
|
results = []
|
10
|
-
@memory.each do |k,
|
12
|
+
@memory.each do |k, _v|
|
11
13
|
results.push(cloud[k]) if prefix.nil? || k.starts_with?(prefix)
|
12
14
|
end
|
13
15
|
results
|
14
16
|
end
|
15
17
|
|
16
18
|
def read(key)
|
17
|
-
raise AssetCloud::AssetNotFoundError, key unless @memory.
|
19
|
+
raise AssetCloud::AssetNotFoundError, key unless @memory.key?(key)
|
20
|
+
|
18
21
|
@memory[key]
|
19
22
|
end
|
20
23
|
|
@@ -29,7 +32,7 @@ module AssetCloud
|
|
29
32
|
end
|
30
33
|
|
31
34
|
def stat(key)
|
32
|
-
return Metadata.non_existing unless @memory.
|
35
|
+
return Metadata.non_existing unless @memory.key?(key)
|
33
36
|
|
34
37
|
Metadata.new(true, read(key).size)
|
35
38
|
end
|