quick_file 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+
6
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in quick_file.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,260 @@
1
+ module QuickFile
2
+
3
+ module Upload
4
+ extend ActiveSupport::Concern
5
+
6
+ ##
7
+ # required fields: id, state, original_filename, styles
8
+ # steps
9
+ # 1. cache
10
+ # 2. process
11
+ # 3. store
12
+
13
+ STATES = {:loaded => 0, :cached => 1, :processing => 2, :processed => 3, :storing => 4, :stored => 5, :deleted => 6, :error => 7}
14
+
15
+ included do
16
+ cattr_accessor :processes
17
+ self.processes = {}
18
+
19
+ end
20
+
21
+ module ClassMethods
22
+ def add_style(style_name, blk)
23
+ processes[style_name.to_s] = {:blk => blk}
24
+ end
25
+ end
26
+
27
+ module InstanceMethods
28
+ def initialize
29
+ @file = nil
30
+ styles = {}
31
+ errors = []
32
+ end
33
+
34
+ def uploaded_file=(uf)
35
+ errors = []
36
+ @file = uf
37
+ self.original_filename = uf.original_filename
38
+ self.state = STATES[:loaded]
39
+ cache!
40
+ end
41
+
42
+ def loaded?
43
+ self.state == STATES[:loaded]
44
+ end
45
+
46
+ def cached?
47
+ self.state == STATES[:cached]
48
+ end
49
+
50
+ def processed?
51
+ self.state == STATES[:processed]
52
+ end
53
+
54
+ def stored?
55
+ self.state == STATES[:stored]
56
+ end
57
+
58
+ def error?
59
+ self.state == STATES[:error]
60
+ end
61
+
62
+ def sanitized_basename
63
+ File.basename original_filename.gsub( /[^a-zA-Z0-9_\-\.]/, '_'), File.extname(original_filename)
64
+ end
65
+
66
+ def sanitized_filename
67
+ File.basename original_filename.gsub( /[^a-zA-Z0-9_\-\.]/, '_')
68
+ end
69
+
70
+ def extension
71
+ File.extname(original_filename)
72
+ end
73
+
74
+ def cache!
75
+ return unless loaded?
76
+ cp = save_cache_file(QuickFile.generate_cache_name(extension), @file)
77
+ styles["original"] = {"cache" => cp,
78
+ "ct" => QuickFile.content_type_for(cp),
79
+ "sz" => File.size(cp)}
80
+ self.validate!
81
+ if errors.size > 0
82
+ self.state = STATES[:error]
83
+ File.delete(cp)
84
+ else
85
+ self.state = STATES[:cached]
86
+ end
87
+ self.save
88
+ end
89
+
90
+ def save_cache_file(cn, file)
91
+ Dir.mkdir QuickFile::CACHE_DIR unless File.directory?(QuickFile::CACHE_DIR)
92
+ cp = QuickFile.cache_path(cn)
93
+ File.open(cp, "wb") { |f| f.write(file.read) }
94
+ cp
95
+ end
96
+
97
+ def process!
98
+ return unless cached?
99
+ self.state = STATES[:processing]
100
+ save
101
+ begin
102
+ puts "#{processes.size} processes"
103
+ processes.each do |style_name, opts|
104
+ puts "Processing #{style_name}..."
105
+ fn = opts[:blk].call(styles["original"]["cache"])
106
+ unless fn.nil?
107
+ if (styles.key?(style_name) && !styles[style_name]["cache"].nil?)
108
+ File.delete(styles[style_name]["cache"])
109
+ end
110
+ styles[style_name.to_s] = {"cache" => fn,
111
+ "ct" => QuickFile.content_type_for(fn),
112
+ "sz" => File.size(fn)}
113
+ end
114
+ end
115
+ self.state = STATES[:processed]
116
+ rescue StandardError => e
117
+ puts e.message
118
+ self.errors << "PROCESS: #{e.message}"
119
+ self.state = STATES[:error]
120
+ end
121
+
122
+ self.save
123
+
124
+ store!
125
+ end
126
+
127
+ def reprocess!
128
+ return unless (stored? || error?)
129
+ # download original file
130
+ cp = QuickFile.new_cache_file File.extname(self.path)
131
+ QuickFile.download(url, cp)
132
+ styles["original"] = {"cache" => cp,
133
+ "ct" => QuickFile.content_type_for(cp),
134
+ "sz" => File.size(cp)}
135
+ self.state = STATES[:cached]
136
+ self.save
137
+ self.process!
138
+ end
139
+
140
+ def add_file!(style_name, path)
141
+ styles[style_name.to_s] = {"path" => path}
142
+ get_style(style_name.to_s)
143
+ self.state = STATES[:stored]
144
+ save
145
+ end
146
+
147
+ def path(style_name=nil)
148
+ style_name ||= :original
149
+ styles[style_name.to_s]["path"]
150
+ end
151
+
152
+ def content_type(style_name=nil)
153
+ style_name ||= :original
154
+ styles[style_name.to_s]["ct"]
155
+ end
156
+
157
+ def size(style_name=nil)
158
+ style_name ||= :original
159
+ styles[style_name.to_s]["sz"]
160
+ end
161
+
162
+ def is_image?(style_name=nil)
163
+ style_name ||= :original
164
+ return false if content_type(style_name).nil?
165
+ content_type(style_name).include? "image"
166
+ end
167
+
168
+ def is_video?(style_name=nil)
169
+ style_name ||= :original
170
+ fp = styles[style_name]["path"] || styles[style_name]["cache"]
171
+ QuickFile.is_video_file? fp
172
+ end
173
+
174
+ def style_exists?(style_name)
175
+ !styles[style_name.to_s].nil?
176
+ end
177
+
178
+ def store!
179
+ return unless processed?
180
+ begin
181
+ styles.keys.each do |style_name|
182
+ store_style! style_name unless styles[style_name]["cache"].nil?
183
+ end
184
+ self.state = STATES[:stored]
185
+ rescue StandardError => e
186
+ puts e.message
187
+ self.errors << "STORE: #{e.message}"
188
+ if self.errors.count < 5
189
+ self.store!
190
+ else
191
+ self.state = STATES[:error]
192
+ end
193
+ end
194
+ save
195
+ end
196
+
197
+ def storage_protocol
198
+ case storage_type.to_sym
199
+ when :s3
200
+ return :fog
201
+ when :fog
202
+ return :fog
203
+ end
204
+ end
205
+
206
+ def store_style!(style_name)
207
+ fn = styles[style_name]["cache"]
208
+ sp = storage_path(style_name, File.extname(fn))
209
+ if storage_protocol == :fog
210
+ QuickFile.fog_directory.files.create({
211
+ :body => File.open(fn).read,
212
+ :content_type => QuickFile.content_type_for(fn),
213
+ :key => sp,
214
+ :public => QuickFile.options[:fog_public]
215
+ })
216
+ end
217
+ styles[style_name]["path"] = sp
218
+ styles[style_name]["ct"] = QuickFile.content_type_for(fn)
219
+ styles[style_name]["sz"] = File.size(fn)
220
+ styles[style_name].delete("cache")
221
+ File.delete(fn)
222
+
223
+ save
224
+ end
225
+
226
+ def get_style(style_name)
227
+ fn = path(style_name)
228
+ if storage_protocol == :fog
229
+ f = QuickFile.fog_directory.files.get(fn)
230
+ if f.nil?
231
+ styles[style_name]["ct"] = QuickFile.content_type_for(fn)
232
+ styles[style_name]["sz"] = 0
233
+ else
234
+ styles[style_name]["ct"] = f.content_type.nil? ? QuickFile.content_type_for(fn) : f.content_type
235
+ styles[style_name]["sz"] = f.content_length
236
+ end
237
+ save
238
+ end
239
+ f
240
+ end
241
+
242
+ def url(style_name=nil, opts={:secure=>true})
243
+ proto = opts[:secure] ? "https://" : "http://"
244
+ style_name ||= "original"
245
+ return default_url(style_name) unless (styles[style_name] && styles[style_name]["path"])
246
+ "#{proto}#{QuickFile.host_url[storage_type.to_sym]}#{styles[style_name]["path"]}"
247
+ end
248
+
249
+ def delete
250
+ # delete uploaded files
251
+ styles.each do |k,v|
252
+ QuickFile.fog_directory.files.new(:key => v["path"]).destroy if v["path"]
253
+ File.delete(v["cache"]) if (v["cache"] && File.exists?(v["cache"]))
254
+ end
255
+ self.state = STATES[:deleted]
256
+ end
257
+ end
258
+
259
+ end
260
+ end
@@ -0,0 +1,3 @@
1
+ module QuickFile
2
+ VERSION = "0.0.1"
3
+ end
data/lib/quick_file.rb ADDED
@@ -0,0 +1,94 @@
1
+ require "quick_file/version"
2
+ require "quick_file/upload"
3
+ require "RMagick"
4
+
5
+ module QuickFile
6
+ CACHE_DIR = "/tmp"
7
+
8
+ class << self
9
+ def configure
10
+ yield options if block_given?
11
+ end
12
+
13
+ def options
14
+ @@options ||= UPLOAD_OPTIONS
15
+ end
16
+
17
+ def generate_cache_name(ext)
18
+ "#{SecureRandom.hex(5)}#{ext}"
19
+ end
20
+
21
+ def new_cache_file(ext)
22
+ QuickFile.cache_path QuickFile.generate_cache_name(ext)
23
+ end
24
+
25
+ def content_type_for(filename)
26
+ mime = MIME::Types.type_for(filename)[0]
27
+ return mime.simplified if mime
28
+ return "application/file"
29
+ end
30
+
31
+ def is_video_file?(filename)
32
+ filename.downcase.end_with?('.mov', '.3gp', '.wmv', '.m4v', '.mp4')
33
+ end
34
+
35
+ def fog_connection
36
+ @@fog_connection ||= begin
37
+ Fog::Storage.new(options[:fog_credentials])
38
+ end
39
+ end
40
+
41
+ def fog_directory
42
+ @@fog_directory ||= begin
43
+ fog_connection.directories.new(
44
+ :key => options[:fog_directory],
45
+ :public => options[:fog_public]
46
+ )
47
+ end
48
+ end
49
+
50
+ def host_url
51
+ @@host_url ||= begin
52
+ {
53
+ :s3 => "s3.amazonaws.com/#{options[:fog_directory]}/"
54
+ }
55
+ end
56
+ end
57
+
58
+ def resize_to_fit(file, x, y)
59
+ img = Magick::Image.read(file).first
60
+ nim = img.resize_to_fit x, y
61
+ outfile = cache_path(generate_cache_name(File.extname(file)))
62
+ nim.write outfile
63
+ outfile
64
+ end
65
+
66
+ def resize_to_fill(file, x, y)
67
+ img = Magick::Image.read(file).first
68
+ nim = img.resize_to_fill(x, y)
69
+ outfile = cache_path(generate_cache_name(File.extname(file)))
70
+ nim.write outfile
71
+ outfile
72
+ end
73
+
74
+ def cache_path(cn)
75
+ File.join(CACHE_DIR, cn)
76
+ end
77
+
78
+ def download(url, to)
79
+ out = open(to, "wb")
80
+ out.write(open(url).read)
81
+ out.close
82
+ end
83
+
84
+ def image_from_url(url)
85
+ open(url, 'rb') do |f|
86
+ image = Magick::Image.from_blob(f.read).first
87
+ end
88
+ image
89
+ end
90
+
91
+
92
+ end
93
+
94
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "quick_file/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "quick_file"
7
+ s.version = QuickFile::VERSION
8
+ s.authors = ["Alan Graham"]
9
+ s.email = ["alangraham5@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{A file upload library}
12
+ s.description = %q{A file upload library for use with MongoDB and S3}
13
+
14
+ s.rubyforge_project = "quick_file"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "rmagick"
22
+ s.add_dependency "fog"
23
+ s.add_dependency "active_support"
24
+
25
+ # specify any dependencies here; for example:
26
+ # s.add_development_dependency "rspec"
27
+ # s.add_runtime_dependency "rest-client"
28
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: quick_file
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alan Graham
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-27 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rmagick
16
+ requirement: &70310906168420 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70310906168420
25
+ - !ruby/object:Gem::Dependency
26
+ name: fog
27
+ requirement: &70310906168000 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70310906168000
36
+ - !ruby/object:Gem::Dependency
37
+ name: active_support
38
+ requirement: &70310906167580 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70310906167580
47
+ description: A file upload library for use with MongoDB and S3
48
+ email:
49
+ - alangraham5@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - Rakefile
57
+ - lib/quick_file.rb
58
+ - lib/quick_file/upload.rb
59
+ - lib/quick_file/version.rb
60
+ - quick_file.gemspec
61
+ homepage: ''
62
+ licenses: []
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project: quick_file
81
+ rubygems_version: 1.8.15
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: A file upload library
85
+ test_files: []