attached 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,4 +2,6 @@ source 'http://rubygems.org'
2
2
 
3
3
  gem 'rails'
4
4
 
5
+ gem 'uuid'
6
+
5
7
  gem 'aws-s3', :require => 'aws/s3'
data/README.rdoc CHANGED
@@ -13,6 +13,7 @@ Migration:
13
13
  class CreateVideo < ActiveRecord::Migration
14
14
  def self.up
15
15
  create_table :videos do |t|
16
+ t.string :video_identifier
16
17
  t.string :video_extension
17
18
  t.integer :video_size
18
19
 
data/lib/attached.rb CHANGED
@@ -36,10 +36,11 @@ module Attached
36
36
  #
37
37
  # Usage:
38
38
  #
39
- # has_attached :avatar
40
- # has_attached :avatar, :storage => :s3
41
- # has_attached :clip, styles => [ :mp4, :ogv ]
42
- # has_attached :clip, styles => { :main => { :size => "480p", :format => "mp4" } }
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}_extension", :message => message,
138
+ validates_presence_of :"#{name}_identifier", :message => message,
133
139
  :if => options[:if], :unless => options[:unless]
134
140
 
135
141
  end
@@ -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 => :fs,
18
- :protocol => 'http',
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' or ':fs'
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 "#{options[:protocol]}://#{@storage.host}#{path(style)}"
93
+ return "#{@storage.host}#{path(style)}"
92
94
  end
93
95
 
94
96
 
95
- # Access the URL
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 = String.new(options[: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
- return options[:styles][style][:extension] if style and options[:styles][style]
133
-
134
- return instance_get(:extension)
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
 
@@ -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(:fs)
13
- # Attached::Storage.medium(:s3)
10
+ # Attached::Storage.medium(s3)
14
11
 
15
- def self.medium(storage = :fs, credentials = nil)
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 (e.g. localhost:3000) for a storage service.
35
+ # Access the host for a storage service or return null if local.
38
36
  #
39
37
  # Usage:
40
38
  #
@@ -1,42 +1,46 @@
1
+ require 'attached/storage/base'
2
+
1
3
  require 'aws/s3'
2
4
 
5
+
3
6
  module Attached
4
- module Storage
5
- class S3 < Base
6
-
7
- attr_reader :bucket
8
- attr_reader :access_key_id
9
- attr_reader :secret_access_key
10
-
11
-
12
- # Create a new Amazon S3 storage interface supporting save and destroy operations.
13
- #
14
- # Usage:
15
- #
16
- # Attached::Storage::S3.new()
17
- # Attached::Storage::S3.new("#{Rails.root}/config/s3.yml")
18
-
19
- def initialize(credentials = "#{Rails.root}/config/s3.yml")
20
- credentials = parse(credentials)
21
-
22
- @bucket = credentials[:bucket] || credentials['bucket']
23
- @access_key_id = credentials[:access_key_id] || credentials['access_key_id']
24
- @secret_access_key = credentials[:secret_access_key] || credentials['secret_access_key']
25
- end
26
-
27
-
28
- # Access the host (e.g. bucket.s3.amazonaws.com) for a storage service.
29
- #
30
- # Usage:
31
- #
32
- # storage.host
33
-
34
- def host()
35
- "#{self.bucket}.s3.amazonaws.com"
36
- end
37
-
38
-
39
- # Save a file to a given path on Amazon S3.
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
- AWS::S3::S3Object.store(path, file, bucket)
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 Amazon S3.
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
- AWS::S3::S3Object.delete(path, bucket)
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
- private
65
-
66
-
67
- # Connect to an Amazon S3 server.
68
-
69
- def connect
70
- return AWS::S3::Base.establish_connection!(
71
- :access_key_id => self.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
- end
75
-
76
-
77
- end
78
- end
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
- - 2
9
- version: 0.0.2
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-02 00:00:00 -05:00
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: aws-s3
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
@@ -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