lastTokenPos)
+ lastTokenPos = thisTokenPos;
+ }
+ return lastTokenPos;
+ }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+ initialize: function(element, update, url, options) {
+ this.baseInitialize(element, update, options);
+ this.options.asynchronous = true;
+ this.options.onComplete = this.onComplete.bind(this);
+ this.options.defaultParams = this.options.parameters || null;
+ this.url = url;
+ },
+
+ getUpdatedChoices: function() {
+ entry = encodeURIComponent(this.options.paramName) + '=' +
+ encodeURIComponent(this.getToken());
+
+ this.options.parameters = this.options.callback ?
+ this.options.callback(this.element, entry) : entry;
+
+ if(this.options.defaultParams)
+ this.options.parameters += '&' + this.options.defaultParams;
+
+ new Ajax.Request(this.url, this.options);
+ },
+
+ onComplete: function(request) {
+ this.updateChoices(request.responseText);
+ }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+// text only at the beginning of strings in the
+// autocomplete array. Defaults to true, which will
+// match text at the beginning of any *word* in the
+// strings in the autocomplete array. If you want to
+// search anywhere in the string, additionally set
+// the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+// a partial match (unlike minChars, which defines
+// how many characters are required to do any match
+// at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+// Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+ initialize: function(element, update, array, options) {
+ this.baseInitialize(element, update, options);
+ this.options.array = array;
+ },
+
+ getUpdatedChoices: function() {
+ this.updateChoices(this.options.selector(this));
+ },
+
+ setOptions: function(options) {
+ this.options = Object.extend({
+ choices: 10,
+ partialSearch: true,
+ partialChars: 2,
+ ignoreCase: true,
+ fullSearch: false,
+ selector: function(instance) {
+ var ret = []; // Beginning matches
+ var partial = []; // Inside matches
+ var entry = instance.getToken();
+ var count = 0;
+
+ for (var i = 0; i < instance.options.array.length &&
+ ret.length < instance.options.choices ; i++) {
+
+ var elem = instance.options.array[i];
+ var foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
+ elem.indexOf(entry);
+
+ while (foundPos != -1) {
+ if (foundPos == 0 && elem.length != entry.length) {
+ ret.push("" + elem.substr(0, entry.length) + " " +
+ elem.substr(entry.length) + " ");
+ break;
+ } else if (entry.length >= instance.options.partialChars &&
+ instance.options.partialSearch && foundPos != -1) {
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+ partial.push("" + elem.substr(0, foundPos) + "" +
+ elem.substr(foundPos, entry.length) + " " + elem.substr(
+ foundPos + entry.length) + " ");
+ break;
+ }
+ }
+
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ elem.indexOf(entry, foundPos + 1);
+
+ }
+ }
+ if (partial.length)
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+ return "";
+ }
+ }, options || {});
+ }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+ setTimeout(function() {
+ Field.activate(field);
+ }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+ initialize: function(element, url, options) {
+ this.url = url;
+ this.element = $(element);
+
+ this.options = Object.extend({
+ paramName: "value",
+ okButton: true,
+ okText: "ok",
+ cancelLink: true,
+ cancelText: "cancel",
+ savingText: "Saving...",
+ clickToEditText: "Click to edit",
+ okText: "ok",
+ rows: 1,
+ onComplete: function(transport, element) {
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+ },
+ onFailure: function(transport) {
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
+ },
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ handleLineBreaks: true,
+ loadingText: 'Loading...',
+ savingClassName: 'inplaceeditor-saving',
+ loadingClassName: 'inplaceeditor-loading',
+ formClassName: 'inplaceeditor-form',
+ highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+ highlightendcolor: "#FFFFFF",
+ externalControl: null,
+ submitOnBlur: false,
+ ajaxOptions: {},
+ evalScripts: false
+ }, options || {});
+
+ if(!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + "-inplaceeditor";
+ if ($(this.options.formId)) {
+ // there's already a form with that name, don't specify an id
+ this.options.formId = null;
+ }
+ }
+
+ if (this.options.externalControl) {
+ this.options.externalControl = $(this.options.externalControl);
+ }
+
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
+ if (!this.originalBackground) {
+ this.originalBackground = "transparent";
+ }
+
+ this.element.title = this.options.clickToEditText;
+
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+ Event.observe(this.element, 'click', this.onclickListener);
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ },
+ enterEditMode: function(evt) {
+ if (this.saving) return;
+ if (this.editing) return;
+ this.editing = true;
+ this.onEnterEditMode();
+ if (this.options.externalControl) {
+ Element.hide(this.options.externalControl);
+ }
+ Element.hide(this.element);
+ this.createForm();
+ this.element.parentNode.insertBefore(this.form, this.element);
+ if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
+ // stop the event to avoid a page refresh in Safari
+ if (evt) {
+ Event.stop(evt);
+ }
+ return false;
+ },
+ createForm: function() {
+ this.form = document.createElement("form");
+ this.form.id = this.options.formId;
+ Element.addClassName(this.form, this.options.formClassName)
+ this.form.onsubmit = this.onSubmit.bind(this);
+
+ this.createEditField();
+
+ if (this.options.textarea) {
+ var br = document.createElement("br");
+ this.form.appendChild(br);
+ }
+
+ if (this.options.okButton) {
+ okButton = document.createElement("input");
+ okButton.type = "submit";
+ okButton.value = this.options.okText;
+ okButton.className = 'editor_ok_button';
+ this.form.appendChild(okButton);
+ }
+
+ if (this.options.cancelLink) {
+ cancelLink = document.createElement("a");
+ cancelLink.href = "#";
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+ cancelLink.onclick = this.onclickCancel.bind(this);
+ cancelLink.className = 'editor_cancel';
+ this.form.appendChild(cancelLink);
+ }
+ },
+ hasHTMLLineBreaks: function(string) {
+ if (!this.options.handleLineBreaks) return false;
+ return string.match(/ /i);
+ },
+ convertHTMLLineBreaks: function(string) {
+ return string.replace(/ /gi, "\n").replace(/ /gi, "\n").replace(/<\/p>/gi, "\n").replace(//gi, "");
+ },
+ createEditField: function() {
+ var text;
+ if(this.options.loadTextURL) {
+ text = this.options.loadingText;
+ } else {
+ text = this.getText();
+ }
+
+ var obj = this;
+
+ if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+ this.options.textarea = false;
+ var textField = document.createElement("input");
+ textField.obj = this;
+ textField.type = "text";
+ textField.name = this.options.paramName;
+ textField.value = text;
+ textField.style.backgroundColor = this.options.highlightcolor;
+ textField.className = 'editor_field';
+ var size = this.options.size || this.options.cols || 0;
+ if (size != 0) textField.size = size;
+ if (this.options.submitOnBlur)
+ textField.onblur = this.onSubmit.bind(this);
+ this.editField = textField;
+ } else {
+ this.options.textarea = true;
+ var textArea = document.createElement("textarea");
+ textArea.obj = this;
+ textArea.name = this.options.paramName;
+ textArea.value = this.convertHTMLLineBreaks(text);
+ textArea.rows = this.options.rows;
+ textArea.cols = this.options.cols || 40;
+ textArea.className = 'editor_field';
+ if (this.options.submitOnBlur)
+ textArea.onblur = this.onSubmit.bind(this);
+ this.editField = textArea;
+ }
+
+ if(this.options.loadTextURL) {
+ this.loadExternalText();
+ }
+ this.form.appendChild(this.editField);
+ },
+ getText: function() {
+ return this.element.innerHTML;
+ },
+ loadExternalText: function() {
+ Element.addClassName(this.form, this.options.loadingClassName);
+ this.editField.disabled = true;
+ new Ajax.Request(
+ this.options.loadTextURL,
+ Object.extend({
+ asynchronous: true,
+ onComplete: this.onLoadedExternalText.bind(this)
+ }, this.options.ajaxOptions)
+ );
+ },
+ onLoadedExternalText: function(transport) {
+ Element.removeClassName(this.form, this.options.loadingClassName);
+ this.editField.disabled = false;
+ this.editField.value = transport.responseText.stripTags();
+ Field.scrollFreeActivate(this.editField);
+ },
+ onclickCancel: function() {
+ this.onComplete();
+ this.leaveEditMode();
+ return false;
+ },
+ onFailure: function(transport) {
+ this.options.onFailure(transport);
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ this.oldInnerHTML = null;
+ }
+ return false;
+ },
+ onSubmit: function() {
+ // onLoading resets these so we need to save them away for the Ajax call
+ var form = this.form;
+ var value = this.editField.value;
+
+ // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+ // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+ // to be displayed indefinitely
+ this.onLoading();
+
+ if (this.options.evalScripts) {
+ new Ajax.Request(
+ this.url, Object.extend({
+ parameters: this.options.callback(form, value),
+ onComplete: this.onComplete.bind(this),
+ onFailure: this.onFailure.bind(this),
+ asynchronous:true,
+ evalScripts:true
+ }, this.options.ajaxOptions));
+ } else {
+ new Ajax.Updater(
+ { success: this.element,
+ // don't update on failure (this could be an option)
+ failure: null },
+ this.url, Object.extend({
+ parameters: this.options.callback(form, value),
+ onComplete: this.onComplete.bind(this),
+ onFailure: this.onFailure.bind(this)
+ }, this.options.ajaxOptions));
+ }
+ // stop the event to avoid a page refresh in Safari
+ if (arguments.length > 1) {
+ Event.stop(arguments[0]);
+ }
+ return false;
+ },
+ onLoading: function() {
+ this.saving = true;
+ this.removeForm();
+ this.leaveHover();
+ this.showSaving();
+ },
+ showSaving: function() {
+ this.oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ Element.addClassName(this.element, this.options.savingClassName);
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ },
+ removeForm: function() {
+ if(this.form) {
+ if (this.form.parentNode) Element.remove(this.form);
+ this.form = null;
+ }
+ },
+ enterHover: function() {
+ if (this.saving) return;
+ this.element.style.backgroundColor = this.options.highlightcolor;
+ if (this.effect) {
+ this.effect.cancel();
+ }
+ Element.addClassName(this.element, this.options.hoverClassName)
+ },
+ leaveHover: function() {
+ if (this.options.backgroundColor) {
+ this.element.style.backgroundColor = this.oldBackground;
+ }
+ Element.removeClassName(this.element, this.options.hoverClassName)
+ if (this.saving) return;
+ this.effect = new Effect.Highlight(this.element, {
+ startcolor: this.options.highlightcolor,
+ endcolor: this.options.highlightendcolor,
+ restorecolor: this.originalBackground
+ });
+ },
+ leaveEditMode: function() {
+ Element.removeClassName(this.element, this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ if (this.options.externalControl) {
+ Element.show(this.options.externalControl);
+ }
+ this.editing = false;
+ this.saving = false;
+ this.oldInnerHTML = null;
+ this.onLeaveEditMode();
+ },
+ onComplete: function(transport) {
+ this.leaveEditMode();
+ this.options.onComplete.bind(this)(transport, this.element);
+ },
+ onEnterEditMode: function() {},
+ onLeaveEditMode: function() {},
+ dispose: function() {
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ }
+ this.leaveEditMode();
+ Event.stopObserving(this.element, 'click', this.onclickListener);
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ }
+};
+
+Ajax.InPlaceCollectionEditor = Class.create();
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
+ createEditField: function() {
+ if (!this.cached_selectTag) {
+ var selectTag = document.createElement("select");
+ var collection = this.options.collection || [];
+ var optionTag;
+ collection.each(function(e,i) {
+ optionTag = document.createElement("option");
+ optionTag.value = (e instanceof Array) ? e[0] : e;
+ if((typeof this.options.value == 'undefined') &&
+ ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
+ if(this.options.value==optionTag.value) optionTag.selected = true;
+ optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
+ selectTag.appendChild(optionTag);
+ }.bind(this));
+ this.cached_selectTag = selectTag;
+ }
+
+ this.editField = this.cached_selectTag;
+ if(this.options.loadTextURL) this.loadExternalText();
+ this.form.appendChild(this.editField);
+ this.options.callback = function(form, value) {
+ return "value=" + encodeURIComponent(value);
+ }
+ }
+});
+
+// Delayed observer, like Form.Element.Observer,
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+ initialize: function(element, delay, callback) {
+ this.delay = delay || 0.5;
+ this.element = $(element);
+ this.callback = callback;
+ this.timer = null;
+ this.lastValue = $F(this.element);
+ Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+ },
+ delayedListener: function(event) {
+ if(this.lastValue == $F(this.element)) return;
+ if(this.timer) clearTimeout(this.timer);
+ this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+ this.lastValue = $F(this.element);
+ },
+ onTimerEvent: function() {
+ this.timer = null;
+ this.callback(this.element, $F(this.element));
+ }
+};
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/dragdrop.js b/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
new file mode 100644
index 00000000..c71ddb82
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
@@ -0,0 +1,942 @@
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+if(typeof Effect == 'undefined')
+ throw("dragdrop.js requires including script.aculo.us' effects.js library");
+
+var Droppables = {
+ drops: [],
+
+ remove: function(element) {
+ this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+ },
+
+ add: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ greedy: true,
+ hoverclass: null,
+ tree: false
+ }, arguments[1] || {});
+
+ // cache containers
+ if(options.containment) {
+ options._containers = [];
+ var containment = options.containment;
+ if((typeof containment == 'object') &&
+ (containment.constructor == Array)) {
+ containment.each( function(c) { options._containers.push($(c)) });
+ } else {
+ options._containers.push($(containment));
+ }
+ }
+
+ if(options.accept) options.accept = [options.accept].flatten();
+
+ Element.makePositioned(element); // fix IE
+ options.element = element;
+
+ this.drops.push(options);
+ },
+
+ findDeepestChild: function(drops) {
+ deepest = drops[0];
+
+ for (i = 1; i < drops.length; ++i)
+ if (Element.isParent(drops[i].element, deepest.element))
+ deepest = drops[i];
+
+ return deepest;
+ },
+
+ isContained: function(element, drop) {
+ var containmentNode;
+ if(drop.tree) {
+ containmentNode = element.treeNode;
+ } else {
+ containmentNode = element.parentNode;
+ }
+ return drop._containers.detect(function(c) { return containmentNode == c });
+ },
+
+ isAffected: function(point, element, drop) {
+ return (
+ (drop.element!=element) &&
+ ((!drop._containers) ||
+ this.isContained(element, drop)) &&
+ ((!drop.accept) ||
+ (Element.classNames(element).detect(
+ function(v) { return drop.accept.include(v) } ) )) &&
+ Position.within(drop.element, point[0], point[1]) );
+ },
+
+ deactivate: function(drop) {
+ if(drop.hoverclass)
+ Element.removeClassName(drop.element, drop.hoverclass);
+ this.last_active = null;
+ },
+
+ activate: function(drop) {
+ if(drop.hoverclass)
+ Element.addClassName(drop.element, drop.hoverclass);
+ this.last_active = drop;
+ },
+
+ show: function(point, element) {
+ if(!this.drops.length) return;
+ var affected = [];
+
+ if(this.last_active) this.deactivate(this.last_active);
+ this.drops.each( function(drop) {
+ if(Droppables.isAffected(point, element, drop))
+ affected.push(drop);
+ });
+
+ if(affected.length>0) {
+ drop = Droppables.findDeepestChild(affected);
+ Position.within(drop.element, point[0], point[1]);
+ if(drop.onHover)
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+
+ Droppables.activate(drop);
+ }
+ },
+
+ fire: function(event, element) {
+ if(!this.last_active) return;
+ Position.prepare();
+
+ if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+ if (this.last_active.onDrop)
+ this.last_active.onDrop(element, this.last_active.element, event);
+ },
+
+ reset: function() {
+ if(this.last_active)
+ this.deactivate(this.last_active);
+ }
+}
+
+var Draggables = {
+ drags: [],
+ observers: [],
+
+ register: function(draggable) {
+ if(this.drags.length == 0) {
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+ this.eventKeypress = this.keyPress.bindAsEventListener(this);
+
+ Event.observe(document, "mouseup", this.eventMouseUp);
+ Event.observe(document, "mousemove", this.eventMouseMove);
+ Event.observe(document, "keypress", this.eventKeypress);
+ }
+ this.drags.push(draggable);
+ },
+
+ unregister: function(draggable) {
+ this.drags = this.drags.reject(function(d) { return d==draggable });
+ if(this.drags.length == 0) {
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ Event.stopObserving(document, "keypress", this.eventKeypress);
+ }
+ },
+
+ activate: function(draggable) {
+ if(draggable.options.delay) {
+ this._timeout = setTimeout(function() {
+ Draggables._timeout = null;
+ window.focus();
+ Draggables.activeDraggable = draggable;
+ }.bind(this), draggable.options.delay);
+ } else {
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+ this.activeDraggable = draggable;
+ }
+ },
+
+ deactivate: function() {
+ this.activeDraggable = null;
+ },
+
+ updateDrag: function(event) {
+ if(!this.activeDraggable) return;
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ // Mozilla-based browsers fire successive mousemove events with
+ // the same coordinates, prevent needless redrawing (moz bug?)
+ if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+ this._lastPointer = pointer;
+
+ this.activeDraggable.updateDrag(event, pointer);
+ },
+
+ endDrag: function(event) {
+ if(this._timeout) {
+ clearTimeout(this._timeout);
+ this._timeout = null;
+ }
+ if(!this.activeDraggable) return;
+ this._lastPointer = null;
+ this.activeDraggable.endDrag(event);
+ this.activeDraggable = null;
+ },
+
+ keyPress: function(event) {
+ if(this.activeDraggable)
+ this.activeDraggable.keyPress(event);
+ },
+
+ addObserver: function(observer) {
+ this.observers.push(observer);
+ this._cacheObserverCallbacks();
+ },
+
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
+ this.observers = this.observers.reject( function(o) { return o.element==element });
+ this._cacheObserverCallbacks();
+ },
+
+ notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
+ if(this[eventName+'Count'] > 0)
+ this.observers.each( function(o) {
+ if(o[eventName]) o[eventName](eventName, draggable, event);
+ });
+ if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
+ },
+
+ _cacheObserverCallbacks: function() {
+ ['onStart','onEnd','onDrag'].each( function(eventName) {
+ Draggables[eventName+'Count'] = Draggables.observers.select(
+ function(o) { return o[eventName]; }
+ ).length;
+ });
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable._dragging = {};
+
+Draggable.prototype = {
+ initialize: function(element) {
+ var defaults = {
+ handle: false,
+ reverteffect: function(element, top_offset, left_offset) {
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+ new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+ queue: {scope:'_draggable', position:'end'}
+ });
+ },
+ endeffect: function(element) {
+ var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
+ queue: {scope:'_draggable', position:'end'},
+ afterFinish: function(){
+ Draggable._dragging[element] = false
+ }
+ });
+ },
+ zindex: 1000,
+ revert: false,
+ scroll: false,
+ scrollSensitivity: 20,
+ scrollSpeed: 15,
+ snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
+ delay: 0
+ };
+
+ if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
+ Object.extend(defaults, {
+ starteffect: function(element) {
+ element._opacity = Element.getOpacity(element);
+ Draggable._dragging[element] = true;
+ new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
+ }
+ });
+
+ var options = Object.extend(defaults, arguments[1] || {});
+
+ this.element = $(element);
+
+ if(options.handle && (typeof options.handle == 'string'))
+ this.handle = this.element.down('.'+options.handle, 0);
+
+ if(!this.handle) this.handle = $(options.handle);
+ if(!this.handle) this.handle = this.element;
+
+ if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
+ options.scroll = $(options.scroll);
+ this._isScrollChild = Element.childOf(this.element, options.scroll);
+ }
+
+ Element.makePositioned(this.element); // fix IE
+
+ this.delta = this.currentDelta();
+ this.options = options;
+ this.dragging = false;
+
+ this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
+
+ Draggables.register(this);
+ },
+
+ destroy: function() {
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+ Draggables.unregister(this);
+ },
+
+ currentDelta: function() {
+ return([
+ parseInt(Element.getStyle(this.element,'left') || '0'),
+ parseInt(Element.getStyle(this.element,'top') || '0')]);
+ },
+
+ initDrag: function(event) {
+ if(typeof Draggable._dragging[this.element] != 'undefined' &&
+ Draggable._dragging[this.element]) return;
+ if(Event.isLeftClick(event)) {
+ // abort on form elements, fixes a Firefox issue
+ var src = Event.element(event);
+ if(src.tagName && (
+ src.tagName=='INPUT' ||
+ src.tagName=='SELECT' ||
+ src.tagName=='OPTION' ||
+ src.tagName=='BUTTON' ||
+ src.tagName=='TEXTAREA')) return;
+
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var pos = Position.cumulativeOffset(this.element);
+ this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+
+ Draggables.activate(this);
+ Event.stop(event);
+ }
+ },
+
+ startDrag: function(event) {
+ this.dragging = true;
+
+ if(this.options.zindex) {
+ this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+ this.element.style.zIndex = this.options.zindex;
+ }
+
+ if(this.options.ghosting) {
+ this._clone = this.element.cloneNode(true);
+ Position.absolutize(this.element);
+ this.element.parentNode.insertBefore(this._clone, this.element);
+ }
+
+ if(this.options.scroll) {
+ if (this.options.scroll == window) {
+ var where = this._getWindowScroll(this.options.scroll);
+ this.originalScrollLeft = where.left;
+ this.originalScrollTop = where.top;
+ } else {
+ this.originalScrollLeft = this.options.scroll.scrollLeft;
+ this.originalScrollTop = this.options.scroll.scrollTop;
+ }
+ }
+
+ Draggables.notify('onStart', this, event);
+
+ if(this.options.starteffect) this.options.starteffect(this.element);
+ },
+
+ updateDrag: function(event, pointer) {
+ if(!this.dragging) this.startDrag(event);
+ Position.prepare();
+ Droppables.show(pointer, this.element);
+ Draggables.notify('onDrag', this, event);
+
+ this.draw(pointer);
+ if(this.options.change) this.options.change(this);
+
+ if(this.options.scroll) {
+ this.stopScrolling();
+
+ var p;
+ if (this.options.scroll == window) {
+ with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+ } else {
+ p = Position.page(this.options.scroll);
+ p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+ p[1] += this.options.scroll.scrollTop + Position.deltaY;
+ p.push(p[0]+this.options.scroll.offsetWidth);
+ p.push(p[1]+this.options.scroll.offsetHeight);
+ }
+ var speed = [0,0];
+ if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+ if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+ if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+ if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+ this.startScrolling(speed);
+ }
+
+ // fix AppleWebKit rendering
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+
+ Event.stop(event);
+ },
+
+ finishDrag: function(event, success) {
+ this.dragging = false;
+
+ if(this.options.ghosting) {
+ Position.relativize(this.element);
+ Element.remove(this._clone);
+ this._clone = null;
+ }
+
+ if(success) Droppables.fire(event, this.element);
+ Draggables.notify('onEnd', this, event);
+
+ var revert = this.options.revert;
+ if(revert && typeof revert == 'function') revert = revert(this.element);
+
+ var d = this.currentDelta();
+ if(revert && this.options.reverteffect) {
+ this.options.reverteffect(this.element,
+ d[1]-this.delta[1], d[0]-this.delta[0]);
+ } else {
+ this.delta = d;
+ }
+
+ if(this.options.zindex)
+ this.element.style.zIndex = this.originalZ;
+
+ if(this.options.endeffect)
+ this.options.endeffect(this.element);
+
+ Draggables.deactivate(this);
+ Droppables.reset();
+ },
+
+ keyPress: function(event) {
+ if(event.keyCode!=Event.KEY_ESC) return;
+ this.finishDrag(event, false);
+ Event.stop(event);
+ },
+
+ endDrag: function(event) {
+ if(!this.dragging) return;
+ this.stopScrolling();
+ this.finishDrag(event, true);
+ Event.stop(event);
+ },
+
+ draw: function(point) {
+ var pos = Position.cumulativeOffset(this.element);
+ if(this.options.ghosting) {
+ var r = Position.realOffset(this.element);
+ pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+ }
+
+ var d = this.currentDelta();
+ pos[0] -= d[0]; pos[1] -= d[1];
+
+ if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
+ pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+ pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+ }
+
+ var p = [0,1].map(function(i){
+ return (point[i]-pos[i]-this.offset[i])
+ }.bind(this));
+
+ if(this.options.snap) {
+ if(typeof this.options.snap == 'function') {
+ p = this.options.snap(p[0],p[1],this);
+ } else {
+ if(this.options.snap instanceof Array) {
+ p = p.map( function(v, i) {
+ return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+ } else {
+ p = p.map( function(v) {
+ return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+ }
+ }}
+
+ var style = this.element.style;
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+ style.left = p[0] + "px";
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
+ style.top = p[1] + "px";
+
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+ },
+
+ stopScrolling: function() {
+ if(this.scrollInterval) {
+ clearInterval(this.scrollInterval);
+ this.scrollInterval = null;
+ Draggables._lastScrollPointer = null;
+ }
+ },
+
+ startScrolling: function(speed) {
+ if(!(speed[0] || speed[1])) return;
+ this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+ this.lastScrolled = new Date();
+ this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+ },
+
+ scroll: function() {
+ var current = new Date();
+ var delta = current - this.lastScrolled;
+ this.lastScrolled = current;
+ if(this.options.scroll == window) {
+ with (this._getWindowScroll(this.options.scroll)) {
+ if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+ var d = delta / 1000;
+ this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+ }
+ }
+ } else {
+ this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+ this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
+ }
+
+ Position.prepare();
+ Droppables.show(Draggables._lastPointer, this.element);
+ Draggables.notify('onDrag', this);
+ if (this._isScrollChild) {
+ Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+ Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+ Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+ if (Draggables._lastScrollPointer[0] < 0)
+ Draggables._lastScrollPointer[0] = 0;
+ if (Draggables._lastScrollPointer[1] < 0)
+ Draggables._lastScrollPointer[1] = 0;
+ this.draw(Draggables._lastScrollPointer);
+ }
+
+ if(this.options.change) this.options.change(this);
+ },
+
+ _getWindowScroll: function(w) {
+ var T, L, W, H;
+ with (w.document) {
+ if (w.document.documentElement && documentElement.scrollTop) {
+ T = documentElement.scrollTop;
+ L = documentElement.scrollLeft;
+ } else if (w.document.body) {
+ T = body.scrollTop;
+ L = body.scrollLeft;
+ }
+ if (w.innerWidth) {
+ W = w.innerWidth;
+ H = w.innerHeight;
+ } else if (w.document.documentElement && documentElement.clientWidth) {
+ W = documentElement.clientWidth;
+ H = documentElement.clientHeight;
+ } else {
+ W = body.offsetWidth;
+ H = body.offsetHeight
+ }
+ }
+ return { top: T, left: L, width: W, height: H };
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+ initialize: function(element, observer) {
+ this.element = $(element);
+ this.observer = observer;
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onStart: function() {
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onEnd: function() {
+ Sortable.unmark();
+ if(this.lastValue != Sortable.serialize(this.element))
+ this.observer(this.element)
+ }
+}
+
+var Sortable = {
+ SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+
+ sortables: {},
+
+ _findRootElement: function(element) {
+ while (element.tagName != "BODY") {
+ if(element.id && Sortable.sortables[element.id]) return element;
+ element = element.parentNode;
+ }
+ },
+
+ options: function(element) {
+ element = Sortable._findRootElement($(element));
+ if(!element) return;
+ return Sortable.sortables[element.id];
+ },
+
+ destroy: function(element){
+ var s = Sortable.options(element);
+
+ if(s) {
+ Draggables.removeObserver(s.element);
+ s.droppables.each(function(d){ Droppables.remove(d) });
+ s.draggables.invoke('destroy');
+
+ delete Sortable.sortables[s.element.id];
+ }
+ },
+
+ create: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ element: element,
+ tag: 'li', // assumes li children, override with tag: 'tagname'
+ dropOnEmpty: false,
+ tree: false,
+ treeTag: 'ul',
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
+ containment: element, // also takes array of elements (or id's); or false
+ handle: false, // or a CSS class
+ only: false,
+ delay: 0,
+ hoverclass: null,
+ ghosting: false,
+ scroll: false,
+ scrollSensitivity: 20,
+ scrollSpeed: 15,
+ format: this.SERIALIZE_RULE,
+ onChange: Prototype.emptyFunction,
+ onUpdate: Prototype.emptyFunction
+ }, arguments[1] || {});
+
+ // clear any old sortable with same element
+ this.destroy(element);
+
+ // build options for the draggables
+ var options_for_draggable = {
+ revert: true,
+ scroll: options.scroll,
+ scrollSpeed: options.scrollSpeed,
+ scrollSensitivity: options.scrollSensitivity,
+ delay: options.delay,
+ ghosting: options.ghosting,
+ constraint: options.constraint,
+ handle: options.handle };
+
+ if(options.starteffect)
+ options_for_draggable.starteffect = options.starteffect;
+
+ if(options.reverteffect)
+ options_for_draggable.reverteffect = options.reverteffect;
+ else
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+ element.style.top = 0;
+ element.style.left = 0;
+ };
+
+ if(options.endeffect)
+ options_for_draggable.endeffect = options.endeffect;
+
+ if(options.zindex)
+ options_for_draggable.zindex = options.zindex;
+
+ // build options for the droppables
+ var options_for_droppable = {
+ overlap: options.overlap,
+ containment: options.containment,
+ tree: options.tree,
+ hoverclass: options.hoverclass,
+ onHover: Sortable.onHover
+ }
+
+ var options_for_tree = {
+ onHover: Sortable.onEmptyHover,
+ overlap: options.overlap,
+ containment: options.containment,
+ hoverclass: options.hoverclass
+ }
+
+ // fix for gecko engine
+ Element.cleanWhitespace(element);
+
+ options.draggables = [];
+ options.droppables = [];
+
+ // drop on empty handling
+ if(options.dropOnEmpty || options.tree) {
+ Droppables.add(element, options_for_tree);
+ options.droppables.push(element);
+ }
+
+ (this.findElements(element, options) || []).each( function(e) {
+ // handles are per-draggable
+ var handle = options.handle ?
+ $(e).down('.'+options.handle,0) : e;
+ options.draggables.push(
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+ Droppables.add(e, options_for_droppable);
+ if(options.tree) e.treeNode = element;
+ options.droppables.push(e);
+ });
+
+ if(options.tree) {
+ (Sortable.findTreeElements(element, options) || []).each( function(e) {
+ Droppables.add(e, options_for_tree);
+ e.treeNode = element;
+ options.droppables.push(e);
+ });
+ }
+
+ // keep reference
+ this.sortables[element.id] = options;
+
+ // for onupdate
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+ },
+
+ // return all suitable-for-sortable elements in a guaranteed order
+ findElements: function(element, options) {
+ return Element.findChildren(
+ element, options.only, options.tree ? true : false, options.tag);
+ },
+
+ findTreeElements: function(element, options) {
+ return Element.findChildren(
+ element, options.only, options.tree ? true : false, options.treeTag);
+ },
+
+ onHover: function(element, dropon, overlap) {
+ if(Element.isParent(dropon, element)) return;
+
+ if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+ return;
+ } else if(overlap>0.5) {
+ Sortable.mark(dropon, 'before');
+ if(dropon.previousSibling != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, dropon);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ } else {
+ Sortable.mark(dropon, 'after');
+ var nextElement = dropon.nextSibling || null;
+ if(nextElement != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, nextElement);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ }
+ },
+
+ onEmptyHover: function(element, dropon, overlap) {
+ var oldParentNode = element.parentNode;
+ var droponOptions = Sortable.options(dropon);
+
+ if(!Element.isParent(dropon, element)) {
+ var index;
+
+ var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
+ var child = null;
+
+ if(children) {
+ var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+
+ for (index = 0; index < children.length; index += 1) {
+ if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+ offset -= Element.offsetSize (children[index], droponOptions.overlap);
+ } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+ child = index + 1 < children.length ? children[index + 1] : null;
+ break;
+ } else {
+ child = children[index];
+ break;
+ }
+ }
+ }
+
+ dropon.insertBefore(element, child);
+
+ Sortable.options(oldParentNode).onChange(element);
+ droponOptions.onChange(element);
+ }
+ },
+
+ unmark: function() {
+ if(Sortable._marker) Sortable._marker.hide();
+ },
+
+ mark: function(dropon, position) {
+ // mark on ghosting only
+ var sortable = Sortable.options(dropon.parentNode);
+ if(sortable && !sortable.ghosting) return;
+
+ if(!Sortable._marker) {
+ Sortable._marker =
+ ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+ hide().addClassName('dropmarker').setStyle({position:'absolute'});
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+ }
+ var offsets = Position.cumulativeOffset(dropon);
+ Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
+
+ if(position=='after')
+ if(sortable.overlap == 'horizontal')
+ Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
+ else
+ Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
+
+ Sortable._marker.show();
+ },
+
+ _tree: function(element, options, parent) {
+ var children = Sortable.findElements(element, options) || [];
+
+ for (var i = 0; i < children.length; ++i) {
+ var match = children[i].id.match(options.format);
+
+ if (!match) continue;
+
+ var child = {
+ id: encodeURIComponent(match ? match[1] : null),
+ element: element,
+ parent: parent,
+ children: [],
+ position: parent.children.length,
+ container: $(children[i]).down(options.treeTag)
+ }
+
+ /* Get the element containing the children and recurse over it */
+ if (child.container)
+ this._tree(child.container, options, child)
+
+ parent.children.push (child);
+ }
+
+ return parent;
+ },
+
+ tree: function(element) {
+ element = $(element);
+ var sortableOptions = this.options(element);
+ var options = Object.extend({
+ tag: sortableOptions.tag,
+ treeTag: sortableOptions.treeTag,
+ only: sortableOptions.only,
+ name: element.id,
+ format: sortableOptions.format
+ }, arguments[1] || {});
+
+ var root = {
+ id: null,
+ parent: null,
+ children: [],
+ container: element,
+ position: 0
+ }
+
+ return Sortable._tree(element, options, root);
+ },
+
+ /* Construct a [i] index for a particular node */
+ _constructIndex: function(node) {
+ var index = '';
+ do {
+ if (node.id) index = '[' + node.position + ']' + index;
+ } while ((node = node.parent) != null);
+ return index;
+ },
+
+ sequence: function(element) {
+ element = $(element);
+ var options = Object.extend(this.options(element), arguments[1] || {});
+
+ return $(this.findElements(element, options) || []).map( function(item) {
+ return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+ });
+ },
+
+ setSequence: function(element, new_sequence) {
+ element = $(element);
+ var options = Object.extend(this.options(element), arguments[2] || {});
+
+ var nodeMap = {};
+ this.findElements(element, options).each( function(n) {
+ if (n.id.match(options.format))
+ nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+ n.parentNode.removeChild(n);
+ });
+
+ new_sequence.each(function(ident) {
+ var n = nodeMap[ident];
+ if (n) {
+ n[1].appendChild(n[0]);
+ delete nodeMap[ident];
+ }
+ });
+ },
+
+ serialize: function(element) {
+ element = $(element);
+ var options = Object.extend(Sortable.options(element), arguments[1] || {});
+ var name = encodeURIComponent(
+ (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+
+ if (options.tree) {
+ return Sortable.tree(element, arguments[1]).children.map( function (item) {
+ return [name + Sortable._constructIndex(item) + "[id]=" +
+ encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+ }).flatten().join('&');
+ } else {
+ return Sortable.sequence(element, arguments[1]).map( function(item) {
+ return name + "[]=" + encodeURIComponent(item);
+ }).join('&');
+ }
+ }
+}
+
+// Returns true if child is contained within element
+Element.isParent = function(child, element) {
+ if (!child.parentNode || child == element) return false;
+ if (child.parentNode == element) return true;
+ return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {
+ if(!element.hasChildNodes()) return null;
+ tagName = tagName.toUpperCase();
+ if(only) only = [only].flatten();
+ var elements = [];
+ $A(element.childNodes).each( function(e) {
+ if(e.tagName && e.tagName.toUpperCase()==tagName &&
+ (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+ elements.push(e);
+ if(recursive) {
+ var grandchildren = Element.findChildren(e, only, recursive, tagName);
+ if(grandchildren) elements.push(grandchildren);
+ }
+ });
+
+ return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+ return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/effects.js b/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/effects.js
new file mode 100644
index 00000000..3b02eda2
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/effects.js
@@ -0,0 +1,1088 @@
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+// Justin Palmer (http://encytemedia.com/)
+// Mark Pilgrim (http://diveintomark.org/)
+// Martin Bialasinki
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
+ var color = '#';
+ if(this.slice(0,4) == 'rgb(') {
+ var cols = this.slice(4,this.length-1).split(',');
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+ } else {
+ if(this.slice(0,1) == '#') {
+ if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+ if(this.length==7) color = this.toLowerCase();
+ }
+ }
+ return(color.length==7 ? color : (arguments[0] || this));
+}
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+ }).flatten().join('');
+}
+
+Element.collectTextNodesIgnoreClass = function(element, className) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
+ Element.collectTextNodesIgnoreClass(node, className) : ''));
+ }).flatten().join('');
+}
+
+Element.setContentZoom = function(element, percent) {
+ element = $(element);
+ element.setStyle({fontSize: (percent/100) + 'em'});
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+ return element;
+}
+
+Element.getOpacity = function(element){
+ element = $(element);
+ var opacity;
+ if (opacity = element.getStyle('opacity'))
+ return parseFloat(opacity);
+ if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if(opacity[1]) return parseFloat(opacity[1]) / 100;
+ return 1.0;
+}
+
+Element.setOpacity = function(element, value){
+ element= $(element);
+ if (value == 1){
+ element.setStyle({ opacity:
+ (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
+ 0.999999 : 1.0 });
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
+ element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
+ } else {
+ if(value < 0.00001) value = 0;
+ element.setStyle({opacity: value});
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
+ element.setStyle(
+ { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
+ 'alpha(opacity='+value*100+')' });
+ }
+ return element;
+}
+
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
+}
+
+Element.forceRerendering = function(element) {
+ try {
+ element = $(element);
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Array.prototype.call = function() {
+ var args = arguments;
+ this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+ _elementDoesNotExistError: {
+ name: 'ElementDoesNotExistError',
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
+ },
+ tagifyText: function(element) {
+ if(typeof Builder == 'undefined')
+ throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
+
+ var tagifyStyle = 'position:relative';
+ if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
+
+ element = $(element);
+ $A(element.childNodes).each( function(child) {
+ if(child.nodeType==3) {
+ child.nodeValue.toArray().each( function(character) {
+ element.insertBefore(
+ Builder.node('span',{style: tagifyStyle},
+ character == ' ' ? String.fromCharCode(160) : character),
+ child);
+ });
+ Element.remove(child);
+ }
+ });
+ },
+ multiple: function(element, effect) {
+ var elements;
+ if(((typeof element == 'object') ||
+ (typeof element == 'function')) &&
+ (element.length))
+ elements = element;
+ else
+ elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || {});
+ var masterDelay = options.delay;
+
+ $A(elements).each( function(element, index) {
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+ });
+ },
+ PAIRS: {
+ 'slide': ['SlideDown','SlideUp'],
+ 'blind': ['BlindDown','BlindUp'],
+ 'appear': ['Appear','Fade']
+ },
+ toggle: function(element, effect) {
+ element = $(element);
+ effect = (effect || 'appear').toLowerCase();
+ var options = Object.extend({
+ queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+ }, arguments[2] || {});
+ Effect[element.visible() ?
+ Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+ }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {
+ linear: Prototype.K,
+ sinoidal: function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+ },
+ reverse: function(pos) {
+ return 1-pos;
+ },
+ flicker: function(pos) {
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+ },
+ wobble: function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+ },
+ pulse: function(pos, pulses) {
+ pulses = pulses || 5;
+ return (
+ Math.round((pos % (1/pulses)) * pulses) == 0 ?
+ ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
+ 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
+ );
+ },
+ none: function(pos) {
+ return 0;
+ },
+ full: function(pos) {
+ return 1;
+ }
+};
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create();
+Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
+ initialize: function() {
+ this.effects = [];
+ this.interval = null;
+ },
+ _each: function(iterator) {
+ this.effects._each(iterator);
+ },
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ var position = (typeof effect.options.queue == 'string') ?
+ effect.options.queue : effect.options.queue.position;
+
+ switch(position) {
+ case 'front':
+ // move unstarted effects after this effect
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+ e.startOn += effect.finishOn;
+ e.finishOn += effect.finishOn;
+ });
+ break;
+ case 'with-last':
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+
+ if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+ this.effects.push(effect);
+
+ if(!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 40);
+ },
+ remove: function(effect) {
+ this.effects = this.effects.reject(function(e) { return e==effect });
+ if(this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ this.effects.invoke('loop', timePos);
+ }
+});
+
+Effect.Queues = {
+ instances: $H(),
+ get: function(queueName) {
+ if(typeof queueName != 'string') return queueName;
+
+ if(!this.instances[queueName])
+ this.instances[queueName] = new Effect.ScopedQueue();
+
+ return this.instances[queueName];
+ }
+}
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.DefaultOptions = {
+ transition: Effect.Transitions.sinoidal,
+ duration: 1.0, // seconds
+ fps: 25.0, // max. 25fps due to Effect.Queue implementation
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+}
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+ position: null,
+ start: function(options) {
+ this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
+ this.currentFrame = 0;
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
+ this.finishOn = this.startOn + (this.options.duration*1000);
+ this.event('beforeStart');
+ if(!this.options.sync)
+ Effect.Queues.get(typeof this.options.queue == 'string' ?
+ 'global' : this.options.queue.scope).add(this);
+ },
+ loop: function(timePos) {
+ if(timePos >= this.startOn) {
+ if(timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ this.event('beforeFinish');
+ if(this.finish) this.finish();
+ this.event('afterFinish');
+ return;
+ }
+ var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
+ var frame = Math.round(pos * this.options.fps * this.options.duration);
+ if(frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ }
+ },
+ render: function(pos) {
+ if(this.state == 'idle') {
+ this.state = 'running';
+ this.event('beforeSetup');
+ if(this.setup) this.setup();
+ this.event('afterSetup');
+ }
+ if(this.state == 'running') {
+ if(this.options.transition) pos = this.options.transition(pos);
+ pos *= (this.options.to-this.options.from);
+ pos += this.options.from;
+ this.position = pos;
+ this.event('beforeUpdate');
+ if(this.update) this.update(pos);
+ this.event('afterUpdate');
+ }
+ },
+ cancel: function() {
+ if(!this.options.sync)
+ Effect.Queues.get(typeof this.options.queue == 'string' ?
+ 'global' : this.options.queue.scope).remove(this);
+ this.state = 'finished';
+ },
+ event: function(eventName) {
+ if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if(this.options[eventName]) this.options[eventName](this);
+ },
+ inspect: function() {
+ return '#';
+ }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ this.effects.invoke('render', position);
+ },
+ finish: function(position) {
+ this.effects.each( function(effect) {
+ effect.render(1.0);
+ effect.cancel();
+ effect.event('beforeFinish');
+ if(effect.finish) effect.finish(position);
+ effect.event('afterFinish');
+ });
+ }
+});
+
+Effect.Event = Class.create();
+Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
+ initialize: function() {
+ var options = Object.extend({
+ duration: 0
+ }, arguments[0] || {});
+ this.start(options);
+ },
+ update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
+ // make this work on IE on elements without 'layout'
+ if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ var options = Object.extend({
+ from: this.element.getOpacity() || 0.0,
+ to: 1.0
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ update: function(position) {
+ this.element.setOpacity(position);
+ }
+});
+
+Effect.Move = Class.create();
+Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ x: 0,
+ y: 0,
+ mode: 'relative'
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function() {
+ // Bug in Opera: Opera returns the "real" position of a static element or
+ // relative element that does not have top/left explicitly set.
+ // ==> Always set top and left for position relative elements in your stylesheets
+ // (to 0 if you do not need them)
+ this.element.makePositioned();
+ this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+ this.originalTop = parseFloat(this.element.getStyle('top') || '0');
+ if(this.options.mode == 'absolute') {
+ // absolute movement, so we need to calc deltaX and deltaY
+ this.options.x = this.options.x - this.originalLeft;
+ this.options.y = this.options.y - this.originalTop;
+ }
+ },
+ update: function(position) {
+ this.element.setStyle({
+ left: Math.round(this.options.x * position + this.originalLeft) + 'px',
+ top: Math.round(this.options.y * position + this.originalTop) + 'px'
+ });
+ }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+ return new Effect.Move(element,
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
+};
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+ initialize: function(element, percent) {
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or {} with provided values
+ scaleFrom: 100.0,
+ scaleTo: percent
+ }, arguments[2] || {});
+ this.start(options);
+ },
+ setup: function() {
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+ this.elementPositioning = this.element.getStyle('position');
+
+ this.originalStyle = {};
+ ['top','left','width','height','fontSize'].each( function(k) {
+ this.originalStyle[k] = this.element.style[k];
+ }.bind(this));
+
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+
+ var fontSize = this.element.getStyle('font-size') || '100%';
+ ['em','px','%','pt'].each( function(fontSizeType) {
+ if(fontSize.indexOf(fontSizeType)>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = fontSizeType;
+ }
+ }.bind(this));
+
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+
+ this.dims = null;
+ if(this.options.scaleMode=='box')
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+ if(/^content/.test(this.options.scaleMode))
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+ if(!this.dims)
+ this.dims = [this.options.scaleMode.originalHeight,
+ this.options.scaleMode.originalWidth];
+ },
+ update: function(position) {
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if(this.options.scaleContent && this.fontSize)
+ this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+ },
+ finish: function(position) {
+ if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+ },
+ setDimensions: function(height, width) {
+ var d = {};
+ if(this.options.scaleX) d.width = Math.round(width) + 'px';
+ if(this.options.scaleY) d.height = Math.round(height) + 'px';
+ if(this.options.scaleFromCenter) {
+ var topd = (height - this.dims[0])/2;
+ var leftd = (width - this.dims[1])/2;
+ if(this.elementPositioning == 'absolute') {
+ if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+ if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+ } else {
+ if(this.options.scaleY) d.top = -topd + 'px';
+ if(this.options.scaleX) d.left = -leftd + 'px';
+ }
+ }
+ this.element.setStyle(d);
+ }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function() {
+ // Prevent executing on elements not in the layout flow
+ if(this.element.getStyle('display')=='none') { this.cancel(); return; }
+ // Disable background image during the effect
+ this.oldStyle = {
+ backgroundImage: this.element.getStyle('background-image') };
+ this.element.setStyle({backgroundImage: 'none'});
+ if(!this.options.endcolor)
+ this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+ if(!this.options.restorecolor)
+ this.options.restorecolor = this.element.getStyle('background-color');
+ // init color calculations
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+ },
+ update: function(position) {
+ this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+ return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+ },
+ finish: function() {
+ this.element.setStyle(Object.extend(this.oldStyle, {
+ backgroundColor: this.options.restorecolor
+ }));
+ }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ this.start(arguments[1] || {});
+ },
+ setup: function() {
+ Position.prepare();
+ var offsets = Position.cumulativeOffset(this.element);
+ if(this.options.offset) offsets[1] += this.options.offset;
+ var max = window.innerHeight ?
+ window.height - window.innerHeight :
+ document.body.scrollHeight -
+ (document.documentElement.clientHeight ?
+ document.documentElement.clientHeight : document.body.clientHeight);
+ this.scrollStart = Position.deltaY;
+ this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+ },
+ update: function(position) {
+ Position.prepare();
+ window.scrollTo(Position.deltaX,
+ this.scrollStart + (position*this.delta));
+ }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ var options = Object.extend({
+ from: element.getOpacity() || 1.0,
+ to: 0.0,
+ afterFinishInternal: function(effect) {
+ if(effect.options.to!=0) return;
+ effect.element.hide().setStyle({opacity: oldOpacity});
+ }}, arguments[1] || {});
+ return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+ to: 1.0,
+ // force Safari to render floated elements properly
+ afterFinishInternal: function(effect) {
+ effect.element.forceRerendering();
+ },
+ beforeSetup: function(effect) {
+ effect.element.setOpacity(effect.options.from).show();
+ }}, arguments[1] || {});
+ return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+ element = $(element);
+ var oldStyle = {
+ opacity: element.getInlineOpacity(),
+ position: element.getStyle('position'),
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height
+ };
+ return new Effect.Parallel(
+ [ new Effect.Scale(element, 200,
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+ Object.extend({ duration: 1.0,
+ beforeSetupInternal: function(effect) {
+ Position.absolutize(effect.effects[0].element)
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().setStyle(oldStyle); }
+ }, arguments[1] || {})
+ );
+}
+
+Effect.BlindUp = function(element) {
+ element = $(element);
+ element.makeClipping();
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ restoreAfterFinish: true,
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
+ }, arguments[1] || {})
+ );
+}
+
+Effect.BlindDown = function(element) {
+ element = $(element);
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping();
+ }
+ }, arguments[1] || {}));
+}
+
+Effect.SwitchOff = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ return new Effect.Appear(element, Object.extend({
+ duration: 0.4,
+ from: 0,
+ transition: Effect.Transitions.flicker,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(effect.element, 1, {
+ duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makePositioned().makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+ }
+ })
+ }
+ }, arguments[1] || {}));
+}
+
+Effect.DropOut = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left'),
+ opacity: element.getInlineOpacity() };
+ return new Effect.Parallel(
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+ Object.extend(
+ { duration: 0.5,
+ beforeSetup: function(effect) {
+ effect.effects[0].element.makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+ }
+ }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left') };
+ return new Effect.Move(element,
+ { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+ effect.element.undoPositioned().setStyle(oldStyle);
+ }}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+ element = $(element).cleanWhitespace();
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+ var oldInnerBottom = element.down().getStyle('bottom');
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: window.opera ? 0 : 1,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.down().makePositioned();
+ if(window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
+ afterUpdateInternal: function(effect) {
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+ }, arguments[1] || {})
+ );
+}
+
+Effect.SlideUp = function(element) {
+ element = $(element).cleanWhitespace();
+ var oldInnerBottom = element.down().getStyle('bottom');
+ return new Effect.Scale(element, window.opera ? 0 : 1,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'box',
+ scaleFrom: 100,
+ restoreAfterFinish: true,
+ beforeStartInternal: function(effect) {
+ effect.element.makePositioned();
+ effect.element.down().makePositioned();
+ if(window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping().show();
+ },
+ afterUpdateInternal: function(effect) {
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
+ effect.element.down().undoPositioned();
+ }
+ }, arguments[1] || {})
+ );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish
+Effect.Squish = function(element) {
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
+ restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
+ });
+}
+
+Effect.Grow = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.full
+ }, arguments[1] || {});
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = dims.width;
+ initialMoveY = moveY = 0;
+ moveX = -dims.width;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = dims.height;
+ moveY = -dims.height;
+ break;
+ case 'bottom-right':
+ initialMoveX = dims.width;
+ initialMoveY = dims.height;
+ moveX = -dims.width;
+ moveY = -dims.height;
+ break;
+ case 'center':
+ initialMoveX = dims.width / 2;
+ initialMoveY = dims.height / 2;
+ moveX = -dims.width / 2;
+ moveY = -dims.height / 2;
+ break;
+ }
+
+ return new Effect.Move(element, {
+ x: initialMoveX,
+ y: initialMoveY,
+ duration: 0.01,
+ beforeSetup: function(effect) {
+ effect.element.hide().makeClipping().makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ new Effect.Parallel(
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+ new Effect.Scale(effect.element, 100, {
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+ ], Object.extend({
+ beforeSetup: function(effect) {
+ effect.effects[0].element.setStyle({height: '0px'}).show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
+ }
+ }, options)
+ )
+ }
+ });
+}
+
+Effect.Shrink = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.none
+ }, arguments[1] || {});
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = dims.width;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = dims.height;
+ break;
+ case 'bottom-right':
+ moveX = dims.width;
+ moveY = dims.height;
+ break;
+ case 'center':
+ moveX = dims.width / 2;
+ moveY = dims.height / 2;
+ break;
+ }
+
+ return new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+ ], Object.extend({
+ beforeStartInternal: function(effect) {
+ effect.effects[0].element.makePositioned().makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+ }, options)
+ );
+}
+
+Effect.Pulsate = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+ var oldOpacity = element.getInlineOpacity();
+ var transition = options.transition || Effect.Transitions.sinoidal;
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
+ reverser.bind(transition);
+ return new Effect.Opacity(element,
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
+ afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+ }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height };
+ element.makeClipping();
+ return new Effect.Scale(element, 5, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
+ scaleY: false,
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().setStyle(oldStyle);
+ } });
+ }}, arguments[1] || {}));
+};
+
+Effect.Morph = Class.create();
+Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ style: ''
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function(){
+ function parseColor(color){
+ if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+ color = color.parseColor();
+ return $R(0,2).map(function(i){
+ return parseInt( color.slice(i*2+1,i*2+3), 16 )
+ });
+ }
+ this.transforms = this.options.style.parseStyle().map(function(property){
+ var originalValue = this.element.getStyle(property[0]);
+ return $H({
+ style: property[0],
+ originalValue: property[1].unit=='color' ?
+ parseColor(originalValue) : parseFloat(originalValue || 0),
+ targetValue: property[1].unit=='color' ?
+ parseColor(property[1].value) : property[1].value,
+ unit: property[1].unit
+ });
+ }.bind(this)).reject(function(transform){
+ return (
+ (transform.originalValue == transform.targetValue) ||
+ (
+ transform.unit != 'color' &&
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+ )
+ )
+ });
+ },
+ update: function(position) {
+ var style = $H(), value = null;
+ this.transforms.each(function(transform){
+ value = transform.unit=='color' ?
+ $R(0,2).inject('#',function(m,v,i){
+ return m+(Math.round(transform.originalValue[i]+
+ (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
+ transform.originalValue + Math.round(
+ ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
+ style[transform.style] = value;
+ });
+ this.element.setStyle(style);
+ }
+});
+
+Effect.Transform = Class.create();
+Object.extend(Effect.Transform.prototype, {
+ initialize: function(tracks){
+ this.tracks = [];
+ this.options = arguments[1] || {};
+ this.addTracks(tracks);
+ },
+ addTracks: function(tracks){
+ tracks.each(function(track){
+ var data = $H(track).values().first();
+ this.tracks.push($H({
+ ids: $H(track).keys().first(),
+ effect: Effect.Morph,
+ options: { style: data }
+ }));
+ }.bind(this));
+ return this;
+ },
+ play: function(){
+ return new Effect.Parallel(
+ this.tracks.map(function(track){
+ var elements = [$(track.ids) || $$(track.ids)].flatten();
+ return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
+ }).flatten(),
+ this.options
+ );
+ }
+});
+
+Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',
+ 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle',
+ 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
+ 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
+ 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
+ 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
+ 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
+ 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
+ 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
+ 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
+ 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
+ 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
+ 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
+ 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
+ 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
+ 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
+ 'width', 'wordSpacing', 'zIndex'];
+
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.prototype.parseStyle = function(){
+ var element = Element.extend(document.createElement('div'));
+ element.innerHTML = '
';
+ var style = element.down().style, styleRules = $H();
+
+ Element.CSS_PROPERTIES.each(function(property){
+ if(style[property]) styleRules[property] = style[property];
+ });
+
+ var result = $H();
+
+ styleRules.each(function(pair){
+ var property = pair[0], value = pair[1], unit = null;
+
+ if(value.parseColor('#zzzzzz') != '#zzzzzz') {
+ value = value.parseColor();
+ unit = 'color';
+ } else if(Element.CSS_LENGTH.test(value))
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
+ value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
+
+ result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
+ }.bind(this));
+
+ return result;
+};
+
+Element.morph = function(element, style) {
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
+ return element;
+};
+
+['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
+ 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
+ function(f) { Element.Methods[f] = Element[f]; }
+);
+
+Element.Methods.visualEffect = function(element, effect, options) {
+ s = effect.gsub(/_/, '-').camelize();
+ effect_class = s.charAt(0).toUpperCase() + s.substring(1);
+ new Effect[effect_class](element, options);
+ return $(element);
+};
+
+Element.addMethods();
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/prototype.js b/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/prototype.js
new file mode 100644
index 00000000..9ec6363c
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/helpers/javascripts/prototype.js
@@ -0,0 +1,2385 @@
+/* Prototype JavaScript framework, version 1.5.0_rc2
+ * (c) 2005, 2006 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.5.0_rc2',
+ BrowserFeatures: {
+ XPath: !!document.evaluate
+ },
+
+ ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)',
+ emptyFunction: function() {},
+ K: function(x) { return x }
+}
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+ for (var property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (object === undefined) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({}, object);
+ }
+});
+
+Function.prototype.bind = function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
+ }
+}
+
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ var digits = this.toString(16);
+ if (this < 16) return '0' + digits;
+ return digits;
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ }
+});
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.callback(this);
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+}
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += (replacement(match) || '').toString();
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = count === undefined ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return this;
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = truncation === undefined ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : this;
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var div = document.createElement('div');
+ var text = document.createTextNode(this);
+ div.appendChild(text);
+ return div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return {};
+
+ return match[1].split(separator || '&').inject({}, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var name = decodeURIComponent(pair[0]);
+ var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
+
+ if (hash[name] !== undefined) {
+ if (hash[name].constructor != Array)
+ hash[name] = [hash[name]];
+ if (value) hash[name].push(value);
+ }
+ else hash[name] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ camelize: function() {
+ var oStringList = this.split('-');
+ if (oStringList.length == 1) return oStringList[0];
+
+ var camelizedString = this.indexOf('-') == 0
+ ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+ : oStringList[0];
+
+ for (var i = 1, length = oStringList.length; i < length; i++) {
+ var s = oStringList[i];
+ camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+ }
+
+ return camelizedString;
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'-').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.replace(/\\/g, '\\\\');
+ if (useDoubleQuotes)
+ return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ else
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (typeof replacement == 'function') return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ return this.template.gsub(this.pattern, function(match) {
+ var before = match[1];
+ if (before == '\\') return match[2];
+ return before + (object[match[3]] || '').toString();
+ });
+ }
+}
+
+var $break = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ try {
+ iterator(value, index++);
+ } catch (e) {
+ if (e != $continue) throw e;
+ }
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator) {
+ var index = -number, slices = [], array = this.toArray();
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.collect(iterator || Prototype.K);
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!(iterator || Prototype.K)(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!(iterator || Prototype.K)(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = fillWith || null;
+ var results = this.eachSlice(number);
+ if (results.length > 0) (number - results.last().length).times(function() {
+ results.last().push(fillWith)
+ });
+ return results;
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.collect(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (result == undefined || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (result == undefined || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.collect(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.collect(Prototype.K);
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ inspect: function() {
+ return '#';
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0, length = iterable.length; i < length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+ Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != undefined || value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(value && value.constructor == Array ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ indexOf: function(object) {
+ for (var i = 0, length = this.length; i < length; i++)
+ if (this[i] == object) return i;
+ return -1;
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function() {
+ return this.inject([], function(array, value) {
+ return array.include(value) ? array : array.concat([value]);
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ }
+});
+
+Array.prototype.toArray = Array.prototype.clone;
+
+if(window.opera){
+ Array.prototype.concat = function(){
+ var array = [];
+ for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for(var i = 0, length = arguments.length; i < length; i++) {
+ if(arguments[i].constructor == Array) {
+ for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ }
+}
+var Hash = {
+ _each: function(iterator) {
+ for (var key in this) {
+ var value = this[key];
+ if (typeof value == 'function') continue;
+
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ merge: function(hash) {
+ return $H(hash).inject(this, function(mergedHash, pair) {
+ mergedHash[pair.key] = pair.value;
+ return mergedHash;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ if (!pair.key) return null;
+
+ if (pair.value && pair.value.constructor == Array) {
+ pair.value = pair.value.compact();
+
+ if (pair.value.length < 2) {
+ pair.value = pair.value.reduce();
+ } else {
+ var key = encodeURIComponent(pair.key);
+ return pair.value.map(function(value) {
+ return key + '=' + encodeURIComponent(value);
+ }).join('&');
+ }
+ }
+
+ if (pair.value == undefined) pair[1] = '';
+ return pair.map(encodeURIComponent).join('=');
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#';
+ }
+}
+
+function $H(object) {
+ var hash = Object.extend({}, object || {});
+ Object.extend(hash, Enumerable);
+ Object.extend(hash, Hash);
+ return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+
+ this.options.method = this.options.method.toLowerCase();
+ this.options.parameters = $H(typeof this.options.parameters == 'string' ?
+ this.options.parameters.toQueryParams() : this.options.parameters);
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ _complete: false,
+
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ var params = this.options.parameters;
+ if (params.any()) params['_'] = '';
+
+ if (!['get', 'post'].include(this.options.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.options.method;
+ this.options.method = 'post';
+ }
+
+ this.url = url;
+
+ // when GET, append parameters to URL
+ if (this.options.method == 'get' && params.any())
+ this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') +
+ params.toQueryString();
+
+ try {
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.options.method.toUpperCase(), this.url,
+ this.options.asynchronous, this.options.username,
+ this.options.password);
+
+ if (this.options.asynchronous)
+ setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ var body = this.options.method == 'post' ?
+ (this.options.postBody || params.toQueryString()) : null;
+
+ this.transport.send(body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.options.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (typeof extras.push == 'function')
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ return !this.transport.status
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + state, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ if ((this.getHeader('Content-type') || '').strip().
+ match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+ this.evalResponse();
+
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) { return null }
+ },
+
+ evalJSON: function() {
+ try {
+ var json = this.getHeader('X-JSON');
+ return json ? eval('(' + json + ')') : null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, param) {
+ this.updateContent();
+ onComplete(transport, param);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.container[this.success() ? 'success' : 'failure'];
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts) response = response.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (this.options.insertion)
+ new this.options.insertion(receiver, response);
+ else
+ receiver.update(response);
+ }
+
+ if (this.success()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(query.snapshotItem(i));
+ return results;
+ }
+}
+
+document.getElementsByClassName = function(className, parentElement) {
+ if (Prototype.BrowserFeatures.XPath) {
+ var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+ return document._getElementsByXPath(q, parentElement);
+ } else {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ var elements = [], child;
+ for (var i = 0, length = children.length; i < length; i++) {
+ child = children[i];
+ if (Element.hasClassName(child, className))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element)
+ var Element = new Object();
+
+Element.extend = function(element) {
+ if (!element) return;
+ if (_nativeExtensions || element.nodeType == 3) return element;
+
+ if (!element._extended && element.tagName && element != window) {
+ var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
+
+ if (element.tagName == 'FORM')
+ Object.extend(methods, Form.Methods);
+ if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
+ Object.extend(methods, Form.Element.Methods);
+
+ Object.extend(methods, Element.Methods.Simulated);
+
+ for (var property in methods) {
+ var value = methods[property];
+ if (typeof value == 'function' && !(property in element))
+ element[property] = cache.findOrStore(value);
+ }
+ }
+
+ element._extended = true;
+ return element;
+}
+
+Element.extend.cache = {
+ findOrStore: function(value) {
+ return this[value] = this[value] || function() {
+ return value.apply(null, [this].concat($A(arguments)));
+ }
+ }
+}
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ $(element).style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ $(element).style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, html) {
+ html = typeof html == 'undefined' ? '' : html.toString();
+ $(element).innerHTML = html.stripScripts();
+ setTimeout(function() {html.evalScripts()}, 10);
+ return element;
+ },
+
+ replace: function(element, html) {
+ element = $(element);
+ if (element.outerHTML) {
+ element.outerHTML = html.stripScripts();
+ } else {
+ var range = element.ownerDocument.createRange();
+ range.selectNodeContents(element);
+ element.parentNode.replaceChild(
+ range.createContextualFragment(html.stripScripts()), element);
+ }
+ setTimeout(function() {html.evalScripts()}, 10);
+ return element;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ element = $(element);
+ return $A(element.getElementsByTagName('*'));
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ element = $(element);
+ if (typeof selector == 'string')
+ selector = new Selector(selector);
+ return selector.match(element);
+ },
+
+ up: function(element, expression, index) {
+ return Selector.findElement($(element).ancestors(), expression, index);
+ },
+
+ down: function(element, expression, index) {
+ return Selector.findElement($(element).descendants(), expression, index);
+ },
+
+ previous: function(element, expression, index) {
+ return Selector.findElement($(element).previousSiblings(), expression, index);
+ },
+
+ next: function(element, expression, index) {
+ return Selector.findElement($(element).nextSiblings(), expression, index);
+ },
+
+ getElementsBySelector: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ getElementsByClassName: function(element, className) {
+ element = $(element);
+ return document.getElementsByClassName(className, element);
+ },
+
+ readAttribute: function(element, name) {
+ return $(element).getAttribute(name);
+ },
+
+ getHeight: function(element) {
+ element = $(element);
+ return element.offsetHeight;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ if (elementClassName.length == 0) return false;
+ if (elementClassName == className ||
+ elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ return true;
+ return false;
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ Element.classNames(element).add(className);
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ Element.classNames(element).remove(className);
+ return element;
+ },
+
+ observe: function() {
+ Event.observe.apply(Event, arguments);
+ return $A(arguments).first();
+ },
+
+ stopObserving: function() {
+ Event.stopObserving.apply(Event, arguments);
+ return $A(arguments).first();
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.match(/^\s*$/);
+ },
+
+ childOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+ while (element = element.parentNode)
+ if (element == ancestor) return true;
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var x = element.x ? element.x : element.offsetLeft,
+ y = element.y ? element.y : element.offsetTop;
+ window.scrollTo(x, y);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ var inline = (style == 'float' ?
+ (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat') : style);
+ var value = element.style[inline.camelize()];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[inline.camelize()];
+ }
+ }
+
+ if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
+ value = element['offset'+style.charAt(0).toUpperCase()+style.substring(1)] + 'px';
+
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+ return value == 'auto' ? null : value;
+ },
+
+ setStyle: function(element, style) {
+ element = $(element);
+ for (var name in style)
+ element.style[ (name == 'float' ?
+ ((typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat') : name).camelize()
+ ] = style[name];
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'display') != 'none')
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = '';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = 'none';
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = element.style.overflow || 'auto';
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ }
+}
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ return $(element).getAttributeNode(attribute).specified;
+ }
+}
+
+// IE is missing .innerHTML support for TABLE-related elements
+if(document.all){
+ Element.Methods.update = function(element, html) {
+ element = $(element);
+ html = typeof html == 'undefined' ? '' : html.toString();
+ var tagName = element.tagName.toUpperCase();
+ if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+ var div = document.createElement('div');
+ switch (tagName) {
+ case 'THEAD':
+ case 'TBODY':
+ div.innerHTML = '' + html.stripScripts() + '
';
+ depth = 2;
+ break;
+ case 'TR':
+ div.innerHTML = '' + html.stripScripts() + '
';
+ depth = 3;
+ break;
+ case 'TD':
+ div.innerHTML = '' + html.stripScripts() + '
';
+ depth = 4;
+ }
+ $A(element.childNodes).each(function(node){
+ element.removeChild(node)
+ });
+ depth.times(function(){ div = div.firstChild });
+
+ $A(div.childNodes).each(
+ function(node){ element.appendChild(node) });
+ } else {
+ element.innerHTML = html.stripScripts();
+ }
+ setTimeout(function() {html.evalScripts()}, 10);
+ return element;
+ }
+}
+
+Object.extend(Element, Element.Methods);
+
+var _nativeExtensions = false;
+
+if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
+ var className = 'HTML' + tag + 'Element';
+ if(window[className]) return;
+ var klass = window[className] = {};
+ klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
+ });
+
+Element.addMethods = function(methods) {
+ Object.extend(Element.Methods, methods || {});
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ var cache = Element.extend.cache;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = cache.findOrStore(value);
+ }
+ }
+
+ if (typeof HTMLElement != 'undefined') {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ copy(Form.Methods, HTMLFormElement.prototype);
+ [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
+ copy(Form.Element.Methods, klass.prototype);
+ });
+ _nativeExtensions = true;
+ }
+}
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content.stripScripts();
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ try {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } catch (e) {
+ var tagName = this.element.tagName.toUpperCase();
+ if (['TBODY', 'TR'].include(tagName)) {
+ this.insertContent(this.contentFromAnonymousTable());
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.insertContent([this.range.createContextualFragment(this.content)]);
+ }
+
+ setTimeout(function() {content.evalScripts()}, 10);
+ },
+
+ contentFromAnonymousTable: function() {
+ var div = document.createElement('div');
+ div.innerHTML = '';
+ return $A(div.childNodes[0].childNodes[0].childNodes);
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment, this.element);
+ }).bind(this));
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function(fragments) {
+ fragments.reverse(false).each((function(fragment) {
+ this.element.insertBefore(fragment, this.element.firstChild);
+ }).bind(this));
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.appendChild(fragment);
+ }).bind(this));
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment,
+ this.element.nextSibling);
+ }).bind(this));
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Selector = Class.create();
+Selector.prototype = {
+ initialize: function(expression) {
+ this.params = {classNames: []};
+ this.expression = expression.toString().strip();
+ this.parseExpression();
+ this.compileMatcher();
+ },
+
+ parseExpression: function() {
+ function abort(message) { throw 'Parse error in selector: ' + message; }
+
+ if (this.expression == '') abort('empty expression');
+
+ var params = this.params, expr = this.expression, match, modifier, clause, rest;
+ while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
+ params.attributes = params.attributes || [];
+ params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
+ expr = match[1];
+ }
+
+ if (expr == '*') return this.params.wildcard = true;
+
+ while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
+ modifier = match[1], clause = match[2], rest = match[3];
+ switch (modifier) {
+ case '#': params.id = clause; break;
+ case '.': params.classNames.push(clause); break;
+ case '':
+ case undefined: params.tagName = clause.toUpperCase(); break;
+ default: abort(expr.inspect());
+ }
+ expr = rest;
+ }
+
+ if (expr.length > 0) abort(expr.inspect());
+ },
+
+ buildMatchExpression: function() {
+ var params = this.params, conditions = [], clause;
+
+ if (params.wildcard)
+ conditions.push('true');
+ if (clause = params.id)
+ conditions.push('element.id == ' + clause.inspect());
+ if (clause = params.tagName)
+ conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
+ if ((clause = params.classNames).length > 0)
+ for (var i = 0, length = clause.length; i < length; i++)
+ conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
+ if (clause = params.attributes) {
+ clause.each(function(attribute) {
+ var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
+ var splitValueBy = function(delimiter) {
+ return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
+ }
+
+ switch (attribute.operator) {
+ case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
+ case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
+ case '|=': conditions.push(
+ splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
+ ); break;
+ case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
+ case '':
+ case undefined: conditions.push(value + ' != null'); break;
+ default: throw 'Unknown operator ' + attribute.operator + ' in selector';
+ }
+ });
+ }
+
+ return conditions.join(' && ');
+ },
+
+ compileMatcher: function() {
+ this.match = new Function('element', 'if (!element.tagName) return false; \
+ return ' + this.buildMatchExpression());
+ },
+
+ findElements: function(scope) {
+ var element;
+
+ if (element = $(this.params.id))
+ if (this.match(element))
+ if (!scope || Element.childOf(element, scope))
+ return [element];
+
+ scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
+
+ var results = [];
+ for (var i = 0, length = scope.length; i < length; i++)
+ if (this.match(element = scope[i]))
+ results.push(Element.extend(element));
+
+ return results;
+ },
+
+ toString: function() {
+ return this.expression;
+ }
+}
+
+Object.extend(Selector, {
+ matchElements: function(elements, expression) {
+ var selector = new Selector(expression);
+ return elements.select(selector.match.bind(selector)).collect(Element.extend);
+ },
+
+ findElement: function(elements, expression, index) {
+ if (typeof expression == 'number') index = expression, expression = false;
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ return expressions.map(function(expression) {
+ return expression.strip().split(/\s+/).inject([null], function(results, expr) {
+ var selector = new Selector(expr);
+ return results.inject([], function(elements, result) {
+ return elements.concat(selector.findElements(result || element));
+ });
+ });
+ }).flatten();
+ }
+});
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements) {
+ return elements.inject([], function(queryComponents, element) {
+ var queryComponent = Form.Element.serialize(element);
+ if (queryComponent) queryComponents.push(queryComponent);
+ return queryComponents;
+ }).join('&');
+ }
+};
+
+Form.Methods = {
+ serialize: function(form) {
+ return Form.serializeElements($(form).getElements());
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name)
+ return inputs;
+
+ var matchingInputs = new Array();
+ for (var i = 0, length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) ||
+ (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ form.getElements().each(function(element) {
+ element.blur();
+ element.disabled = 'true';
+ });
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ form.getElements().each(function(element) {
+ element.disabled = '';
+ });
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ return $(form).getElements().find(function(element) {
+ return element.type != 'hidden' && !element.disabled &&
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ }
+}
+
+Object.extend(Form, Form.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+}
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (element.disabled) return '';
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter) {
+ var key = encodeURIComponent(parameter[0]);
+ if (key.length == 0) return;
+
+ if (parameter[1].constructor != Array)
+ parameter[1] = [parameter[1]];
+
+ return parameter[1].map(function(value) {
+ return key + '=' + encodeURIComponent(value);
+ }).join('&');
+ }
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return parameter[1];
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ element.focus();
+ if (element.select && ( element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type) ) )
+ element.select();
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.blur();
+ element.disabled = false;
+ return element;
+ }
+}
+
+Object.extend(Form.Element, Form.Element.Methods);
+var Field = Form.Element;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ default:
+ return Form.Element.Serializers.textarea(element);
+ }
+ return false;
+ },
+
+ inputSelector: function(element) {
+ if (element.checked)
+ return [element.name, element.value];
+ },
+
+ textarea: function(element) {
+ return [element.name, element.value];
+ },
+
+ select: function(element) {
+ return Form.Element.Serializers[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ },
+
+ selectOne: function(element) {
+ var value = '', opt, index = element.selectedIndex;
+ if (index >= 0) {
+ opt = Element.extend(element.options[index]);
+ // Uses the new potential extension if hasAttribute isn't native.
+ value = opt.hasAttribute('value') ? opt.value : opt.text;
+ }
+ return [element.name, value];
+ },
+
+ selectMany: function(element) {
+ var value = [];
+ for (var i = 0, length = element.length; i < length; i++) {
+ var opt = Element.extend(element.options[i]);
+ if (opt.selected)
+ // Uses the new potential extension if hasAttribute isn't native.
+ value.push(opt.hasAttribute('value') ? opt.value : opt.text);
+ }
+ return [element.name, value];
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback.bind(this));
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) {
+ var Event = new Object();
+}
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+
+ element: function(event) {
+ return event.target || event.srcElement;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ event.cancelBubble = true;
+ }
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
+ },
+
+ observers: false,
+
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
+ },
+
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0, length = Event.observers.length; i < length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
+ },
+
+ observe: function(element, name, observer, useCapture) {
+ element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.attachEvent))
+ name = 'keydown';
+
+ Event._observeAndCache(element, name, observer, useCapture);
+ },
+
+ stopObserving: function(element, name, observer, useCapture) {
+ element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.detachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ try {
+ element.detachEvent('on' + name, observer);
+ } catch (e) {}
+ }
+ }
+});
+
+/* prevent memory leaks in IE */
+if (navigator.appVersion.match(/\bMSIE\b/))
+ Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if(element.tagName=='BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ offsetParent: function(element) {
+ if (element.offsetParent) return element.offsetParent;
+ if (element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return element;
+
+ return document.body;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ page: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ if (!window.opera || element.tagName=='BODY') {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+ },
+
+ clone: function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.style.position == 'absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';;
+ element.style.left = left + 'px';;
+ element.style.width = width + 'px';;
+ element.style.height = height + 'px';;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.style.position == 'relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned. For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return [valueL, valueT];
+ }
+}
+
+Element.addMethods();
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/helpers/number_helper.rb b/tracks/vendor/rails/actionpack/lib/action_view/helpers/number_helper.rb
new file mode 100644
index 00000000..979ba03f
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/helpers/number_helper.rb
@@ -0,0 +1,155 @@
+module ActionView
+ module Helpers #:nodoc:
+ # Provides methods for converting a numbers into formatted strings.
+ # Methods are provided for phone numbers, currency, percentage,
+ # precision, positional notation, and file size.
+ module NumberHelper
+ # Formats a +number+ into a US phone number. You can customize the format
+ # in the +options+ hash.
+ # * :area_code - Adds parentheses around the area code.
+ # * :delimiter - Specifies the delimiter to use, defaults to "-".
+ # * :extension - Specifies an extension to add to the end of the
+ # generated number
+ # * :country_code - Sets the country code for the phone number.
+ #
+ # number_to_phone(1235551234) => 123-555-1234
+ # number_to_phone(1235551234, :area_code => true) => (123) 555-1234
+ # number_to_phone(1235551234, :delimiter => " ") => 123 555 1234
+ # number_to_phone(1235551234, :area_code => true, :extension => 555) => (123) 555-1234 x 555
+ # number_to_phone(1235551234, :country_code => 1)
+ def number_to_phone(number, options = {})
+ number = number.to_s.strip unless number.nil?
+ options = options.stringify_keys
+ area_code = options["area_code"] || nil
+ delimiter = options["delimiter"] || "-"
+ extension = options["extension"].to_s.strip || nil
+ country_code = options["country_code"] || nil
+
+ begin
+ str = ""
+ str << "+#{country_code}#{delimiter}" unless country_code.blank?
+ str << if area_code
+ number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
+ else
+ number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
+ end
+ str << " x #{extension}" unless extension.blank?
+ str
+ rescue
+ number
+ end
+ end
+
+ # Formats a +number+ into a currency string. You can customize the format
+ # in the +options+ hash.
+ # * :precision - Sets the level of precision, defaults to 2
+ # * :unit - Sets the denomination of the currency, defaults to "$"
+ # * :separator - Sets the separator between the units, defaults to "."
+ # * :delimiter - Sets the thousands delimiter, defaults to ","
+ #
+ # number_to_currency(1234567890.50) => $1,234,567,890.50
+ # number_to_currency(1234567890.506) => $1,234,567,890.51
+ # number_to_currency(1234567890.506, :precision => 3) => $1,234,567,890.506
+ # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "")
+ # => £1234567890,50
+ def number_to_currency(number, options = {})
+ options = options.stringify_keys
+ precision = options["precision"] || 2
+ unit = options["unit"] || "$"
+ separator = precision > 0 ? options["separator"] || "." : ""
+ delimiter = options["delimiter"] || ","
+
+ begin
+ parts = number_with_precision(number, precision).split('.')
+ unit + number_with_delimiter(parts[0], delimiter) + separator + parts[1].to_s
+ rescue
+ number
+ end
+ end
+
+ # Formats a +number+ as a percentage string. You can customize the
+ # format in the +options+ hash.
+ # * :precision - Sets the level of precision, defaults to 3
+ # * :separator - Sets the separator between the units, defaults to "."
+ #
+ # number_to_percentage(100) => 100.000%
+ # number_to_percentage(100, {:precision => 0}) => 100%
+ # number_to_percentage(302.0574, {:precision => 2}) => 302.06%
+ def number_to_percentage(number, options = {})
+ options = options.stringify_keys
+ precision = options["precision"] || 3
+ separator = options["separator"] || "."
+
+ begin
+ number = number_with_precision(number, precision)
+ parts = number.split('.')
+ if parts.at(1).nil?
+ parts[0] + "%"
+ else
+ parts[0] + separator + parts[1].to_s + "%"
+ end
+ rescue
+ number
+ end
+ end
+
+ # Formats a +number+ with grouped thousands using +delimiter+. You
+ # can customize the format in the +options+ hash.
+ # * :delimiter - Sets the thousands delimiter, defaults to ","
+ # * :separator - Sets the separator between the units, defaults to "."
+ #
+ # number_with_delimiter(12345678) => 12,345,678
+ # number_with_delimiter(12345678.05) => 12,345,678.05
+ # number_with_delimiter(12345678, :delimiter => ".") => 12.345.678
+ def number_with_delimiter(number, delimiter=",", separator=".")
+ begin
+ parts = number.to_s.split(separator)
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
+ parts.join separator
+ rescue
+ number
+ end
+ end
+
+ # Formats a +number+ with the specified level of +precision+. The default
+ # level of precision is 3.
+ #
+ # number_with_precision(111.2345) => 111.235
+ # number_with_precision(111.2345, 2) => 111.24
+ def number_with_precision(number, precision=3)
+ "%01.#{precision}f" % number
+ rescue
+ number
+ end
+
+ # Formats the bytes in +size+ into a more understandable representation.
+ # Useful for reporting file sizes to users. This method returns nil if
+ # +size+ cannot be converted into a number. You can change the default
+ # precision of 1 in +precision+.
+ #
+ # number_to_human_size(123) => 123 Bytes
+ # number_to_human_size(1234) => 1.2 KB
+ # number_to_human_size(12345) => 12.1 KB
+ # number_to_human_size(1234567) => 1.2 MB
+ # number_to_human_size(1234567890) => 1.1 GB
+ # number_to_human_size(1234567890123) => 1.1 TB
+ # number_to_human_size(1234567, 2) => 1.18 MB
+ def number_to_human_size(size, precision=1)
+ size = Kernel.Float(size)
+ case
+ when size == 1 : "1 Byte"
+ when size < 1.kilobyte: "%d Bytes" % size
+ when size < 1.megabyte: "%.#{precision}f KB" % (size / 1.0.kilobyte)
+ when size < 1.gigabyte: "%.#{precision}f MB" % (size / 1.0.megabyte)
+ when size < 1.terabyte: "%.#{precision}f GB" % (size / 1.0.gigabyte)
+ else "%.#{precision}f TB" % (size / 1.0.terabyte)
+ end.sub('.0', '')
+ rescue
+ nil
+ end
+
+ alias_method :human_size, :number_to_human_size # deprecated alias
+ deprecate :human_size => :number_to_human_size
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/helpers/pagination_helper.rb b/tracks/vendor/rails/actionpack/lib/action_view/helpers/pagination_helper.rb
new file mode 100644
index 00000000..6123b738
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/helpers/pagination_helper.rb
@@ -0,0 +1,86 @@
+module ActionView
+ module Helpers
+ # Provides methods for linking to ActionController::Pagination objects.
+ #
+ # You can also build your links manually, like in this example:
+ #
+ # <%= link_to "Previous page", { :page => paginator.current.previous } if paginator.current.previous %>
+ #
+ # <%= link_to "Next page", { :page => paginator.current.next } if paginator.current.next %>
+ module PaginationHelper
+ unless const_defined?(:DEFAULT_OPTIONS)
+ DEFAULT_OPTIONS = {
+ :name => :page,
+ :window_size => 2,
+ :always_show_anchors => true,
+ :link_to_current_page => false,
+ :params => {}
+ }
+ end
+
+ # Creates a basic HTML link bar for the given +paginator+.
+ # +html_options+ are passed to +link_to+.
+ #
+ # +options+ are:
+ # :name :: the routing name for this paginator
+ # (defaults to +page+)
+ # :window_size :: the number of pages to show around
+ # the current page (defaults to +2+)
+ # :always_show_anchors :: whether or not the first and last
+ # pages should always be shown
+ # (defaults to +true+)
+ # :link_to_current_page :: whether or not the current page
+ # should be linked to (defaults to
+ # +false+)
+ # :params :: any additional routing parameters
+ # for page URLs
+ def pagination_links(paginator, options={}, html_options={})
+ name = options[:name] || DEFAULT_OPTIONS[:name]
+ params = (options[:params] || DEFAULT_OPTIONS[:params]).clone
+
+ pagination_links_each(paginator, options) do |n|
+ params[name] = n
+ link_to(n.to_s, params, html_options)
+ end
+ end
+
+ # Iterate through the pages of a given +paginator+, invoking a
+ # block for each page number that needs to be rendered as a link.
+ def pagination_links_each(paginator, options)
+ options = DEFAULT_OPTIONS.merge(options)
+ link_to_current_page = options[:link_to_current_page]
+ always_show_anchors = options[:always_show_anchors]
+
+ current_page = paginator.current_page
+ window_pages = current_page.window(options[:window_size]).pages
+ return if window_pages.length <= 1 unless link_to_current_page
+
+ first, last = paginator.first, paginator.last
+
+ html = ''
+ if always_show_anchors and not (wp_first = window_pages[0]).first?
+ html << yield(first.number)
+ html << ' ... ' if wp_first.number - first.number > 1
+ html << ' '
+ end
+
+ window_pages.each do |page|
+ if current_page == page && !link_to_current_page
+ html << page.number.to_s
+ else
+ html << yield(page.number)
+ end
+ html << ' '
+ end
+
+ if always_show_anchors and not (wp_last = window_pages[-1]).last?
+ html << ' ... ' if last.number - wp_last.number > 1
+ html << yield(last.number)
+ end
+
+ html
+ end
+
+ end # PaginationHelper
+ end # Helpers
+end # ActionView
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/helpers/prototype_helper.rb b/tracks/vendor/rails/actionpack/lib/action_view/helpers/prototype_helper.rb
new file mode 100644
index 00000000..dab10a6b
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -0,0 +1,876 @@
+require 'set'
+
+module ActionView
+ module Helpers
+ # Provides a set of helpers for calling Prototype JavaScript functions,
+ # including functionality to call remote methods using
+ # Ajax[http://www.adaptivepath.com/publications/essays/archives/000385.php].
+ # This means that you can call actions in your controllers without
+ # reloading the page, but still update certain parts of it using
+ # injections into the DOM. The common use case is having a form that adds
+ # a new element to a list without reloading the page.
+ #
+ # To be able to use these helpers, you must include the Prototype
+ # JavaScript framework in your pages. See the documentation for
+ # ActionView::Helpers::JavaScriptHelper for more information on including
+ # the necessary JavaScript.
+ #
+ # See link_to_remote for documentation of options common to all Ajax
+ # helpers.
+ #
+ # See also ActionView::Helpers::ScriptaculousHelper for helpers which work
+ # with the Scriptaculous controls and visual effects library.
+ #
+ # See JavaScriptGenerator for information on updating multiple elements
+ # on the page in an Ajax response.
+ module PrototypeHelper
+ unless const_defined? :CALLBACKS
+ CALLBACKS = Set.new([ :uninitialized, :loading, :loaded,
+ :interactive, :complete, :failure, :success ] +
+ (100..599).to_a)
+ AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
+ :asynchronous, :method, :insertion, :position,
+ :form, :with, :update, :script ]).merge(CALLBACKS)
+ end
+
+ # Returns a link to a remote action defined by options[:url]
+ # (using the url_for format) that's called in the background using
+ # XMLHttpRequest. The result of that request can then be inserted into a
+ # DOM object whose id can be specified with options[:update] .
+ # Usually, the result would be a partial prepared by the controller with
+ # render :partial.
+ #
+ # Examples:
+ # link_to_remote "Delete this post", :update => "posts",
+ # :url => { :action => "destroy", :id => post.id }
+ # link_to_remote(image_tag("refresh"), :update => "emails",
+ # :url => { :action => "list_emails" })
+ #
+ # You can also specify a hash for options[:update] to allow for
+ # easy redirection of output to an other DOM element if a server-side
+ # error occurs:
+ #
+ # Example:
+ # link_to_remote "Delete this post",
+ # :url => { :action => "destroy", :id => post.id },
+ # :update => { :success => "posts", :failure => "error" }
+ #
+ # Optionally, you can use the options[:position] parameter to
+ # influence how the target DOM element is updated. It must be one of
+ # :before , :top , :bottom , or :after .
+ #
+ # The method used is by default POST. You can also specify GET or you
+ # can simulate PUT or DELETE over POST. All specified with options[:method]
+ #
+ # Example:
+ # link_to_remote "Destroy", person_url(:id => person), :method => :delete
+ #
+ # By default, these remote requests are processed asynchronous during
+ # which various JavaScript callbacks can be triggered (for progress
+ # indicators and the likes). All callbacks get access to the
+ # request object, which holds the underlying XMLHttpRequest.
+ #
+ # To access the server response, use request.responseText , to
+ # find out the HTTP status, use request.status .
+ #
+ # Example:
+ # link_to_remote word,
+ # :url => { :action => "undo", :n => word_counter },
+ # :complete => "undoRequestCompleted(request)"
+ #
+ # The callbacks that may be specified are (in order):
+ #
+ # :loading :: Called when the remote document is being
+ # loaded with data by the browser.
+ # :loaded :: Called when the browser has finished loading
+ # the remote document.
+ # :interactive :: Called when the user can interact with the
+ # remote document, even though it has not
+ # finished loading.
+ # :success :: Called when the XMLHttpRequest is completed,
+ # and the HTTP status code is in the 2XX range.
+ # :failure :: Called when the XMLHttpRequest is completed,
+ # and the HTTP status code is not in the 2XX
+ # range.
+ # :complete :: Called when the XMLHttpRequest is complete
+ # (fires after success/failure if they are
+ # present).
+ #
+ # You can further refine :success and :failure by
+ # adding additional callbacks for specific status codes.
+ #
+ # Example:
+ # link_to_remote word,
+ # :url => { :action => "action" },
+ # 404 => "alert('Not found...? Wrong URL...?')",
+ # :failure => "alert('HTTP Error ' + request.status + '!')"
+ #
+ # A status code callback overrides the success/failure handlers if
+ # present.
+ #
+ # If you for some reason or another need synchronous processing (that'll
+ # block the browser while the request is happening), you can specify
+ # options[:type] = :synchronous .
+ #
+ # You can customize further browser side call logic by passing in
+ # JavaScript code snippets via some optional parameters. In their order
+ # of use these are:
+ #
+ # :confirm :: Adds confirmation dialog.
+ # :condition :: Perform remote request conditionally
+ # by this expression. Use this to
+ # describe browser-side conditions when
+ # request should not be initiated.
+ # :before :: Called before request is initiated.
+ # :after :: Called immediately after request was
+ # initiated and before :loading .
+ # :submit :: Specifies the DOM element ID that's used
+ # as the parent of the form elements. By
+ # default this is the current form, but
+ # it could just as well be the ID of a
+ # table row or any other DOM element.
+ def link_to_remote(name, options = {}, html_options = {})
+ link_to_function(name, remote_function(options), html_options)
+ end
+
+ # Periodically calls the specified url (options[:url] ) every
+ # options[:frequency] seconds (default is 10). Usually used to
+ # update a specified div (options[:update] ) with the results
+ # of the remote call. The options for specifying the target with :url
+ # and defining callbacks is the same as link_to_remote.
+ def periodically_call_remote(options = {})
+ frequency = options[:frequency] || 10 # every ten seconds by default
+ code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})"
+ javascript_tag(code)
+ end
+
+ # Returns a form tag that will submit using XMLHttpRequest in the
+ # background instead of the regular reloading POST arrangement. Even
+ # though it's using JavaScript to serialize the form elements, the form
+ # submission will work just like a regular submission as viewed by the
+ # receiving side (all elements available in params ). The options for
+ # specifying the target with :url and defining callbacks is the same as
+ # link_to_remote.
+ #
+ # A "fall-through" target for browsers that doesn't do JavaScript can be
+ # specified with the :action/:method options on :html.
+ #
+ # Example:
+ # form_remote_tag :html => { :action =>
+ # url_for(:controller => "some", :action => "place") }
+ #
+ # The Hash passed to the :html key is equivalent to the options (2nd)
+ # argument in the FormTagHelper.form_tag method.
+ #
+ # By default the fall-through action is the same as the one specified in
+ # the :url (and the default method is :post).
+ #
+ # form_remote_tag also takes a block, like form_tag:
+ # <% form_remote_tag :url => '/posts' do -%>
+ # <%= submit_tag 'Save' %>
+ # <% end -%>
+ def form_remote_tag(options = {}, &block)
+ options[:form] = true
+
+ options[:html] ||= {}
+ options[:html][:onsubmit] =
+ (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") +
+ "#{remote_function(options)}; return false;"
+
+ form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &block)
+ end
+
+ # Works like form_remote_tag, but uses form_for semantics.
+ def remote_form_for(object_name, *args, &proc)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ concat(form_remote_tag(options), proc.binding)
+ fields_for(object_name, *(args << options), &proc)
+ concat('', proc.binding)
+ end
+ alias_method :form_remote_for, :remote_form_for
+
+ # Returns a button input tag that will submit form using XMLHttpRequest
+ # in the background instead of regular reloading POST arrangement.
+ # options argument is the same as in form_remote_tag .
+ def submit_to_remote(name, value, options = {})
+ options[:with] ||= 'Form.serialize(this.form)'
+
+ options[:html] ||= {}
+ options[:html][:type] = 'button'
+ options[:html][:onclick] = "#{remote_function(options)}; return false;"
+ options[:html][:name] = name
+ options[:html][:value] = value
+
+ tag("input", options[:html], false)
+ end
+
+ # Returns 'eval(request.responseText)' which is the JavaScript function
+ # that form_remote_tag can call in :complete to evaluate a multiple
+ # update return document using update_element_function calls.
+ def evaluate_remote_response
+ "eval(request.responseText)"
+ end
+
+ # Returns the JavaScript needed for a remote function.
+ # Takes the same arguments as link_to_remote.
+ #
+ # Example:
+ # { :action => :update_options }) %>">
+ # Hello
+ # World
+ #
+ def remote_function(options)
+ javascript_options = options_for_ajax(options)
+
+ update = ''
+ if options[:update] && options[:update].is_a?(Hash)
+ update = []
+ update << "success:'#{options[:update][:success]}'" if options[:update][:success]
+ update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
+ update = '{' + update.join(',') + '}'
+ elsif options[:update]
+ update << "'#{options[:update]}'"
+ end
+
+ function = update.empty? ?
+ "new Ajax.Request(" :
+ "new Ajax.Updater(#{update}, "
+
+ url_options = options[:url]
+ url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
+ function << "'#{url_for(url_options)}'"
+ function << ", #{javascript_options})"
+
+ function = "#{options[:before]}; #{function}" if options[:before]
+ function = "#{function}; #{options[:after]}" if options[:after]
+ function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
+ function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
+
+ return function
+ end
+
+ # Observes the field with the DOM ID specified by +field_id+ and makes
+ # an Ajax call when its contents have changed.
+ #
+ # Required +options+ are either of:
+ # :url :: +url_for+-style options for the action to call
+ # when the field has changed.
+ # :function :: Instead of making a remote call to a URL, you
+ # can specify a function to be called instead.
+ #
+ # Additional options are:
+ # :frequency :: The frequency (in seconds) at which changes to
+ # this field will be detected. Not setting this
+ # option at all or to a value equal to or less than
+ # zero will use event based observation instead of
+ # time based observation.
+ # :update :: Specifies the DOM ID of the element whose
+ # innerHTML should be updated with the
+ # XMLHttpRequest response text.
+ # :with :: A JavaScript expression specifying the
+ # parameters for the XMLHttpRequest. This defaults
+ # to 'value', which in the evaluated context
+ # refers to the new field value. If you specify a
+ # string without a "=", it'll be extended to mean
+ # the form key that the value should be assigned to.
+ # So :with => "term" gives "'term'=value". If a "=" is
+ # present, no extension will happen.
+ # :on :: Specifies which event handler to observe. By default,
+ # it's set to "changed" for text fields and areas and
+ # "click" for radio buttons and checkboxes. With this,
+ # you can specify it instead to be "blur" or "focus" or
+ # any other event.
+ #
+ # Additionally, you may specify any of the options documented in
+ # link_to_remote.
+ def observe_field(field_id, options = {})
+ if options[:frequency] && options[:frequency] > 0
+ build_observer('Form.Element.Observer', field_id, options)
+ else
+ build_observer('Form.Element.EventObserver', field_id, options)
+ end
+ end
+
+ # Like +observe_field+, but operates on an entire form identified by the
+ # DOM ID +form_id+. +options+ are the same as +observe_field+, except
+ # the default value of the :with option evaluates to the
+ # serialized (request string) value of the form.
+ def observe_form(form_id, options = {})
+ if options[:frequency]
+ build_observer('Form.Observer', form_id, options)
+ else
+ build_observer('Form.EventObserver', form_id, options)
+ end
+ end
+
+ # All the methods were moved to GeneratorMethods so that
+ # #include_helpers_from_context has nothing to overwrite.
+ class JavaScriptGenerator #:nodoc:
+ def initialize(context, &block) #:nodoc:
+ @context, @lines = context, []
+ include_helpers_from_context
+ @context.instance_exec(self, &block)
+ end
+
+ private
+ def include_helpers_from_context
+ @context.extended_by.each do |mod|
+ extend mod unless mod.name =~ /^ActionView::Helpers/
+ end
+ extend GeneratorMethods
+ end
+
+ # JavaScriptGenerator generates blocks of JavaScript code that allow you
+ # to change the content and presentation of multiple DOM elements. Use
+ # this in your Ajax response bodies, either in a ')
+ # => <script> do_nasty_stuff() </script>
+ # sanitize('Click here for $100 ')
+ # => Click here for $100
+ def sanitize(html)
+ # only do this if absolutely necessary
+ if html.index("<")
+ tokenizer = HTML::Tokenizer.new(html)
+ new_text = ""
+
+ while token = tokenizer.next
+ node = HTML::Node.parse(nil, 0, 0, token, false)
+ new_text << case node
+ when HTML::Tag
+ if VERBOTEN_TAGS.include?(node.name)
+ node.to_s.gsub(/, "<")
+ else
+ if node.closing != :close
+ node.attributes.delete_if { |attr,v| attr =~ VERBOTEN_ATTRS }
+ %w(href src).each do |attr|
+ node.attributes.delete attr if node.attributes[attr] =~ /^javascript:/i
+ end
+ end
+ node.to_s
+ end
+ else
+ node.to_s.gsub(/, "<")
+ end
+ end
+
+ html = new_text
+ end
+
+ html
+ end
+
+ # Strips all HTML tags from the +html+, including comments. This uses the
+ # html-scanner tokenizer and so its HTML parsing ability is limited by
+ # that of html-scanner.
+ def strip_tags(html)
+ return html if html.blank?
+ if html.index("<")
+ text = ""
+ tokenizer = HTML::Tokenizer.new(html)
+
+ while token = tokenizer.next
+ node = HTML::Node.parse(nil, 0, 0, token, false)
+ # result is only the content of any Text nodes
+ text << node.to_s if node.class == HTML::Text
+ end
+ # strip any comments, and if they have a newline at the end (ie. line with
+ # only a comment) strip that too
+ text.gsub(/[\n]?/m, "")
+ else
+ html # already plain text
+ end
+ end
+
+ # Creates a Cycle object whose _to_s_ method cycles through elements of an
+ # array every time it is called. This can be used for example, to alternate
+ # classes for table rows:
+ #
+ # <% @items.each do |item| %>
+ # ">
+ # item
+ #
+ # <% end %>
+ #
+ # You can use named cycles to allow nesting in loops. Passing a Hash as
+ # the last parameter with a :name key will create a named cycle.
+ # You can manually reset a cycle by calling reset_cycle and passing the
+ # name of the cycle.
+ #
+ # <% @items.each do |item| %>
+ # "row_class")
+ #
+ # <% item.values.each do |value| %>
+ # "colors") -%>">
+ # value
+ #
+ # <% end %>
+ # <% reset_cycle("colors") %>
+ #
+ #
+ # <% end %>
+ def cycle(first_value, *values)
+ if (values.last.instance_of? Hash)
+ params = values.pop
+ name = params[:name]
+ else
+ name = "default"
+ end
+ values.unshift(first_value)
+
+ cycle = get_cycle(name)
+ if (cycle.nil? || cycle.values != values)
+ cycle = set_cycle(name, Cycle.new(*values))
+ end
+ return cycle.to_s
+ end
+
+ # Resets a cycle so that it starts from the first element the next time
+ # it is called. Pass in +name+ to reset a named cycle.
+ def reset_cycle(name = "default")
+ cycle = get_cycle(name)
+ cycle.reset unless cycle.nil?
+ end
+
+ class Cycle #:nodoc:
+ attr_reader :values
+
+ def initialize(first_value, *values)
+ @values = values.unshift(first_value)
+ reset
+ end
+
+ def reset
+ @index = 0
+ end
+
+ def to_s
+ value = @values[@index].to_s
+ @index = (@index + 1) % @values.size
+ return value
+ end
+ end
+
+ private
+ # The cycle helpers need to store the cycles in a place that is
+ # guaranteed to be reset every time a page is rendered, so it
+ # uses an instance variable of ActionView::Base.
+ def get_cycle(name)
+ @_cycles = Hash.new unless defined?(@_cycles)
+ return @_cycles[name]
+ end
+
+ def set_cycle(name, cycle_object)
+ @_cycles = Hash.new unless defined?(@_cycles)
+ @_cycles[name] = cycle_object
+ end
+
+ AUTO_LINK_RE = %r{
+ ( # leading text
+ <\w+.*?>| # leading HTML tag, or
+ [^=!:'"/]| # leading punctuation, or
+ ^ # beginning of line
+ )
+ (
+ (?:https?://)| # protocol spec, or
+ (?:www\.) # www.*
+ )
+ (
+ [-\w]+ # subdomain or domain
+ (?:\.[-\w]+)* # remaining subdomains or domain
+ (?::\d+)? # port
+ (?:/(?:[~\w%.;-]+)?)* # path
+ (?:\?[\w%&=.;-]+)? # query string
+ (?:\#\w*)? # trailing anchor
+ )
+ ([[:punct:]]|\s|<|$) # trailing text
+ }x unless const_defined?(:AUTO_LINK_RE)
+
+ # Turns all urls into clickable links. If a block is given, each url
+ # is yielded and the result is used as the link text.
+ def auto_link_urls(text, href_options = {})
+ extra_options = tag_options(href_options.stringify_keys) || ""
+ text.gsub(AUTO_LINK_RE) do
+ all, a, b, c, d = $&, $1, $2, $3, $4
+ if a =~ /#{text} #{d})
+ end
+ end
+ end
+
+ # Turns all email addresses into clickable links. If a block is given,
+ # each email is yielded and the result is used as the link text.
+ def auto_link_email_addresses(text)
+ text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
+ text = $1
+ text = yield(text) if block_given?
+ %{#{text} }
+ end
+ end
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb b/tracks/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb
new file mode 100644
index 00000000..21b5e907
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb
@@ -0,0 +1,400 @@
+require File.dirname(__FILE__) + '/javascript_helper'
+
+module ActionView
+ module Helpers #:nodoc:
+ # Provides a set of methods for making easy links and getting urls that
+ # depend on the controller and action. This means that you can use the
+ # same format for links in the views that you do in the controller.
+ module UrlHelper
+ include JavaScriptHelper
+
+ # Returns the URL for the set of +options+ provided. This takes the
+ # same options as url_for in action controller. For a list, see the
+ # documentation for ActionController::Base#url_for. Note that it'll
+ # set :only_path => true so you'll get the relative /controller/action
+ # instead of the fully qualified http://example.com/controller/action.
+ #
+ # When called from a view, url_for returns an HTML escaped url. If you
+ # need an unescaped url, pass :escape => false in the +options+.
+ def url_for(options = {}, *parameters_for_method_reference)
+ if options.kind_of? Hash
+ options = { :only_path => true }.update(options.symbolize_keys)
+ escape = options.key?(:escape) ? options.delete(:escape) : true
+ else
+ escape = true
+ end
+
+ url = @controller.send(:url_for, options, *parameters_for_method_reference)
+ escape ? html_escape(url) : url
+ end
+
+ # Creates a link tag of the given +name+ using a URL created by the set
+ # of +options+. See the valid options in the documentation for
+ # ActionController::Base#url_for. It's also possible to pass a string instead
+ # of an options hash to get a link tag that uses the value of the string as the
+ # href for the link. If nil is passed as a name, the link itself will become
+ # the name.
+ #
+ # The +html_options+ will accept a hash of html attributes for the link tag.
+ # It also accepts 3 modifiers that specialize the link behavior.
+ #
+ # * :confirm => 'question?' : This will add a JavaScript confirm
+ # prompt with the question specified. If the user accepts, the link is
+ # processed normally, otherwise no action is taken.
+ # * :popup => true || array of window options : This will force the
+ # link to open in a popup window. By passing true, a default browser window
+ # will be opened with the URL. You can also specify an array of options
+ # that are passed-thru to JavaScripts window.open method.
+ # * :method => symbol of HTTP verb : This modifier will dynamically
+ # create an HTML form and immediately submit the form for processing using
+ # the HTTP verb specified. Useful for having links perform a POST operation
+ # in dangerous actions like deleting a record (which search bots can follow
+ # while spidering your site). Supported verbs are :post, :delete and :put.
+ # Note that if the user has JavaScript disabled, the request will fall back
+ # to using GET. If you are relying on the POST behavior, your should check
+ # for it in your controllers action by using the request objects methods
+ # for post?, delete? or put?.
+ #
+ # You can mix and match the +html_options+ with the exception of
+ # :popup and :method which will raise an ActionView::ActionViewError
+ # exception.
+ #
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
+ # link_to "Help", { :action => "help" }, :popup => true
+ # link_to "View Image", { :action => "view" }, :popup => ['new_window_name', 'height=300,width=600']
+ # link_to "Delete Image", { :action => "delete", :id => @image.id }, :confirm => "Are you sure?", :method => :delete
+ def link_to(name, options = {}, html_options = nil, *parameters_for_method_reference)
+ if html_options
+ html_options = html_options.stringify_keys
+ convert_options_to_javascript!(html_options)
+ tag_options = tag_options(html_options)
+ else
+ tag_options = nil
+ end
+
+ url = options.is_a?(String) ? options : self.url_for(options, *parameters_for_method_reference)
+ "#{name || url} "
+ end
+
+ # Generates a form containing a single button that submits to the URL created
+ # by the set of +options+. This is the safest method to ensure links that
+ # cause changes to your data are not triggered by search bots or accelerators.
+ # If the HTML button does not work with your layout, you can also consider
+ # using the link_to method with the :method modifier as described in
+ # the link_to documentation.
+ #
+ # The generated FORM element has a class name of button-to
+ # to allow styling of the form itself and its children. You can control
+ # the form submission and input element behavior using +html_options+.
+ # This method accepts the :method and :confirm modifiers
+ # described in the link_to documentation. If no :method modifier
+ # is given, it will default to performing a POST operation. You can also
+ # disable the button by passing :disabled => true in +html_options+.
+ #
+ # button_to "New", :action => "new"
+ #
+ # Generates the following HTML:
+ #
+ #
+ #
+ # If you are using RESTful routes, you can pass the :method
+ # to change the HTTP verb used to submit the form.
+ #
+ # button_to "Delete Image", { :action => "delete", :id => @image.id },
+ # :confirm => "Are you sure?", :method => :delete
+ #
+ # Which generates the following HTML:
+ #
+ #
+ def button_to(name, options = {}, html_options = {})
+ html_options = html_options.stringify_keys
+ convert_boolean_attributes!(html_options, %w( disabled ))
+
+ method_tag = ''
+ if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
+ method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
+ end
+
+ form_method = method.to_s == 'get' ? 'get' : 'post'
+
+ if confirm = html_options.delete("confirm")
+ html_options["onclick"] = "return #{confirm_javascript_function(confirm)};"
+ end
+
+ url = options.is_a?(String) ? options : self.url_for(options)
+ name ||= url
+
+ html_options.merge!("type" => "submit", "value" => name)
+
+ ""
+ end
+
+
+ # DEPRECATED. It is reccommended to use the AssetTagHelper::image_tag within
+ # a link_to method to generate a linked image.
+ #
+ # link_to(image_tag("rss", :size => "30x45", :border => 0), "http://www.example.com")
+ def link_image_to(src, options = {}, html_options = {}, *parameters_for_method_reference)
+ image_options = { "src" => src.include?("/") ? src : "/images/#{src}" }
+ image_options["src"] += ".png" unless image_options["src"].include?(".")
+
+ html_options = html_options.stringify_keys
+ if html_options["alt"]
+ image_options["alt"] = html_options["alt"]
+ html_options.delete "alt"
+ else
+ image_options["alt"] = src.split("/").last.split(".").first.capitalize
+ end
+
+ if html_options["size"]
+ image_options["width"], image_options["height"] = html_options["size"].split("x")
+ html_options.delete "size"
+ end
+
+ if html_options["border"]
+ image_options["border"] = html_options["border"]
+ html_options.delete "border"
+ end
+
+ if html_options["align"]
+ image_options["align"] = html_options["align"]
+ html_options.delete "align"
+ end
+
+ link_to(tag("img", image_options), options, html_options, *parameters_for_method_reference)
+ end
+
+ alias_method :link_to_image, :link_image_to
+ deprecate :link_to_image => "use link_to(image_tag(...), url)",
+ :link_image_to => "use link_to(image_tag(...), url)"
+
+ # Creates a link tag of the given +name+ using a URL created by the set of
+ # +options+ unless the current request uri is the same as the links, in
+ # which case only the name is returned (or the given block is yielded, if
+ # one exists). Refer to the documentation for link_to_unless for block usage.
+ #
+ #
+ # <%= link_to_unless_current("Home", { :action => "index" }) %>
+ # <%= link_to_unless_current("About Us", { :action => "about" }) %>
+ #
+ #
+ # This will render the following HTML when on the about us page:
+ #
+ #
+ # Home
+ # About Us
+ #
+ def link_to_unless_current(name, options = {}, html_options = {}, *parameters_for_method_reference, &block)
+ link_to_unless current_page?(options), name, options, html_options, *parameters_for_method_reference, &block
+ end
+
+ # Creates a link tag of the given +name+ using a URL created by the set of
+ # +options+ unless +condition+ is true, in which case only the name is
+ # returned. To specialize the default behavior, you can pass a block that
+ # accepts the name or the full argument list for link_to_unless (see the example).
+ #
+ # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
+ #
+ # This example uses a block to modify the link if the condition isn't met.
+ #
+ # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
+ # link_to(name, { :controller => "accounts", :action => "signup" })
+ # end %>
+ def link_to_unless(condition, name, options = {}, html_options = {}, *parameters_for_method_reference, &block)
+ if condition
+ if block_given?
+ block.arity <= 1 ? yield(name) : yield(name, options, html_options, *parameters_for_method_reference)
+ else
+ name
+ end
+ else
+ link_to(name, options, html_options, *parameters_for_method_reference)
+ end
+ end
+
+ # Creates a link tag of the given +name+ using a URL created by the set of
+ # +options+ if +condition+ is true, in which case only the name is
+ # returned. To specialize the default behavior, you can pass a block that
+ # accepts the name or the full argument list for link_to_unless (see the examples
+ # in link_to_unless).
+ def link_to_if(condition, name, options = {}, html_options = {}, *parameters_for_method_reference, &block)
+ link_to_unless !condition, name, options, html_options, *parameters_for_method_reference, &block
+ end
+
+ # Creates a mailto link tag to the specified +email_address+, which is
+ # also used as the name of the link unless +name+ is specified. Additional
+ # html attributes for the link can be passed in +html_options+.
+ #
+ # mail_to has several methods for hindering email harvestors and customizing
+ # the email itself by passing special keys to +html_options+.
+ #
+ # Special HTML Options:
+ #
+ # * :encode - This key will accept the strings "javascript" or "hex".
+ # Passing "javascript" will dynamically create and encode the mailto: link then
+ # eval it into the DOM of the page. This method will not show the link on
+ # the page if the user has JavaScript disabled. Passing "hex" will hex
+ # encode the +email_address+ before outputting the mailto: link.
+ # * :replace_at - When the link +name+ isn't provided, the
+ # +email_address+ is used for the link label. You can use this option to
+ # obfuscate the +email_address+ by substituting the @ sign with the string
+ # given as the value.
+ # * :replace_dot - When the link +name+ isn't provided, the
+ # +email_address+ is used for the link label. You can use this option to
+ # obfuscate the +email_address+ by substituting the . in the email with the
+ # string given as the value.
+ # * :subject - Preset the subject line of the email.
+ # * :body - Preset the body of the email.
+ # * :cc - Carbon Copy addition recipients on the email.
+ # * :bcc - Blind Carbon Copy additional recipients on the email.
+ #
+ # Examples:
+ # mail_to "me@domain.com" # => me@domain.com
+ # mail_to "me@domain.com", "My email", :encode => "javascript" # =>
+ #
+ #
+ # mail_to "me@domain.com", "My email", :encode => "hex" # =>
+ # My email
+ #
+ # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email" # =>
+ # me_at_domain_dot_com
+ #
+ # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com",
+ # :subject => "This is an example email" # =>
+ # My email
+ def mail_to(email_address, name = nil, html_options = {})
+ html_options = html_options.stringify_keys
+ encode = html_options.delete("encode")
+ cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body")
+
+ string = ''
+ extras = ''
+ extras << "cc=#{CGI.escape(cc).gsub("+", "%20")}&" unless cc.nil?
+ extras << "bcc=#{CGI.escape(bcc).gsub("+", "%20")}&" unless bcc.nil?
+ extras << "body=#{CGI.escape(body).gsub("+", "%20")}&" unless body.nil?
+ extras << "subject=#{CGI.escape(subject).gsub("+", "%20")}&" unless subject.nil?
+ extras = "?" << extras.gsub!(/&?$/,"") unless extras.empty?
+
+ email_address = email_address.to_s
+
+ email_address_obfuscated = email_address.dup
+ email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
+ email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
+
+ if encode == "javascript"
+ tmp = "document.write('#{content_tag("a", name || email_address, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');"
+ for i in 0...tmp.length
+ string << sprintf("%%%x",tmp[i])
+ end
+ ""
+ elsif encode == "hex"
+ for i in 0...email_address.length
+ if email_address[i,1] =~ /\w/
+ string << sprintf("%%%x",email_address[i])
+ else
+ string << email_address[i,1]
+ end
+ end
+ content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{string}#{extras}" })
+ else
+ content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
+ end
+ end
+
+ # Returns true if the current page uri is generated by the +options+ passed.
+ def current_page?(options)
+ CGI.escapeHTML(self.url_for(options)) == @controller.request.request_uri
+ end
+
+ private
+ def convert_options_to_javascript!(html_options)
+ confirm, popup = html_options.delete("confirm"), html_options.delete("popup")
+
+ # post is deprecated, but if its specified and method is not, assume that method = :post
+ method, post = html_options.delete("method"), html_options.delete("post")
+ if !method && post
+ ActiveSupport::Deprecation.warn(
+ "Passing :post as a link modifier is deprecated. " +
+ "Use :method => \"post\" instead. :post will be removed in Rails 2.0."
+ )
+ method = :post
+ end
+
+ html_options["onclick"] = case
+ when popup && method
+ raise ActionView::ActionViewError, "You can't use :popup and :post in the same link"
+ when confirm && popup
+ "if (#{confirm_javascript_function(confirm)}) { #{popup_javascript_function(popup)} };return false;"
+ when confirm && method
+ "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method)} };return false;"
+ when confirm
+ "return #{confirm_javascript_function(confirm)};"
+ when method
+ "#{method_javascript_function(method)}return false;"
+ when popup
+ popup_javascript_function(popup) + 'return false;'
+ else
+ html_options["onclick"]
+ end
+ end
+
+ def confirm_javascript_function(confirm)
+ "confirm('#{escape_javascript(confirm)}')"
+ end
+
+ def popup_javascript_function(popup)
+ popup.is_a?(Array) ? "window.open(this.href,'#{popup.first}','#{popup.last}');" : "window.open(this.href);"
+ end
+
+ def method_javascript_function(method)
+ submit_function =
+ "var f = document.createElement('form'); f.style.display = 'none'; " +
+ "this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;"
+
+ unless method == :post
+ submit_function << "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); "
+ submit_function << "m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);"
+ end
+
+ submit_function << "f.submit();"
+ end
+
+ # Processes the _html_options_ hash, converting the boolean
+ # attributes from true/false form into the form required by
+ # HTML/XHTML. (An attribute is considered to be boolean if
+ # its name is listed in the given _bool_attrs_ array.)
+ #
+ # More specifically, for each boolean attribute in _html_options_
+ # given as:
+ #
+ # "attr" => bool_value
+ #
+ # if the associated _bool_value_ evaluates to true, it is
+ # replaced with the attribute's name; otherwise the attribute is
+ # removed from the _html_options_ hash. (See the XHTML 1.0 spec,
+ # section 4.5 "Attribute Minimization" for more:
+ # http://www.w3.org/TR/xhtml1/#h-4.5)
+ #
+ # Returns the updated _html_options_ hash, which is also modified
+ # in place.
+ #
+ # Example:
+ #
+ # convert_boolean_attributes!( html_options,
+ # %w( checked disabled readonly ) )
+ def convert_boolean_attributes!(html_options, bool_attrs)
+ bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
+ html_options
+ end
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/partials.rb b/tracks/vendor/rails/actionpack/lib/action_view/partials.rb
new file mode 100644
index 00000000..063ff568
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/partials.rb
@@ -0,0 +1,128 @@
+module ActionView
+ # There's also a convenience method for rendering sub templates within the current controller that depends on a single object
+ # (we call this kind of sub templates for partials). It relies on the fact that partials should follow the naming convention of being
+ # prefixed with an underscore -- as to separate them from regular templates that could be rendered on their own.
+ #
+ # In a template for Advertiser#account:
+ #
+ # <%= render :partial => "account" %>
+ #
+ # This would render "advertiser/_account.rhtml" and pass the instance variable @account in as a local variable +account+ to
+ # the template for display.
+ #
+ # In another template for Advertiser#buy, we could have:
+ #
+ # <%= render :partial => "account", :locals => { :account => @buyer } %>
+ #
+ # <% for ad in @advertisements %>
+ # <%= render :partial => "ad", :locals => { :ad => ad } %>
+ # <% end %>
+ #
+ # This would first render "advertiser/_account.rhtml" with @buyer passed in as the local variable +account+, then render
+ # "advertiser/_ad.rhtml" and pass the local variable +ad+ to the template for display.
+ #
+ # == Rendering a collection of partials
+ #
+ # The example of partial use describes a familiar pattern where a template needs to iterate over an array and render a sub
+ # template for each of the elements. This pattern has been implemented as a single method that accepts an array and renders
+ # a partial by the same name as the elements contained within. So the three-lined example in "Using partials" can be rewritten
+ # with a single line:
+ #
+ # <%= render :partial => "ad", :collection => @advertisements %>
+ #
+ # This will render "advertiser/_ad.rhtml" and pass the local variable +ad+ to the template for display. An iteration counter
+ # will automatically be made available to the template with a name of the form +partial_name_counter+. In the case of the
+ # example above, the template would be fed +ad_counter+.
+ #
+ # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also just keep domain objects,
+ # like Active Records, in there.
+ #
+ # == Rendering shared partials
+ #
+ # Two controllers can share a set of partials and render them like this:
+ #
+ # <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %>
+ #
+ # This will render the partial "advertisement/_ad.rhtml" regardless of which controller this is being called from.
+ module Partials
+ # Deprecated, use render :partial
+ def render_partial(partial_path, local_assigns = nil, deprecated_local_assigns = nil) #:nodoc:
+ path, partial_name = partial_pieces(partial_path)
+ object = extracting_object(partial_name, local_assigns, deprecated_local_assigns)
+ local_assigns = extract_local_assigns(local_assigns, deprecated_local_assigns)
+ local_assigns = local_assigns ? local_assigns.clone : {}
+ add_counter_to_local_assigns!(partial_name, local_assigns)
+ add_object_to_local_assigns!(partial_name, local_assigns, object)
+
+ if logger
+ ActionController::Base.benchmark("Rendered #{path}/_#{partial_name}", Logger::DEBUG, false) do
+ render("#{path}/_#{partial_name}", local_assigns)
+ end
+ else
+ render("#{path}/_#{partial_name}", local_assigns)
+ end
+ end
+
+ # Deprecated, use render :partial, :collection
+ def render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = nil) #:nodoc:
+ collection_of_partials = Array.new
+ counter_name = partial_counter_name(partial_name)
+ local_assigns = local_assigns ? local_assigns.clone : {}
+ collection.each_with_index do |element, counter|
+ local_assigns[counter_name] = counter
+ collection_of_partials.push(render_partial(partial_name, element, local_assigns))
+ end
+
+ return " " if collection_of_partials.empty?
+
+ if partial_spacer_template
+ spacer_path, spacer_name = partial_pieces(partial_spacer_template)
+ collection_of_partials.join(render("#{spacer_path}/_#{spacer_name}"))
+ else
+ collection_of_partials.join
+ end
+ end
+
+ alias_method :render_collection_of_partials, :render_partial_collection
+
+ private
+ def partial_pieces(partial_path)
+ if partial_path.include?('/')
+ return File.dirname(partial_path), File.basename(partial_path)
+ else
+ return controller.class.controller_path, partial_path
+ end
+ end
+
+ def partial_counter_name(partial_name)
+ "#{partial_name.split('/').last}_counter".intern
+ end
+
+ def extracting_object(partial_name, local_assigns, deprecated_local_assigns)
+ if local_assigns.is_a?(Hash) || local_assigns.nil?
+ controller.instance_variable_get("@#{partial_name}")
+ else
+ # deprecated form where object could be passed in as second parameter
+ local_assigns
+ end
+ end
+
+ def extract_local_assigns(local_assigns, deprecated_local_assigns)
+ local_assigns.is_a?(Hash) ? local_assigns : deprecated_local_assigns
+ end
+
+ def add_counter_to_local_assigns!(partial_name, local_assigns)
+ counter_name = partial_counter_name(partial_name)
+ local_assigns[counter_name] = 1 unless local_assigns.has_key?(counter_name)
+ end
+
+ def add_object_to_local_assigns!(partial_name, local_assigns, object)
+ local_assigns[partial_name.intern] ||=
+ if object.is_a?(ActionView::Base::ObjectWrapper)
+ object.value
+ else
+ object
+ end || controller.instance_variable_get("@#{partial_name}")
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/lib/action_view/template_error.rb b/tracks/vendor/rails/actionpack/lib/action_view/template_error.rb
new file mode 100644
index 00000000..546db41f
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/lib/action_view/template_error.rb
@@ -0,0 +1,110 @@
+module ActionView
+ # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
+ # bunch of intimate details and uses it to report a very precise exception message.
+ class TemplateError < ActionViewError #:nodoc:
+ SOURCE_CODE_RADIUS = 3
+
+ attr_reader :original_exception
+
+ def initialize(base_path, file_path, assigns, source, original_exception)
+ @base_path, @assigns, @source, @original_exception =
+ base_path, assigns.dup, source, original_exception
+ @file_path = file_path
+
+ remove_deprecated_assigns!
+ end
+
+ def message
+ ActiveSupport::Deprecation.silence { original_exception.message }
+ end
+
+ def clean_backtrace
+ original_exception.clean_backtrace
+ end
+
+ def sub_template_message
+ if @sub_templates
+ "Trace of template inclusion: " +
+ @sub_templates.collect { |template| strip_base_path(template) }.join(", ")
+ else
+ ""
+ end
+ end
+
+ def source_extract(indentation = 0)
+ return unless num = line_number
+ num = num.to_i
+
+ source_code = IO.readlines(@file_path)
+
+ start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
+ end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
+
+ indent = ' ' * indentation
+ line_counter = start_on_line
+
+ source_code[start_on_line..end_on_line].sum do |line|
+ line_counter += 1
+ "#{indent}#{line_counter}: #{line}"
+ end
+ end
+
+ def sub_template_of(template_path)
+ @sub_templates ||= []
+ @sub_templates << template_path
+ end
+
+ def line_number
+ @line_number ||=
+ if file_name
+ regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
+
+ $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
+ end
+ end
+
+ def file_name
+ stripped = strip_base_path(@file_path)
+ stripped.slice!(0,1) if stripped[0] == ?/
+ stripped
+ end
+
+ def to_s
+ "\n\n#{self.class} (#{message}) #{source_location}:\n" +
+ "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
+ end
+
+ def backtrace
+ [
+ "#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
+ clean_backtrace.join("\n ")
+ ]
+ end
+
+ private
+ def remove_deprecated_assigns!
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |ivar|
+ @assigns.delete(ivar)
+ end
+ end
+
+ def strip_base_path(path)
+ File.expand_path(path).
+ gsub(/^#{Regexp.escape File.expand_path(RAILS_ROOT)}/, '').
+ gsub(@base_path, "")
+ end
+
+ def source_location
+ if line_number
+ "on line ##{line_number} of "
+ else
+ 'in '
+ end + file_name
+ end
+ end
+end
+
+if defined?(Exception::TraceSubstitutions)
+ Exception::TraceSubstitutions << [/:in\s+`_run_(html|xml).*'\s*$/, '']
+ Exception::TraceSubstitutions << [%r{^\s*#{Regexp.escape RAILS_ROOT}}, '#{RAILS_ROOT}'] if defined?(RAILS_ROOT)
+end
diff --git a/tracks/vendor/rails/actionpack/test/abstract_unit.rb b/tracks/vendor/rails/actionpack/test/abstract_unit.rb
new file mode 100644
index 00000000..de4ca439
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/abstract_unit.rb
@@ -0,0 +1,16 @@
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib/active_support')
+$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
+
+require 'yaml'
+require 'test/unit'
+require 'action_controller'
+require 'breakpoint'
+require 'action_controller/test_process'
+
+# Show backtraces for deprecated behavior for quicker cleanup.
+ActiveSupport::Deprecation.debug = true
+
+ActionController::Base.logger = nil
+ActionController::Base.ignore_missing_templates = false
+ActionController::Routing::Routes.reload rescue nil
diff --git a/tracks/vendor/rails/actionpack/test/active_record_unit.rb b/tracks/vendor/rails/actionpack/test/active_record_unit.rb
new file mode 100644
index 00000000..02cf3f70
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/active_record_unit.rb
@@ -0,0 +1,116 @@
+require File.dirname(__FILE__) + '/abstract_unit'
+
+# Define the essentials
+class ActiveRecordTestConnector
+ cattr_accessor :able_to_connect
+ cattr_accessor :connected
+
+ # Set our defaults
+ self.connected = false
+ self.able_to_connect = true
+end
+
+# Try to grab AR
+if defined?(ActiveRecord) && defined?(Fixtures)
+ $stderr.puts 'Active Record is already loaded, running tests'
+else
+ $stderr.print 'Attempting to load Active Record... '
+ begin
+ PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib"
+ raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR)
+ $LOAD_PATH.unshift PATH_TO_AR
+ require 'active_record'
+ require 'active_record/fixtures'
+ $stderr.puts 'success'
+ rescue LoadError => e
+ $stderr.print "failed. Skipping Active Record assertion tests: #{e}"
+ ActiveRecordTestConnector.able_to_connect = false
+ end
+end
+$stderr.flush
+
+
+
+# Define the rest of the connector
+class ActiveRecordTestConnector
+ class << self
+ def setup
+ unless self.connected || !self.able_to_connect
+ setup_connection
+ load_schema
+ require_fixture_models
+ self.connected = true
+ end
+ rescue Exception => e # errors from ActiveRecord setup
+ $stderr.puts "\nSkipping ActiveRecord assertion tests: #{e}"
+ #$stderr.puts " #{e.backtrace.join("\n ")}\n"
+ self.able_to_connect = false
+ end
+
+ private
+
+ def setup_connection
+ if Object.const_defined?(:ActiveRecord)
+ begin
+ connection_options = {:adapter => 'sqlite3', :dbfile => ':memory:'}
+ ActiveRecord::Base.establish_connection(connection_options)
+ ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => connection_options }
+ ActiveRecord::Base.connection
+ rescue Exception # errors from establishing a connection
+ $stderr.puts 'SQLite 3 unavailable; falling to SQLite 2.'
+ connection_options = {:adapter => 'sqlite', :dbfile => ':memory:'}
+ ActiveRecord::Base.establish_connection(connection_options)
+ ActiveRecord::Base.configurations = { 'sqlite2_ar_integration' => connection_options }
+ ActiveRecord::Base.connection
+ end
+
+ Object.send(:const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')) unless Object.const_defined?(:QUOTED_TYPE)
+ else
+ raise "Couldn't locate ActiveRecord."
+ end
+ end
+
+ # Load actionpack sqlite tables
+ def load_schema
+ File.read(File.dirname(__FILE__) + "/fixtures/db_definitions/sqlite.sql").split(';').each do |sql|
+ ActiveRecord::Base.connection.execute(sql) unless sql.blank?
+ end
+ end
+
+ def require_fixture_models
+ Dir.glob(File.dirname(__FILE__) + "/fixtures/*.rb").each {|f| require f}
+ end
+ end
+end
+
+# Test case for inheiritance
+class ActiveRecordTestCase < Test::Unit::TestCase
+ # Set our fixture path
+ if ActiveRecordTestConnector.able_to_connect
+ self.fixture_path = "#{File.dirname(__FILE__)}/fixtures/"
+ self.use_transactional_fixtures = false
+ end
+
+ def self.fixtures(*args)
+ super if ActiveRecordTestConnector.connected
+ end
+
+ def setup
+ abort_tests unless ActiveRecordTestConnector.connected
+ end
+
+ # Default so Test::Unit::TestCase doesn't complain
+ def test_truth
+ end
+
+ private
+ # If things go wrong, we don't want to run our test cases. We'll just define them to test nothing.
+ def abort_tests
+ $stderr.puts 'No Active Record connection, aborting tests.'
+ self.class.public_instance_methods.grep(/^test./).each do |method|
+ self.class.class_eval { define_method(method.to_sym){} }
+ end
+ end
+end
+
+ActiveRecordTestConnector.setup
diff --git a/tracks/vendor/rails/actionpack/test/activerecord/active_record_assertions_test.rb b/tracks/vendor/rails/actionpack/test/activerecord/active_record_assertions_test.rb
new file mode 100644
index 00000000..9a3c1127
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/activerecord/active_record_assertions_test.rb
@@ -0,0 +1,92 @@
+require "#{File.dirname(__FILE__)}/../active_record_unit"
+
+class ActiveRecordAssertionsController < ActionController::Base
+ self.template_root = "#{File.dirname(__FILE__)}/../fixtures/"
+
+ # fail with 1 bad column
+ def nasty_columns_1
+ @company = Company.new
+ @company.name = "B"
+ @company.rating = 2
+ render :inline => "snicker...."
+ end
+
+ # fail with 2 bad columns
+ def nasty_columns_2
+ @company = Company.new
+ @company.name = ""
+ @company.rating = 2
+ render :inline => "double snicker...."
+ end
+
+ # this will pass validation
+ def good_company
+ @company = Company.new
+ @company.name = "A"
+ @company.rating = 69
+ render :inline => "Goodness Gracious!"
+ end
+
+ # this will fail validation
+ def bad_company
+ @company = Company.new
+ render :inline => "Who's Bad?"
+ end
+
+ # the safety dance......
+ def rescue_action(e) raise; end
+end
+
+class ActiveRecordAssertionsControllerTest < ActiveRecordTestCase
+ fixtures :companies
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = ActiveRecordAssertionsController.new
+ super
+ end
+
+ # test for 1 bad apple column
+ def test_some_invalid_columns
+ process :nasty_columns_1
+ assert_response :success
+
+ assert_deprecated_assertion { assert_invalid_record 'company' }
+ assert_deprecated_assertion { assert_invalid_column_on_record 'company', 'rating' }
+ assert_deprecated_assertion { assert_valid_column_on_record 'company', 'name' }
+ assert_deprecated_assertion { assert_valid_column_on_record 'company', %w(name id) }
+ end
+
+ # test for 2 bad apples columns
+ def test_all_invalid_columns
+ process :nasty_columns_2
+ assert_response :success
+
+ assert_deprecated_assertion { assert_invalid_record 'company' }
+ assert_deprecated_assertion { assert_invalid_column_on_record 'company', 'rating' }
+ assert_deprecated_assertion { assert_invalid_column_on_record 'company', 'name' }
+ assert_deprecated_assertion { assert_invalid_column_on_record 'company', %w(name rating) }
+ end
+
+ # ensure we have no problems with an ActiveRecord
+ def test_valid_record
+ process :good_company
+ assert_response :success
+
+ assert_deprecated_assertion { assert_valid_record 'company' }
+ end
+
+ # ensure we have problems with an ActiveRecord
+ def test_invalid_record
+ process :bad_company
+ assert_response :success
+
+ assert_deprecated_assertion { assert_invalid_record 'company' }
+ end
+
+ protected
+ def assert_deprecated_assertion(message = nil, &block)
+ assert_deprecated(/assert_.*from test_/, &block)
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb b/tracks/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb
new file mode 100644
index 00000000..9d92593a
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb
@@ -0,0 +1,142 @@
+# These tests exercise CGI::Session::ActiveRecordStore, so you're going to
+# need AR in a sibling directory to AP and have SQLite installed.
+require File.dirname(__FILE__) + '/../active_record_unit'
+require 'action_controller/session/active_record_store'
+
+
+module CommonActiveRecordStoreTests
+ def test_basics
+ s = session_class.new(:session_id => '1234', :data => { 'foo' => 'bar' })
+ assert_equal 'bar', s.data['foo']
+ assert s.save
+ assert_equal 'bar', s.data['foo']
+
+ assert_not_nil t = session_class.find_by_session_id('1234')
+ assert_not_nil t.data
+ assert_equal 'bar', t.data['foo']
+ end
+
+ def test_reload_same_session
+ @new_session.update
+ reloaded = CGI::Session.new(CGI.new, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
+ assert_equal 'bar', reloaded['foo']
+ end
+
+ def test_tolerates_close_close
+ assert_nothing_raised do
+ @new_session.close
+ @new_session.close
+ end
+ end
+end
+
+class ActiveRecordStoreTest < ActiveRecordTestCase
+ include CommonActiveRecordStoreTests
+
+ def session_class
+ CGI::Session::ActiveRecordStore::Session
+ end
+
+ def session_id_column
+ "session_id"
+ end
+
+ def setup
+ session_class.create_table!
+
+ ENV['REQUEST_METHOD'] = 'GET'
+ ENV['REQUEST_URI'] = '/'
+ CGI::Session::ActiveRecordStore.session_class = session_class
+
+ @cgi = CGI.new
+ @new_session = CGI::Session.new(@cgi, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
+ @new_session['foo'] = 'bar'
+ end
+
+# this test only applies for eager sesssion saving
+# def test_another_instance
+# @another = CGI::Session.new(@cgi, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
+# assert_equal @new_session.session_id, @another.session_id
+# end
+
+ def test_model_attribute
+ assert_kind_of CGI::Session::ActiveRecordStore::Session, @new_session.model
+ assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
+ end
+
+ def test_save_unloaded_session
+ c = session_class.connection
+ bogus_class = c.quote(Base64.encode64("\004\010o:\vBlammo\000"))
+ c.insert("INSERT INTO #{session_class.table_name} ('#{session_id_column}', 'data') VALUES ('abcdefghijklmnop', #{bogus_class})")
+
+ sess = session_class.find_by_session_id('abcdefghijklmnop')
+ assert_not_nil sess
+ assert !sess.loaded?
+
+ # because the session is not loaded, the save should be a no-op. If it
+ # isn't, this'll try and unmarshall the bogus class, and should get an error.
+ assert_nothing_raised { sess.save }
+ end
+
+ def teardown
+ session_class.drop_table!
+ end
+end
+
+class ColumnLimitTest < ActiveRecordTestCase
+ def setup
+ @session_class = CGI::Session::ActiveRecordStore::Session
+ @session_class.create_table!
+ end
+
+ def teardown
+ @session_class.drop_table!
+ end
+
+ def test_protection_from_data_larger_than_column
+ # Can't test this unless there is a limit
+ return unless limit = @session_class.data_column_size_limit
+ too_big = ':(' * limit
+ s = @session_class.new(:session_id => '666', :data => {'foo' => too_big})
+ s.data
+ assert_raise(ActionController::SessionOverflowError) { s.save }
+ end
+end
+
+class DeprecatedActiveRecordStoreTest < ActiveRecordStoreTest
+ def session_id_column
+ "sessid"
+ end
+
+ def setup
+ session_class.connection.execute 'create table old_sessions (id integer primary key, sessid text unique, data text)'
+ session_class.table_name = 'old_sessions'
+ session_class.send :setup_sessid_compatibility!
+
+ ENV['REQUEST_METHOD'] = 'GET'
+ CGI::Session::ActiveRecordStore.session_class = session_class
+
+ @new_session = CGI::Session.new(CGI.new, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
+ @new_session['foo'] = 'bar'
+ end
+
+ def teardown
+ session_class.connection.execute 'drop table old_sessions'
+ session_class.table_name = 'sessions'
+ end
+end
+
+class SqlBypassActiveRecordStoreTest < ActiveRecordStoreTest
+ def session_class
+ unless @session_class
+ @session_class = CGI::Session::ActiveRecordStore::SqlBypass
+ @session_class.connection = CGI::Session::ActiveRecordStore::Session.connection
+ end
+ @session_class
+ end
+
+ def test_model_attribute
+ assert_kind_of CGI::Session::ActiveRecordStore::SqlBypass, @new_session.model
+ assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/activerecord/pagination_test.rb b/tracks/vendor/rails/actionpack/test/activerecord/pagination_test.rb
new file mode 100644
index 00000000..ddd2cec8
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/activerecord/pagination_test.rb
@@ -0,0 +1,156 @@
+require File.dirname(__FILE__) + '/../active_record_unit'
+
+class PaginationTest < ActiveRecordTestCase
+ fixtures :topics, :replies, :developers, :projects, :developers_projects
+
+ class PaginationController < ActionController::Base
+ self.template_root = "#{File.dirname(__FILE__)}/../fixtures/"
+
+ def simple_paginate
+ @topic_pages, @topics = paginate(:topics)
+ render :nothing => true
+ end
+
+ def paginate_with_per_page
+ @topic_pages, @topics = paginate(:topics, :per_page => 1)
+ render :nothing => true
+ end
+
+ def paginate_with_order
+ @topic_pages, @topics = paginate(:topics, :order => 'created_at asc')
+ render :nothing => true
+ end
+
+ def paginate_with_order_by
+ @topic_pages, @topics = paginate(:topics, :order_by => 'created_at asc')
+ render :nothing => true
+ end
+
+ def paginate_with_include_and_order
+ @topic_pages, @topics = paginate(:topics, :include => :replies, :order => 'replies.created_at asc, topics.created_at asc')
+ render :nothing => true
+ end
+
+ def paginate_with_conditions
+ @topic_pages, @topics = paginate(:topics, :conditions => ["created_at > ?", 30.minutes.ago])
+ render :nothing => true
+ end
+
+ def paginate_with_class_name
+ @developer_pages, @developers = paginate(:developers, :class_name => "DeVeLoPeR")
+ render :nothing => true
+ end
+
+ def paginate_with_singular_name
+ @developer_pages, @developers = paginate()
+ render :nothing => true
+ end
+
+ def paginate_with_joins
+ @developer_pages, @developers = paginate(:developers,
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+ :conditions => 'project_id=1')
+ render :nothing => true
+ end
+
+ def paginate_with_join
+ @developer_pages, @developers = paginate(:developers,
+ :join => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+ :conditions => 'project_id=1')
+ render :nothing => true
+ end
+
+ def paginate_with_join_and_count
+ @developer_pages, @developers = paginate(:developers,
+ :join => 'd LEFT JOIN developers_projects ON d.id = developers_projects.developer_id',
+ :conditions => 'project_id=1',
+ :count => "d.id")
+ render :nothing => true
+ end
+
+ def rescue_errors(e) raise e end
+
+ def rescue_action(e) raise end
+
+ end
+
+ def setup
+ @controller = PaginationController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ super
+ end
+
+ # Single Action Pagination Tests
+
+ def test_simple_paginate
+ get :simple_paginate
+ assert_equal 1, assigns(:topic_pages).page_count
+ assert_equal 3, assigns(:topics).size
+ end
+
+ def test_paginate_with_per_page
+ get :paginate_with_per_page
+ assert_equal 1, assigns(:topics).size
+ assert_equal 3, assigns(:topic_pages).page_count
+ end
+
+ def test_paginate_with_order
+ get :paginate_with_order
+ expected = [topics(:futurama),
+ topics(:harvey_birdman),
+ topics(:rails)]
+ assert_equal expected, assigns(:topics)
+ assert_equal 1, assigns(:topic_pages).page_count
+ end
+
+ def test_paginate_with_order_by
+ get :paginate_with_order
+ expected = assigns(:topics)
+ get :paginate_with_order_by
+ assert_equal expected, assigns(:topics)
+ assert_equal 1, assigns(:topic_pages).page_count
+ end
+
+ def test_paginate_with_conditions
+ get :paginate_with_conditions
+ expected = [topics(:rails)]
+ assert_equal expected, assigns(:topics)
+ assert_equal 1, assigns(:topic_pages).page_count
+ end
+
+ def test_paginate_with_class_name
+ get :paginate_with_class_name
+
+ assert assigns(:developers).size > 0
+ assert_equal DeVeLoPeR, assigns(:developers).first.class
+ end
+
+ def test_paginate_with_joins
+ get :paginate_with_joins
+ assert_equal 2, assigns(:developers).size
+ developer_names = assigns(:developers).map { |d| d.name }
+ assert developer_names.include?('David')
+ assert developer_names.include?('Jamis')
+ end
+
+ def test_paginate_with_join_and_conditions
+ get :paginate_with_joins
+ expected = assigns(:developers)
+ get :paginate_with_join
+ assert_equal expected, assigns(:developers)
+ end
+
+ def test_paginate_with_join_and_count
+ get :paginate_with_joins
+ expected = assigns(:developers)
+ get :paginate_with_join_and_count
+ assert_equal expected, assigns(:developers)
+ end
+
+ def test_paginate_with_include_and_order
+ get :paginate_with_include_and_order
+ expected = Topic.find(:all, :include => 'replies', :order => 'replies.created_at asc, topics.created_at asc', :limit => 10)
+ assert_equal expected, assigns(:topics)
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb b/tracks/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb
new file mode 100644
index 00000000..5f5e1dca
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb
@@ -0,0 +1,587 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+# a controller class to facilitate the tests
+class ActionPackAssertionsController < ActionController::Base
+
+ # this does absolutely nothing
+ def nothing() head :ok end
+
+ # a standard template
+ def hello_world() render "test/hello_world"; end
+
+ # a standard template
+ def hello_xml_world() render "test/hello_xml_world"; end
+
+ # a redirect to an internal location
+ def redirect_internal() redirect_to "/nothing"; end
+
+ def redirect_to_action() redirect_to :action => "flash_me", :id => 1, :params => { "panda" => "fun" }; end
+
+ def redirect_to_controller() redirect_to :controller => "elsewhere", :action => "flash_me"; end
+
+ def redirect_to_path() redirect_to '/some/path' end
+
+ def redirect_to_named_route() redirect_to route_one_url end
+
+ # a redirect to an external location
+ def redirect_external() redirect_to_url "http://www.rubyonrails.org"; end
+
+ # a 404
+ def response404() head '404 AWOL' end
+
+ # a 500
+ def response500() head '500 Sorry' end
+
+ # a fictional 599
+ def response599() head '599 Whoah!' end
+
+ # putting stuff in the flash
+ def flash_me
+ flash['hello'] = 'my name is inigo montoya...'
+ render_text "Inconceivable!"
+ end
+
+ # we have a flash, but nothing is in it
+ def flash_me_naked
+ flash.clear
+ render_text "wow!"
+ end
+
+ # assign some template instance variables
+ def assign_this
+ @howdy = "ho"
+ render :inline => "Mr. Henke"
+ end
+
+ def render_based_on_parameters
+ render_text "Mr. #{params[:name]}"
+ end
+
+ def render_url
+ render_text "#{url_for(:action => 'flash_me', :only_path => true)}
"
+ end
+
+ def render_text_with_custom_content_type
+ render :text => "Hello!", :content_type => Mime::RSS
+ end
+
+ # puts something in the session
+ def session_stuffing
+ session['xmas'] = 'turkey'
+ render_text "ho ho ho"
+ end
+
+ # raises exception on get requests
+ def raise_on_get
+ raise "get" if request.get?
+ render_text "request method: #{request.env['REQUEST_METHOD']}"
+ end
+
+ # raises exception on post requests
+ def raise_on_post
+ raise "post" if request.post?
+ render_text "request method: #{request.env['REQUEST_METHOD']}"
+ end
+
+ def get_valid_record
+ @record = Class.new do
+ def valid?
+ true
+ end
+
+ def errors
+ Class.new do
+ def full_messages; []; end
+ end.new
+ end
+
+ end.new
+
+ render :nothing => true
+ end
+
+
+ def get_invalid_record
+ @record = Class.new do
+
+ def valid?
+ false
+ end
+
+ def errors
+ Class.new do
+ def full_messages; ['...stuff...']; end
+ end.new
+ end
+ end.new
+
+ render :nothing => true
+ end
+
+ # 911
+ def rescue_action(e) raise; end
+end
+
+module Admin
+ class InnerModuleController < ActionController::Base
+ def index
+ render :nothing => true
+ end
+
+ def redirect_to_index
+ redirect_to admin_inner_module_path
+ end
+
+ def redirect_to_absolute_controller
+ redirect_to :controller => '/content'
+ end
+
+ def redirect_to_fellow_controller
+ redirect_to :controller => 'user'
+ end
+ end
+end
+
+# ---------------------------------------------------------------------------
+
+
+# tell the controller where to find its templates but start from parent
+# directory of test_request_response to simulate the behaviour of a
+# production environment
+ActionPackAssertionsController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+
+# a test case to exercise the new capabilities TestRequest & TestResponse
+class ActionPackAssertionsControllerTest < Test::Unit::TestCase
+ # let's get this party started
+ def setup
+ @controller = ActionPackAssertionsController.new
+ @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+ end
+
+ # -- assertion-based testing ------------------------------------------------
+
+ def test_assert_tag_and_url_for
+ get :render_url
+ assert_tag :content => "/action_pack_assertions/flash_me"
+ end
+
+ # test the session assertion to make sure something is there.
+ def test_assert_session_has
+ process :session_stuffing
+ assert_deprecated_assertion { assert_session_has 'xmas' }
+ assert_deprecated_assertion { assert_session_has_no 'halloween' }
+ end
+
+ # test the get method, make sure the request really was a get
+ def test_get
+ assert_raise(RuntimeError) { get :raise_on_get }
+ get :raise_on_post
+ assert_equal @response.body, 'request method: GET'
+ end
+
+ # test the get method, make sure the request really was a get
+ def test_post
+ assert_raise(RuntimeError) { post :raise_on_post }
+ post :raise_on_get
+ assert_equal @response.body, 'request method: POST'
+ end
+
+# the following test fails because the request_method is now cached on the request instance
+# test the get/post switch within one test action
+# def test_get_post_switch
+# post :raise_on_get
+# assert_equal @response.body, 'request method: POST'
+# get :raise_on_post
+# assert_equal @response.body, 'request method: GET'
+# post :raise_on_get
+# assert_equal @response.body, 'request method: POST'
+# get :raise_on_post
+# assert_equal @response.body, 'request method: GET'
+# end
+
+ # test the assertion of goodies in the template
+ def test_assert_template_has
+ process :assign_this
+ assert_deprecated_assertion { assert_template_has 'howdy' }
+ end
+
+ # test the assertion for goodies that shouldn't exist in the template
+ def test_assert_template_has_no
+ process :nothing
+ assert_deprecated_assertion { assert_template_has_no 'maple syrup' }
+ assert_deprecated_assertion { assert_template_has_no 'howdy' }
+ end
+
+ # test the redirection assertions
+ def test_assert_redirect
+ process :redirect_internal
+ assert_deprecated_assertion { assert_redirect }
+ end
+
+ # test the redirect url string
+ def test_assert_redirect_url
+ process :redirect_external
+ assert_deprecated_assertion do
+ assert_redirect_url 'http://www.rubyonrails.org'
+ end
+ end
+
+ # test the redirection pattern matching on a string
+ def test_assert_redirect_url_match_string
+ process :redirect_external
+ assert_deprecated_assertion do
+ assert_redirect_url_match 'rails.org'
+ end
+ end
+
+ # test the redirection pattern matching on a pattern
+ def test_assert_redirect_url_match_pattern
+ process :redirect_external
+ assert_deprecated_assertion do
+ assert_redirect_url_match /ruby/
+ end
+ end
+
+ # test the redirection to a named route
+ def test_assert_redirect_to_named_route
+ with_routing do |set|
+ set.draw do |map|
+ map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing'
+ map.connect ':controller/:action/:id'
+ end
+ set.named_routes.install
+
+ process :redirect_to_named_route
+ assert_redirected_to 'http://test.host/route_one'
+ assert_redirected_to route_one_url
+ assert_redirected_to :route_one_url
+ end
+ end
+
+ def test_assert_redirect_to_named_route_failure
+ with_routing do |set|
+ set.draw do |map|
+ map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing', :id => 'one'
+ map.route_two 'route_two', :controller => 'action_pack_assertions', :action => 'nothing', :id => 'two'
+ map.connect ':controller/:action/:id'
+ end
+ process :redirect_to_named_route
+ assert_raise(Test::Unit::AssertionFailedError) do
+ assert_redirected_to 'http://test.host/route_two'
+ end
+ assert_raise(Test::Unit::AssertionFailedError) do
+ assert_redirected_to :controller => 'action_pack_assertions', :action => 'nothing', :id => 'two'
+ end
+ assert_raise(Test::Unit::AssertionFailedError) do
+ assert_redirected_to route_two_url
+ end
+ assert_raise(Test::Unit::AssertionFailedError) do
+ assert_redirected_to :route_two_url
+ end
+ end
+ end
+
+ def test_assert_redirect_to_nested_named_route
+ with_routing do |set|
+ set.draw do |map|
+ map.admin_inner_module 'admin/inner_module', :controller => 'admin/inner_module', :action => 'index'
+ map.connect ':controller/:action/:id'
+ end
+ @controller = Admin::InnerModuleController.new
+ process :redirect_to_index
+ # redirection is <{"action"=>"index", "controller"=>"admin/admin/inner_module"}>
+ assert_redirected_to admin_inner_module_path
+ end
+ end
+
+ # test the flash-based assertions with something is in the flash
+ def test_flash_assertions_full
+ process :flash_me
+ assert @response.has_flash_with_contents?
+ assert_deprecated_assertion { assert_flash_exists }
+ assert_deprecated_assertion { assert_flash_not_empty }
+ assert_deprecated_assertion { assert_flash_has 'hello' }
+ assert_deprecated_assertion { assert_flash_has_no 'stds' }
+ end
+
+ # test the flash-based assertions with no flash at all
+ def test_flash_assertions_negative
+ process :nothing
+ assert_deprecated_assertion { assert_flash_empty }
+ assert_deprecated_assertion { assert_flash_has_no 'hello' }
+ assert_deprecated_assertion { assert_flash_has_no 'qwerty' }
+ end
+
+ # test the assert_rendered_file
+ def test_assert_rendered_file
+ assert_deprecated(/render/) { process :hello_world }
+ assert_deprecated_assertion { assert_rendered_file 'test/hello_world' }
+ assert_deprecated_assertion { assert_rendered_file 'hello_world' }
+ end
+
+ # test the assert_success assertion
+ def test_assert_success
+ process :nothing
+ assert_deprecated_assertion { assert_success }
+ end
+
+ # -- standard request/response object testing --------------------------------
+
+ # ensure our session is working properly
+ def test_session_objects
+ process :session_stuffing
+ assert @response.has_session_object?('xmas')
+ assert_deprecated_assertion { assert_session_equal 'turkey', 'xmas' }
+ assert !@response.has_session_object?('easter')
+ end
+
+ # make sure that the template objects exist
+ def test_template_objects_alive
+ process :assign_this
+ assert !@response.has_template_object?('hi')
+ assert @response.has_template_object?('howdy')
+ end
+
+ # make sure we don't have template objects when we shouldn't
+ def test_template_object_missing
+ process :nothing
+ assert_nil @response.template_objects['howdy']
+ end
+
+ def test_assigned_equal
+ process :assign_this
+ assert_deprecated_assertion { assert_assigned_equal "ho", :howdy }
+ end
+
+ # check the empty flashing
+ def test_flash_me_naked
+ process :flash_me_naked
+ assert !@response.has_flash?
+ assert !@response.has_flash_with_contents?
+ end
+
+ # check if we have flash objects
+ def test_flash_haves
+ process :flash_me
+ assert @response.has_flash?
+ assert @response.has_flash_with_contents?
+ assert @response.has_flash_object?('hello')
+ end
+
+ # ensure we don't have flash objects
+ def test_flash_have_nots
+ process :nothing
+ assert !@response.has_flash?
+ assert !@response.has_flash_with_contents?
+ assert_nil @response.flash['hello']
+ end
+
+ # examine that the flash objects are what we expect
+ def test_flash_equals
+ process :flash_me
+ assert_deprecated_assertion do
+ assert_flash_equal 'my name is inigo montoya...', 'hello'
+ end
+ end
+
+ # check if we were rendered by a file-based template?
+ def test_rendered_action
+ process :nothing
+ assert !@response.rendered_with_file?
+
+ assert_deprecated(/render/) { process :hello_world }
+ assert @response.rendered_with_file?
+ assert 'hello_world', @response.rendered_file
+ end
+
+ # check the redirection location
+ def test_redirection_location
+ process :redirect_internal
+ assert_equal 'http://test.host/nothing', @response.redirect_url
+
+ process :redirect_external
+ assert_equal 'http://www.rubyonrails.org', @response.redirect_url
+ end
+
+ def test_no_redirect_url
+ process :nothing
+ assert_nil @response.redirect_url
+ end
+
+
+ # check server errors
+ def test_server_error_response_code
+ process :response500
+ assert @response.server_error?
+
+ process :response599
+ assert @response.server_error?
+
+ process :response404
+ assert !@response.server_error?
+ end
+
+ # check a 404 response code
+ def test_missing_response_code
+ process :response404
+ assert @response.missing?
+ end
+
+ # check to see if our redirection matches a pattern
+ def test_redirect_url_match
+ process :redirect_external
+ assert @response.redirect?
+ assert @response.redirect_url_match?("rubyonrails")
+ assert @response.redirect_url_match?(/rubyonrails/)
+ assert !@response.redirect_url_match?("phpoffrails")
+ assert !@response.redirect_url_match?(/perloffrails/)
+ end
+
+ # check for a redirection
+ def test_redirection
+ process :redirect_internal
+ assert @response.redirect?
+
+ process :redirect_external
+ assert @response.redirect?
+
+ process :nothing
+ assert !@response.redirect?
+ end
+
+ # check a successful response code
+ def test_successful_response_code
+ process :nothing
+ assert @response.success?
+ end
+
+ # a basic check to make sure we have a TestResponse object
+ def test_has_response
+ process :nothing
+ assert_kind_of ActionController::TestResponse, @response
+ end
+
+ def test_render_based_on_parameters
+ process :render_based_on_parameters, "name" => "David"
+ assert_equal "Mr. David", @response.body
+ end
+
+ def test_assert_template_xpath_match_no_matches
+ assert_deprecated(/render/) { process :hello_xml_world }
+ assert_raises Test::Unit::AssertionFailedError do
+ assert_deprecated_assertion do
+ assert_template_xpath_match('/no/such/node/in/document')
+ end
+ end
+ end
+
+ def test_simple_one_element_xpath_match
+ assert_deprecated(/render/) { process :hello_xml_world }
+ assert_deprecated_assertion do
+ assert_template_xpath_match('//title', "Hello World")
+ end
+ end
+
+ def test_array_of_elements_in_xpath_match
+ assert_deprecated(/render/) { process :hello_xml_world }
+ assert_deprecated_assertion do
+ assert_template_xpath_match('//p', %w( abes monks wiseguys ))
+ end
+ end
+
+ def test_follow_redirect
+ process :redirect_to_action
+ assert_redirected_to :action => "flash_me"
+
+ follow_redirect
+ assert_equal 1, @request.parameters["id"].to_i
+
+ assert "Inconceivable!", @response.body
+ end
+
+ def test_follow_redirect_outside_current_action
+ process :redirect_to_controller
+ assert_redirected_to :controller => "elsewhere", :action => "flash_me"
+
+ assert_raises(RuntimeError, "Can't follow redirects outside of current controller (elsewhere)") { follow_redirect }
+ end
+
+ def test_assert_redirection_fails_with_incorrect_controller
+ process :redirect_to_controller
+ assert_raise(Test::Unit::AssertionFailedError) do
+ assert_redirected_to :controller => "action_pack_assertions", :action => "flash_me"
+ end
+ end
+
+ def test_assert_redirection_with_extra_controller_option
+ get :redirect_to_action
+ assert_redirected_to :controller => 'action_pack_assertions', :action => "flash_me", :id => 1, :params => { :panda => 'fun' }
+ end
+
+ def test_redirected_to_url_leadling_slash
+ process :redirect_to_path
+ assert_redirected_to '/some/path'
+ end
+ def test_redirected_to_url_no_leadling_slash
+ process :redirect_to_path
+ assert_redirected_to 'some/path'
+ end
+ def test_redirected_to_url_full_url
+ process :redirect_to_path
+ assert_redirected_to 'http://test.host/some/path'
+ end
+
+ def test_redirected_to_with_nested_controller
+ @controller = Admin::InnerModuleController.new
+ get :redirect_to_absolute_controller
+ assert_redirected_to :controller => 'content'
+
+ get :redirect_to_fellow_controller
+ assert_redirected_to :controller => 'admin/user'
+ end
+
+ def test_assert_valid
+ get :get_valid_record
+ assert_valid assigns('record')
+ end
+
+ def test_assert_valid_failing
+ get :get_invalid_record
+
+ begin
+ assert_valid assigns('record')
+ assert false
+ rescue Test::Unit::AssertionFailedError => e
+ end
+ end
+
+ protected
+ def assert_deprecated_assertion(&block)
+ assert_deprecated(/assert/, &block)
+ end
+end
+
+class ActionPackHeaderTest < Test::Unit::TestCase
+ def setup
+ @controller = ActionPackAssertionsController.new
+ @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+ end
+
+ def test_rendering_xml_sets_content_type
+ assert_deprecated(/render/) { process :hello_xml_world }
+ assert_equal('application/xml; charset=utf-8', @controller.headers['Content-Type'])
+ end
+
+ def test_rendering_xml_respects_content_type
+ @response.headers['Content-Type'] = 'application/pdf'
+ assert_deprecated(/render/) { process :hello_xml_world }
+ assert_equal('application/pdf; charset=utf-8', @controller.headers['Content-Type'])
+ end
+
+
+ def test_render_text_with_custom_content_type
+ get :render_text_with_custom_content_type
+ assert_equal 'application/rss+xml; charset=utf-8', @response.headers['Content-Type']
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/addresses_render_test.rb b/tracks/vendor/rails/actionpack/test/controller/addresses_render_test.rb
new file mode 100644
index 00000000..4de1c805
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/addresses_render_test.rb
@@ -0,0 +1,45 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class Address
+
+ def Address.count(conditions = nil, join = nil)
+ nil
+ end
+
+ def Address.find_all(arg1, arg2, arg3, arg4)
+ []
+ end
+
+ def self.find(*args)
+ []
+ end
+end
+
+class AddressesTestController < ActionController::Base
+ scaffold :address
+
+ def self.controller_name; "addresses"; end
+ def self.controller_path; "addresses"; end
+end
+
+AddressesTestController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class AddressesTest < Test::Unit::TestCase
+ def setup
+ @controller = AddressesTestController.new
+
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ @controller.logger = Logger.new(nil)
+
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_list
+ get :list
+ assert_equal "We only need to get this far!", @response.body.chomp
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/assert_select_test.rb b/tracks/vendor/rails/actionpack/test/controller/assert_select_test.rb
new file mode 100644
index 00000000..2c180dbc
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/assert_select_test.rb
@@ -0,0 +1,576 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+require File.dirname(__FILE__) + '/../abstract_unit'
+require File.dirname(__FILE__) + '/fake_controllers'
+
+
+unless defined?(ActionMailer)
+ begin
+ $:.unshift(File.dirname(__FILE__) + "/../../../actionmailer/lib")
+ require 'action_mailer'
+ rescue LoadError
+ require 'rubygems'
+ require_gem 'actionmailer'
+ end
+end
+
+class AssertSelectTest < Test::Unit::TestCase
+ class AssertSelectController < ActionController::Base
+ def response_with=(content)
+ @content = content
+ end
+
+ def response_with(&block)
+ @update = block
+ end
+
+ def html()
+ render :text=>@content, :layout=>false, :content_type=>Mime::HTML
+ @content = nil
+ end
+
+ def rjs()
+ render :update do |page|
+ @update.call page
+ end
+ @update = nil
+ end
+
+ def xml()
+ render :text=>@content, :layout=>false, :content_type=>Mime::XML
+ @content = nil
+ end
+
+ def rescue_action(e)
+ raise e
+ end
+ end
+
+ class AssertSelectMailer < ActionMailer::Base
+ def test(html)
+ recipients "test "
+ from "test@test.host"
+ subject "Test e-mail"
+ part :content_type=>"text/html", :body=>html
+ end
+ end
+
+ AssertionFailedError = Test::Unit::AssertionFailedError
+
+ def setup
+ @controller = AssertSelectController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ ActionMailer::Base.delivery_method = :test
+ ActionMailer::Base.perform_deliveries = true
+ ActionMailer::Base.deliveries = []
+ end
+
+
+ def teardown
+ ActionMailer::Base.deliveries.clear
+ end
+
+
+ #
+ # Test assert select.
+ #
+
+ def test_assert_select
+ render_html %Q{
}
+ assert_select "div", 2
+ assert_raises(AssertionFailedError) { assert_select "div", 3 }
+ assert_raises(AssertionFailedError){ assert_select "p" }
+ end
+
+
+ def test_equality_true_false
+ render_html %Q{
}
+ assert_nothing_raised { assert_select "div" }
+ assert_raises(AssertionFailedError) { assert_select "p" }
+ assert_nothing_raised { assert_select "div", true }
+ assert_raises(AssertionFailedError) { assert_select "p", true }
+ assert_raises(AssertionFailedError) { assert_select "div", false }
+ assert_nothing_raised { assert_select "p", false }
+ end
+
+
+ def test_equality_string_and_regexp
+ render_html %Q{foo
foo
}
+ assert_nothing_raised { assert_select "div", "foo" }
+ assert_raises(AssertionFailedError) { assert_select "div", "bar" }
+ assert_nothing_raised { assert_select "div", :text=>"foo" }
+ assert_raises(AssertionFailedError) { assert_select "div", :text=>"bar" }
+ assert_nothing_raised { assert_select "div", /(foo|bar)/ }
+ assert_raises(AssertionFailedError) { assert_select "div", /foobar/ }
+ assert_nothing_raised { assert_select "div", :text=>/(foo|bar)/ }
+ assert_raises(AssertionFailedError) { assert_select "div", :text=>/foobar/ }
+ assert_raises(AssertionFailedError) { assert_select "p", :text=>/foobar/ }
+ end
+
+
+ def test_equality_of_html
+ render_html %Q{\n"This is not a big problem," he said.\n
}
+ text = "\"This is not a big problem,\" he said."
+ html = "\"This is not a big problem,\" he said."
+ assert_nothing_raised { assert_select "p", text }
+ assert_raises(AssertionFailedError) { assert_select "p", html }
+ assert_nothing_raised { assert_select "p", :html=>html }
+ assert_raises(AssertionFailedError) { assert_select "p", :html=>text }
+ # No stripping for pre.
+ render_html %Q{\n"This is not a big problem," he said.\n }
+ text = "\n\"This is not a big problem,\" he said.\n"
+ html = "\n\"This is not a big problem,\" he said.\n"
+ assert_nothing_raised { assert_select "pre", text }
+ assert_raises(AssertionFailedError) { assert_select "pre", html }
+ assert_nothing_raised { assert_select "pre", :html=>html }
+ assert_raises(AssertionFailedError) { assert_select "pre", :html=>text }
+ end
+
+
+ def test_equality_of_instances
+ render_html %Q{foo
foo
}
+ assert_nothing_raised { assert_select "div", 2 }
+ assert_raises(AssertionFailedError) { assert_select "div", 3 }
+ assert_nothing_raised { assert_select "div", 1..2 }
+ assert_raises(AssertionFailedError) { assert_select "div", 3..4 }
+ assert_nothing_raised { assert_select "div", :count=>2 }
+ assert_raises(AssertionFailedError) { assert_select "div", :count=>3 }
+ assert_nothing_raised { assert_select "div", :minimum=>1 }
+ assert_nothing_raised { assert_select "div", :minimum=>2 }
+ assert_raises(AssertionFailedError) { assert_select "div", :minimum=>3 }
+ assert_nothing_raised { assert_select "div", :maximum=>2 }
+ assert_nothing_raised { assert_select "div", :maximum=>3 }
+ assert_raises(AssertionFailedError) { assert_select "div", :maximum=>1 }
+ assert_nothing_raised { assert_select "div", :minimum=>1, :maximum=>2 }
+ assert_raises(AssertionFailedError) { assert_select "div", :minimum=>3, :maximum=>4 }
+ end
+
+
+ def test_substitution_values
+ render_html %Q{foo
foo
}
+ assert_select "div#?", /\d+/ do |elements|
+ assert_equal 2, elements.size
+ end
+ assert_select "div" do
+ assert_select "div#?", /\d+/ do |elements|
+ assert_equal 2, elements.size
+ assert_select "#1"
+ assert_select "#2"
+ end
+ end
+ end
+
+
+ def test_nested_assert_select
+ render_html %Q{foo
foo
}
+ assert_select "div" do |elements|
+ assert_equal 2, elements.size
+ assert_select elements[0], "#1"
+ assert_select elements[1], "#2"
+ end
+ assert_select "div" do
+ assert_select "div" do |elements|
+ assert_equal 2, elements.size
+ # Testing in a group is one thing
+ assert_select "#1,#2"
+ # Testing individually is another.
+ assert_select "#1"
+ assert_select "#2"
+ assert_select "#3", false
+ end
+ end
+ end
+
+
+ def test_assert_select_text_match
+ render_html %Q{foo
bar
}
+ assert_select "div" do
+ assert_nothing_raised { assert_select "div", "foo" }
+ assert_nothing_raised { assert_select "div", "bar" }
+ assert_nothing_raised { assert_select "div", /\w*/ }
+ assert_nothing_raised { assert_select "div", /\w*/, :count=>2 }
+ assert_raises(AssertionFailedError) { assert_select "div", :text=>"foo", :count=>2 }
+ assert_nothing_raised { assert_select "div", :html=>"bar " }
+ assert_nothing_raised { assert_select "div", :html=>"bar " }
+ assert_nothing_raised { assert_select "div", :html=>/\w*/ }
+ assert_nothing_raised { assert_select "div", :html=>/\w*/, :count=>2 }
+ assert_raises(AssertionFailedError) { assert_select "div", :html=>"foo ", :count=>2 }
+ end
+ end
+
+
+ # With single result.
+ def test_assert_select_from_rjs_with_single_result
+ render_rjs do |page|
+ page.replace_html "test", "foo
\nfoo
"
+ end
+ assert_select "div" do |elements|
+ assert elements.size == 2
+ assert_select "#1"
+ assert_select "#2"
+ end
+ assert_select "div#?", /\d+/ do |elements|
+ assert_select "#1"
+ assert_select "#2"
+ end
+ end
+
+ # With multiple results.
+ def test_assert_select_from_rjs_with_multiple_results
+ render_rjs do |page|
+ page.replace_html "test", "foo
"
+ page.replace_html "test2", "foo
"
+ end
+ assert_select "div" do |elements|
+ assert elements.size == 2
+ assert_select "#1"
+ assert_select "#2"
+ end
+ end
+
+
+ #
+ # Test css_select.
+ #
+
+
+ def test_css_select
+ render_html %Q{
}
+ assert 2, css_select("div").size
+ assert 0, css_select("p").size
+ end
+
+
+ def test_nested_css_select
+ render_html %Q{foo
foo
}
+ assert_select "div#?", /\d+/ do |elements|
+ assert_equal 1, css_select(elements[0], "div").size
+ assert_equal 1, css_select(elements[1], "div").size
+ end
+ assert_select "div" do
+ assert_equal 2, css_select("div").size
+ css_select("div").each do |element|
+ # Testing as a group is one thing
+ assert !css_select("#1,#2").empty?
+ # Testing individually is another
+ assert !css_select("#1").empty?
+ assert !css_select("#2").empty?
+ end
+ end
+ end
+
+
+ # With one result.
+ def test_css_select_from_rjs_with_single_result
+ render_rjs do |page|
+ page.replace_html "test", "foo
\nfoo
"
+ end
+ assert_equal 2, css_select("div").size
+ assert_equal 1, css_select("#1").size
+ assert_equal 1, css_select("#2").size
+ end
+
+ # With multiple results.
+ def test_css_select_from_rjs_with_multiple_results
+ render_rjs do |page|
+ page.replace_html "test", "foo
"
+ page.replace_html "test2", "foo
"
+ end
+
+ assert_equal 2, css_select("div").size
+ assert_equal 1, css_select("#1").size
+ assert_equal 1, css_select("#2").size
+ end
+
+
+ #
+ # Test assert_select_rjs.
+ #
+
+
+ # Test that we can pick up all statements in the result.
+ def test_assert_select_rjs_picks_up_all_statements
+ render_rjs do |page|
+ page.replace "test", "foo
"
+ page.replace_html "test2", "foo
"
+ page.insert_html :top, "test3", "foo
"
+ end
+
+ found = false
+ assert_select_rjs do
+ assert_select "#1"
+ assert_select "#2"
+ assert_select "#3"
+ found = true
+ end
+ assert found
+ end
+
+ # Test that we fail if there is nothing to pick.
+ def test_assert_select_rjs_fails_if_nothing_to_pick
+ render_rjs { }
+ assert_raises(AssertionFailedError) { assert_select_rjs }
+ end
+
+ def test_assert_select_rjs_with_unicode
+ # Test that non-ascii characters (which are converted into \uXXXX in RJS) are decoded correctly.
+ render_rjs do |page|
+ page.replace "test", "\343\203\201\343\202\261\343\203\203\343\203\210
"
+ end
+ assert_select_rjs do
+ assert_select "#1", :text => "\343\203\201\343\202\261\343\203\203\343\203\210"
+ assert_select "#1", "\343\203\201\343\202\261\343\203\203\343\203\210"
+ assert_select "#1", Regexp.new("\343\203\201..\343\203\210",0,'U')
+ assert_raises(AssertionFailedError) { assert_select "#1", Regexp.new("\343\203\201.\343\203\210",0,'U') }
+ end
+ end
+
+ def test_assert_select_rjs_with_id
+ # Test that we can pick up all statements in the result.
+ render_rjs do |page|
+ page.replace "test1", "foo
"
+ page.replace_html "test2", "foo
"
+ page.insert_html :top, "test3", "foo
"
+ end
+ assert_select_rjs "test1" do
+ assert_select "div", 1
+ assert_select "#1"
+ end
+ assert_select_rjs "test2" do
+ assert_select "div", 1
+ assert_select "#2"
+ end
+ assert_select_rjs "test3" do
+ assert_select "div", 1
+ assert_select "#3"
+ end
+ assert_raises(AssertionFailedError) { assert_select_rjs "test4" }
+ end
+
+
+ def test_assert_select_rjs_for_replace
+ render_rjs do |page|
+ page.replace "test1", "foo
"
+ page.replace_html "test2", "foo
"
+ page.insert_html :top, "test3", "foo
"
+ end
+ # Replace.
+ assert_select_rjs :replace do
+ assert_select "div", 1
+ assert_select "#1"
+ end
+ assert_select_rjs :replace, "test1" do
+ assert_select "div", 1
+ assert_select "#1"
+ end
+ assert_raises(AssertionFailedError) { assert_select_rjs :replace, "test2" }
+ # Replace HTML.
+ assert_select_rjs :replace_html do
+ assert_select "div", 1
+ assert_select "#2"
+ end
+ assert_select_rjs :replace_html, "test2" do
+ assert_select "div", 1
+ assert_select "#2"
+ end
+ assert_raises(AssertionFailedError) { assert_select_rjs :replace_html, "test1" }
+ end
+
+ def test_assert_select_rjs_for_chained_replace
+ render_rjs do |page|
+ page['test1'].replace "foo
"
+ page['test2'].replace_html "foo
"
+ page.insert_html :top, "test3", "foo
"
+ end
+ # Replace.
+ assert_select_rjs :chained_replace do
+ assert_select "div", 1
+ assert_select "#1"
+ end
+ assert_select_rjs :chained_replace, "test1" do
+ assert_select "div", 1
+ assert_select "#1"
+ end
+ assert_raises(AssertionFailedError) { assert_select_rjs :chained_replace, "test2" }
+ # Replace HTML.
+ assert_select_rjs :chained_replace_html do
+ assert_select "div", 1
+ assert_select "#2"
+ end
+ assert_select_rjs :chained_replace_html, "test2" do
+ assert_select "div", 1
+ assert_select "#2"
+ end
+ assert_raises(AssertionFailedError) { assert_select_rjs :replace_html, "test1" }
+ end
+
+ # Non-positioned insert.
+ def test_assert_select_rjs_for_nonpositioned_insert
+ render_rjs do |page|
+ page.replace "test1", "foo
"
+ page.replace_html "test2", "foo
"
+ page.insert_html :top, "test3", "foo
"
+ end
+ assert_select_rjs :insert_html do
+ assert_select "div", 1
+ assert_select "#3"
+ end
+ assert_select_rjs :insert_html, "test3" do
+ assert_select "div", 1
+ assert_select "#3"
+ end
+ assert_raises(AssertionFailedError) { assert_select_rjs :insert_html, "test1" }
+ end
+
+ # Positioned insert.
+ def test_assert_select_rjs_for_positioned_insert
+ render_rjs do |page|
+ page.insert_html :top, "test1", "foo
"
+ page.insert_html :bottom, "test2", "foo
"
+ page.insert_html :before, "test3", "foo
"
+ page.insert_html :after, "test4", "foo
"
+ end
+ assert_select_rjs :insert, :top do
+ assert_select "div", 1
+ assert_select "#1"
+ end
+ assert_select_rjs :insert, :bottom do
+ assert_select "div", 1
+ assert_select "#2"
+ end
+ assert_select_rjs :insert, :before do
+ assert_select "div", 1
+ assert_select "#3"
+ end
+ assert_select_rjs :insert, :after do
+ assert_select "div", 1
+ assert_select "#4"
+ end
+ assert_select_rjs :insert_html do
+ assert_select "div", 4
+ end
+ end
+
+
+ # Simple selection from a single result.
+ def test_nested_assert_select_rjs_with_single_result
+ render_rjs do |page|
+ page.replace_html "test", "foo
\nfoo
"
+ end
+
+ assert_select_rjs "test" do |elements|
+ assert_equal 2, elements.size
+ assert_select "#1"
+ assert_select "#2"
+ end
+ end
+
+ # Deal with two results.
+ def test_nested_assert_select_rjs_with_two_results
+ render_rjs do |page|
+ page.replace_html "test", "foo
"
+ page.replace_html "test2", "foo
"
+ end
+
+ assert_select_rjs "test" do |elements|
+ assert_equal 1, elements.size
+ assert_select "#1"
+ end
+
+ assert_select_rjs "test2" do |elements|
+ assert_equal 1, elements.size
+ assert_select "#2"
+ end
+ end
+
+
+ def test_feed_item_encoded
+ render_xml <<-EOF
+
+
+ -
+
+ Test 1
+ ]]>
+
+
+ -
+
+ Test 2
+ ]]>
+
+
+
+
+EOF
+ assert_select "channel item description" do
+ # Test element regardless of wrapper.
+ assert_select_encoded do
+ assert_select "p", :count=>2, :text=>/Test/
+ end
+ # Test through encoded wrapper.
+ assert_select_encoded do
+ assert_select "encoded p", :count=>2, :text=>/Test/
+ end
+ # Use :root instead (recommended)
+ assert_select_encoded do
+ assert_select ":root p", :count=>2, :text=>/Test/
+ end
+ # Test individually.
+ assert_select "description" do |elements|
+ assert_select_encoded elements[0] do
+ assert_select "p", "Test 1"
+ end
+ assert_select_encoded elements[1] do
+ assert_select "p", "Test 2"
+ end
+ end
+ end
+
+ # Test that we only un-encode element itself.
+ assert_select "channel item" do
+ assert_select_encoded do
+ assert_select "p", 0
+ end
+ end
+ end
+
+
+ #
+ # Test assert_select_email
+ #
+
+ def test_assert_select_email
+ assert_raises(AssertionFailedError) { assert_select_email {} }
+ AssertSelectMailer.deliver_test ""
+ assert_select_email do
+ assert_select "div:root" do
+ assert_select "p:first-child", "foo"
+ assert_select "p:last-child", "bar"
+ end
+ end
+ end
+
+
+ protected
+ def render_html(html)
+ @controller.response_with = html
+ get :html
+ end
+
+ def render_rjs(&block)
+ @controller.response_with &block
+ get :rjs
+ end
+
+ def render_xml(xml)
+ @controller.response_with = xml
+ get :xml
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/base_test.rb b/tracks/vendor/rails/actionpack/test/controller/base_test.rb
new file mode 100644
index 00000000..bcc47326
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/base_test.rb
@@ -0,0 +1,136 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+require 'test/unit'
+require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
+
+# Provide some controller to run the tests on.
+module Submodule
+ class ContainedEmptyController < ActionController::Base
+ end
+ class ContainedNonEmptyController < ActionController::Base
+ def public_action
+ end
+
+ hide_action :hidden_action
+ def hidden_action
+ raise "Noooo!"
+ end
+
+ def another_hidden_action
+ end
+ hide_action :another_hidden_action
+ end
+ class SubclassedController < ContainedNonEmptyController
+ hide_action :public_action # Hiding it here should not affect the superclass.
+ end
+end
+class EmptyController < ActionController::Base
+end
+class NonEmptyController < ActionController::Base
+ def public_action
+ end
+
+ hide_action :hidden_action
+ def hidden_action
+ end
+end
+
+class MethodMissingController < ActionController::Base
+
+ hide_action :shouldnt_be_called
+ def shouldnt_be_called
+ raise "NO WAY!"
+ end
+
+protected
+
+ def method_missing(selector)
+ render :text => selector.to_s
+ end
+
+end
+
+class ControllerClassTests < Test::Unit::TestCase
+ def test_controller_path
+ assert_equal 'empty', EmptyController.controller_path
+ assert_equal EmptyController.controller_path, EmptyController.new.controller_path
+ assert_equal 'submodule/contained_empty', Submodule::ContainedEmptyController.controller_path
+ assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path
+ end
+ def test_controller_name
+ assert_equal 'empty', EmptyController.controller_name
+ assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name
+ end
+end
+
+class ControllerInstanceTests < Test::Unit::TestCase
+ def setup
+ @empty = EmptyController.new
+ @contained = Submodule::ContainedEmptyController.new
+ @empty_controllers = [@empty, @contained, Submodule::SubclassedController.new]
+
+ @non_empty_controllers = [NonEmptyController.new,
+ Submodule::ContainedNonEmptyController.new]
+ end
+
+ def test_action_methods
+ @empty_controllers.each do |c|
+ hide_mocha_methods_from_controller(c)
+ assert_equal Set.new, c.send(:action_methods), "#{c.controller_path} should be empty!"
+ end
+ @non_empty_controllers.each do |c|
+ hide_mocha_methods_from_controller(c)
+ assert_equal Set.new('public_action'), c.send(:action_methods), "#{c.controller_path} should not be empty!"
+ end
+ end
+
+ protected
+
+ # Mocha adds methods to Object which are then included in the public_instance_methods
+ # This method hides those from the controller so the above tests won't know the difference
+ def hide_mocha_methods_from_controller(controller)
+ mocha_methods = [:expects, :metaclass, :mocha, :mocha_inspect, :reset_mocha, :stubba_object, :stubba_method, :stubs, :verify]
+ controller.class.send(:hide_action, *mocha_methods)
+ end
+
+end
+
+
+class PerformActionTest < Test::Unit::TestCase
+ def use_controller(controller_class)
+ @controller = controller_class.new
+
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ @controller.logger = Logger.new(nil)
+
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_get_on_priv_should_show_selector
+ use_controller MethodMissingController
+ get :shouldnt_be_called
+ assert_response :success
+ assert_equal 'shouldnt_be_called', @response.body
+ end
+
+ def test_method_missing_is_not_an_action_name
+ use_controller MethodMissingController
+ assert ! @controller.send(:action_methods).include?('method_missing')
+
+ get :method_missing
+ assert_response :success
+ assert_equal 'method_missing', @response.body
+ end
+
+ def test_get_on_hidden_should_fail
+ use_controller NonEmptyController
+ get :hidden_action
+ assert_response 404
+
+ get :another_hidden_action
+ assert_response 404
+ end
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/controller/benchmark_test.rb b/tracks/vendor/rails/actionpack/test/controller/benchmark_test.rb
new file mode 100644
index 00000000..f346e575
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/benchmark_test.rb
@@ -0,0 +1,33 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+require 'test/unit'
+
+# Provide some static controllers.
+class BenchmarkedController < ActionController::Base
+ def public_action
+ render :nothing => true
+ end
+
+ def rescue_action(e)
+ raise e
+ end
+end
+
+class BenchmarkTest < Test::Unit::TestCase
+ class MockLogger
+ def method_missing(*args)
+ end
+ end
+
+ def setup
+ @controller = BenchmarkedController.new
+ # benchmark doesn't do anything unless a logger is set
+ @controller.logger = MockLogger.new
+ @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+ @request.host = "test.actioncontroller.i"
+ end
+
+ def test_with_http_1_0_request
+ @request.host = nil
+ assert_nothing_raised { get :public_action }
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/caching_test.rb b/tracks/vendor/rails/actionpack/test/controller/caching_test.rb
new file mode 100644
index 00000000..1935cf5a
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/caching_test.rb
@@ -0,0 +1,165 @@
+require 'fileutils'
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+CACHE_DIR = 'test_cache'
+# Don't change '/../temp/' cavalierly or you might hoze something you don't want hozed
+FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
+ActionController::Base.perform_caching = true
+ActionController::Base.fragment_cache_store = :file_store, FILE_STORE_PATH
+
+class PageCachingTest < Test::Unit::TestCase
+ def setup
+ ActionController::Routing::Routes.draw do |map|
+ map.main '', :controller => 'posts'
+ map.resources :posts
+ map.connect ':controller/:action/:id'
+ end
+
+ @request = ActionController::TestRequest.new
+ @params = {:controller => 'posts', :action => 'index', :only_path => true, :skip_relative_url_root => true}
+ @rewriter = ActionController::UrlRewriter.new(@request, @params)
+ end
+
+ def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
+ @params[:format] = 'rss'
+ assert_equal '/posts.rss', @rewriter.rewrite(@params)
+ @params[:format] = nil
+ assert_equal '/', @rewriter.rewrite(@params)
+ end
+end
+
+class ActionCachingTestController < ActionController::Base
+ caches_action :index
+
+ def index
+ @cache_this = Time.now.to_f.to_s
+ render :text => @cache_this
+ end
+
+ def expire
+ expire_action :controller => 'action_caching_test', :action => 'index'
+ render :nothing => true
+ end
+
+end
+
+class ActionCachingMockController
+ attr_accessor :mock_url_for
+ attr_accessor :mock_path
+
+ def initialize
+ yield self if block_given?
+ end
+
+ def url_for(*args)
+ @mock_url_for
+ end
+
+ def request
+ mocked_path = @mock_path
+ Object.new.instance_eval(<<-EVAL)
+ def path; '#{@mock_path}' end
+ self
+ EVAL
+ end
+end
+
+class ActionCacheTest < Test::Unit::TestCase
+ def setup
+ reset!
+ FileUtils.mkdir_p(FILE_STORE_PATH)
+ @path_class = ActionController::Caching::Actions::ActionCachePath
+ @mock_controller = ActionCachingMockController.new
+ end
+
+ def teardown
+ FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
+ end
+
+ def test_simple_action_cache
+ get :index
+ cached_time = content_to_cache
+ assert_equal cached_time, @response.body
+ reset!
+
+ get :index
+ assert_equal cached_time, @response.body
+ end
+
+ def test_cache_expiration
+ get :index
+ cached_time = content_to_cache
+ reset!
+
+ get :index
+ assert_equal cached_time, @response.body
+ reset!
+
+ get :expire
+ reset!
+
+ get :index
+ new_cached_time = content_to_cache
+ assert_not_equal cached_time, @response.body
+ reset!
+
+ get :index
+ assert_response :success
+ assert_equal new_cached_time, @response.body
+ end
+
+ def test_cache_is_scoped_by_subdomain
+ @request.host = 'jamis.hostname.com'
+ get :index
+ jamis_cache = content_to_cache
+
+ @request.host = 'david.hostname.com'
+ get :index
+ david_cache = content_to_cache
+ assert_not_equal jamis_cache, @response.body
+
+ @request.host = 'jamis.hostname.com'
+ get :index
+ assert_equal jamis_cache, @response.body
+
+ @request.host = 'david.hostname.com'
+ get :index
+ assert_equal david_cache, @response.body
+ end
+
+ def test_xml_version_of_resource_is_treated_as_different_cache
+ @mock_controller.mock_url_for = 'http://example.org/posts/'
+ @mock_controller.mock_path = '/posts/index.xml'
+ path_object = @path_class.new(@mock_controller)
+ assert_equal 'xml', path_object.extension
+ assert_equal 'example.org/posts/index.xml', path_object.path
+ end
+
+ def test_empty_path_is_normalized
+ @mock_controller.mock_url_for = 'http://example.org/'
+ @mock_controller.mock_path = '/'
+
+ assert_equal 'example.org/index', @path_class.path_for(@mock_controller)
+ end
+
+ def test_file_extensions
+ get :index, :id => 'kitten.jpg'
+ get :index, :id => 'kitten.jpg'
+
+ assert_response :success
+ end
+
+ private
+
+ def content_to_cache
+ assigns(:cache_this)
+ end
+
+ def reset!
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = ActionCachingTestController.new
+ @request.host = 'hostname.com'
+ end
+
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/controller/capture_test.rb b/tracks/vendor/rails/actionpack/test/controller/capture_test.rb
new file mode 100644
index 00000000..43bf346e
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/capture_test.rb
@@ -0,0 +1,82 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class CaptureController < ActionController::Base
+ def self.controller_name; "test"; end
+ def self.controller_path; "test"; end
+
+ def content_for
+ render :layout => "talk_from_action"
+ end
+
+ def erb_content_for
+ render :layout => "talk_from_action"
+ end
+
+ def block_content_for
+ render :layout => "talk_from_action"
+ end
+
+ def non_erb_block_content_for
+ render :layout => "talk_from_action"
+ end
+
+ def rescue_action(e) raise end
+end
+
+CaptureController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class CaptureTest < Test::Unit::TestCase
+ def setup
+ @controller = CaptureController.new
+
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ @controller.logger = Logger.new(nil)
+
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_simple_capture
+ get :capturing
+ assert_equal "Dreamy days", @response.body.strip
+ end
+
+ def test_content_for
+ get :content_for
+ assert_equal expected_content_for_output, @response.body
+ end
+
+ def test_erb_content_for
+ get :content_for
+ assert_equal expected_content_for_output, @response.body
+ end
+
+ def test_block_content_for
+ get :block_content_for
+ assert_equal expected_content_for_output, @response.body
+ end
+
+ def test_non_erb_block_content_for
+ get :non_erb_block_content_for
+ assert_equal expected_content_for_output, @response.body
+ end
+
+ def test_update_element_with_capture
+ assert_deprecated 'update_element_function' do
+ get :update_element_with_capture
+ end
+ assert_equal(
+ "" +
+ "\n\n$('status').innerHTML = '\\n You bought something! \\n';",
+ @response.body.strip
+ )
+ end
+
+ private
+ def expected_content_for_output
+ "Putting stuff in the title! \n\nGreat stuff!"
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/cgi_test.rb b/tracks/vendor/rails/actionpack/test/controller/cgi_test.rb
new file mode 100755
index 00000000..1d2888ad
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/cgi_test.rb
@@ -0,0 +1,428 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+require 'action_controller/cgi_process'
+require 'action_controller/cgi_ext/cgi_ext'
+
+
+require 'stringio'
+
+class CGITest < Test::Unit::TestCase
+ def setup
+ @query_string = "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1"
+ @query_string_with_nil = "action=create_customer&full_name="
+ @query_string_with_array = "action=create_customer&selected[]=1&selected[]=2&selected[]=3"
+ @query_string_with_amps = "action=create_customer&name=Don%27t+%26+Does"
+ @query_string_with_multiple_of_same_name =
+ "action=update_order&full_name=Lau%20Taarnskov&products=4&products=2&products=3"
+ @query_string_with_many_equal = "action=create_customer&full_name=abc=def=ghi"
+ @query_string_without_equal = "action"
+ @query_string_with_many_ampersands =
+ "&action=create_customer&&&full_name=David%20Heinemeier%20Hansson"
+ @query_string_with_empty_key = "action=create_customer&full_name=David%20Heinemeier%20Hansson&=Save"
+ end
+
+ def test_query_string
+ assert_equal(
+ { "action" => "create_customer", "full_name" => "David Heinemeier Hansson", "customerId" => "1"},
+ CGIMethods.parse_query_parameters(@query_string)
+ )
+ end
+
+ def test_deep_query_string
+ expected = {'x' => {'y' => {'z' => '10'}}}
+ assert_equal(expected, CGIMethods.parse_query_parameters('x[y][z]=10'))
+ end
+
+ def test_deep_query_string_with_array
+ assert_equal({'x' => {'y' => {'z' => ['10']}}}, CGIMethods.parse_query_parameters('x[y][z][]=10'))
+ assert_equal({'x' => {'y' => {'z' => ['10', '5']}}}, CGIMethods.parse_query_parameters('x[y][z][]=10&x[y][z][]=5'))
+ end
+
+ def test_deep_query_string_with_array_of_hash
+ assert_equal({'x' => {'y' => [{'z' => '10'}]}}, CGIMethods.parse_query_parameters('x[y][][z]=10'))
+ assert_equal({'x' => {'y' => [{'z' => '10', 'w' => '10'}]}}, CGIMethods.parse_query_parameters('x[y][][z]=10&x[y][][w]=10'))
+ end
+
+ def test_deep_query_string_with_array_of_hashes_with_one_pair
+ assert_equal({'x' => {'y' => [{'z' => '10'}, {'z' => '20'}]}}, CGIMethods.parse_query_parameters('x[y][][z]=10&x[y][][z]=20'))
+ assert_equal("10", CGIMethods.parse_query_parameters('x[y][][z]=10&x[y][][z]=20')["x"]["y"].first["z"])
+ assert_equal("10", CGIMethods.parse_query_parameters('x[y][][z]=10&x[y][][z]=20').with_indifferent_access[:x][:y].first[:z])
+ end
+
+ def test_request_hash_parsing
+ query = {
+ "note[viewers][viewer][][type]" => ["User", "Group"],
+ "note[viewers][viewer][][id]" => ["1", "2"]
+ }
+
+ expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } }
+
+ assert_equal(expected, CGIMethods.parse_request_parameters(query))
+ end
+
+ def test_deep_query_string_with_array_of_hashes_with_multiple_pairs
+ assert_equal(
+ {'x' => {'y' => [{'z' => '10', 'w' => 'a'}, {'z' => '20', 'w' => 'b'}]}},
+ CGIMethods.parse_query_parameters('x[y][][z]=10&x[y][][w]=a&x[y][][z]=20&x[y][][w]=b')
+ )
+ end
+
+ def test_query_string_with_nil
+ assert_equal(
+ { "action" => "create_customer", "full_name" => nil},
+ CGIMethods.parse_query_parameters(@query_string_with_nil)
+ )
+ end
+
+ def test_query_string_with_array
+ assert_equal(
+ { "action" => "create_customer", "selected" => ["1", "2", "3"]},
+ CGIMethods.parse_query_parameters(@query_string_with_array)
+ )
+ end
+
+ def test_query_string_with_amps
+ assert_equal(
+ { "action" => "create_customer", "name" => "Don't & Does"},
+ CGIMethods.parse_query_parameters(@query_string_with_amps)
+ )
+ end
+
+ def test_query_string_with_many_equal
+ assert_equal(
+ { "action" => "create_customer", "full_name" => "abc=def=ghi"},
+ CGIMethods.parse_query_parameters(@query_string_with_many_equal)
+ )
+ end
+
+ def test_query_string_without_equal
+ assert_equal(
+ { "action" => nil },
+ CGIMethods.parse_query_parameters(@query_string_without_equal)
+ )
+ end
+
+ def test_query_string_with_empty_key
+ assert_equal(
+ { "action" => "create_customer", "full_name" => "David Heinemeier Hansson" },
+ CGIMethods.parse_query_parameters(@query_string_with_empty_key)
+ )
+ end
+
+ def test_query_string_with_many_ampersands
+ assert_equal(
+ { "action" => "create_customer", "full_name" => "David Heinemeier Hansson"},
+ CGIMethods.parse_query_parameters(@query_string_with_many_ampersands)
+ )
+ end
+
+ def test_parse_params
+ input = {
+ "customers[boston][first][name]" => [ "David" ],
+ "customers[boston][first][url]" => [ "http://David" ],
+ "customers[boston][second][name]" => [ "Allan" ],
+ "customers[boston][second][url]" => [ "http://Allan" ],
+ "something_else" => [ "blah" ],
+ "something_nil" => [ nil ],
+ "something_empty" => [ "" ],
+ "products[first]" => [ "Apple Computer" ],
+ "products[second]" => [ "Pc" ],
+ "" => [ 'Save' ]
+ }
+
+ expected_output = {
+ "customers" => {
+ "boston" => {
+ "first" => {
+ "name" => "David",
+ "url" => "http://David"
+ },
+ "second" => {
+ "name" => "Allan",
+ "url" => "http://Allan"
+ }
+ }
+ },
+ "something_else" => "blah",
+ "something_empty" => "",
+ "something_nil" => "",
+ "products" => {
+ "first" => "Apple Computer",
+ "second" => "Pc"
+ }
+ }
+
+ assert_equal expected_output, CGIMethods.parse_request_parameters(input)
+ end
+
+ def test_parse_params_from_multipart_upload
+ mockup = Struct.new(:content_type, :original_filename, :read, :rewind)
+ file = mockup.new('img/jpeg', 'foo.jpg')
+ ie_file = mockup.new('img/jpeg', 'c:\\Documents and Settings\\foo\\Desktop\\bar.jpg')
+ non_file_text_part = mockup.new('text/plain', '', 'abc')
+
+ input = {
+ "something" => [ StringIO.new("") ],
+ "array_of_stringios" => [[ StringIO.new("One"), StringIO.new("Two") ]],
+ "mixed_types_array" => [[ StringIO.new("Three"), "NotStringIO" ]],
+ "mixed_types_as_checkboxes[strings][nested]" => [[ file, "String", StringIO.new("StringIO")]],
+ "ie_mixed_types_as_checkboxes[strings][nested]" => [[ ie_file, "String", StringIO.new("StringIO")]],
+ "products[string]" => [ StringIO.new("Apple Computer") ],
+ "products[file]" => [ file ],
+ "ie_products[string]" => [ StringIO.new("Microsoft") ],
+ "ie_products[file]" => [ ie_file ],
+ "text_part" => [non_file_text_part]
+ }
+
+ expected_output = {
+ "something" => "",
+ "array_of_stringios" => ["One", "Two"],
+ "mixed_types_array" => [ "Three", "NotStringIO" ],
+ "mixed_types_as_checkboxes" => {
+ "strings" => {
+ "nested" => [ file, "String", "StringIO" ]
+ },
+ },
+ "ie_mixed_types_as_checkboxes" => {
+ "strings" => {
+ "nested" => [ ie_file, "String", "StringIO" ]
+ },
+ },
+ "products" => {
+ "string" => "Apple Computer",
+ "file" => file
+ },
+ "ie_products" => {
+ "string" => "Microsoft",
+ "file" => ie_file
+ },
+ "text_part" => "abc"
+ }
+
+ params = CGIMethods.parse_request_parameters(input)
+ assert_equal expected_output, params
+
+ # Lone filenames are preserved.
+ assert_equal 'foo.jpg', params['mixed_types_as_checkboxes']['strings']['nested'].first.original_filename
+ assert_equal 'foo.jpg', params['products']['file'].original_filename
+
+ # But full Windows paths are reduced to their basename.
+ assert_equal 'bar.jpg', params['ie_mixed_types_as_checkboxes']['strings']['nested'].first.original_filename
+ assert_equal 'bar.jpg', params['ie_products']['file'].original_filename
+ end
+
+ def test_parse_params_with_file
+ input = {
+ "customers[boston][first][name]" => [ "David" ],
+ "something_else" => [ "blah" ],
+ "logo" => [ File.new(File.dirname(__FILE__) + "/cgi_test.rb").path ]
+ }
+
+ expected_output = {
+ "customers" => {
+ "boston" => {
+ "first" => {
+ "name" => "David"
+ }
+ }
+ },
+ "something_else" => "blah",
+ "logo" => File.new(File.dirname(__FILE__) + "/cgi_test.rb").path,
+ }
+
+ assert_equal expected_output, CGIMethods.parse_request_parameters(input)
+ end
+
+ def test_parse_params_with_array
+ input = { "selected[]" => [ "1", "2", "3" ] }
+
+ expected_output = { "selected" => [ "1", "2", "3" ] }
+
+ assert_equal expected_output, CGIMethods.parse_request_parameters(input)
+ end
+
+ def test_parse_params_with_non_alphanumeric_name
+ input = { "a/b[c]" => %w(d) }
+ expected = { "a/b" => { "c" => "d" }}
+ assert_equal expected, CGIMethods.parse_request_parameters(input)
+ end
+
+ def test_parse_params_with_single_brackets_in_middle
+ input = { "a/b[c]d" => %w(e) }
+ expected = { "a/b" => {} }
+ assert_equal expected, CGIMethods.parse_request_parameters(input)
+ end
+
+ def test_parse_params_with_separated_brackets
+ input = { "a/b@[c]d[e]" => %w(f) }
+ expected = { "a/b@" => { }}
+ assert_equal expected, CGIMethods.parse_request_parameters(input)
+ end
+
+ def test_parse_params_with_separated_brackets_and_array
+ input = { "a/b@[c]d[e][]" => %w(f) }
+ expected = { "a/b@" => { }}
+ assert_equal expected , CGIMethods.parse_request_parameters(input)
+ end
+
+ def test_parse_params_with_unmatched_brackets_and_array
+ input = { "a/b@[c][d[e][]" => %w(f) }
+ expected = { "a/b@" => { "c" => { }}}
+ assert_equal expected, CGIMethods.parse_request_parameters(input)
+ end
+
+ def test_parse_params_with_nil_key
+ input = { nil => nil, "test2" => %w(value1) }
+ expected = { "test2" => "value1" }
+ assert_equal expected, CGIMethods.parse_request_parameters(input)
+ end
+end
+
+
+class MultipartCGITest < Test::Unit::TestCase
+ FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart'
+
+ def setup
+ ENV['REQUEST_METHOD'] = 'POST'
+ ENV['CONTENT_LENGTH'] = '0'
+ ENV['CONTENT_TYPE'] = 'multipart/form-data, boundary=AaB03x'
+ end
+
+ def test_single_parameter
+ params = process('single_parameter')
+ assert_equal({ 'foo' => 'bar' }, params)
+ end
+
+ def test_text_file
+ params = process('text_file')
+ assert_equal %w(file foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ file = params['file']
+ assert_kind_of StringIO, file
+ assert_equal 'file.txt', file.original_filename
+ assert_equal "text/plain\r", file.content_type
+ assert_equal 'contents', file.read
+ end
+
+ def test_large_text_file
+ params = process('large_text_file')
+ assert_equal %w(file foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ file = params['file']
+ assert_kind_of Tempfile, file
+ assert_equal 'file.txt', file.original_filename
+ assert_equal "text/plain\r", file.content_type
+ assert ('a' * 20480) == file.read
+ end
+
+ def test_binary_file
+ params = process('binary_file')
+ assert_equal %w(file flowers foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ file = params['file']
+ assert_kind_of StringIO, file
+ assert_equal 'file.txt', file.original_filename
+ assert_equal "text/plain\r", file.content_type
+ assert_equal 'contents', file.read
+
+ file = params['flowers']
+ assert_kind_of StringIO, file
+ assert_equal 'flowers.jpg', file.original_filename
+ assert_equal "image/jpeg\r", file.content_type
+ assert_equal 19512, file.size
+ #assert_equal File.read(File.dirname(__FILE__) + '/../../../activerecord/test/fixtures/flowers.jpg'), file.read
+ end
+
+ def test_mixed_files
+ params = process('mixed_files')
+ assert_equal %w(files foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ # Ruby CGI doesn't handle multipart/mixed for us.
+ assert_kind_of String, params['files']
+ assert_equal 19756, params['files'].size
+ end
+
+ # Rewind readable cgi params so others may reread them (such as CGI::Session
+ # when passing the session id in a multipart form).
+ def test_multipart_param_rewound
+ params = process('text_file')
+ assert_equal 'bar', @cgi.params['foo'][0].read
+ end
+
+ private
+ def process(name)
+ old_stdin = $stdin
+ File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
+ ENV['CONTENT_LENGTH'] = file.stat.size.to_s
+ $stdin = file
+ @cgi = CGI.new
+ CGIMethods.parse_request_parameters @cgi.params
+ end
+ ensure
+ $stdin = old_stdin
+ end
+end
+
+# Ensures that PUT works with multipart as well as POST.
+class PutMultipartCGITest < MultipartCGITest
+ def setup
+ super
+ ENV['REQUEST_METHOD'] = 'PUT'
+ end
+end
+
+
+class CGIRequestTest < Test::Unit::TestCase
+ def setup
+ @request_hash = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"}
+ # cookie as returned by some Nokia phone browsers (no space after semicolon separator)
+ @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2;is_admin=yes"}
+ @fake_cgi = Struct.new(:env_table).new(@request_hash)
+ @request = ActionController::CgiRequest.new(@fake_cgi)
+ end
+
+ def test_proxy_request
+ assert_equal 'glu.ttono.us', @request.host_with_port
+ end
+
+ def test_http_host
+ @request_hash.delete "HTTP_X_FORWARDED_HOST"
+ @request_hash['HTTP_HOST'] = "rubyonrails.org:8080"
+ assert_equal "rubyonrails.org:8080", @request.host_with_port
+
+ @request_hash['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
+ assert_equal "www.secondhost.org", @request.host
+ end
+
+ def test_http_host_with_default_port_overrides_server_port
+ @request_hash.delete "HTTP_X_FORWARDED_HOST"
+ @request_hash['HTTP_HOST'] = "rubyonrails.org"
+ assert_equal "rubyonrails.org", @request.host_with_port
+ end
+
+ def test_host_with_port_defaults_to_server_name_if_no_host_headers
+ @request_hash.delete "HTTP_X_FORWARDED_HOST"
+ @request_hash.delete "HTTP_HOST"
+ assert_equal "glu.ttono.us:8007", @request.host_with_port
+ end
+
+ def test_host_with_port_falls_back_to_server_addr_if_necessary
+ @request_hash.delete "HTTP_X_FORWARDED_HOST"
+ @request_hash.delete "HTTP_HOST"
+ @request_hash.delete "SERVER_NAME"
+ assert_equal "207.7.108.53:8007", @request.host_with_port
+ end
+
+ def test_cookie_syntax_resilience
+ cookies = CGI::Cookie::parse(@request_hash["HTTP_COOKIE"]);
+ assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"]
+ assert_equal ["yes"], cookies["is_admin"]
+
+ alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
+ assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], alt_cookies["_session_id"]
+ assert_equal ["yes"], alt_cookies["is_admin"]
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/components_test.rb b/tracks/vendor/rails/actionpack/test/controller/components_test.rb
new file mode 100644
index 00000000..fbe46375
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/components_test.rb
@@ -0,0 +1,151 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class CallerController < ActionController::Base
+ def calling_from_controller
+ render_component(:controller => "callee", :action => "being_called")
+ end
+
+ def calling_from_controller_with_params
+ render_component(:controller => "callee", :action => "being_called", :params => { "name" => "David" })
+ end
+
+ def calling_from_controller_with_different_status_code
+ render_component(:controller => "callee", :action => "blowing_up")
+ end
+
+ def calling_from_template
+ render_template "Ring, ring: <%= render_component(:controller => 'callee', :action => 'being_called') %>"
+ end
+
+ def internal_caller
+ render_template "Are you there? <%= render_component(:action => 'internal_callee') %>"
+ end
+
+ def internal_callee
+ render_text "Yes, ma'am"
+ end
+
+ def set_flash
+ render_component(:controller => "callee", :action => "set_flash")
+ end
+
+ def use_flash
+ render_component(:controller => "callee", :action => "use_flash")
+ end
+
+ def calling_redirected
+ render_component(:controller => "callee", :action => "redirected")
+ end
+
+ def calling_redirected_as_string
+ render_template "<%= render_component(:controller => 'callee', :action => 'redirected') %>"
+ end
+
+ def rescue_action(e) raise end
+end
+
+class CalleeController < ActionController::Base
+ def being_called
+ render_text "#{params[:name] || "Lady"} of the House, speaking"
+ end
+
+ def blowing_up
+ render_text "It's game over, man, just game over, man!", "500 Internal Server Error"
+ end
+
+ def set_flash
+ flash[:notice] = 'My stoney baby'
+ render :text => 'flash is set'
+ end
+
+ def use_flash
+ render :text => flash[:notice] || 'no flash'
+ end
+
+ def redirected
+ redirect_to :controller => "callee", :action => "being_called"
+ end
+
+ def rescue_action(e) raise end
+end
+
+class ComponentsTest < Test::Unit::TestCase
+ def setup
+ @controller = CallerController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_calling_from_controller
+ get :calling_from_controller
+ assert_equal "Lady of the House, speaking", @response.body
+ end
+
+ def test_calling_from_controller_with_params
+ get :calling_from_controller_with_params
+ assert_equal "David of the House, speaking", @response.body
+ end
+
+ def test_calling_from_controller_with_different_status_code
+ get :calling_from_controller_with_different_status_code
+ assert_equal 500, @response.response_code
+ end
+
+ def test_calling_from_template
+ get :calling_from_template
+ assert_equal "Ring, ring: Lady of the House, speaking", @response.body
+ end
+
+ def test_internal_calling
+ get :internal_caller
+ assert_equal "Are you there? Yes, ma'am", @response.body
+ end
+
+ def test_flash
+ get :set_flash
+ assert_equal 'My stoney baby', flash[:notice]
+ get :use_flash
+ assert_equal 'My stoney baby', @response.body
+ get :use_flash
+ assert_equal 'no flash', @response.body
+ end
+
+ def test_component_redirect_redirects
+ get :calling_redirected
+
+ assert_redirected_to :action => "being_called"
+ end
+
+ def test_component_multiple_redirect_redirects
+ test_component_redirect_redirects
+ test_internal_calling
+ end
+
+ def test_component_as_string_redirect_renders_redirecte_action
+ get :calling_redirected_as_string
+
+ assert_equal "Lady of the House, speaking", @response.body
+ end
+end
+
+module A
+ module B
+ module C
+ class NestedController < ActionController::Base
+ # Stub for uses_component_template_root
+ def self.caller
+ [ '/path/to/active_support/deprecation.rb:93:in `uses_component_template_root',
+ './test/fixtures/a/b/c/nested_controller.rb' ]
+ end
+ end
+ end
+ end
+end
+
+class UsesComponentTemplateRootTest < Test::Unit::TestCase
+ def test_uses_component_template_root
+ assert_deprecated 'uses_component_template_root' do
+ assert_equal './test/fixtures/', A::B::C::NestedController.uses_component_template_root
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/content_type_test.rb b/tracks/vendor/rails/actionpack/test/controller/content_type_test.rb
new file mode 100644
index 00000000..6f0618da
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/content_type_test.rb
@@ -0,0 +1,139 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class ContentTypeController < ActionController::Base
+ def render_content_type_from_body
+ response.content_type = Mime::RSS
+ render :text => "hello world!"
+ end
+
+ def render_defaults
+ render :text => "hello world!"
+ end
+
+ def render_content_type_from_render
+ render :text => "hello world!", :content_type => Mime::RSS
+ end
+
+ def render_charset_from_body
+ response.charset = "utf-16"
+ render :text => "hello world!"
+ end
+
+ def render_default_for_rhtml
+ end
+
+ def render_default_for_rxml
+ end
+
+ def render_default_for_rjs
+ end
+
+ def render_change_for_rxml
+ response.content_type = Mime::HTML
+ render :action => "render_default_for_rxml"
+ end
+
+ def render_default_content_types_for_respond_to
+ respond_to do |format|
+ format.html { render :text => "hello world!" }
+ format.xml { render :action => "render_default_content_types_for_respond_to.rhtml" }
+ format.js { render :text => "hello world!" }
+ format.rss { render :text => "hello world!", :content_type => Mime::XML }
+ end
+ end
+
+ def rescue_action(e) raise end
+end
+
+ContentTypeController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class ContentTypeTest < Test::Unit::TestCase
+ def setup
+ @controller = ContentTypeController.new
+
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ @controller.logger = Logger.new(nil)
+
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_render_defaults
+ get :render_defaults
+ assert_equal "utf-8", @response.charset
+ assert_equal Mime::HTML, @response.content_type
+ end
+
+ def test_render_changed_charset_default
+ ContentTypeController.default_charset = "utf-16"
+ get :render_defaults
+ assert_equal "utf-16", @response.charset
+ assert_equal Mime::HTML, @response.content_type
+ ContentTypeController.default_charset = "utf-8"
+ end
+
+ def test_content_type_from_body
+ get :render_content_type_from_body
+ assert_equal "application/rss+xml", @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_content_type_from_render
+ get :render_content_type_from_render
+ assert_equal "application/rss+xml", @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_charset_from_body
+ get :render_charset_from_body
+ assert_equal "utf-16", @response.charset
+ assert_equal Mime::HTML, @response.content_type
+ end
+
+ def test_default_for_rhtml
+ get :render_default_for_rhtml
+ assert_equal Mime::HTML, @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_default_for_rxml
+ get :render_default_for_rxml
+ assert_equal Mime::XML, @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_default_for_rjs
+ xhr :post, :render_default_for_rjs
+ assert_equal Mime::JS, @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_change_for_rxml
+ get :render_change_for_rxml
+ assert_equal Mime::HTML, @response.content_type
+ assert_equal "utf-8", @response.charset
+ end
+
+ def test_render_default_content_types_for_respond_to
+ @request.env["HTTP_ACCEPT"] = Mime::HTML.to_s
+ get :render_default_content_types_for_respond_to
+ assert_equal Mime::HTML, @response.content_type
+
+ @request.env["HTTP_ACCEPT"] = Mime::JS.to_s
+ get :render_default_content_types_for_respond_to
+ assert_equal Mime::JS, @response.content_type
+ end
+
+ def test_render_default_content_types_for_respond_to_with_template
+ @request.env["HTTP_ACCEPT"] = Mime::XML.to_s
+ get :render_default_content_types_for_respond_to
+ assert_equal Mime::XML, @response.content_type
+ end
+
+ def test_render_default_content_types_for_respond_to_with_overwrite
+ @request.env["HTTP_ACCEPT"] = Mime::RSS.to_s
+ get :render_default_content_types_for_respond_to
+ assert_equal Mime::XML, @response.content_type
+ end
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb b/tracks/vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/tracks/vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/user_controller.rb b/tracks/vendor/rails/actionpack/test/controller/controller_fixtures/app/controllers/user_controller.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/tracks/vendor/rails/actionpack/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb b/tracks/vendor/rails/actionpack/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/tracks/vendor/rails/actionpack/test/controller/cookie_test.rb b/tracks/vendor/rails/actionpack/test/controller/cookie_test.rb
new file mode 100644
index 00000000..2e1c7f0e
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/cookie_test.rb
@@ -0,0 +1,80 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class CookieTest < Test::Unit::TestCase
+ class TestController < ActionController::Base
+ def authenticate_with_deprecated_writer
+ cookie "name" => "user_name", "value" => "david"
+ render_text "hello world"
+ end
+
+ def authenticate
+ cookies["user_name"] = "david"
+ render_text "hello world"
+ end
+
+ def authenticate_for_fourten_days
+ cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) }
+ render_text "hello world"
+ end
+
+ def authenticate_for_fourten_days_with_symbols
+ cookies[:user_name] = { :value => "david", :expires => Time.local(2005, 10, 10) }
+ render_text "hello world"
+ end
+
+ def set_multiple_cookies
+ cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) }
+ cookies["login"] = "XJ-122"
+ render_text "hello world"
+ end
+
+ def access_frozen_cookies
+ cookies["will"] = "work"
+ render_text "hello world"
+ end
+
+ def rescue_action(e) raise end
+ end
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_setting_cookie_with_deprecated_writer
+ @request.action = "authenticate_with_deprecated_writer"
+ assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david") ], process_request.headers["cookie"]
+ end
+
+ def test_setting_cookie
+ @request.action = "authenticate"
+ assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david") ], process_request.headers["cookie"]
+ end
+
+ def test_setting_cookie_for_fourteen_days
+ @request.action = "authenticate_for_fourten_days"
+ assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], process_request.headers["cookie"]
+ end
+
+ def test_setting_cookie_for_fourteen_days_with_symbols
+ @request.action = "authenticate_for_fourten_days"
+ assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], process_request.headers["cookie"]
+ end
+
+ def test_multiple_cookies
+ @request.action = "set_multiple_cookies"
+ assert_equal 2, process_request.headers["cookie"].size
+ end
+
+ def test_setting_test_cookie
+ @request.action = "access_frozen_cookies"
+ assert_nothing_raised { process_request }
+ end
+
+ private
+ def process_request
+ TestController.process(@request, @response)
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/custom_handler_test.rb b/tracks/vendor/rails/actionpack/test/controller/custom_handler_test.rb
new file mode 100644
index 00000000..2747a0f3
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/custom_handler_test.rb
@@ -0,0 +1,41 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class CustomHandler
+ def initialize( view )
+ @view = view
+ end
+
+ def render( template, local_assigns )
+ [ template,
+ local_assigns,
+ @view ]
+ end
+end
+
+class CustomHandlerTest < Test::Unit::TestCase
+ def setup
+ ActionView::Base.register_template_handler "foo", CustomHandler
+ ActionView::Base.register_template_handler :foo2, CustomHandler
+ @view = ActionView::Base.new
+ end
+
+ def test_custom_render
+ result = @view.render_template( "foo", "hello <%= one %>", nil, :one => "two" )
+ assert_equal(
+ [ "hello <%= one %>", { :one => "two" }, @view ],
+ result )
+ end
+
+ def test_custom_render2
+ result = @view.render_template( "foo2", "hello <%= one %>", nil, :one => "two" )
+ assert_equal(
+ [ "hello <%= one %>", { :one => "two" }, @view ],
+ result )
+ end
+
+ def test_unhandled_extension
+ # uses the ERb handler by default if the extension isn't recognized
+ result = @view.render_template( "bar", "hello <%= one %>", nil, :one => "two" )
+ assert_equal "hello two", result
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb b/tracks/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
new file mode 100644
index 00000000..7865a69e
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
@@ -0,0 +1,48 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DeprecatedControllerInstanceVariablesTest < Test::Unit::TestCase
+ class Target < ActionController::Base
+ def initialize(run = nil)
+ instance_eval(run) if run
+ super()
+ end
+
+ def noop
+ render :nothing => true
+ end
+
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
+ class_eval "def old_#{var}; render :text => @#{var}.to_s end"
+ class_eval "def new_#{var}; render :text => #{var}.to_s end"
+ class_eval "def internal_#{var}; render :text => @_#{var}.to_s end"
+ end
+
+ def rescue_action(e) raise e end
+ end
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = Target.new
+ end
+
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
+ class_eval <<-end_eval, __FILE__, __LINE__
+ def test_old_#{var}_is_deprecated
+ assert_deprecated('@#{var}') { get :old_#{var} }
+ end
+ def test_new_#{var}_isnt_deprecated
+ assert_not_deprecated { get :new_#{var} }
+ end
+ def test_internal_#{var}_isnt_deprecated
+ assert_not_deprecated { get :internal_#{var} }
+ end
+ def test_#{var}_raises_if_already_set
+ assert_raise(RuntimeError) do
+ @controller = Target.new '@#{var} = Object.new'
+ get :noop
+ end
+ end
+ end_eval
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb b/tracks/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
new file mode 100644
index 00000000..3d5f6606
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
@@ -0,0 +1,43 @@
+require File.dirname(__FILE__) + '/../../abstract_unit'
+
+class DeprecatedBaseMethodsTest < Test::Unit::TestCase
+ class Target < ActionController::Base
+ def deprecated_symbol_parameter_to_url_for
+ redirect_to(url_for(:home_url, "superstars"))
+ end
+
+ def deprecated_render_parameters
+ render "fun/games/hello_world"
+ end
+
+ def home_url(greeting)
+ "http://example.com/#{greeting}"
+ end
+
+ def rescue_action(e) raise e end
+ end
+
+ Target.template_root = File.dirname(__FILE__) + "/../../fixtures"
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = Target.new
+ end
+
+ def test_deprecated_symbol_parameter_to_url_for
+ assert_deprecated("url_for(:home_url)") do
+ get :deprecated_symbol_parameter_to_url_for
+ end
+
+ assert_redirected_to "http://example.com/superstars"
+ end
+
+ def test_deprecated_render_parameters
+ assert_deprecated("render('fun/games/hello_world')") do
+ get :deprecated_render_parameters
+ end
+
+ assert_equal "Living in a nested world", @response.body
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/fake_controllers.rb b/tracks/vendor/rails/actionpack/test/controller/fake_controllers.rb
new file mode 100644
index 00000000..5f958b28
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/fake_controllers.rb
@@ -0,0 +1,16 @@
+class << Object; alias_method :const_available?, :const_defined?; end
+
+class ContentController < Class.new(ActionController::Base)
+end
+class NotAController
+end
+module Admin
+ class << self; alias_method :const_available?, :const_defined?; end
+ class UserController < Class.new(ActionController::Base); end
+ class NewsFeedController < Class.new(ActionController::Base); end
+end
+
+ActionController::Routing::Routes.draw do |map|
+ map.route_one 'route_one', :controller => 'elsewhere', :action => 'flash_me'
+ map.connect ':controller/:action/:id'
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/filter_params_test.rb b/tracks/vendor/rails/actionpack/test/controller/filter_params_test.rb
new file mode 100644
index 00000000..5ad0d7f8
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/filter_params_test.rb
@@ -0,0 +1,42 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class FilterParamController < ActionController::Base
+end
+
+class FilterParamTest < Test::Unit::TestCase
+ def setup
+ @controller = FilterParamController.new
+ end
+
+ def test_filter_parameters
+ assert FilterParamController.respond_to?(:filter_parameter_logging)
+ assert !@controller.respond_to?(:filter_parameters)
+
+ FilterParamController.filter_parameter_logging
+ assert @controller.respond_to?(:filter_parameters)
+
+ test_hashes = [[{},{},[]],
+ [{'foo'=>'bar'},{'foo'=>'bar'},[]],
+ [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
+ [{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'],
+ [{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'],
+ [{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
+ [{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
+ [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana']]
+
+ test_hashes.each do |before_filter, after_filter, filter_words|
+ FilterParamController.filter_parameter_logging(*filter_words)
+ assert_equal after_filter, @controller.filter_parameters(before_filter)
+
+ filter_words.push('blah')
+ FilterParamController.filter_parameter_logging(*filter_words) do |key, value|
+ value.reverse! if key =~ /bargain/
+ end
+
+ before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}}
+ after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}}
+
+ assert_equal after_filter, @controller.filter_parameters(before_filter)
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/filters_test.rb b/tracks/vendor/rails/actionpack/test/controller/filters_test.rb
new file mode 100644
index 00000000..3a74eba0
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/filters_test.rb
@@ -0,0 +1,695 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class FilterTest < Test::Unit::TestCase
+ class TestController < ActionController::Base
+ before_filter :ensure_login
+ after_filter :clean_up
+
+ def show
+ render :inline => "ran action"
+ end
+
+ private
+ def ensure_login
+ @ran_filter ||= []
+ @ran_filter << "ensure_login"
+ end
+
+ def clean_up
+ @ran_after_filter ||= []
+ @ran_after_filter << "clean_up"
+ end
+ end
+
+ class ChangingTheRequirementsController < TestController
+ before_filter :ensure_login, :except => [:go_wild]
+
+ def go_wild
+ render :text => "gobble"
+ end
+ end
+
+ class TestMultipleFiltersController < ActionController::Base
+ before_filter :try_1
+ before_filter :try_2
+ before_filter :try_3
+
+ (1..3).each do |i|
+ define_method "fail_#{i}" do
+ render :text => i.to_s
+ end
+ end
+
+ protected
+ (1..3).each do |i|
+ define_method "try_#{i}" do
+ instance_variable_set :@try, i
+ action_name != "fail_#{i}"
+ end
+ end
+ end
+
+ class RenderingController < ActionController::Base
+ before_filter :render_something_else
+
+ def show
+ @ran_action = true
+ render :inline => "ran action"
+ end
+
+ private
+ def render_something_else
+ render :inline => "something else"
+ end
+ end
+
+ class ConditionalFilterController < ActionController::Base
+ def show
+ render :inline => "ran action"
+ end
+
+ def another_action
+ render :inline => "ran action"
+ end
+
+ def show_without_filter
+ render :inline => "ran action without filter"
+ end
+
+ private
+ def ensure_login
+ @ran_filter ||= []
+ @ran_filter << "ensure_login"
+ end
+
+ def clean_up_tmp
+ @ran_filter ||= []
+ @ran_filter << "clean_up_tmp"
+ end
+
+ def rescue_action(e) raise(e) end
+ end
+
+ class ConditionalCollectionFilterController < ConditionalFilterController
+ before_filter :ensure_login, :except => [ :show_without_filter, :another_action ]
+ end
+
+ class OnlyConditionSymController < ConditionalFilterController
+ before_filter :ensure_login, :only => :show
+ end
+
+ class ExceptConditionSymController < ConditionalFilterController
+ before_filter :ensure_login, :except => :show_without_filter
+ end
+
+ class BeforeAndAfterConditionController < ConditionalFilterController
+ before_filter :ensure_login, :only => :show
+ after_filter :clean_up_tmp, :only => :show
+ end
+
+ class OnlyConditionProcController < ConditionalFilterController
+ before_filter(:only => :show) {|c| c.assigns["ran_proc_filter"] = true }
+ end
+
+ class ExceptConditionProcController < ConditionalFilterController
+ before_filter(:except => :show_without_filter) {|c| c.assigns["ran_proc_filter"] = true }
+ end
+
+ class ConditionalClassFilter
+ def self.filter(controller) controller.assigns["ran_class_filter"] = true end
+ end
+
+ class OnlyConditionClassController < ConditionalFilterController
+ before_filter ConditionalClassFilter, :only => :show
+ end
+
+ class ExceptConditionClassController < ConditionalFilterController
+ before_filter ConditionalClassFilter, :except => :show_without_filter
+ end
+
+ class AnomolousYetValidConditionController < ConditionalFilterController
+ before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.assigns["ran_proc_filter1"] = true }, :except => :show_without_filter) { |c| c.assigns["ran_proc_filter2"] = true}
+ end
+
+ class PrependingController < TestController
+ prepend_before_filter :wonderful_life
+ # skip_before_filter :fire_flash
+
+ private
+ def wonderful_life
+ @ran_filter ||= []
+ @ran_filter << "wonderful_life"
+ end
+ end
+
+ class ConditionalSkippingController < TestController
+ skip_before_filter :ensure_login, :only => [ :login ]
+ skip_after_filter :clean_up, :only => [ :login ]
+
+ before_filter :find_user, :only => [ :change_password ]
+
+ def login
+ render :inline => "ran action"
+ end
+
+ def change_password
+ render :inline => "ran action"
+ end
+
+ protected
+ def find_user
+ @ran_filter ||= []
+ @ran_filter << "find_user"
+ end
+ end
+
+ class ConditionalParentOfConditionalSkippingController < ConditionalFilterController
+ before_filter :conditional_in_parent, :only => [:show, :another_action]
+ after_filter :conditional_in_parent, :only => [:show, :another_action]
+
+ private
+
+ def conditional_in_parent
+ @ran_filter ||= []
+ @ran_filter << 'conditional_in_parent'
+ end
+ end
+
+ class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
+ skip_before_filter :conditional_in_parent, :only => :another_action
+ skip_after_filter :conditional_in_parent, :only => :another_action
+ end
+
+ class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController
+ skip_before_filter :conditional_in_parent, :only => :show
+ end
+
+ class ProcController < PrependingController
+ before_filter(proc { |c| c.assigns["ran_proc_filter"] = true })
+ end
+
+ class ImplicitProcController < PrependingController
+ before_filter { |c| c.assigns["ran_proc_filter"] = true }
+ end
+
+ class AuditFilter
+ def self.filter(controller)
+ controller.assigns["was_audited"] = true
+ end
+ end
+
+ class AroundFilter
+ def before(controller)
+ @execution_log = "before"
+ controller.class.execution_log << " before aroundfilter " if controller.respond_to? :execution_log
+ controller.assigns["before_ran"] = true
+ end
+
+ def after(controller)
+ controller.assigns["execution_log"] = @execution_log + " and after"
+ controller.assigns["after_ran"] = true
+ controller.class.execution_log << " after aroundfilter " if controller.respond_to? :execution_log
+ end
+ end
+
+ class AppendedAroundFilter
+ def before(controller)
+ controller.class.execution_log << " before appended aroundfilter "
+ end
+
+ def after(controller)
+ controller.class.execution_log << " after appended aroundfilter "
+ end
+ end
+
+ class AuditController < ActionController::Base
+ before_filter(AuditFilter)
+
+ def show
+ render_text "hello"
+ end
+ end
+
+ class AroundFilterController < PrependingController
+ around_filter AroundFilter.new
+ end
+
+ class MixedFilterController < PrependingController
+ cattr_accessor :execution_log
+
+ def initialize
+ @@execution_log = ""
+ end
+
+ before_filter { |c| c.class.execution_log << " before procfilter " }
+ prepend_around_filter AroundFilter.new
+
+ after_filter { |c| c.class.execution_log << " after procfilter " }
+ append_around_filter AppendedAroundFilter.new
+ end
+
+ class MixedSpecializationController < ActionController::Base
+ class OutOfOrder < StandardError; end
+
+ before_filter :first
+ before_filter :second, :only => :foo
+
+ def foo
+ render_text 'foo'
+ end
+
+ def bar
+ render_text 'bar'
+ end
+
+ protected
+ def first
+ @first = true
+ end
+
+ def second
+ raise OutOfOrder unless @first
+ end
+ end
+
+ class DynamicDispatchController < ActionController::Base
+ before_filter :choose
+
+ %w(foo bar baz).each do |action|
+ define_method(action) { render :text => action }
+ end
+
+ private
+ def choose
+ self.action_name = params[:choose]
+ end
+ end
+
+ def test_added_filter_to_inheritance_graph
+ assert_equal [ :ensure_login ], TestController.before_filters
+ end
+
+ def test_base_class_in_isolation
+ assert_equal [ ], ActionController::Base.before_filters
+ end
+
+ def test_prepending_filter
+ assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters
+ end
+
+ def test_running_filters
+ assert_equal %w( wonderful_life ensure_login ), test_process(PrependingController).template.assigns["ran_filter"]
+ end
+
+ def test_running_filters_with_proc
+ assert test_process(ProcController).template.assigns["ran_proc_filter"]
+ end
+
+ def test_running_filters_with_implicit_proc
+ assert test_process(ImplicitProcController).template.assigns["ran_proc_filter"]
+ end
+
+ def test_running_filters_with_class
+ assert test_process(AuditController).template.assigns["was_audited"]
+ end
+
+ def test_running_anomolous_yet_valid_condition_filters
+ response = test_process(AnomolousYetValidConditionController)
+ assert_equal %w( ensure_login ), response.template.assigns["ran_filter"]
+ assert response.template.assigns["ran_class_filter"]
+ assert response.template.assigns["ran_proc_filter1"]
+ assert response.template.assigns["ran_proc_filter2"]
+
+ response = test_process(AnomolousYetValidConditionController, "show_without_filter")
+ assert_equal nil, response.template.assigns["ran_filter"]
+ assert !response.template.assigns["ran_class_filter"]
+ assert !response.template.assigns["ran_proc_filter1"]
+ assert !response.template.assigns["ran_proc_filter2"]
+ end
+
+ def test_running_collection_condition_filters
+ assert_equal %w( ensure_login ), test_process(ConditionalCollectionFilterController).template.assigns["ran_filter"]
+ assert_equal nil, test_process(ConditionalCollectionFilterController, "show_without_filter").template.assigns["ran_filter"]
+ assert_equal nil, test_process(ConditionalCollectionFilterController, "another_action").template.assigns["ran_filter"]
+ end
+
+ def test_running_only_condition_filters
+ assert_equal %w( ensure_login ), test_process(OnlyConditionSymController).template.assigns["ran_filter"]
+ assert_equal nil, test_process(OnlyConditionSymController, "show_without_filter").template.assigns["ran_filter"]
+
+ assert test_process(OnlyConditionProcController).template.assigns["ran_proc_filter"]
+ assert !test_process(OnlyConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
+
+ assert test_process(OnlyConditionClassController).template.assigns["ran_class_filter"]
+ assert !test_process(OnlyConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
+ end
+
+ def test_running_except_condition_filters
+ assert_equal %w( ensure_login ), test_process(ExceptConditionSymController).template.assigns["ran_filter"]
+ assert_equal nil, test_process(ExceptConditionSymController, "show_without_filter").template.assigns["ran_filter"]
+
+ assert test_process(ExceptConditionProcController).template.assigns["ran_proc_filter"]
+ assert !test_process(ExceptConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"]
+
+ assert test_process(ExceptConditionClassController).template.assigns["ran_class_filter"]
+ assert !test_process(ExceptConditionClassController, "show_without_filter").template.assigns["ran_class_filter"]
+ end
+
+ def test_running_before_and_after_condition_filters
+ assert_equal %w( ensure_login clean_up_tmp), test_process(BeforeAndAfterConditionController).template.assigns["ran_filter"]
+ assert_equal nil, test_process(BeforeAndAfterConditionController, "show_without_filter").template.assigns["ran_filter"]
+ end
+
+ def test_bad_filter
+ bad_filter_controller = Class.new(ActionController::Base)
+ assert_raises(ActionController::ActionControllerError) do
+ bad_filter_controller.before_filter 2
+ end
+ end
+
+ def test_around_filter
+ controller = test_process(AroundFilterController)
+ assert controller.template.assigns["before_ran"]
+ assert controller.template.assigns["after_ran"]
+ end
+
+ def test_having_properties_in_around_filter
+ controller = test_process(AroundFilterController)
+ assert_equal "before and after", controller.template.assigns["execution_log"]
+ end
+
+ def test_prepending_and_appending_around_filter
+ controller = test_process(MixedFilterController)
+ assert_equal " before aroundfilter before procfilter before appended aroundfilter " +
+ " after appended aroundfilter after aroundfilter after procfilter ",
+ MixedFilterController.execution_log
+ end
+
+ def test_rendering_breaks_filtering_chain
+ response = test_process(RenderingController)
+ assert_equal "something else", response.body
+ assert !response.template.assigns["ran_action"]
+ end
+
+ def test_filters_with_mixed_specialization_run_in_order
+ assert_nothing_raised do
+ response = test_process(MixedSpecializationController, 'bar')
+ assert_equal 'bar', response.body
+ end
+
+ assert_nothing_raised do
+ response = test_process(MixedSpecializationController, 'foo')
+ assert_equal 'foo', response.body
+ end
+ end
+
+ def test_dynamic_dispatch
+ %w(foo bar baz).each do |action|
+ request = ActionController::TestRequest.new
+ request.query_parameters[:choose] = action
+ response = DynamicDispatchController.process(request, ActionController::TestResponse.new)
+ assert_equal action, response.body
+ end
+ end
+
+ def test_conditional_skipping_of_filters
+ assert_nil test_process(ConditionalSkippingController, "login").template.assigns["ran_filter"]
+ assert_equal %w( ensure_login find_user ), test_process(ConditionalSkippingController, "change_password").template.assigns["ran_filter"]
+
+ assert_nil test_process(ConditionalSkippingController, "login").template.controller.instance_variable_get("@ran_after_filter")
+ assert_equal %w( clean_up ), test_process(ConditionalSkippingController, "change_password").template.controller.instance_variable_get("@ran_after_filter")
+ end
+
+ def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional
+ assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter']
+ assert_nil test_process(ChildOfConditionalParentController, 'another_action').template.assigns['ran_filter']
+ end
+
+ def test_condition_skipping_of_filters_when_siblings_also_have_conditions
+ assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter'], "1"
+ assert_equal nil, test_process(AnotherChildOfConditionalParentController).template.assigns['ran_filter']
+ assert_equal %w( conditional_in_parent conditional_in_parent ), test_process(ChildOfConditionalParentController).template.assigns['ran_filter']
+ end
+
+ def test_changing_the_requirements
+ assert_equal nil, test_process(ChangingTheRequirementsController, "go_wild").template.assigns['ran_filter']
+ end
+
+ private
+ def test_process(controller, action = "show")
+ request = ActionController::TestRequest.new
+ request.action = action
+ controller.process(request, ActionController::TestResponse.new)
+ end
+end
+
+
+
+class PostsController < ActionController::Base
+ def rescue_action(e); raise e; end
+
+ module AroundExceptions
+ class Error < StandardError ; end
+ class Before < Error ; end
+ class After < Error ; end
+ end
+ include AroundExceptions
+
+ class DefaultFilter
+ include AroundExceptions
+ end
+
+ module_eval %w(raises_before raises_after raises_both no_raise no_filter).map { |action| "def #{action}; default_action end" }.join("\n")
+
+ private
+ def default_action
+ render :inline => "#{action_name} called"
+ end
+end
+
+class ControllerWithSymbolAsFilter < PostsController
+ around_filter :raise_before, :only => :raises_before
+ around_filter :raise_after, :only => :raises_after
+ around_filter :without_exception, :only => :no_raise
+
+ private
+ def raise_before
+ raise Before
+ yield
+ end
+
+ def raise_after
+ yield
+ raise After
+ end
+
+ def without_exception
+ # Do stuff...
+ 1 + 1
+
+ yield
+
+ # Do stuff...
+ 1 + 1
+ end
+end
+
+class ControllerWithFilterClass < PostsController
+ class YieldingFilter < DefaultFilter
+ def self.filter(controller)
+ yield
+ raise After
+ end
+ end
+
+ around_filter YieldingFilter, :only => :raises_after
+end
+
+class ControllerWithFilterInstance < PostsController
+ class YieldingFilter < DefaultFilter
+ def filter(controller)
+ yield
+ raise After
+ end
+ end
+
+ around_filter YieldingFilter.new, :only => :raises_after
+end
+
+class ControllerWithFilterMethod < PostsController
+ class YieldingFilter < DefaultFilter
+ def filter(controller)
+ yield
+ raise After
+ end
+ end
+
+ around_filter YieldingFilter.new.method(:filter), :only => :raises_after
+end
+
+class ControllerWithProcFilter < PostsController
+ around_filter(:only => :no_raise) do |c,b|
+ c.assigns['before'] = true
+ b.call
+ c.assigns['after'] = true
+ end
+end
+
+class ControllerWithWrongFilterType < PostsController
+ around_filter lambda { yield }, :only => :no_raise
+end
+
+class ControllerWithNestedFilters < ControllerWithSymbolAsFilter
+ around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both
+end
+
+class ControllerWithAllTypesOfFilters < PostsController
+ before_filter :before
+ around_filter :around
+ after_filter :after
+ around_filter :around_again
+
+ private
+ def before
+ @ran_filter ||= []
+ @ran_filter << 'before'
+ end
+
+ def around
+ @ran_filter << 'around (before yield)'
+ yield
+ @ran_filter << 'around (after yield)'
+ end
+
+ def after
+ @ran_filter << 'after'
+ end
+
+ def around_again
+ @ran_filter << 'around_again (before yield)'
+ yield
+ @ran_filter << 'around_again (after yield)'
+ end
+end
+
+class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters
+ skip_filter :around_again
+ skip_filter :after
+end
+
+class YieldingAroundFiltersTest < Test::Unit::TestCase
+ include PostsController::AroundExceptions
+
+ def test_filters_registering
+ assert_equal 1, ControllerWithFilterMethod.filter_chain.size
+ assert_equal 1, ControllerWithFilterClass.filter_chain.size
+ assert_equal 1, ControllerWithFilterInstance.filter_chain.size
+ assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
+ assert_equal 1, ControllerWithWrongFilterType.filter_chain.size
+ assert_equal 6, ControllerWithNestedFilters.filter_chain.size
+ assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
+ end
+
+ def test_wrong_filter_type
+ assert_raise(ActionController::ActionControllerError) do
+ test_process(ControllerWithWrongFilterType,'no_raise')
+ end
+ end
+
+ def test_base
+ controller = PostsController
+ assert_nothing_raised { test_process(controller,'no_raise') }
+ assert_nothing_raised { test_process(controller,'raises_before') }
+ assert_nothing_raised { test_process(controller,'raises_after') }
+ assert_nothing_raised { test_process(controller,'no_filter') }
+ end
+
+ def test_with_symbol
+ controller = ControllerWithSymbolAsFilter
+ assert_nothing_raised { test_process(controller,'no_raise') }
+ assert_raise(Before) { test_process(controller,'raises_before') }
+ assert_raise(After) { test_process(controller,'raises_after') }
+ assert_nothing_raised { test_process(controller,'no_raise') }
+ end
+
+ def test_with_class
+ controller = ControllerWithFilterClass
+ assert_nothing_raised { test_process(controller,'no_raise') }
+ assert_raise(After) { test_process(controller,'raises_after') }
+ end
+
+ def test_with_instance
+ controller = ControllerWithFilterInstance
+ assert_nothing_raised { test_process(controller,'no_raise') }
+ assert_raise(After) { test_process(controller,'raises_after') }
+ end
+
+ def test_with_method
+ controller = ControllerWithFilterMethod
+ assert_nothing_raised { test_process(controller,'no_raise') }
+ assert_raise(After) { test_process(controller,'raises_after') }
+ end
+
+ def test_with_proc
+ controller = test_process(ControllerWithProcFilter,'no_raise')
+ assert controller.template.assigns['before']
+ assert controller.template.assigns['after']
+ end
+
+ def test_nested_filters
+ controller = ControllerWithNestedFilters
+ assert_nothing_raised do
+ begin
+ test_process(controller,'raises_both')
+ rescue Before, After
+ end
+ end
+ assert_raise Before do
+ begin
+ test_process(controller,'raises_both')
+ rescue After
+ end
+ end
+ end
+
+ def test_filter_order_with_all_filter_types
+ controller = test_process(ControllerWithAllTypesOfFilters,'no_raise')
+ assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) around (after yield) after',controller.template.assigns['ran_filter'].join(' ')
+ end
+
+ def test_filter_order_with_skip_filter_method
+ controller = test_process(ControllerWithTwoLessFilters,'no_raise')
+ assert_equal 'before around (before yield) around (after yield)',controller.template.assigns['ran_filter'].join(' ')
+ end
+
+ def test_first_filter_in_multiple_before_filter_chain_halts
+ controller = ::FilterTest::TestMultipleFiltersController.new
+ response = test_process(controller, 'fail_1')
+ assert_equal '', response.body
+ assert_equal 1, controller.instance_variable_get(:@try)
+ assert controller.instance_variable_get(:@before_filter_chain_aborted)
+ end
+
+ def test_second_filter_in_multiple_before_filter_chain_halts
+ controller = ::FilterTest::TestMultipleFiltersController.new
+ response = test_process(controller, 'fail_2')
+ assert_equal '', response.body
+ assert_equal 2, controller.instance_variable_get(:@try)
+ assert controller.instance_variable_get(:@before_filter_chain_aborted)
+ end
+
+ def test_last_filter_in_multiple_before_filter_chain_halts
+ controller = ::FilterTest::TestMultipleFiltersController.new
+ response = test_process(controller, 'fail_3')
+ assert_equal '', response.body
+ assert_equal 3, controller.instance_variable_get(:@try)
+ assert controller.instance_variable_get(:@before_filter_chain_aborted)
+ end
+
+ protected
+ def test_process(controller, action = "show")
+ request = ActionController::TestRequest.new
+ request.action = action
+ controller.process(request, ActionController::TestResponse.new)
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/flash_test.rb b/tracks/vendor/rails/actionpack/test/controller/flash_test.rb
new file mode 100644
index 00000000..d12ced85
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/flash_test.rb
@@ -0,0 +1,102 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class FlashTest < Test::Unit::TestCase
+ class TestController < ActionController::Base
+ def set_flash
+ flash["that"] = "hello"
+ render :inline => "hello"
+ end
+
+ def set_flash_now
+ flash.now["that"] = "hello"
+ flash.now["foo"] ||= "bar"
+ flash.now["foo"] ||= "err"
+ @flashy = flash.now["that"]
+ @flash_copy = {}.update flash
+ render :inline => "hello"
+ end
+
+ def attempt_to_use_flash_now
+ @flash_copy = {}.update flash
+ @flashy = flash["that"]
+ render :inline => "hello"
+ end
+
+ def use_flash
+ @flash_copy = {}.update flash
+ @flashy = flash["that"]
+ render :inline => "hello"
+ end
+
+ def use_flash_and_keep_it
+ @flash_copy = {}.update flash
+ @flashy = flash["that"]
+ silence_warnings { keep_flash }
+ render :inline => "hello"
+ end
+
+ def use_flash_after_reset_session
+ flash["that"] = "hello"
+ @flashy_that = flash["that"]
+ reset_session
+ @flashy_that_reset = flash["that"]
+ flash["this"] = "good-bye"
+ @flashy_this = flash["this"]
+ render :inline => "hello"
+ end
+
+ def rescue_action(e)
+ raise unless ActionController::MissingTemplate === e
+ end
+ end
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = TestController.new
+ end
+
+ def test_flash
+ get :set_flash
+
+ get :use_flash
+ assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
+ assert_equal "hello", @response.template.assigns["flashy"]
+
+ get :use_flash
+ assert_nil @response.template.assigns["flash_copy"]["that"], "On second flash"
+ end
+
+ def test_keep_flash
+ get :set_flash
+
+ assert_deprecated(/keep_flash/) { get :use_flash_and_keep_it }
+ assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
+ assert_equal "hello", @response.template.assigns["flashy"]
+
+ get :use_flash
+ assert_equal "hello", @response.template.assigns["flash_copy"]["that"], "On second flash"
+
+ get :use_flash
+ assert_nil @response.template.assigns["flash_copy"]["that"], "On third flash"
+ end
+
+ def test_flash_now
+ get :set_flash_now
+ assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
+ assert_equal "bar" , @response.template.assigns["flash_copy"]["foo"]
+ assert_equal "hello", @response.template.assigns["flashy"]
+
+ get :attempt_to_use_flash_now
+ assert_nil @response.template.assigns["flash_copy"]["that"]
+ assert_nil @response.template.assigns["flash_copy"]["foo"]
+ assert_nil @response.template.assigns["flashy"]
+ end
+
+ def test_flash_after_reset_session
+ get :use_flash_after_reset_session
+ assert_equal "hello", @response.template.assigns["flashy_that"]
+ assert_equal "good-bye", @response.template.assigns["flashy_this"]
+ assert_nil @response.template.assigns["flashy_that_reset"]
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/fragment_store_setting_test.rb b/tracks/vendor/rails/actionpack/test/controller/fragment_store_setting_test.rb
new file mode 100644
index 00000000..cb872f65
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/fragment_store_setting_test.rb
@@ -0,0 +1,45 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+MemCache = Struct.new(:MemCache, :address) unless Object.const_defined?(:MemCache)
+
+class FragmentCacheStoreSettingTest < Test::Unit::TestCase
+ def teardown
+ ActionController::Base.fragment_cache_store = ActionController::Caching::Fragments::MemoryStore.new
+ end
+
+ def test_file_fragment_cache_store
+ ActionController::Base.fragment_cache_store = :file_store, "/path/to/cache/directory"
+ assert_kind_of(
+ ActionController::Caching::Fragments::FileStore,
+ ActionController::Base.fragment_cache_store
+ )
+ assert_equal "/path/to/cache/directory", ActionController::Base.fragment_cache_store.cache_path
+ end
+
+ def test_drb_fragment_cache_store
+ ActionController::Base.fragment_cache_store = :drb_store, "druby://localhost:9192"
+ assert_kind_of(
+ ActionController::Caching::Fragments::DRbStore,
+ ActionController::Base.fragment_cache_store
+ )
+ assert_equal "druby://localhost:9192", ActionController::Base.fragment_cache_store.address
+ end
+
+ def test_mem_cache_fragment_cache_store
+ ActionController::Base.fragment_cache_store = :mem_cache_store, "localhost"
+ assert_kind_of(
+ ActionController::Caching::Fragments::MemCacheStore,
+ ActionController::Base.fragment_cache_store
+ )
+ assert_equal %w(localhost), ActionController::Base.fragment_cache_store.addresses
+ end
+
+ def test_object_assigned_fragment_cache_store
+ ActionController::Base.fragment_cache_store = ActionController::Caching::Fragments::FileStore.new("/path/to/cache/directory")
+ assert_kind_of(
+ ActionController::Caching::Fragments::FileStore,
+ ActionController::Base.fragment_cache_store
+ )
+ assert_equal "/path/to/cache/directory", ActionController::Base.fragment_cache_store.cache_path
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/helper_test.rb b/tracks/vendor/rails/actionpack/test/controller/helper_test.rb
new file mode 100644
index 00000000..98c8f7e7
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/helper_test.rb
@@ -0,0 +1,187 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class TestController < ActionController::Base
+ attr_accessor :delegate_attr
+ def delegate_method() end
+ def rescue_action(e) raise end
+end
+
+module Fun
+ class GamesController < ActionController::Base
+ def render_hello_world
+ render :inline => "hello: <%= stratego %>"
+ end
+
+ def rescue_action(e) raise end
+ end
+
+ class PDFController < ActionController::Base
+ def test
+ render :inline => "test: <%= foobar %>"
+ end
+
+ def rescue_action(e) raise end
+ end
+end
+
+module LocalAbcHelper
+ def a() end
+ def b() end
+ def c() end
+end
+
+class HelperTest < Test::Unit::TestCase
+ def setup
+ # Increment symbol counter.
+ @symbol = (@@counter ||= 'A0').succ!.dup
+
+ # Generate new controller class.
+ controller_class_name = "Helper#{@symbol}Controller"
+ eval("class #{controller_class_name} < TestController; end")
+ @controller_class = self.class.const_get(controller_class_name)
+
+ # Generate new template class and assign to controller.
+ template_class_name = "Test#{@symbol}View"
+ eval("class #{template_class_name} < ActionView::Base; end")
+ @template_class = self.class.const_get(template_class_name)
+ @controller_class.template_class = @template_class
+
+ # Set default test helper.
+ self.test_helper = LocalAbcHelper
+ end
+
+ def teardown
+ # Reset template class.
+ #ActionController::Base.template_class = ActionView::Base
+ end
+
+
+ def test_deprecated_helper
+ assert_equal expected_helper_methods, missing_methods
+ assert_nothing_raised { @controller_class.helper TestHelper }
+ assert_equal [], missing_methods
+ end
+
+ def test_declare_helper
+ require 'abc_helper'
+ self.test_helper = AbcHelper
+ assert_equal expected_helper_methods, missing_methods
+ assert_nothing_raised { @controller_class.helper :abc }
+ assert_equal [], missing_methods
+ end
+
+ def test_declare_missing_helper
+ assert_equal expected_helper_methods, missing_methods
+ assert_raise(MissingSourceFile) { @controller_class.helper :missing }
+ end
+
+ def test_declare_missing_file_from_helper
+ require 'broken_helper'
+ rescue LoadError => e
+ assert_nil(/\bbroken_helper\b/.match(e.to_s)[1])
+ end
+
+ def test_helper_block
+ assert_nothing_raised {
+ @controller_class.helper { def block_helper_method; end }
+ }
+ assert master_helper_methods.include?('block_helper_method')
+ end
+
+ def test_helper_block_include
+ assert_equal expected_helper_methods, missing_methods
+ assert_nothing_raised {
+ @controller_class.helper { include TestHelper }
+ }
+ assert [], missing_methods
+ end
+
+ def test_helper_method
+ assert_nothing_raised { @controller_class.helper_method :delegate_method }
+ assert master_helper_methods.include?('delegate_method')
+ end
+
+ def test_helper_attr
+ assert_nothing_raised { @controller_class.helper_attr :delegate_attr }
+ assert master_helper_methods.include?('delegate_attr')
+ assert master_helper_methods.include?('delegate_attr=')
+ end
+
+ def test_helper_for_nested_controller
+ request = ActionController::TestRequest.new
+ response = ActionController::TestResponse.new
+ request.action = 'render_hello_world'
+
+ assert_equal 'hello: Iz guuut!', Fun::GamesController.process(request, response).body
+ end
+
+ def test_helper_for_acronym_controller
+ request = ActionController::TestRequest.new
+ response = ActionController::TestResponse.new
+ request.action = 'test'
+
+ assert_equal 'test: baz', Fun::PDFController.process(request, response).body
+ end
+
+ private
+ def expected_helper_methods
+ TestHelper.instance_methods
+ end
+
+ def master_helper_methods
+ @controller_class.master_helper_module.instance_methods
+ end
+
+ def missing_methods
+ expected_helper_methods - master_helper_methods
+ end
+
+ def test_helper=(helper_module)
+ silence_warnings { self.class.const_set('TestHelper', helper_module) }
+ end
+end
+
+
+class IsolatedHelpersTest < Test::Unit::TestCase
+ class A < ActionController::Base
+ def index
+ render :inline => '<%= shout %>'
+ end
+
+ def rescue_action(e) raise end
+ end
+
+ class B < A
+ helper { def shout; 'B' end }
+
+ def index
+ render :inline => '<%= shout %>'
+ end
+ end
+
+ class C < A
+ helper { def shout; 'C' end }
+
+ def index
+ render :inline => '<%= shout %>'
+ end
+ end
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @request.action = 'index'
+ end
+
+ def test_helper_in_a
+ assert_raise(NameError) { A.process(@request, @response) }
+ end
+
+ def test_helper_in_b
+ assert_equal 'B', B.process(@request, @response).body
+ end
+
+ def test_helper_in_c
+ assert_equal 'C', C.process(@request, @response).body
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/integration_test.rb b/tracks/vendor/rails/actionpack/test/controller/integration_test.rb
new file mode 100644
index 00000000..665c5901
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/integration_test.rb
@@ -0,0 +1,154 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+$:.unshift File.dirname(__FILE__) + '/../../../railties/lib'
+require 'action_controller/integration'
+
+begin # rescue LoadError
+require 'mocha'
+require 'stubba'
+
+# Stub process for testing.
+module ActionController
+ module Integration
+ class Session
+ def process
+ end
+
+ def generic_url_rewriter
+ end
+ end
+ end
+end
+
+class SessionTest < Test::Unit::TestCase
+ def setup
+ @session = ActionController::Integration::Session.new
+ end
+ def test_https_bang_works_and_sets_truth_by_default
+ assert !@session.https?
+ @session.https!
+ assert @session.https?
+ @session.https! false
+ assert !@session.https?
+ end
+
+ def test_host!
+ assert_not_equal "glu.ttono.us", @session.host
+ @session.host! "rubyonrails.com"
+ assert_equal "rubyonrails.com", @session.host
+ end
+
+ def test_follow_redirect_raises_when_no_redirect
+ @session.stubs(:redirect?).returns(false)
+ assert_raise(RuntimeError) { @session.follow_redirect! }
+ end
+
+ def test_follow_redirect_calls_get_and_returns_status
+ @session.stubs(:redirect?).returns(true)
+ @session.stubs(:headers).returns({"location" => ["www.google.com"]})
+ @session.stubs(:status).returns(200)
+ @session.expects(:get)
+ assert_equal 200, @session.follow_redirect!
+ end
+
+ def test_get_via_redirect
+ path = "/somepath"; args = {:id => '1'}
+
+ @session.expects(:get).with(path,args)
+
+ redirects = [true, true, false]
+ @session.stubs(:redirect?).returns(lambda { redirects.shift })
+ @session.expects(:follow_redirect!).times(2)
+
+ @session.stubs(:status).returns(200)
+ assert_equal 200, @session.get_via_redirect(path, args)
+ end
+
+ def test_post_via_redirect
+ path = "/somepath"; args = {:id => '1'}
+
+ @session.expects(:post).with(path,args)
+
+ redirects = [true, true, false]
+ @session.stubs(:redirect?).returns(lambda { redirects.shift })
+ @session.expects(:follow_redirect!).times(2)
+
+ @session.stubs(:status).returns(200)
+ assert_equal 200, @session.post_via_redirect(path, args)
+ end
+
+ def test_url_for_with_controller
+ options = {:action => 'show'}
+ mock_controller = mock()
+ mock_controller.expects(:url_for).with(options).returns('/show')
+ @session.stubs(:controller).returns(mock_controller)
+ assert_equal '/show', @session.url_for(options)
+ end
+
+ def test_url_for_without_controller
+ options = {:action => 'show'}
+ mock_rewriter = mock()
+ mock_rewriter.expects(:rewrite).with(options).returns('/show')
+ @session.stubs(:generic_url_rewriter).returns(mock_rewriter)
+ @session.stubs(:controller).returns(nil)
+ assert_equal '/show', @session.url_for(options)
+ end
+
+ def test_redirect_bool_with_status_in_300s
+ @session.stubs(:status).returns 301
+ assert @session.redirect?
+ end
+
+ def test_redirect_bool_with_status_in_200s
+ @session.stubs(:status).returns 200
+ assert !@session.redirect?
+ end
+
+ def test_get
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:get,path,params,headers)
+ @session.get(path,params,headers)
+ end
+
+ def test_post
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:post,path,params,headers)
+ @session.post(path,params,headers)
+ end
+
+ def test_put
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:put,path,params,headers)
+ @session.put(path,params,headers)
+ end
+
+ def test_delete
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:delete,path,params,headers)
+ @session.delete(path,params,headers)
+ end
+
+ def test_head
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ @session.expects(:process).with(:head,path,params,headers)
+ @session.head(path,params,headers)
+ end
+
+ def test_xml_http_request
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ headers_after_xhr = headers.merge(
+ "X-Requested-With" => "XMLHttpRequest",
+ "Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
+ )
+ @session.expects(:post).with(path,params,headers_after_xhr)
+ @session.xml_http_request(path,params,headers)
+ end
+end
+
+# TODO
+# class MockCGITest < Test::Unit::TestCase
+# end
+
+rescue LoadError
+ $stderr.puts "Skipping integration tests. `gem install mocha` and try again."
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/layout_test.rb b/tracks/vendor/rails/actionpack/test/controller/layout_test.rb
new file mode 100644
index 00000000..3e1d3d96
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/layout_test.rb
@@ -0,0 +1,176 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+# The template_root must be set on Base and not LayoutTest so that LayoutTest's inherited method has access to
+# the template_root when looking for a layout
+ActionController::Base.template_root = File.dirname(__FILE__) + '/../fixtures/layout_tests/'
+
+class LayoutTest < ActionController::Base
+ def self.controller_path; 'views' end
+end
+
+# Restore template root to be unset
+ActionController::Base.template_root = nil
+
+class ProductController < LayoutTest
+end
+
+class ItemController < LayoutTest
+end
+
+class ThirdPartyTemplateLibraryController < LayoutTest
+end
+
+module ControllerNameSpace
+end
+
+class ControllerNameSpace::NestedController < LayoutTest
+end
+
+class MabView
+ def initialize(view)
+ end
+
+ def render(text, locals = {})
+ text
+ end
+end
+
+ActionView::Base::register_template_handler :mab, MabView
+
+class LayoutAutoDiscoveryTest < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_application_layout_is_default_when_no_controller_match
+ @controller = ProductController.new
+ get :hello
+ assert_equal 'layout_test.rhtml hello.rhtml', @response.body
+ end
+
+ def test_controller_name_layout_name_match
+ @controller = ItemController.new
+ get :hello
+ assert_equal 'item.rhtml hello.rhtml', @response.body
+ end
+
+ def test_third_party_template_library_auto_discovers_layout
+ @controller = ThirdPartyTemplateLibraryController.new
+ get :hello
+ assert_equal 'layouts/third_party_template_library', @controller.active_layout
+ assert_equal 'Mab', @response.body
+ end
+
+ def test_namespaced_controllers_auto_detect_layouts
+ @controller = ControllerNameSpace::NestedController.new
+ get :hello
+ assert_equal 'layouts/controller_name_space/nested', @controller.active_layout
+ assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
+ end
+end
+
+class ExemptFromLayoutTest < Test::Unit::TestCase
+ def setup
+ @controller = LayoutTest.new
+ end
+
+ def test_rjs_exempt_from_layout
+ assert @controller.send(:template_exempt_from_layout?, 'test.rjs')
+ end
+
+ def test_rhtml_and_rxml_not_exempt_from_layout
+ assert !@controller.send(:template_exempt_from_layout?, 'test.rhtml')
+ assert !@controller.send(:template_exempt_from_layout?, 'test.rxml')
+ end
+
+ def test_other_extension_not_exempt_from_layout
+ assert !@controller.send(:template_exempt_from_layout?, 'test.random')
+ end
+
+ def test_add_extension_to_exempt_from_layout
+ ['rpdf', :rpdf].each do |ext|
+ assert_nothing_raised do
+ ActionController::Base.exempt_from_layout ext
+ end
+ assert @controller.send(:template_exempt_from_layout?, "test.#{ext}")
+ end
+ end
+
+ def test_add_regexp_to_exempt_from_layout
+ ActionController::Base.exempt_from_layout /\.rdoc/
+ assert @controller.send(:template_exempt_from_layout?, 'test.rdoc')
+ end
+end
+
+
+class DefaultLayoutController < LayoutTest
+end
+
+class HasOwnLayoutController < LayoutTest
+ layout 'item'
+end
+
+class SetsLayoutInRenderController < LayoutTest
+ def hello
+ render :layout => 'third_party_template_library'
+ end
+end
+
+class RendersNoLayoutController < LayoutTest
+ def hello
+ render :layout => false
+ end
+end
+
+class LayoutSetInResponseTest < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_layout_set_when_using_default_layout
+ @controller = DefaultLayoutController.new
+ get :hello
+ assert_equal 'layouts/layout_test', @response.layout
+ end
+
+ def test_layout_set_when_set_in_controller
+ @controller = HasOwnLayoutController.new
+ get :hello
+ assert_equal 'layouts/item', @response.layout
+ end
+
+ def test_layout_set_when_using_render
+ @controller = SetsLayoutInRenderController.new
+ get :hello
+ assert_equal 'layouts/third_party_template_library', @response.layout
+ end
+
+ def test_layout_is_not_set_when_none_rendered
+ @controller = RendersNoLayoutController.new
+ get :hello
+ assert_nil @response.layout
+ end
+end
+
+
+class SetsNonExistentLayoutFile < LayoutTest
+ layout "nofile.rhtml"
+end
+
+class LayoutExceptionRaised < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_exception_raised_when_layout_file_not_found
+ @controller = SetsNonExistentLayoutFile.new
+ get :hello
+ @response.template.class.module_eval { attr_accessor :exception }
+ assert_equal ActionController::MissingTemplate, @response.template.exception.class
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/mime_responds_test.rb b/tracks/vendor/rails/actionpack/test/controller/mime_responds_test.rb
new file mode 100644
index 00000000..c08062ca
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/mime_responds_test.rb
@@ -0,0 +1,351 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class RespondToController < ActionController::Base
+ layout :set_layout
+
+ def html_xml_or_rss
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.xml { render :text => "XML" }
+ type.rss { render :text => "RSS" }
+ type.all { render :text => "Nothing" }
+ end
+ end
+
+ def js_or_html
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.js { render :text => "JS" }
+ type.all { render :text => "Nothing" }
+ end
+ end
+
+ def json_or_yaml
+ respond_to do |type|
+ type.json { render :text => "JSON" }
+ type.yaml { render :text => "YAML" }
+ end
+ end
+
+ def html_or_xml
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.xml { render :text => "XML" }
+ type.all { render :text => "Nothing" }
+ end
+ end
+
+ def just_xml
+ respond_to do |type|
+ type.xml { render :text => "XML" }
+ end
+ end
+
+ def using_defaults
+ respond_to do |type|
+ type.html
+ type.js
+ type.xml
+ end
+ end
+
+ def using_defaults_with_type_list
+ respond_to(:html, :js, :xml)
+ end
+
+ def made_for_content_type
+ respond_to do |type|
+ type.rss { render :text => "RSS" }
+ type.atom { render :text => "ATOM" }
+ type.all { render :text => "Nothing" }
+ end
+ end
+
+ def custom_type_handling
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.custom("application/crazy-xml") { render :text => "Crazy XML" }
+ type.all { render :text => "Nothing" }
+ end
+ end
+
+ def custom_constant_handling
+ Mime::Type.register("text/x-mobile", :mobile)
+
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.mobile { render :text => "Mobile" }
+ end
+
+ Mime.send :remove_const, :MOBILE
+ end
+
+ def custom_constant_handling_without_block
+ Mime::Type.register("text/x-mobile", :mobile)
+
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.mobile
+ end
+
+ Mime.send :remove_const, :MOBILE
+ end
+
+
+ def handle_any
+ respond_to do |type|
+ type.html { render :text => "HTML" }
+ type.any(:js, :xml) { render :text => "Either JS or XML" }
+ end
+ end
+
+ def all_types_with_layout
+ respond_to do |type|
+ type.html
+ type.js
+ end
+ end
+
+ def rescue_action(e)
+ raise
+ end
+
+ protected
+ def set_layout
+ if action_name == "all_types_with_layout"
+ "standard"
+ end
+ end
+end
+
+RespondToController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class MimeControllerTest < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @controller = RespondToController.new
+ @request.host = "www.example.com"
+ end
+
+ def test_html
+ @request.env["HTTP_ACCEPT"] = "text/html"
+ get :js_or_html
+ assert_equal 'HTML', @response.body
+
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+
+ get :just_xml
+ assert_response 406
+ end
+
+ def test_all
+ @request.env["HTTP_ACCEPT"] = "*/*"
+ get :js_or_html
+ assert_equal 'HTML', @response.body # js is not part of all
+
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+
+ get :just_xml
+ assert_equal 'XML', @response.body
+ end
+
+ def test_xml
+ @request.env["HTTP_ACCEPT"] = "application/xml"
+ get :html_xml_or_rss
+ assert_equal 'XML', @response.body
+ end
+
+ def test_js_or_html
+ @request.env["HTTP_ACCEPT"] = "text/javascript, text/html"
+ get :js_or_html
+ assert_equal 'JS', @response.body
+
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+
+ get :just_xml
+ assert_response 406
+ end
+
+ def test_json_or_yaml
+ get :json_or_yaml
+ assert_equal 'JSON', @response.body
+
+ get :json_or_yaml, :format => 'json'
+ assert_equal 'JSON', @response.body
+
+ get :json_or_yaml, :format => 'yaml'
+ assert_equal 'YAML', @response.body
+
+ { 'YAML' => %w(text/yaml),
+ 'JSON' => %w(application/json text/x-json)
+ }.each do |body, content_types|
+ content_types.each do |content_type|
+ @request.env['HTTP_ACCEPT'] = content_type
+ get :json_or_yaml
+ assert_equal body, @response.body
+ end
+ end
+ end
+
+ def test_js_or_anything
+ @request.env["HTTP_ACCEPT"] = "text/javascript, */*"
+ get :js_or_html
+ assert_equal 'JS', @response.body
+
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+
+ get :just_xml
+ assert_equal 'XML', @response.body
+ end
+
+ def test_using_defaults
+ @request.env["HTTP_ACCEPT"] = "*/*"
+ get :using_defaults
+ assert_equal 'Hello world!', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "text/javascript"
+ get :using_defaults
+ assert_equal '$("body").visualEffect("highlight");', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "application/xml"
+ get :using_defaults
+ assert_equal "Hello world!
\n", @response.body
+ end
+
+ def test_using_defaults_with_type_list
+ @request.env["HTTP_ACCEPT"] = "*/*"
+ get :using_defaults_with_type_list
+ assert_equal 'Hello world!', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "text/javascript"
+ get :using_defaults_with_type_list
+ assert_equal '$("body").visualEffect("highlight");', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "application/xml"
+ get :using_defaults_with_type_list
+ assert_equal "Hello world!
\n", @response.body
+ end
+
+ def test_with_content_type
+ @request.env["CONTENT_TYPE"] = "application/atom+xml"
+ get :made_for_content_type
+ assert_equal "ATOM", @response.body
+
+ @request.env["CONTENT_TYPE"] = "application/rss+xml"
+ get :made_for_content_type
+ assert_equal "RSS", @response.body
+ end
+
+ def test_synonyms
+ @request.env["HTTP_ACCEPT"] = "application/javascript"
+ get :js_or_html
+ assert_equal 'JS', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "application/x-xml"
+ get :html_xml_or_rss
+ assert_equal "XML", @response.body
+ end
+
+ def test_custom_types
+ @request.env["HTTP_ACCEPT"] = "application/crazy-xml"
+ get :custom_type_handling
+ assert_equal 'Crazy XML', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "text/html"
+ get :custom_type_handling
+ assert_equal 'HTML', @response.body
+ end
+
+ def test_xhtml_alias
+ @request.env["HTTP_ACCEPT"] = "application/xhtml+xml,application/xml"
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+ end
+
+ def test_firefox_simulation
+ @request.env["HTTP_ACCEPT"] = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
+ get :html_or_xml
+ assert_equal 'HTML', @response.body
+ end
+
+ def test_handle_any
+ @request.env["HTTP_ACCEPT"] = "*/*"
+ get :handle_any
+ assert_equal 'HTML', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "text/javascript"
+ get :handle_any
+ assert_equal 'Either JS or XML', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "text/xml"
+ get :handle_any
+ assert_equal 'Either JS or XML', @response.body
+ end
+
+ def test_all_types_with_layout
+ @request.env["HTTP_ACCEPT"] = "text/javascript"
+ get :all_types_with_layout
+ assert_equal 'RJS for all_types_with_layout', @response.body
+
+ @request.env["HTTP_ACCEPT"] = "text/html"
+ get :all_types_with_layout
+ assert_equal 'HTML for all_types_with_layout', @response.body
+ end
+
+ def test_xhr
+ xhr :get, :js_or_html
+ assert_equal 'JS', @response.body
+
+ xhr :get, :using_defaults
+ assert_equal '$("body").visualEffect("highlight");', @response.body
+ end
+
+ def test_custom_constant
+ get :custom_constant_handling, :format => "mobile"
+ assert_equal "Mobile", @response.body
+ end
+
+ def custom_constant_handling_without_block
+
+ assert_raised(ActionController::RenderError) do
+ get :custom_constant_handling, :format => "mobile"
+ end
+ end
+
+ def test_forced_format
+ get :html_xml_or_rss
+ assert_equal "HTML", @response.body
+
+ get :html_xml_or_rss, :format => "html"
+ assert_equal "HTML", @response.body
+
+ get :html_xml_or_rss, :format => "xml"
+ assert_equal "XML", @response.body
+
+ get :html_xml_or_rss, :format => "rss"
+ assert_equal "RSS", @response.body
+ end
+
+ def test_render_action_for_html
+ @controller.instance_eval do
+ def render(*args)
+ unless args.empty?
+ @action = args.first[:action]
+ end
+ response.body = @action
+ end
+ end
+
+ get :using_defaults
+ assert_equal "using_defaults", @response.body
+
+ get :using_defaults, :format => "xml"
+ assert_equal "using_defaults.rxml", @response.body
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/mime_type_test.rb b/tracks/vendor/rails/actionpack/test/controller/mime_type_test.rb
new file mode 100644
index 00000000..65acbbf3
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/mime_type_test.rb
@@ -0,0 +1,33 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class MimeTypeTest < Test::Unit::TestCase
+ Mime::PNG = Mime::Type.new("image/png")
+ Mime::PLAIN = Mime::Type.new("text/plain")
+
+ def test_parse_single
+ Mime::LOOKUP.keys.each do |mime_type|
+ assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type)
+ end
+ end
+
+ def test_parse_without_q
+ accept = "text/xml,application/xhtml+xml,text/yaml,application/xml,text/html,image/png,text/plain,*/*"
+ expect = [Mime::HTML, Mime::XML, Mime::YAML, Mime::PNG, Mime::PLAIN, Mime::ALL]
+ assert_equal expect, Mime::Type.parse(accept)
+ end
+
+ def test_parse_with_q
+ accept = "text/xml,application/xhtml+xml,text/yaml; q=0.3,application/xml,text/html; q=0.8,image/png,text/plain; q=0.5,*/*; q=0.2"
+ expect = [Mime::HTML, Mime::XML, Mime::PNG, Mime::PLAIN, Mime::YAML, Mime::ALL]
+ assert_equal expect, Mime::Type.parse(accept)
+ end
+
+ def test_custom_type
+ Mime::Type.register("image/gif", :gif)
+ assert_nothing_raised do
+ Mime::GIF
+ assert_equal Mime::GIF, Mime::SET.last
+ end
+ Mime.send :remove_const, :GIF
+ end
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/controller/new_render_test.rb b/tracks/vendor/rails/actionpack/test/controller/new_render_test.rb
new file mode 100644
index 00000000..6e1ec341
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/new_render_test.rb
@@ -0,0 +1,738 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+silence_warnings { Customer = Struct.new("Customer", :name) }
+
+module Fun
+ class GamesController < ActionController::Base
+ def hello_world
+ end
+ end
+end
+
+module NewRenderTestHelper
+ def rjs_helper_method_from_module
+ page.visual_effect :highlight
+ end
+end
+
+class NewRenderTestController < ActionController::Base
+ layout :determine_layout
+
+ def self.controller_name; "test"; end
+ def self.controller_path; "test"; end
+
+ def hello_world
+ end
+
+ def render_hello_world
+ render :template => "test/hello_world"
+ end
+
+ def render_hello_world_from_variable
+ @person = "david"
+ render :text => "hello #{@person}"
+ end
+
+ def render_action_hello_world
+ render :action => "hello_world"
+ end
+
+ def render_action_hello_world_as_symbol
+ render :action => :hello_world
+ end
+
+ def render_text_hello_world
+ render :text => "hello world"
+ end
+
+ def render_text_hello_world_with_layout
+ @variable_for_layout = ", I'm here!"
+ render :text => "hello world", :layout => true
+ end
+
+ def hello_world_with_layout_false
+ render :layout => false
+ end
+
+ def render_custom_code
+ render :text => "hello world", :status => "404 Moved"
+ end
+
+ def render_file_with_instance_variables
+ @secret = 'in the sauce'
+ path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.rhtml')
+ render :file => path
+ end
+
+ def render_file_with_locals
+ path = File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.rhtml')
+ render :file => path, :locals => {:secret => 'in the sauce'}
+ end
+
+ def render_file_not_using_full_path
+ @secret = 'in the sauce'
+ render :file => 'test/render_file_with_ivar', :use_full_path => true
+ end
+
+ def render_file_not_using_full_path_with_relative_path
+ @secret = 'in the sauce'
+ render :file => 'test/../test/render_file_with_ivar', :use_full_path => true
+ end
+
+ def render_file_not_using_full_path_with_dot_in_path
+ @secret = 'in the sauce'
+ render :file => 'test/dot.directory/render_file_with_ivar', :use_full_path => true
+ end
+
+ def render_xml_hello
+ @name = "David"
+ render :template => "test/hello"
+ end
+
+ def greeting
+ # let's just rely on the template
+ end
+
+ def layout_test
+ render :action => "hello_world"
+ end
+
+ def layout_test_with_different_layout
+ render :action => "hello_world", :layout => "standard"
+ end
+
+ def rendering_without_layout
+ render :action => "hello_world", :layout => false
+ end
+
+ def layout_overriding_layout
+ render :action => "hello_world", :layout => "standard"
+ end
+
+ def rendering_nothing_on_layout
+ render :nothing => true
+ end
+
+ def builder_layout_test
+ render :action => "hello"
+ end
+
+ def partials_list
+ @test_unchanged = 'hello'
+ @customers = [ Customer.new("david"), Customer.new("mary") ]
+ render :action => "list"
+ end
+
+ def partial_only
+ render :partial => true
+ end
+
+ def partial_only_with_layout
+ render :partial => "partial_only", :layout => true
+ end
+
+ def partial_with_locals
+ render :partial => "customer", :locals => { :customer => Customer.new("david") }
+ end
+
+ def partial_collection
+ render :partial => "customer", :collection => [ Customer.new("david"), Customer.new("mary") ]
+ end
+
+ def partial_collection_with_locals
+ render :partial => "customer_greeting", :collection => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" }
+ end
+
+ def empty_partial_collection
+ render :partial => "customer", :collection => []
+ end
+
+ def partial_with_hash_object
+ render :partial => "hash_object", :object => {:first_name => "Sam"}
+ end
+
+ def partial_with_implicit_local_assignment
+ @customer = Customer.new("Marcel")
+ render :partial => "customer"
+ end
+
+ def hello_in_a_string
+ @customers = [ Customer.new("david"), Customer.new("mary") ]
+ render :text => "How's there? #{render_to_string("test/list")}"
+ end
+
+ def render_to_string_with_assigns
+ @before = "i'm before the render"
+ render_to_string :text => "foo"
+ @after = "i'm after the render"
+ render :action => "test/hello_world"
+ end
+
+ def render_to_string_with_exception
+ render_to_string :file => "exception that will not be caught - this will certainly not work", :use_full_path => true
+ end
+
+ def render_to_string_with_caught_exception
+ @before = "i'm before the render"
+ begin
+ render_to_string :file => "exception that will be caught- hope my future instance vars still work!", :use_full_path => true
+ rescue
+ end
+ @after = "i'm after the render"
+ render :action => "test/hello_world"
+ end
+
+ def accessing_params_in_template
+ render :inline => "Hello: <%= params[:name] %>"
+ end
+
+ def accessing_params_in_template_with_layout
+ render :layout => nil, :inline => "Hello: <%= params[:name] %>"
+ end
+
+ def render_with_explicit_template
+ render "test/hello_world"
+ end
+
+ def double_render
+ render :text => "hello"
+ render :text => "world"
+ end
+
+ def double_redirect
+ redirect_to :action => "double_render"
+ redirect_to :action => "double_render"
+ end
+
+ def render_and_redirect
+ render :text => "hello"
+ redirect_to :action => "double_render"
+ end
+
+ def render_to_string_and_render
+ @stuff = render_to_string :text => "here is some cached stuff"
+ render :text => "Hi web users! #{@stuff}"
+ end
+
+ def rendering_with_conflicting_local_vars
+ @name = "David"
+ def @template.name() nil end
+ render :action => "potential_conflicts"
+ end
+
+ def hello_world_from_rxml_using_action
+ render :action => "hello_world.rxml"
+ end
+
+ def hello_world_from_rxml_using_template
+ render :template => "test/hello_world.rxml"
+ end
+
+ def head_with_location_header
+ head :location => "/foo"
+ end
+
+ def head_with_symbolic_status
+ head :status => params[:status].intern
+ end
+
+ def head_with_integer_status
+ head :status => params[:status].to_i
+ end
+
+ def head_with_string_status
+ head :status => params[:status]
+ end
+
+ def head_with_custom_header
+ head :x_custom_header => "something"
+ end
+
+ def head_with_status_code_first
+ head :forbidden, :x_custom_header => "something"
+ end
+
+ helper NewRenderTestHelper
+ helper do
+ def rjs_helper_method(value)
+ page.visual_effect :highlight, value
+ end
+ end
+
+ def enum_rjs_test
+ render :update do |page|
+ page.select('.product').each do |value|
+ page.rjs_helper_method_from_module
+ page.rjs_helper_method(value)
+ page.sortable(value, :url => { :action => "order" })
+ page.draggable(value)
+ end
+ end
+ end
+
+ def delete_with_js
+ @project_id = 4
+ end
+
+ def render_js_with_explicit_template
+ @project_id = 4
+ render :template => 'test/delete_with_js'
+ end
+
+ def render_js_with_explicit_action_template
+ @project_id = 4
+ render :action => 'delete_with_js'
+ end
+
+ def update_page
+ render :update do |page|
+ page.replace_html 'balance', '$37,000,000.00'
+ page.visual_effect :highlight, 'balance'
+ end
+ end
+
+ def update_page_with_instance_variables
+ @money = '$37,000,000.00'
+ @div_id = 'balance'
+ render :update do |page|
+ page.replace_html @div_id, @money
+ page.visual_effect :highlight, @div_id
+ end
+ end
+
+ def action_talk_to_layout
+ # Action template sets variable that's picked up by layout
+ end
+
+ def render_text_with_assigns
+ @hello = "world"
+ render :text => "foo"
+ end
+
+ def yield_content_for
+ render :action => "content_for", :layout => "yield"
+ end
+
+ def render_content_type_from_body
+ response.content_type = Mime::RSS
+ render :text => "hello world!"
+ end
+
+ def rescue_action(e) raise end
+
+ private
+ def determine_layout
+ case action_name
+ when "hello_world", "layout_test", "rendering_without_layout",
+ "rendering_nothing_on_layout", "render_text_hello_world",
+ "render_text_hello_world_with_layout",
+ "hello_world_with_layout_false",
+ "partial_only", "partial_only_with_layout",
+ "accessing_params_in_template",
+ "accessing_params_in_template_with_layout",
+ "render_with_explicit_template",
+ "render_js_with_explicit_template",
+ "render_js_with_explicit_action_template",
+ "delete_with_js", "update_page", "update_page_with_instance_variables"
+
+ "layouts/standard"
+ when "builder_layout_test"
+ "layouts/builder"
+ when "action_talk_to_layout", "layout_overriding_layout"
+ "layouts/talk_from_action"
+ end
+ end
+end
+
+NewRenderTestController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+Fun::GamesController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class NewRenderTest < Test::Unit::TestCase
+ def setup
+ @controller = NewRenderTestController.new
+
+ # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+ # a more accurate simulation of what happens in "real life".
+ @controller.logger = Logger.new(nil)
+
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_simple_show
+ get :hello_world
+ assert_response :success
+ assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
+ end
+
+ def test_do_with_render
+ get :render_hello_world
+ assert_template "test/hello_world"
+ end
+
+ def test_do_with_render_from_variable
+ get :render_hello_world_from_variable
+ assert_equal "hello david", @response.body
+ end
+
+ def test_do_with_render_action
+ get :render_action_hello_world
+ assert_template "test/hello_world"
+ end
+
+ def test_do_with_render_action_as_symbol
+ get :render_action_hello_world_as_symbol
+ assert_template "test/hello_world"
+ end
+
+ def test_do_with_render_text
+ get :render_text_hello_world
+ assert_equal "hello world", @response.body
+ end
+
+ def test_do_with_render_text_and_layout
+ get :render_text_hello_world_with_layout
+ assert_equal "hello world, I'm here!", @response.body
+ end
+
+ def test_do_with_render_action_and_layout_false
+ get :hello_world_with_layout_false
+ assert_equal 'Hello world!', @response.body
+ end
+
+ def test_do_with_render_custom_code
+ get :render_custom_code
+ assert_response :missing
+ end
+
+ def test_render_file_with_instance_variables
+ get :render_file_with_instance_variables
+ assert_equal "The secret is in the sauce\n", @response.body
+ end
+
+ def test_render_file_not_using_full_path
+ get :render_file_not_using_full_path
+ assert_equal "The secret is in the sauce\n", @response.body
+ end
+
+ def test_render_file_not_using_full_path_with_relative_path
+ get :render_file_not_using_full_path_with_relative_path
+ assert_equal "The secret is in the sauce\n", @response.body
+ end
+
+ def test_render_file_not_using_full_path_with_dot_in_path
+ get :render_file_not_using_full_path_with_dot_in_path
+ assert_equal "The secret is in the sauce\n", @response.body
+ end
+
+ def test_render_file_with_locals
+ get :render_file_with_locals
+ assert_equal "The secret is in the sauce\n", @response.body
+ end
+
+ def test_attempt_to_access_object_method
+ assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
+ end
+
+ def test_private_methods
+ assert_raises(ActionController::UnknownAction, "No action responded to [determine_layout]") { get :determine_layout }
+ end
+
+ def test_access_to_request_in_view
+ view_internals_old_value = ActionController::Base.view_controller_internals
+
+ ActionController::Base.view_controller_internals = false
+ ActionController::Base.protected_variables_cache = nil
+
+ get :hello_world
+ assert !assigns.include?('request'), 'request should not be in assigns'
+
+ ActionController::Base.view_controller_internals = true
+ ActionController::Base.protected_variables_cache = nil
+
+ get :hello_world
+ assert assigns.include?('request'), 'request should be in assigns'
+ assert_deprecated 'request' do
+ assert_kind_of ActionController::AbstractRequest, assigns['request']
+ end
+ assert_not_deprecated do
+ assert_kind_of ActionController::AbstractRequest, @response.template.request
+ assert_kind_of ActionController::AbstractRequest, assigns['_request']
+ end
+
+ ensure
+ ActionController::Base.view_controller_internals = view_internals_old_value
+ ActionController::Base.protected_variables_cache = nil
+ end
+
+ def test_render_xml
+ get :render_xml_hello
+ assert_equal "\n Hello David
\nThis is grand!
\n\n", @response.body
+ end
+
+ def test_enum_rjs_test
+ get :enum_rjs_test
+ assert_equal <<-EOS.strip, @response.body
+$$(".product").each(function(value, index) {
+new Effect.Highlight(element,{});
+new Effect.Highlight(value,{});
+Sortable.create(value, {onUpdate:function(){new Ajax.Request('/test/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(value)})}});
+new Draggable(value, {});
+});
+EOS
+ end
+
+ def test_render_xml_with_default
+ get :greeting
+ assert_equal "This is grand!
\n", @response.body
+ end
+
+ def test_render_rjs_with_default
+ get :delete_with_js
+ assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+ end
+
+ def test_render_rjs_template_explicitly
+ get :render_js_with_explicit_template
+ assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+ end
+
+ def test_rendering_rjs_action_explicitly
+ get :render_js_with_explicit_action_template
+ assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
+ end
+
+ def test_layout_rendering
+ get :layout_test
+ assert_equal "Hello world!", @response.body
+ end
+
+ def test_layout_test_with_different_layout
+ get :layout_test_with_different_layout
+ assert_equal "Hello world!", @response.body
+ end
+
+ def test_rendering_without_layout
+ get :rendering_without_layout
+ assert_equal "Hello world!", @response.body
+ end
+
+ def test_layout_overriding_layout
+ get :layout_overriding_layout
+ assert_no_match %r{}, @response.body
+ end
+
+ def test_rendering_nothing_on_layout
+ get :rendering_nothing_on_layout
+ assert_equal " ", @response.body
+ end
+
+ def test_render_xml_with_layouts
+ get :builder_layout_test
+ assert_equal "\n\n Hello
\nThis is grand!
\n\n \n", @response.body
+ end
+
+ def test_partial_only
+ get :partial_only
+ assert_equal "only partial", @response.body
+ end
+
+ def test_partial_only_with_layout
+ get :partial_only_with_layout
+ assert_equal "only partial", @response.body
+ end
+
+ def test_render_to_string
+ assert_not_deprecated { get :hello_in_a_string }
+ assert_equal "How's there? goodbyeHello: davidHello: marygoodbye\n", @response.body
+ end
+
+ def test_render_to_string_doesnt_break_assigns
+ get :render_to_string_with_assigns
+ assert_equal "i'm before the render", assigns(:before)
+ assert_equal "i'm after the render", assigns(:after)
+ end
+
+ def test_bad_render_to_string_still_throws_exception
+ assert_raises(ActionController::MissingTemplate) { get :render_to_string_with_exception }
+ end
+
+ def test_render_to_string_that_throws_caught_exception_doesnt_break_assigns
+ assert_nothing_raised { get :render_to_string_with_caught_exception }
+ assert_equal "i'm before the render", assigns(:before)
+ assert_equal "i'm after the render", assigns(:after)
+ end
+
+ def test_nested_rendering
+ get :hello_world
+ assert_equal "Living in a nested world", Fun::GamesController.process(@request, @response).body
+ end
+
+ def test_accessing_params_in_template
+ get :accessing_params_in_template, :name => "David"
+ assert_equal "Hello: David", @response.body
+ end
+
+ def test_accessing_params_in_template_with_layout
+ get :accessing_params_in_template_with_layout, :name => "David"
+ assert_equal "Hello: David", @response.body
+ end
+
+ def test_render_with_explicit_template
+ assert_deprecated(/render/) { get :render_with_explicit_template }
+ assert_response :success
+ end
+
+ def test_double_render
+ assert_raises(ActionController::DoubleRenderError) { get :double_render }
+ end
+
+ def test_double_redirect
+ assert_raises(ActionController::DoubleRenderError) { get :double_redirect }
+ end
+
+ def test_render_and_redirect
+ assert_raises(ActionController::DoubleRenderError) { get :render_and_redirect }
+ end
+
+ # specify the one exception to double render rule - render_to_string followed by render
+ def test_render_to_string_and_render
+ get :render_to_string_and_render
+ assert_equal("Hi web users! here is some cached stuff", @response.body)
+ end
+
+ def test_rendering_with_conflicting_local_vars
+ get :rendering_with_conflicting_local_vars
+ assert_equal("First: David\nSecond: Stephan\nThird: David\nFourth: David\nFifth: ", @response.body)
+ end
+
+ def test_action_talk_to_layout
+ get :action_talk_to_layout
+ assert_equal "Talking to the layout \nAction was here!", @response.body
+ end
+
+ def test_partials_list
+ get :partials_list
+ assert_equal "goodbyeHello: davidHello: marygoodbye\n", @response.body
+ end
+
+ def test_partial_with_locals
+ get :partial_with_locals
+ assert_equal "Hello: david", @response.body
+ end
+
+ def test_partial_collection
+ get :partial_collection
+ assert_equal "Hello: davidHello: mary", @response.body
+ end
+
+ def test_partial_collection_with_locals
+ get :partial_collection_with_locals
+ assert_equal "Bonjour: davidBonjour: mary", @response.body
+ end
+
+ def test_empty_partial_collection
+ get :empty_partial_collection
+ assert_equal " ", @response.body
+ end
+
+ def test_partial_with_hash_object
+ get :partial_with_hash_object
+ assert_equal "Sam", @response.body
+ end
+
+ def test_partial_with_implicit_local_assignment
+ get :partial_with_implicit_local_assignment
+ assert_equal "Hello: Marcel", @response.body
+ end
+
+ def test_render_text_with_assigns
+ get :render_text_with_assigns
+ assert_equal "world", assigns["hello"]
+ end
+
+ def test_update_page
+ get :update_page
+ assert_template nil
+ assert_equal 'text/javascript; charset=utf-8', @response.headers['Content-Type']
+ assert_equal 2, @response.body.split($/).length
+ end
+
+ def test_update_page_with_instance_variables
+ get :update_page_with_instance_variables
+ assert_template nil
+ assert_equal 'text/javascript; charset=utf-8', @response.headers['Content-Type']
+ assert_match /balance/, @response.body
+ assert_match /\$37/, @response.body
+ end
+
+ def test_yield_content_for
+ assert_not_deprecated { get :yield_content_for }
+ assert_equal "Putting stuff in the title! \n\nGreat stuff!\n", @response.body
+ end
+
+
+ def test_overwritting_rendering_relative_file_with_extension
+ get :hello_world_from_rxml_using_template
+ assert_equal "\n Hello
\n\n", @response.body
+
+ get :hello_world_from_rxml_using_action
+ assert_equal "\n Hello
\n\n", @response.body
+ end
+
+
+ def test_head_with_location_header
+ get :head_with_location_header
+ assert @response.body.blank?
+ assert_equal "/foo", @response.headers["Location"]
+ assert_response :ok
+ end
+
+ def test_head_with_custom_header
+ get :head_with_custom_header
+ assert @response.body.blank?
+ assert_equal "something", @response.headers["X-Custom-Header"]
+ assert_response :ok
+ end
+
+ def test_head_with_symbolic_status
+ get :head_with_symbolic_status, :status => "ok"
+ assert_equal "200 OK", @response.headers["Status"]
+ assert_response :ok
+
+ get :head_with_symbolic_status, :status => "not_found"
+ assert_equal "404 Not Found", @response.headers["Status"]
+ assert_response :not_found
+
+ ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.each do |status, code|
+ get :head_with_symbolic_status, :status => status.to_s
+ assert_equal code, @response.response_code
+ assert_response status
+ end
+ end
+
+ def test_head_with_integer_status
+ ActionController::StatusCodes::STATUS_CODES.each do |code, message|
+ get :head_with_integer_status, :status => code.to_s
+ assert_equal message, @response.message
+ end
+ end
+
+ def test_head_with_string_status
+ get :head_with_string_status, :status => "404 Eat Dirt"
+ assert_equal 404, @response.response_code
+ assert_equal "Eat Dirt", @response.message
+ assert_response :not_found
+ end
+
+ def test_head_with_status_code_first
+ get :head_with_status_code_first
+ assert_equal 403, @response.response_code
+ assert_equal "Forbidden", @response.message
+ assert_equal "something", @response.headers["X-Custom-Header"]
+ assert_response :forbidden
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/raw_post_test.rb b/tracks/vendor/rails/actionpack/test/controller/raw_post_test.rb
new file mode 100644
index 00000000..c9816484
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/raw_post_test.rb
@@ -0,0 +1,68 @@
+require 'test/unit'
+require 'cgi'
+require 'stringio'
+require File.dirname(__FILE__) + '/../../lib/action_controller/cgi_ext/raw_post_data_fix'
+
+class RawPostDataTest < Test::Unit::TestCase
+ def setup
+ ENV.delete('RAW_POST_DATA')
+ @request_body = 'a=1'
+ end
+
+ def test_post_with_urlencoded_body
+ ENV['REQUEST_METHOD'] = 'POST'
+ ENV['CONTENT_TYPE'] = ' apPlication/x-Www-form-urlEncoded; charset=utf-8'
+ assert_equal ['1'], cgi_params['a']
+ assert_has_raw_post_data
+ end
+
+ def test_post_with_empty_content_type_treated_as_urlencoded
+ ENV['REQUEST_METHOD'] = 'POST'
+ ENV['CONTENT_TYPE'] = ''
+ assert_equal ['1'], cgi_params['a']
+ assert_has_raw_post_data
+ end
+
+ def test_post_with_unrecognized_content_type_reads_body_but_doesnt_parse_params
+ ENV['REQUEST_METHOD'] = 'POST'
+ ENV['CONTENT_TYPE'] = 'foo/bar'
+ assert cgi_params.empty?
+ assert_has_raw_post_data
+ end
+
+ def test_put_with_urlencoded_body
+ ENV['REQUEST_METHOD'] = 'PUT'
+ ENV['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
+ assert_equal ['1'], cgi_params['a']
+ assert_has_raw_post_data
+ end
+
+ def test_put_with_empty_content_type_ignores_body
+ ENV['REQUEST_METHOD'] = 'PUT'
+ ENV['CONTENT_TYPE'] = ''
+ assert cgi_params.empty?
+ assert_has_raw_post_data
+ end
+
+ def test_put_with_unrecognized_content_type_ignores_body
+ ENV['REQUEST_METHOD'] = 'PUT'
+ ENV['CONTENT_TYPE'] = 'foo/bar'
+ assert cgi_params.empty?
+ assert_has_raw_post_data
+ end
+
+ private
+ def cgi_params
+ old_stdin, $stdin = $stdin, StringIO.new(@request_body.dup)
+ ENV['CONTENT_LENGTH'] = $stdin.size.to_s
+ CGI.new.params
+ ensure
+ $stdin = old_stdin
+ end
+
+ def assert_has_raw_post_data(expected_body = @request_body)
+ assert_not_nil ENV['RAW_POST_DATA']
+ assert ENV['RAW_POST_DATA'].frozen?
+ assert_equal expected_body, ENV['RAW_POST_DATA']
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/redirect_test.rb b/tracks/vendor/rails/actionpack/test/controller/redirect_test.rb
new file mode 100755
index 00000000..575532c1
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/redirect_test.rb
@@ -0,0 +1,156 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class RedirectController < ActionController::Base
+ def simple_redirect
+ redirect_to :action => "hello_world"
+ end
+
+ def method_redirect
+ redirect_to :dashbord_url, 1, "hello"
+ end
+
+ def host_redirect
+ redirect_to :action => "other_host", :only_path => false, :host => 'other.test.host'
+ end
+
+ def module_redirect
+ redirect_to :controller => 'module_test/module_redirect', :action => "hello_world"
+ end
+
+ def redirect_with_assigns
+ @hello = "world"
+ redirect_to :action => "hello_world"
+ end
+
+ def redirect_to_back
+ redirect_to :back
+ end
+
+ def rescue_errors(e) raise e end
+
+ def rescue_action(e) raise end
+
+ protected
+ def dashbord_url(id, message)
+ url_for :action => "dashboard", :params => { "id" => id, "message" => message }
+ end
+end
+
+class RedirectTest < Test::Unit::TestCase
+ def setup
+ @controller = RedirectController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_simple_redirect
+ get :simple_redirect
+ assert_response :redirect
+ assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+ end
+
+ def test_redirect_with_method_reference_and_parameters
+ assert_deprecated(/redirect_to/) { get :method_redirect }
+ assert_response :redirect
+ assert_equal "http://test.host/redirect/dashboard/1?message=hello", redirect_to_url
+ end
+
+ def test_simple_redirect_using_options
+ get :host_redirect
+ assert_response :redirect
+ assert_redirected_to :action => "other_host", :only_path => false, :host => 'other.test.host'
+ end
+
+ def test_redirect_error_with_pretty_diff
+ get :host_redirect
+ assert_response :redirect
+ begin
+ assert_redirected_to :action => "other_host", :only_path => true
+ rescue Test::Unit::AssertionFailedError => err
+ redirection_msg, diff_msg = err.message.scan(/<\{[^\}]+\}>/).collect { |s| s[2..-3] }
+ assert_match %r("only_path"=>false), redirection_msg
+ assert_match %r("host"=>"other.test.host"), redirection_msg
+ assert_match %r("action"=>"other_host"), redirection_msg
+ assert_match %r("only_path"=>true), diff_msg
+ assert_match %r("host"=>"other.test.host"), diff_msg
+ end
+ end
+
+ def test_module_redirect
+ get :module_redirect
+ assert_response :redirect
+ assert_redirected_to "http://test.host/module_test/module_redirect/hello_world"
+ end
+
+ def test_module_redirect_using_options
+ get :module_redirect
+ assert_response :redirect
+ assert_redirected_to :controller => 'module_test/module_redirect', :action => 'hello_world'
+ end
+
+ def test_redirect_with_assigns
+ get :redirect_with_assigns
+ assert_response :redirect
+ assert_equal "world", assigns["hello"]
+ end
+
+ def test_redirect_to_back
+ @request.env["HTTP_REFERER"] = "http://www.example.com/coming/from"
+ get :redirect_to_back
+ assert_response :redirect
+ assert_equal "http://www.example.com/coming/from", redirect_to_url
+ end
+
+ def test_redirect_to_back_with_no_referer
+ assert_raises(ActionController::RedirectBackError) {
+ @request.env["HTTP_REFERER"] = nil
+ get :redirect_to_back
+ }
+ end
+end
+
+module ModuleTest
+ class ModuleRedirectController < ::RedirectController
+ def module_redirect
+ redirect_to :controller => '/redirect', :action => "hello_world"
+ end
+ end
+
+ class ModuleRedirectTest < Test::Unit::TestCase
+ def setup
+ @controller = ModuleRedirectController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_simple_redirect
+ get :simple_redirect
+ assert_response :redirect
+ assert_equal "http://test.host/module_test/module_redirect/hello_world", redirect_to_url
+ end
+
+ def test_redirect_with_method_reference_and_parameters
+ assert_deprecated(/redirect_to/) { get :method_redirect }
+ assert_response :redirect
+ assert_equal "http://test.host/module_test/module_redirect/dashboard/1?message=hello", redirect_to_url
+ end
+
+ def test_simple_redirect_using_options
+ get :host_redirect
+ assert_response :redirect
+ assert_redirected_to :action => "other_host", :only_path => false, :host => 'other.test.host'
+ end
+
+ def test_module_redirect
+ get :module_redirect
+ assert_response :redirect
+ assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+ end
+
+ def test_module_redirect_using_options
+ get :module_redirect
+ assert_response :redirect
+ assert_redirected_to :controller => 'redirect', :action => "hello_world"
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/render_test.rb b/tracks/vendor/rails/actionpack/test/controller/render_test.rb
new file mode 100644
index 00000000..b223862c
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/render_test.rb
@@ -0,0 +1,282 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+unless defined?(Customer)
+ Customer = Struct.new("Customer", :name)
+end
+
+module Fun
+ class GamesController < ActionController::Base
+ def hello_world
+ end
+ end
+end
+
+
+class TestController < ActionController::Base
+ layout :determine_layout
+
+ def hello_world
+ end
+
+ def render_hello_world
+ render "test/hello_world"
+ end
+
+ def render_hello_world_from_variable
+ @person = "david"
+ render_text "hello #{@person}"
+ end
+
+ def render_action_hello_world
+ render_action "hello_world"
+ end
+
+ def render_action_hello_world_with_symbol
+ render_action :hello_world
+ end
+
+ def render_text_hello_world
+ render_text "hello world"
+ end
+
+ def render_json_hello_world
+ render_json({:hello => 'world'}.to_json)
+ end
+
+ def render_json_hello_world_with_callback
+ render_json({:hello => 'world'}.to_json, 'alert')
+ end
+
+ def render_custom_code
+ render_text "hello world", "404 Moved"
+ end
+
+ def render_text_appendix
+ render_text "hello world"
+ render_text ", goodbye!", "404 Not Found", true
+ end
+
+ def render_nothing_with_appendix
+ render_text "appended", nil, true
+ end
+
+ def render_xml_hello
+ @name = "David"
+ render "test/hello"
+ end
+
+ def greeting
+ # let's just rely on the template
+ end
+
+ def layout_test
+ render_action "hello_world"
+ end
+
+ def builder_layout_test
+ render_action "hello"
+ end
+
+ def builder_partial_test
+ render_action "hello_world_container"
+ end
+
+ def partials_list
+ @test_unchanged = 'hello'
+ @customers = [ Customer.new("david"), Customer.new("mary") ]
+ render_action "list"
+ end
+
+ def partial_only
+ render_partial
+ end
+
+ def hello_in_a_string
+ @customers = [ Customer.new("david"), Customer.new("mary") ]
+ render_text "How's there? #{render_to_string("test/list")}"
+ end
+
+ def accessing_params_in_template
+ render_template "Hello: <%= params[:name] %>"
+ end
+
+ def accessing_local_assigns_in_inline_template
+ name = params[:local_name]
+ render :inline => "<%= 'Goodbye, ' + local_name %>",
+ :locals => { :local_name => name }
+ end
+
+ def accessing_local_assigns_in_inline_template_with_string_keys
+ name = params[:local_name]
+ ActionView::Base.local_assigns_support_string_keys = true
+ render :inline => "<%= 'Goodbye, ' + local_name %>",
+ :locals => { "local_name" => name }
+ ActionView::Base.local_assigns_support_string_keys = false
+ end
+
+ def render_to_string_test
+ @foo = render_to_string :inline => "this is a test"
+ end
+
+ def rescue_action(e) raise end
+
+ private
+ def determine_layout
+ case action_name
+ when "layout_test": "layouts/standard"
+ when "builder_layout_test": "layouts/builder"
+ end
+ end
+end
+
+TestController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+Fun::GamesController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class RenderTest < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = TestController.new
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_simple_show
+ get :hello_world
+ assert_response 200
+ assert_template "test/hello_world"
+ end
+
+ def test_do_with_render
+ assert_deprecated_render { get :render_hello_world }
+ assert_template "test/hello_world"
+ end
+
+ def test_do_with_render_from_variable
+ get :render_hello_world_from_variable
+ assert_equal "hello david", @response.body
+ end
+
+ def test_do_with_render_action
+ get :render_action_hello_world
+ assert_template "test/hello_world"
+ end
+
+ def test_do_with_render_action_with_symbol
+ get :render_action_hello_world_with_symbol
+ assert_template "test/hello_world"
+ end
+
+ def test_do_with_render_text
+ get :render_text_hello_world
+ assert_equal "hello world", @response.body
+ end
+
+ def test_do_with_render_json
+ get :render_json_hello_world
+ assert_equal '{hello: "world"}', @response.body
+ assert_equal 'application/json', @response.content_type
+ end
+
+ def test_do_with_render_json_with_callback
+ get :render_json_hello_world_with_callback
+ assert_equal 'alert({hello: "world"})', @response.body
+ assert_equal 'application/json', @response.content_type
+ end
+
+ def test_do_with_render_custom_code
+ get :render_custom_code
+ assert_response 404
+ end
+
+ def test_do_with_render_text_appendix
+ get :render_text_appendix
+ assert_response 404
+ assert_equal 'hello world, goodbye!', @response.body
+ end
+
+ def test_do_with_render_nothing_with_appendix
+ get :render_nothing_with_appendix
+ assert_response 200
+ assert_equal 'appended', @response.body
+ end
+
+ def test_attempt_to_access_object_method
+ assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
+ end
+
+ def test_private_methods
+ assert_raises(ActionController::UnknownAction, "No action responded to [determine_layout]") { get :determine_layout }
+ end
+
+ def test_render_xml
+ assert_deprecated_render { get :render_xml_hello }
+ assert_equal "\n Hello David
\nThis is grand!
\n\n", @response.body
+ end
+
+ def test_render_xml_with_default
+ get :greeting
+ assert_equal "This is grand!
\n", @response.body
+ end
+
+ def test_render_xml_with_partial
+ get :builder_partial_test
+ assert_equal "\n \n \n", @response.body
+ end
+
+ def test_layout_rendering
+ get :layout_test
+ assert_equal "Hello world!", @response.body
+ end
+
+ def test_render_xml_with_layouts
+ get :builder_layout_test
+ assert_equal "\n\n Hello
\nThis is grand!
\n\n \n", @response.body
+ end
+
+ # def test_partials_list
+ # get :partials_list
+ # assert_equal "goodbyeHello: davidHello: marygoodbye\n", process_request.body
+ # end
+
+ def test_partial_only
+ get :partial_only
+ assert_equal "only partial", @response.body
+ end
+
+ def test_render_to_string
+ get :hello_in_a_string
+ assert_equal "How's there? goodbyeHello: davidHello: marygoodbye\n", @response.body
+ end
+
+ def test_render_to_string_resets_assigns
+ get :render_to_string_test
+ assert_equal "The value of foo is: ::this is a test::\n", @response.body
+ end
+
+ def test_nested_rendering
+ @controller = Fun::GamesController.new
+ get :hello_world
+ assert_equal "Living in a nested world", @response.body
+ end
+
+ def test_accessing_params_in_template
+ get :accessing_params_in_template, :name => "David"
+ assert_equal "Hello: David", @response.body
+ end
+
+ def test_accessing_local_assigns_in_inline_template
+ get :accessing_local_assigns_in_inline_template, :local_name => "Local David"
+ assert_equal "Goodbye, Local David", @response.body
+ end
+
+ def test_accessing_local_assigns_in_inline_template_with_string_keys
+ get :accessing_local_assigns_in_inline_template_with_string_keys, :local_name => "Local David"
+ assert_equal "Goodbye, Local David", @response.body
+ end
+
+ protected
+ def assert_deprecated_render(&block)
+ assert_deprecated(/render/, &block)
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/request_test.rb b/tracks/vendor/rails/actionpack/test/controller/request_test.rb
new file mode 100644
index 00000000..65e88b74
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/request_test.rb
@@ -0,0 +1,311 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class RequestTest < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ end
+
+ def test_remote_ip
+ assert_equal '0.0.0.0', @request.remote_ip
+
+ @request.remote_addr = '1.2.3.4'
+ assert_equal '1.2.3.4', @request.remote_ip
+
+ @request.env['HTTP_CLIENT_IP'] = '2.3.4.5'
+ assert_equal '2.3.4.5', @request.remote_ip
+ @request.env.delete 'HTTP_CLIENT_IP'
+
+ @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
+ assert_equal '3.4.5.6', @request.remote_ip
+
+ @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,3.4.5.6'
+ assert_equal '3.4.5.6', @request.remote_ip
+
+ @request.env['HTTP_X_FORWARDED_FOR'] = '172.16.0.1,3.4.5.6'
+ assert_equal '3.4.5.6', @request.remote_ip
+
+ @request.env['HTTP_X_FORWARDED_FOR'] = '192.168.0.1,3.4.5.6'
+ assert_equal '3.4.5.6', @request.remote_ip
+
+ @request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1,3.4.5.6'
+ assert_equal '3.4.5.6', @request.remote_ip
+
+ @request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6'
+ assert_equal '127.0.0.1', @request.remote_ip
+
+ @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1'
+ assert_equal '1.2.3.4', @request.remote_ip
+ @request.env.delete 'HTTP_X_FORWARDED_FOR'
+ end
+
+ def test_domains
+ @request.host = "www.rubyonrails.org"
+ assert_equal "rubyonrails.org", @request.domain
+
+ @request.host = "www.rubyonrails.co.uk"
+ assert_equal "rubyonrails.co.uk", @request.domain(2)
+
+ @request.host = "192.168.1.200"
+ assert_nil @request.domain
+
+ @request.host = nil
+ assert_nil @request.domain
+ end
+
+ def test_subdomains
+ @request.host = "www.rubyonrails.org"
+ assert_equal %w( www ), @request.subdomains
+
+ @request.host = "www.rubyonrails.co.uk"
+ assert_equal %w( www ), @request.subdomains(2)
+
+ @request.host = "dev.www.rubyonrails.co.uk"
+ assert_equal %w( dev www ), @request.subdomains(2)
+
+ @request.host = "foobar.foobar.com"
+ assert_equal %w( foobar ), @request.subdomains
+
+ @request.host = nil
+ assert_equal [], @request.subdomains
+ end
+
+ def test_port_string
+ @request.port = 80
+ assert_equal "", @request.port_string
+
+ @request.port = 8080
+ assert_equal ":8080", @request.port_string
+ end
+
+ def test_relative_url_root
+ @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+ @request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3'
+ assert_equal '', @request.relative_url_root, "relative_url_root should be disabled on lighttpd"
+
+ @request.env['SERVER_SOFTWARE'] = 'apache/1.2.3 some random text'
+
+ @request.env['SCRIPT_NAME'] = nil
+ assert_equal "", @request.relative_url_root
+
+ @request.env['SCRIPT_NAME'] = "/dispatch.cgi"
+ assert_equal "", @request.relative_url_root
+
+ @request.env['SCRIPT_NAME'] = "/myapp.rb"
+ assert_equal "", @request.relative_url_root
+
+ @request.relative_url_root = nil
+ @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+ assert_equal "/hieraki", @request.relative_url_root
+
+ @request.relative_url_root = nil
+ @request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
+ assert_equal "/collaboration/hieraki", @request.relative_url_root
+
+ # apache/scgi case
+ @request.relative_url_root = nil
+ @request.env['SCRIPT_NAME'] = "/collaboration/hieraki"
+ assert_equal "/collaboration/hieraki", @request.relative_url_root
+
+ @request.relative_url_root = nil
+ @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+ @request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3'
+ @request.env['RAILS_RELATIVE_URL_ROOT'] = "/hieraki"
+ assert_equal "/hieraki", @request.relative_url_root
+
+ # @env overrides path guess
+ @request.relative_url_root = nil
+ @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+ @request.env['SERVER_SOFTWARE'] = 'apache/1.2.3 some random text'
+ @request.env['RAILS_RELATIVE_URL_ROOT'] = "/real_url"
+ assert_equal "/real_url", @request.relative_url_root
+ end
+
+ def test_request_uri
+ @request.env['SERVER_SOFTWARE'] = 'Apache 42.342.3432'
+
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1"
+ assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
+ assert_equal "/path/of/some/uri", @request.path
+
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri"
+ assert_equal "/path/of/some/uri", @request.request_uri
+ assert_equal "/path/of/some/uri", @request.path
+
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI "/path/of/some/uri"
+ assert_equal "/path/of/some/uri", @request.request_uri
+ assert_equal "/path/of/some/uri", @request.path
+
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI "/"
+ assert_equal "/", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI "/?m=b"
+ assert_equal "/?m=b", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI "/"
+ @request.env['SCRIPT_NAME'] = "/dispatch.cgi"
+ assert_equal "/", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI "/hieraki/"
+ @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+ assert_equal "/hieraki/", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI "/collaboration/hieraki/books/edit/2"
+ @request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
+ assert_equal "/collaboration/hieraki/books/edit/2", @request.request_uri
+ assert_equal "/books/edit/2", @request.path
+
+ # The following tests are for when REQUEST_URI is not supplied (as in IIS)
+ @request.relative_url_root = nil
+ @request.set_REQUEST_URI nil
+ @request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1"
+ @request.env['SCRIPT_NAME'] = nil #"/path/dispatch.rb"
+ assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
+ assert_equal "/path/of/some/uri", @request.path
+
+ @request.relative_url_root = nil
+ @request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1"
+ @request.env['SCRIPT_NAME'] = "/path/dispatch.rb"
+ assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
+ assert_equal "/of/some/uri", @request.path
+
+ @request.relative_url_root = nil
+ @request.env['PATH_INFO'] = "/path/of/some/uri"
+ @request.env['SCRIPT_NAME'] = nil
+ assert_equal "/path/of/some/uri", @request.request_uri
+ assert_equal "/path/of/some/uri", @request.path
+
+ @request.relative_url_root = nil
+ @request.env['PATH_INFO'] = "/"
+ assert_equal "/", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.relative_url_root = nil
+ @request.env['PATH_INFO'] = "/?m=b"
+ assert_equal "/?m=b", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.relative_url_root = nil
+ @request.env['PATH_INFO'] = "/"
+ @request.env['SCRIPT_NAME'] = "/dispatch.cgi"
+ assert_equal "/", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.relative_url_root = nil
+ @request.env['PATH_INFO'] = "/hieraki/"
+ @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+ assert_equal "/hieraki/", @request.request_uri
+ assert_equal "/", @request.path
+
+ @request.set_REQUEST_URI '/hieraki/dispatch.cgi'
+ @request.relative_url_root = '/hieraki'
+ assert_equal "/dispatch.cgi", @request.path
+ @request.relative_url_root = nil
+
+ @request.set_REQUEST_URI '/hieraki/dispatch.cgi'
+ @request.relative_url_root = '/foo'
+ assert_equal "/hieraki/dispatch.cgi", @request.path
+ @request.relative_url_root = nil
+
+ # This test ensures that Rails uses REQUEST_URI over PATH_INFO
+ @request.relative_url_root = nil
+ @request.env['REQUEST_URI'] = "/some/path"
+ @request.env['PATH_INFO'] = "/another/path"
+ @request.env['SCRIPT_NAME'] = "/dispatch.cgi"
+ assert_equal "/some/path", @request.request_uri
+ assert_equal "/some/path", @request.path
+ end
+
+
+ def test_host_with_port
+ @request.host = "rubyonrails.org"
+ @request.port = 80
+ assert_equal "rubyonrails.org", @request.host_with_port
+
+ @request.host = "rubyonrails.org"
+ @request.port = 81
+ assert_equal "rubyonrails.org:81", @request.host_with_port
+ end
+
+ def test_server_software
+ assert_equal nil, @request.server_software
+
+ @request.env['SERVER_SOFTWARE'] = 'Apache3.422'
+ assert_equal 'apache', @request.server_software
+
+ @request.env['SERVER_SOFTWARE'] = 'lighttpd(1.1.4)'
+ assert_equal 'lighttpd', @request.server_software
+ end
+
+ def test_xml_http_request
+ assert !@request.xml_http_request?
+ assert !@request.xhr?
+
+ @request.env['HTTP_X_REQUESTED_WITH'] = "DefinitelyNotAjax1.0"
+ assert !@request.xml_http_request?
+ assert !@request.xhr?
+
+ @request.env['HTTP_X_REQUESTED_WITH'] = "XMLHttpRequest"
+ assert @request.xml_http_request?
+ assert @request.xhr?
+ end
+
+ def test_reports_ssl
+ assert !@request.ssl?
+ @request.env['HTTPS'] = 'on'
+ assert @request.ssl?
+ end
+
+ def test_reports_ssl_when_proxied_via_lighttpd
+ assert !@request.ssl?
+ @request.env['HTTP_X_FORWARDED_PROTO'] = 'https'
+ assert @request.ssl?
+ end
+
+ def test_symbolized_request_methods
+ [:get, :post, :put, :delete].each do |method|
+ set_request_method_to method
+ assert_equal method, @request.method
+ end
+ end
+
+ def test_allow_method_hacking_on_post
+ set_request_method_to :post
+ [:get, :put, :delete].each do |method|
+ @request.instance_eval { @parameters = { :_method => method } ; @request_method = nil }
+ assert_equal method, @request.method
+ end
+ end
+
+ def test_restrict_method_hacking
+ @request.instance_eval { @parameters = { :_method => 'put' } }
+ [:get, :put, :delete].each do |method|
+ set_request_method_to method
+ assert_equal method, @request.method
+ end
+ end
+
+ def test_head_masquarading_as_get
+ set_request_method_to :head
+ assert_equal :get, @request.method
+ assert @request.get?
+ assert @request.head?
+ end
+
+ protected
+ def set_request_method_to(method)
+ @request.env['REQUEST_METHOD'] = method.to_s.upcase
+ @request.instance_eval { @request_method = nil }
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/resources_test.rb b/tracks/vendor/rails/actionpack/test/controller/resources_test.rb
new file mode 100644
index 00000000..32b55460
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/resources_test.rb
@@ -0,0 +1,267 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class ResourcesController < ActionController::Base
+ def index() render :nothing => true end
+ def rescue_action(e) raise e end
+end
+
+class ThreadsController < ResourcesController; end
+class MessagesController < ResourcesController; end
+class CommentsController < ResourcesController; end
+
+
+class ResourcesTest < Test::Unit::TestCase
+ def test_should_arrange_actions
+ resource = ActionController::Resources::Resource.new(:messages,
+ :collection => { :rss => :get, :reorder => :post, :csv => :post },
+ :member => { :rss => :get, :atom => :get, :upload => :post, :fix => :post },
+ :new => { :preview => :get, :draft => :get })
+
+ assert_resource_methods [:rss], resource, :collection, :get
+ assert_resource_methods [:csv, :reorder], resource, :collection, :post
+ assert_resource_methods [:edit, :rss, :atom], resource, :member, :get
+ assert_resource_methods [:upload, :fix], resource, :member, :post
+ assert_resource_methods [:new, :preview, :draft], resource, :new, :get
+ end
+
+ def test_default_restful_routes
+ with_restful_routing :messages do
+ assert_simply_restful_for :messages
+ end
+ end
+
+ def test_multiple_default_restful_routes
+ with_restful_routing :messages, :comments do
+ assert_simply_restful_for :messages
+ assert_simply_restful_for :comments
+ end
+ end
+
+ def test_with_path_prefix
+ with_restful_routing :messages, :path_prefix => '/thread/:thread_id' do
+ assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
+ end
+ end
+
+ def test_multile_with_path_prefix
+ with_restful_routing :messages, :comments, :path_prefix => '/thread/:thread_id' do
+ assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
+ assert_simply_restful_for :comments, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
+ end
+ end
+
+ def test_with_collection_action
+ rss_options = {:action => 'rss'}
+ rss_path = "/messages;rss"
+ actions = { 'a' => :put, 'b' => :post, 'c' => :delete }
+
+ with_restful_routing :messages, :collection => { :rss => :get }.merge(actions) do
+ assert_restful_routes_for :messages do |options|
+ assert_routing rss_path, options.merge(rss_options)
+
+ actions.each do |action, method|
+ assert_recognizes(options.merge(:action => action), :path => "/messages;#{action}", :method => method)
+ end
+ end
+
+ assert_restful_named_routes_for :messages do |options|
+ assert_named_route rss_path, :rss_messages_path, rss_options
+ actions.keys.each do |action|
+ assert_named_route "/messages;#{action}", "#{action}_messages_path", :action => action
+ end
+ end
+ end
+ end
+
+ def test_with_member_action
+ [:put, :post].each do |method|
+ with_restful_routing :messages, :member => { :mark => method } do
+ mark_options = {:action => 'mark', :id => '1'}
+ mark_path = "/messages/1;mark"
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method)
+ end
+
+ assert_restful_named_routes_for :messages do |options|
+ assert_named_route mark_path, :mark_message_path, mark_options
+ end
+ end
+ end
+ end
+
+ def test_with_two_member_actions_with_same_method
+ [:put, :post].each do |method|
+ with_restful_routing :messages, :member => { :mark => method, :unmark => method } do
+ %w(mark unmark).each do |action|
+ action_options = {:action => action, :id => '1'}
+ action_path = "/messages/1;#{action}"
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(action_options), :path => action_path, :method => method)
+ end
+
+ assert_restful_named_routes_for :messages do |options|
+ assert_named_route action_path, "#{action}_message_path".to_sym, action_options
+ end
+ end
+ end
+ end
+ end
+
+
+ def test_with_new_action
+ with_restful_routing :messages, :new => { :preview => :post } do
+ preview_options = {:action => 'preview'}
+ preview_path = "/messages/new;preview"
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post)
+ end
+
+ assert_restful_named_routes_for :messages do |options|
+ assert_named_route preview_path, :preview_new_message_path, preview_options
+ end
+ end
+ end
+
+ def test_override_new_method
+ with_restful_routing :messages do
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get)
+ assert_raises(ActionController::RoutingError) do
+ ActionController::Routing::Routes.recognize_path("/messages/new", :method => :post)
+ end
+ end
+ end
+
+ with_restful_routing :messages, :new => { :new => :any } do
+ assert_restful_routes_for :messages do |options|
+ assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :post)
+ assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get)
+ end
+ end
+ end
+
+ def test_nested_restful_routes
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :threads do |map|
+ map.resources :messages do |map|
+ map.resources :comments
+ end
+ end
+ end
+
+ assert_simply_restful_for :threads
+ assert_simply_restful_for :messages,
+ :path_prefix => 'threads/1/',
+ :options => { :thread_id => '1' }
+ assert_simply_restful_for :comments,
+ :path_prefix => 'threads/1/messages/2/',
+ :options => { :thread_id => '1', :message_id => '2' }
+ end
+ end
+
+ def test_restful_routes_dont_generate_duplicates
+ with_restful_routing :messages do
+ routes = ActionController::Routing::Routes.routes
+ routes.each do |route|
+ routes.each do |r|
+ next if route === r # skip the comparison instance
+ assert distinct_routes?(route, r), "Duplicate Route: #{route}"
+ end
+ end
+ end
+ end
+
+ protected
+ def with_restful_routing(*args)
+ with_routing do |set|
+ set.draw { |map| map.resources(*args) }
+ yield
+ end
+ end
+
+ # runs assert_restful_routes_for and assert_restful_named_routes for on the controller_name and options, without passing a block.
+ def assert_simply_restful_for(controller_name, options = {})
+ assert_restful_routes_for controller_name, options
+ assert_restful_named_routes_for controller_name, options
+ end
+
+ def assert_restful_routes_for(controller_name, options = {})
+ (options[:options] ||= {})[:controller] = controller_name.to_s
+
+ collection_path = "/#{options[:path_prefix]}#{controller_name}"
+ member_path = "#{collection_path}/1"
+ new_path = "#{collection_path}/new"
+
+ with_options(options[:options]) do |controller|
+ controller.assert_routing collection_path, :action => 'index'
+ controller.assert_routing "#{collection_path}.xml" , :action => 'index', :format => 'xml'
+ controller.assert_routing new_path, :action => 'new'
+ controller.assert_routing member_path, :action => 'show', :id => '1'
+ controller.assert_routing "#{member_path};edit", :action => 'edit', :id => '1'
+ controller.assert_routing "#{member_path}.xml", :action => 'show', :id => '1', :format => 'xml'
+ end
+
+ assert_recognizes(
+ options[:options].merge(:action => 'create'),
+ :path => collection_path, :method => :post)
+
+ assert_recognizes(
+ options[:options].merge(:action => 'update', :id => '1'),
+ :path => member_path, :method => :put)
+
+ assert_recognizes(
+ options[:options].merge(:action => 'destroy', :id => '1'),
+ :path => member_path, :method => :delete)
+
+ yield options[:options] if block_given?
+ end
+
+ # test named routes like foo_path and foos_path map to the correct options.
+ def assert_restful_named_routes_for(controller_name, singular_name = nil, options = {})
+ if singular_name.is_a?(Hash)
+ options = singular_name
+ singular_name = nil
+ end
+ singular_name ||= controller_name.to_s.singularize
+ (options[:options] ||= {})[:controller] = controller_name.to_s
+ @controller = "#{controller_name.to_s.camelize}Controller".constantize.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ get :index, options[:options]
+ options[:options].delete :action
+
+ full_prefix = "/#{options[:path_prefix]}#{controller_name}"
+
+ assert_named_route "#{full_prefix}", "#{controller_name}_path", options[:options]
+ assert_named_route "#{full_prefix}.xml", "formatted_#{controller_name}_path", options[:options].merge(:format => 'xml')
+ assert_named_route "#{full_prefix}/new", "new_#{singular_name}_path", options[:options]
+ assert_named_route "#{full_prefix}/1", "#{singular_name}_path", options[:options].merge(:id => '1')
+ assert_named_route "#{full_prefix}/1;edit", "edit_#{singular_name}_path", options[:options].merge(:id => '1')
+ assert_named_route "#{full_prefix}/1.xml", "formatted_#{singular_name}_path", options[:options].merge(:format => 'xml', :id => '1')
+ yield options[:options] if block_given?
+ end
+
+ def assert_named_route(expected, route, options)
+ actual = @controller.send(route, options) rescue $!.class.name
+ assert_equal expected, actual, "Error on route: #{route}(#{options.inspect})"
+ end
+
+ def assert_resource_methods(expected, resource, action_method, method)
+ assert_equal expected.length, resource.send("#{action_method}_methods")[method].size, "#{resource.send("#{action_method}_methods")[method].inspect}"
+ expected.each do |action|
+ assert resource.send("#{action_method}_methods")[method].include?(action),
+ "#{method} not in #{action_method} methods: #{resource.send("#{action_method}_methods")[method].inspect}"
+ end
+ end
+
+ def distinct_routes? (r1, r2)
+ if r1.conditions == r2.conditions and r1.requirements == r2.requirements then
+ if r1.segments.collect(&:to_s) == r2.segments.collect(&:to_s) then
+ return false
+ end
+ end
+ true
+ end
+
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/routing_test.rb b/tracks/vendor/rails/actionpack/test/controller/routing_test.rb
new file mode 100644
index 00000000..9a41440e
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/routing_test.rb
@@ -0,0 +1,1758 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+require 'test/unit'
+require File.dirname(__FILE__) + '/fake_controllers'
+require 'action_controller/routing'
+
+RunTimeTests = ARGV.include? 'time'
+ROUTING = ActionController::Routing
+
+class ROUTING::RouteBuilder
+ attr_reader :warn_output
+
+ def warn(msg)
+ (@warn_output ||= []) << msg
+ end
+end
+
+class LegacyRouteSetTests < Test::Unit::TestCase
+ attr_reader :rs
+ def setup
+ @rs = ::ActionController::Routing::RouteSet.new
+ @rs.draw {|m| m.connect ':controller/:action/:id' }
+ ActionController::Routing.use_controllers! %w(content admin/user admin/news_feed)
+ end
+
+ def test_default_setup
+ assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/content"))
+ assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/content/list"))
+ assert_equal({:controller => "content", :action => 'show', :id => '10'}, rs.recognize_path("/content/show/10"))
+
+ assert_equal({:controller => "admin/user", :action => 'show', :id => '10'}, rs.recognize_path("/admin/user/show/10"))
+
+ assert_equal '/admin/user/show/10', rs.generate(:controller => 'admin/user', :action => 'show', :id => 10)
+
+ assert_equal '/admin/user/show', rs.generate({:action => 'show'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+ assert_equal '/admin/user/list/10', rs.generate({}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+
+ assert_equal '/admin/stuff', rs.generate({:controller => 'stuff'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+ assert_equal '/stuff', rs.generate({:controller => '/stuff'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+ end
+
+ def test_ignores_leading_slash
+ @rs.draw {|m| m.connect '/:controller/:action/:id'}
+ test_default_setup
+ end
+
+ def test_time_recognition
+ n = 10000
+ if RunTimeTests
+ GC.start
+ rectime = Benchmark.realtime do
+ n.times do
+ rs.recognize_path("content")
+ rs.recognize_path("content/list")
+ rs.recognize_path("content/show/10")
+ rs.recognize_path("admin/user")
+ rs.recognize_path("admin/user/list")
+ rs.recognize_path("admin/user/show/10")
+ end
+ end
+ puts "\n\nRecognition (RouteSet):"
+ per_url = rectime / (n * 6)
+ puts "#{per_url * 1000} ms/url"
+ puts "#{1 / per_url} url/s\n\n"
+ end
+ end
+ def test_time_generation
+ n = 5000
+ if RunTimeTests
+ GC.start
+ pairs = [
+ [{:controller => 'content', :action => 'index'}, {:controller => 'content', :action => 'show'}],
+ [{:controller => 'content'}, {:controller => 'content', :action => 'index'}],
+ [{:controller => 'content', :action => 'list'}, {:controller => 'content', :action => 'index'}],
+ [{:controller => 'content', :action => 'show', :id => '10'}, {:controller => 'content', :action => 'list'}],
+ [{:controller => 'admin/user', :action => 'index'}, {:controller => 'admin/user', :action => 'show'}],
+ [{:controller => 'admin/user'}, {:controller => 'admin/user', :action => 'index'}],
+ [{:controller => 'admin/user', :action => 'list'}, {:controller => 'admin/user', :action => 'index'}],
+ [{:controller => 'admin/user', :action => 'show', :id => '10'}, {:controller => 'admin/user', :action => 'list'}],
+ ]
+ p = nil
+ gentime = Benchmark.realtime do
+ n.times do
+ pairs.each {|(a, b)| rs.generate(a, b)}
+ end
+ end
+
+ puts "\n\nGeneration (RouteSet): (#{(n * 8)} urls)"
+ per_url = gentime / (n * 8)
+ puts "#{per_url * 1000} ms/url"
+ puts "#{1 / per_url} url/s\n\n"
+ end
+ end
+
+ def test_route_with_colon_first
+ rs.draw do |map|
+ map.connect '/:controller/:action/:id', :action => 'index', :id => nil
+ map.connect ':url', :controller => 'tiny_url', :action => 'translate'
+ end
+ end
+
+ def test_route_with_regexp_for_controller
+ rs.draw do |map|
+ map.connect ':controller/:admintoken/:action/:id', :controller => /admin\/.+/
+ map.connect ':controller/:action/:id'
+ end
+ assert_equal({:controller => "admin/user", :admintoken => "foo", :action => "index"},
+ rs.recognize_path("/admin/user/foo"))
+ assert_equal({:controller => "content", :action => "foo"}, rs.recognize_path("/content/foo"))
+ assert_equal '/admin/user/foo', rs.generate(:controller => "admin/user", :admintoken => "foo", :action => "index")
+ assert_equal '/content/foo', rs.generate(:controller => "content", :action => "foo")
+ end
+
+ def test_route_with_regexp_and_dot
+ rs.draw do |map|
+ map.connect ':controller/:action/:file',
+ :controller => /admin|user/,
+ :action => /upload|download/,
+ :defaults => {:file => nil},
+ :requirements => {:file => %r{[^/]+(\.[^/]+)?}}
+ end
+ # Without a file extension
+ assert_equal '/user/download/file',
+ rs.generate(:controller => "user", :action => "download", :file => "file")
+ assert_equal(
+ {:controller => "user", :action => "download", :file => "file"},
+ rs.recognize_path("/user/download/file"))
+
+ # Now, let's try a file with an extension, really a dot (.)
+ assert_equal '/user/download/file.jpg',
+ rs.generate(
+ :controller => "user", :action => "download", :file => "file.jpg")
+ assert_equal(
+ {:controller => "user", :action => "download", :file => "file.jpg"},
+ rs.recognize_path("/user/download/file.jpg"))
+ end
+
+ def test_basic_named_route
+ rs.add_named_route :home, '', :controller => 'content', :action => 'list'
+ x = setup_for_named_route.new
+ assert_equal({:controller => 'content', :action => 'list', :use_route => :home, :only_path => false},
+ x.send(:home_url))
+ end
+
+ def test_named_route_with_option
+ rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page'
+ x = setup_for_named_route.new
+ assert_equal({:controller => 'content', :action => 'show_page', :title => 'new stuff', :use_route => :page, :only_path => false},
+ x.send(:page_url, :title => 'new stuff'))
+ end
+
+ def test_named_route_with_default
+ rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page', :title => 'AboutPage'
+ x = setup_for_named_route.new
+ assert_equal({:controller => 'content', :action => 'show_page', :title => 'AboutPage', :use_route => :page, :only_path => false},
+ x.send(:page_url))
+ assert_equal({:controller => 'content', :action => 'show_page', :title => 'AboutRails', :use_route => :page, :only_path => false},
+ x.send(:page_url, :title => "AboutRails"))
+
+ end
+
+ def test_named_route_with_nested_controller
+ rs.add_named_route :users, 'admin/user', :controller => '/admin/user', :action => 'index'
+ x = setup_for_named_route.new
+ assert_equal({:controller => '/admin/user', :action => 'index', :use_route => :users, :only_path => false},
+ x.send(:users_url))
+ end
+
+ def setup_for_named_route
+ x = Class.new
+ x.send(:define_method, :url_for) {|x| x}
+ rs.named_routes.install(x)
+ x
+ end
+
+ def test_named_route_without_hash
+ rs.draw do |map|
+ map.normal ':controller/:action/:id'
+ end
+ end
+
+ def test_named_route_with_regexps
+ rs.draw do |map|
+ map.article 'page/:year/:month/:day/:title', :controller => 'page', :action => 'show',
+ :year => /\d+/, :month => /\d+/, :day => /\d+/
+ map.connect ':controller/:action/:id'
+ end
+ x = setup_for_named_route.new
+ assert_equal(
+ {:controller => 'page', :action => 'show', :title => 'hi', :use_route => :article, :only_path => false},
+ x.send(:article_url, :title => 'hi')
+ )
+ assert_equal(
+ {:controller => 'page', :action => 'show', :title => 'hi', :day => 10, :year => 2005, :month => 6, :use_route => :article, :only_path => false},
+ x.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6)
+ )
+ end
+
+ def test_changing_controller
+ assert_equal '/admin/stuff/show/10', rs.generate(
+ {:controller => 'stuff', :action => 'show', :id => 10},
+ {:controller => 'admin/user', :action => 'index'}
+ )
+ end
+
+ def test_paths_escaped
+ rs.draw do |map|
+ map.path 'file/*path', :controller => 'content', :action => 'show_file'
+ map.connect ':controller/:action/:id'
+ end
+ results = rs.recognize_path "/file/hello+world/how+are+you%3F"
+ assert results, "Recognition should have succeeded"
+ assert_equal ['hello world', 'how are you?'], results[:path]
+
+ results = rs.recognize_path "/file"
+ assert results, "Recognition should have succeeded"
+ assert_equal [], results[:path]
+ end
+
+ def test_non_controllers_cannot_be_matched
+ rs.draw do |map|
+ map.connect ':controller/:action/:id'
+ end
+ assert_raises(ActionController::RoutingError) { rs.recognize_path("/not_a/show/10") }
+ end
+
+ def test_paths_do_not_accept_defaults
+ assert_raises(ActionController::RoutingError) do
+ rs.draw do |map|
+ map.path 'file/*path', :controller => 'content', :action => 'show_file', :path => %w(fake default)
+ map.connect ':controller/:action/:id'
+ end
+ end
+
+ rs.draw do |map|
+ map.path 'file/*path', :controller => 'content', :action => 'show_file', :path => []
+ map.connect ':controller/:action/:id'
+ end
+ end
+
+ def test_dynamic_path_allowed
+ rs.draw do |map|
+ map.connect '*path', :controller => 'content', :action => 'show_file'
+ end
+
+ assert_equal '/pages/boo', rs.generate(:controller => 'content', :action => 'show_file', :path => %w(pages boo))
+ end
+
+ def test_dynamic_recall_paths_allowed
+ rs.draw do |map|
+ map.connect '*path', :controller => 'content', :action => 'show_file'
+ end
+
+ recall_path = ActionController::Routing::PathSegment::Result.new(%w(pages boo))
+ assert_equal '/pages/boo', rs.generate({}, :controller => 'content', :action => 'show_file', :path => recall_path)
+ end
+
+ def test_backwards
+ rs.draw do |map|
+ map.connect 'page/:id/:action', :controller => 'pages', :action => 'show'
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/page/20', rs.generate({:id => 20}, {:controller => 'pages', :action => 'show'})
+ assert_equal '/page/20', rs.generate(:controller => 'pages', :id => 20, :action => 'show')
+ assert_equal '/pages/boo', rs.generate(:controller => 'pages', :action => 'boo')
+ end
+
+ def test_route_with_fixnum_default
+ rs.draw do |map|
+ map.connect 'page/:id', :controller => 'content', :action => 'show_page', :id => 1
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page')
+ assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page', :id => 1)
+ assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page', :id => '1')
+ assert_equal '/page/10', rs.generate(:controller => 'content', :action => 'show_page', :id => 10)
+
+ assert_equal({:controller => "content", :action => 'show_page', :id => '1'}, rs.recognize_path("/page"))
+ assert_equal({:controller => "content", :action => 'show_page', :id => '1'}, rs.recognize_path("/page/1"))
+ assert_equal({:controller => "content", :action => 'show_page', :id => '10'}, rs.recognize_path("/page/10"))
+ end
+
+ # For newer revision
+ def test_route_with_text_default
+ rs.draw do |map|
+ map.connect 'page/:id', :controller => 'content', :action => 'show_page', :id => 1
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/page/foo', rs.generate(:controller => 'content', :action => 'show_page', :id => 'foo')
+ assert_equal({:controller => "content", :action => 'show_page', :id => 'foo'}, rs.recognize_path("/page/foo"))
+
+ token = "\321\202\320\265\320\272\321\201\321\202" # 'text' in russian
+ escaped_token = CGI::escape(token)
+
+ assert_equal '/page/' + escaped_token, rs.generate(:controller => 'content', :action => 'show_page', :id => token)
+ assert_equal({:controller => "content", :action => 'show_page', :id => token}, rs.recognize_path("/page/#{escaped_token}"))
+ end
+
+ def test_action_expiry
+ assert_equal '/content', rs.generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
+ end
+
+ def test_recognition_with_uppercase_controller_name
+ assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/Content"))
+ assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/ConTent/list"))
+ assert_equal({:controller => "content", :action => 'show', :id => '10'}, rs.recognize_path("/CONTENT/show/10"))
+
+ # these used to work, before the routes rewrite, but support for this was pulled in the new version...
+ #assert_equal({'controller' => "admin/news_feed", 'action' => 'index'}, rs.recognize_path("Admin/NewsFeed"))
+ #assert_equal({'controller' => "admin/news_feed", 'action' => 'index'}, rs.recognize_path("Admin/News_Feed"))
+ end
+
+ def test_requirement_should_prevent_optional_id
+ rs.draw do |map|
+ map.post 'post/:id', :controller=> 'post', :action=> 'show', :requirements => {:id => /\d+/}
+ end
+
+ assert_equal '/post/10', rs.generate(:controller => 'post', :action => 'show', :id => 10)
+
+ assert_raises ActionController::RoutingError do
+ rs.generate(:controller => 'post', :action => 'show')
+ end
+ end
+
+ def test_both_requirement_and_optional
+ rs.draw do |map|
+ map.blog('test/:year', :controller => 'post', :action => 'show',
+ :defaults => { :year => nil },
+ :requirements => { :year => /\d{4}/ }
+ )
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/test', rs.generate(:controller => 'post', :action => 'show')
+ assert_equal '/test', rs.generate(:controller => 'post', :action => 'show', :year => nil)
+
+ x = setup_for_named_route.new
+ assert_equal({:controller => 'post', :action => 'show', :use_route => :blog, :only_path => false},
+ x.send(:blog_url))
+ end
+
+ def test_set_to_nil_forgets
+ rs.draw do |map|
+ map.connect 'pages/:year/:month/:day', :controller => 'content', :action => 'list_pages', :month => nil, :day => nil
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/pages/2005',
+ rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005)
+ assert_equal '/pages/2005/6',
+ rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005, :month => 6)
+ assert_equal '/pages/2005/6/12',
+ rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005, :month => 6, :day => 12)
+
+ assert_equal '/pages/2005/6/4',
+ rs.generate({:day => 4}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+
+ assert_equal '/pages/2005/6',
+ rs.generate({:day => nil}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+
+ assert_equal '/pages/2005',
+ rs.generate({:day => nil, :month => nil}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+ end
+
+ def test_url_with_no_action_specified
+ rs.draw do |map|
+ map.connect '', :controller => 'content'
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/', rs.generate(:controller => 'content', :action => 'index')
+ assert_equal '/', rs.generate(:controller => 'content')
+ end
+
+ def test_named_url_with_no_action_specified
+ rs.draw do |map|
+ map.home '', :controller => 'content'
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/', rs.generate(:controller => 'content', :action => 'index')
+ assert_equal '/', rs.generate(:controller => 'content')
+
+ x = setup_for_named_route.new
+ assert_equal({:controller => 'content', :action => 'index', :use_route => :home, :only_path => false},
+ x.send(:home_url))
+ end
+
+ def test_url_generated_when_forgetting_action
+ [{:controller => 'content', :action => 'index'}, {:controller => 'content'}].each do |hash|
+ rs.draw do |map|
+ map.home '', hash
+ map.connect ':controller/:action/:id'
+ end
+ assert_equal '/', rs.generate({:action => nil}, {:controller => 'content', :action => 'hello'})
+ assert_equal '/', rs.generate({:controller => 'content'})
+ assert_equal '/content/hi', rs.generate({:controller => 'content', :action => 'hi'})
+ end
+ end
+
+ def test_named_route_method
+ rs.draw do |map|
+ map.categories 'categories', :controller => 'content', :action => 'categories'
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/categories', rs.generate(:controller => 'content', :action => 'categories')
+ assert_equal '/content/hi', rs.generate({:controller => 'content', :action => 'hi'})
+ end
+
+ def test_named_routes_array
+ test_named_route_method
+ assert_equal [:categories], rs.named_routes.names
+ end
+
+ def test_nil_defaults
+ rs.draw do |map|
+ map.connect 'journal',
+ :controller => 'content',
+ :action => 'list_journal',
+ :date => nil, :user_id => nil
+ map.connect ':controller/:action/:id'
+ end
+
+ assert_equal '/journal', rs.generate(:controller => 'content', :action => 'list_journal', :date => nil, :user_id => nil)
+ end
+
+ def setup_request_method_routes_for(method)
+ @request = ActionController::TestRequest.new
+ @request.env["REQUEST_METHOD"] = method
+ @request.request_uri = "/match"
+
+ rs.draw do |r|
+ r.connect '/match', :controller => 'books', :action => 'get', :conditions => { :method => :get }
+ r.connect '/match', :controller => 'books', :action => 'post', :conditions => { :method => :post }
+ r.connect '/match', :controller => 'books', :action => 'put', :conditions => { :method => :put }
+ r.connect '/match', :controller => 'books', :action => 'delete', :conditions => { :method => :delete }
+ end
+ end
+
+ %w(GET POST PUT DELETE).each do |request_method|
+ define_method("test_request_method_recognized_with_#{request_method}") do
+ begin
+ Object.const_set(:BooksController, Class.new(ActionController::Base))
+
+ setup_request_method_routes_for(request_method)
+
+ assert_nothing_raised { rs.recognize(@request) }
+ assert_equal request_method.downcase, @request.path_parameters[:action]
+ ensure
+ Object.send(:remove_const, :BooksController) rescue nil
+ end
+ end
+ end
+
+ def test_subpath_recognized
+ Object.const_set(:SubpathBooksController, Class.new(ActionController::Base))
+
+ rs.draw do |r|
+ r.connect '/books/:id;edit', :controller => 'subpath_books', :action => 'edit'
+ r.connect '/items/:id;:action', :controller => 'subpath_books'
+ r.connect '/posts/new;:action', :controller => 'subpath_books'
+ r.connect '/posts/:id', :controller => 'subpath_books', :action => "show"
+ end
+
+ hash = rs.recognize_path "/books/17;edit"
+ assert_not_nil hash
+ assert_equal %w(subpath_books 17 edit), [hash[:controller], hash[:id], hash[:action]]
+
+ hash = rs.recognize_path "/items/3;complete"
+ assert_not_nil hash
+ assert_equal %w(subpath_books 3 complete), [hash[:controller], hash[:id], hash[:action]]
+
+ hash = rs.recognize_path "/posts/new;preview"
+ assert_not_nil hash
+ assert_equal %w(subpath_books preview), [hash[:controller], hash[:action]]
+
+ hash = rs.recognize_path "/posts/7"
+ assert_not_nil hash
+ assert_equal %w(subpath_books show 7), [hash[:controller], hash[:action], hash[:id]]
+ ensure
+ Object.send(:remove_const, :SubpathBooksController) rescue nil
+ end
+
+ def test_subpath_generated
+ Object.const_set(:SubpathBooksController, Class.new(ActionController::Base))
+
+ rs.draw do |r|
+ r.connect '/books/:id;edit', :controller => 'subpath_books', :action => 'edit'
+ r.connect '/items/:id;:action', :controller => 'subpath_books'
+ r.connect '/posts/new;:action', :controller => 'subpath_books'
+ end
+
+ assert_equal "/books/7;edit", rs.generate(:controller => "subpath_books", :id => 7, :action => "edit")
+ assert_equal "/items/15;complete", rs.generate(:controller => "subpath_books", :id => 15, :action => "complete")
+ assert_equal "/posts/new;preview", rs.generate(:controller => "subpath_books", :action => "preview")
+ ensure
+ Object.send(:remove_const, :SubpathBooksController) rescue nil
+ end
+end
+
+class SegmentTest < Test::Unit::TestCase
+
+ def test_first_segment_should_interpolate_for_structure
+ s = ROUTING::Segment.new
+ def s.interpolation_statement(array) 'hello' end
+ assert_equal 'hello', s.continue_string_structure([])
+ end
+
+ def test_interpolation_statement
+ s = ROUTING::StaticSegment.new
+ s.value = "Hello"
+ assert_equal "Hello", eval(s.interpolation_statement([]))
+ assert_equal "HelloHello", eval(s.interpolation_statement([s]))
+
+ s2 = ROUTING::StaticSegment.new
+ s2.value = "-"
+ assert_equal "Hello-Hello", eval(s.interpolation_statement([s, s2]))
+
+ s3 = ROUTING::StaticSegment.new
+ s3.value = "World"
+ assert_equal "Hello-World", eval(s3.interpolation_statement([s, s2]))
+ end
+
+end
+
+class StaticSegmentTest < Test::Unit::TestCase
+
+ def test_interpolation_chunk_should_respect_raw
+ s = ROUTING::StaticSegment.new
+ s.value = 'Hello/World'
+ assert ! s.raw?
+ assert_equal 'Hello/World', CGI.unescape(s.interpolation_chunk)
+
+ s.raw = true
+ assert s.raw?
+ assert_equal 'Hello/World', s.interpolation_chunk
+ end
+
+ def test_regexp_chunk_should_escape_specials
+ s = ROUTING::StaticSegment.new
+
+ s.value = 'Hello*World'
+ assert_equal 'Hello\*World', s.regexp_chunk
+
+ s.value = 'HelloWorld'
+ assert_equal 'HelloWorld', s.regexp_chunk
+ end
+
+ def test_regexp_chunk_should_add_question_mark_for_optionals
+ s = ROUTING::StaticSegment.new
+ s.value = "/"
+ s.is_optional = true
+ assert_equal "/?", s.regexp_chunk
+
+ s.value = "hello"
+ assert_equal "(?:hello)?", s.regexp_chunk
+ end
+
+end
+
+class DynamicSegmentTest < Test::Unit::TestCase
+
+ def segment
+ unless @segment
+ @segment = ROUTING::DynamicSegment.new
+ @segment.key = :a
+ end
+ @segment
+ end
+
+ def test_extract_value
+ s = ROUTING::DynamicSegment.new
+ s.key = :a
+
+ hash = {:a => '10', :b => '20'}
+ assert_equal '10', eval(s.extract_value)
+
+ hash = {:b => '20'}
+ assert_equal nil, eval(s.extract_value)
+
+ s.default = '20'
+ assert_equal '20', eval(s.extract_value)
+ end
+
+ def test_default_local_name
+ assert_equal 'a_value', segment.local_name,
+ "Unexpected name -- all value_check tests will fail!"
+ end
+
+ def test_presence_value_check
+ a_value = 10
+ assert eval(segment.value_check)
+ end
+
+ def test_regexp_value_check_rejects_nil
+ segment.regexp = /\d+/
+ a_value = nil
+ assert ! eval(segment.value_check)
+ end
+
+ def test_optional_regexp_value_check_should_accept_nil
+ segment.regexp = /\d+/
+ segment.is_optional = true
+ a_value = nil
+ assert eval(segment.value_check)
+ end
+
+ def test_regexp_value_check_rejects_no_match
+ segment.regexp = /\d+/
+
+ a_value = "Hello20World"
+ assert ! eval(segment.value_check)
+
+ a_value = "20Hi"
+ assert ! eval(segment.value_check)
+ end
+
+ def test_regexp_value_check_accepts_match
+ segment.regexp = /\d+/
+
+ a_value = "30"
+ assert eval(segment.value_check)
+ end
+
+ def test_value_check_fails_on_nil
+ a_value = nil
+ assert ! eval(segment.value_check)
+ end
+
+ def test_optional_value_needs_no_check
+ segment.is_optional = true
+ a_value = nil
+ assert_equal nil, segment.value_check
+ end
+
+ def test_regexp_value_check_should_accept_match_with_default
+ segment.regexp = /\d+/
+ segment.default = '200'
+
+ a_value = '100'
+ assert eval(segment.value_check)
+ end
+
+ def test_expiry_should_not_trigger_once_expired
+ expired = true
+ hash = merged = {:a => 2, :b => 3}
+ options = {:b => 3}
+ expire_on = Hash.new { raise 'No!!!' }
+
+ eval(segment.expiry_statement)
+ rescue RuntimeError
+ flunk "Expiry check should not have occured!"
+ end
+
+ def test_expiry_should_occur_according_to_expire_on
+ expired = false
+ hash = merged = {:a => 2, :b => 3}
+ options = {:b => 3}
+
+ expire_on = {:b => true, :a => false}
+ eval(segment.expiry_statement)
+ assert !expired
+ assert_equal({:a => 2, :b => 3}, hash)
+
+ expire_on = {:b => true, :a => true}
+ eval(segment.expiry_statement)
+ assert expired
+ assert_equal({:b => 3}, hash)
+ end
+
+ def test_extraction_code_should_return_on_nil
+ hash = merged = {:b => 3}
+ options = {:b => 3}
+ a_value = nil
+
+ # Local jump because of return inside eval.
+ assert_raises(LocalJumpError) { eval(segment.extraction_code) }
+ end
+
+ def test_extraction_code_should_return_on_mismatch
+ segment.regexp = /\d+/
+ hash = merged = {:a => 'Hi', :b => '3'}
+ options = {:b => '3'}
+ a_value = nil
+
+ # Local jump because of return inside eval.
+ assert_raises(LocalJumpError) { eval(segment.extraction_code) }
+ end
+
+ def test_extraction_code_should_accept_value_and_set_local
+ hash = merged = {:a => 'Hi', :b => '3'}
+ options = {:b => '3'}
+ a_value = nil
+ expired = true
+
+ eval(segment.extraction_code)
+ assert_equal 'Hi', a_value
+ end
+
+ def test_extraction_should_work_without_value_check
+ segment.default = 'hi'
+ hash = merged = {:b => '3'}
+ options = {:b => '3'}
+ a_value = nil
+ expired = true
+
+ eval(segment.extraction_code)
+ assert_equal 'hi', a_value
+ end
+
+ def test_extraction_code_should_perform_expiry
+ expired = false
+ hash = merged = {:a => 'Hi', :b => '3'}
+ options = {:b => '3'}
+ expire_on = {:a => true}
+ a_value = nil
+
+ eval(segment.extraction_code)
+ assert_equal 'Hi', a_value
+ assert expired
+ assert_equal options, hash
+ end
+
+ def test_interpolation_chunk_should_replace_value
+ a_value = 'Hi'
+ assert_equal a_value, eval(%("#{segment.interpolation_chunk}"))
+ end
+
+ def test_value_regexp_should_be_nil_without_regexp
+ assert_equal nil, segment.value_regexp
+ end
+
+ def test_value_regexp_should_match_exacly
+ segment.regexp = /\d+/
+ assert_no_match segment.value_regexp, "Hello 10 World"
+ assert_no_match segment.value_regexp, "Hello 10"
+ assert_no_match segment.value_regexp, "10 World"
+ assert_match segment.value_regexp, "10"
+ end
+
+ def test_regexp_chunk_should_return_string
+ segment.regexp = /\d+/
+ assert_kind_of String, segment.regexp_chunk
+ end
+
+end
+
+class ControllerSegmentTest < Test::Unit::TestCase
+
+ def test_regexp_should_only_match_possible_controllers
+ ActionController::Routing.with_controllers %w(admin/accounts admin/users account pages) do
+ cs = ROUTING::ControllerSegment.new :controller
+ regexp = %r{\A#{cs.regexp_chunk}\Z}
+
+ ActionController::Routing.possible_controllers.each do |name|
+ assert_match regexp, name
+ assert_no_match regexp, "#{name}_fake"
+
+ match = regexp.match name
+ assert_equal name, match[1]
+ end
+ end
+ end
+
+end
+
+class RouteTest < Test::Unit::TestCase
+
+ def setup
+ @route = ROUTING::Route.new
+ end
+
+ def slash_segment(is_optional = false)
+ returning ROUTING::DividerSegment.new('/') do |s|
+ s.is_optional = is_optional
+ end
+ end
+
+ def default_route
+ unless @default_route
+ @default_route = ROUTING::Route.new
+
+ @default_route.segments << (s = ROUTING::StaticSegment.new)
+ s.value = '/'
+ s.raw = true
+
+ @default_route.segments << (s = ROUTING::DynamicSegment.new)
+ s.key = :controller
+
+ @default_route.segments << slash_segment(:optional)
+ @default_route.segments << (s = ROUTING::DynamicSegment.new)
+ s.key = :action
+ s.default = 'index'
+ s.is_optional = true
+
+ @default_route.segments << slash_segment(:optional)
+ @default_route.segments << (s = ROUTING::DynamicSegment.new)
+ s.key = :id
+ s.is_optional = true
+
+ @default_route.segments << slash_segment(:optional)
+ end
+ @default_route
+ end
+
+ def test_default_route_recognition
+ expected = {:controller => 'accounts', :action => 'show', :id => '10'}
+ assert_equal expected, default_route.recognize('/accounts/show/10')
+ assert_equal expected, default_route.recognize('/accounts/show/10/')
+
+ expected[:id] = 'jamis'
+ assert_equal expected, default_route.recognize('/accounts/show/jamis/')
+
+ expected.delete :id
+ assert_equal expected, default_route.recognize('/accounts/show')
+ assert_equal expected, default_route.recognize('/accounts/show/')
+
+ expected[:action] = 'index'
+ assert_equal expected, default_route.recognize('/accounts/')
+ assert_equal expected, default_route.recognize('/accounts')
+
+ assert_equal nil, default_route.recognize('/')
+ assert_equal nil, default_route.recognize('/accounts/how/goood/it/is/to/be/free')
+ end
+
+ def test_default_route_should_omit_default_action
+ o = {:controller => 'accounts', :action => 'index'}
+ assert_equal '/accounts', default_route.generate(o, o, {})
+ end
+
+ def test_default_route_should_include_default_action_when_id_present
+ o = {:controller => 'accounts', :action => 'index', :id => '20'}
+ assert_equal '/accounts/index/20', default_route.generate(o, o, {})
+ end
+
+ def test_default_route_should_work_with_action_but_no_id
+ o = {:controller => 'accounts', :action => 'list_all'}
+ assert_equal '/accounts/list_all', default_route.generate(o, o, {})
+ end
+
+ def test_parameter_shell
+ page_url = ROUTING::Route.new
+ page_url.requirements = {:controller => 'pages', :action => 'show', :id => /\d+/}
+ assert_equal({:controller => 'pages', :action => 'show'}, page_url.parameter_shell)
+ end
+
+ def test_defaults
+ route = ROUTING::RouteBuilder.new.build '/users/:id.:format', :controller => "users", :action => "show", :format => "html"
+ assert_equal(
+ { :controller => "users", :action => "show", :format => "html" },
+ route.defaults)
+ end
+
+ def test_builder_complains_without_controller
+ assert_raises(ArgumentError) do
+ ROUTING::RouteBuilder.new.build '/contact', :contoller => "contact", :action => "index"
+ end
+ end
+
+ def test_significant_keys_for_default_route
+ keys = default_route.significant_keys.sort_by {|k| k.to_s }
+ assert_equal [:action, :controller, :id], keys
+ end
+
+ def test_significant_keys
+ user_url = ROUTING::Route.new
+ user_url.segments << (s = ROUTING::StaticSegment.new)
+ s.value = '/'
+ s.raw = true
+
+ user_url.segments << (s = ROUTING::StaticSegment.new)
+ s.value = 'user'
+
+ user_url.segments << (s = ROUTING::StaticSegment.new)
+ s.value = '/'
+ s.raw = true
+ s.is_optional = true
+
+ user_url.segments << (s = ROUTING::DynamicSegment.new)
+ s.key = :user
+
+ user_url.segments << (s = ROUTING::StaticSegment.new)
+ s.value = '/'
+ s.raw = true
+ s.is_optional = true
+
+ user_url.requirements = {:controller => 'users', :action => 'show'}
+
+ keys = user_url.significant_keys.sort_by { |k| k.to_s }
+ assert_equal [:action, :controller, :user], keys
+ end
+
+ def test_build_empty_query_string
+ assert_equal '', @route.build_query_string({})
+ end
+
+ def test_build_query_string_with_nil_value
+ assert_equal '', @route.build_query_string({:x => nil})
+ end
+
+ def test_simple_build_query_string
+ assert_equal '?x=1&y=2', order_query_string(@route.build_query_string(:x => '1', :y => '2'))
+ end
+
+ def test_convert_ints_build_query_string
+ assert_equal '?x=1&y=2', order_query_string(@route.build_query_string(:x => 1, :y => 2))
+ end
+
+ def test_escape_spaces_build_query_string
+ assert_equal '?x=hello+world&y=goodbye+world', order_query_string(@route.build_query_string(:x => 'hello world', :y => 'goodbye world'))
+ end
+
+ def test_expand_array_build_query_string
+ assert_equal '?x[]=1&x[]=2', order_query_string(@route.build_query_string(:x => [1, 2]))
+ end
+
+ def test_escape_spaces_build_query_string_selected_keys
+ assert_equal '?x=hello+world', order_query_string(@route.build_query_string({:x => 'hello world', :y => 'goodbye world'}, [:x]))
+ end
+
+ private
+ def order_query_string(qs)
+ '?' + qs[1..-1].split('&').sort.join('&')
+ end
+end
+
+class RouteBuilderTest < Test::Unit::TestCase
+
+ def builder
+ @builder ||= ROUTING::RouteBuilder.new
+ end
+
+ def build(path, options)
+ builder.build(path, options)
+ end
+
+ def test_options_should_not_be_modified
+ requirements1 = { :id => /\w+/, :controller => /(?:[a-z](?:-?[a-z]+)*)/ }
+ requirements2 = requirements1.dup
+
+ assert_equal requirements1, requirements2
+
+ with_options(:controller => 'folder',
+ :requirements => requirements2) do |m|
+ m.build 'folders/new', :action => 'new'
+ end
+
+ assert_equal requirements1, requirements2
+ end
+
+ def test_segment_for_static
+ segment, rest = builder.segment_for 'ulysses'
+ assert_equal '', rest
+ assert_kind_of ROUTING::StaticSegment, segment
+ assert_equal 'ulysses', segment.value
+ end
+
+ def test_segment_for_action
+ segment, rest = builder.segment_for ':action'
+ assert_equal '', rest
+ assert_kind_of ROUTING::DynamicSegment, segment
+ assert_equal :action, segment.key
+ assert_equal 'index', segment.default
+ end
+
+ def test_segment_for_dynamic
+ segment, rest = builder.segment_for ':login'
+ assert_equal '', rest
+ assert_kind_of ROUTING::DynamicSegment, segment
+ assert_equal :login, segment.key
+ assert_equal nil, segment.default
+ assert ! segment.optional?
+ end
+
+ def test_segment_for_with_rest
+ segment, rest = builder.segment_for ':login/:action'
+ assert_equal :login, segment.key
+ assert_equal '/:action', rest
+ segment, rest = builder.segment_for rest
+ assert_equal '/', segment.value
+ assert_equal ':action', rest
+ segment, rest = builder.segment_for rest
+ assert_equal :action, segment.key
+ assert_equal '', rest
+ end
+
+ def test_segments_for
+ segments = builder.segments_for_route_path '/:controller/:action/:id'
+
+ assert_kind_of ROUTING::DividerSegment, segments[0]
+ assert_equal '/', segments[2].value
+
+ assert_kind_of ROUTING::DynamicSegment, segments[1]
+ assert_equal :controller, segments[1].key
+
+ assert_kind_of ROUTING::DividerSegment, segments[2]
+ assert_equal '/', segments[2].value
+
+ assert_kind_of ROUTING::DynamicSegment, segments[3]
+ assert_equal :action, segments[3].key
+
+ assert_kind_of ROUTING::DividerSegment, segments[4]
+ assert_equal '/', segments[4].value
+
+ assert_kind_of ROUTING::DynamicSegment, segments[5]
+ assert_equal :id, segments[5].key
+ end
+
+ def test_segment_for_action
+ s, r = builder.segment_for(':action/something/else')
+ assert_equal '/something/else', r
+ assert_equal :action, s.key
+ end
+
+ def test_action_default_should_not_trigger_on_prefix
+ s, r = builder.segment_for ':action_name/something/else'
+ assert_equal '/something/else', r
+ assert_equal :action_name, s.key
+ assert_equal nil, s.default
+ end
+
+ def test_divide_route_options
+ segments = builder.segments_for_route_path '/cars/:action/:person/:car/'
+ defaults, requirements = builder.divide_route_options(segments,
+ :action => 'buy', :person => /\w+/, :car => /\w+/,
+ :defaults => {:person => nil, :car => nil}
+ )
+
+ assert_equal({:action => 'buy', :person => nil, :car => nil}, defaults)
+ assert_equal({:person => /\w+/, :car => /\w+/}, requirements)
+ end
+
+ def test_assign_route_options
+ segments = builder.segments_for_route_path '/cars/:action/:person/:car/'
+ defaults = {:action => 'buy', :person => nil, :car => nil}
+ requirements = {:person => /\w+/, :car => /\w+/}
+
+ route_requirements = builder.assign_route_options(segments, defaults, requirements)
+ assert_equal({}, route_requirements)
+
+ assert_equal :action, segments[3].key
+ assert_equal 'buy', segments[3].default
+
+ assert_equal :person, segments[5].key
+ assert_equal %r/\w+/, segments[5].regexp
+ assert segments[5].optional?
+
+ assert_equal :car, segments[7].key
+ assert_equal %r/\w+/, segments[7].regexp
+ assert segments[7].optional?
+ end
+
+ def test_assign_route_options_with_anchor_chars
+ segments = builder.segments_for_route_path '/cars/:action/:person/:car/'
+ defaults = {:action => 'buy', :person => nil, :car => nil}
+ requirements = {:person => /\w+/, :car => /^\w+$/}
+
+ assert_raises ArgumentError do
+ route_requirements = builder.assign_route_options(segments, defaults, requirements)
+ end
+
+ requirements[:car] = /[^\/]+/
+ route_requirements = builder.assign_route_options(segments, defaults, requirements)
+ end
+
+
+ def test_optional_segments_preceding_required_segments
+ segments = builder.segments_for_route_path '/cars/:action/:person/:car/'
+ defaults = {:action => 'buy', :person => nil, :car => "model-t"}
+ assert builder.assign_route_options(segments, defaults, {}).empty?
+
+ 0.upto(1) { |i| assert !segments[i].optional?, "segment #{i} is optional and it shouldn't be" }
+ assert segments[2].optional?
+
+ assert_equal nil, builder.warn_output # should only warn on the :person segment
+ end
+
+ def test_segmentation_of_semicolon_path
+ segments = builder.segments_for_route_path '/books/:id;:action'
+ defaults = { :action => 'show' }
+ assert builder.assign_route_options(segments, defaults, {}).empty?
+ segments.each do |segment|
+ assert ! segment.optional? || segment.key == :action
+ end
+ end
+
+ def test_segmentation_of_dot_path
+ segments = builder.segments_for_route_path '/books/:action.rss'
+ assert builder.assign_route_options(segments, {}, {}).empty?
+ assert_equal 6, segments.length # "/", "books", "/", ":action", ".", "rss"
+ assert !segments.any? { |seg| seg.optional? }
+ end
+
+ def test_segmentation_of_dynamic_dot_path
+ segments = builder.segments_for_route_path '/books/:action.:format'
+ assert builder.assign_route_options(segments, {}, {}).empty?
+ assert_equal 6, segments.length # "/", "books", "/", ":action", ".", ":format"
+ assert !segments.any? { |seg| seg.optional? }
+ assert_kind_of ROUTING::DynamicSegment, segments.last
+ end
+
+ def test_assignment_of_default_options
+ segments = builder.segments_for_route_path '/:controller/:action/:id/'
+ action, id = segments[-4], segments[-2]
+
+ assert_equal :action, action.key
+ assert_equal :id, id.key
+ assert ! action.optional?
+ assert ! id.optional?
+
+ builder.assign_default_route_options(segments)
+
+ assert_equal 'index', action.default
+ assert action.optional?
+ assert id.optional?
+ end
+
+ def test_assignment_of_default_options_respects_existing_defaults
+ segments = builder.segments_for_route_path '/:controller/:action/:id/'
+ action, id = segments[-4], segments[-2]
+
+ assert_equal :action, action.key
+ assert_equal :id, id.key
+ action.default = 'show'
+ action.is_optional = true
+
+ id.default = 'Welcome'
+ id.is_optional = true
+
+ builder.assign_default_route_options(segments)
+
+ assert_equal 'show', action.default
+ assert action.optional?
+ assert_equal 'Welcome', id.default
+ assert id.optional?
+ end
+
+ def test_assignment_of_default_options_respects_regexps
+ segments = builder.segments_for_route_path '/:controller/:action/:id/'
+ action = segments[-4]
+
+ assert_equal :action, action.key
+ action.regexp = /show|in/ # Use 'in' to check partial matches
+
+ builder.assign_default_route_options(segments)
+
+ assert_equal nil, action.default
+ assert ! action.optional?
+ end
+
+ def test_assignment_of_is_optional_when_default
+ segments = builder.segments_for_route_path '/books/:action.rss'
+ assert_equal segments[3].key, :action
+ segments[3].default = 'changes'
+ builder.ensure_required_segments(segments)
+ assert ! segments[3].optional?
+ end
+
+ def test_is_optional_is_assigned_to_default_segments
+ segments = builder.segments_for_route_path '/books/:action'
+ builder.assign_route_options(segments, {:action => 'index'}, {})
+
+ assert_equal segments[3].key, :action
+ assert segments[3].optional?
+ assert_kind_of ROUTING::DividerSegment, segments[2]
+ assert segments[2].optional?
+ end
+
+ # XXX is optional not being set right?
+ # /blah/:defaulted_segment <-- is the second slash optional? it should be.
+
+ def test_route_build
+ ActionController::Routing.with_controllers %w(users pages) do
+ r = builder.build '/:controller/:action/:id/', :action => nil
+
+ [0, 2, 4].each do |i|
+ assert_kind_of ROUTING::DividerSegment, r.segments[i]
+ assert_equal '/', r.segments[i].value
+ assert r.segments[i].optional? if i > 1
+ end
+
+ assert_kind_of ROUTING::DynamicSegment, r.segments[1]
+ assert_equal :controller, r.segments[1].key
+ assert_equal nil, r.segments[1].default
+
+ assert_kind_of ROUTING::DynamicSegment, r.segments[3]
+ assert_equal :action, r.segments[3].key
+ assert_equal 'index', r.segments[3].default
+
+ assert_kind_of ROUTING::DynamicSegment, r.segments[5]
+ assert_equal :id, r.segments[5].key
+ assert r.segments[5].optional?
+ end
+ end
+
+ def test_slashes_are_implied
+ routes = [
+ builder.build('/:controller/:action/:id/', :action => nil),
+ builder.build('/:controller/:action/:id', :action => nil),
+ builder.build(':controller/:action/:id', :action => nil),
+ builder.build('/:controller/:action/:id/', :action => nil)
+ ]
+ expected = routes.first.segments.length
+ routes.each_with_index do |route, i|
+ found = route.segments.length
+ assert_equal expected, found, "Route #{i + 1} has #{found} segments, expected #{expected}"
+ end
+ end
+
+end
+
+class RouteSetTest < Test::Unit::TestCase
+ class MockController
+ attr_accessor :routes
+
+ def initialize(routes)
+ self.routes = routes
+ end
+
+ def url_for(options)
+ only_path = options.delete(:only_path)
+ path = routes.generate(options)
+ only_path ? path : "http://named.route.test#{path}"
+ end
+ end
+
+ class MockRequest
+ attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method
+
+ def initialize(values={})
+ values.each { |key, value| send("#{key}=", value) }
+ if values[:host]
+ subdomain, self.domain = values[:host].split(/\./, 2)
+ self.subdomains = [subdomain]
+ end
+ end
+ end
+
+ def set
+ @set ||= ROUTING::RouteSet.new
+ end
+
+ def request
+ @request ||= MockRequest.new(:host => "named.routes.test", :method => :get)
+ end
+
+ def test_generate_extras
+ set.draw { |m| m.connect ':controller/:action/:id' }
+ path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world")
+ assert_equal "/foo/bar/15", path
+ assert_equal %w(that this), extras.map(&:to_s).sort
+ end
+
+ def test_extra_keys
+ set.draw { |m| m.connect ':controller/:action/:id' }
+ extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world")
+ assert_equal %w(that this), extras.map(&:to_s).sort
+ end
+
+ def test_generate_extras_not_first
+ set.draw do |map|
+ map.connect ':controller/:action/:id.:format'
+ map.connect ':controller/:action/:id'
+ end
+ path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world")
+ assert_equal "/foo/bar/15", path
+ assert_equal %w(that this), extras.map(&:to_s).sort
+ end
+
+ def test_generate_not_first
+ set.draw do |map|
+ map.connect ':controller/:action/:id.:format'
+ map.connect ':controller/:action/:id'
+ end
+ assert_equal "/foo/bar/15?this=hello", set.generate(:controller => "foo", :action => "bar", :id => 15, :this => "hello")
+ end
+
+ def test_extra_keys_not_first
+ set.draw do |map|
+ map.connect ':controller/:action/:id.:format'
+ map.connect ':controller/:action/:id'
+ end
+ extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world")
+ assert_equal %w(that this), extras.map(&:to_s).sort
+ end
+
+ def test_draw
+ assert_equal 0, set.routes.size
+ set.draw do |map|
+ map.connect '/hello/world', :controller => 'a', :action => 'b'
+ end
+ assert_equal 1, set.routes.size
+ end
+
+ def test_named_draw
+ assert_equal 0, set.routes.size
+ set.draw do |map|
+ map.hello '/hello/world', :controller => 'a', :action => 'b'
+ end
+ assert_equal 1, set.routes.size
+ assert_equal set.routes.first, set.named_routes[:hello]
+ end
+
+ def test_later_named_routes_take_precedence
+ set.draw do |map|
+ map.hello '/hello/world', :controller => 'a', :action => 'b'
+ map.hello '/hello', :controller => 'a', :action => 'b'
+ end
+ assert_equal set.routes.last, set.named_routes[:hello]
+ end
+
+ def setup_named_route_test
+ set.draw do |map|
+ map.show '/people/:id', :controller => 'people', :action => 'show'
+ map.index '/people', :controller => 'people', :action => 'index'
+ map.multi '/people/go/:foo/:bar/joe/:id', :controller => 'people', :action => 'multi'
+ map.users '/admin/users', :controller => 'admin/users', :action => 'index'
+ end
+
+ klass = Class.new(MockController)
+ set.named_routes.install(klass)
+ klass.new(set)
+ end
+
+ def test_named_route_hash_access_method
+ controller = setup_named_route_test
+
+ assert_equal(
+ { :controller => 'people', :action => 'show', :id => 5, :use_route => :show, :only_path => false },
+ controller.send(:hash_for_show_url, :id => 5))
+
+ assert_equal(
+ { :controller => 'people', :action => 'index', :use_route => :index, :only_path => false },
+ controller.send(:hash_for_index_url))
+
+ assert_equal(
+ { :controller => 'people', :action => 'show', :id => 5, :use_route => :show, :only_path => true },
+ controller.send(:hash_for_show_path, :id => 5)
+ )
+ end
+
+ def test_named_route_url_method
+ controller = setup_named_route_test
+
+ assert_equal "http://named.route.test/people/5", controller.send(:show_url, :id => 5)
+ assert_equal "/people/5", controller.send(:show_path, :id => 5)
+
+ assert_equal "http://named.route.test/people", controller.send(:index_url)
+ assert_equal "/people", controller.send(:index_path)
+
+ assert_equal "http://named.route.test/admin/users", controller.send(:users_url)
+ assert_equal '/admin/users', controller.send(:users_path)
+ assert_equal '/admin/users', set.generate(controller.send(:hash_for_users_url), {:controller => 'users', :action => 'index'})
+ end
+
+ def test_namd_route_url_method_with_ordered_parameters
+ controller = setup_named_route_test
+ assert_equal "http://named.route.test/people/go/7/hello/joe/5",
+ controller.send(:multi_url, 7, "hello", 5)
+ end
+
+ def test_draw_default_route
+ ActionController::Routing.with_controllers(['users']) do
+ set.draw do |map|
+ map.connect '/:controller/:action/:id'
+ end
+
+ assert_equal 1, set.routes.size
+ route = set.routes.first
+
+ assert route.segments.last.optional?
+
+ assert_equal '/users/show/10', set.generate(:controller => 'users', :action => 'show', :id => 10)
+ assert_equal '/users/index/10', set.generate(:controller => 'users', :id => 10)
+
+ assert_equal({:controller => 'users', :action => 'index', :id => '10'}, set.recognize_path('/users/index/10'))
+ assert_equal({:controller => 'users', :action => 'index', :id => '10'}, set.recognize_path('/users/index/10/'))
+ end
+ end
+
+ def test_draw_default_route_with_default_controller
+ ActionController::Routing.with_controllers(['users']) do
+ set.draw do |map|
+ map.connect '/:controller/:action/:id', :controller => 'users'
+ end
+ assert_equal({:controller => 'users', :action => 'index'}, set.recognize_path('/'))
+ end
+ end
+
+ def test_route_with_parameter_shell
+ ActionController::Routing.with_controllers(['users', 'pages']) do
+ set.draw do |map|
+ map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
+ map.connect '/:controller/:action/:id'
+ end
+
+ assert_equal({:controller => 'pages', :action => 'index'}, set.recognize_path('/pages'))
+ assert_equal({:controller => 'pages', :action => 'index'}, set.recognize_path('/pages/index'))
+ assert_equal({:controller => 'pages', :action => 'list'}, set.recognize_path('/pages/list'))
+
+ assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/pages/show/10'))
+ assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10'))
+ end
+ end
+
+ def test_route_requirements_with_anchor_chars_are_invalid
+ assert_raises ArgumentError do
+ set.draw do |map|
+ map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /^\d+/
+ end
+ end
+ assert_raises ArgumentError do
+ set.draw do |map|
+ map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\A\d+/
+ end
+ end
+ assert_raises ArgumentError do
+ set.draw do |map|
+ map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+$/
+ end
+ end
+ assert_raises ArgumentError do
+ set.draw do |map|
+ map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+\Z/
+ end
+ end
+ assert_raises ArgumentError do
+ set.draw do |map|
+ map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+\z/
+ end
+ end
+ assert_nothing_raised do
+ set.draw do |map|
+ map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /\d+/, :name => /^(david|jamis)/
+ end
+ assert_raises ActionController::RoutingError do
+ set.generate :controller => 'pages', :action => 'show', :id => 10
+ end
+ end
+ end
+
+ def test_non_path_route_requirements_match_all
+ set.draw do |map|
+ map.connect 'page/37s', :controller => 'pages', :action => 'show', :name => /(jamis|david)/
+ end
+ assert_equal '/page/37s', set.generate(:controller => 'pages', :action => 'show', :name => 'jamis')
+ assert_raises ActionController::RoutingError do
+ set.generate(:controller => 'pages', :action => 'show', :name => 'not_jamis')
+ end
+ assert_raises ActionController::RoutingError do
+ set.generate(:controller => 'pages', :action => 'show', :name => 'nor_jamis_and_david')
+ end
+ end
+
+ def test_recognize_with_encoded_id_and_regex
+ set.draw do |map|
+ map.connect 'page/:id', :controller => 'pages', :action => 'show', :id => /[a-zA-Z0-9 ]+/
+ end
+
+ assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10'))
+ assert_equal({:controller => 'pages', :action => 'show', :id => 'hello world'}, set.recognize_path('/page/hello+world'))
+ end
+
+ def test_recognize_with_conditions
+ Object.const_set(:PeopleController, Class.new)
+
+ set.draw do |map|
+ map.with_options(:controller => "people") do |people|
+ people.people "/people", :action => "index", :conditions => { :method => :get }
+ people.connect "/people", :action => "create", :conditions => { :method => :post }
+ people.person "/people/:id", :action => "show", :conditions => { :method => :get }
+ people.connect "/people/:id", :action => "update", :conditions => { :method => :put }
+ people.connect "/people/:id", :action => "destroy", :conditions => { :method => :delete }
+ end
+ end
+
+ request.path = "/people"
+ request.method = :get
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("index", request.path_parameters[:action])
+
+ request.method = :post
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("create", request.path_parameters[:action])
+
+ request.method = :put
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("update", request.path_parameters[:action])
+
+ request.method = :update
+ assert_raises(ActionController::RoutingError) { set.recognize(request) }
+
+ request.path = "/people/5"
+ request.method = :get
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("show", request.path_parameters[:action])
+ assert_equal("5", request.path_parameters[:id])
+
+ request.method = :put
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("update", request.path_parameters[:action])
+ assert_equal("5", request.path_parameters[:id])
+
+ request.method = :delete
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("destroy", request.path_parameters[:action])
+ assert_equal("5", request.path_parameters[:id])
+
+ request.method = :post
+ assert_raises(ActionController::RoutingError) { set.recognize(request) }
+
+ ensure
+ Object.send(:remove_const, :PeopleController)
+ end
+
+ def test_typo_recognition
+ Object.const_set(:ArticlesController, Class.new)
+
+ set.draw do |map|
+ map.connect 'articles/:year/:month/:day/:title',
+ :controller => 'articles', :action => 'permalink',
+ :year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/
+ end
+
+ request.path = "/articles/2005/11/05/a-very-interesting-article"
+ request.method = :get
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("permalink", request.path_parameters[:action])
+ assert_equal("2005", request.path_parameters[:year])
+ assert_equal("11", request.path_parameters[:month])
+ assert_equal("05", request.path_parameters[:day])
+ assert_equal("a-very-interesting-article", request.path_parameters[:title])
+
+ ensure
+ Object.send(:remove_const, :ArticlesController)
+ end
+
+ def test_routing_traversal_does_not_load_extra_classes
+ assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded"
+ set.draw do |map|
+ map.connect '/profile', :controller => 'profile'
+ end
+
+ request.path = '/profile'
+
+ set.recognize(request) rescue nil
+
+ assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded"
+ end
+
+ def test_recognize_with_conditions_and_format
+ Object.const_set(:PeopleController, Class.new)
+
+ set.draw do |map|
+ map.with_options(:controller => "people") do |people|
+ people.person "/people/:id", :action => "show", :conditions => { :method => :get }
+ people.connect "/people/:id", :action => "update", :conditions => { :method => :put }
+ people.connect "/people/:id.:_format", :action => "show", :conditions => { :method => :get }
+ end
+ end
+
+ request.path = "/people/5"
+ request.method = :get
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("show", request.path_parameters[:action])
+ assert_equal("5", request.path_parameters[:id])
+
+ request.method = :put
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("update", request.path_parameters[:action])
+
+ request.path = "/people/5.png"
+ request.method = :get
+ assert_nothing_raised { set.recognize(request) }
+ assert_equal("show", request.path_parameters[:action])
+ assert_equal("5", request.path_parameters[:id])
+ assert_equal("png", request.path_parameters[:_format])
+ ensure
+ Object.send(:remove_const, :PeopleController)
+ end
+
+ def test_deprecation_warning_for_root_route
+ Object.const_set(:PeopleController, Class.new)
+
+ set.draw do |map|
+ assert_deprecated do
+ map.root('', :controller => "people")
+ end
+ end
+ ensure
+ Object.send(:remove_const, :PeopleController)
+ end
+
+ def test_generate_with_default_action
+ set.draw do |map|
+ map.connect "/people", :controller => "people"
+ map.connect "/people/list", :controller => "people", :action => "list"
+ end
+
+ url = set.generate(:controller => "people", :action => "list")
+ assert_equal "/people/list", url
+ end
+
+ def test_generate_finds_best_fit
+ set.draw do |map|
+ map.connect "/people", :controller => "people", :action => "index"
+ map.connect "/ws/people", :controller => "people", :action => "index", :ws => true
+ end
+
+ url = set.generate(:controller => "people", :action => "index", :ws => true)
+ assert_equal "/ws/people", url
+ end
+
+ def test_generate_changes_controller_module
+ set.draw { |map| map.connect ':controller/:action/:id' }
+ current = { :controller => "bling/bloop", :action => "bap", :id => 9 }
+ url = set.generate({:controller => "foo/bar", :action => "baz", :id => 7}, current)
+ assert_equal "/foo/bar/baz/7", url
+ end
+
+ def test_id_is_not_impossibly_sticky
+ set.draw do |map|
+ map.connect 'foo/:number', :controller => "people", :action => "index"
+ map.connect ':controller/:action/:id'
+ end
+
+ url = set.generate({:controller => "people", :action => "index", :number => 3},
+ {:controller => "people", :action => "index", :id => "21"})
+ assert_equal "/foo/3", url
+ end
+
+ def test_id_is_sticky_when_it_ought_to_be
+ set.draw do |map|
+ map.connect ':controller/:id/:action'
+ end
+
+ url = set.generate({:action => "destroy"}, {:controller => "people", :action => "show", :id => "7"})
+ assert_equal "/people/7/destroy", url
+ end
+
+ def test_use_static_path_when_possible
+ set.draw do |map|
+ map.connect 'about', :controller => "welcome", :action => "about"
+ map.connect ':controller/:action/:id'
+ end
+
+ url = set.generate({:controller => "welcome", :action => "about"},
+ {:controller => "welcome", :action => "get", :id => "7"})
+ assert_equal "/about", url
+ end
+
+ def test_generate
+ set.draw { |map| map.connect ':controller/:action/:id' }
+
+ args = { :controller => "foo", :action => "bar", :id => "7", :x => "y" }
+ assert_equal "/foo/bar/7?x=y", set.generate(args)
+ assert_equal ["/foo/bar/7", [:x]], set.generate_extras(args)
+ assert_equal [:x], set.extra_keys(args)
+ end
+
+ def test_named_routes_are_never_relative_to_modules
+ set.draw do |map|
+ map.connect "/connection/manage/:action", :controller => 'connection/manage'
+ map.connect "/connection/connection", :controller => "connection/connection"
+ map.family_connection "/connection", :controller => "connection"
+ end
+
+ url = set.generate({:controller => "connection"}, {:controller => 'connection/manage'})
+ assert_equal "/connection/connection", url
+
+ url = set.generate({:use_route => :family_connection, :controller => "connection"}, {:controller => 'connection/manage'})
+ assert_equal "/connection", url
+ end
+
+ def test_action_left_off_when_id_is_recalled
+ set.draw do |map|
+ map.connect ':controller/:action/:id'
+ end
+ assert_equal '/post', set.generate(
+ {:controller => 'post', :action => 'index'},
+ {:controller => 'post', :action => 'show', :id => '10'}
+ )
+ end
+
+end
+
+class RoutingTest < Test::Unit::TestCase
+
+ def test_possible_controllers
+ true_controller_paths = ActionController::Routing.controller_paths
+
+ ActionController::Routing.use_controllers! nil
+
+ silence_warnings do
+ Object.send(:const_set, :RAILS_ROOT, File.dirname(__FILE__) + '/controller_fixtures')
+ end
+
+ ActionController::Routing.controller_paths = [
+ RAILS_ROOT, RAILS_ROOT + '/app/controllers', RAILS_ROOT + '/vendor/plugins/bad_plugin/lib'
+ ]
+
+ assert_equal ["admin/user", "plugin", "user"], ActionController::Routing.possible_controllers.sort
+ ensure
+ if true_controller_paths
+ ActionController::Routing.controller_paths = true_controller_paths
+ end
+ ActionController::Routing.use_controllers! nil
+ Object.send(:remove_const, :RAILS_ROOT) rescue nil
+ end
+
+ def test_possible_controllers_are_reset_on_each_load
+ true_possible_controllers = ActionController::Routing.possible_controllers
+ true_controller_paths = ActionController::Routing.controller_paths
+
+ ActionController::Routing.use_controllers! nil
+ root = File.dirname(__FILE__) + '/controller_fixtures'
+
+ ActionController::Routing.controller_paths = []
+ assert_equal [], ActionController::Routing.possible_controllers
+
+ ActionController::Routing::Routes.load!
+ ActionController::Routing.controller_paths = [
+ root, root + '/app/controllers', root + '/vendor/plugins/bad_plugin/lib'
+ ]
+
+ assert_equal ["admin/user", "plugin", "user"], ActionController::Routing.possible_controllers.sort
+ ensure
+ ActionController::Routing.controller_paths = true_controller_paths
+ ActionController::Routing.use_controllers! true_possible_controllers
+ Object.send(:remove_const, :RAILS_ROOT) rescue nil
+
+ ActionController::Routing::Routes.clear!
+ ActionController::Routing::Routes.load_routes!
+ end
+
+ def test_with_controllers
+ c = %w(admin/accounts admin/users account pages)
+ ActionController::Routing.with_controllers c do
+ assert_equal c, ActionController::Routing.possible_controllers
+ end
+ end
+
+ def test_normalize_unix_paths
+ load_paths = %w(. config/../app/controllers config/../app//helpers script/../config/../vendor/rails/actionpack/lib vendor/rails/railties/builtin/rails_info app/models lib script/../config/../foo/bar/../../app/models)
+ paths = ActionController::Routing.normalize_paths(load_paths)
+ assert_equal %w(vendor/rails/railties/builtin/rails_info vendor/rails/actionpack/lib app/controllers app/helpers app/models lib .), paths
+ end
+
+ def test_normalize_windows_paths
+ load_paths = %w(. config\\..\\app\\controllers config\\..\\app\\\\helpers script\\..\\config\\..\\vendor\\rails\\actionpack\\lib vendor\\rails\\railties\\builtin\\rails_info app\\models lib script\\..\\config\\..\\foo\\bar\\..\\..\\app\\models)
+ paths = ActionController::Routing.normalize_paths(load_paths)
+ assert_equal %w(vendor\\rails\\railties\\builtin\\rails_info vendor\\rails\\actionpack\\lib app\\controllers app\\helpers app\\models lib .), paths
+ end
+
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/selector_test.rb b/tracks/vendor/rails/actionpack/test/controller/selector_test.rb
new file mode 100644
index 00000000..d0042900
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/selector_test.rb
@@ -0,0 +1,628 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+require File.dirname(__FILE__) + '/../abstract_unit'
+require File.dirname(__FILE__) + '/fake_controllers'
+
+class SelectorTest < Test::Unit::TestCase
+ #
+ # Basic selector: element, id, class, attributes.
+ #
+
+ def test_element
+ parse(%Q{
})
+ # Match element by name.
+ select("div")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # Not case sensitive.
+ select("DIV")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # Universal match (all elements).
+ select("*")
+ assert_equal 3, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal nil, @matches[1].attributes["id"]
+ assert_equal "2", @matches[2].attributes["id"]
+ end
+
+
+ def test_identifier
+ parse(%Q{
})
+ # Match element by ID.
+ select("div#1")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Match element by ID, substitute value.
+ select("div#?", 2)
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Element name does not match ID.
+ select("p#?", 2)
+ assert_equal 0, @matches.size
+ # Use regular expression.
+ select("#?", /\d/)
+ assert_equal 2, @matches.size
+ end
+
+
+ def test_class_name
+ parse(%Q{
})
+ # Match element with specified class.
+ select("div.foo")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Match any element with specified class.
+ select("*.foo")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # Match elements with other class.
+ select("*.bar")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Match only element with both class names.
+ select("*.bar.foo")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ end
+
+
+ def test_attribute
+ parse(%Q{
})
+ # Match element with attribute.
+ select("div[title]")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Match any element with attribute.
+ select("*[title]")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Match alement with attribute value.
+ select("*[title=foo]")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Match alement with attribute and attribute value.
+ select("[bar=foo][title]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Not case sensitive.
+ select("[BAR=foo][TiTle]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ end
+
+
+ def test_attribute_quoted
+ parse(%Q{
})
+ # Match without quotes.
+ select("[title = bar]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Match with single quotes.
+ select("[title = 'bar' ]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Match with double quotes.
+ select("[title = \"bar\" ]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Match with spaces.
+ select("[title = \" bar \" ]")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ end
+
+
+ def test_attribute_equality
+ parse(%Q{
})
+ # Match (fail) complete value.
+ select("[title=bar]")
+ assert_equal 0, @matches.size
+ # Match space-separate word.
+ select("[title~=foo]")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("[title~=bar]")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Match beginning of value.
+ select("[title^=ba]")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Match end of value.
+ select("[title$=ar]")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Match text in value.
+ select("[title*=bar]")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # Match first space separated word.
+ select("[title|=foo]")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("[title|=bar]")
+ assert_equal 0, @matches.size
+ end
+
+
+ #
+ # Selector composition: groups, sibling, children
+ #
+
+
+ def test_selector_group
+ parse(%Q{ })
+ # Simple group selector.
+ select("h1,h3")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("h1 , h3")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Complex group selector.
+ parse(%Q{ })
+ select("h1 a, h3 a")
+ assert_equal 2, @matches.size
+ assert_equal "foo", @matches[0].attributes["href"]
+ assert_equal "baz", @matches[1].attributes["href"]
+ # And now for the three selector challange.
+ parse(%Q{ })
+ select("h1 a, h2 a, h3 a")
+ assert_equal 3, @matches.size
+ assert_equal "foo", @matches[0].attributes["href"]
+ assert_equal "bar", @matches[1].attributes["href"]
+ assert_equal "baz", @matches[2].attributes["href"]
+ end
+
+
+ def test_sibling_selector
+ parse(%Q{ })
+ # Test next sibling.
+ select("h1+*")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ select("h1+h2")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ select("h1+h3")
+ assert_equal 0, @matches.size
+ select("*+h3")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Test any sibling.
+ select("h1~*")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("h2~*")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ end
+
+
+ def test_children_selector
+ parse(%Q{})
+ # Test child selector.
+ select("div>p")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("div>span")
+ assert_equal 0, @matches.size
+ select("div>p#3")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ select("div>p>span")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ # Test descendant selector.
+ select("div p")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("div span")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ select("div *#3")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ select("div *#4")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ # This is here because it failed before when whitespaces
+ # were not properly stripped.
+ select("div .foo")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ end
+
+
+ #
+ # Pseudo selectors: root, nth-child, empty, content, etc
+ #
+
+
+ def test_root_selector
+ parse(%Q{})
+ # Can only find element if it's root.
+ select(":root")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("#1:root")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("#2:root")
+ assert_equal 0, @matches.size
+ # Opposite for nth-child.
+ select("#1:nth-child(1)")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_child_odd_even
+ parse(%Q{})
+ # Test odd nth children.
+ select("tr:nth-child(odd)")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Test even nth children.
+ select("tr:nth-child(even)")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ end
+
+
+ def test_nth_child_a_is_zero
+ parse(%Q{})
+ # Test the third child.
+ select("tr:nth-child(0n+3)")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Same but an can be omitted when zero.
+ select("tr:nth-child(3)")
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ # Second element (but not every second element).
+ select("tr:nth-child(0n+2)")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Before first and past last returns nothing.:
+ assert_raises(ArgumentError) { select("tr:nth-child(-1)") }
+ select("tr:nth-child(0)")
+ assert_equal 0, @matches.size
+ select("tr:nth-child(5)")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_child_a_is_one
+ parse(%Q{})
+ # a is group of one, pick every element in group.
+ select("tr:nth-child(1n+0)")
+ assert_equal 4, @matches.size
+ # Same but a can be omitted when one.
+ select("tr:nth-child(n+0)")
+ assert_equal 4, @matches.size
+ # Same but b can be omitted when zero.
+ select("tr:nth-child(n)")
+ assert_equal 4, @matches.size
+ end
+
+
+ def test_nth_child_b_is_zero
+ parse(%Q{})
+ # If b is zero, pick the n-th element (here each one).
+ select("tr:nth-child(n+0)")
+ assert_equal 4, @matches.size
+ # If b is zero, pick the n-th element (here every second).
+ select("tr:nth-child(2n+0)")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # If a and b are both zero, no element selected.
+ select("tr:nth-child(0n+0)")
+ assert_equal 0, @matches.size
+ select("tr:nth-child(0)")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_child_a_is_negative
+ parse(%Q{})
+ # Since a is -1, picks the first three elements.
+ select("tr:nth-child(-n+3)")
+ assert_equal 3, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ assert_equal "3", @matches[2].attributes["id"]
+ # Since a is -2, picks the first in every second of first four elements.
+ select("tr:nth-child(-2n+3)")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ # Since a is -2, picks the first in every second of first three elements.
+ select("tr:nth-child(-2n+2)")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ end
+
+
+ def test_nth_child_b_is_negative
+ parse(%Q{})
+ # Select last of four.
+ select("tr:nth-child(4n-1)")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ # Select first of four.
+ select("tr:nth-child(4n-4)")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ # Select last of every second.
+ select("tr:nth-child(2n-1)")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ # Select nothing since an+b always < 0
+ select("tr:nth-child(-1n-1)")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_child_substitution_values
+ parse(%Q{})
+ # Test with ?n?.
+ select("tr:nth-child(?n?)", 2, 1)
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "3", @matches[1].attributes["id"]
+ select("tr:nth-child(?n?)", 2, 2)
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ select("tr:nth-child(?n?)", 4, 2)
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ # Test with ? (b only).
+ select("tr:nth-child(?)", 3)
+ assert_equal 1, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ select("tr:nth-child(?)", 5)
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_nth_last_child
+ parse(%Q{})
+ # Last two elements.
+ select("tr:nth-last-child(-n+2)")
+ assert_equal 2, @matches.size
+ assert_equal "3", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ # All old elements counting from last one.
+ select("tr:nth-last-child(odd)")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ end
+
+
+ def test_nth_of_type
+ parse(%Q{})
+ # First two elements.
+ select("tr:nth-of-type(-n+2)")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ # All old elements counting from last one.
+ select("tr:nth-last-of-type(odd)")
+ assert_equal 2, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ assert_equal "4", @matches[1].attributes["id"]
+ end
+
+
+ def test_first_and_last
+ parse(%Q{})
+ # First child.
+ select("tr:first-child")
+ assert_equal 0, @matches.size
+ select(":first-child")
+ assert_equal 1, @matches.size
+ assert_equal "thead", @matches[0].name
+ # First of type.
+ select("tr:first-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ select("thead:first-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "thead", @matches[0].name
+ select("div:first-of-type")
+ assert_equal 0, @matches.size
+ # Last child.
+ select("tr:last-child")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ # Last of type.
+ select("tr:last-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "4", @matches[0].attributes["id"]
+ select("thead:last-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "thead", @matches[0].name
+ select("div:last-of-type")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_first_and_last
+ # Only child.
+ parse(%Q{})
+ select("table:only-child")
+ assert_equal 0, @matches.size
+ select("tr:only-child")
+ assert_equal 1, @matches.size
+ assert_equal "tr", @matches[0].name
+ parse(%Q{})
+ select("tr:only-child")
+ assert_equal 0, @matches.size
+ # Only of type.
+ parse(%Q{})
+ select("thead:only-of-type")
+ assert_equal 1, @matches.size
+ assert_equal "thead", @matches[0].name
+ select("td:only-of-type")
+ assert_equal 0, @matches.size
+ end
+
+
+ def test_empty
+ parse(%Q{})
+ select("table:empty")
+ assert_equal 0, @matches.size
+ select("tr:empty")
+ assert_equal 1, @matches.size
+ parse(%Q{
})
+ select("div:empty")
+ assert_equal 1, @matches.size
+ end
+
+
+ def test_content
+ parse(%Q{
})
+ select("div:content()")
+ assert_equal 1, @matches.size
+ parse(%Q{something
})
+ select("div:content()")
+ assert_equal 0, @matches.size
+ select("div:content(something)")
+ assert_equal 1, @matches.size
+ select("div:content( 'something' )")
+ assert_equal 1, @matches.size
+ select("div:content( \"something\" )")
+ assert_equal 1, @matches.size
+ select("div:content(?)", "something")
+ assert_equal 1, @matches.size
+ select("div:content(?)", /something/)
+ assert_equal 1, @matches.size
+ end
+
+
+ #
+ # Test negation.
+ #
+
+
+ def test_element_negation
+ parse(%Q{
})
+ select("*")
+ assert_equal 2, @matches.size
+ select("*:not(p)")
+ assert_equal 1, @matches.size
+ assert_equal "div", @matches[0].name
+ select("*:not(div)")
+ assert_equal 1, @matches.size
+ assert_equal "p", @matches[0].name
+ select("*:not(span)")
+ assert_equal 2, @matches.size
+ end
+
+
+ def test_id_negation
+ parse(%Q{
})
+ select("p")
+ assert_equal 2, @matches.size
+ select(":not(#1)")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ select(":not(#2)")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ end
+
+
+ def test_class_name_negation
+ parse(%Q{
})
+ select("p")
+ assert_equal 2, @matches.size
+ select(":not(.foo)")
+ assert_equal 1, @matches.size
+ assert_equal "bar", @matches[0].attributes["class"]
+ select(":not(.bar)")
+ assert_equal 1, @matches.size
+ assert_equal "foo", @matches[0].attributes["class"]
+ end
+
+
+ def test_attribute_negation
+ parse(%Q{
})
+ select("p")
+ assert_equal 2, @matches.size
+ select(":not([title=foo])")
+ assert_equal 1, @matches.size
+ assert_equal "bar", @matches[0].attributes["title"]
+ select(":not([title=bar])")
+ assert_equal 1, @matches.size
+ assert_equal "foo", @matches[0].attributes["title"]
+ end
+
+
+ def test_pseudo_class_negation
+ parse(%Q{})
+ select("p")
+ assert_equal 2, @matches.size
+ select("p:not(:first-child)")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ select("p:not(:nth-child(2))")
+ assert_equal 1, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ end
+
+
+ def test_negation_details
+ parse(%Q{
})
+ assert_raises(ArgumentError) { select(":not(") }
+ assert_raises(ArgumentError) { select(":not(:not())") }
+ select("p:not(#1):not(#3)")
+ assert_equal 1, @matches.size
+ assert_equal "2", @matches[0].attributes["id"]
+ end
+
+
+ def test_select_from_element
+ parse(%Q{})
+ select("div")
+ @matches = @matches[0].select("p")
+ assert_equal 2, @matches.size
+ assert_equal "1", @matches[0].attributes["id"]
+ assert_equal "2", @matches[1].attributes["id"]
+ end
+
+
+protected
+
+ def parse(html)
+ @html = HTML::Document.new(html).root
+ end
+
+ def select(*selector)
+ @matches = HTML.selector(*selector).select(@html)
+ end
+
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/send_file_test.rb b/tracks/vendor/rails/actionpack/test/controller/send_file_test.rb
new file mode 100644
index 00000000..83590fd7
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/send_file_test.rb
@@ -0,0 +1,117 @@
+require File.join(File.dirname(__FILE__), '..', 'abstract_unit')
+
+
+module TestFileUtils
+ def file_name() File.basename(__FILE__) end
+ def file_path() File.expand_path(__FILE__) end
+ def file_data() File.open(file_path, 'rb') { |f| f.read } end
+end
+
+
+class SendFileController < ActionController::Base
+ include TestFileUtils
+ layout "layouts/standard" # to make sure layouts don't interfere
+
+ attr_writer :options
+ def options() @options ||= {} end
+
+ def file() send_file(file_path, options) end
+ def data() send_data(file_data, options) end
+
+ def rescue_action(e) raise end
+end
+
+SendFileController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+
+class SendFileTest < Test::Unit::TestCase
+ include TestFileUtils
+
+ def setup
+ @controller = SendFileController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_file_nostream
+ @controller.options = { :stream => false }
+ response = nil
+ assert_nothing_raised { response = process('file') }
+ assert_not_nil response
+ assert_kind_of String, response.body
+ assert_equal file_data, response.body
+ end
+
+ def test_file_stream
+ response = nil
+ assert_nothing_raised { response = process('file') }
+ assert_not_nil response
+ assert_kind_of Proc, response.body
+
+ require 'stringio'
+ output = StringIO.new
+ output.binmode
+ assert_nothing_raised { response.body.call(response, output) }
+ assert_equal file_data, output.string
+ end
+
+ def test_data
+ response = nil
+ assert_nothing_raised { response = process('data') }
+ assert_not_nil response
+
+ assert_kind_of String, response.body
+ assert_equal file_data, response.body
+ end
+
+ def test_headers_after_send_shouldnt_include_charset
+ response = process('data')
+ assert_equal "application/octet-stream", response.headers["Content-Type"]
+
+ response = process('file')
+ assert_equal "application/octet-stream", response.headers["Content-Type"]
+ end
+
+ # Test that send_file_headers! is setting the correct HTTP headers.
+ def test_send_file_headers!
+ options = {
+ :length => 1,
+ :type => 'type',
+ :disposition => 'disposition',
+ :filename => 'filename'
+ }
+
+ # Do it a few times: the resulting headers should be identical
+ # no matter how many times you send with the same options.
+ # Test resolving Ticket #458.
+ @controller.headers = {}
+ @controller.send(:send_file_headers!, options)
+ @controller.send(:send_file_headers!, options)
+ @controller.send(:send_file_headers!, options)
+
+ h = @controller.headers
+ assert_equal 1, h['Content-Length']
+ assert_equal 'type', h['Content-Type']
+ assert_equal 'disposition; filename="filename"', h['Content-Disposition']
+ assert_equal 'binary', h['Content-Transfer-Encoding']
+
+ # test overriding Cache-Control: no-cache header to fix IE open/save dialog
+ @controller.headers = { 'Cache-Control' => 'no-cache' }
+ @controller.send(:send_file_headers!, options)
+ h = @controller.headers
+ assert_equal 'private', h['Cache-Control']
+ end
+
+ %w(file data).each do |method|
+ define_method "test_send_#{method}_status" do
+ @controller.options = { :stream => false, :status => 500 }
+ assert_nothing_raised { assert_not_nil process(method) }
+ assert_equal '500 Internal Server Error', @controller.headers['Status']
+ end
+
+ define_method "test_default_send_#{method}_status" do
+ @controller.options = { :stream => false }
+ assert_nothing_raised { assert_not_nil process(method) }
+ assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @controller.headers['Status']
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/session_management_test.rb b/tracks/vendor/rails/actionpack/test/controller/session_management_test.rb
new file mode 100644
index 00000000..6e100c0c
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/session_management_test.rb
@@ -0,0 +1,145 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class SessionManagementTest < Test::Unit::TestCase
+ class SessionOffController < ActionController::Base
+ session :off
+
+ def show
+ render_text "done"
+ end
+
+ def tell
+ render_text "done"
+ end
+ end
+
+ class TestController < ActionController::Base
+ session :off, :only => :show
+ session :session_secure => true, :except => :show
+ session :off, :only => :conditional,
+ :if => Proc.new { |r| r.parameters[:ws] }
+
+ def show
+ render_text "done"
+ end
+
+ def tell
+ render_text "done"
+ end
+
+ def conditional
+ render_text ">>>#{params[:ws]}<<<"
+ end
+ end
+
+ class SpecializedController < SessionOffController
+ session :disabled => false, :only => :something
+
+ def something
+ render_text "done"
+ end
+
+ def another
+ render_text "done"
+ end
+ end
+
+ class AssociationCachingTestController < ActionController::Base
+ class ObjectWithAssociationCache
+ def initialize
+ @cached_associations = false
+ end
+
+ def fetch_associations
+ @cached_associations = true
+ end
+
+ def clear_association_cache
+ @cached_associations = false
+ end
+
+ def has_cached_associations?
+ @cached_associations
+ end
+ end
+
+ def show
+ session[:object] = ObjectWithAssociationCache.new
+ session[:object].fetch_associations
+ if session[:object].has_cached_associations?
+ render :text => "has cached associations"
+ else
+ render :text => "does not have cached associations"
+ end
+ end
+
+ def tell
+ if session[:object]
+ if session[:object].has_cached_associations?
+ render :text => "has cached associations"
+ else
+ render :text => "does not have cached associations"
+ end
+ else
+ render :text => "there is no object"
+ end
+ end
+ end
+
+
+ def setup
+ @request, @response = ActionController::TestRequest.new,
+ ActionController::TestResponse.new
+ end
+
+ def test_session_off_globally
+ @controller = SessionOffController.new
+ get :show
+ assert_equal false, @request.session_options
+ get :tell
+ assert_equal false, @request.session_options
+ end
+
+ def test_session_off_conditionally
+ @controller = TestController.new
+ get :show
+ assert_equal false, @request.session_options
+ get :tell
+ assert_instance_of Hash, @request.session_options
+ assert @request.session_options[:session_secure]
+ end
+
+ def test_controller_specialization_overrides_settings
+ @controller = SpecializedController.new
+ get :something
+ assert_instance_of Hash, @request.session_options
+ get :another
+ assert_equal false, @request.session_options
+ end
+
+ def test_session_off_with_if
+ @controller = TestController.new
+ get :conditional
+ assert_instance_of Hash, @request.session_options
+ get :conditional, :ws => "ws"
+ assert_equal false, @request.session_options
+ end
+
+ def test_session_store_setting
+ ActionController::Base.session_store = :drb_store
+ assert_equal CGI::Session::DRbStore, ActionController::Base.session_store
+
+ if Object.const_defined?(:ActiveRecord)
+ ActionController::Base.session_store = :active_record_store
+ assert_equal CGI::Session::ActiveRecordStore, ActionController::Base.session_store
+ end
+ end
+
+ def test_process_cleanup_with_session_management_support
+ @controller = AssociationCachingTestController.new
+ get :show
+ assert_equal "has cached associations", @response.body
+ get :tell
+ assert_equal "does not have cached associations", @response.body
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/test_test.rb b/tracks/vendor/rails/actionpack/test/controller/test_test.rb
new file mode 100644
index 00000000..bc2651c2
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/test_test.rb
@@ -0,0 +1,495 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+require File.dirname(__FILE__) + '/fake_controllers'
+
+class TestTest < Test::Unit::TestCase
+ class TestController < ActionController::Base
+ def set_flash
+ flash["test"] = ">#{flash["test"]}<"
+ render :text => 'ignore me'
+ end
+
+ def render_raw_post
+ raise Test::Unit::AssertionFailedError, "#raw_post is blank" if request.raw_post.blank?
+ render :text => request.raw_post
+ end
+
+ def test_params
+ render :text => params.inspect
+ end
+
+ def test_uri
+ render :text => request.request_uri
+ end
+
+ def test_html_output
+ render :text => <
+
+
+
+
+
+
+
+
+HTML
+ end
+
+ def test_only_one_param
+ render :text => (params[:left] && params[:right]) ? "EEP, Both here!" : "OK"
+ end
+
+ def test_remote_addr
+ render :text => (request.remote_addr || "not specified")
+ end
+
+ def test_file_upload
+ render :text => params[:file].size
+ end
+
+ def redirect_to_symbol
+ redirect_to :generate_url, :id => 5
+ end
+
+ def redirect_to_same_controller
+ redirect_to :controller => 'test', :action => 'test_uri', :id => 5
+ end
+
+ def redirect_to_different_controller
+ redirect_to :controller => 'fail', :id => 5
+ end
+
+ def create
+ headers['Location'] = 'created resource'
+ head :created
+ end
+
+ private
+ def rescue_action(e)
+ raise e
+ end
+
+ def generate_url(opts)
+ url_for(opts.merge(:action => "test_uri"))
+ end
+ end
+
+ def setup
+ @controller = TestController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ ActionController::Routing::Routes.reload
+ ActionController::Routing.use_controllers! %w(content admin/user)
+ end
+
+ def teardown
+ ActionController::Routing::Routes.reload
+ end
+
+ def test_raw_post_handling
+ params = {:page => {:name => 'page name'}, 'some key' => 123}
+ get :render_raw_post, params.dup
+
+ raw_post = params.map {|k,v| [CGI::escape(k.to_s), CGI::escape(v.to_s)].join('=')}.sort.join('&')
+ assert_equal raw_post, @response.body
+ end
+
+ def test_process_without_flash
+ process :set_flash
+ assert_equal '><', flash['test']
+ end
+
+ def test_process_with_flash
+ process :set_flash, nil, nil, { "test" => "value" }
+ assert_equal '>value<', flash['test']
+ end
+
+ def test_process_with_request_uri_with_no_params
+ process :test_uri
+ assert_equal "/test_test/test/test_uri", @response.body
+ end
+
+ def test_process_with_request_uri_with_params
+ process :test_uri, :id => 7
+ assert_equal "/test_test/test/test_uri/7", @response.body
+ end
+
+ def test_process_with_request_uri_with_params_with_explicit_uri
+ @request.set_REQUEST_URI "/explicit/uri"
+ process :test_uri, :id => 7
+ assert_equal "/explicit/uri", @response.body
+ end
+
+ def test_multiple_calls
+ process :test_only_one_param, :left => true
+ assert_equal "OK", @response.body
+ process :test_only_one_param, :right => true
+ assert_equal "OK", @response.body
+ end
+
+ def test_assert_tag_tag
+ process :test_html_output
+
+ # there is a 'form' tag
+ assert_tag :tag => 'form'
+ # there is not an 'hr' tag
+ assert_no_tag :tag => 'hr'
+ end
+
+ def test_assert_tag_attributes
+ process :test_html_output
+
+ # there is a tag with an 'id' of 'bar'
+ assert_tag :attributes => { :id => "bar" }
+ # there is no tag with a 'name' of 'baz'
+ assert_no_tag :attributes => { :name => "baz" }
+ end
+
+ def test_assert_tag_parent
+ process :test_html_output
+
+ # there is a tag with a parent 'form' tag
+ assert_tag :parent => { :tag => "form" }
+ # there is no tag with a parent of 'input'
+ assert_no_tag :parent => { :tag => "input" }
+ end
+
+ def test_assert_tag_child
+ process :test_html_output
+
+ # there is a tag with a child 'input' tag
+ assert_tag :child => { :tag => "input" }
+ # there is no tag with a child 'strong' tag
+ assert_no_tag :child => { :tag => "strong" }
+ end
+
+ def test_assert_tag_ancestor
+ process :test_html_output
+
+ # there is a 'li' tag with an ancestor having an id of 'foo'
+ assert_tag :ancestor => { :attributes => { :id => "foo" } }, :tag => "li"
+ # there is no tag of any kind with an ancestor having an href matching 'foo'
+ assert_no_tag :ancestor => { :attributes => { :href => /foo/ } }
+ end
+
+ def test_assert_tag_descendant
+ process :test_html_output
+
+ # there is a tag with a decendant 'li' tag
+ assert_tag :descendant => { :tag => "li" }
+ # there is no tag with a descendant 'html' tag
+ assert_no_tag :descendant => { :tag => "html" }
+ end
+
+ def test_assert_tag_sibling
+ process :test_html_output
+
+ # there is a tag with a sibling of class 'item'
+ assert_tag :sibling => { :attributes => { :class => "item" } }
+ # there is no tag with a sibling 'ul' tag
+ assert_no_tag :sibling => { :tag => "ul" }
+ end
+
+ def test_assert_tag_after
+ process :test_html_output
+
+ # there is a tag following a sibling 'div' tag
+ assert_tag :after => { :tag => "div" }
+ # there is no tag following a sibling tag with id 'bar'
+ assert_no_tag :after => { :attributes => { :id => "bar" } }
+ end
+
+ def test_assert_tag_before
+ process :test_html_output
+
+ # there is a tag preceeding a tag with id 'bar'
+ assert_tag :before => { :attributes => { :id => "bar" } }
+ # there is no tag preceeding a 'form' tag
+ assert_no_tag :before => { :tag => "form" }
+ end
+
+ def test_assert_tag_children_count
+ process :test_html_output
+
+ # there is a tag with 2 children
+ assert_tag :children => { :count => 2 }
+ # in particular, there is a tag with two children (a nameless pair of s)
+ assert_tag :tag => 'ul', :children => { :count => 2 }
+ # there is no tag with 4 children
+ assert_no_tag :children => { :count => 4 }
+ end
+
+ def test_assert_tag_children_less_than
+ process :test_html_output
+
+ # there is a tag with less than 5 children
+ assert_tag :children => { :less_than => 5 }
+ # there is no 'ul' tag with less than 2 children
+ assert_no_tag :children => { :less_than => 2 }, :tag => "ul"
+ end
+
+ def test_assert_tag_children_greater_than
+ process :test_html_output
+
+ # there is a 'body' tag with more than 1 children
+ assert_tag :children => { :greater_than => 1 }, :tag => "body"
+ # there is no tag with more than 10 children
+ assert_no_tag :children => { :greater_than => 10 }
+ end
+
+ def test_assert_tag_children_only
+ process :test_html_output
+
+ # there is a tag containing only one child with an id of 'foo'
+ assert_tag :children => { :count => 1,
+ :only => { :attributes => { :id => "foo" } } }
+ # there is no tag containing only one 'li' child
+ assert_no_tag :children => { :count => 1, :only => { :tag => "li" } }
+ end
+
+ def test_assert_tag_content
+ process :test_html_output
+
+ # the output contains the string "Name"
+ assert_tag :content => /Name/
+ # the output does not contain the string "test"
+ assert_no_tag :content => /test/
+ end
+
+ def test_assert_tag_multiple
+ process :test_html_output
+
+ # there is a 'div', id='bar', with an immediate child whose 'action'
+ # attribute matches the regexp /somewhere/.
+ assert_tag :tag => "div", :attributes => { :id => "bar" },
+ :child => { :attributes => { :action => /somewhere/ } }
+
+ # there is no 'div', id='foo', with a 'ul' child with more than
+ # 2 "li" children.
+ assert_no_tag :tag => "div", :attributes => { :id => "foo" },
+ :child => {
+ :tag => "ul",
+ :children => { :greater_than => 2,
+ :only => { :tag => "li" } } }
+ end
+
+ def test_assert_tag_children_without_content
+ process :test_html_output
+
+ # there is a form tag with an 'input' child which is a self closing tag
+ assert_tag :tag => "form",
+ :children => { :count => 1,
+ :only => { :tag => "input" } }
+
+ # the body tag has an 'a' child which in turn has an 'img' child
+ assert_tag :tag => "body",
+ :children => { :count => 1,
+ :only => { :tag => "a",
+ :children => { :count => 1,
+ :only => { :tag => "img" } } } }
+ end
+
+ def test_assert_tag_attribute_matching
+ @response.body = ' '
+ assert_tag :tag => 'input',
+ :attributes => { :name => /my/, :type => 'text' }
+ assert_no_tag :tag => 'input',
+ :attributes => { :name => 'my', :type => 'text' }
+ assert_no_tag :tag => 'input',
+ :attributes => { :name => /^my$/, :type => 'text' }
+ end
+
+ def test_assert_tag_content_matching
+ @response.body = "hello world
"
+ assert_tag :tag => "p", :content => "hello world"
+ assert_tag :tag => "p", :content => /hello/
+ assert_no_tag :tag => "p", :content => "hello"
+ end
+
+ def test_assert_generates
+ assert_generates 'controller/action/5', :controller => 'controller', :action => 'action', :id => '5'
+ assert_generates 'controller/action/7', {:id => "7"}, {:controller => "controller", :action => "action"}
+ assert_generates 'controller/action/5', {:controller => "controller", :action => "action", :id => "5", :name => "bob"}, {}, {:name => "bob"}
+ assert_generates 'controller/action/7', {:id => "7", :name => "bob"}, {:controller => "controller", :action => "action"}, {:name => "bob"}
+ assert_generates 'controller/action/7', {:id => "7"}, {:controller => "controller", :action => "action", :name => "bob"}, {}
+ end
+
+ def test_assert_routing
+ assert_routing 'content', :controller => 'content', :action => 'index'
+ end
+
+ def test_assert_routing_in_module
+ assert_routing 'admin/user', :controller => 'admin/user', :action => 'index'
+ end
+
+ def test_params_passing
+ get :test_params, :page => {:name => "Page name", :month => '4', :year => '2004', :day => '6'}
+ parsed_params = eval(@response.body)
+ assert_equal(
+ {'controller' => 'test_test/test', 'action' => 'test_params',
+ 'page' => {'name' => "Page name", 'month' => '4', 'year' => '2004', 'day' => '6'}},
+ parsed_params
+ )
+ end
+
+ def test_id_converted_to_string
+ get :test_params, :id => 20, :foo => Object.new
+ assert_kind_of String, @request.path_parameters['id']
+ end
+
+ def test_array_path_parameter_handled_properly
+ with_routing do |set|
+ set.draw do |map|
+ map.connect 'file/*path', :controller => 'test_test/test', :action => 'test_params'
+ map.connect ':controller/:action/:id'
+ end
+
+ get :test_params, :path => ['hello', 'world']
+ assert_equal ['hello', 'world'], @request.path_parameters['path']
+ assert_equal 'hello/world', @request.path_parameters['path'].to_s
+ end
+ end
+
+ def test_assert_realistic_path_parameters
+ get :test_params, :id => 20, :foo => Object.new
+
+ # All elements of path_parameters should use string keys
+ @request.path_parameters.keys.each do |key|
+ assert_kind_of String, key
+ end
+ end
+
+ def test_with_routing_places_routes_back
+ assert ActionController::Routing::Routes
+ routes_id = ActionController::Routing::Routes.object_id
+
+ begin
+ with_routing { raise 'fail' }
+ fail 'Should not be here.'
+ rescue RuntimeError
+ end
+
+ assert ActionController::Routing::Routes
+ assert_equal routes_id, ActionController::Routing::Routes.object_id
+ end
+
+ def test_remote_addr
+ get :test_remote_addr
+ assert_equal "0.0.0.0", @response.body
+
+ @request.remote_addr = "192.0.0.1"
+ get :test_remote_addr
+ assert_equal "192.0.0.1", @response.body
+ end
+
+ def test_header_properly_reset_after_remote_http_request
+ xhr :get, :test_params
+ assert_nil @request.env['HTTP_X_REQUESTED_WITH']
+ end
+
+ def test_header_properly_reset_after_get_request
+ get :test_params
+ @request.recycle!
+ assert_nil @request.instance_variable_get("@request_method")
+ end
+
+ %w(controller response request).each do |variable|
+ %w(get post put delete head process).each do |method|
+ define_method("test_#{variable}_missing_for_#{method}_raises_error") do
+ remove_instance_variable "@#{variable}"
+ begin
+ send(method, :test_remote_addr)
+ assert false, "expected RuntimeError, got nothing"
+ rescue RuntimeError => error
+ assert true
+ assert_match %r{@#{variable} is nil}, error.message
+ rescue => error
+ assert false, "expected RuntimeError, got #{error.class}"
+ end
+ end
+ end
+ end
+
+ FILES_DIR = File.dirname(__FILE__) + '/../fixtures/multipart'
+
+ def test_test_uploaded_file
+ filename = 'mona_lisa.jpg'
+ path = "#{FILES_DIR}/#{filename}"
+ content_type = 'image/png'
+
+ file = ActionController::TestUploadedFile.new(path, content_type)
+ assert_equal filename, file.original_filename
+ assert_equal content_type, file.content_type
+ assert_equal file.path, file.local_path
+ assert_equal File.read(path), file.read
+ end
+
+ def test_fixture_file_upload
+ post :test_file_upload, :file => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")
+ assert_equal 159528, @response.body
+ end
+
+ def test_test_uploaded_file_exception_when_file_doesnt_exist
+ assert_raise(RuntimeError) { ActionController::TestUploadedFile.new('non_existent_file') }
+ end
+
+ def test_assert_redirected_to_symbol
+ with_foo_routing do |set|
+ assert_deprecated(/generate_url.*redirect_to/) do
+ get :redirect_to_symbol
+ end
+ assert_response :redirect
+ assert_redirected_to :generate_url
+ end
+ end
+
+ def test_assert_follow_redirect_to_same_controller
+ with_foo_routing do |set|
+ get :redirect_to_same_controller
+ assert_response :redirect
+ assert_redirected_to :controller => 'test_test/test', :action => 'test_uri', :id => 5
+ assert_nothing_raised { follow_redirect }
+ end
+ end
+
+ def test_assert_follow_redirect_to_different_controller
+ with_foo_routing do |set|
+ get :redirect_to_different_controller
+ assert_response :redirect
+ assert_redirected_to :controller => 'fail', :id => 5
+ assert_raise(RuntimeError) { follow_redirect }
+ end
+ end
+
+ def test_redirect_url_only_cares_about_location_header
+ get :create
+ assert_response :created
+
+ # Redirect url doesn't care that it wasn't a :redirect response.
+ assert_equal 'created resource', @response.redirect_url
+ assert_equal @response.redirect_url, redirect_to_url
+
+ # Must be a :redirect response.
+ assert_raise(Test::Unit::AssertionFailedError) do
+ assert_redirected_to 'created resource'
+ end
+ end
+
+ protected
+ def with_foo_routing
+ with_routing do |set|
+ set.draw do |map|
+ map.generate_url 'foo', :controller => 'test'
+ map.connect ':controller/:action/:id'
+ end
+ yield set
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/url_rewriter_test.rb b/tracks/vendor/rails/actionpack/test/controller/url_rewriter_test.rb
new file mode 100644
index 00000000..882add49
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/url_rewriter_test.rb
@@ -0,0 +1,115 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class UrlRewriterTests < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @params = {}
+ @rewriter = ActionController::UrlRewriter.new(@request, @params)
+ end
+
+ def test_overwrite_params
+ @params[:controller] = 'hi'
+ @params[:action] = 'bye'
+ @params[:id] = '2'
+
+ assert_equal '/hi/hi/2', @rewriter.rewrite(:only_path => true, :overwrite_params => {:action => 'hi'})
+ u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'})
+ assert_match %r(/hi/hi/2$), u
+ end
+
+
+ private
+ def split_query_string(str)
+ [str[0].chr] + str[1..-1].split(/&/).sort
+ end
+
+ def assert_query_equal(q1, q2)
+ assert_equal(split_query_string(q1), split_query_string(q2))
+ end
+end
+
+class UrlWriterTests < Test::Unit::TestCase
+
+ class W
+ include ActionController::UrlWriter
+ end
+
+ def teardown
+ W.default_url_options.clear
+ end
+
+ def add_host!
+ W.default_url_options[:host] = 'www.basecamphq.com'
+ end
+
+ def test_exception_is_thrown_without_host
+ assert_raises RuntimeError do
+ W.new.url_for :controller => 'c', :action => 'a', :id => 'i'
+ end
+ end
+
+ def test_default_host
+ add_host!
+ assert_equal('http://www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i')
+ )
+ end
+
+ def test_host_may_be_overridden
+ add_host!
+ assert_equal('http://37signals.basecamphq.com/c/a/i',
+ W.new.url_for(:host => '37signals.basecamphq.com', :controller => 'c', :action => 'a', :id => 'i')
+ )
+ end
+
+ def test_port
+ add_host!
+ assert_equal('http://www.basecamphq.com:3000/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :port => 3000)
+ )
+ end
+
+ def test_protocol
+ add_host!
+ assert_equal('https://www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
+ )
+ end
+
+ def test_named_route
+ ActionController::Routing::Routes.draw do |map|
+ map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
+ map.connect ':controller/:action/:id'
+ end
+
+ # We need to create a new class in order to install the new named route.
+ kls = Class.new { include ActionController::UrlWriter }
+ controller = kls.new
+ assert controller.respond_to?(:home_url)
+ assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
+ controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
+
+ assert_equal("/home/sweet/home/alabama", controller.send(:home_path, :user => 'alabama', :host => 'unused'))
+ ensure
+ ActionController::Routing::Routes.load!
+ end
+
+ def test_only_path
+ ActionController::Routing::Routes.draw do |map|
+ map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
+ map.connect ':controller/:action/:id'
+ end
+
+ # We need to create a new class in order to install the new named route.
+ kls = Class.new { include ActionController::UrlWriter }
+ controller = kls.new
+ assert controller.respond_to?(:home_url)
+ assert_equal '/brave/new/world',
+ controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true)
+
+ assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true))
+ ensure
+ ActionController::Routing::Routes.load!
+ end
+
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/verification_test.rb b/tracks/vendor/rails/actionpack/test/controller/verification_test.rb
new file mode 100644
index 00000000..05012cf7
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/verification_test.rb
@@ -0,0 +1,227 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class VerificationTest < Test::Unit::TestCase
+ class TestController < ActionController::Base
+ verify :only => :guarded_one, :params => "one",
+ :add_flash => { :error => 'unguarded' },
+ :redirect_to => { :action => "unguarded" }
+
+ verify :only => :guarded_two, :params => %w( one two ),
+ :redirect_to => { :action => "unguarded" }
+
+ verify :only => :guarded_with_flash, :params => "one",
+ :add_flash => { :notice => "prereqs failed" },
+ :redirect_to => { :action => "unguarded" }
+
+ verify :only => :guarded_in_session, :session => "one",
+ :redirect_to => { :action => "unguarded" }
+
+ verify :only => [:multi_one, :multi_two], :session => %w( one two ),
+ :redirect_to => { :action => "unguarded" }
+
+ verify :only => :guarded_by_method, :method => :post,
+ :redirect_to => { :action => "unguarded" }
+
+ verify :only => :guarded_by_xhr, :xhr => true,
+ :redirect_to => { :action => "unguarded" }
+
+ verify :only => :guarded_by_not_xhr, :xhr => false,
+ :redirect_to => { :action => "unguarded" }
+
+ before_filter :unconditional_redirect, :only => :two_redirects
+ verify :only => :two_redirects, :method => :post,
+ :redirect_to => { :action => "unguarded" }
+
+ verify :only => :must_be_post, :method => :post, :render => { :status => 405, :text => "Must be post" }, :add_headers => { "Allow" => "POST" }
+
+ def guarded_one
+ render :text => "#{params[:one]}"
+ end
+
+ def guarded_with_flash
+ render :text => "#{params[:one]}"
+ end
+
+ def guarded_two
+ render :text => "#{params[:one]}:#{params[:two]}"
+ end
+
+ def guarded_in_session
+ render :text => "#{session["one"]}"
+ end
+
+ def multi_one
+ render :text => "#{session["one"]}:#{session["two"]}"
+ end
+
+ def multi_two
+ render :text => "#{session["two"]}:#{session["one"]}"
+ end
+
+ def guarded_by_method
+ render :text => "#{request.method}"
+ end
+
+ def guarded_by_xhr
+ render :text => "#{request.xhr?}"
+ end
+
+ def guarded_by_not_xhr
+ render :text => "#{request.xhr?}"
+ end
+
+ def unguarded
+ render :text => "#{params[:one]}"
+ end
+
+ def two_redirects
+ render :nothing => true
+ end
+
+ def must_be_post
+ render :text => "Was a post!"
+ end
+
+ protected
+ def rescue_action(e) raise end
+
+ def unconditional_redirect
+ redirect_to :action => "unguarded"
+ end
+ end
+
+ def setup
+ @controller = TestController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_guarded_one_with_prereqs
+ get :guarded_one, :one => "here"
+ assert_equal "here", @response.body
+ end
+
+ def test_guarded_one_without_prereqs
+ get :guarded_one
+ assert_redirected_to :action => "unguarded"
+ assert_equal 'unguarded', flash[:error]
+ end
+
+ def test_guarded_with_flash_with_prereqs
+ get :guarded_with_flash, :one => "here"
+ assert_equal "here", @response.body
+ assert flash.empty?
+ end
+
+ def test_guarded_with_flash_without_prereqs
+ get :guarded_with_flash
+ assert_redirected_to :action => "unguarded"
+ assert_equal "prereqs failed", flash[:notice]
+ end
+
+ def test_guarded_two_with_prereqs
+ get :guarded_two, :one => "here", :two => "there"
+ assert_equal "here:there", @response.body
+ end
+
+ def test_guarded_two_without_prereqs_one
+ get :guarded_two, :two => "there"
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_guarded_two_without_prereqs_two
+ get :guarded_two, :one => "here"
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_guarded_two_without_prereqs_both
+ get :guarded_two
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_unguarded_with_params
+ get :unguarded, :one => "here"
+ assert_equal "here", @response.body
+ end
+
+ def test_unguarded_without_params
+ get :unguarded
+ assert_equal "", @response.body
+ end
+
+ def test_guarded_in_session_with_prereqs
+ get :guarded_in_session, {}, "one" => "here"
+ assert_equal "here", @response.body
+ end
+
+ def test_guarded_in_session_without_prereqs
+ get :guarded_in_session
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_multi_one_with_prereqs
+ get :multi_one, {}, "one" => "here", "two" => "there"
+ assert_equal "here:there", @response.body
+ end
+
+ def test_multi_one_without_prereqs
+ get :multi_one
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_multi_two_with_prereqs
+ get :multi_two, {}, "one" => "here", "two" => "there"
+ assert_equal "there:here", @response.body
+ end
+
+ def test_multi_two_without_prereqs
+ get :multi_two
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_guarded_by_method_with_prereqs
+ post :guarded_by_method
+ assert_equal "post", @response.body
+ end
+
+ def test_guarded_by_method_without_prereqs
+ get :guarded_by_method
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_guarded_by_xhr_with_prereqs
+ xhr :post, :guarded_by_xhr
+ assert_equal "true", @response.body
+ end
+
+ def test_guarded_by_xhr_without_prereqs
+ get :guarded_by_xhr
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_guarded_by_not_xhr_with_prereqs
+ get :guarded_by_not_xhr
+ assert_equal "false", @response.body
+ end
+
+ def test_guarded_by_not_xhr_without_prereqs
+ xhr :post, :guarded_by_not_xhr
+ assert_redirected_to :action => "unguarded"
+ end
+
+ def test_guarded_post_and_calls_render_succeeds
+ post :must_be_post
+ assert_equal "Was a post!", @response.body
+ end
+
+ def test_guarded_post_and_calls_render_fails_and_sets_allow_header
+ get :must_be_post
+ assert_response 405
+ assert_equal "Must be post", @response.body
+ assert_equal "POST", @response.headers["Allow"]
+ end
+
+ def test_second_redirect
+ assert_nothing_raised { get :two_redirects }
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/controller/webservice_test.rb b/tracks/vendor/rails/actionpack/test/controller/webservice_test.rb
new file mode 100644
index 00000000..e60e3a84
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/controller/webservice_test.rb
@@ -0,0 +1,248 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+require 'stringio'
+
+class WebServiceTest < Test::Unit::TestCase
+
+ class MockCGI < CGI #:nodoc:
+ attr_accessor :stdinput, :stdoutput, :env_table
+
+ def initialize(env, data = '')
+ self.env_table = env
+ self.stdinput = StringIO.new(data)
+ self.stdoutput = StringIO.new
+ super()
+ end
+ end
+
+
+ class TestController < ActionController::Base
+ session :off
+
+ def assign_parameters
+ if params[:full]
+ render :text => dump_params_keys
+ else
+ render :text => (params.keys - ['controller', 'action']).sort.join(", ")
+ end
+ end
+
+ def dump_params_keys(hash=params)
+ hash.keys.sort.inject("") do |s, k|
+ value = hash[k]
+ value = Hash === value ? "(#{dump_params_keys(value)})" : ""
+ s << ", " unless s.empty?
+ s << "#{k}#{value}"
+ end
+ end
+
+ def rescue_action(e) raise end
+ end
+
+ def setup
+ @controller = TestController.new
+ ActionController::Base.param_parsers.clear
+ ActionController::Base.param_parsers[Mime::XML] = :xml_node
+ end
+
+ def test_check_parameters
+ process('GET')
+ assert_equal '', @controller.response.body
+ end
+
+ def test_post_xml
+ process('POST', 'application/xml', 'content... ')
+
+ assert_equal 'entry', @controller.response.body
+ assert @controller.params.has_key?(:entry)
+ assert_equal 'content...', @controller.params["entry"].summary.node_value
+ assert_equal 'true', @controller.params["entry"]['attributed']
+ end
+
+ def test_put_xml
+ process('PUT', 'application/xml', 'content... ')
+
+ assert_equal 'entry', @controller.response.body
+ assert @controller.params.has_key?(:entry)
+ assert_equal 'content...', @controller.params["entry"].summary.node_value
+ assert_equal 'true', @controller.params["entry"]['attributed']
+ end
+
+ def test_register_and_use_yaml
+ ActionController::Base.param_parsers[Mime::YAML] = Proc.new { |d| YAML.load(d) }
+ process('POST', 'application/x-yaml', {"entry" => "loaded from yaml"}.to_yaml)
+ assert_equal 'entry', @controller.response.body
+ assert @controller.params.has_key?(:entry)
+ assert_equal 'loaded from yaml', @controller.params["entry"]
+ end
+
+ def test_register_and_use_yaml_as_symbol
+ ActionController::Base.param_parsers[Mime::YAML] = :yaml
+ process('POST', 'application/x-yaml', {"entry" => "loaded from yaml"}.to_yaml)
+ assert_equal 'entry', @controller.response.body
+ assert @controller.params.has_key?(:entry)
+ assert_equal 'loaded from yaml', @controller.params["entry"]
+ end
+
+ def test_register_and_use_xml_simple
+ ActionController::Base.param_parsers[Mime::XML] = Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
+ process('POST', 'application/xml', 'content... SimpleXml ' )
+ assert_equal 'summary, title', @controller.response.body
+ assert @controller.params.has_key?(:summary)
+ assert @controller.params.has_key?(:title)
+ assert_equal 'content...', @controller.params["summary"]
+ assert_equal 'SimpleXml', @controller.params["title"]
+ end
+
+ def test_use_xml_ximple_with_empty_request
+ ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+ assert_nothing_raised { process('POST', 'application/xml', "") }
+ assert_equal "", @controller.response.body
+ end
+
+ def test_deprecated_request_methods
+ process('POST', 'application/x-yaml')
+ assert_equal Mime::YAML, @controller.request.content_type
+ assert_equal true, @controller.request.post?
+ assert_equal :yaml, @controller.request.post_format
+ assert_equal true, @controller.request.yaml_post?
+ assert_equal false, @controller.request.xml_post?
+ end
+
+ def test_dasherized_keys_as_xml
+ ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+ process('POST', 'application/xml', "\n... \n ", true)
+ assert_equal 'action, controller, first_key(sub_key), full', @controller.response.body
+ assert_equal "...", @controller.params[:first_key][:sub_key]
+ end
+
+ def test_typecast_as_xml
+ ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+ process('POST', 'application/xml', <<-XML)
+
+ 15
+ false
+ true
+ 2005-03-17
+ 2005-03-17T21:41:07Z
+ unparsed
+ 1
+ hello
+ 1974-07-25
+
+ XML
+ params = @controller.params
+ assert_equal 15, params[:data][:a]
+ assert_equal false, params[:data][:b]
+ assert_equal true, params[:data][:c]
+ assert_equal Date.new(2005,3,17), params[:data][:d]
+ assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e]
+ assert_equal "unparsed", params[:data][:f]
+ assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g]
+ end
+
+ def test_entities_unescaped_as_xml_simple
+ ActionController::Base.param_parsers[Mime::XML] = :xml_simple
+ process('POST', 'application/xml', <<-XML)
+ <foo "bar's" & friends>
+ XML
+ assert_equal %(), @controller.params[:data]
+ end
+
+ def test_typecast_as_yaml
+ ActionController::Base.param_parsers[Mime::YAML] = :yaml
+ process('POST', 'application/x-yaml', <<-YAML)
+ ---
+ data:
+ a: 15
+ b: false
+ c: true
+ d: 2005-03-17
+ e: 2005-03-17T21:41:07Z
+ f: unparsed
+ g:
+ - 1
+ - hello
+ - 1974-07-25
+ YAML
+ params = @controller.params
+ assert_equal 15, params[:data][:a]
+ assert_equal false, params[:data][:b]
+ assert_equal true, params[:data][:c]
+ assert_equal Date.new(2005,3,17), params[:data][:d]
+ assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e]
+ assert_equal "unparsed", params[:data][:f]
+ assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g]
+ end
+
+ private
+
+ def process(verb, content_type = 'application/x-www-form-urlencoded', data = '', full=false)
+
+ cgi = MockCGI.new({
+ 'REQUEST_METHOD' => verb,
+ 'CONTENT_TYPE' => content_type,
+ 'QUERY_STRING' => "action=assign_parameters&controller=webservicetest/test#{"&full=1" if full}",
+ "REQUEST_URI" => "/",
+ "HTTP_HOST" => 'testdomain.com',
+ "CONTENT_LENGTH" => data.size,
+ "SERVER_PORT" => "80",
+ "HTTPS" => "off"}, data)
+
+ @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi))
+ end
+
+end
+
+
+class XmlNodeTest < Test::Unit::TestCase
+ def test_all
+ xn = XmlNode.from_xml(%{
+
+
+ With O'Reilly and Adaptive Path
+
+
+ Staying at the Savoy
+
+
+
+
+
+
+
+
+ }
+ )
+ assert_equal 'UTF-8', xn.node.document.encoding
+ assert_equal '1.0', xn.node.document.version
+ assert_equal 'true', xn['success']
+ assert_equal 'response', xn.node_name
+ assert_equal 'Ajax Summit', xn.page['title']
+ assert_equal '1133', xn.page['id']
+ assert_equal "With O'Reilly and Adaptive Path", xn.page.description.node_value
+ assert_equal nil, xn.nonexistent
+ assert_equal "Staying at the Savoy", xn.page.notes.note.node_value.strip
+ assert_equal 'Technology', xn.page.tags.tag[0]['name']
+ assert_equal 'Travel', xn.page.tags.tag[1][:name]
+ matches = xn.xpath('//@id').map{ |id| id.to_i }
+ assert_equal [4, 5, 1020, 1133], matches.sort
+ matches = xn.xpath('//tag').map{ |tag| tag['name'] }
+ assert_equal ['Technology', 'Travel'], matches.sort
+ assert_equal "Ajax Summit", xn.page['title']
+ xn.page['title'] = 'Ajax Summit V2'
+ assert_equal "Ajax Summit V2", xn.page['title']
+ assert_equal "Staying at the Savoy", xn.page.notes.note.node_value.strip
+ xn.page.notes.note.node_value = "Staying at the Ritz"
+ assert_equal "Staying at the Ritz", xn.page.notes.note.node_value.strip
+ assert_equal '5', xn.page.tags.tag[1][:id]
+ xn.page.tags.tag[1]['id'] = '7'
+ assert_equal '7', xn.page.tags.tag[1]['id']
+ end
+
+
+ def test_small_entry
+ node = XmlNode.from_xml('hi ')
+ assert_equal 'hi', node.node_value
+ end
+
+end
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/addresses/list.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/addresses/list.rhtml
new file mode 100644
index 00000000..c75e01ee
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/addresses/list.rhtml
@@ -0,0 +1 @@
+We only need to get this far!
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/companies.yml b/tracks/vendor/rails/actionpack/test/fixtures/companies.yml
new file mode 100644
index 00000000..707f72ab
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/companies.yml
@@ -0,0 +1,24 @@
+thirty_seven_signals:
+ id: 1
+ name: 37Signals
+ rating: 4
+
+TextDrive:
+ id: 2
+ name: TextDrive
+ rating: 4
+
+PlanetArgon:
+ id: 3
+ name: Planet Argon
+ rating: 4
+
+Google:
+ id: 4
+ name: Google
+ rating: 4
+
+Ionist:
+ id: 5
+ name: Ioni.st
+ rating: 4
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/company.rb b/tracks/vendor/rails/actionpack/test/fixtures/company.rb
new file mode 100644
index 00000000..0d1c29b9
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/company.rb
@@ -0,0 +1,9 @@
+class Company < ActiveRecord::Base
+ attr_protected :rating
+ set_sequence_name :companies_nonstd_seq
+
+ validates_presence_of :name
+ def validate
+ errors.add('rating', 'rating should not be 2') if rating == 2
+ end
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml
new file mode 100644
index 00000000..25dc7468
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml
@@ -0,0 +1 @@
+world
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml
new file mode 100644
index 00000000..c7926d48
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rhtml.rhtml
@@ -0,0 +1 @@
+<%= 'hello world!' %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs b/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs
new file mode 100644
index 00000000..8d614d04
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rjs.rjs
@@ -0,0 +1 @@
+page.alert 'hello world!'
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml b/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml
new file mode 100644
index 00000000..598d62e2
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/content_type/render_default_for_rxml.rxml
@@ -0,0 +1 @@
+xml.p "Hello world!"
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/db_definitions/sqlite.sql b/tracks/vendor/rails/actionpack/test/fixtures/db_definitions/sqlite.sql
new file mode 100644
index 00000000..b4e7539d
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/db_definitions/sqlite.sql
@@ -0,0 +1,42 @@
+CREATE TABLE 'companies' (
+ 'id' INTEGER PRIMARY KEY NOT NULL,
+ 'name' TEXT DEFAULT NULL,
+ 'rating' INTEGER DEFAULT 1
+);
+
+CREATE TABLE 'replies' (
+ 'id' INTEGER PRIMARY KEY NOT NULL,
+ 'content' text,
+ 'created_at' datetime,
+ 'updated_at' datetime,
+ 'topic_id' integer
+);
+
+CREATE TABLE 'topics' (
+ 'id' INTEGER PRIMARY KEY NOT NULL,
+ 'title' varchar(255),
+ 'subtitle' varchar(255),
+ 'content' text,
+ 'created_at' datetime,
+ 'updated_at' datetime
+);
+
+CREATE TABLE 'developers' (
+ 'id' INTEGER PRIMARY KEY NOT NULL,
+ 'name' TEXT DEFAULT NULL,
+ 'salary' INTEGER DEFAULT 70000,
+ 'created_at' DATETIME DEFAULT NULL,
+ 'updated_at' DATETIME DEFAULT NULL
+);
+
+CREATE TABLE 'projects' (
+ 'id' INTEGER PRIMARY KEY NOT NULL,
+ 'name' TEXT DEFAULT NULL
+);
+
+CREATE TABLE 'developers_projects' (
+ 'developer_id' INTEGER NOT NULL,
+ 'project_id' INTEGER NOT NULL,
+ 'joined_on' DATE DEFAULT NULL,
+ 'access_level' INTEGER DEFAULT 1
+);
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml
new file mode 100644
index 00000000..4e8a2d80
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml
@@ -0,0 +1 @@
+<%= @cookies[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml
new file mode 100644
index 00000000..68e88bb7
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml
@@ -0,0 +1 @@
+<%= cookies[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml
new file mode 100644
index 00000000..4b4782b2
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml
@@ -0,0 +1 @@
+<%= @flash[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml
new file mode 100644
index 00000000..f7f9d091
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_flash_method.rhtml
@@ -0,0 +1 @@
+<%= flash[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml
new file mode 100644
index 00000000..1176c93a
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml
@@ -0,0 +1 @@
+<%= @headers[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml
new file mode 100644
index 00000000..308c4eb6
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_headers_method.rhtml
@@ -0,0 +1 @@
+<%= headers[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml
new file mode 100644
index 00000000..1eea6875
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml
@@ -0,0 +1 @@
+<%= @params[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml
new file mode 100644
index 00000000..7e349b4c
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_params_method.rhtml
@@ -0,0 +1 @@
+<%= params[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml
new file mode 100644
index 00000000..a1680c23
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml
@@ -0,0 +1 @@
+<%= @request.method %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml
new file mode 100644
index 00000000..0c74cf1c
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_request_method.rhtml
@@ -0,0 +1 @@
+<%= request.method %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml
new file mode 100644
index 00000000..2f12d2ce
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml
@@ -0,0 +1 @@
+<%= @response.body %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml
new file mode 100644
index 00000000..948c7592
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_response_method.rhtml
@@ -0,0 +1 @@
+<%= response.body %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml
new file mode 100644
index 00000000..3acc1b85
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml
@@ -0,0 +1 @@
+<%= @session[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml
new file mode 100644
index 00000000..a899387c
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/deprecated_instance_variables/_session_method.rhtml
@@ -0,0 +1 @@
+<%= session[:test] %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/developer.rb b/tracks/vendor/rails/actionpack/test/fixtures/developer.rb
new file mode 100644
index 00000000..f5e5b901
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/developer.rb
@@ -0,0 +1,7 @@
+class Developer < ActiveRecord::Base
+ has_and_belongs_to_many :projects
+end
+
+class DeVeLoPeR < ActiveRecord::Base
+ set_table_name "developers"
+end
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/developers.yml b/tracks/vendor/rails/actionpack/test/fixtures/developers.yml
new file mode 100644
index 00000000..308bf75d
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/developers.yml
@@ -0,0 +1,21 @@
+david:
+ id: 1
+ name: David
+ salary: 80000
+
+jamis:
+ id: 2
+ name: Jamis
+ salary: 150000
+
+<% for digit in 3..10 %>
+dev_<%= digit %>:
+ id: <%= digit %>
+ name: fixture_<%= digit %>
+ salary: 100000
+<% end %>
+
+poor_jamis:
+ id: 11
+ name: Jamis
+ salary: 9000
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/developers_projects.yml b/tracks/vendor/rails/actionpack/test/fixtures/developers_projects.yml
new file mode 100644
index 00000000..cee359c7
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/developers_projects.yml
@@ -0,0 +1,13 @@
+david_action_controller:
+ developer_id: 1
+ project_id: 2
+ joined_on: 2004-10-10
+
+david_active_record:
+ developer_id: 1
+ project_id: 1
+ joined_on: 2004-10-10
+
+jamis_active_record:
+ developer_id: 2
+ project_id: 1
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/fun/games/hello_world.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/fun/games/hello_world.rhtml
new file mode 100644
index 00000000..1ebfbe25
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/fun/games/hello_world.rhtml
@@ -0,0 +1 @@
+Living in a nested world
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/helpers/abc_helper.rb b/tracks/vendor/rails/actionpack/test/fixtures/helpers/abc_helper.rb
new file mode 100644
index 00000000..7104ff37
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/helpers/abc_helper.rb
@@ -0,0 +1,5 @@
+module AbcHelper
+ def bare_a() end
+ def bare_b() end
+ def bare_c() end
+end
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/helpers/fun/games_helper.rb b/tracks/vendor/rails/actionpack/test/fixtures/helpers/fun/games_helper.rb
new file mode 100644
index 00000000..bf60d9db
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/helpers/fun/games_helper.rb
@@ -0,0 +1,3 @@
+module Fun::GamesHelper
+ def stratego() "Iz guuut!" end
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/helpers/fun/pdf_helper.rb b/tracks/vendor/rails/actionpack/test/fixtures/helpers/fun/pdf_helper.rb
new file mode 100644
index 00000000..1890f6c9
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/helpers/fun/pdf_helper.rb
@@ -0,0 +1,3 @@
+module Fun::PDFHelper
+ def foobar() 'baz' end
+end
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml
new file mode 100644
index 00000000..5f86a7de
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml
@@ -0,0 +1 @@
+controller_name_space/nested.rhtml <%= yield %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/item.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/item.rhtml
new file mode 100644
index 00000000..1bc7cbda
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/item.rhtml
@@ -0,0 +1 @@
+item.rhtml <%= yield %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml
new file mode 100644
index 00000000..c0f2642b
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml
@@ -0,0 +1 @@
+layout_test.rhtml <%= yield %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab
new file mode 100644
index 00000000..018abfb0
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/layouts/third_party_template_library.mab
@@ -0,0 +1 @@
+Mab
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/views/hello.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/views/hello.rhtml
new file mode 100644
index 00000000..bbccf091
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layout_tests/views/hello.rhtml
@@ -0,0 +1 @@
+hello.rhtml
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layouts/builder.rxml b/tracks/vendor/rails/actionpack/test/fixtures/layouts/builder.rxml
new file mode 100644
index 00000000..729af4b8
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layouts/builder.rxml
@@ -0,0 +1,3 @@
+xml.wrapper do
+ xml << @content_for_layout
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layouts/standard.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/layouts/standard.rhtml
new file mode 100644
index 00000000..368764e6
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layouts/standard.rhtml
@@ -0,0 +1 @@
+<%= @content_for_layout %><%= @variable_for_layout %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layouts/talk_from_action.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/layouts/talk_from_action.rhtml
new file mode 100644
index 00000000..187aab07
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layouts/talk_from_action.rhtml
@@ -0,0 +1,2 @@
+<%= @title || @content_for_title %>
+<%= @content_for_layout -%>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/layouts/yield.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/layouts/yield.rhtml
new file mode 100644
index 00000000..482dc902
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/layouts/yield.rhtml
@@ -0,0 +1,2 @@
+<%= yield :title %>
+<%= yield %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/multipart/binary_file b/tracks/vendor/rails/actionpack/test/fixtures/multipart/binary_file
new file mode 100644
index 00000000..7e4c68c6
Binary files /dev/null and b/tracks/vendor/rails/actionpack/test/fixtures/multipart/binary_file differ
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/multipart/large_text_file b/tracks/vendor/rails/actionpack/test/fixtures/multipart/large_text_file
new file mode 100644
index 00000000..7f97fb1d
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/multipart/large_text_file
@@ -0,0 +1,10 @@
+--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x
+Content-Disposition: form-data; name="file"; filename="file.txt"
+Content-Type: text/plain
+
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+--AaB03x--
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/multipart/mixed_files b/tracks/vendor/rails/actionpack/test/fixtures/multipart/mixed_files
new file mode 100644
index 00000000..5eba7a6b
Binary files /dev/null and b/tracks/vendor/rails/actionpack/test/fixtures/multipart/mixed_files differ
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/multipart/mona_lisa.jpg b/tracks/vendor/rails/actionpack/test/fixtures/multipart/mona_lisa.jpg
new file mode 100644
index 00000000..5cf3bef3
Binary files /dev/null and b/tracks/vendor/rails/actionpack/test/fixtures/multipart/mona_lisa.jpg differ
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/multipart/single_parameter b/tracks/vendor/rails/actionpack/test/fixtures/multipart/single_parameter
new file mode 100644
index 00000000..8962c354
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/multipart/single_parameter
@@ -0,0 +1,5 @@
+--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x--
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/multipart/text_file b/tracks/vendor/rails/actionpack/test/fixtures/multipart/text_file
new file mode 100644
index 00000000..e0367d68
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/multipart/text_file
@@ -0,0 +1,10 @@
+--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x
+Content-Disposition: form-data; name="file"; filename="file.txt"
+Content-Type: text/plain
+
+contents
+--AaB03x--
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/project.rb b/tracks/vendor/rails/actionpack/test/fixtures/project.rb
new file mode 100644
index 00000000..2b53d39e
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/project.rb
@@ -0,0 +1,3 @@
+class Project < ActiveRecord::Base
+ has_and_belongs_to_many :developers, :uniq => true
+end
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/projects.yml b/tracks/vendor/rails/actionpack/test/fixtures/projects.yml
new file mode 100644
index 00000000..02800c78
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/projects.yml
@@ -0,0 +1,7 @@
+action_controller:
+ id: 2
+ name: Active Controller
+
+active_record:
+ id: 1
+ name: Active Record
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/public/images/rails.png b/tracks/vendor/rails/actionpack/test/fixtures/public/images/rails.png
new file mode 100644
index 00000000..b8441f18
Binary files /dev/null and b/tracks/vendor/rails/actionpack/test/fixtures/public/images/rails.png differ
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/replies.yml b/tracks/vendor/rails/actionpack/test/fixtures/replies.yml
new file mode 100644
index 00000000..284c9c07
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/replies.yml
@@ -0,0 +1,13 @@
+witty_retort:
+ id: 1
+ topic_id: 1
+ content: Birdman is better!
+ created_at: <%= 6.hours.ago.to_s(:db) %>
+ updated_at: nil
+
+another:
+ id: 2
+ topic_id: 2
+ content: Nuh uh!
+ created_at: <%= 1.hour.ago.to_s(:db) %>
+ updated_at: nil
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/reply.rb b/tracks/vendor/rails/actionpack/test/fixtures/reply.rb
new file mode 100644
index 00000000..ea84042b
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/reply.rb
@@ -0,0 +1,5 @@
+class Reply < ActiveRecord::Base
+ belongs_to :topic, :include => [:replies]
+
+ validates_presence_of :content
+end
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/all_types_with_layout.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/all_types_with_layout.rhtml
new file mode 100644
index 00000000..84a84049
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/all_types_with_layout.rhtml
@@ -0,0 +1 @@
+HTML for all_types_with_layout
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/all_types_with_layout.rjs b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/all_types_with_layout.rjs
new file mode 100644
index 00000000..b7aec7c5
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/all_types_with_layout.rjs
@@ -0,0 +1 @@
+page << "RJS for all_types_with_layout"
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/layouts/standard.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/layouts/standard.rhtml
new file mode 100644
index 00000000..fcb28ec7
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/layouts/standard.rhtml
@@ -0,0 +1 @@
+<%= @content_for_layout %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rhtml
new file mode 100644
index 00000000..6769dd60
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rhtml
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rjs b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rjs
new file mode 100644
index 00000000..469fcd8e
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rjs
@@ -0,0 +1 @@
+page[:body].visual_effect :highlight
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rxml b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rxml
new file mode 100644
index 00000000..598d62e2
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults.rxml
@@ -0,0 +1 @@
+xml.p "Hello world!"
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rhtml
new file mode 100644
index 00000000..6769dd60
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rhtml
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rjs b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rjs
new file mode 100644
index 00000000..469fcd8e
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rjs
@@ -0,0 +1 @@
+page[:body].visual_effect :highlight
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rxml b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rxml
new file mode 100644
index 00000000..598d62e2
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.rxml
@@ -0,0 +1 @@
+xml.p "Hello world!"
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/scope/test/modgreet.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/scope/test/modgreet.rhtml
new file mode 100644
index 00000000..8947726e
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/scope/test/modgreet.rhtml
@@ -0,0 +1 @@
+Beautiful modules!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/_customer.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/_customer.rhtml
new file mode 100644
index 00000000..872d8c44
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/_customer.rhtml
@@ -0,0 +1 @@
+Hello: <%= customer.name %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/_customer_greeting.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/_customer_greeting.rhtml
new file mode 100644
index 00000000..6acbcb20
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/_customer_greeting.rhtml
@@ -0,0 +1 @@
+<%= greeting %>: <%= customer_greeting.name %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/_hash_object.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/_hash_object.rhtml
new file mode 100644
index 00000000..037a7368
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/_hash_object.rhtml
@@ -0,0 +1 @@
+<%= hash_object[:first_name] %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/_hello.rxml b/tracks/vendor/rails/actionpack/test/fixtures/test/_hello.rxml
new file mode 100644
index 00000000..ef52f632
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/_hello.rxml
@@ -0,0 +1 @@
+xm.hello
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/_partial_only.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/_partial_only.rhtml
new file mode 100644
index 00000000..a44b3eed
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/_partial_only.rhtml
@@ -0,0 +1 @@
+only partial
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/_person.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/_person.rhtml
new file mode 100644
index 00000000..b2e56889
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/_person.rhtml
@@ -0,0 +1,2 @@
+Second: <%= name %>
+Third: <%= @name %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/action_talk_to_layout.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/action_talk_to_layout.rhtml
new file mode 100644
index 00000000..36e896da
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/action_talk_to_layout.rhtml
@@ -0,0 +1,2 @@
+<% @title = "Talking to the layout" -%>
+Action was here!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/block_content_for.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/block_content_for.rhtml
new file mode 100644
index 00000000..95103373
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/block_content_for.rhtml
@@ -0,0 +1,2 @@
+<% block_content_for :title do 'Putting stuff in the title!' end %>
+Great stuff!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/capturing.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/capturing.rhtml
new file mode 100644
index 00000000..1addaa40
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/capturing.rhtml
@@ -0,0 +1,4 @@
+<% days = capture do %>
+ Dreamy days
+<% end %>
+<%= days %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/content_for.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/content_for.rhtml
new file mode 100644
index 00000000..0e47ca8c
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/content_for.rhtml
@@ -0,0 +1,2 @@
+<% content_for :title do %>Putting stuff in the title!<% end %>
+Great stuff!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/delete_with_js.rjs b/tracks/vendor/rails/actionpack/test/fixtures/test/delete_with_js.rjs
new file mode 100644
index 00000000..4b75a955
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/delete_with_js.rjs
@@ -0,0 +1,2 @@
+page.remove 'person'
+page.visual_effect :highlight, "project-#{@project_id}"
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/dot.directory/render_file_with_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/dot.directory/render_file_with_ivar.rhtml
new file mode 100644
index 00000000..8b8a4492
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/dot.directory/render_file_with_ivar.rhtml
@@ -0,0 +1 @@
+The secret is <%= @secret %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/enum_rjs_test.rjs b/tracks/vendor/rails/actionpack/test/fixtures/test/enum_rjs_test.rjs
new file mode 100644
index 00000000..e3004076
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/enum_rjs_test.rjs
@@ -0,0 +1,6 @@
+page.select('.product').each do |value|
+ page.visual_effect :highlight
+ page.visual_effect :highlight, value
+ page.sortable(value, :url => { :action => "order" })
+ page.draggable(value)
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/erb_content_for.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/erb_content_for.rhtml
new file mode 100644
index 00000000..c3bdd136
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/erb_content_for.rhtml
@@ -0,0 +1,2 @@
+<% erb_content_for :title do %>Putting stuff in the title!<% end %>
+Great stuff!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/greeting.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/greeting.rhtml
new file mode 100644
index 00000000..62fb0293
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/greeting.rhtml
@@ -0,0 +1 @@
+This is grand!
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/hello.rxml b/tracks/vendor/rails/actionpack/test/fixtures/test/hello.rxml
new file mode 100644
index 00000000..82a4a310
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/hello.rxml
@@ -0,0 +1,4 @@
+xml.html do
+ xml.p "Hello #{@name}"
+ xml << render_file("test/greeting")
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world.rhtml
new file mode 100644
index 00000000..6769dd60
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world.rhtml
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world.rxml b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world.rxml
new file mode 100644
index 00000000..bffd2191
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world.rxml
@@ -0,0 +1,3 @@
+xml.html do
+ xml.p "Hello"
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml
new file mode 100644
index 00000000..e48d75c4
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world_container.rxml
@@ -0,0 +1,3 @@
+xml.test do
+ render :partial => 'hello', :locals => { :xm => xml }
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world_with_layout_false.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world_with_layout_false.rhtml
new file mode 100644
index 00000000..6769dd60
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_world_with_layout_false.rhtml
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/hello_xml_world.rxml b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_xml_world.rxml
new file mode 100644
index 00000000..02b14fe8
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/hello_xml_world.rxml
@@ -0,0 +1,11 @@
+xml.html do
+ xml.head do
+ xml.title "Hello World"
+ end
+
+ xml.body do
+ xml.p "abes"
+ xml.p "monks"
+ xml.p "wiseguys"
+ end
+end
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/list.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/list.rhtml
new file mode 100644
index 00000000..cd0ab45d
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/list.rhtml
@@ -0,0 +1 @@
+<%= @test_unchanged = 'goodbye' %><%= render_collection_of_partials "customer", @customers %><%= @test_unchanged %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/non_erb_block_content_for.rxml b/tracks/vendor/rails/actionpack/test/fixtures/test/non_erb_block_content_for.rxml
new file mode 100644
index 00000000..6ff6db0f
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/non_erb_block_content_for.rxml
@@ -0,0 +1,4 @@
+content_for :title do
+ 'Putting stuff in the title!'
+end
+xml << "\nGreat stuff!"
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/potential_conflicts.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/potential_conflicts.rhtml
new file mode 100644
index 00000000..a5e964e3
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/potential_conflicts.rhtml
@@ -0,0 +1,4 @@
+First: <%= @name %>
+<%= render :partial => "person", :locals => { :name => "Stephan" } -%>
+Fourth: <%= @name %>
+Fifth: <%= name %>
\ No newline at end of file
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/render_file_with_ivar.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/render_file_with_ivar.rhtml
new file mode 100644
index 00000000..8b8a4492
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/render_file_with_ivar.rhtml
@@ -0,0 +1 @@
+The secret is <%= @secret %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/render_file_with_locals.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/render_file_with_locals.rhtml
new file mode 100644
index 00000000..ebe09fae
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/render_file_with_locals.rhtml
@@ -0,0 +1 @@
+The secret is <%= secret %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/render_to_string_test.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/render_to_string_test.rhtml
new file mode 100644
index 00000000..6e267e86
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/render_to_string_test.rhtml
@@ -0,0 +1 @@
+The value of foo is: ::<%= @foo %>::
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/test/update_element_with_capture.rhtml b/tracks/vendor/rails/actionpack/test/fixtures/test/update_element_with_capture.rhtml
new file mode 100644
index 00000000..fa3ef200
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/test/update_element_with_capture.rhtml
@@ -0,0 +1,9 @@
+<% replacement_function = update_element_function("products", :action => :update) do %>
+ Product 1
+ Product 2
+<% end %>
+<%= javascript_tag(replacement_function) %>
+
+<% update_element_function("status", :action => :update, :binding => binding) do %>
+ You bought something!
+<% end %>
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/topic.rb b/tracks/vendor/rails/actionpack/test/fixtures/topic.rb
new file mode 100644
index 00000000..0beeecf2
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/topic.rb
@@ -0,0 +1,3 @@
+class Topic < ActiveRecord::Base
+ has_many :replies, :include => [:user], :dependent => :destroy
+end
diff --git a/tracks/vendor/rails/actionpack/test/fixtures/topics.yml b/tracks/vendor/rails/actionpack/test/fixtures/topics.yml
new file mode 100644
index 00000000..61ea02d7
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/fixtures/topics.yml
@@ -0,0 +1,22 @@
+futurama:
+ id: 1
+ title: Isnt futurama awesome?
+ subtitle: It really is, isnt it.
+ content: I like futurama
+ created_at: <%= 1.day.ago.to_s(:db) %>
+ updated_at:
+
+harvey_birdman:
+ id: 2
+ title: Harvey Birdman is the king of all men
+ subtitle: yup
+ content: It really is
+ created_at: <%= 2.hours.ago.to_s(:db) %>
+ updated_at:
+
+rails:
+ id: 3
+ title: Rails is nice
+ subtitle: It makes me happy
+ content: except when I have to hack internals to fix pagination. even then really.
+ created_at: <%= 20.minutes.ago.to_s(:db) %>
diff --git a/tracks/vendor/rails/actionpack/test/template/active_record_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/active_record_helper_test.rb
new file mode 100644
index 00000000..8b6a7abd
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/active_record_helper_test.rb
@@ -0,0 +1,196 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+require File.dirname(__FILE__) + '/../../lib/action_view/helpers/date_helper'
+require File.dirname(__FILE__) + '/../../lib/action_view/helpers/form_helper'
+require File.dirname(__FILE__) + '/../../lib/action_view/helpers/text_helper'
+require File.dirname(__FILE__) + '/../../lib/action_view/helpers/tag_helper'
+require File.dirname(__FILE__) + '/../../lib/action_view/helpers/url_helper'
+require File.dirname(__FILE__) + '/../../lib/action_view/helpers/form_tag_helper'
+# require File.dirname(__FILE__) + '/../../lib/action_view/helpers/active_record_helper'
+
+class ActiveRecordHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::FormHelper
+ include ActionView::Helpers::ActiveRecordHelper
+ include ActionView::Helpers::TextHelper
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::UrlHelper
+ include ActionView::Helpers::FormTagHelper
+
+ silence_warnings do
+ Post = Struct.new("Post", :title, :author_name, :body, :secret, :written_on)
+ Post.class_eval do
+ alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast)
+ alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast)
+ alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast)
+ end
+
+ User = Struct.new("User", :email)
+ User.class_eval do
+ alias_method :email_before_type_cast, :email unless respond_to?(:email_before_type_cast)
+ end
+
+ Column = Struct.new("Column", :type, :name, :human_name)
+ end
+
+ def setup_post
+ @post = Post.new
+ def @post.errors
+ Class.new {
+ def on(field) field == "author_name" || field == "body" end
+ def empty?() false end
+ def count() 1 end
+ def full_messages() [ "Author name can't be empty" ] end
+ }.new
+ end
+
+ def @post.new_record?() true end
+ def @post.to_param() nil end
+
+ def @post.column_for_attribute(attr_name)
+ Post.content_columns.select { |column| column.name == attr_name }.first
+ end
+
+ silence_warnings do
+ def Post.content_columns() [ Column.new(:string, "title", "Title"), Column.new(:text, "body", "Body") ] end
+ end
+
+ @post.title = "Hello World"
+ @post.author_name = ""
+ @post.body = "Back to the hill and over it again!"
+ @post.secret = 1
+ @post.written_on = Date.new(2004, 6, 15)
+ end
+
+ def setup_user
+ @user = User.new
+ def @user.errors
+ Class.new {
+ def on(field) field == "email" end
+ def empty?() false end
+ def count() 1 end
+ def full_messages() [ "User email can't be empty" ] end
+ }.new
+ end
+
+ def @user.new_record?() true end
+ def @user.to_param() nil end
+
+ def @user.column_for_attribute(attr_name)
+ User.content_columns.select { |column| column.name == attr_name }.first
+ end
+
+ silence_warnings do
+ def User.content_columns() [ Column.new(:string, "email", "Email") ] end
+ end
+
+ @user.email = ""
+ end
+
+ def setup
+ setup_post
+ setup_user
+
+ @controller = Object.new
+ def @controller.url_for(options, *parameters_for_method_reference)
+ options = options.symbolize_keys
+
+ [options[:action], options[:id].to_param].compact.join('/')
+ end
+ end
+
+ def test_generic_input_tag
+ assert_dom_equal(
+ %( ), input("post", "title")
+ )
+ end
+
+ def test_text_area_with_errors
+ assert_dom_equal(
+ %(
),
+ text_area("post", "body")
+ )
+ end
+
+ def test_text_field_with_errors
+ assert_dom_equal(
+ %(
),
+ text_field("post", "author_name")
+ )
+ end
+
+ def test_form_with_string
+ assert_dom_equal(
+ %(),
+ form("post")
+ )
+
+ silence_warnings do
+ class << @post
+ def new_record?() false end
+ def to_param() id end
+ def id() 1 end
+ end
+ end
+
+ assert_dom_equal(
+ %(Title
\nBody
Back to the hill and over it again!
),
+ form("post")
+ )
+ end
+
+ def test_form_with_date
+ silence_warnings do
+ def Post.content_columns() [ Column.new(:date, "written_on", "Written on") ] end
+ end
+
+ assert_dom_equal(
+ %(Written on \n1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n \n\nJanuary \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n \n\n1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n \n
),
+ form("post")
+ )
+ end
+
+ def test_form_with_datetime
+ silence_warnings do
+ def Post.content_columns() [ Column.new(:datetime, "written_on", "Written on") ] end
+ end
+ @post.written_on = Time.gm(2004, 6, 15, 16, 30)
+
+ assert_dom_equal(
+ %(Written on \n1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n \n\nJanuary \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n \n\n1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n \n — \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n \n : \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n \n
),
+ form("post")
+ )
+ end
+
+ def test_error_for_block
+ assert_dom_equal %(1 error prohibited this post from being saved There were problems with the following fields:
Author name can't be empty ), error_messages_for("post")
+ assert_equal %(1 error prohibited this post from being saved There were problems with the following fields:
Author name can't be empty ), error_messages_for("post", :class => "errorDeathByClass", :id => "errorDeathById", :header_tag => "h1")
+ assert_equal %(1 error prohibited this post from being saved There were problems with the following fields:
Author name can't be empty ), error_messages_for("post", :class => nil, :id => "errorDeathById", :header_tag => "h1")
+ assert_equal %(1 error prohibited this post from being saved There were problems with the following fields:
Author name can't be empty ), error_messages_for("post", :class => "errorDeathByClass", :id => nil, :header_tag => "h1")
+ end
+
+ def test_error_messages_for_handles_nil
+ assert_equal "", error_messages_for("notthere")
+ end
+
+ def test_error_messages_for_many_objects
+ assert_dom_equal %(2 errors prohibited this post from being saved There were problems with the following fields:
Author name can't be empty User email can't be empty ), error_messages_for("post", "user")
+
+ # reverse the order, error order changes and so does the title
+ assert_dom_equal %(2 errors prohibited this user from being saved There were problems with the following fields:
User email can't be empty Author name can't be empty ), error_messages_for("user", "post")
+
+ # add the default to put post back in the title
+ assert_dom_equal %(2 errors prohibited this post from being saved There were problems with the following fields:
User email can't be empty Author name can't be empty ), error_messages_for("user", "post", :object_name => "post")
+
+ # symbols work as well
+ assert_dom_equal %(2 errors prohibited this post from being saved There were problems with the following fields:
User email can't be empty Author name can't be empty ), error_messages_for(:user, :post, :object_name => :post)
+
+ # any default works too
+ assert_dom_equal %(2 errors prohibited this monkey from being saved There were problems with the following fields:
User email can't be empty Author name can't be empty ), error_messages_for(:user, :post, :object_name => "monkey")
+ end
+
+ def test_form_with_string_multipart
+ assert_dom_equal(
+ %(Title
\nBody
Back to the hill and over it again!
),
+ form("post", :multipart => true)
+ )
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/asset_tag_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/asset_tag_helper_test.rb
new file mode 100644
index 00000000..4b941921
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/asset_tag_helper_test.rb
@@ -0,0 +1,271 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class AssetTagHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::UrlHelper
+ include ActionView::Helpers::AssetTagHelper
+
+ def setup
+ @controller = Class.new do
+
+ attr_accessor :request
+
+ def url_for(options, *parameters_for_method_reference)
+ "http://www.example.com"
+ end
+
+ end.new
+
+ @request = Class.new do
+ def relative_url_root
+ ""
+ end
+ end.new
+
+ @controller.request = @request
+
+ ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
+ end
+
+ def teardown
+ Object.send(:remove_const, :RAILS_ROOT) if defined?(RAILS_ROOT)
+ ENV["RAILS_ASSET_ID"] = nil
+ end
+
+ AutoDiscoveryToTag = {
+ %(auto_discovery_link_tag) => %( ),
+ %(auto_discovery_link_tag(:atom)) => %( ),
+ %(auto_discovery_link_tag(:rss, :action => "feed")) => %( ),
+ %(auto_discovery_link_tag(:rss, "http://localhost/feed")) => %( ),
+ %(auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "My RSS"})) => %( ),
+ %(auto_discovery_link_tag(:rss, {}, {:title => "My RSS"})) => %( ),
+ %(auto_discovery_link_tag(nil, {}, {:type => "text/html"})) => %( ),
+ %(auto_discovery_link_tag(nil, {}, {:title => "No stream.. really", :type => "text/html"})) => %( ),
+ %(auto_discovery_link_tag(:rss, {}, {:title => "My RSS", :type => "text/html"})) => %( ),
+ %(auto_discovery_link_tag(:atom, {}, {:rel => "Not so alternate"})) => %( ),
+ }
+
+ JavascriptPathToTag = {
+ %(javascript_path("xmlhr")) => %(/javascripts/xmlhr.js),
+ %(javascript_path("super/xmlhr")) => %(/javascripts/super/xmlhr.js)
+ }
+
+ JavascriptIncludeToTag = {
+ %(javascript_include_tag("xmlhr")) => %(),
+ %(javascript_include_tag("xmlhr", :lang => "vbscript")) => %(),
+ %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(\n),
+ %(javascript_include_tag(:defaults)) => %(\n\n\n),
+ %(javascript_include_tag(:defaults, "test")) => %(\n\n\n\n),
+ %(javascript_include_tag("test", :defaults)) => %(\n\n\n\n)
+ }
+
+ StylePathToTag = {
+ %(stylesheet_path("style")) => %(/stylesheets/style.css),
+ %(stylesheet_path('dir/file')) => %(/stylesheets/dir/file.css),
+ %(stylesheet_path('/dir/file')) => %(/dir/file.css)
+ }
+
+ StyleLinkToTag = {
+ %(stylesheet_link_tag("style")) => %( ),
+ %(stylesheet_link_tag("/dir/file")) => %( ),
+ %(stylesheet_link_tag("dir/file")) => %( ),
+ %(stylesheet_link_tag("style", :media => "all")) => %( ),
+ %(stylesheet_link_tag("random.styles", "/css/stylish")) => %( \n ),
+ %(stylesheet_link_tag("http://www.example.com/styles/style")) => %( )
+ }
+
+ ImagePathToTag = {
+ %(image_path("xml")) => %(/images/xml.png),
+ }
+
+ ImageLinkToTag = {
+ %(image_tag("xml")) => %( ),
+ %(image_tag("rss", :alt => "rss syndication")) => %( ),
+ %(image_tag("gold", :size => "45x70")) => %( ),
+ %(image_tag("symbolize", "size" => "45x70")) => %( ),
+ %(image_tag("http://www.rubyonrails.com/images/rails")) => %( )
+ }
+
+ def test_auto_discovery
+ AutoDiscoveryToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_javascript_path
+ JavascriptPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_javascript_include
+ JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_register_javascript_include_default
+ ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'slider'
+ assert_dom_equal %(\n\n\n\n), javascript_include_tag(:defaults)
+ ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'lib1', '/elsewhere/blub/lib2'
+ assert_dom_equal %(\n\n\n\n\n\n), javascript_include_tag(:defaults)
+ end
+
+ def test_style_path
+ StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_style_link
+ StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_image_path
+ ImagePathToTag.each do |method, tag|
+ assert_deprecated(/image_path/) { assert_dom_equal(tag, eval(method)) }
+ end
+ end
+
+ def test_image_tag
+ ImageLinkToTag.each do |method, tag|
+ assert_deprecated(/image_path/) { assert_dom_equal(tag, eval(method)) }
+ end
+ end
+
+ def test_timebased_asset_id
+ Object.send(:const_set, :RAILS_ROOT, File.dirname(__FILE__) + "/../fixtures/")
+ expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
+ assert_equal %( ), image_tag("rails.png")
+ end
+
+ def test_skipping_asset_id_on_complete_url
+ Object.send(:const_set, :RAILS_ROOT, File.dirname(__FILE__) + "/../fixtures/")
+ assert_equal %( ), image_tag("http://www.example.com/rails.png")
+ end
+
+ def test_preset_asset_id
+ Object.send(:const_set, :RAILS_ROOT, File.dirname(__FILE__) + "/../fixtures/")
+ ENV["RAILS_ASSET_ID"] = "4500"
+ assert_equal %( ), image_tag("rails.png")
+ end
+
+ def test_preset_empty_asset_id
+ Object.send(:const_set, :RAILS_ROOT, File.dirname(__FILE__) + "/../fixtures/")
+ ENV["RAILS_ASSET_ID"] = ""
+ assert_equal %( ), image_tag("rails.png")
+ end
+
+ def test_url_dup_image_tag
+ Object.send(:const_set, :RAILS_ROOT, File.dirname(__FILE__) + "/../fixtures/")
+ img_url = '/images/rails.png'
+ url_copy = img_url.dup
+ image_tag(img_url)
+
+ assert_equal url_copy, img_url
+ end
+end
+
+class AssetTagHelperNonVhostTest < Test::Unit::TestCase
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::UrlHelper
+ include ActionView::Helpers::AssetTagHelper
+
+ def setup
+ @controller = Class.new do
+ attr_accessor :request
+
+ def url_for(options, *parameters_for_method_reference)
+ "http://www.example.com/calloboration/hieraki"
+ end
+ end.new
+
+ @request = Class.new do
+ def relative_url_root
+ "/calloboration/hieraki"
+ end
+ end.new
+
+ @controller.request = @request
+
+ ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
+ end
+
+ AutoDiscoveryToTag = {
+ %(auto_discovery_link_tag(:rss, :action => "feed")) => %( ),
+ %(auto_discovery_link_tag(:atom)) => %( ),
+ %(auto_discovery_link_tag) => %( ),
+ }
+
+ JavascriptPathToTag = {
+ %(javascript_path("xmlhr")) => %(/calloboration/hieraki/javascripts/xmlhr.js),
+ }
+
+ JavascriptIncludeToTag = {
+ %(javascript_include_tag("xmlhr")) => %(),
+ %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(\n),
+ %(javascript_include_tag(:defaults)) => %(\n\n\n)
+ }
+
+ StylePathToTag = {
+ %(stylesheet_path("style")) => %(/calloboration/hieraki/stylesheets/style.css),
+ }
+
+ StyleLinkToTag = {
+ %(stylesheet_link_tag("style")) => %( ),
+ %(stylesheet_link_tag("random.styles", "/css/stylish")) => %( \n )
+ }
+
+ ImagePathToTag = {
+ %(image_path("xml")) => %(/calloboration/hieraki/images/xml.png),
+ }
+
+ ImageLinkToTag = {
+ %(image_tag("xml")) => %( ),
+ %(image_tag("rss", :alt => "rss syndication")) => %( ),
+ %(image_tag("gold", :size => "45x70")) => %( ),
+ %(image_tag("symbolize", "size" => "45x70")) => %( )
+ }
+
+ def test_auto_discovery
+ AutoDiscoveryToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_javascript_path
+ JavascriptPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_javascript_include
+ JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_register_javascript_include_default
+ ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'slider'
+ assert_dom_equal %(\n\n\n\n), javascript_include_tag(:defaults)
+ ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'lib1', '/elsewhere/blub/lib2'
+ assert_dom_equal %(\n\n\n\n\n\n), javascript_include_tag(:defaults)
+ end
+
+ def test_style_path
+ StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_style_link
+ StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_image_path
+ ImagePathToTag.each { |method, tag| assert_deprecated(/image_path/) { assert_dom_equal(tag, eval(method)) } }
+ end
+
+ def test_image_tag
+ ImageLinkToTag.each do |method, tag|
+ assert_deprecated(/image_path/) { assert_dom_equal(tag, eval(method)) }
+ end
+ # Assigning a default alt tag should not cause an exception to be raised
+ assert_nothing_raised { image_tag('') }
+ end
+
+ def test_stylesheet_with_asset_host_already_encoded
+ ActionController::Base.asset_host = "http://foo.example.com"
+ result = stylesheet_link_tag("http://bar.example.com/stylesheets/style.css")
+ assert_dom_equal(
+ %( ),
+ result)
+ ensure
+ ActionController::Base.asset_host = ""
+ end
+
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/benchmark_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/benchmark_helper_test.rb
new file mode 100644
index 00000000..3c7d9b56
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/benchmark_helper_test.rb
@@ -0,0 +1,72 @@
+require 'test/unit'
+require File.dirname(__FILE__) + '/../../lib/action_view/helpers/benchmark_helper'
+
+class BenchmarkHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::BenchmarkHelper
+
+ class MockLogger
+ attr_reader :logged
+
+ def initialize
+ @logged = []
+ end
+
+ def method_missing(method, *args)
+ @logged << [method, args]
+ end
+ end
+
+ def setup
+ @logger = MockLogger.new
+ end
+
+ def test_without_logger_or_block
+ @logger = nil
+ assert_nothing_raised { benchmark }
+ end
+
+ def test_without_block
+ assert_raise(LocalJumpError) { benchmark }
+ assert @logger.logged.empty?
+ end
+
+ def test_without_logger
+ @logger = nil
+ i_was_run = false
+ benchmark { i_was_run = true }
+ assert !i_was_run
+ end
+
+ def test_defaults
+ i_was_run = false
+ benchmark { i_was_run = true }
+ assert i_was_run
+ assert 1, @logger.logged.size
+ assert_last_logged
+ end
+
+ def test_with_message
+ i_was_run = false
+ benchmark('test_run') { i_was_run = true }
+ assert i_was_run
+ assert 1, @logger.logged.size
+ assert_last_logged 'test_run'
+ end
+
+ def test_with_message_and_level
+ i_was_run = false
+ benchmark('debug_run', :debug) { i_was_run = true }
+ assert i_was_run
+ assert 1, @logger.logged.size
+ assert_last_logged 'debug_run', :debug
+ end
+
+ private
+ def assert_last_logged(message = 'Benchmarking', level = :info)
+ last = @logger.logged.last
+ assert 2, last.size
+ assert_equal level, last.first
+ assert 1, last[1].size
+ assert last[1][0] =~ /^#{message} \(.*\)$/
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/compiled_templates_test.rb b/tracks/vendor/rails/actionpack/test/template/compiled_templates_test.rb
new file mode 100644
index 00000000..e46543d6
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/compiled_templates_test.rb
@@ -0,0 +1,135 @@
+require 'test/unit'
+require File.dirname(__FILE__) + '/../../lib/action_view/helpers/date_helper'
+require File.dirname(__FILE__) + '/../../lib/action_view/compiled_templates'
+require File.dirname(__FILE__) + "/../abstract_unit"
+
+class CompiledTemplateTests < Test::Unit::TestCase
+
+ def setup
+ @ct = ActionView::CompiledTemplates.new
+ @v = Class.new
+ @v.send :include, @ct
+ @a = './test_compile_template_a.rhtml'
+ @b = './test_compile_template_b.rhtml'
+ @s = './test_compile_template_link.rhtml'
+ end
+ def teardown
+ [@a, @b, @s].each do |f|
+ `rm #{f}` if File.exist?(f) || File.symlink?(f)
+ end
+ end
+ attr_reader :ct, :v
+
+ def test_name_allocation
+ hi_world = ct.method_names['hi world']
+ hi_sexy = ct.method_names['hi sexy']
+ wish_upon_a_star = ct.method_names['I love seeing decent error messages']
+
+ assert_equal hi_world, ct.method_names['hi world']
+ assert_equal hi_sexy, ct.method_names['hi sexy']
+ assert_equal wish_upon_a_star, ct.method_names['I love seeing decent error messages']
+ assert_equal 3, [hi_world, hi_sexy, wish_upon_a_star].uniq.length
+ end
+
+ def test_wrap_source
+ assert_equal(
+ "def aliased_assignment(value)\nself.value = value\nend",
+ @ct.wrap_source(:aliased_assignment, [:value], 'self.value = value')
+ )
+
+ assert_equal(
+ "def simple()\nnil\nend",
+ @ct.wrap_source(:simple, [], 'nil')
+ )
+ end
+
+ def test_compile_source_single_method
+ selector = ct.compile_source('doubling method', [:a], 'a + a')
+ assert_equal 2, @v.new.send(selector, 1)
+ assert_equal 4, @v.new.send(selector, 2)
+ assert_equal -4, @v.new.send(selector, -2)
+ assert_equal 0, @v.new.send(selector, 0)
+ selector
+ end
+
+ def test_compile_source_two_method
+ sel1 = test_compile_source_single_method # compile the method in the other test
+ sel2 = ct.compile_source('doubling method', [:a, :b], 'a + b + a + b')
+ assert_not_equal sel1, sel2
+
+ assert_equal 2, @v.new.send(sel1, 1)
+ assert_equal 4, @v.new.send(sel1, 2)
+
+ assert_equal 6, @v.new.send(sel2, 1, 2)
+ assert_equal 32, @v.new.send(sel2, 15, 1)
+ end
+
+ def test_mtime
+ t1 = Time.now
+ test_compile_source_single_method
+ assert (t1..Time.now).include?(ct.mtime('doubling method', [:a]))
+ end
+
+ def test_compile_time
+ `echo '#{@a}' > #{@a}; echo '#{@b}' > #{@b}; ln -s #{@a} #{@s}`
+
+ v = ActionView::Base.new
+ v.base_path = '.'
+ v.cache_template_loading = false;
+
+ sleep 1
+ t = Time.now
+ v.compile_and_render_template(:rhtml, '', @a)
+ v.compile_and_render_template(:rhtml, '', @b)
+ v.compile_and_render_template(:rhtml, '', @s)
+ a_n = v.method_names[@a]
+ b_n = v.method_names[@b]
+ s_n = v.method_names[@s]
+ # all of the files have changed since last compile
+ assert v.compile_time[a_n] > t
+ assert v.compile_time[b_n] > t
+ assert v.compile_time[s_n] > t
+
+ sleep 1
+ t = Time.now
+ v.compile_and_render_template(:rhtml, '', @a)
+ v.compile_and_render_template(:rhtml, '', @b)
+ v.compile_and_render_template(:rhtml, '', @s)
+ # none of the files have changed since last compile
+ assert v.compile_time[a_n] < t
+ assert v.compile_time[b_n] < t
+ assert v.compile_time[s_n] < t
+
+ `rm #{@s}; ln -s #{@b} #{@s}`
+ v.compile_and_render_template(:rhtml, '', @a)
+ v.compile_and_render_template(:rhtml, '', @b)
+ v.compile_and_render_template(:rhtml, '', @s)
+ # the symlink has changed since last compile
+ assert v.compile_time[a_n] < t
+ assert v.compile_time[b_n] < t
+ assert v.compile_time[s_n] > t
+
+ sleep 1
+ `touch #{@b}`
+ t = Time.now
+ v.compile_and_render_template(:rhtml, '', @a)
+ v.compile_and_render_template(:rhtml, '', @b)
+ v.compile_and_render_template(:rhtml, '', @s)
+ # the file at the end of the symlink has changed since last compile
+ # both the symlink and the file at the end of it should be recompiled
+ assert v.compile_time[a_n] < t
+ assert v.compile_time[b_n] > t
+ assert v.compile_time[s_n] > t
+ end
+end
+
+module ActionView
+ class Base
+ def compile_time
+ @@compile_time
+ end
+ def method_names
+ @@method_names
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/date_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/date_helper_test.rb
new file mode 100755
index 00000000..c03ba69f
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/date_helper_test.rb
@@ -0,0 +1,1279 @@
+require 'test/unit'
+require File.dirname(__FILE__) + "/../abstract_unit"
+
+class DateHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::DateHelper
+ include ActionView::Helpers::FormHelper
+
+ silence_warnings do
+ Post = Struct.new("Post", :id, :written_on, :updated_at)
+ Post.class_eval do
+ def id
+ 123
+ end
+ def id_before_type_cast
+ 123
+ end
+ end
+ end
+
+ def test_distance_in_words
+ from = Time.mktime(2004, 3, 6, 21, 45, 0)
+
+ # 0..1 with include_seconds
+ assert_equal "less than 5 seconds", distance_of_time_in_words(from, from + 0.seconds, true)
+ assert_equal "less than 5 seconds", distance_of_time_in_words(from, from + 4.seconds, true)
+ assert_equal "less than 10 seconds", distance_of_time_in_words(from, from + 5.seconds, true)
+ assert_equal "less than 10 seconds", distance_of_time_in_words(from, from + 9.seconds, true)
+ assert_equal "less than 20 seconds", distance_of_time_in_words(from, from + 10.seconds, true)
+ assert_equal "less than 20 seconds", distance_of_time_in_words(from, from + 19.seconds, true)
+ assert_equal "half a minute", distance_of_time_in_words(from, from + 20.seconds, true)
+ assert_equal "half a minute", distance_of_time_in_words(from, from + 39.seconds, true)
+ assert_equal "less than a minute", distance_of_time_in_words(from, from + 40.seconds, true)
+ assert_equal "less than a minute", distance_of_time_in_words(from, from + 59.seconds, true)
+ assert_equal "1 minute", distance_of_time_in_words(from, from + 60.seconds, true)
+ assert_equal "1 minute", distance_of_time_in_words(from, from + 89.seconds, true)
+
+ # First case 0..1
+ assert_equal "less than a minute", distance_of_time_in_words(from, from + 0.seconds)
+ assert_equal "less than a minute", distance_of_time_in_words(from, from + 29.seconds)
+ assert_equal "1 minute", distance_of_time_in_words(from, from + 30.seconds)
+ assert_equal "1 minute", distance_of_time_in_words(from, from + 1.minutes + 29.seconds)
+
+ # 2..44
+ assert_equal "2 minutes", distance_of_time_in_words(from, from + 1.minutes + 30.seconds)
+ assert_equal "44 minutes", distance_of_time_in_words(from, from + 44.minutes + 29.seconds)
+
+ # 45..89
+ assert_equal "about 1 hour", distance_of_time_in_words(from, from + 44.minutes + 30.seconds)
+ assert_equal "about 1 hour", distance_of_time_in_words(from, from + 89.minutes + 29.seconds)
+
+ # 90..1439
+ assert_equal "about 2 hours", distance_of_time_in_words(from, from + 89.minutes + 30.seconds)
+ assert_equal "about 24 hours", distance_of_time_in_words(from, from + 23.hours + 59.minutes + 29.seconds)
+
+ # 1440..2879
+ assert_equal "1 day", distance_of_time_in_words(from, from + 23.hours + 59.minutes + 30.seconds)
+ assert_equal "1 day", distance_of_time_in_words(from, from + 47.hours + 59.minutes + 29.seconds)
+
+ # 2880..43199
+ assert_equal "2 days", distance_of_time_in_words(from, from + 47.hours + 59.minutes + 30.seconds)
+ assert_equal "29 days", distance_of_time_in_words(from, from + 29.days + 23.hours + 59.minutes + 29.seconds)
+
+ # 43200..86399
+ assert_equal "about 1 month", distance_of_time_in_words(from, from + 29.days + 23.hours + 59.minutes + 30.seconds)
+ assert_equal "about 1 month", distance_of_time_in_words(from, from + 59.days + 23.hours + 59.minutes + 29.seconds)
+
+ # 86400..525959
+ assert_equal "2 months", distance_of_time_in_words(from, from + 59.days + 23.hours + 59.minutes + 30.seconds)
+ assert_equal "12 months", distance_of_time_in_words(from, from + 1.years - 31.seconds)
+
+ # 525960..1051919
+ assert_equal "about 1 year", distance_of_time_in_words(from, from + 1.years - 30.seconds)
+ assert_equal "about 1 year", distance_of_time_in_words(from, from + 2.years - 31.seconds)
+
+ # > 1051920
+ assert_equal "over 2 years", distance_of_time_in_words(from, from + 2.years - 30.seconds)
+ assert_equal "over 10 years", distance_of_time_in_words(from, from + 10.years)
+
+ # test to < from
+ assert_equal "about 4 hours", distance_of_time_in_words(from + 4.hours, from)
+ assert_equal "less than 20 seconds", distance_of_time_in_words(from + 19.seconds, from, true)
+
+ # test with integers
+ assert_equal "less than a minute", distance_of_time_in_words(59)
+ assert_equal "about 1 hour", distance_of_time_in_words(60*60)
+ assert_equal "less than a minute", distance_of_time_in_words(0, 59)
+ assert_equal "about 1 hour", distance_of_time_in_words(60*60, 0)
+ end
+
+ def test_distance_in_words_with_dates
+ start_date = Date.new 1975, 1, 31
+ end_date = Date.new 1977, 1, 31
+ assert_equal("over 2 years", distance_of_time_in_words(start_date, end_date))
+ end
+
+ def test_time_ago_in_words
+ t = Time.now - 1.years
+ assert_equal "about 1 year", time_ago_in_words(t)
+ end
+
+ def test_select_day
+ expected = %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_day(Time.mktime(2003, 8, 16))
+ assert_equal expected, select_day(16)
+ end
+
+ def test_select_day_with_blank
+ expected = %(\n)
+ expected << %( \n1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_day(Time.mktime(2003, 8, 16), :include_blank => true)
+ assert_equal expected, select_day(16, :include_blank => true)
+ end
+
+ def test_select_day_nil_with_blank
+ expected = %(\n)
+ expected << %( \n1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_day(nil, :include_blank => true)
+ end
+
+ def test_select_month
+ expected = %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16))
+ assert_equal expected, select_month(8)
+ end
+
+ def test_select_month_with_disabled
+ expected = %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :disabled => true)
+ assert_equal expected, select_month(8, :disabled => true)
+ end
+
+ def test_select_month_with_field_name_override
+ expected = %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :field_name => 'mois')
+ assert_equal expected, select_month(8, :field_name => 'mois')
+ end
+
+ def test_select_month_with_blank
+ expected = %(\n)
+ expected << %( \nJanuary \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :include_blank => true)
+ assert_equal expected, select_month(8, :include_blank => true)
+ end
+
+ def test_select_month_nil_with_blank
+ expected = %(\n)
+ expected << %( \nJanuary \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(nil, :include_blank => true)
+ end
+
+ def test_select_month_with_numbers
+ expected = %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :use_month_numbers => true)
+ assert_equal expected, select_month(8, :use_month_numbers => true)
+ end
+
+ def test_select_month_with_numbers_and_names
+ expected = %(\n)
+ expected << %(1 - January \n2 - February \n3 - March \n4 - April \n5 - May \n6 - June \n7 - July \n8 - August \n9 - September \n10 - October \n11 - November \n12 - December \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :add_month_numbers => true)
+ assert_equal expected, select_month(8, :add_month_numbers => true)
+ end
+
+ def test_select_month_with_numbers_and_names_with_abbv
+ expected = %(\n)
+ expected << %(1 - Jan \n2 - Feb \n3 - Mar \n4 - Apr \n5 - May \n6 - Jun \n7 - Jul \n8 - Aug \n9 - Sep \n10 - Oct \n11 - Nov \n12 - Dec \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :add_month_numbers => true, :use_short_month => true)
+ assert_equal expected, select_month(8, :add_month_numbers => true, :use_short_month => true)
+ end
+
+ def test_select_month_with_abbv
+ expected = %(\n)
+ expected << %(Jan \nFeb \nMar \nApr \nMay \nJun \nJul \nAug \nSep \nOct \nNov \nDec \n)
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :use_short_month => true)
+ assert_equal expected, select_month(8, :use_short_month => true)
+ end
+
+ def test_select_month_with_custom_names
+ month_names = %w(nil Januar Februar Marts April Maj Juni Juli August September Oktober November December)
+
+ expected = %(\n)
+ 1.upto(12) { |month| expected << %(#{month_names[month]} \n) }
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :use_month_names => month_names)
+ assert_equal expected, select_month(8, :use_month_names => month_names)
+ end
+
+ def test_select_month_with_zero_indexed_custom_names
+ month_names = %w(Januar Februar Marts April Maj Juni Juli August September Oktober November December)
+
+ expected = %(\n)
+ 1.upto(12) { |month| expected << %(#{month_names[month-1]} \n) }
+ expected << " \n"
+
+ assert_equal expected, select_month(Time.mktime(2003, 8, 16), :use_month_names => month_names)
+ assert_equal expected, select_month(8, :use_month_names => month_names)
+ end
+
+ def test_select_year
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ assert_equal expected, select_year(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005)
+ assert_equal expected, select_year(2003, :start_year => 2003, :end_year => 2005)
+ end
+
+ def test_select_year_with_disabled
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ assert_equal expected, select_year(Time.mktime(2003, 8, 16), :disabled => true, :start_year => 2003, :end_year => 2005)
+ assert_equal expected, select_year(2003, :disabled => true, :start_year => 2003, :end_year => 2005)
+ end
+
+ def test_select_year_with_field_name_override
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ assert_equal expected, select_year(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :field_name => 'annee')
+ assert_equal expected, select_year(2003, :start_year => 2003, :end_year => 2005, :field_name => 'annee')
+ end
+
+ def test_select_year_with_type_discarding
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ assert_equal expected, select_year(
+ Time.mktime(2003, 8, 16), :prefix => "date_year", :discard_type => true, :start_year => 2003, :end_year => 2005)
+ assert_equal expected, select_year(
+ 2003, :prefix => "date_year", :discard_type => true, :start_year => 2003, :end_year => 2005)
+ end
+
+ def test_select_year_descending
+ expected = %(\n)
+ expected << %(2005 \n2004 \n2003 \n)
+ expected << " \n"
+
+ assert_equal expected, select_year(Time.mktime(2005, 8, 16), :start_year => 2005, :end_year => 2003)
+ assert_equal expected, select_year(2005, :start_year => 2005, :end_year => 2003)
+ end
+
+ def test_select_hour
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ assert_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18))
+ end
+
+ def test_select_hour_with_disabled
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ assert_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :disabled => true)
+ end
+
+ def test_select_hour_with_field_name_override
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ assert_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :field_name => 'heure')
+ end
+
+ def test_select_hour_with_blank
+ expected = %(\n)
+ expected << %( \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ assert_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :include_blank => true)
+ end
+
+ def test_select_hour_nil_with_blank
+ expected = %(\n)
+ expected << %( \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ assert_equal expected, select_hour(nil, :include_blank => true)
+ end
+
+ def test_select_minute
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18))
+ end
+
+ def test_select_minute_with_disabled
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :disabled => true)
+ end
+
+ def test_select_minute_with_field_name_override
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :field_name => 'minuto')
+ end
+
+ def test_select_minute_with_blank
+ expected = %(\n)
+ expected << %( \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :include_blank => true)
+ end
+
+ def test_select_minute_with_blank_and_step
+ expected = %(\n)
+ expected << %( \n00 \n15 \n30 \n45 \n)
+ expected << " \n"
+
+ assert_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), { :include_blank => true , :minute_step => 15 })
+ end
+
+ def test_select_minute_nil_with_blank
+ expected = %(\n)
+ expected << %( \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_minute(nil, :include_blank => true)
+ end
+
+ def test_select_minute_nil_with_blank_and_step
+ expected = %(\n)
+ expected << %( \n00 \n15 \n30 \n45 \n)
+ expected << " \n"
+
+ assert_equal expected, select_minute(nil, { :include_blank => true , :minute_step => 15 })
+ end
+
+ def test_select_second
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18))
+ end
+
+ def test_select_second_with_disabled
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), :disabled => true)
+ end
+
+ def test_select_second_with_field_name_override
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), :field_name => 'segundo')
+ end
+
+ def test_select_second_with_blank
+ expected = %(\n)
+ expected << %( \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), :include_blank => true)
+ end
+
+ def test_select_second_nil_with_blank
+ expected = %(\n)
+ expected << %( \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_second(nil, :include_blank => true)
+ end
+
+ def test_select_date
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]")
+ end
+
+ def test_select_date_with_order
+ expected = %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :order => [:month, :day, :year])
+ end
+
+ def test_select_date_with_incomplete_order
+ expected = %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :order => [:day])
+ end
+
+ def test_select_date_with_disabled
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :disabled => true)
+ end
+
+ def test_select_date_with_no_start_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+1) do |y|
+ if y == Date.today.year
+ expected << %(#{y} \n)
+ else
+ expected << %(#{y} \n)
+ end
+ end
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(
+ Time.mktime(Date.today.year, 8, 16), :end_year => Date.today.year+1, :prefix => "date[first]"
+ )
+ end
+
+ def test_select_date_with_no_end_year
+ expected = %(\n)
+ 2003.upto(2008) do |y|
+ if y == 2003
+ expected << %(#{y} \n)
+ else
+ expected << %(#{y} \n)
+ end
+ end
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(
+ Time.mktime(2003, 8, 16), :start_year => 2003, :prefix => "date[first]"
+ )
+ end
+
+ def test_select_date_with_no_start_or_end_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+5) do |y|
+ if y == Date.today.year
+ expected << %(#{y} \n)
+ else
+ expected << %(#{y} \n)
+ end
+ end
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(
+ Time.mktime(Date.today.year, 8, 16), :prefix => "date[first]"
+ )
+ end
+
+ def test_select_date_with_zero_value
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(0, :start_year => 2003, :end_year => 2005, :prefix => "date[first]")
+ end
+
+ def test_select_date_with_zero_value_and_no_start_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(0, :end_year => Date.today.year+1, :prefix => "date[first]")
+ end
+
+ def test_select_date_with_zero_value_and_no_end_year
+ expected = %(\n)
+ last_year = Time.now.year + 5
+ 2003.upto(last_year) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(0, :start_year => 2003, :prefix => "date[first]")
+ end
+
+ def test_select_date_with_zero_value_and_no_start_and_end_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(0, :prefix => "date[first]")
+ end
+
+ def test_select_date_with_nil_value_and_no_start_and_end_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(nil, :prefix => "date[first]")
+ end
+
+ def test_select_datetime
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), :start_year => 2003, :end_year => 2005, :prefix => "date[first]")
+ end
+
+ def test_select_datetime_with_separators
+ expected = %(\n)
+ expected << %(2003 \n2004 \n2005 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ expected << " — "
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ expected << " : "
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :datetime_separator => ' — ', :time_separator => ' : ')
+ end
+
+ def test_select_datetime_with_nil_value_and_no_start_and_end_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_datetime(nil, :prefix => "date[first]")
+ end
+
+ def test_select_time
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18))
+ assert_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :include_seconds => false)
+ end
+
+ def test_select_time_with_separator
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ expected << " : "
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :time_separator => ' : ')
+ assert_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :time_separator => ' : ', :include_seconds => false)
+ end
+
+ def test_select_time_with_seconds
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :include_seconds => true)
+ end
+
+ def test_select_time_with_seconds_and_separator
+ expected = %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ expected << " : "
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ expected << " : "
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), :include_seconds => true, :time_separator => ' : ')
+ end
+
+ def test_date_select
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{\n}
+ expected << %{1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n}
+
+ expected << " \n"
+
+ assert_equal expected, date_select("post", "written_on")
+ end
+
+ def test_date_select_within_fields_for
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ _erbout = ''
+
+ fields_for :post, @post do |f|
+ _erbout.concat f.date_select(:written_on)
+ end
+
+ expected = "\n1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n \n"
+ expected << "\nJanuary \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n \n"
+ expected << "\n1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n \n"
+
+ assert_dom_equal(expected, _erbout)
+ end
+
+ def test_date_select_with_index
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+ id = 456
+
+ expected = %{\n}
+ expected << %{1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n}
+
+ expected << " \n"
+
+ assert_equal expected, date_select("post", "written_on", :index => id)
+ end
+
+ def test_date_select_with_auto_index
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+ id = 123
+
+ expected = %{\n}
+ expected << %{1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n}
+ expected << " \n"
+
+ assert_equal expected, date_select("post[]", "written_on")
+ end
+
+ def test_date_select_with_different_order
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{\n}
+ 1.upto(31) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+
+ expected << %{\n}
+ 1.upto(12) { |i| expected << %(#{Time.now.change(:month => i).strftime("%B")} \n) }
+ expected << " \n"
+
+ expected << %{\n}
+ 1999.upto(2009) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+
+ assert_equal expected, date_select("post", "written_on", :order => [:day, :month, :year])
+ end
+
+ def test_date_select_cant_override_discard_hour
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{\n}
+ expected << %{1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n}
+ expected << " \n"
+
+ assert_equal expected, date_select("post", "written_on", :discard_hour => false)
+ end
+
+ def test_time_select
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{ \n}
+ expected << %{ \n}
+ expected << %{ \n}
+
+ expected << %(\n)
+ 0.upto(23) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %(\n)
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+
+ assert_equal expected, time_select("post", "written_on")
+ end
+
+ def test_time_select_with_seconds
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{ \n}
+ expected << %{ \n}
+ expected << %{ \n}
+
+ expected << %(\n)
+ 0.upto(23) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %(\n)
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %(\n)
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+
+ assert_equal expected, time_select("post", "written_on", :include_seconds => true)
+ end
+
+ def test_datetime_select
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+ expected = %{\n}
+ expected << %{1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n}
+ expected << " \n"
+
+ expected << " — "
+
+ expected << %{\n}
+ expected << %{00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n}
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ expected << %{00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n}
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post", "updated_at")
+ end
+
+ def test_datetime_select_within_fields_for
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+ _erbout = ''
+
+ fields_for :post, @post do |f|
+ _erbout.concat f.datetime_select(:updated_at)
+ end
+
+ expected = "\n1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n \n"
+ expected << "\nJanuary \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n \n"
+ expected << "\n1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n \n"
+ expected << " — \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n \n"
+ expected << " : \n00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n \n"
+
+ assert_dom_equal(expected, _erbout)
+ end
+
+ def test_date_select_with_zero_value_and_no_start_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected <<
+%(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(0, :end_year => Date.today.year+1, :prefix => "date[first]")
+ end
+
+ def test_date_select_with_zero_value_and_no_end_year
+ expected = %(\n)
+ last_year = Time.now.year + 5
+ 2003.upto(last_year) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected <<
+%(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(0, :start_year => 2003, :prefix => "date[first]")
+ end
+
+ def test_date_select_with_zero_value_and_no_start_and_end_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected <<
+%(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(0, :prefix => "date[first]")
+ end
+
+ def test_date_select_with_nil_value_and_no_start_and_end_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected <<
+%(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ assert_equal expected, select_date(nil, :prefix => "date[first]")
+ end
+
+ def test_datetime_select_with_nil_value_and_no_start_and_end_year
+ expected = %(\n)
+ (Date.today.year-5).upto(Date.today.year+5) { |y| expected << %(#{y} \n) }
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected <<
+%(1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n)
+ expected << " \n"
+
+ expected << %(\n)
+ expected << %(00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n)
+ expected << " \n"
+
+ assert_equal expected, select_datetime(nil, :prefix => "date[first]")
+ end
+
+
+ def test_datetime_select_with_options_index
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+ id = 456
+
+ expected = %{\n}
+ expected << %{1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n}
+ expected << " \n"
+
+ expected << " — "
+
+ expected << %{\n}
+ expected << %{00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n}
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ expected << %{00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n}
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post", "updated_at", :index => id)
+ end
+
+ def test_datetime_select_with_auto_index
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+ id = @post.id
+
+ expected = %{\n}
+ expected << %{1999 \n2000 \n2001 \n2002 \n2003 \n2004 \n2005 \n2006 \n2007 \n2008 \n2009 \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{January \nFebruary \nMarch \nApril \nMay \nJune \nJuly \nAugust \nSeptember \nOctober \nNovember \nDecember \n}
+ expected << " \n"
+
+ expected << %{\n}
+ expected << %{1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n}
+ expected << " \n"
+
+ expected << " — "
+
+ expected << %{\n}
+ expected << %{00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n}
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ expected << %{00 \n01 \n02 \n03 \n04 \n05 \n06 \n07 \n08 \n09 \n10 \n11 \n12 \n13 \n14 \n15 \n16 \n17 \n18 \n19 \n20 \n21 \n22 \n23 \n24 \n25 \n26 \n27 \n28 \n29 \n30 \n31 \n32 \n33 \n34 \n35 \n36 \n37 \n38 \n39 \n40 \n41 \n42 \n43 \n44 \n45 \n46 \n47 \n48 \n49 \n50 \n51 \n52 \n53 \n54 \n55 \n56 \n57 \n58 \n59 \n}
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post[]", "updated_at")
+ end
+
+ def test_datetime_select_with_seconds
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{\n}
+ 1999.upto(2009) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+ expected << %{\n}
+ 1.upto(12) { |i| expected << %(#{Time.now.change(:month => i).strftime("%B")} \n) }
+ expected << " \n"
+ expected << %{\n}
+ 1.upto(31) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+
+ expected << " — "
+
+ expected << %{\n}
+ 0.upto(23) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post", "updated_at", :include_seconds => true)
+ end
+
+ def test_datetime_select_discard_year
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{ \n}
+ expected << %{\n}
+ 1.upto(12) { |i| expected << %(#{Time.now.change(:month => i).strftime("%B")} \n) }
+ expected << " \n"
+ expected << %{\n}
+ 1.upto(31) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+
+ expected << " — "
+
+ expected << %{\n}
+ 0.upto(23) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post", "updated_at", :discard_year => true)
+ end
+
+ def test_datetime_select_discard_month
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{\n}
+ 1999.upto(2009) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+ expected << %{ \n}
+ expected << %{ \n}
+
+ expected << " — "
+
+ expected << %{\n}
+ 0.upto(23) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post", "updated_at", :discard_month => true)
+ end
+
+ def test_datetime_select_discard_year_and_month
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{ \n}
+ expected << %{ \n}
+ expected << %{ \n}
+
+ expected << %{\n}
+ 0.upto(23) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post", "updated_at", :discard_year => true, :discard_month => true)
+ end
+
+ def test_datetime_select_invalid_order
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{\n}
+ 1.upto(31) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+ expected << %{\n}
+ 1.upto(12) { |i| expected << %(#{Time.now.change(:month => i).strftime("%B")} \n) }
+ expected << " \n"
+ expected << %{\n}
+ 1999.upto(2009) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+
+ expected << " — "
+
+ expected << %{\n}
+ 0.upto(23) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post", "updated_at", :order => [:minute, :day, :hour, :month, :year, :second])
+ end
+
+ def test_datetime_select_discard_with_order
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{ \n}
+ expected << %{\n}
+ 1.upto(31) { |i| expected << %(#{i} \n) }
+ expected << " \n"
+ expected << %{\n}
+ 1.upto(12) { |i| expected << %(#{Time.now.change(:month => i).strftime("%B")} \n) }
+ expected << " \n"
+
+ expected << " — "
+
+ expected << %{\n}
+ 0.upto(23) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+ expected << " : "
+ expected << %{\n}
+ 0.upto(59) { |i| expected << %(#{leading_zero_on_single_digits(i)} \n) }
+ expected << " \n"
+
+ assert_equal expected, datetime_select("post", "updated_at", :order => [:day, :month])
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/deprecated_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/deprecated_helper_test.rb
new file mode 100644
index 00000000..836bb035
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/deprecated_helper_test.rb
@@ -0,0 +1,36 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DeprecatedHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::JavaScriptHelper
+ include ActionView::Helpers::CaptureHelper
+
+ def test_update_element_function
+ assert_deprecated 'update_element_function' do
+
+ assert_equal %($('myelement').innerHTML = 'blub';\n),
+ update_element_function('myelement', :content => 'blub')
+ assert_equal %($('myelement').innerHTML = 'blub';\n),
+ update_element_function('myelement', :action => :update, :content => 'blub')
+ assert_equal %($('myelement').innerHTML = '';\n),
+ update_element_function('myelement', :action => :empty)
+ assert_equal %(Element.remove('myelement');\n),
+ update_element_function('myelement', :action => :remove)
+
+ assert_equal %(new Insertion.Bottom('myelement','blub');\n),
+ update_element_function('myelement', :position => 'bottom', :content => 'blub')
+ assert_equal %(new Insertion.Bottom('myelement','blub');\n),
+ update_element_function('myelement', :action => :update, :position => :bottom, :content => 'blub')
+
+ _erbout = ""
+ assert_equal %($('myelement').innerHTML = 'test';\n),
+ update_element_function('myelement') { _erbout << "test" }
+
+ _erbout = ""
+ assert_equal %($('myelement').innerHTML = 'blockstuff';\n),
+ update_element_function('myelement', :content => 'paramstuff') { _erbout << "blockstuff" }
+
+ end
+ end
+
+end
+
diff --git a/tracks/vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb b/tracks/vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb
new file mode 100644
index 00000000..c6931ae9
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/deprecated_instance_variables_test.rb
@@ -0,0 +1,43 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DeprecatedViewInstanceVariablesTest < Test::Unit::TestCase
+ class DeprecatedInstanceVariablesController < ActionController::Base
+ self.template_root = "#{File.dirname(__FILE__)}/../fixtures/"
+
+ def self.controller_path; 'deprecated_instance_variables' end
+
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
+ class_eval <<-end_eval
+ def old_#{var}_inline; render :inline => '<%= @#{var}.to_s %>' end
+ def new_#{var}_inline; render :inline => '<%= #{var}.to_s %>' end
+ def old_#{var}_partial; render :partial => '#{var}_ivar' end
+ def new_#{var}_partial; render :partial => '#{var}_method' end
+ end_eval
+ end
+
+ def rescue_action(e) raise e end
+ end
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = DeprecatedInstanceVariablesController.new
+ end
+
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |var|
+ class_eval <<-end_eval, __FILE__, __LINE__
+ def test_old_#{var}_is_deprecated
+ assert_deprecated('@#{var}') { get :old_#{var}_inline }
+ end
+ def test_new_#{var}_isnt_deprecated
+ assert_not_deprecated { get :new_#{var}_inline }
+ end
+ def test_old_#{var}_partial_is_deprecated
+ assert_deprecated('@#{var}') { get :old_#{var}_partial }
+ end
+ def test_new_#{var}_partial_isnt_deprecated
+ assert_not_deprecated { get :new_#{var}_partial }
+ end
+ end_eval
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/form_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/form_helper_test.rb
new file mode 100644
index 00000000..a7ceb6b3
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/form_helper_test.rb
@@ -0,0 +1,499 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class FormHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::FormHelper
+ include ActionView::Helpers::FormTagHelper
+ include ActionView::Helpers::UrlHelper
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::TextHelper
+
+ silence_warnings do
+ Post = Struct.new("Post", :title, :author_name, :body, :secret, :written_on, :cost)
+ Post.class_eval do
+ alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast)
+ alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast)
+ alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast)
+ end
+ end
+
+ def setup
+ @post = Post.new
+ def @post.errors() Class.new{ def on(field) field == "author_name" end }.new end
+
+ def @post.id; 123; end
+ def @post.id_before_type_cast; 123; end
+
+ @post.title = "Hello World"
+ @post.author_name = ""
+ @post.body = "Back to the hill and over it again!"
+ @post.secret = 1
+ @post.written_on = Date.new(2004, 6, 15)
+
+ @controller = Class.new do
+ attr_reader :url_for_options
+ def url_for(options, *parameters_for_method_reference)
+ @url_for_options = options
+ "http://www.example.com"
+ end
+ end
+ @controller = @controller.new
+ end
+
+ def test_text_field
+ assert_dom_equal(
+ ' ', text_field("post", "title")
+ )
+ assert_dom_equal(
+ ' ', password_field("post", "title")
+ )
+ assert_dom_equal(
+ ' ', password_field("person", "name")
+ )
+ end
+
+ def test_text_field_with_escapes
+ @post.title = "Hello World "
+ assert_dom_equal(
+ ' ', text_field("post", "title")
+ )
+ end
+
+ def test_text_field_with_options
+ expected = ' '
+ assert_dom_equal expected, text_field("post", "title", "size" => 35)
+ assert_dom_equal expected, text_field("post", "title", :size => 35)
+ end
+
+ def test_text_field_assuming_size
+ expected = ' '
+ assert_dom_equal expected, text_field("post", "title", "maxlength" => 35)
+ assert_dom_equal expected, text_field("post", "title", :maxlength => 35)
+ end
+
+ def test_text_field_doesnt_change_param_values
+ object_name = 'post[]'
+ expected = ' '
+ assert_equal expected, text_field(object_name, "title")
+ assert_equal object_name, "post[]"
+ end
+
+ def test_check_box
+ assert_dom_equal(
+ ' ',
+ check_box("post", "secret")
+ )
+ @post.secret = 0
+ assert_dom_equal(
+ ' ',
+ check_box("post", "secret")
+ )
+ assert_dom_equal(
+ ' ',
+ check_box("post", "secret" ,{"checked"=>"checked"})
+ )
+ @post.secret = true
+ assert_dom_equal(
+ ' ',
+ check_box("post", "secret")
+ )
+ end
+
+ def test_check_box_with_explicit_checked_and_unchecked_values
+ @post.secret = "on"
+ assert_dom_equal(
+ ' ',
+ check_box("post", "secret", {}, "on", "off")
+ )
+ end
+
+ def test_radio_button
+ assert_dom_equal(' ',
+ radio_button("post", "title", "Hello World")
+ )
+ assert_dom_equal(' ',
+ radio_button("post", "title", "Goodbye World")
+ )
+ end
+
+ def test_radio_button_is_checked_with_integers
+ assert_dom_equal(' ',
+ radio_button("post", "secret", "1")
+ )
+ end
+
+ def test_radio_button_respects_passed_in_id
+ assert_dom_equal(' ',
+ radio_button("post", "secret", "1", :id=>"foo")
+ )
+ end
+
+ def test_text_area
+ assert_dom_equal(
+ 'Back to the hill and over it again! ',
+ text_area("post", "body")
+ )
+ end
+
+ def test_text_area_with_escapes
+ @post.body = "Back to the hill and over it again!"
+ assert_dom_equal(
+ 'Back to <i>the</i> hill and over it again! ',
+ text_area("post", "body")
+ )
+ end
+
+ def test_text_area_with_alternate_value
+ assert_dom_equal(
+ 'Testing alternate values. ',
+ text_area("post", "body", :value => 'Testing alternate values.')
+ )
+ end
+
+ def test_text_area_with_size_option
+ assert_dom_equal(
+ 'Back to the hill and over it again! ',
+ text_area("post", "body", :size => "183x820")
+ )
+ end
+
+ def test_date_selects
+ assert_dom_equal(
+ 'Back to the hill and over it again! ',
+ text_area("post", "body")
+ )
+ end
+
+ def test_explicit_name
+ assert_dom_equal(
+ ' ', text_field("post", "title", "name" => "dont guess")
+ )
+ assert_dom_equal(
+ 'Back to the hill and over it again! ',
+ text_area("post", "body", "name" => "really!")
+ )
+ assert_dom_equal(
+ ' ',
+ check_box("post", "secret", "name" => "i mean it")
+ )
+ assert_dom_equal text_field("post", "title", "name" => "dont guess"),
+ text_field("post", "title", :name => "dont guess")
+ assert_dom_equal text_area("post", "body", "name" => "really!"),
+ text_area("post", "body", :name => "really!")
+ assert_dom_equal check_box("post", "secret", "name" => "i mean it"),
+ check_box("post", "secret", :name => "i mean it")
+ end
+
+ def test_explicit_id
+ assert_dom_equal(
+ ' ', text_field("post", "title", "id" => "dont guess")
+ )
+ assert_dom_equal(
+ 'Back to the hill and over it again! ',
+ text_area("post", "body", "id" => "really!")
+ )
+ assert_dom_equal(
+ ' ',
+ check_box("post", "secret", "id" => "i mean it")
+ )
+ assert_dom_equal text_field("post", "title", "id" => "dont guess"),
+ text_field("post", "title", :id => "dont guess")
+ assert_dom_equal text_area("post", "body", "id" => "really!"),
+ text_area("post", "body", :id => "really!")
+ assert_dom_equal check_box("post", "secret", "id" => "i mean it"),
+ check_box("post", "secret", :id => "i mean it")
+ end
+
+ def test_auto_index
+ pid = @post.id
+ assert_dom_equal(
+ " ", text_field("post[]","title")
+ )
+ assert_dom_equal(
+ "Back to the hill and over it again! ",
+ text_area("post[]", "body")
+ )
+ assert_dom_equal(
+ " ",
+ check_box("post[]", "secret")
+ )
+ assert_dom_equal(
+" ",
+ radio_button("post[]", "title", "Hello World")
+ )
+ assert_dom_equal(" ",
+ radio_button("post[]", "title", "Goodbye World")
+ )
+ end
+
+ def test_form_for
+ _erbout = ''
+
+ form_for(:post, @post, :html => { :id => 'create-post' }) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ "" +
+ " " +
+ "Back to the hill and over it again! " +
+ " " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_form_for_with_method
+ _erbout = ''
+
+ form_for(:post, @post, :html => { :id => 'create-post', :method => :put }) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ "" +
+ "
" +
+ " " +
+ "Back to the hill and over it again! " +
+ " " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_form_for_without_object
+ _erbout = ''
+
+ form_for(:post, :html => { :id => 'create-post' }) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ "" +
+ " " +
+ "Back to the hill and over it again! " +
+ " " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_form_for_with_index
+ _erbout = ''
+
+ form_for("post[]", @post) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ "" +
+ " " +
+ "Back to the hill and over it again! " +
+ " " +
+ " " +
+ " "
+ end
+
+ def test_fields_for
+ _erbout = ''
+
+ fields_for(:post, @post) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ " " +
+ "Back to the hill and over it again! " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_fields_for_without_object
+ _erbout = ''
+ fields_for(:post) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ " " +
+ "Back to the hill and over it again! " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_form_builder_does_not_have_form_for_method
+ assert ! ActionView::Helpers::FormBuilder.instance_methods.include?('form_for')
+ end
+
+ def test_form_for_and_fields_for
+ _erbout = ''
+
+ form_for(:post, @post, :html => { :id => 'create-post' }) do |post_form|
+ _erbout.concat post_form.text_field(:title)
+ _erbout.concat post_form.text_area(:body)
+
+ fields_for(:parent_post, @post) do |parent_fields|
+ _erbout.concat parent_fields.check_box(:secret)
+ end
+ end
+
+ expected =
+ "" +
+ " " +
+ "Back to the hill and over it again! " +
+ " " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ class LabelledFormBuilder < ActionView::Helpers::FormBuilder
+ (field_helpers - %w(hidden_field)).each do |selector|
+ src = <<-END_SRC
+ def #{selector}(field, *args, &proc)
+ "\#{field.to_s.humanize}: " + super + " "
+ end
+ END_SRC
+ class_eval src, __FILE__, __LINE__
+ end
+ end
+
+ def test_form_for_with_labelled_builder
+ _erbout = ''
+
+ form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ "" +
+ "Title: " +
+ "Body: Back to the hill and over it again! " +
+ "Secret: " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_default_form_builder
+ old_default_form_builder, ActionView::Base.default_form_builder =
+ ActionView::Base.default_form_builder, LabelledFormBuilder
+
+ _erbout = ''
+ form_for(:post, @post) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ "" +
+ "Title: " +
+ "Body: Back to the hill and over it again! " +
+ "Secret: " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ ensure
+ ActionView::Base.default_form_builder = old_default_form_builder
+ end
+
+ # Perhaps this test should be moved to prototype helper tests.
+ def test_remote_form_for_with_labelled_builder
+ self.extend ActionView::Helpers::PrototypeHelper
+ _erbout = ''
+
+ remote_form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ %() +
+ "Title: " +
+ "Body: Back to the hill and over it again! " +
+ "Secret: " +
+ " " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_fields_for_with_labelled_builder
+ _erbout = ''
+
+ fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+ _erbout.concat f.text_field(:title)
+ _erbout.concat f.text_area(:body)
+ _erbout.concat f.check_box(:secret)
+ end
+
+ expected =
+ "Title: " +
+ "Body: Back to the hill and over it again! " +
+ "Secret: " +
+ " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_form_for_with_html_options_adds_options_to_form_tag
+ _erbout = ''
+
+ form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
+ expected = " "
+
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_form_for_with_string_url_option
+ _erbout = ''
+
+ form_for(:post, @post, :url => 'http://www.otherdomain.com') do |f| end
+
+ assert_equal 'http://www.otherdomain.com', @controller.url_for_options
+ end
+
+ def test_form_for_with_hash_url_option
+ _erbout = ''
+
+ form_for(:post, @post, :url => {:controller => 'controller', :action => 'action'}) do |f| end
+
+ assert_equal 'controller', @controller.url_for_options[:controller]
+ assert_equal 'action', @controller.url_for_options[:action]
+ end
+
+ def test_remote_form_for_with_html_options_adds_options_to_form_tag
+ self.extend ActionView::Helpers::PrototypeHelper
+ _erbout = ''
+
+ remote_form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
+ expected = " "
+
+ assert_dom_equal expected, _erbout
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/form_options_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/form_options_helper_test.rb
new file mode 100644
index 00000000..ad293417
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/form_options_helper_test.rb
@@ -0,0 +1,470 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class MockTimeZone
+ attr_reader :name
+
+ def initialize( name )
+ @name = name
+ end
+
+ def self.all
+ [ "A", "B", "C", "D", "E" ].map { |s| new s }
+ end
+
+ def ==( z )
+ z && @name == z.name
+ end
+
+ def to_s
+ @name
+ end
+end
+
+ActionView::Helpers::FormOptionsHelper::TimeZone = MockTimeZone
+
+class FormOptionsHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::FormHelper
+ include ActionView::Helpers::FormOptionsHelper
+
+ silence_warnings do
+ Post = Struct.new('Post', :title, :author_name, :body, :secret, :written_on, :category, :origin)
+ Continent = Struct.new('Continent', :continent_name, :countries)
+ Country = Struct.new('Country', :country_id, :country_name)
+ Firm = Struct.new('Firm', :time_zone)
+ end
+
+ def test_collection_options
+ @posts = [
+ Post.new(" went home", "", "To a little house", "shh!"),
+ Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+ Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+ ]
+
+ assert_dom_equal(
+ "<Abe> went home \nBabe went home \nCabe went home ",
+ options_from_collection_for_select(@posts, "author_name", "title")
+ )
+ end
+
+
+ def test_collection_options_with_preselected_value
+ @posts = [
+ Post.new(" went home", "", "To a little house", "shh!"),
+ Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+ Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+ ]
+
+ assert_dom_equal(
+ "<Abe> went home \nBabe went home \nCabe went home ",
+ options_from_collection_for_select(@posts, "author_name", "title", "Babe")
+ )
+ end
+
+ def test_collection_options_with_preselected_value_array
+ @posts = [
+ Post.new(" went home", "", "To a little house", "shh!"),
+ Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+ Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+ ]
+
+ assert_dom_equal(
+ "<Abe> went home \nBabe went home \nCabe went home ",
+ options_from_collection_for_select(@posts, "author_name", "title", [ "Babe", "Cabe" ])
+ )
+ end
+
+ def test_array_options_for_select
+ assert_dom_equal(
+ "<Denmark> \nUSA \nSweden ",
+ options_for_select([ "", "USA", "Sweden" ])
+ )
+ end
+
+ def test_array_options_for_select_with_selection
+ assert_dom_equal(
+ "Denmark \n<USA> \nSweden ",
+ options_for_select([ "Denmark", "", "Sweden" ], "")
+ )
+ end
+
+ def test_array_options_for_select_with_selection_array
+ assert_dom_equal(
+ "Denmark \n<USA> \nSweden ",
+ options_for_select([ "Denmark", "", "Sweden" ], [ "", "Sweden" ])
+ )
+ end
+
+ def test_array_options_for_string_include_in_other_string_bug_fix
+ assert_dom_equal(
+ "ruby \nrubyonrails ",
+ options_for_select([ "ruby", "rubyonrails" ], "rubyonrails")
+ )
+ assert_dom_equal(
+ "ruby \nrubyonrails ",
+ options_for_select([ "ruby", "rubyonrails" ], "ruby")
+ )
+ assert_dom_equal(
+ %(ruby \nrubyonrails \n ),
+ options_for_select([ "ruby", "rubyonrails", nil ], "ruby")
+ )
+ end
+
+ def test_hash_options_for_select
+ assert_dom_equal(
+ "<DKR> \n$ ",
+ options_for_select({ "$" => "Dollar", "" => "" })
+ )
+ assert_dom_equal(
+ "<DKR> \n$ ",
+ options_for_select({ "$" => "Dollar", "" => "" }, "Dollar")
+ )
+ assert_dom_equal(
+ "<DKR> \n$ ",
+ options_for_select({ "$" => "Dollar", "" => "" }, [ "Dollar", "" ])
+ )
+ end
+
+ def test_ducktyped_options_for_select
+ quack = Struct.new(:first, :last)
+ assert_dom_equal(
+ "<DKR> \n$ ",
+ options_for_select([quack.new("", ""), quack.new("$", "Dollar")])
+ )
+ assert_dom_equal(
+ "<DKR> \n$ ",
+ options_for_select([quack.new("", ""), quack.new("$", "Dollar")], "Dollar")
+ )
+ assert_dom_equal(
+ "<DKR> \n$ ",
+ options_for_select([quack.new("", ""), quack.new("$", "Dollar")], ["Dollar", ""])
+ )
+ end
+
+ def test_html_option_groups_from_collection
+ @continents = [
+ Continent.new("", [Country.new("", ""), Country.new("so", "Somalia")] ),
+ Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] )
+ ]
+
+ assert_dom_equal(
+ "<South Africa> \nSomalia Denmark \nIreland ",
+ option_groups_from_collection_for_select(@continents, "countries", "continent_name", "country_id", "country_name", "dk")
+ )
+ end
+
+ def test_time_zone_options_no_parms
+ opts = time_zone_options_for_select
+ assert_dom_equal "A \n" +
+ "B \n" +
+ "C \n" +
+ "D \n" +
+ "E ",
+ opts
+ end
+
+ def test_time_zone_options_with_selected
+ opts = time_zone_options_for_select( "D" )
+ assert_dom_equal "A \n" +
+ "B \n" +
+ "C \n" +
+ "D \n" +
+ "E ",
+ opts
+ end
+
+ def test_time_zone_options_with_unknown_selected
+ opts = time_zone_options_for_select( "K" )
+ assert_dom_equal "A \n" +
+ "B \n" +
+ "C \n" +
+ "D \n" +
+ "E ",
+ opts
+ end
+
+ def test_time_zone_options_with_priority_zones
+ zones = [ TimeZone.new( "B" ), TimeZone.new( "E" ) ]
+ opts = time_zone_options_for_select( nil, zones )
+ assert_dom_equal "B \n" +
+ "E " +
+ "------------- \n" +
+ "A \n" +
+ "C \n" +
+ "D ",
+ opts
+ end
+
+ def test_time_zone_options_with_selected_priority_zones
+ zones = [ TimeZone.new( "B" ), TimeZone.new( "E" ) ]
+ opts = time_zone_options_for_select( "E", zones )
+ assert_dom_equal "B \n" +
+ "E " +
+ "------------- \n" +
+ "A \n" +
+ "C \n" +
+ "D ",
+ opts
+ end
+
+ def test_time_zone_options_with_unselected_priority_zones
+ zones = [ TimeZone.new( "B" ), TimeZone.new( "E" ) ]
+ opts = time_zone_options_for_select( "C", zones )
+ assert_dom_equal "B \n" +
+ "E " +
+ "------------- \n" +
+ "A \n" +
+ "C \n" +
+ "D ",
+ opts
+ end
+
+ def test_select
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ "abe \n<mus> \nhest ",
+ select("post", "category", %w( abe hest))
+ )
+ end
+
+ def test_select_under_fields_for
+ @post = Post.new
+ @post.category = ""
+
+ _erbout = ''
+
+ fields_for :post, @post do |f|
+ _erbout.concat f.select(:category, %w( abe hest))
+ end
+
+ assert_dom_equal(
+ "abe \n<mus> \nhest ",
+ _erbout
+ )
+ end
+
+ def test_select_with_blank
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ " \nabe \n<mus> \nhest ",
+ select("post", "category", %w( abe hest), :include_blank => true)
+ )
+ end
+
+ def test_select_with_default_prompt
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ "Please select \nabe \n<mus> \nhest ",
+ select("post", "category", %w( abe hest), :prompt => true)
+ )
+ end
+
+ def test_select_no_prompt_when_select_has_value
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ "abe \n<mus> \nhest ",
+ select("post", "category", %w( abe hest), :prompt => true)
+ )
+ end
+
+ def test_select_with_given_prompt
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ "The prompt \nabe \n<mus> \nhest ",
+ select("post", "category", %w( abe hest), :prompt => 'The prompt')
+ )
+ end
+
+ def test_select_with_prompt_and_blank
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ "Please select \n \nabe \n<mus> \nhest ",
+ select("post", "category", %w( abe hest), :prompt => true, :include_blank => true)
+ )
+ end
+
+ def test_select_with_selected_value
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ "abe \n<mus> \nhest ",
+ select("post", "category", %w( abe hest ), :selected => 'abe')
+ )
+ end
+
+ def test_select_with_selected_nil
+ @post = Post.new
+ @post.category = ""
+ assert_dom_equal(
+ "abe \n<mus> \nhest ",
+ select("post", "category", %w( abe hest ), :selected => nil)
+ )
+ end
+
+ def test_collection_select
+ @posts = [
+ Post.new(" went home", "", "To a little house", "shh!"),
+ Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+ Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+ ]
+
+ @post = Post.new
+ @post.author_name = "Babe"
+
+ assert_dom_equal(
+ "<Abe> \nBabe \nCabe ",
+ collection_select("post", "author_name", @posts, "author_name", "author_name")
+ )
+ end
+
+ def test_collection_select_under_fields_for
+ @posts = [
+ Post.new(" went home", "", "To a little house", "shh!"),
+ Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+ Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+ ]
+
+ @post = Post.new
+ @post.author_name = "Babe"
+
+ _erbout = ''
+
+ fields_for :post, @post do |f|
+ _erbout.concat f.collection_select(:author_name, @posts, :author_name, :author_name)
+ end
+
+ assert_dom_equal(
+ "<Abe> \nBabe \nCabe ",
+ _erbout
+ )
+ end
+
+ def test_collection_select_with_blank_and_style
+ @posts = [
+ Post.new(" went home", "", "To a little house", "shh!"),
+ Post.new("Babe went home", "Babe", "To a little house", "shh!"),
+ Post.new("Cabe went home", "Cabe", "To a little house", "shh!")
+ ]
+
+ @post = Post.new
+ @post.author_name = "Babe"
+
+ assert_dom_equal(
+ " \n<Abe> \nBabe \nCabe ",
+ collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => true }, "style" => "width: 200px")
+ )
+ end
+
+ def test_country_select
+ @post = Post.new
+ @post.origin = "Denmark"
+ assert_dom_equal(
+ "Afghanistan \nAlbania \nAlgeria \nAmerican Samoa \nAndorra \nAngola \nAnguilla \nAntarctica \nAntigua And Barbuda \nArgentina \nArmenia \nAruba \nAustralia \nAustria \nAzerbaijan \nBahamas \nBahrain \nBangladesh \nBarbados \nBelarus \nBelgium \nBelize \nBenin \nBermuda \nBhutan \nBolivia \nBosnia and Herzegowina \nBotswana \nBouvet Island \nBrazil \nBritish Indian Ocean Territory \nBrunei Darussalam \nBulgaria \nBurkina Faso \nBurma \nBurundi \nCambodia \nCameroon \nCanada \nCape Verde \nCayman Islands \nCentral African Republic \nChad \nChile \nChina \nChristmas Island \nCocos (Keeling) Islands \nColombia \nComoros \nCongo \nCongo, the Democratic Republic of the \nCook Islands \nCosta Rica \nCote d'Ivoire \nCroatia \nCuba \nCyprus \nCzech Republic \nDenmark \nDjibouti \nDominica \nDominican Republic \nEast Timor \nEcuador \nEgypt \nEl Salvador \nEngland" +
+ " \nEquatorial Guinea \nEritrea \nEspana \nEstonia \nEthiopia \nFalkland Islands \nFaroe Islands \nFiji \nFinland \nFrance \nFrench Guiana \nFrench Polynesia \nFrench Southern Territories \nGabon \nGambia \nGeorgia \nGermany \nGhana \nGibraltar \nGreat Britain \nGreece \nGreenland \nGrenada \nGuadeloupe \nGuam \nGuatemala \nGuinea \nGuinea-Bissau \nGuyana \nHaiti \nHeard and Mc Donald Islands \nHonduras \nHong Kong \nHungary \nIceland \nIndia \nIndonesia \nIreland \nIsrael \nItaly \nIran \nIraq \nJamaica \nJapan \nJordan \nKazakhstan \nKenya \nKiribati \nKorea, Republic of \nKorea (South) \nKuwait \nKyrgyzstan \nLao People's Democratic Republic \nLatvia \nLebanon \nLesotho \nLiberia \nLiechtenstein \nLithuania \nLuxembourg \nMacau \nMacedonia \nMadagascar \nMalawi \nMalaysia \nMaldives \nMali \nMalta \nMarshall Islands \nMartinique \nMauritania \nMauritius \nMayotte \nMexico \nMicronesia, Federated States of \nMoldova, Republic of \nMonaco \nMongolia \nMontserrat \nMorocco \nMozambique \nMyanmar \nNamibia \nNauru \nNepal \nNetherlands \nNetherlands Antilles \nNew Caledonia " +
+ "\nNew Zealand \nNicaragua \nNiger \nNigeria \nNiue \nNorfolk Island \nNorthern Ireland \nNorthern Mariana Islands \nNorway \nOman \nPakistan \nPalau \nPanama \nPapua New Guinea \nParaguay \nPeru \nPhilippines \nPitcairn \nPoland \nPortugal \nPuerto Rico \nQatar \nReunion \nRomania \nRussia \nRwanda \nSaint Kitts and Nevis \nSaint Lucia \nSaint Vincent and the Grenadines \nSamoa (Independent) \nSan Marino \nSao Tome and Principe \nSaudi Arabia \nScotland \nSenegal \nSerbia and Montenegro \nSeychelles \nSierra Leone \nSingapore \nSlovakia \nSlovenia \nSolomon Islands \nSomalia \nSouth Africa \nSouth Georgia and the South Sandwich Islands \nSouth Korea \nSpain \nSri Lanka \nSt. Helena \nSt. Pierre and Miquelon \nSuriname \nSvalbard and Jan Mayen Islands \nSwaziland \nSweden \nSwitzerland \nTaiwan \nTajikistan \nTanzania \nThailand \nTogo \nTokelau \nTonga \nTrinidad \nTrinidad and Tobago \nTunisia \nTurkey \n" +
+ "Turkmenistan \nTurks and Caicos Islands \nTuvalu \nUganda \nUkraine \nUnited Arab Emirates \nUnited Kingdom \nUnited States \nUnited States Minor Outlying Islands \nUruguay \nUzbekistan \nVanuatu \nVatican City State (Holy See) \nVenezuela \nViet Nam \nVirgin Islands (British) \nVirgin Islands (U.S.) \nWales \nWallis and Futuna Islands \nWestern Sahara \nYemen \nZambia \nZimbabwe ",
+ country_select("post", "origin")
+ )
+ end
+
+ def test_time_zone_select
+ @firm = Firm.new("D")
+ html = time_zone_select( "firm", "time_zone" )
+ assert_dom_equal "" +
+ "A \n" +
+ "B \n" +
+ "C \n" +
+ "D \n" +
+ "E " +
+ " ",
+ html
+ end
+
+ def test_time_zone_select_under_fields_for
+ @firm = Firm.new("D")
+
+ _erbout = ''
+
+ fields_for :firm, @firm do |f|
+ _erbout.concat f.time_zone_select(:time_zone)
+ end
+
+ assert_dom_equal(
+ "" +
+ "A \n" +
+ "B \n" +
+ "C \n" +
+ "D \n" +
+ "E " +
+ " ",
+ _erbout
+ )
+ end
+
+ def test_time_zone_select_with_blank
+ @firm = Firm.new("D")
+ html = time_zone_select("firm", "time_zone", nil, :include_blank => true)
+ assert_dom_equal "" +
+ " \n" +
+ "A \n" +
+ "B \n" +
+ "C \n" +
+ "D \n" +
+ "E " +
+ " ",
+ html
+ end
+
+ def test_time_zone_select_with_style
+ @firm = Firm.new("D")
+ html = time_zone_select("firm", "time_zone", nil, {},
+ "style" => "color: red")
+ assert_dom_equal "" +
+ "A \n" +
+ "B \n" +
+ "C \n" +
+ "D \n" +
+ "E " +
+ " ",
+ html
+ assert_dom_equal html, time_zone_select("firm", "time_zone", nil, {},
+ :style => "color: red")
+ end
+
+ def test_time_zone_select_with_blank_and_style
+ @firm = Firm.new("D")
+ html = time_zone_select("firm", "time_zone", nil,
+ { :include_blank => true }, "style" => "color: red")
+ assert_dom_equal "" +
+ " \n" +
+ "A \n" +
+ "B \n" +
+ "C \n" +
+ "D \n" +
+ "E " +
+ " ",
+ html
+ assert_dom_equal html, time_zone_select("firm", "time_zone", nil,
+ { :include_blank => true }, :style => "color: red")
+ end
+
+ def test_time_zone_select_with_priority_zones
+ @firm = Firm.new("D")
+ zones = [ TimeZone.new("A"), TimeZone.new("D") ]
+ html = time_zone_select("firm", "time_zone", zones )
+ assert_dom_equal "" +
+ "A \n" +
+ "D " +
+ "------------- \n" +
+ "B \n" +
+ "C \n" +
+ "E " +
+ " ",
+ html
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/form_tag_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/form_tag_helper_test.rb
new file mode 100644
index 00000000..7c42f9d4
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/form_tag_helper_test.rb
@@ -0,0 +1,172 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class FormTagHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::UrlHelper
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::FormTagHelper
+ include ActionView::Helpers::TextHelper
+ include ActionView::Helpers::CaptureHelper
+
+ def setup
+ @controller = Class.new do
+ def url_for(options, *parameters_for_method_reference)
+ "http://www.example.com"
+ end
+ end
+ @controller = @controller.new
+ end
+
+ def test_check_box_tag
+ actual = check_box_tag "admin"
+ expected = %( )
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_tag
+ actual = form_tag
+ expected = %()
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_tag_multipart
+ actual = form_tag({}, { 'multipart' => true })
+ expected = %( )
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_tag_with_method
+ actual = form_tag({}, { :method => :put })
+ expected = %(
)
+ assert_dom_equal expected, actual
+ end
+
+ def test_form_tag_with_block
+ _erbout = ''
+ form_tag("http://example.com") { _erbout.concat "Hello world!" }
+
+ expected = %(Hello world! )
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_form_tag_with_block_and_method
+ _erbout = ''
+ form_tag("http://example.com", :method => :put) { _erbout.concat "Hello world!" }
+
+ expected = %(
Hello world! )
+ assert_dom_equal expected, _erbout
+ end
+
+ def test_hidden_field_tag
+ actual = hidden_field_tag "id", 3
+ expected = %( )
+ assert_dom_equal expected, actual
+ end
+
+ def test_password_field_tag
+ actual = password_field_tag
+ expected = %( )
+ assert_dom_equal expected, actual
+ end
+
+ def test_radio_button_tag
+ actual = radio_button_tag "people", "david"
+ expected = %( )
+ assert_dom_equal expected, actual
+
+ actual = radio_button_tag("num_people", 5)
+ expected = %( )
+ assert_dom_equal expected, actual
+
+ actual = radio_button_tag("gender", "m") + radio_button_tag("gender", "f")
+ expected = %( )
+ assert_dom_equal expected, actual
+
+ actual = radio_button_tag("opinion", "-1") + radio_button_tag("opinion", "1")
+ expected = %( )
+ assert_dom_equal expected, actual
+
+ end
+
+ def test_select_tag
+ actual = select_tag "people", "david "
+ expected = %(david )
+ assert_dom_equal expected, actual
+ end
+
+ def test_text_area_tag_size_string
+ actual = text_area_tag "body", "hello world", "size" => "20x40"
+ expected = %(hello world )
+ assert_dom_equal expected, actual
+ end
+
+ def test_text_area_tag_size_symbol
+ actual = text_area_tag "body", "hello world", :size => "20x40"
+ expected = %(hello world )
+ assert_dom_equal expected, actual
+ end
+
+ def test_text_field_tag
+ actual = text_field_tag "title", "Hello!"
+ expected = %( )
+ assert_dom_equal expected, actual
+ end
+
+ def test_text_field_tag_class_string
+ actual = text_field_tag "title", "Hello!", "class" => "admin"
+ expected = %( )
+ assert_dom_equal expected, actual
+ end
+
+ def test_boolean_optios
+ assert_dom_equal %( ), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes")
+ assert_dom_equal %( ), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil)
+ assert_dom_equal %(david ), select_tag("people", "david ", :multiple => true)
+ assert_dom_equal %(david ), select_tag("people", "david ", :multiple => nil)
+ end
+
+ def test_stringify_symbol_keys
+ actual = text_field_tag "title", "Hello!", :id => "admin"
+ expected = %( )
+ assert_dom_equal expected, actual
+ end
+
+ def test_submit_tag
+ assert_dom_equal(
+ %( ),
+ submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!')")
+ )
+ end
+
+ def test_pass
+ assert_equal 1, 1
+ end
+end
+
+class DeprecatedFormTagHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::UrlHelper
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::FormTagHelper
+ include ActionView::Helpers::TextHelper
+ include ActionView::Helpers::CaptureHelper
+
+ def setup
+ @controller = Class.new do
+ def url_for(options, *parameters_for_method_reference)
+ "http://www.example.com"
+ end
+ end
+ @controller = @controller.new
+ end
+
+ def test_start_form_tag_deprecation
+ assert_deprecated /start_form_tag/ do
+ start_form_tag
+ end
+ end
+
+ def test_end_form_tag_deprecation
+ assert_deprecated /end_form_tag/ do
+ end_form_tag
+ end
+ end
+end
diff --git a/tracks/vendor/rails/actionpack/test/template/java_script_macros_helper_test.rb b/tracks/vendor/rails/actionpack/test/template/java_script_macros_helper_test.rb
new file mode 100644
index 00000000..ac629b97
--- /dev/null
+++ b/tracks/vendor/rails/actionpack/test/template/java_script_macros_helper_test.rb
@@ -0,0 +1,109 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class JavaScriptMacrosHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::JavaScriptHelper
+ include ActionView::Helpers::JavaScriptMacrosHelper
+
+ include ActionView::Helpers::UrlHelper
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::TextHelper
+ include ActionView::Helpers::FormHelper
+ include ActionView::Helpers::CaptureHelper
+
+ def setup
+ @controller = Class.new do
+ def url_for(options, *parameters_for_method_reference)
+ url = "http://www.example.com/"
+ url << options[:action].to_s if options and options[:action]
+ url
+ end
+ end
+ @controller = @controller.new
+ end
+
+
+ def test_auto_complete_field
+ assert_dom_equal %(),
+ auto_complete_field("some_input", :url => { :action => "autocomplete" });
+ assert_dom_equal %(),
+ auto_complete_field("some_input", :url => { :action => "autocomplete" }, :tokens => ',');
+ assert_dom_equal %(),
+ auto_complete_field("some_input", :url => { :action => "autocomplete" }, :tokens => [',']);
+ assert_dom_equal %(),
+ auto_complete_field("some_input", :url => { :action => "autocomplete" }, :min_chars => 3);
+ assert_dom_equal %(),
+ auto_complete_field("some_input", :url => { :action => "autocomplete" }, :on_hide => "function(element, update){alert('me');}");
+ assert_dom_equal %(),
+ auto_complete_field("some_input", :url => { :action => "autocomplete" }, :frequency => 2);
+ assert_dom_equal %(),
+ auto_complete_field("some_input", :url => { :action => "autocomplete" },
+ :after_update_element => "function(element,value){alert('You have chosen: '+value)}");
+ assert_dom_equal %(),
+ auto_complete_field("some_input", :url => { :action => "autocomplete" }, :param_name => 'huidriwusch');
+ end
+
+ def test_auto_complete_result
+ result = [ { :title => 'test1' }, { :title => 'test2' } ]
+ assert_equal %(),
+ auto_complete_result(result, :title)
+ assert_equal %(),
+ auto_complete_result(result, :title, "est")
+
+ resultuniq = [ { :title => 'test1' }, { :title => 'test1' } ]
+ assert_equal %(),
+ auto_complete_result(resultuniq, :title, "est")
+ end
+
+ def test_text_field_with_auto_complete
+ assert_match %(
+
+
+
+<%= @content_for_layout %>
+
+
+