actionpack 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +39 -0
- data/lib/action_controller.rb +2 -0
- data/lib/action_controller/base.rb +9 -0
- data/lib/action_controller/caching.rb +42 -3
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +1 -1
- data/lib/action_controller/dependencies.rb +3 -9
- data/lib/action_controller/helpers.rb +6 -6
- data/lib/action_controller/rescue.rb +2 -2
- data/lib/action_controller/routing.rb +1 -1
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +15 -12
- data/lib/action_controller/test_process.rb +2 -1
- data/lib/action_controller/url_rewriter.rb +13 -47
- data/lib/action_controller/verification.rb +79 -0
- data/lib/action_view/helpers/date_helper.rb +18 -7
- data/lib/action_view/helpers/javascript_helper.rb +8 -6
- data/lib/action_view/helpers/javascripts/prototype.js +133 -15
- data/lib/action_view/helpers/pagination_helper.rb +3 -3
- data/lib/action_view/helpers/text_helper.rb +20 -0
- data/rakefile +122 -23
- data/test/controller/helper_test.rb +1 -1
- data/test/controller/test_test.rb +25 -0
- data/test/controller/verification_test.rb +137 -0
- data/test/template/date_helper_test.rb +16 -0
- data/test/template/javascript_helper.rb +9 -0
- data/test/template/text_helper_test.rb +18 -0
- metadata +7 -4
- data/test/controller/url_obsolete.rb +0 -487
@@ -15,12 +15,23 @@ module ActionView
|
|
15
15
|
|
16
16
|
# Reports the approximate distance in time between to Time objects. For example, if the distance is 47 minutes, it'll return
|
17
17
|
# "about 1 hour". See the source for the complete wording list.
|
18
|
-
|
19
|
-
|
18
|
+
#Set <tt>include_seconds</tt> to true if you want more detailed approximations if distance < 1 minute
|
19
|
+
def distance_of_time_in_words(from_time, to_time, include_seconds = false)
|
20
|
+
distance_in_minutes = ((to_time - from_time) / 60).round.abs
|
21
|
+
distance_in_seconds = ((to_time - from_time)).round.abs
|
20
22
|
|
21
23
|
case distance_in_minutes
|
22
|
-
when 0
|
23
|
-
|
24
|
+
when 0..1
|
25
|
+
return (distance_in_minutes==0) ? "less than a minute" : "1 minute" unless include_seconds
|
26
|
+
case distance_in_seconds
|
27
|
+
when 0..5 then "less than 5 seconds"
|
28
|
+
when 6..10 then "less than 10 seconds"
|
29
|
+
when 11..20 then "less than 20 seconds"
|
30
|
+
when 21..40 then "half a minute"
|
31
|
+
when 41..59 then "less than a minute"
|
32
|
+
else "1 minute"
|
33
|
+
end
|
34
|
+
|
24
35
|
when 2..45 then "#{distance_in_minutes} minutes"
|
25
36
|
when 46..90 then "about 1 hour"
|
26
37
|
when 90..1440 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
|
@@ -28,10 +39,10 @@ module ActionView
|
|
28
39
|
else "#{(distance_in_minutes / 1440).round} days"
|
29
40
|
end
|
30
41
|
end
|
31
|
-
|
42
|
+
|
32
43
|
# Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
|
33
|
-
def distance_of_time_in_words_to_now(from_time)
|
34
|
-
distance_of_time_in_words(from_time, Time.now)
|
44
|
+
def distance_of_time_in_words_to_now(from_time, include_seconds = false)
|
45
|
+
distance_of_time_in_words(from_time, Time.now, include_seconds)
|
35
46
|
end
|
36
47
|
|
37
48
|
# Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based attribute (identified by
|
@@ -148,17 +148,19 @@ module ActionView
|
|
148
148
|
build_observer('Form.Observer', form_id, options)
|
149
149
|
end
|
150
150
|
|
151
|
-
|
151
|
+
# Escape carrier returns and single and double quotes for Javascript segments.
|
152
152
|
def escape_javascript(javascript)
|
153
|
-
(javascript || '').gsub(
|
153
|
+
(javascript || '').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
|
154
154
|
end
|
155
|
-
|
155
|
+
|
156
|
+
private
|
156
157
|
def options_for_ajax(options)
|
157
158
|
js_options = build_callbacks(options)
|
158
159
|
|
159
160
|
js_options['asynchronous'] = options[:type] != :synchronous
|
160
|
-
js_options['method']
|
161
|
-
|
161
|
+
js_options['method'] = options[:method] if options[:method]
|
162
|
+
js_options['insertion'] = "Insertion.#{options[:position].to_s.camelize}" if options[:position]
|
163
|
+
|
162
164
|
if options[:form]
|
163
165
|
js_options['parameters'] = 'Form.serialize(this)'
|
164
166
|
elsif options[:with]
|
@@ -181,7 +183,7 @@ module ActionView
|
|
181
183
|
CALLBACKS.inject({}) do |callbacks, callback|
|
182
184
|
if options[callback]
|
183
185
|
name = 'on' + callback.to_s.capitalize
|
184
|
-
code =
|
186
|
+
code = options[callback]
|
185
187
|
callbacks[name] = "function(request){#{code}}"
|
186
188
|
end
|
187
189
|
callbacks
|
@@ -5,11 +5,11 @@
|
|
5
5
|
* For details, see http://prototype.conio.net/
|
6
6
|
*/
|
7
7
|
|
8
|
-
Prototype = {
|
9
|
-
Version: '1.0
|
8
|
+
var Prototype = {
|
9
|
+
Version: '1.1.0'
|
10
10
|
}
|
11
11
|
|
12
|
-
Class = {
|
12
|
+
var Class = {
|
13
13
|
create: function() {
|
14
14
|
return function() {
|
15
15
|
this.initialize.apply(this, arguments);
|
@@ -17,7 +17,7 @@ Class = {
|
|
17
17
|
}
|
18
18
|
}
|
19
19
|
|
20
|
-
Abstract = new Object();
|
20
|
+
var Abstract = new Object();
|
21
21
|
|
22
22
|
Object.prototype.extend = function(object) {
|
23
23
|
for (property in object) {
|
@@ -40,7 +40,13 @@ Function.prototype.bindAsEventListener = function(object) {
|
|
40
40
|
}
|
41
41
|
}
|
42
42
|
|
43
|
-
|
43
|
+
Number.prototype.toColorPart = function() {
|
44
|
+
var digits = this.toString(16);
|
45
|
+
if (this < 16) return '0' + digits;
|
46
|
+
return digits;
|
47
|
+
}
|
48
|
+
|
49
|
+
var Try = {
|
44
50
|
these: function() {
|
45
51
|
var returnValue;
|
46
52
|
|
@@ -56,10 +62,10 @@ Try = {
|
|
56
62
|
}
|
57
63
|
}
|
58
64
|
|
59
|
-
Toggle = {
|
65
|
+
var Toggle = {
|
60
66
|
display: function() {
|
61
|
-
for (var i = 0; i <
|
62
|
-
var element = $(
|
67
|
+
for (var i = 0; i < arguments.length; i++) {
|
68
|
+
var element = $(arguments[i]);
|
63
69
|
element.style.display =
|
64
70
|
(element.style.display == 'none' ? '' : 'none');
|
65
71
|
}
|
@@ -85,8 +91,8 @@ function $() {
|
|
85
91
|
return elements;
|
86
92
|
}
|
87
93
|
|
88
|
-
function getElementsByClassName(className
|
89
|
-
var children =
|
94
|
+
function getElementsByClassName(className) {
|
95
|
+
var children = document.getElementsByTagName('*') || document.all;
|
90
96
|
var elements = new Array();
|
91
97
|
|
92
98
|
for (var i = 0; i < children.length; i++) {
|
@@ -105,7 +111,7 @@ function getElementsByClassName(className, element) {
|
|
105
111
|
|
106
112
|
/*--------------------------------------------------------------------------*/
|
107
113
|
|
108
|
-
Ajax = {
|
114
|
+
var Ajax = {
|
109
115
|
getTransport: function() {
|
110
116
|
return Try.these(
|
111
117
|
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
|
@@ -191,14 +197,22 @@ Ajax.Updater.prototype = (new Ajax.Base()).extend({
|
|
191
197
|
},
|
192
198
|
|
193
199
|
updateContent: function() {
|
194
|
-
this.
|
195
|
-
|
200
|
+
if (!this.options.insertion) {
|
201
|
+
this.container.innerHTML = this.request.transport.responseText;
|
202
|
+
} else {
|
203
|
+
new this.options.insertion(this.container,
|
204
|
+
this.request.transport.responseText);
|
205
|
+
}
|
206
|
+
|
207
|
+
if (this.onComplete) {
|
208
|
+
setTimeout((function() {this.onComplete(this.request)}).bind(this), 10);
|
209
|
+
}
|
196
210
|
}
|
197
211
|
});
|
198
212
|
|
199
213
|
/*--------------------------------------------------------------------------*/
|
200
214
|
|
201
|
-
Field = {
|
215
|
+
var Field = {
|
202
216
|
clear: function() {
|
203
217
|
for (var i = 0; i < arguments.length; i++)
|
204
218
|
$(arguments[i]).value = '';
|
@@ -217,7 +231,7 @@ Field = {
|
|
217
231
|
|
218
232
|
/*--------------------------------------------------------------------------*/
|
219
233
|
|
220
|
-
Form = {
|
234
|
+
var Form = {
|
221
235
|
serialize: function(form) {
|
222
236
|
var elements = Form.getElements($(form));
|
223
237
|
var queryComponents = new Array();
|
@@ -269,12 +283,14 @@ Form.Element.Serializers = {
|
|
269
283
|
input: function(element) {
|
270
284
|
switch (element.type.toLowerCase()) {
|
271
285
|
case 'hidden':
|
286
|
+
case 'password':
|
272
287
|
case 'text':
|
273
288
|
return Form.Element.Serializers.textarea(element);
|
274
289
|
case 'checkbox':
|
275
290
|
case 'radio':
|
276
291
|
return Form.Element.Serializers.inputSelector(element);
|
277
292
|
}
|
293
|
+
return false;
|
278
294
|
},
|
279
295
|
|
280
296
|
inputSelector: function(element) {
|
@@ -334,3 +350,105 @@ Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
|
|
334
350
|
}
|
335
351
|
});
|
336
352
|
|
353
|
+
|
354
|
+
/*--------------------------------------------------------------------------*/
|
355
|
+
|
356
|
+
Abstract.Insertion = function(adjacency) {
|
357
|
+
this.adjacency = adjacency;
|
358
|
+
}
|
359
|
+
|
360
|
+
Abstract.Insertion.prototype = {
|
361
|
+
initialize: function(element, content) {
|
362
|
+
this.element = $(element);
|
363
|
+
this.content = content;
|
364
|
+
|
365
|
+
if (this.adjacency && this.element.insertAdjacentHTML) {
|
366
|
+
this.element.insertAdjacentHTML(this.adjacency, this.content);
|
367
|
+
} else {
|
368
|
+
this.range = this.element.ownerDocument.createRange();
|
369
|
+
if (this.initializeRange) this.initializeRange();
|
370
|
+
this.fragment = this.range.createContextualFragment(this.content);
|
371
|
+
this.insertContent();
|
372
|
+
}
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
376
|
+
var Insertion = new Object();
|
377
|
+
|
378
|
+
Insertion.Before = Class.create();
|
379
|
+
Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
|
380
|
+
initializeRange: function() {
|
381
|
+
this.range.setStartBefore(this.element);
|
382
|
+
},
|
383
|
+
|
384
|
+
insertContent: function() {
|
385
|
+
this.element.parentNode.insertBefore(this.fragment, this.element);
|
386
|
+
}
|
387
|
+
});
|
388
|
+
|
389
|
+
Insertion.Top = Class.create();
|
390
|
+
Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
|
391
|
+
initializeRange: function() {
|
392
|
+
this.range.selectNodeContents(this.element);
|
393
|
+
this.range.collapse(true);
|
394
|
+
},
|
395
|
+
|
396
|
+
insertContent: function() {
|
397
|
+
this.element.insertBefore(this.fragment, this.element.firstChild);
|
398
|
+
}
|
399
|
+
});
|
400
|
+
|
401
|
+
Insertion.Bottom = Class.create();
|
402
|
+
Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
|
403
|
+
initializeRange: function() {
|
404
|
+
this.range.selectNodeContents(this.element);
|
405
|
+
this.range.collapse(this.element);
|
406
|
+
},
|
407
|
+
|
408
|
+
insertContent: function() {
|
409
|
+
this.element.appendChild(this.fragment);
|
410
|
+
}
|
411
|
+
});
|
412
|
+
|
413
|
+
Insertion.After = Class.create();
|
414
|
+
Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
|
415
|
+
initializeRange: function() {
|
416
|
+
this.range.setStartAfter(this.element);
|
417
|
+
},
|
418
|
+
|
419
|
+
insertContent: function() {
|
420
|
+
this.element.parentNode.insertBefore(this.fragment,
|
421
|
+
this.element.nextSibling);
|
422
|
+
}
|
423
|
+
});
|
424
|
+
|
425
|
+
/*--------------------------------------------------------------------------*/
|
426
|
+
|
427
|
+
var Effect = new Object();
|
428
|
+
|
429
|
+
Effect.Highlight = Class.create();
|
430
|
+
Effect.Highlight.prototype = {
|
431
|
+
initialize: function(element) {
|
432
|
+
this.element = $(element);
|
433
|
+
this.start = 153;
|
434
|
+
this.finish = 255;
|
435
|
+
this.current = this.start;
|
436
|
+
this.fade();
|
437
|
+
},
|
438
|
+
|
439
|
+
fade: function() {
|
440
|
+
if (this.isFinished()) return;
|
441
|
+
if (this.timer) clearTimeout(this.timer);
|
442
|
+
this.highlight(this.element, this.current);
|
443
|
+
this.current += 17;
|
444
|
+
this.timer = setTimeout(this.fade.bind(this), 250);
|
445
|
+
},
|
446
|
+
|
447
|
+
isFinished: function() {
|
448
|
+
return this.current > this.finish;
|
449
|
+
},
|
450
|
+
|
451
|
+
highlight: function(element, current) {
|
452
|
+
element.style.backgroundColor = "#ffff" + current.toColorPart();
|
453
|
+
}
|
454
|
+
}
|
@@ -45,7 +45,7 @@ module ActionView
|
|
45
45
|
|
46
46
|
returning html = '' do
|
47
47
|
if options[:always_show_anchors] and not window_pages[0].first?
|
48
|
-
html << link_to(first.number, options[:name] => first)
|
48
|
+
html << link_to(first.number, { options[:name] => first }.update( options[:params] ))
|
49
49
|
html << ' ... ' if window_pages[0].number - first.number > 1
|
50
50
|
html << ' '
|
51
51
|
end
|
@@ -54,14 +54,14 @@ module ActionView
|
|
54
54
|
if paginator.current == page && !options[:link_to_current_page]
|
55
55
|
html << page.number.to_s
|
56
56
|
else
|
57
|
-
html << link_to(page.number, options[:name] => page)
|
57
|
+
html << link_to(page.number, { options[:name] => page }.update( options[:params] ))
|
58
58
|
end
|
59
59
|
html << ' '
|
60
60
|
end
|
61
61
|
|
62
62
|
if options[:always_show_anchors] && !window_pages.last.last?
|
63
63
|
html << ' ... ' if last.number - window_pages[-1].number > 1
|
64
|
-
html << link_to(paginator.last.number, options[:name] => last)
|
64
|
+
html << link_to(paginator.last.number, { options[:name] => last }.update( options[:params]))
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -128,6 +128,26 @@ module ActionView
|
|
128
128
|
def strip_links(text)
|
129
129
|
text.gsub(/<a.*>(.*)<\/a>/m, '\1')
|
130
130
|
end
|
131
|
+
|
132
|
+
# Returns a formatted-for-humans file size.
|
133
|
+
#
|
134
|
+
# Examples:
|
135
|
+
# human_size(123) => 123 Bytes
|
136
|
+
# human_size(1234) => 1.2 KB
|
137
|
+
# human_size(12345) => 12.1 KB
|
138
|
+
# human_size(1234567) => 1.2 MB
|
139
|
+
# human_size(1234567890) => 1.1 GB
|
140
|
+
def human_size(size)
|
141
|
+
begin
|
142
|
+
return "%d Bytes" % size if size < 1.kilobytes
|
143
|
+
return "%.1f KB" % (size/1.0.kilobytes) if size < 1.megabytes
|
144
|
+
return "%.1f MB" % (size/1.0.megabytes) if size < 1.gigabytes
|
145
|
+
return "%.1f GB" % (size/1.0.gigabytes) if size < 1.terabytes
|
146
|
+
return "%.1f TB" % (size/1.0.terabytes)
|
147
|
+
rescue
|
148
|
+
# just return nothing
|
149
|
+
end
|
150
|
+
end
|
131
151
|
|
132
152
|
private
|
133
153
|
# Returns a version of the text that's safe to use in a regular expression without triggering engine features.
|
data/rakefile
CHANGED
@@ -8,9 +8,14 @@ require 'rake/contrib/rubyforgepublisher'
|
|
8
8
|
|
9
9
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
10
10
|
PKG_NAME = 'actionpack'
|
11
|
-
PKG_VERSION = '1.
|
11
|
+
PKG_VERSION = '1.7.0' + PKG_BUILD
|
12
12
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
13
13
|
|
14
|
+
RELEASE_NAME = "REL #{PKG_VERSION}"
|
15
|
+
|
16
|
+
RUBY_FORGE_PROJECT = "actionpack"
|
17
|
+
RUBY_FORGE_USER = "webster132"
|
18
|
+
|
14
19
|
desc "Default Task"
|
15
20
|
task :default => [ :test ]
|
16
21
|
|
@@ -32,14 +37,13 @@ Rake::RDocTask.new { |rdoc|
|
|
32
37
|
rdoc.rdoc_dir = 'doc'
|
33
38
|
rdoc.title = "Action Pack -- On rails from request to response"
|
34
39
|
rdoc.options << '--line-numbers --inline-source --main README'
|
40
|
+
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
35
41
|
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
|
36
42
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
37
43
|
}
|
38
44
|
|
39
45
|
|
40
46
|
# Create compressed packages
|
41
|
-
|
42
|
-
|
43
47
|
dist_dirs = [ "lib", "test", "examples" ]
|
44
48
|
|
45
49
|
spec = Gem::Specification.new do |s|
|
@@ -57,7 +61,7 @@ spec = Gem::Specification.new do |s|
|
|
57
61
|
s.has_rdoc = true
|
58
62
|
s.requirements << 'none'
|
59
63
|
|
60
|
-
s.add_dependency('activesupport', '= 1.0.
|
64
|
+
s.add_dependency('activesupport', '= 1.0.3' + PKG_BUILD)
|
61
65
|
|
62
66
|
s.require_path = 'lib'
|
63
67
|
s.autorequire = 'action_controller'
|
@@ -77,35 +81,130 @@ Rake::GemPackageTask.new(spec) do |p|
|
|
77
81
|
end
|
78
82
|
|
79
83
|
|
80
|
-
#
|
84
|
+
# Publishing ------------------------------------------------------
|
85
|
+
|
81
86
|
desc "Publish the API documentation"
|
82
87
|
task :pgem => [:package] do
|
83
88
|
Rake::SshFilePublisher.new("davidhh@comox.textdrive.com", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
84
89
|
`ssh davidhh@comox.textdrive.com './gemupdate.sh'`
|
85
90
|
end
|
86
91
|
|
87
|
-
# Publish documentation
|
88
92
|
desc "Publish the API documentation"
|
89
93
|
task :pdoc => [:rdoc] do
|
90
94
|
Rake::SshDirPublisher.new("davidhh@comox.textdrive.com", "public_html/ap", "doc").upload
|
91
95
|
end
|
92
96
|
|
97
|
+
desc "Publish the release files to RubyForge."
|
98
|
+
task :release => [:package] do
|
99
|
+
files = ["gem", "tgz", "zip"].map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
|
100
|
+
|
101
|
+
if RUBY_FORGE_PROJECT then
|
102
|
+
require 'net/http'
|
103
|
+
require 'open-uri'
|
104
|
+
|
105
|
+
project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
|
106
|
+
project_data = open(project_uri) { |data| data.read }
|
107
|
+
group_id = project_data[/[?&]group_id=(\d+)/, 1]
|
108
|
+
raise "Couldn't get group id" unless group_id
|
109
|
+
|
110
|
+
# This echos password to shell which is a bit sucky
|
111
|
+
if ENV["RUBY_FORGE_PASSWORD"]
|
112
|
+
password = ENV["RUBY_FORGE_PASSWORD"]
|
113
|
+
else
|
114
|
+
print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
|
115
|
+
password = STDIN.gets.chomp
|
116
|
+
end
|
93
117
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
f = File.open("lib/action_controller/" + file_name)
|
102
|
-
|
103
|
-
while line = f.gets
|
104
|
-
lines += 1
|
105
|
-
next if line =~ /^\s*$/
|
106
|
-
next if line =~ /^\s*#/
|
107
|
-
codelines += 1
|
118
|
+
login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
|
119
|
+
data = [
|
120
|
+
"login=1",
|
121
|
+
"form_loginname=#{RUBY_FORGE_USER}",
|
122
|
+
"form_pw=#{password}"
|
123
|
+
].join("&")
|
124
|
+
http.post("/account/login.php", data)
|
108
125
|
end
|
109
|
-
|
110
|
-
|
111
|
-
|
126
|
+
|
127
|
+
cookie = login_response["set-cookie"]
|
128
|
+
raise "Login failed" unless cookie
|
129
|
+
headers = { "Cookie" => cookie }
|
130
|
+
|
131
|
+
release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
|
132
|
+
release_data = open(release_uri, headers) { |data| data.read }
|
133
|
+
package_id = release_data[/[?&]package_id=(\d+)/, 1]
|
134
|
+
raise "Couldn't get package id" unless package_id
|
135
|
+
|
136
|
+
first_file = true
|
137
|
+
release_id = ""
|
138
|
+
|
139
|
+
files.each do |filename|
|
140
|
+
basename = File.basename(filename)
|
141
|
+
file_ext = File.extname(filename)
|
142
|
+
file_data = File.open(filename, "rb") { |file| file.read }
|
143
|
+
|
144
|
+
puts "Releasing #{basename}..."
|
145
|
+
|
146
|
+
release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
|
147
|
+
release_date = Time.now.strftime("%Y-%m-%d %H:%M")
|
148
|
+
type_map = {
|
149
|
+
".zip" => "3000",
|
150
|
+
".tgz" => "3110",
|
151
|
+
".gz" => "3110",
|
152
|
+
".gem" => "1400"
|
153
|
+
}; type_map.default = "9999"
|
154
|
+
type = type_map[file_ext]
|
155
|
+
boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
|
156
|
+
|
157
|
+
query_hash = if first_file then
|
158
|
+
{
|
159
|
+
"group_id" => group_id,
|
160
|
+
"package_id" => package_id,
|
161
|
+
"release_name" => RELEASE_NAME,
|
162
|
+
"release_date" => release_date,
|
163
|
+
"type_id" => type,
|
164
|
+
"processor_id" => "8000", # Any
|
165
|
+
"release_notes" => "",
|
166
|
+
"release_changes" => "",
|
167
|
+
"preformatted" => "1",
|
168
|
+
"submit" => "1"
|
169
|
+
}
|
170
|
+
else
|
171
|
+
{
|
172
|
+
"group_id" => group_id,
|
173
|
+
"release_id" => release_id,
|
174
|
+
"package_id" => package_id,
|
175
|
+
"step2" => "1",
|
176
|
+
"type_id" => type,
|
177
|
+
"processor_id" => "8000", # Any
|
178
|
+
"submit" => "Add This File"
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
query = "?" + query_hash.map do |(name, value)|
|
183
|
+
[name, URI.encode(value)].join("=")
|
184
|
+
end.join("&")
|
185
|
+
|
186
|
+
data = [
|
187
|
+
"--" + boundary,
|
188
|
+
"Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
|
189
|
+
"Content-Type: application/octet-stream",
|
190
|
+
"Content-Transfer-Encoding: binary",
|
191
|
+
"", file_data, ""
|
192
|
+
].join("\x0D\x0A")
|
193
|
+
|
194
|
+
release_headers = headers.merge(
|
195
|
+
"Content-Type" => "multipart/form-data; boundary=#{boundary}"
|
196
|
+
)
|
197
|
+
|
198
|
+
target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
|
199
|
+
http.post(target + query, data, release_headers)
|
200
|
+
end
|
201
|
+
|
202
|
+
if first_file then
|
203
|
+
release_id = release_response.body[/release_id=(\d+)/, 1]
|
204
|
+
raise("Couldn't get release id") unless release_id
|
205
|
+
end
|
206
|
+
|
207
|
+
first_file = false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|