inline_uploader 0.1.0

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