quick_file 0.0.1

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/.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: []