asset_cloud 1.0.2
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 +7 -0
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +13 -0
- data/Rakefile +24 -0
- data/asset_cloud.gemspec +24 -0
- data/lib/asset_cloud.rb +54 -0
- data/lib/asset_cloud/asset.rb +187 -0
- data/lib/asset_cloud/asset_extension.rb +42 -0
- data/lib/asset_cloud/base.rb +247 -0
- data/lib/asset_cloud/bucket.rb +39 -0
- data/lib/asset_cloud/buckets/active_record_bucket.rb +57 -0
- data/lib/asset_cloud/buckets/blackhole_bucket.rb +23 -0
- data/lib/asset_cloud/buckets/bucket_chain.rb +84 -0
- data/lib/asset_cloud/buckets/file_system_bucket.rb +79 -0
- data/lib/asset_cloud/buckets/invalid_bucket.rb +28 -0
- data/lib/asset_cloud/buckets/memory_bucket.rb +42 -0
- data/lib/asset_cloud/buckets/versioned_memory_bucket.rb +33 -0
- data/lib/asset_cloud/callbacks.rb +63 -0
- data/lib/asset_cloud/free_key_locator.rb +28 -0
- data/lib/asset_cloud/metadata.rb +29 -0
- data/lib/asset_cloud/validations.rb +52 -0
- data/spec/active_record_bucket_spec.rb +95 -0
- data/spec/asset_extension_spec.rb +103 -0
- data/spec/asset_spec.rb +177 -0
- data/spec/base_spec.rb +114 -0
- data/spec/blackhole_bucket_spec.rb +41 -0
- data/spec/bucket_chain_spec.rb +158 -0
- data/spec/callbacks_spec.rb +125 -0
- data/spec/file_system_spec.rb +74 -0
- data/spec/files/products/key.txt +1 -0
- data/spec/files/versioned_stuff/foo +1 -0
- data/spec/find_free_key_spec.rb +39 -0
- data/spec/memory_bucket_spec.rb +52 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/validations_spec.rb +53 -0
- data/spec/versioned_memory_bucket_spec.rb +36 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a3f0421040a068165fb6219c055d4d490c0d1b56
|
4
|
+
data.tar.gz: 539fc589e72c4b3be9eb61e45fb895d75f96f0ae
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 67092177460792fa3892e585bb939b97969b80b96e621d32a619fe24022caccc781db5426c6bc3b8921ed8b950886259ab57a910472cbee922ae27ff9e2f3a28
|
7
|
+
data.tar.gz: b532387d3a18ceceb4710f8a3bd006693e063171ec1792f040d0ee3d1c2534aa3d95f31edc26ad05a604bbfd916ed387571d08785fdb5829c403ce3a058908c6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008-2013 Tobias Lütke & Shopify, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
= AssetCloud
|
2
|
+
|
3
|
+
An abstraction layer around arbitrary and diverse asset stores.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
=== as a Gem
|
8
|
+
|
9
|
+
gem install asset_cloud
|
10
|
+
|
11
|
+
== Copyright
|
12
|
+
|
13
|
+
Copyright (c) 2008-2013 Tobias Lütke & Shopify, Inc. Released under the MIT license (see LICENSE for details).
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rdoc/task'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
desc 'Default: run unit tests.'
|
9
|
+
task :default => [:spec]
|
10
|
+
|
11
|
+
desc "Run all spec examples"
|
12
|
+
RSpec::Core::RakeTask.new do |t|
|
13
|
+
t.pattern = 'spec/**/*_spec.rb'
|
14
|
+
t.rspec_opts = ['--color']
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Generate documentation for the asset_cloud plugin.'
|
18
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
20
|
+
rdoc.title = 'AssetCloud'
|
21
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
+
rdoc.rdoc_files.include('README')
|
23
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
+
end
|
data/asset_cloud.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{asset_cloud}
|
5
|
+
s.version = "1.0.2"
|
6
|
+
|
7
|
+
s.authors = %w(Shopify)
|
8
|
+
s.date = %q{2009-08-04}
|
9
|
+
s.summary = %q{An abstraction layer around arbitrary and diverse asset stores.}
|
10
|
+
s.description = %q{An abstraction layer around arbitrary and diverse asset stores.}
|
11
|
+
|
12
|
+
s.email = %q{developers@shopify.com}
|
13
|
+
s.homepage = %q{http://github.com/Shopify/asset_cloud}
|
14
|
+
s.require_paths = %w(lib)
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split($/)
|
17
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
18
|
+
|
19
|
+
s.add_dependency 'activesupport'
|
20
|
+
s.add_dependency 'class_inheritable_attributes'
|
21
|
+
|
22
|
+
s.add_development_dependency 'rspec'
|
23
|
+
s.add_development_dependency 'rake'
|
24
|
+
end
|
data/lib/asset_cloud.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
# Core
|
4
|
+
require 'asset_cloud/asset'
|
5
|
+
require 'asset_cloud/metadata'
|
6
|
+
require 'asset_cloud/bucket'
|
7
|
+
require 'asset_cloud/buckets/active_record_bucket'
|
8
|
+
require 'asset_cloud/buckets/blackhole_bucket'
|
9
|
+
require 'asset_cloud/buckets/bucket_chain'
|
10
|
+
require 'asset_cloud/buckets/file_system_bucket'
|
11
|
+
require 'asset_cloud/buckets/invalid_bucket'
|
12
|
+
require 'asset_cloud/buckets/memory_bucket'
|
13
|
+
require 'asset_cloud/buckets/versioned_memory_bucket'
|
14
|
+
require 'asset_cloud/base'
|
15
|
+
|
16
|
+
|
17
|
+
# Extensions
|
18
|
+
require 'asset_cloud/free_key_locator'
|
19
|
+
require 'asset_cloud/callbacks'
|
20
|
+
require 'asset_cloud/validations'
|
21
|
+
|
22
|
+
require 'asset_cloud/asset_extension'
|
23
|
+
|
24
|
+
|
25
|
+
AssetCloud::Base.class_eval do
|
26
|
+
include AssetCloud::FreeKeyLocator
|
27
|
+
include AssetCloud::Callbacks
|
28
|
+
callback_methods :write, :delete
|
29
|
+
end
|
30
|
+
|
31
|
+
AssetCloud::Asset.class_eval do
|
32
|
+
include AssetCloud::Callbacks
|
33
|
+
callback_methods :store, :delete
|
34
|
+
|
35
|
+
include AssetCloud::Validations
|
36
|
+
callback_methods :validate
|
37
|
+
validate :valid_key
|
38
|
+
|
39
|
+
def execute_callbacks(symbol, args)
|
40
|
+
super
|
41
|
+
@extensions.each {|ext| ext.execute_callbacks(symbol, args)}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def valid_key
|
47
|
+
if key.blank?
|
48
|
+
add_error "key cannot be empty"
|
49
|
+
elsif key !~ AssetCloud::Base::VALID_PATHS
|
50
|
+
add_error "#{key.inspect} contains illegal characters"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module AssetCloud
|
2
|
+
|
3
|
+
class AssetError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class AssetNotSaved < AssetError
|
7
|
+
end
|
8
|
+
|
9
|
+
class Asset
|
10
|
+
include Comparable
|
11
|
+
attr_accessor :key, :value, :cloud, :metadata, :new_asset
|
12
|
+
attr_reader :extensions
|
13
|
+
|
14
|
+
def initialize(cloud, key, value = nil, metadata = Metadata.non_existing)
|
15
|
+
@new_asset = true
|
16
|
+
@cloud = cloud
|
17
|
+
@key = key
|
18
|
+
@value = value
|
19
|
+
@metadata = metadata
|
20
|
+
|
21
|
+
apply_extensions
|
22
|
+
|
23
|
+
if @cloud.blank?
|
24
|
+
raise ArgumentError, "cloud is not a valid AssetCloud::Base"
|
25
|
+
end
|
26
|
+
|
27
|
+
yield self if block_given?
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.at(cloud, key, value = nil, metadata = nil, &block)
|
31
|
+
file = self.new(cloud, key, value, metadata, &block)
|
32
|
+
file.new_asset = false
|
33
|
+
file
|
34
|
+
end
|
35
|
+
|
36
|
+
def <=>(other)
|
37
|
+
cloud.object_id <=> other.cloud.object_id && key <=> other.key
|
38
|
+
end
|
39
|
+
|
40
|
+
def new_asset?
|
41
|
+
@new_asset
|
42
|
+
end
|
43
|
+
|
44
|
+
def relative_key
|
45
|
+
@key.split("/",2).last
|
46
|
+
end
|
47
|
+
|
48
|
+
def relative_key_without_ext
|
49
|
+
relative_key.gsub(/\.[^.]+$/,"")
|
50
|
+
end
|
51
|
+
|
52
|
+
def dirname
|
53
|
+
File.dirname(@key)
|
54
|
+
end
|
55
|
+
|
56
|
+
def extname
|
57
|
+
File.extname(@key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def format
|
61
|
+
extname.sub('.', '')
|
62
|
+
end
|
63
|
+
|
64
|
+
def basename
|
65
|
+
File.basename(@key)
|
66
|
+
end
|
67
|
+
|
68
|
+
def basename_without_ext
|
69
|
+
File.basename(@key, extname)
|
70
|
+
end
|
71
|
+
|
72
|
+
def size
|
73
|
+
metadata.size
|
74
|
+
end
|
75
|
+
|
76
|
+
def exist?
|
77
|
+
metadata.exist?
|
78
|
+
end
|
79
|
+
|
80
|
+
def created_at
|
81
|
+
metadata.created_at
|
82
|
+
end
|
83
|
+
|
84
|
+
def updated_at
|
85
|
+
metadata.updated_at
|
86
|
+
end
|
87
|
+
|
88
|
+
def value_hash
|
89
|
+
metadata.value_hash
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete
|
93
|
+
if new_asset?
|
94
|
+
false
|
95
|
+
else
|
96
|
+
cloud.delete(key)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def metadata
|
101
|
+
@metadata ||= cloud.stat(key)
|
102
|
+
end
|
103
|
+
|
104
|
+
def value
|
105
|
+
@value ||= if new_asset?
|
106
|
+
nil
|
107
|
+
else
|
108
|
+
cloud.read(key)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def store
|
113
|
+
unless @value.nil?
|
114
|
+
@new_asset = false
|
115
|
+
@metadata = nil
|
116
|
+
cloud.write(key, value)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def store!
|
121
|
+
store or raise AssetNotSaved
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_param
|
125
|
+
basename
|
126
|
+
end
|
127
|
+
|
128
|
+
def handle
|
129
|
+
basename.to_handle
|
130
|
+
end
|
131
|
+
|
132
|
+
def url(options = {})
|
133
|
+
cloud.url_for key, options
|
134
|
+
end
|
135
|
+
|
136
|
+
def bucket_name
|
137
|
+
@key.split('/').first
|
138
|
+
end
|
139
|
+
|
140
|
+
def bucket
|
141
|
+
cloud.buckets[bucket_name.to_sym]
|
142
|
+
end
|
143
|
+
|
144
|
+
def inspect
|
145
|
+
"#<#{self.class.name}: #{key}>"
|
146
|
+
end
|
147
|
+
|
148
|
+
# versioning
|
149
|
+
|
150
|
+
def versioned?
|
151
|
+
bucket.versioned?
|
152
|
+
end
|
153
|
+
|
154
|
+
def rollback(version)
|
155
|
+
self.value = cloud.read_version(key, version)
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
def versions
|
160
|
+
cloud.versions(key)
|
161
|
+
end
|
162
|
+
|
163
|
+
def version_details
|
164
|
+
cloud.version_details(key)
|
165
|
+
end
|
166
|
+
|
167
|
+
def method_missing(method, *args)
|
168
|
+
@extensions.each do |ext|
|
169
|
+
begin
|
170
|
+
return ext.send(method, *args)
|
171
|
+
rescue NoMethodError, NotImplementedError => e
|
172
|
+
nil
|
173
|
+
end
|
174
|
+
end
|
175
|
+
super
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def apply_extensions
|
181
|
+
@extensions ||= []
|
182
|
+
@cloud.asset_extension_classes_for_bucket(bucket_name).each do |ext|
|
183
|
+
@extensions << ext.new(self) if ext.applies_to_asset?(self)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module AssetCloud
|
2
|
+
class AssetExtension
|
3
|
+
class AssetMismatch < StandardError
|
4
|
+
end
|
5
|
+
def store; true; end
|
6
|
+
def delete; true; end
|
7
|
+
|
8
|
+
include AssetCloud::Callbacks
|
9
|
+
include AssetCloud::Validations
|
10
|
+
|
11
|
+
callback_methods :store, :delete, :validate
|
12
|
+
|
13
|
+
attr_reader :asset
|
14
|
+
delegate :add_error, :to => :asset
|
15
|
+
|
16
|
+
class_attribute :extnames
|
17
|
+
|
18
|
+
def self.applies_to(*args)
|
19
|
+
extnames = args.map do |arg|
|
20
|
+
arg = arg.to_s.downcase
|
21
|
+
arg = ".#{arg}" unless arg.starts_with?('.')
|
22
|
+
arg
|
23
|
+
end
|
24
|
+
self.extnames = extnames
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.applies_to_asset?(asset)
|
28
|
+
extnames = self.extnames || []
|
29
|
+
extnames.each do |extname|
|
30
|
+
return true if asset.key.downcase.ends_with?(extname)
|
31
|
+
end
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(asset)
|
36
|
+
unless self.class.applies_to_asset?(asset)
|
37
|
+
raise AssetMismatch, "Instances of #{self.class.name} cannot be applied to asset #{asset.key.inspect}"
|
38
|
+
end
|
39
|
+
@asset = asset
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'class_inheritable_attributes'
|
3
|
+
|
4
|
+
module AssetCloud
|
5
|
+
|
6
|
+
class IllegalPath < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class Base
|
10
|
+
cattr_accessor :logger
|
11
|
+
|
12
|
+
VALID_PATHS = /\A[a-z0-9][a-z0-9_\-\/]+([a-z0-9][\w\-\ \.\@]*\.\w{2,6})?\z/i
|
13
|
+
MATCH_BUCKET = /^(\w+)(\/|$)/
|
14
|
+
|
15
|
+
attr_accessor :url, :root
|
16
|
+
|
17
|
+
class_attribute :root_bucket_class
|
18
|
+
self.root_bucket_class = 'AssetCloud::FileSystemBucket'
|
19
|
+
class_attribute :root_asset_class
|
20
|
+
self.root_asset_class = 'AssetCloud::Asset'
|
21
|
+
|
22
|
+
class_inheritable_hash :bucket_classes
|
23
|
+
self.bucket_classes = {}
|
24
|
+
class_inheritable_hash :asset_classes
|
25
|
+
self.asset_classes = {}
|
26
|
+
class_inheritable_hash :asset_extension_classes
|
27
|
+
self.asset_extension_classes = {}
|
28
|
+
|
29
|
+
def self.bucket(*args)
|
30
|
+
asset_class = if args.last.is_a? Hash
|
31
|
+
convert_to_class_name_if_possible(args.pop[:asset_class])
|
32
|
+
end
|
33
|
+
|
34
|
+
bucket_class = if args.last.is_a? Class
|
35
|
+
convert_to_class_name_if_possible(args.pop)
|
36
|
+
else
|
37
|
+
raise ArgumentError, 'requires a bucket class'
|
38
|
+
end
|
39
|
+
|
40
|
+
if bucket_name = args.first
|
41
|
+
self.bucket_classes[bucket_name.to_sym] = bucket_class
|
42
|
+
self.asset_classes[bucket_name.to_sym] = asset_class if asset_class
|
43
|
+
else
|
44
|
+
self.root_bucket_class = bucket_class
|
45
|
+
self.root_asset_class = asset_class if asset_class
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.asset_extensions(*args)
|
50
|
+
opts = args.last.is_a?(Hash) ? args.pop.slice(:only, :except) : {}
|
51
|
+
opts.each do |k,v|
|
52
|
+
opts[k] = [v].flatten.map(&:to_sym)
|
53
|
+
end
|
54
|
+
|
55
|
+
args.each do |klass|
|
56
|
+
klass = convert_to_class_name_if_possible(klass)
|
57
|
+
self.asset_extension_classes[klass] = opts
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def buckets
|
62
|
+
@buckets ||= Hash.new do |hash, key|
|
63
|
+
if klass = self.class.bucket_classes[key]
|
64
|
+
hash[key] = constantize_if_necessary(klass).new(self, key)
|
65
|
+
else
|
66
|
+
hash[key] = nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize(root, url = '/')
|
72
|
+
@root, @url = root, url
|
73
|
+
end
|
74
|
+
|
75
|
+
def url_for(key, options={})
|
76
|
+
File.join(@url, URI.encode(key))
|
77
|
+
end
|
78
|
+
|
79
|
+
def path_for(key)
|
80
|
+
File.join(path, key)
|
81
|
+
end
|
82
|
+
|
83
|
+
def path
|
84
|
+
root
|
85
|
+
end
|
86
|
+
|
87
|
+
def find(key)
|
88
|
+
asset = asset_at(key)
|
89
|
+
asset.value
|
90
|
+
asset
|
91
|
+
end
|
92
|
+
|
93
|
+
def asset_at(*args)
|
94
|
+
asset_class_for(args.first).at(self, *args)
|
95
|
+
end
|
96
|
+
|
97
|
+
def asset_at!(*args)
|
98
|
+
check_key_for_errors(args.first)
|
99
|
+
asset_at(*args)
|
100
|
+
end
|
101
|
+
|
102
|
+
def move(source, destination)
|
103
|
+
return if source == destination
|
104
|
+
|
105
|
+
object = copy(source, destination)
|
106
|
+
if object.errors.none?
|
107
|
+
asset_at(source).delete
|
108
|
+
end
|
109
|
+
object
|
110
|
+
end
|
111
|
+
|
112
|
+
def copy(source, destination)
|
113
|
+
return if source == destination
|
114
|
+
|
115
|
+
object = build(destination, read(source))
|
116
|
+
object.store
|
117
|
+
object
|
118
|
+
end
|
119
|
+
|
120
|
+
def build(key, value = nil, &block)
|
121
|
+
logger.info { " [#{self.class.name}] Building asset #{key}" } if logger
|
122
|
+
asset_class_for(key).new(self, key, value, Metadata.non_existing, &block)
|
123
|
+
end
|
124
|
+
|
125
|
+
def write(key, value)
|
126
|
+
check_key_for_errors(key)
|
127
|
+
logger.info { " [#{self.class.name}] Writing #{value.size} bytes to #{key}" } if logger
|
128
|
+
|
129
|
+
bucket_for(key).write(key, value)
|
130
|
+
end
|
131
|
+
|
132
|
+
def read(key)
|
133
|
+
logger.info { " [#{self.class.name}] Reading from #{key}" } if logger
|
134
|
+
|
135
|
+
bucket_for(key).read(key)
|
136
|
+
end
|
137
|
+
|
138
|
+
def stat(key)
|
139
|
+
logger.info { " [#{self.class.name}] Statting #{key}" } if logger
|
140
|
+
|
141
|
+
bucket_for(key).stat(key)
|
142
|
+
end
|
143
|
+
|
144
|
+
def ls(key)
|
145
|
+
logger.info { " [#{self.class.name}] Listing objects in #{key}" } if logger
|
146
|
+
|
147
|
+
bucket_for(key).ls(key)
|
148
|
+
end
|
149
|
+
|
150
|
+
def exist?(key)
|
151
|
+
if fp = stat(key)
|
152
|
+
fp.exist?
|
153
|
+
else
|
154
|
+
false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def supports?(key)
|
159
|
+
key =~ VALID_PATHS
|
160
|
+
end
|
161
|
+
|
162
|
+
def delete(key)
|
163
|
+
logger.info { " [#{self.class.name}] Deleting #{key}" } if logger
|
164
|
+
|
165
|
+
bucket_for(key).delete(key)
|
166
|
+
end
|
167
|
+
|
168
|
+
def bucket_for(key)
|
169
|
+
bucket = buckets[bucket_symbol_for_key(key)]
|
170
|
+
bucket ? bucket : root_bucket
|
171
|
+
end
|
172
|
+
|
173
|
+
def []=(key, value)
|
174
|
+
asset = self[key]
|
175
|
+
asset.value = value
|
176
|
+
asset.store
|
177
|
+
end
|
178
|
+
|
179
|
+
def [](key)
|
180
|
+
asset_at!(key)
|
181
|
+
end
|
182
|
+
|
183
|
+
# versioning
|
184
|
+
|
185
|
+
def read_version(key, version)
|
186
|
+
logger.info { " [#{self.class.name}] Reading from #{key} at version #{version}" } if logger
|
187
|
+
bucket_for(key).read_version(key, version)
|
188
|
+
end
|
189
|
+
|
190
|
+
def versions(key)
|
191
|
+
logger.info { " [#{self.class.name}] Getting all versions for #{key}" } if logger
|
192
|
+
bucket_for(key).versions(key)
|
193
|
+
end
|
194
|
+
|
195
|
+
def version_details(key)
|
196
|
+
logger.info { " [#{self.class.name}] Getting all version details for #{key}" } if logger
|
197
|
+
bucket_for(key).version_details(key)
|
198
|
+
end
|
199
|
+
|
200
|
+
def asset_class_for(key)
|
201
|
+
klass = self.class.asset_classes[bucket_symbol_for_key(key)] || self.class.root_asset_class
|
202
|
+
constantize_if_necessary(klass)
|
203
|
+
end
|
204
|
+
|
205
|
+
def asset_extension_classes_for_bucket(bucket)
|
206
|
+
bucket = bucket.to_sym
|
207
|
+
extensions = self.class.asset_extension_classes
|
208
|
+
klasses = extensions.keys.select do |ext|
|
209
|
+
opts = extensions[ext]
|
210
|
+
(opts.key?(:only) ? opts[:only].include?(bucket) : true) &&
|
211
|
+
(opts.key?(:except) ? !opts[:except].include?(bucket) : true)
|
212
|
+
end
|
213
|
+
klasses.map {|klass| constantize_if_necessary(klass)}
|
214
|
+
end
|
215
|
+
|
216
|
+
protected
|
217
|
+
|
218
|
+
def bucket_symbol_for_key(key)
|
219
|
+
$1.to_sym if key =~ MATCH_BUCKET
|
220
|
+
end
|
221
|
+
|
222
|
+
def root_bucket
|
223
|
+
@default_bucket ||= constantize_if_necessary(self.class.root_bucket_class).new(self, '')
|
224
|
+
end
|
225
|
+
|
226
|
+
def constantize_if_necessary(klass)
|
227
|
+
klass.is_a?(Class) ? klass : klass.constantize
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.convert_to_class_name_if_possible(klass)
|
231
|
+
if klass.is_a?(Class) && klass.name.present?
|
232
|
+
klass.name
|
233
|
+
else
|
234
|
+
klass
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def check_key_for_errors(key)
|
239
|
+
raise IllegalPath, "key cannot be empty" if key.blank?
|
240
|
+
raise IllegalPath, "#{key.inspect} contains illegal characters" unless supports?(key)
|
241
|
+
rescue => e
|
242
|
+
logger.info { " [#{self.class.name}] bad key #{e.message}" } if logger
|
243
|
+
raise
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
end
|