attached 0.0.2 → 0.0.3
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/Gemfile +2 -0
- data/README.rdoc +1 -0
- data/lib/attached.rb +11 -5
- data/lib/attached/attachment.rb +62 -19
- data/lib/attached/storage.rb +2 -6
- data/lib/attached/storage/base.rb +1 -3
- data/lib/attached/storage/s3.rb +68 -55
- metadata +17 -5
- data/lib/attached/storage/fs.rb +0 -39
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
data/lib/attached.rb
CHANGED
@@ -36,10 +36,11 @@ module Attached
|
|
36
36
|
#
|
37
37
|
# Usage:
|
38
38
|
#
|
39
|
-
# has_attached :
|
40
|
-
# has_attached :
|
41
|
-
# has_attached :
|
42
|
-
# has_attached :
|
39
|
+
# has_attached :video
|
40
|
+
# has_attached :video, :storage => :s3
|
41
|
+
# has_attached :video, styles => [ :mp4, :ogv ]
|
42
|
+
# has_attached :video, styles => { :main => { :size => "480p", :format => "mp4" } }
|
43
|
+
# has_attached :thumbnail, :range => (1..4)
|
43
44
|
|
44
45
|
def has_attached(name, options = {})
|
45
46
|
|
@@ -73,8 +74,13 @@ module Attached
|
|
73
74
|
self.errors.add(name, message)
|
74
75
|
end
|
75
76
|
|
77
|
+
self.errors[:"#{name}_identifier"].each do |message|
|
78
|
+
self.errors.add(name, message)
|
79
|
+
end
|
80
|
+
|
76
81
|
self.errors.delete(:"#{name}_size")
|
77
82
|
self.errors.delete(:"#{name}_extension")
|
83
|
+
self.errors.delete(:"#{name}_identifier")
|
78
84
|
|
79
85
|
end
|
80
86
|
|
@@ -129,7 +135,7 @@ module Attached
|
|
129
135
|
|
130
136
|
message = options[:message] || "must be attached"
|
131
137
|
|
132
|
-
validates_presence_of :"#{name}
|
138
|
+
validates_presence_of :"#{name}_identifier", :message => message,
|
133
139
|
:if => options[:if], :unless => options[:unless]
|
134
140
|
|
135
141
|
end
|
data/lib/attached/attachment.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'uuid'
|
2
|
+
|
1
3
|
require 'attached/storage'
|
2
4
|
|
3
5
|
module Attached
|
@@ -9,14 +11,12 @@ module Attached
|
|
9
11
|
attr_reader :name
|
10
12
|
attr_reader :instance
|
11
13
|
attr_reader :options
|
12
|
-
attr_reader :storage
|
13
14
|
|
14
15
|
|
15
16
|
def self.options
|
16
17
|
@options ||= {
|
17
|
-
:storage => :
|
18
|
-
:
|
19
|
-
:path => "/:name/:style/:id:extension",
|
18
|
+
:storage => :s3,
|
19
|
+
:path => "/:name/:style/:identifier:extension",
|
20
20
|
:styles => {},
|
21
21
|
}
|
22
22
|
end
|
@@ -32,7 +32,7 @@ module Attached
|
|
32
32
|
# Options:
|
33
33
|
#
|
34
34
|
# * :path - The location where the attachment is stored
|
35
|
-
# * :storage - The storage medium represented as a symbol such as ':s3'
|
35
|
+
# * :storage - The storage medium represented as a symbol such as ':s3'
|
36
36
|
# * :credentials - A file, hash, or path used to authenticate with the specified storage medium
|
37
37
|
|
38
38
|
def initialize(name, instance, options = {})
|
@@ -47,13 +47,14 @@ module Attached
|
|
47
47
|
#
|
48
48
|
# @object.avatar.assign(...)
|
49
49
|
|
50
|
-
def assign(file)
|
51
|
-
@file = file
|
50
|
+
def assign(file, identifier = UUID.generate)
|
51
|
+
@file = file.tempfile
|
52
52
|
|
53
53
|
extension = File.extname(file.original_filename)
|
54
54
|
|
55
55
|
instance_set :size, file.size
|
56
56
|
instance_set :extension, extension
|
57
|
+
instance_set :identifier, identifier
|
57
58
|
end
|
58
59
|
|
59
60
|
|
@@ -64,7 +65,7 @@ module Attached
|
|
64
65
|
def save
|
65
66
|
@storage ||= Attached::Storage.medium(options[:storage], options[:credentials])
|
66
67
|
|
67
|
-
storage.save(self.file, self.path) if self.file
|
68
|
+
@storage.save(self.file, self.path) if self.file and self.path
|
68
69
|
end
|
69
70
|
|
70
71
|
|
@@ -75,10 +76,11 @@ module Attached
|
|
75
76
|
def destroy
|
76
77
|
@storage ||= Attached::Storage.medium(options[:storage], options[:credentials])
|
77
78
|
|
78
|
-
storage.destroy(self.path)
|
79
|
+
@storage.destroy(self.path) if self.path
|
79
80
|
end
|
80
81
|
|
81
|
-
|
82
|
+
# Acesss the URL for an attachment.
|
83
|
+
#
|
82
84
|
# Usage:
|
83
85
|
#
|
84
86
|
# @object.avatar.url
|
@@ -88,11 +90,11 @@ module Attached
|
|
88
90
|
def url(style = :original)
|
89
91
|
@storage ||= Attached::Storage.medium(options[:storage], options[:credentials])
|
90
92
|
|
91
|
-
return "#{
|
93
|
+
return "#{@storage.host}#{path(style)}"
|
92
94
|
end
|
93
95
|
|
94
96
|
|
95
|
-
# Access the
|
97
|
+
# Access the path for an attachment.
|
96
98
|
#
|
97
99
|
# Usage:
|
98
100
|
#
|
@@ -101,12 +103,12 @@ module Attached
|
|
101
103
|
# @object.avatar.url(:large)
|
102
104
|
|
103
105
|
def path(style = :original)
|
104
|
-
path =
|
106
|
+
path = options[:path].clone
|
105
107
|
|
106
|
-
path.gsub!(/:id/, instance.id.to_s)
|
107
108
|
path.gsub!(/:name/, name.to_s)
|
108
109
|
path.gsub!(/:style/, style.to_s)
|
109
110
|
path.gsub!(/:extension/, extension(style).to_s)
|
111
|
+
path.gsub!(/:identifier/, identifier(style).to_s)
|
110
112
|
|
111
113
|
return path
|
112
114
|
end
|
@@ -122,16 +124,57 @@ module Attached
|
|
122
124
|
end
|
123
125
|
|
124
126
|
|
125
|
-
# Access the extension for an attachment.
|
127
|
+
# Access the extension for an attachment. It will first check the styles
|
128
|
+
# to see if one is specified before checking the instance.
|
126
129
|
#
|
127
130
|
# Usage:
|
128
131
|
#
|
129
132
|
# @object.avatar.extension
|
130
133
|
|
131
|
-
def extension(style)
|
132
|
-
|
133
|
-
|
134
|
-
|
134
|
+
def extension(style = nil)
|
135
|
+
style and
|
136
|
+
options[:styles] and
|
137
|
+
options[:styles][style] and
|
138
|
+
options[:styles][style][:extension] or
|
139
|
+
instance_get(:extension)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Access the identifier for an attachment. It will first check the styles
|
143
|
+
# to see if one is specified before checking the instance.
|
144
|
+
#
|
145
|
+
# Usage:
|
146
|
+
#
|
147
|
+
# @object.avatar.identifier
|
148
|
+
|
149
|
+
def identifier(style = nil)
|
150
|
+
style and
|
151
|
+
options[:styles] and
|
152
|
+
options[:styles][style] and
|
153
|
+
options[:styles][style][:identifier] or
|
154
|
+
instance_get(:identifier)
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
# Set the extension for an attachment. It will act independently of the
|
159
|
+
# defined style.
|
160
|
+
#
|
161
|
+
# Usage:
|
162
|
+
#
|
163
|
+
# @object.avatar.extension = ".png"
|
164
|
+
|
165
|
+
def extension=(extension)
|
166
|
+
instance_set(:extension, extension)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Set the identifier for an attachment. It will act independently of the
|
170
|
+
# defined style.
|
171
|
+
#
|
172
|
+
# Usage:
|
173
|
+
#
|
174
|
+
# @object.avatar.identifier = "1234"
|
175
|
+
|
176
|
+
def identifier=(identifier)
|
177
|
+
instance_set(:identifier, identifier)
|
135
178
|
end
|
136
179
|
|
137
180
|
|
data/lib/attached/storage.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'attached/storage/base'
|
2
|
-
require 'attached/storage/fs'
|
3
1
|
require 'attached/storage/s3'
|
4
2
|
|
5
3
|
module Attached
|
@@ -9,13 +7,11 @@ module Attached
|
|
9
7
|
#
|
10
8
|
# Usage:
|
11
9
|
#
|
12
|
-
# Attached::Storage.medium(
|
13
|
-
# Attached::Storage.medium(:s3)
|
10
|
+
# Attached::Storage.medium(s3)
|
14
11
|
|
15
|
-
def self.medium(storage = :
|
12
|
+
def self.medium(storage = :s3, credentials = nil)
|
16
13
|
|
17
14
|
case storage
|
18
|
-
when :fs then return Attached::Storage::FS.new(credentials)
|
19
15
|
when :s3 then return Attached::Storage::S3.new(credentials)
|
20
16
|
end
|
21
17
|
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'attached/storage/base'
|
2
|
-
|
3
1
|
module Attached
|
4
2
|
module Storage
|
5
3
|
class Base
|
@@ -34,7 +32,7 @@ module Attached
|
|
34
32
|
end
|
35
33
|
|
36
34
|
|
37
|
-
# Access the host
|
35
|
+
# Access the host for a storage service or return null if local.
|
38
36
|
#
|
39
37
|
# Usage:
|
40
38
|
#
|
data/lib/attached/storage/s3.rb
CHANGED
@@ -1,42 +1,46 @@
|
|
1
|
+
require 'attached/storage/base'
|
2
|
+
|
1
3
|
require 'aws/s3'
|
2
4
|
|
5
|
+
|
3
6
|
module Attached
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
7
|
+
module Storage
|
8
|
+
class S3 < Base
|
9
|
+
|
10
|
+
|
11
|
+
attr_reader :bucket
|
12
|
+
attr_reader :access_key_id
|
13
|
+
attr_reader :secret_access_key
|
14
|
+
|
15
|
+
|
16
|
+
# Create a new AWS interface supporting save and destroy operations.
|
17
|
+
#
|
18
|
+
# Usage:
|
19
|
+
#
|
20
|
+
# Attached::Storage::S3.new()
|
21
|
+
# Attached::Storage::S3.new("s3.yml")
|
22
|
+
|
23
|
+
def initialize(credentials)
|
24
|
+
credentials = parse(credentials)
|
25
|
+
|
26
|
+
@bucket = credentials[:bucket] || credentials['bucket']
|
27
|
+
@access_key_id = credentials[:access_key_id] || credentials['access_key_id']
|
28
|
+
@secret_access_key = credentials[:secret_access_key] || credentials['secret_access_key']
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Access the host (e.g. bucket.s3.amazonaws.com) for a storage service.
|
33
|
+
#
|
34
|
+
# Usage:
|
35
|
+
#
|
36
|
+
# storage.host
|
37
|
+
|
38
|
+
def host()
|
39
|
+
"https://#{self.bucket}.s3.amazonaws.com"
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Save a file to a given path on AWS S3.
|
40
44
|
#
|
41
45
|
# Parameters:
|
42
46
|
#
|
@@ -45,11 +49,16 @@ module Attached
|
|
45
49
|
|
46
50
|
def save(file, path)
|
47
51
|
connect()
|
48
|
-
|
52
|
+
begin
|
53
|
+
AWS::S3::S3Object.store(path, file, bucket, :access => :authenticated_read)
|
54
|
+
rescue AWS::S3::NoSuchBucket => e
|
55
|
+
AWS::S3::Bucket.create(bucket)
|
56
|
+
retry
|
57
|
+
end
|
49
58
|
end
|
50
59
|
|
51
60
|
|
52
|
-
# Destroy a file at a given path on
|
61
|
+
# Destroy a file at a given path on AWS S3.
|
53
62
|
#
|
54
63
|
# Parameters:
|
55
64
|
#
|
@@ -57,23 +66,27 @@ module Attached
|
|
57
66
|
|
58
67
|
def destroy(path)
|
59
68
|
connect()
|
60
|
-
|
69
|
+
begin
|
70
|
+
AWS::S3::S3Object.delete(path, bucket, :access => :authenticated_read)
|
71
|
+
rescue AWS::S3::NoSuchBucket => e
|
72
|
+
AWS::S3::Bucket.create(bucket)
|
73
|
+
retry
|
74
|
+
end
|
61
75
|
end
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
:access_key_id =>
|
72
|
-
:secret_access_key => self.secret_access_key
|
76
|
+
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
|
81
|
+
# Connect to an AWS S3 server.
|
82
|
+
|
83
|
+
def connect()
|
84
|
+
@connection ||= AWS::S3::Base.establish_connection!(
|
85
|
+
:access_key_id => access_key_id, :secret_access_key => secret_access_key
|
73
86
|
)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
79
92
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Kevin Sylvestre
|
@@ -14,11 +14,11 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-12-
|
17
|
+
date: 2010-12-07 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: uuid
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
@@ -30,6 +30,19 @@ dependencies:
|
|
30
30
|
version: "0"
|
31
31
|
type: :runtime
|
32
32
|
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: aws-s3
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :runtime
|
45
|
+
version_requirements: *id002
|
33
46
|
description: Attached is a Ruby on Rails cloud attachment and processor library inspired by Paperclip. Attached lets users push files to the cloud, then perform remote processing on the files.
|
34
47
|
email:
|
35
48
|
- kevin@ksylvest.com
|
@@ -43,7 +56,6 @@ files:
|
|
43
56
|
- lib/attached/attachment.rb
|
44
57
|
- lib/attached/railtie.rb
|
45
58
|
- lib/attached/storage/base.rb
|
46
|
-
- lib/attached/storage/fs.rb
|
47
59
|
- lib/attached/storage/s3.rb
|
48
60
|
- lib/attached/storage.rb
|
49
61
|
- lib/attached.rb
|
data/lib/attached/storage/fs.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
module Attached
|
2
|
-
module Storage
|
3
|
-
class FS < Base
|
4
|
-
|
5
|
-
|
6
|
-
# Create a new file system storage interface supporting save and destroy operations.
|
7
|
-
#
|
8
|
-
# Usage:
|
9
|
-
#
|
10
|
-
# FS.new()
|
11
|
-
|
12
|
-
def initialize(credentials = nil)
|
13
|
-
credentials = parse(credentials)
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
# Save a file to a given path on a file system.
|
18
|
-
#
|
19
|
-
# Parameters:
|
20
|
-
#
|
21
|
-
# * file - The file to save.
|
22
|
-
# * path - The path to save.
|
23
|
-
|
24
|
-
def save(file, path)
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
# Destroy a file at a given path on a file system.
|
29
|
-
#
|
30
|
-
# Parameters:
|
31
|
-
#
|
32
|
-
# * path - The path to destroy.
|
33
|
-
|
34
|
-
def destroy(path)
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|