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 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