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.

@@ -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
- def distance_of_time_in_words(from_time, to_time)
19
- distance_in_minutes = ((to_time - from_time) / 60).round
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 then "less than a minute"
23
- when 1 then "1 minute"
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
- private
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'] = options[:method] if 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 = escape_javascript(options[callback])
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.1'
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
- Try = {
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 < elements.length; i++) {
62
- var element = $(elements[i]);
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, element) {
89
- var children = (element || document).getElementsByTagName('*');
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.container.innerHTML = this.request.transport.responseText;
195
- if (this.onComplete) this.onComplete(this.request);
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.6.0' + PKG_BUILD
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.2' + PKG_BUILD)
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
- # Publish beta gem
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
- desc "Count lines in the main rake file"
95
- task :lines do
96
- lines = 0
97
- codelines = 0
98
- Dir.foreach("lib/action_controller") { |file_name|
99
- next unless file_name =~ /.*rb/
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
- puts "Lines #{lines}, LOC #{codelines}"
111
- end
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