inline_uploader 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Matt Freels
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = inline_uploader
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 Matt Freels. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "inline_uploader"
8
+ gem.summary = %Q{rack endpoint and handler for ajax uploads}
9
+ gem.description = %Q{Provides an upload endpoint for ajax uploads and easy attachement of ajax uploads as POST params for normal requests.}
10
+ gem.email = "matt@freels.name"
11
+ gem.homepage = "http://github.com/freels/inline_uploader"
12
+ gem.authors = ["Matt Freels"]
13
+ #gem.add_development_dependency "thoughtbot-shoulda"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/*_test.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION')
47
+ version = File.read('VERSION')
48
+ else
49
+ version = ""
50
+ end
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "inline_uploader #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,201 @@
1
+
2
+ jQuery.extend({
3
+
4
+
5
+ createUploadIframe: function(id, uri)
6
+ {
7
+ //create frame
8
+ var frameId = 'jUploadFrame' + id;
9
+
10
+ if(window.ActiveXObject) {
11
+ var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
12
+ if(typeof uri== 'boolean'){
13
+ io.src = 'javascript:false';
14
+ }
15
+ else if(typeof uri== 'string'){
16
+ io.src = uri;
17
+ }
18
+ }
19
+ else {
20
+ var io = document.createElement('iframe');
21
+ io.id = frameId;
22
+ io.name = frameId;
23
+ }
24
+ io.style.position = 'absolute';
25
+ io.style.top = '-1000px';
26
+ io.style.left = '-1000px';
27
+
28
+ document.body.appendChild(io);
29
+
30
+ return io
31
+ },
32
+ createUploadForm: function(id, fileElementId)
33
+ {
34
+ //create form
35
+ var formId = 'jUploadForm' + id;
36
+ var fileId = 'jUploadFile' + id;
37
+ var form = $('<form action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');
38
+ var oldElement = $('#' + fileElementId);
39
+ var newElement = $(oldElement).clone();
40
+ $(oldElement).attr('id', fileId);
41
+ $(oldElement).before(newElement);
42
+ $(oldElement).appendTo(form);
43
+ //set attributes
44
+ $(form).css('position', 'absolute');
45
+ $(form).css('top', '-1200px');
46
+ $(form).css('left', '-1200px');
47
+ $(form).appendTo('body');
48
+ return form;
49
+ },
50
+
51
+ ajaxFileUpload: function(s) {
52
+ // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
53
+ s = jQuery.extend({}, jQuery.ajaxSettings, s);
54
+ var id = new Date().getTime()
55
+ var form = jQuery.createUploadForm(id, s.fileElementId);
56
+ var io = jQuery.createUploadIframe(id, s.secureuri);
57
+ var frameId = 'jUploadFrame' + id;
58
+ var formId = 'jUploadForm' + id;
59
+ // Watch for a new set of requests
60
+ if ( s.global && ! jQuery.active++ )
61
+ {
62
+ jQuery.event.trigger( "ajaxStart" );
63
+ }
64
+ var requestDone = false;
65
+ // Create the request object
66
+ var xml = {}
67
+ if ( s.global )
68
+ jQuery.event.trigger("ajaxSend", [xml, s]);
69
+ // Wait for a response to come back
70
+ var uploadCallback = function(isTimeout)
71
+ {
72
+ var io = document.getElementById(frameId);
73
+ try
74
+ {
75
+ if(io.contentWindow)
76
+ {
77
+ xml.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;
78
+ xml.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
79
+
80
+ }else if(io.contentDocument)
81
+ {
82
+ xml.responseText = io.contentDocument.document.body?io.contentDocument.document.body.innerHTML:null;
83
+ xml.responseXML = io.contentDocument.document.XMLDocument?io.contentDocument.document.XMLDocument:io.contentDocument.document;
84
+ }
85
+ }catch(e)
86
+ {
87
+ jQuery.handleError(s, xml, null, e);
88
+ }
89
+ if ( xml || isTimeout == "timeout")
90
+ {
91
+ requestDone = true;
92
+ var status;
93
+ try {
94
+ status = isTimeout != "timeout" ? "success" : "error";
95
+ // Make sure that the request was successful or notmodified
96
+ if ( status != "error" )
97
+ {
98
+ // process the data (runs the xml through httpData regardless of callback)
99
+ var data = jQuery.uploadHttpData( xml, s.dataType );
100
+ // If a local callback was specified, fire it and pass it the data
101
+ if ( s.success )
102
+ s.success( data, status );
103
+
104
+ // Fire the global callback
105
+ if( s.global )
106
+ jQuery.event.trigger( "ajaxSuccess", [xml, s] );
107
+ } else
108
+ jQuery.handleError(s, xml, status);
109
+ } catch(e)
110
+ {
111
+ status = "error";
112
+ jQuery.handleError(s, xml, status, e);
113
+ }
114
+
115
+ // The request was completed
116
+ if( s.global )
117
+ jQuery.event.trigger( "ajaxComplete", [xml, s] );
118
+
119
+ // Handle the global AJAX counter
120
+ if ( s.global && ! --jQuery.active )
121
+ jQuery.event.trigger( "ajaxStop" );
122
+
123
+ // Process result
124
+ if ( s.complete )
125
+ s.complete(xml, status);
126
+
127
+ jQuery(io).unbind()
128
+
129
+ setTimeout(function()
130
+ { try
131
+ {
132
+ $(io).remove();
133
+ $(form).remove();
134
+
135
+ } catch(e)
136
+ {
137
+ jQuery.handleError(s, xml, null, e);
138
+ }
139
+
140
+ }, 100)
141
+
142
+ xml = null
143
+
144
+ }
145
+ }
146
+ // Timeout checker
147
+ if ( s.timeout > 0 )
148
+ {
149
+ setTimeout(function(){
150
+ // Check to see if the request is still happening
151
+ if( !requestDone ) uploadCallback( "timeout" );
152
+ }, s.timeout);
153
+ }
154
+ try
155
+ {
156
+ // var io = $('#' + frameId);
157
+ var form = $('#' + formId);
158
+ $(form).attr('action', s.url);
159
+ $(form).attr('method', 'POST');
160
+ $(form).attr('target', frameId);
161
+ if(form.encoding)
162
+ {
163
+ form.encoding = 'multipart/form-data';
164
+ }
165
+ else
166
+ {
167
+ form.enctype = 'multipart/form-data';
168
+ }
169
+ $(form).submit();
170
+
171
+ } catch(e)
172
+ {
173
+ jQuery.handleError(s, xml, null, e);
174
+ }
175
+ if(window.attachEvent){
176
+ document.getElementById(frameId).attachEvent('onload', uploadCallback);
177
+ }
178
+ else{
179
+ document.getElementById(frameId).addEventListener('load', uploadCallback, false);
180
+ }
181
+ return {abort: function () {}};
182
+
183
+ },
184
+
185
+ uploadHttpData: function( r, type ) {
186
+ var data = !type;
187
+ data = type == "xml" || data ? r.responseXML : r.responseText;
188
+ // If the type is "script", eval it in global context
189
+ if ( type == "script" )
190
+ jQuery.globalEval( data );
191
+ // Get the JavaScript object, if JSON is used.
192
+ if ( type == "json" )
193
+ eval( "data = " + data );
194
+ // evaluate scripts within html
195
+ if ( type == "html" )
196
+ jQuery("<div>").html(data).evalScripts();
197
+ //alert($('param', data).each(function(){alert($(this).attr('value'));}));
198
+ return data;
199
+ }
200
+ })
201
+
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'inline_uploader'
4
+ require 'fileutils'
5
+
6
+ save_dir = File.join(Dir.pwd, 'public/uploads')
7
+ FileUtils.mkdir_p save_dir
8
+
9
+ # teh app
10
+
11
+ set :port, 3000
12
+ enable :show_exceptions
13
+
14
+ use InlineUploader
15
+
16
+ helpers do
17
+ include InlineUploader::Helpers
18
+ end
19
+
20
+ get '/' do
21
+ entries = Dir[File.join(save_dir, '*')]
22
+ haml :index, :locals => {:entries => entries}
23
+ end
24
+
25
+ post '/save_upload' do
26
+ upload = params[:upload]
27
+ path = File.join(save_dir, File.basename(upload[:filename]))
28
+ FileUtils.move(upload[:tempfile].path, path)
29
+
30
+ redirect '/'
31
+ end
32
+
33
+ template :index do
34
+ <<-end_haml
35
+ !!! Strict
36
+ %html
37
+ %head
38
+ %script(type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js")
39
+ %script(type="text/javascript" src="/ajaxfileupload.js")
40
+ :javascript
41
+ $(function(){
42
+ $('#submit_upload').click(function(){
43
+ $.ajaxFileUpload({
44
+ url : '/inline_upload',
45
+ fileElementId : 'upload_field',
46
+ success : function(){
47
+ $('#upload_container').text('File uploaded.');
48
+ }
49
+ });
50
+
51
+ $('#upload_container').text('Uploading...');
52
+
53
+ return false;
54
+ });
55
+ });
56
+
57
+ %body
58
+ %h1 uploads
59
+ %ul
60
+ - entries.each do |entry|
61
+ %li= entry
62
+
63
+ %h2 Add something new:
64
+
65
+ %form(method="POST" action="/save_upload")
66
+ %input(type="hidden" name="has_inline_uploads" value="1")
67
+ %input(type="hidden" name="upload" value="\#{inline_upload_tag}")
68
+
69
+ %div#upload_container
70
+ %input(type="file" id="upload_field" name="\#{inline_upload_tag}")
71
+ %a#submit_upload(href="#") upload file
72
+
73
+ %div
74
+ %input(type="submit" value="Save")
75
+ end_haml
76
+ end
77
+
@@ -0,0 +1,53 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{inline_uploader}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Matt Freels"]
12
+ s.date = %q{2009-10-10}
13
+ s.description = %q{Provides an upload endpoint for ajax uploads and easy attachement of ajax uploads as POST params for normal requests.}
14
+ s.email = %q{matt@freels.name}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "examples/uploader/public/ajaxfileupload.js",
27
+ "examples/uploader/uploader.rb",
28
+ "inline_uploader.gemspec",
29
+ "lib/inline_uploader.rb",
30
+ "test/inline_uploader_test.rb",
31
+ "test/test_helper.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/freels/inline_uploader}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.4}
37
+ s.summary = %q{rack endpoint and handler for ajax uploads}
38
+ s.test_files = [
39
+ "test/inline_uploader_test.rb",
40
+ "test/test_helper.rb",
41
+ "examples/uploader/uploader.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ else
50
+ end
51
+ else
52
+ end
53
+ end
@@ -0,0 +1,186 @@
1
+ require 'digest/md5'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require 'logger'
5
+ require 'yaml'
6
+
7
+ class InlineUploader
8
+
9
+ # 32 is the length of an md5 hex digest
10
+ TAG_PREFIX = 'inline_upload_'.freeze
11
+ TAG_REGEXP = /\A#{TAG_PREFIX}[a-z0-9]{32,32}\Z/i.freeze
12
+ TAG_LENGTH = TAG_PREFIX.length + 32
13
+
14
+ DEFAULT_ENDPOINT = '/inline_upload'
15
+ DEFAULT_UPLOAD_FLAG = 'has_inline_uploads'
16
+
17
+ def self.new(inner_app, options = {})
18
+ uploader = allocate
19
+ uploader.send :initialize, options
20
+
21
+ Rack::Builder.app do
22
+ map uploader.endpoint do
23
+ run InlineUploader::EndPoint.new(uploader)
24
+ end
25
+
26
+ map '/' do
27
+ use InlineUploader::Attacher, uploader
28
+ run inner_app
29
+ end
30
+ end
31
+ end
32
+
33
+ attr_reader :upload_dir, :endpoint, :flag_param, :logger
34
+
35
+ def initialize(options)
36
+ @upload_dir = options[:tmp_dir] || File.join(Dir.tmpdir, "inline_uploader_#{Process.pid}#{Time.now.to_i}")
37
+ @flag_param = options[:upload_flag] || DEFAULT_UPLOAD_FLAG
38
+ @endpoint = options[:endpoint] || DEFAULT_ENDPOINT
39
+ @logger = options[:logger] || Logger.new($stderr)
40
+
41
+ FileUtils.mkdir_p(upload_dir)
42
+ end
43
+
44
+ class Attacher
45
+ def initialize(app, delegate)
46
+ @delegate = delegate
47
+ @app = app
48
+ end
49
+
50
+ def call(env)
51
+ request = Rack::Request.new(env)
52
+
53
+ # we have to look at both get and post as if Rack::Request.params
54
+ # has been called, GET will also contain all POST params. go figure.
55
+ attach_previous_uploads request.GET, request.POST do
56
+ return @app.call(env)
57
+ end
58
+ end
59
+
60
+ def attach_previous_uploads(*hashes)
61
+ return yield unless hashes.inject(false) {|others,h| !h.delete(@delegate.flag_param).nil? or others }
62
+
63
+ logger.info "Attaching Inline Upload Tags."
64
+ fds = {}
65
+
66
+ begin
67
+ hashes.each {|h| fds.merge! attach_fds(h) }
68
+
69
+ yield
70
+
71
+ ensure
72
+ fds.values.each do |f|
73
+ f.close unless f.closed?
74
+ FileUtils.rm(f.path) if File.exist?(f.path)
75
+ end
76
+ end
77
+ end
78
+
79
+ def attach_fds(params)
80
+ params.inject({}) do |fds, kv|
81
+ key, val = kv
82
+
83
+ if val.is_a? Hash
84
+ fds.merge! attach_fds(params[key])
85
+
86
+ elsif tag = get_valid_tag(val)
87
+ upload = File.join(@delegate.upload_dir, tag)
88
+
89
+ if File.exist?(upload)
90
+ file = YAML.load_file("#{upload}_meta")
91
+ file[:name] = key
92
+ file[:tempfile] = File.open(upload, 'rb')
93
+
94
+ params[key] = file
95
+ fds[tag] = file[:tempfile]
96
+
97
+ logger.info "attaching upload file at #{upload}"
98
+ else
99
+ params[key] = nil
100
+
101
+ logger.info "no upload file found at #{upload}"
102
+ end
103
+ end
104
+
105
+ fds
106
+ end
107
+ end
108
+
109
+ # returns a valid tag or nil.
110
+ def get_valid_tag(val)
111
+ val if val.is_a? String and val.length == TAG_LENGTH and val =~ TAG_REGEXP
112
+ end
113
+
114
+ def logger
115
+ @delegate.logger
116
+ end
117
+ end
118
+
119
+ class EndPoint
120
+ HEADERS = {'Content-Type' => 'text/plain'}
121
+ SUCCESS = [200, HEADERS, ['success']]
122
+ BAD_REQUEST = [400, HEADERS, ['bad request']]
123
+ ERROR = [500, HEADERS, ['error']]
124
+
125
+ class BadRequestError < StandardError; end
126
+
127
+ def initialize(delegate)
128
+ @delegate = delegate
129
+ end
130
+
131
+ def call(env)
132
+ request = Rack::Request.new(env)
133
+ raise BadRequestError unless valid_request? request
134
+
135
+ tag = request.params.keys.detect {|k| valid_tag? k }
136
+ raise BadRequestError unless tag
137
+
138
+ file = request.params[tag]
139
+ raise BadRequestError unless file.member?(:tempfile)
140
+
141
+ p request.params
142
+
143
+ upload = File.join(@delegate.upload_dir, tag)
144
+
145
+ FileUtils.mkdir_p @delegate.upload_dir
146
+ FileUtils.move file[:tempfile].path, upload
147
+
148
+ File.open("#{upload}_meta", 'w') do |f|
149
+ file.delete(:tempfile)
150
+ f.puts file.to_yaml
151
+ end
152
+
153
+ SUCCESS
154
+ rescue BadRequestError
155
+ BAD_REQUEST
156
+ rescue Exception => e
157
+ logger.error e.inspect
158
+ logger.error e.backtrace.join("\n")
159
+ ERROR
160
+ end
161
+
162
+ private
163
+
164
+ def valid_request?(req)
165
+ req.post? and req.content_type =~ %r|multipart/form-data|
166
+ end
167
+
168
+ def valid_tag?(tag)
169
+ tag.is_a? String and tag.length == TAG_LENGTH and tag =~ TAG_REGEXP
170
+ end
171
+
172
+ def logger
173
+ @delegate.logger
174
+ end
175
+ end
176
+
177
+ module Helpers
178
+ def inline_upload_tag(label = :default)
179
+ @inline_upload_tags ||= {}
180
+ @inline_upload_tags[label] ||= begin
181
+ hsh = Digest::MD5.hexdigest("I like turtles! #{rand} #{Time.now.to_f}")
182
+ "#{TAG_PREFIX}#{hsh}"
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class InlineUploaderTest < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'inline_uploader'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: inline_uploader
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Freels
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-10 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Provides an upload endpoint for ajax uploads and easy attachement of ajax uploads as POST params for normal requests.
17
+ email: matt@freels.name
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - .document
27
+ - .gitignore
28
+ - LICENSE
29
+ - README.rdoc
30
+ - Rakefile
31
+ - VERSION
32
+ - examples/uploader/public/ajaxfileupload.js
33
+ - examples/uploader/uploader.rb
34
+ - inline_uploader.gemspec
35
+ - lib/inline_uploader.rb
36
+ - test/inline_uploader_test.rb
37
+ - test/test_helper.rb
38
+ has_rdoc: true
39
+ homepage: http://github.com/freels/inline_uploader
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --charset=UTF-8
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.3.4
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: rack endpoint and handler for ajax uploads
66
+ test_files:
67
+ - test/inline_uploader_test.rb
68
+ - test/test_helper.rb
69
+ - examples/uploader/uploader.rb