(April 2007).
// 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
@@ -465,353 +476,472 @@ Field.scrollFreeActivate = function(field) {
}, 1);
}
-Ajax.InPlaceEditor = Class.create();
-Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
-Ajax.InPlaceEditor.prototype = {
+Ajax.InPlaceEditor = Class.create({
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;
- }
+ this.element = element = $(element);
+ this.prepareOptions();
+ this._controls = { };
+ arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+ Object.extend(this.options, options || { });
+ if (!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + '-inplaceeditor';
+ if ($(this.options.formId))
+ this.options.formId = '';
}
-
- if (this.options.externalControl) {
+ if (this.options.externalControl)
this.options.externalControl = $(this.options.externalControl);
- }
-
- this.originalBackground = Element.getStyle(this.element, 'background-color');
- if (!this.originalBackground) {
- this.originalBackground = "transparent";
- }
-
+ if (!this.options.externalControl)
+ this.options.externalControlOnly = false;
+ this._originalBackground = this.element.getStyle('background-color') || '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);
- }
+ this._boundCancelHandler = this.handleFormCancellation.bind(this);
+ this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+ this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+ this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+ this._boundWrapperHandler = this.wrapUp.bind(this);
+ this.registerListeners();
},
- 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;
+ checkForEscapeOrReturn: function(e) {
+ if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+ if (Event.KEY_ESC == e.keyCode)
+ this.handleFormCancellation(e);
+ else if (Event.KEY_RETURN == e.keyCode)
+ this.handleFormSubmission(e);
},
- 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);
+ createControl: function(mode, handler, extraClasses) {
+ var control = this.options[mode + 'Control'];
+ var text = this.options[mode + 'Text'];
+ if ('button' == control) {
+ var btn = document.createElement('input');
+ btn.type = 'submit';
+ btn.value = text;
+ btn.className = 'editor_' + mode + '_button';
+ if ('cancel' == mode)
+ btn.onclick = this._boundCancelHandler;
+ this._form.appendChild(btn);
+ this._controls[mode] = btn;
+ } else if ('link' == control) {
+ var link = document.createElement('a');
+ link.href = '#';
+ link.appendChild(document.createTextNode(text));
+ link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+ link.className = 'editor_' + mode + '_link';
+ if (extraClasses)
+ link.className += ' ' + extraClasses;
+ this._form.appendChild(link);
+ this._controls[mode] = link;
}
-
- 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 text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+ var fld;
+ if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+ fld = document.createElement('input');
+ fld.type = 'text';
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;
+ if (0 < size) fld.size = size;
} 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;
+ fld = document.createElement('textarea');
+ fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+ fld.cols = this.options.cols || 40;
}
-
- if(this.options.loadTextURL) {
+ fld.name = this.options.paramName;
+ fld.value = text; // No HTML breaks conversion anymore
+ fld.className = 'editor_field';
+ if (this.options.submitOnBlur)
+ fld.onblur = this._boundSubmitHandler;
+ this._controls.editor = fld;
+ if (this.options.loadTextURL)
this.loadExternalText();
- }
- this.form.appendChild(this.editField);
+ this._form.appendChild(this._controls.editor);
+ },
+ createForm: function() {
+ var ipe = this;
+ function addText(mode, condition) {
+ var text = ipe.options['text' + mode + 'Controls'];
+ if (!text || condition === false) return;
+ ipe._form.appendChild(document.createTextNode(text));
+ };
+ this._form = $(document.createElement('form'));
+ this._form.id = this.options.formId;
+ this._form.addClassName(this.options.formClassName);
+ this._form.onsubmit = this._boundSubmitHandler;
+ this.createEditField();
+ if ('textarea' == this._controls.editor.tagName.toLowerCase())
+ this._form.appendChild(document.createElement('br'));
+ if (this.options.onFormCustomization)
+ this.options.onFormCustomization(this, this._form);
+ addText('Before', this.options.okControl || this.options.cancelControl);
+ this.createControl('ok', this._boundSubmitHandler);
+ addText('Between', this.options.okControl && this.options.cancelControl);
+ this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+ addText('After', this.options.okControl || this.options.cancelControl);
+ },
+ destroy: function() {
+ if (this._oldInnerHTML)
+ this.element.innerHTML = this._oldInnerHTML;
+ this.leaveEditMode();
+ this.unregisterListeners();
+ },
+ enterEditMode: function(e) {
+ if (this._saving || this._editing) return;
+ this._editing = true;
+ this.triggerCallback('onEnterEditMode');
+ if (this.options.externalControl)
+ this.options.externalControl.hide();
+ this.element.hide();
+ this.createForm();
+ this.element.parentNode.insertBefore(this._form, this.element);
+ if (!this.options.loadTextURL)
+ this.postProcessEditField();
+ if (e) Event.stop(e);
+ },
+ enterHover: function(e) {
+ if (this.options.hoverClassName)
+ this.element.addClassName(this.options.hoverClassName);
+ if (this._saving) return;
+ this.triggerCallback('onEnterHover');
},
getText: function() {
return this.element.innerHTML;
},
+ handleAJAXFailure: function(transport) {
+ this.triggerCallback('onFailure', transport);
+ if (this._oldInnerHTML) {
+ this.element.innerHTML = this._oldInnerHTML;
+ this._oldInnerHTML = null;
+ }
+ },
+ handleFormCancellation: function(e) {
+ this.wrapUp();
+ if (e) Event.stop(e);
+ },
+ handleFormSubmission: function(e) {
+ var form = this._form;
+ var value = $F(this._controls.editor);
+ this.prepareSubmission();
+ var params = this.options.callback(form, value) || '';
+ if (Object.isString(params))
+ params = params.toQueryParams();
+ params.editorId = this.element.id;
+ if (this.options.htmlResponse) {
+ var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: params,
+ onComplete: this._boundWrapperHandler,
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Updater({ success: this.element }, this.url, options);
+ } else {
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: params,
+ onComplete: this._boundWrapperHandler,
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Request(this.url, options);
+ }
+ if (e) Event.stop(e);
+ },
+ leaveEditMode: function() {
+ this.element.removeClassName(this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this._originalBackground;
+ this.element.show();
+ if (this.options.externalControl)
+ this.options.externalControl.show();
+ this._saving = false;
+ this._editing = false;
+ this._oldInnerHTML = null;
+ this.triggerCallback('onLeaveEditMode');
+ },
+ leaveHover: function(e) {
+ if (this.options.hoverClassName)
+ this.element.removeClassName(this.options.hoverClassName);
+ if (this._saving) return;
+ this.triggerCallback('onLeaveHover');
+ },
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)
- );
+ this._form.addClassName(this.options.loadingClassName);
+ this._controls.editor.disabled = true;
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ this._form.removeClassName(this.options.loadingClassName);
+ var text = transport.responseText;
+ if (this.options.stripLoadedTextTags)
+ text = text.stripTags();
+ this._controls.editor.value = text;
+ this._controls.editor.disabled = false;
+ this.postProcessEditField();
+ }.bind(this),
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Request(this.options.loadTextURL, options);
},
- onLoadedExternalText: function(transport) {
- Element.removeClassName(this.form, this.options.loadingClassName);
- this.editField.disabled = false;
- this.editField.value = transport.responseText.stripTags();
- Field.scrollFreeActivate(this.editField);
+ postProcessEditField: function() {
+ var fpc = this.options.fieldPostCreation;
+ if (fpc)
+ $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
},
- onclickCancel: function() {
- this.onComplete();
- this.leaveEditMode();
- return false;
+ prepareOptions: function() {
+ this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+ Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+ [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+ Object.extend(this.options, defs);
+ }.bind(this));
},
- 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;
+ prepareSubmission: 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);
+ registerListeners: function() {
+ this._listeners = { };
+ var listener;
+ $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+ listener = this[pair.value].bind(this);
+ this._listeners[pair.key] = listener;
+ if (!this.options.externalControlOnly)
+ this.element.observe(pair.key, listener);
+ if (this.options.externalControl)
+ this.options.externalControl.observe(pair.key, listener);
+ }.bind(this));
},
removeForm: function() {
- if(this.form) {
- if (this.form.parentNode) Element.remove(this.form);
- this.form = null;
+ if (!this._form) return;
+ this._form.remove();
+ this._form = null;
+ this._controls = { };
+ },
+ showSaving: function() {
+ this._oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ this.element.addClassName(this.options.savingClassName);
+ this.element.style.backgroundColor = this._originalBackground;
+ this.element.show();
+ },
+ triggerCallback: function(cbName, arg) {
+ if ('function' == typeof this.options[cbName]) {
+ this.options[cbName](this, arg);
}
},
- 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)
+ unregisterListeners: function() {
+ $H(this._listeners).each(function(pair) {
+ if (!this.options.externalControlOnly)
+ this.element.stopObserving(pair.key, pair.value);
+ if (this.options.externalControl)
+ this.options.externalControl.stopObserving(pair.key, pair.value);
+ }.bind(this));
},
- 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) {
+ wrapUp: 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);
- }
+ // Can't use triggerCallback due to backward compatibility: requires
+ // binding + direct element
+ this._boundComplete(transport, this.element);
}
});
+Object.extend(Ajax.InPlaceEditor.prototype, {
+ dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+ initialize: function($super, element, url, options) {
+ this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+ $super(element, url, options);
+ },
+
+ createEditField: function() {
+ var list = document.createElement('select');
+ list.name = this.options.paramName;
+ list.size = 1;
+ this._controls.editor = list;
+ this._collection = this.options.collection || [];
+ if (this.options.loadCollectionURL)
+ this.loadCollection();
+ else
+ this.checkForExternalText();
+ this._form.appendChild(this._controls.editor);
+ },
+
+ loadCollection: function() {
+ this._form.addClassName(this.options.loadingClassName);
+ this.showLoadingText(this.options.loadingCollectionText);
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ var js = transport.responseText.strip();
+ if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+ throw 'Server returned an invalid collection representation.';
+ this._collection = eval(js);
+ this.checkForExternalText();
+ }.bind(this),
+ onFailure: this.onFailure
+ });
+ new Ajax.Request(this.options.loadCollectionURL, options);
+ },
+
+ showLoadingText: function(text) {
+ this._controls.editor.disabled = true;
+ var tempOption = this._controls.editor.firstChild;
+ if (!tempOption) {
+ tempOption = document.createElement('option');
+ tempOption.value = '';
+ this._controls.editor.appendChild(tempOption);
+ tempOption.selected = true;
+ }
+ tempOption.update((text || '').stripScripts().stripTags());
+ },
+
+ checkForExternalText: function() {
+ this._text = this.getText();
+ if (this.options.loadTextURL)
+ this.loadExternalText();
+ else
+ this.buildOptionList();
+ },
+
+ loadExternalText: function() {
+ this.showLoadingText(this.options.loadingText);
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ this._text = transport.responseText.strip();
+ this.buildOptionList();
+ }.bind(this),
+ onFailure: this.onFailure
+ });
+ new Ajax.Request(this.options.loadTextURL, options);
+ },
+
+ buildOptionList: function() {
+ this._form.removeClassName(this.options.loadingClassName);
+ this._collection = this._collection.map(function(entry) {
+ return 2 === entry.length ? entry : [entry, entry].flatten();
+ });
+ var marker = ('value' in this.options) ? this.options.value : this._text;
+ var textFound = this._collection.any(function(entry) {
+ return entry[0] == marker;
+ }.bind(this));
+ this._controls.editor.update('');
+ var option;
+ this._collection.each(function(entry, index) {
+ option = document.createElement('option');
+ option.value = entry[0];
+ option.selected = textFound ? entry[0] == marker : 0 == index;
+ option.appendChild(document.createTextNode(entry[1]));
+ this._controls.editor.appendChild(option);
+ }.bind(this));
+ this._controls.editor.disabled = false;
+ Field.scrollFreeActivate(this._controls.editor);
+ }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only exists for a while, in order to let ****
+//**** users adapt to the new API. Read up on the new ****
+//**** API and convert your code to it ASAP! ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+ if (!options) return;
+ function fallback(name, expr) {
+ if (name in options || expr === undefined) return;
+ options[name] = expr;
+ };
+ fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+ options.cancelLink == options.cancelButton == false ? false : undefined)));
+ fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+ options.okLink == options.okButton == false ? false : undefined)));
+ fallback('highlightColor', options.highlightcolor);
+ fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+ DefaultOptions: {
+ ajaxOptions: { },
+ autoRows: 3, // Use when multi-line w/ rows == 1
+ cancelControl: 'link', // 'link'|'button'|false
+ cancelText: 'cancel',
+ clickToEditText: 'Click to edit',
+ externalControl: null, // id|elt
+ externalControlOnly: false,
+ fieldPostCreation: 'activate', // 'activate'|'focus'|false
+ formClassName: 'inplaceeditor-form',
+ formId: null, // id|elt
+ highlightColor: '#ffff99',
+ highlightEndColor: '#ffffff',
+ hoverClassName: '',
+ htmlResponse: true,
+ loadingClassName: 'inplaceeditor-loading',
+ loadingText: 'Loading...',
+ okControl: 'button', // 'link'|'button'|false
+ okText: 'ok',
+ paramName: 'value',
+ rows: 1, // If 1 and multi-line, uses autoRows
+ savingClassName: 'inplaceeditor-saving',
+ savingText: 'Saving...',
+ size: 0,
+ stripLoadedTextTags: false,
+ submitOnBlur: false,
+ textAfterControls: '',
+ textBeforeControls: '',
+ textBetweenControls: ''
+ },
+ DefaultCallbacks: {
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ onComplete: function(transport, element) {
+ // For backward compatibility, this one is bound to the IPE, and passes
+ // the element directly. It was too often customized, so we don't break it.
+ new Effect.Highlight(element, {
+ startcolor: this.options.highlightColor, keepBackgroundImage: true });
+ },
+ onEnterEditMode: null,
+ onEnterHover: function(ipe) {
+ ipe.element.style.backgroundColor = ipe.options.highlightColor;
+ if (ipe._effect)
+ ipe._effect.cancel();
+ },
+ onFailure: function(transport, ipe) {
+ alert('Error communication with the server: ' + transport.responseText.stripTags());
+ },
+ onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+ onLeaveEditMode: null,
+ onLeaveHover: function(ipe) {
+ ipe._effect = new Effect.Highlight(ipe.element, {
+ startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+ restorecolor: ipe._originalBackground, keepBackgroundImage: true
+ });
+ }
+ },
+ Listeners: {
+ click: 'enterEditMode',
+ keydown: 'checkForEscapeOrReturn',
+ mouseover: 'enterHover',
+ mouseout: 'leaveHover'
+ }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+ loadingCollectionText: 'Loading options...'
+};
+
// 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 = {
+Form.Element.DelayedObserver = Class.create({
initialize: function(element, delay, callback) {
this.delay = delay || 0.5;
this.element = $(element);
@@ -830,4 +960,4 @@ Form.Element.DelayedObserver.prototype = {
this.timer = null;
this.callback(this.element, $F(this.element));
}
-};
+});
diff --git a/vendor/rails/actionpack/lib/action_view/helpers/javascripts/dragdrop.js b/vendor/rails/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
index c71ddb82..ccf4a1e4 100644
--- a/vendor/rails/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
+++ b/vendor/rails/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
@@ -1,10 +1,10 @@
-// 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)
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005-2007 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')
+if(Object.isUndefined(Effect))
throw("dragdrop.js requires including script.aculo.us' effects.js library");
var Droppables = {
@@ -20,14 +20,13 @@ var Droppables = {
greedy: true,
hoverclass: null,
tree: false
- }, arguments[1] || {});
+ }, arguments[1] || { });
// cache containers
if(options.containment) {
options._containers = [];
var containment = options.containment;
- if((typeof containment == 'object') &&
- (containment.constructor == Array)) {
+ if(Object.isArray(containment)) {
containment.each( function(c) { options._containers.push($(c)) });
} else {
options._containers.push($(containment));
@@ -87,21 +86,23 @@ var Droppables = {
show: function(point, element) {
if(!this.drops.length) return;
- var affected = [];
+ var drop, 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) {
+ if(affected.length>0)
drop = Droppables.findDeepestChild(affected);
+
+ if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
+ if (drop) {
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);
+ if (drop != this.last_active) Droppables.activate(drop);
}
},
@@ -110,8 +111,10 @@ var Droppables = {
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);
+ if (this.last_active.onDrop) {
+ this.last_active.onDrop(element, this.last_active.element, event);
+ return true;
+ }
},
reset: function() {
@@ -219,10 +222,7 @@ var Draggables = {
/*--------------------------------------------------------------------------*/
-var Draggable = Class.create();
-Draggable._dragging = {};
-
-Draggable.prototype = {
+var Draggable = Class.create({
initialize: function(element) {
var defaults = {
handle: false,
@@ -233,7 +233,7 @@ Draggable.prototype = {
});
},
endeffect: function(element) {
- var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
+ var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
queue: {scope:'_draggable', position:'end'},
afterFinish: function(){
@@ -243,6 +243,7 @@ Draggable.prototype = {
},
zindex: 1000,
revert: false,
+ quiet: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
@@ -250,7 +251,7 @@ Draggable.prototype = {
delay: 0
};
- if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
+ if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
Object.extend(defaults, {
starteffect: function(element) {
element._opacity = Element.getOpacity(element);
@@ -259,11 +260,11 @@ Draggable.prototype = {
}
});
- var options = Object.extend(defaults, arguments[1] || {});
+ var options = Object.extend(defaults, arguments[1] || { });
this.element = $(element);
- if(options.handle && (typeof options.handle == 'string'))
+ if(options.handle && Object.isString(options.handle))
this.handle = this.element.down('.'+options.handle, 0);
if(!this.handle) this.handle = $(options.handle);
@@ -276,7 +277,6 @@ Draggable.prototype = {
Element.makePositioned(this.element); // fix IE
- this.delta = this.currentDelta();
this.options = options;
this.dragging = false;
@@ -298,17 +298,17 @@ Draggable.prototype = {
},
initDrag: function(event) {
- if(typeof Draggable._dragging[this.element] != 'undefined' &&
+ if(!Object.isUndefined(Draggable._dragging[this.element]) &&
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;
+ if((tag_name = src.tagName.toUpperCase()) && (
+ tag_name=='INPUT' ||
+ tag_name=='SELECT' ||
+ tag_name=='OPTION' ||
+ tag_name=='BUTTON' ||
+ tag_name=='TEXTAREA')) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var pos = Position.cumulativeOffset(this.element);
@@ -321,6 +321,8 @@ Draggable.prototype = {
startDrag: function(event) {
this.dragging = true;
+ if(!this.delta)
+ this.delta = this.currentDelta();
if(this.options.zindex) {
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
@@ -329,7 +331,9 @@ Draggable.prototype = {
if(this.options.ghosting) {
this._clone = this.element.cloneNode(true);
- Position.absolutize(this.element);
+ this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+ if (!this.element._originallyAbsolute)
+ Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element);
}
@@ -351,8 +355,12 @@ Draggable.prototype = {
updateDrag: function(event, pointer) {
if(!this.dragging) this.startDrag(event);
- Position.prepare();
- Droppables.show(pointer, this.element);
+
+ if(!this.options.quiet){
+ Position.prepare();
+ Droppables.show(pointer, this.element);
+ }
+
Draggables.notify('onDrag', this, event);
this.draw(pointer);
@@ -380,30 +388,44 @@ Draggable.prototype = {
}
// fix AppleWebKit rendering
- if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+ if(Prototype.Browser.WebKit) window.scrollBy(0,0);
Event.stop(event);
},
finishDrag: function(event, success) {
this.dragging = false;
+
+ if(this.options.quiet){
+ Position.prepare();
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ Droppables.show(pointer, this.element);
+ }
if(this.options.ghosting) {
- Position.relativize(this.element);
+ if (!this.element._originallyAbsolute)
+ Position.relativize(this.element);
+ delete this.element._originallyAbsolute;
Element.remove(this._clone);
this._clone = null;
}
- if(success) Droppables.fire(event, this.element);
+ var dropped = false;
+ if(success) {
+ dropped = Droppables.fire(event, this.element);
+ if (!dropped) dropped = false;
+ }
+ if(dropped && this.options.onDropped) this.options.onDropped(this.element);
Draggables.notify('onEnd', this, event);
var revert = this.options.revert;
- if(revert && typeof revert == 'function') revert = revert(this.element);
+ if(revert && Object.isFunction(revert)) 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]);
+ if (dropped == 0 || revert != 'failure')
+ this.options.reverteffect(this.element,
+ d[1]-this.delta[1], d[0]-this.delta[0]);
} else {
this.delta = d;
}
@@ -451,15 +473,15 @@ Draggable.prototype = {
}.bind(this));
if(this.options.snap) {
- if(typeof this.options.snap == 'function') {
+ if(Object.isFunction(this.options.snap)) {
p = this.options.snap(p[0],p[1],this);
} else {
- if(this.options.snap instanceof Array) {
+ if(Object.isArray(this.options.snap)) {
p = p.map( function(v, i) {
- return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+ return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
} else {
p = p.map( function(v) {
- return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+ return (v/this.options.snap).round()*this.options.snap }.bind(this))
}
}}
@@ -543,12 +565,13 @@ Draggable.prototype = {
}
return { top: T, left: L, width: W, height: H };
}
-}
+});
+
+Draggable._dragging = { };
/*--------------------------------------------------------------------------*/
-var SortableObserver = Class.create();
-SortableObserver.prototype = {
+var SortableObserver = Class.create({
initialize: function(element, observer) {
this.element = $(element);
this.observer = observer;
@@ -564,15 +587,15 @@ SortableObserver.prototype = {
if(this.lastValue != Sortable.serialize(this.element))
this.observer(this.element)
}
-}
+});
var Sortable = {
SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
- sortables: {},
+ sortables: { },
_findRootElement: function(element) {
- while (element.tagName != "BODY") {
+ while (element.tagName.toUpperCase() != "BODY") {
if(element.id && Sortable.sortables[element.id]) return element;
element = element.parentNode;
}
@@ -612,13 +635,20 @@ var Sortable = {
delay: 0,
hoverclass: null,
ghosting: false,
+ quiet: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
format: this.SERIALIZE_RULE,
+
+ // these take arrays of elements or ids and can be
+ // used for better initialization performance
+ elements: false,
+ handles: false,
+
onChange: Prototype.emptyFunction,
onUpdate: Prototype.emptyFunction
- }, arguments[1] || {});
+ }, arguments[1] || { });
// clear any old sortable with same element
this.destroy(element);
@@ -626,6 +656,7 @@ var Sortable = {
// build options for the draggables
var options_for_draggable = {
revert: true,
+ quiet: options.quiet,
scroll: options.scroll,
scrollSpeed: options.scrollSpeed,
scrollSensitivity: options.scrollSensitivity,
@@ -679,10 +710,9 @@ var Sortable = {
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.elements || this.findElements(element, options) || []).each( function(e,i) {
+ var handle = options.handles ? $(options.handles[i]) :
+ (options.handle ? $(e).select('.' + options.handle)[0] : e);
options.draggables.push(
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
Droppables.add(e, options_for_droppable);
@@ -842,7 +872,7 @@ var Sortable = {
only: sortableOptions.only,
name: element.id,
format: sortableOptions.format
- }, arguments[1] || {});
+ }, arguments[1] || { });
var root = {
id: null,
@@ -866,7 +896,7 @@ var Sortable = {
sequence: function(element) {
element = $(element);
- var options = Object.extend(this.options(element), arguments[1] || {});
+ 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] : '';
@@ -875,9 +905,9 @@ var Sortable = {
setSequence: function(element, new_sequence) {
element = $(element);
- var options = Object.extend(this.options(element), arguments[2] || {});
+ var options = Object.extend(this.options(element), arguments[2] || { });
- var nodeMap = {};
+ 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];
@@ -895,7 +925,7 @@ var Sortable = {
serialize: function(element) {
element = $(element);
- var options = Object.extend(Sortable.options(element), arguments[1] || {});
+ var options = Object.extend(Sortable.options(element), arguments[1] || { });
var name = encodeURIComponent(
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
@@ -919,7 +949,7 @@ Element.isParent = function(child, element) {
return Element.isParent(child.parentNode, element);
}
-Element.findChildren = function(element, only, recursive, tagName) {
+Element.findChildren = function(element, only, recursive, tagName) {
if(!element.hasChildNodes()) return null;
tagName = tagName.toUpperCase();
if(only) only = [only].flatten();
diff --git a/vendor/rails/actionpack/lib/action_view/helpers/javascripts/effects.js b/vendor/rails/actionpack/lib/action_view/helpers/javascripts/effects.js
index 3b02eda2..65aed239 100644
--- a/vendor/rails/actionpack/lib/action_view/helpers/javascripts/effects.js
+++ b/vendor/rails/actionpack/lib/action_view/helpers/javascripts/effects.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
// Justin Palmer (http://encytemedia.com/)
// Mark Pilgrim (http://diveintomark.org/)
@@ -11,17 +11,17 @@
// returns self (or first argument) if not convertable
String.prototype.parseColor = function() {
var color = '#';
- if(this.slice(0,4) == 'rgb(') {
+ 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();
+ 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));
-}
+ return (color.length==7 ? color : (arguments[0] || this));
+};
/*--------------------------------------------------------------------------*/
@@ -30,7 +30,7 @@ Element.collectTextNodes = function(element) {
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) {
@@ -38,47 +38,18 @@ Element.collectTextNodesIgnoreClass = function(element, className) {
((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);
+ if (Prototype.Browser.WebKit) 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){
+Element.getInlineOpacity = function(element){
return $(element).style.opacity || '';
-}
+};
Element.forceRerendering = function(element) {
try {
@@ -91,31 +62,63 @@ Element.forceRerendering = function(element) {
/*--------------------------------------------------------------------------*/
-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'
},
+ 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) {
+ var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+ return pos > 1 ? 1 : pos;
+ },
+ wobble: function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+ },
+ pulse: function(pos, pulses) {
+ pulses = pulses || 5;
+ return (
+ ((pos % (1/pulses)) * pulses).round() == 0 ?
+ ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
+ 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
+ );
+ },
+ spring: function(pos) {
+ return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
+ },
+ none: function(pos) {
+ return 0;
+ },
+ full: function(pos) {
+ return 1;
+ }
+ },
+ DefaultOptions: {
+ duration: 1.0, // seconds
+ fps: 100, // 100= assume 66fps max.
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+ },
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';
+ if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
element = $(element);
$A(element.childNodes).each( function(child) {
- if(child.nodeType==3) {
+ if (child.nodeType==3) {
child.nodeValue.toArray().each( function(character) {
element.insertBefore(
- Builder.node('span',{style: tagifyStyle},
+ new Element('span', {style: tagifyStyle}).update(
character == ' ' ? String.fromCharCode(160) : character),
child);
});
@@ -125,8 +128,8 @@ var Effect = {
},
multiple: function(element, effect) {
var elements;
- if(((typeof element == 'object') ||
- (typeof element == 'function')) &&
+ if (((typeof element == 'object') ||
+ Object.isFunction(element)) &&
(element.length))
elements = element;
else
@@ -135,7 +138,7 @@ var Effect = {
var options = Object.extend({
speed: 0.1,
delay: 0.0
- }, arguments[2] || {});
+ }, arguments[2] || { });
var masterDelay = options.delay;
$A(elements).each( function(element, index) {
@@ -152,53 +155,20 @@ var Effect = {
effect = (effect || 'appear').toLowerCase();
var options = Object.extend({
queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
- }, arguments[2] || {});
+ }, 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;
- }
-};
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
/* ------------- core effects ------------- */
-Effect.ScopedQueue = Class.create();
-Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
+Effect.ScopedQueue = Class.create(Enumerable, {
initialize: function() {
this.effects = [];
- this.interval = null;
+ this.interval = null;
},
_each: function(iterator) {
this.effects._each(iterator);
@@ -206,7 +176,7 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
add: function(effect) {
var timestamp = new Date().getTime();
- var position = (typeof effect.options.queue == 'string') ?
+ var position = Object.isString(effect.options.queue) ?
effect.options.queue : effect.options.queue.position;
switch(position) {
@@ -229,115 +199,111 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
effect.startOn += timestamp;
effect.finishOn += timestamp;
- if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+ 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);
+ if (!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 15);
},
remove: function(effect) {
this.effects = this.effects.reject(function(e) { return e==effect });
- if(this.effects.length == 0) {
+ if (this.effects.length == 0) {
clearInterval(this.interval);
this.interval = null;
}
},
loop: function() {
var timePos = new Date().getTime();
- this.effects.invoke('loop', timePos);
+ for(var i=0, len=this.effects.length;i= this.startOn) {
- if(timePos >= this.finishOn) {
+ if (timePos >= this.startOn) {
+ if (timePos >= this.finishOn) {
this.render(1.0);
this.cancel();
this.event('beforeFinish');
- if(this.finish) this.finish();
+ 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) {
+ var pos = (timePos - this.startOn) / this.totalTime,
+ frame = (pos * this.totalFrames).round();
+ 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' ?
+ if (!this.options.sync)
+ Effect.Queues.get(Object.isString(this.options.queue) ?
'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);
+ if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if (this.options[eventName]) this.options[eventName](this);
},
inspect: function() {
- return '#';
+ var data = $H();
+ for(property in this)
+ if (!Object.isFunction(this[property])) data.set(property, this[property]);
+ return '#';
}
-}
+});
-Effect.Parallel = Class.create();
-Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+Effect.Parallel = Class.create(Effect.Base, {
initialize: function(effects) {
this.effects = effects || [];
this.start(arguments[1]);
@@ -350,35 +316,45 @@ Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
effect.render(1.0);
effect.cancel();
effect.event('beforeFinish');
- if(effect.finish) effect.finish(position);
+ if (effect.finish) effect.finish(position);
effect.event('afterFinish');
});
}
});
-Effect.Event = Class.create();
-Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
+Effect.Tween = Class.create(Effect.Base, {
+ initialize: function(object, from, to) {
+ object = Object.isString(object) ? $(object) : object;
+ var args = $A(arguments), method = args.last(),
+ options = args.length == 5 ? args[3] : null;
+ this.method = Object.isFunction(method) ? method.bind(object) :
+ Object.isFunction(object[method]) ? object[method].bind(object) :
+ function(value) { object[method] = value };
+ this.start(Object.extend({ from: from, to: to }, options || { }));
+ },
+ update: function(position) {
+ this.method(position);
+ }
+});
+
+Effect.Event = Class.create(Effect.Base, {
initialize: function() {
- var options = Object.extend({
- duration: 0
- }, arguments[0] || {});
- this.start(options);
+ this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
},
update: Prototype.emptyFunction
});
-Effect.Opacity = Class.create();
-Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+Effect.Opacity = Class.create(Effect.Base, {
initialize: function(element) {
this.element = $(element);
- if(!this.element) throw(Effect._elementDoesNotExistError);
+ 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))
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
this.element.setStyle({zoom: 1});
var options = Object.extend({
from: this.element.getOpacity() || 0.0,
to: 1.0
- }, arguments[1] || {});
+ }, arguments[1] || { });
this.start(options);
},
update: function(position) {
@@ -386,36 +362,30 @@ Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
}
});
-Effect.Move = Class.create();
-Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
+Effect.Move = Class.create(Effect.Base, {
initialize: function(element) {
this.element = $(element);
- if(!this.element) throw(Effect._elementDoesNotExistError);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
x: 0,
y: 0,
mode: 'relative'
- }, arguments[1] || {});
+ }, 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
+ if (this.options.mode == 'absolute') {
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'
+ left: (this.options.x * position + this.originalLeft).round() + 'px',
+ top: (this.options.y * position + this.originalTop).round() + 'px'
});
}
});
@@ -423,30 +393,29 @@ Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
return new Effect.Move(element,
- Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
};
-Effect.Scale = Class.create();
-Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+Effect.Scale = Class.create(Effect.Base, {
initialize: function(element, percent) {
this.element = $(element);
- if(!this.element) throw(Effect._elementDoesNotExistError);
+ 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
+ scaleMode: 'box', // 'box' or 'contents' or { } with provided values
scaleFrom: 100.0,
scaleTo: percent
- }, arguments[2] || {});
+ }, arguments[2] || { });
this.start(options);
},
setup: function() {
this.restoreAfterFinish = this.options.restoreAfterFinish || false;
this.elementPositioning = this.element.getStyle('position');
- this.originalStyle = {};
+ this.originalStyle = { };
['top','left','width','height','fontSize'].each( function(k) {
this.originalStyle[k] = this.element.style[k];
}.bind(this));
@@ -456,7 +425,7 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
var fontSize = this.element.getStyle('font-size') || '100%';
['em','px','%','pt'].each( function(fontSizeType) {
- if(fontSize.indexOf(fontSizeType)>0) {
+ if (fontSize.indexOf(fontSizeType)>0) {
this.fontSize = parseFloat(fontSize);
this.fontSizeType = fontSizeType;
}
@@ -465,60 +434,61 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
this.dims = null;
- if(this.options.scaleMode=='box')
+ if (this.options.scaleMode=='box')
this.dims = [this.element.offsetHeight, this.element.offsetWidth];
- if(/^content/.test(this.options.scaleMode))
+ if (/^content/.test(this.options.scaleMode))
this.dims = [this.element.scrollHeight, this.element.scrollWidth];
- if(!this.dims)
+ 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)
+ 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);
+ 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 d = { };
+ if (this.options.scaleX) d.width = width.round() + 'px';
+ if (this.options.scaleY) d.height = height.round() + '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';
+ 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';
+ 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), {
+Effect.Highlight = Class.create(Effect.Base, {
initialize: function(element) {
this.element = $(element);
- if(!this.element) throw(Effect._elementDoesNotExistError);
- var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+ 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; }
+ 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.oldStyle = { };
+ if (!this.options.keepBackgroundImage) {
+ 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)
+ 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));
@@ -526,7 +496,7 @@ Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype),
},
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)) });
+ return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
},
finish: function() {
this.element.setStyle(Object.extend(this.oldStyle, {
@@ -535,30 +505,21 @@ Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype),
}
});
-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));
- }
-});
+Effect.ScrollTo = function(element) {
+ var options = arguments[1] || { },
+ scrollOffsets = document.viewport.getScrollOffsets(),
+ elementOffsets = $(element).cumulativeOffset(),
+ max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
+
+ if (options.offset) elementOffsets[1] += options.offset;
+
+ return new Effect.Tween(null,
+ scrollOffsets.top,
+ elementOffsets[1] > max ? max : elementOffsets[1],
+ options,
+ function(p){ scrollTo(scrollOffsets.left, p.round()) }
+ );
+};
/* ------------- combination effects ------------- */
@@ -566,14 +527,15 @@ 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] || {});
+ 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);
@@ -586,9 +548,9 @@ Effect.Appear = function(element) {
},
beforeSetup: function(effect) {
effect.element.setOpacity(effect.options.from).show();
- }}, arguments[1] || {});
+ }}, arguments[1] || { });
return new Effect.Opacity(element,options);
-}
+};
Effect.Puff = function(element) {
element = $(element);
@@ -610,9 +572,9 @@ Effect.Puff = function(element) {
},
afterFinishInternal: function(effect) {
effect.effects[0].element.hide().setStyle(oldStyle); }
- }, arguments[1] || {})
+ }, arguments[1] || { })
);
-}
+};
Effect.BlindUp = function(element) {
element = $(element);
@@ -624,9 +586,9 @@ Effect.BlindUp = function(element) {
afterFinishInternal: function(effect) {
effect.element.hide().undoClipping();
}
- }, arguments[1] || {})
+ }, arguments[1] || { })
);
-}
+};
Effect.BlindDown = function(element) {
element = $(element);
@@ -643,8 +605,8 @@ Effect.BlindDown = function(element) {
afterFinishInternal: function(effect) {
effect.element.undoClipping();
}
- }, arguments[1] || {}));
-}
+ }, arguments[1] || { }));
+};
Effect.SwitchOff = function(element) {
element = $(element);
@@ -665,8 +627,8 @@ Effect.SwitchOff = function(element) {
}
})
}
- }, arguments[1] || {}));
-}
+ }, arguments[1] || { }));
+};
Effect.DropOut = function(element) {
element = $(element);
@@ -685,29 +647,35 @@ Effect.DropOut = function(element) {
afterFinishInternal: function(effect) {
effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
}
- }, arguments[1] || {}));
-}
+ }, arguments[1] || { }));
+};
Effect.Shake = function(element) {
element = $(element);
+ var options = Object.extend({
+ distance: 20,
+ duration: 0.5
+ }, arguments[1] || {});
+ var distance = parseFloat(options.distance);
+ var split = parseFloat(options.duration) / 10.0;
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) {
+ return new Effect.Move(element,
+ { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
- { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
- { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
- { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
- { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
- { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+ { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
effect.element.undoPositioned().setStyle(oldStyle);
}}) }}) }}) }}) }}) }});
-}
+};
Effect.SlideDown = function(element) {
element = $(element).cleanWhitespace();
@@ -723,7 +691,7 @@ Effect.SlideDown = function(element) {
afterSetup: function(effect) {
effect.element.makePositioned();
effect.element.down().makePositioned();
- if(window.opera) effect.element.setStyle({top: ''});
+ if (window.opera) effect.element.setStyle({top: ''});
effect.element.makeClipping().setStyle({height: '0px'}).show();
},
afterUpdateInternal: function(effect) {
@@ -733,23 +701,25 @@ Effect.SlideDown = function(element) {
afterFinishInternal: function(effect) {
effect.element.undoClipping().undoPositioned();
effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
- }, arguments[1] || {})
+ }, arguments[1] || { })
);
-}
+};
Effect.SlideUp = function(element) {
element = $(element).cleanWhitespace();
var oldInnerBottom = element.down().getStyle('bottom');
+ var elementDimensions = element.getDimensions();
return new Effect.Scale(element, window.opera ? 0 : 1,
Object.extend({ scaleContent: false,
scaleX: false,
scaleMode: 'box',
scaleFrom: 100,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
- beforeStartInternal: function(effect) {
+ afterSetup: function(effect) {
effect.element.makePositioned();
effect.element.down().makePositioned();
- if(window.opera) effect.element.setStyle({top: ''});
+ if (window.opera) effect.element.setStyle({top: ''});
effect.element.makeClipping().show();
},
afterUpdateInternal: function(effect) {
@@ -757,12 +727,12 @@ Effect.SlideUp = function(element) {
(effect.dims[0] - effect.element.clientHeight) + 'px' });
},
afterFinishInternal: function(effect) {
- effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
- effect.element.down().undoPositioned();
+ effect.element.hide().undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
}
- }, arguments[1] || {})
+ }, arguments[1] || { })
);
-}
+};
// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
@@ -775,7 +745,7 @@ Effect.Squish = function(element) {
effect.element.hide().undoClipping();
}
});
-}
+};
Effect.Grow = function(element) {
element = $(element);
@@ -784,7 +754,7 @@ Effect.Grow = function(element) {
moveTransition: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.full
- }, arguments[1] || {});
+ }, arguments[1] || { });
var oldStyle = {
top: element.style.top,
left: element.style.left,
@@ -849,7 +819,7 @@ Effect.Grow = function(element) {
)
}
});
-}
+};
Effect.Shrink = function(element) {
element = $(element);
@@ -858,7 +828,7 @@ Effect.Shrink = function(element) {
moveTransition: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.none
- }, arguments[1] || {});
+ }, arguments[1] || { });
var oldStyle = {
top: element.style.top,
left: element.style.left,
@@ -903,11 +873,11 @@ Effect.Shrink = function(element) {
effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
}, options)
);
-}
+};
Effect.Pulsate = function(element) {
element = $(element);
- var options = arguments[1] || {};
+ 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)) };
@@ -916,7 +886,7 @@ Effect.Pulsate = function(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);
@@ -936,37 +906,71 @@ Effect.Fold = function(element) {
afterFinishInternal: function(effect) {
effect.element.hide().undoClipping().setStyle(oldStyle);
} });
- }}, arguments[1] || {}));
+ }}, arguments[1] || { }));
};
-Effect.Morph = Class.create();
-Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
+Effect.Morph = Class.create(Effect.Base, {
initialize: function(element) {
this.element = $(element);
- if(!this.element) throw(Effect._elementDoesNotExistError);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
- style: ''
- }, arguments[1] || {});
+ style: { }
+ }, arguments[1] || { });
+
+ if (!Object.isString(options.style)) this.style = $H(options.style);
+ else {
+ if (options.style.include(':'))
+ this.style = options.style.parseStyle();
+ else {
+ this.element.addClassName(options.style);
+ this.style = $H(this.element.getStyles());
+ this.element.removeClassName(options.style);
+ var css = this.element.getStyles();
+ this.style = this.style.reject(function(style) {
+ return style.value == css[style.key];
+ });
+ options.afterFinishInternal = function(effect) {
+ effect.element.addClassName(effect.options.style);
+ effect.transforms.each(function(transform) {
+ effect.element.style[transform.style] = '';
+ });
+ }
+ }
+ }
this.start(options);
},
+
setup: function(){
function parseColor(color){
- if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+ 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
- });
+ this.transforms = this.style.map(function(pair){
+ var property = pair[0], value = pair[1], unit = null;
+
+ if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+ value = value.parseColor();
+ unit = 'color';
+ } else if (property == 'opacity') {
+ value = parseFloat(value);
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ } else if (Element.CSS_LENGTH.test(value)) {
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+ value = parseFloat(components[1]);
+ unit = (components.length == 3) ? components[2] : null;
+ }
+
+ var originalValue = this.element.getStyle(property);
+ return {
+ style: property.camelize(),
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
+ targetValue: unit=='color' ? parseColor(value) : value,
+ unit: unit
+ };
}.bind(this)).reject(function(transform){
return (
(transform.originalValue == transform.targetValue) ||
@@ -978,32 +982,35 @@ Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
});
},
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);
+ var style = { }, transform, i = this.transforms.length;
+ while(i--)
+ style[(transform = this.transforms[i]).style] =
+ transform.unit=='color' ? '#'+
+ (Math.round(transform.originalValue[0]+
+ (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+ (Math.round(transform.originalValue[1]+
+ (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+ (Math.round(transform.originalValue[2]+
+ (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+ (transform.originalValue +
+ (transform.targetValue - transform.originalValue) * position).toFixed(3) +
+ (transform.unit === null ? '' : transform.unit);
+ this.element.setStyle(style, true);
}
});
-Effect.Transform = Class.create();
-Object.extend(Effect.Transform.prototype, {
+Effect.Transform = Class.create({
initialize: function(tracks){
this.tracks = [];
- this.options = arguments[1] || {};
+ this.options = arguments[1] || { };
this.addTracks(tracks);
},
addTracks: function(tracks){
tracks.each(function(track){
- var data = $H(track).values().first();
+ track = $H(track);
+ var data = track.values().first();
this.tracks.push($H({
- ids: $H(track).keys().first(),
+ ids: track.keys().first(),
effect: Effect.Morph,
options: { style: data }
}));
@@ -1013,76 +1020,101 @@ Object.extend(Effect.Transform.prototype, {
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)) });
+ var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+ var elements = [$(ids) || $$(ids)].flatten();
+ return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, 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_PROPERTIES = $w(
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
+ 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+ 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+ 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+ 'fontSize fontWeight height left letterSpacing lineHeight ' +
+ 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+ 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+ 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+ 'right textIndent top width wordSpacing zIndex');
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
- var element = Element.extend(document.createElement('div'));
- element.innerHTML = '
';
- var style = element.down().style, styleRules = $H();
+ var style, styleRules = $H();
+ if (Prototype.Browser.WebKit)
+ style = new Element('div',{style:this}).style;
+ else {
+ String.__parseStyleElement.innerHTML = '
';
+ style = String.__parseStyleElement.childNodes[0].style;
+ }
Element.CSS_PROPERTIES.each(function(property){
- if(style[property]) styleRules[property] = style[property];
+ if (style[property]) styleRules.set(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;
+ if (Prototype.Browser.IE && this.include('opacity'))
+ styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+ return styleRules;
};
-Element.morph = function(element, style) {
- new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
- return element;
+if (document.defaultView && document.defaultView.getComputedStyle) {
+ Element.getStyles = function(element) {
+ var css = document.defaultView.getComputedStyle($(element), null);
+ return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+ styles[property] = css[property];
+ return styles;
+ });
+ };
+} else {
+ Element.getStyles = function(element) {
+ element = $(element);
+ var css = element.currentStyle, styles;
+ styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
+ hash.set(property, css[property]);
+ return hash;
+ });
+ if (!styles.opacity) styles.set('opacity', element.getOpacity());
+ return styles;
+ };
};
-['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
- 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
- function(f) { Element.Methods[f] = Element[f]; }
+Effect.Methods = {
+ morph: function(element, style) {
+ element = $(element);
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+ return element;
+ },
+ visualEffect: function(element, effect, options) {
+ element = $(element)
+ var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+ new Effect[klass](element, options);
+ return element;
+ },
+ highlight: function(element, options) {
+ element = $(element);
+ new Effect.Highlight(element, options);
+ return element;
+ }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+ 'pulsate shake puff squish switchOff dropOut').each(
+ function(effect) {
+ Effect.Methods[effect] = function(element, options){
+ element = $(element);
+ Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+ return element;
+ }
+ }
);
-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);
-};
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
+ function(f) { Effect.Methods[f] = Element[f]; }
+);
-Element.addMethods();
\ No newline at end of file
+Element.addMethods(Effect.Methods);
diff --git a/vendor/rails/actionpack/lib/action_view/helpers/javascripts/prototype.js b/vendor/rails/actionpack/lib/action_view/helpers/javascripts/prototype.js
index 50582217..546f9fe4 100644
--- a/vendor/rails/actionpack/lib/action_view/helpers/javascripts/prototype.js
+++ b/vendor/rails/actionpack/lib/action_view/helpers/javascripts/prototype.js
@@ -1,43 +1,114 @@
-/* Prototype JavaScript framework, version 1.5.0
+/* Prototype JavaScript framework, version 1.6.0.1
* (c) 2005-2007 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/
+ * For details, see the Prototype web site: http://www.prototypejs.org/
*
-/*--------------------------------------------------------------------------*/
+ *--------------------------------------------------------------------------*/
var Prototype = {
- Version: '1.5.0',
- BrowserFeatures: {
- XPath: !!document.evaluate
+ Version: '1.6.0.1',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
},
- ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)',
- emptyFunction: function() {},
- K: function(x) { return x }
-}
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ document.createElement('div').__proto__ &&
+ document.createElement('div').__proto__ !==
+ document.createElement('form').__proto__
+ },
+ ScriptFragment: '')
- # => <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:
+ # classes for table rows. 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.
#
+ # ==== Examples
+ # # Alternate CSS classes for even and odd numbers...
+ # @items = [1,2,3,4]
+ #
# <% @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.
#
+ # # Cycle CSS classes for rows, and text colors for values within each row
+ # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
+ # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
+ # {:first => 'June', :middle => 'Dae', :last => 'Jones'}]
# <% @items.each do |item| %>
- # "row_class")
+ # "row_class") -%>">
#
# <% item.values.each do |value| %>
+ # <%# Create a named cycle "colors" %>
# "colors") -%>">
- # value
+ # <%= value %>
#
# <% end %>
# <% reset_cycle("colors") %>
@@ -317,6 +367,23 @@ module ActionView
# 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.
+ #
+ # ==== Example
+ # # Alternate CSS classes for even and odd numbers...
+ # @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
+ #
+ # <% @items.each do |item| %>
+ # ">
+ # <% item.each do |value| %>
+ # "colors") -%>">
+ # <%= value %>
+ #
+ # <% end %>
+ #
+ # <% reset_cycle("colors") %>
+ #
+ # <% end %>
+ #
def reset_cycle(name = "default")
cycle = get_cycle(name)
cycle.reset unless cycle.nil?
@@ -340,7 +407,7 @@ module ActionView
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
@@ -369,8 +436,8 @@ module ActionView
[-\w]+ # subdomain or domain
(?:\.[-\w]+)* # remaining subdomains or domain
(?::\d+)? # port
- (?:/(?:(?:[~\w\+%-]|(?:[,.;:][^\s$]))+)?)* # path
- (?:\?[\w\+%&=.;-]+)? # query string
+ (?:/(?:(?:[~\w\+@%-]|(?:[,.;:][^\s$]))+)?)* # path
+ (?:\?[\w\+@%&=.;-]+)? # query string
(?:\#[\w\-]*)? # trailing anchor
)
([[:punct:]]|\s|<|$) # trailing text
@@ -395,10 +462,16 @@ module ActionView
# 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)
+ body = text.dup
text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
text = $1
- text = yield(text) if block_given?
- %{#{text} }
+
+ if body.match(/]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
+ text
+ else
+ display_text = (block_given?) ? yield(text) : text
+ %{ #{display_text} }
+ end
end
end
end
diff --git a/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb
index 6a665853..eb339118 100644
--- a/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb
@@ -1,79 +1,160 @@
-require File.dirname(__FILE__) + '/javascript_helper'
+require 'action_view/helpers/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.
+ # Provides a set of methods for making links and getting URLs that
+ # depend on the routing subsystem (see ActionController::Routing).
+ # This allows you to use the same format for links in views
+ # and controllers.
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
+
+ # Returns the URL for the set of +options+ provided. This takes the
+ # same options as url_for in ActionController (see the
+ # documentation for ActionController::Base#url_for). Note that by default
+ # :only_path is true so you'll get the relative /controller/action
+ # instead of the fully qualified URL like 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
+ #
+ # ==== Options
+ # * :anchor -- specifies the anchor name to be appended to the path.
+ # * :only_path -- if true, returns the relative URL (omitting the protocol, host name, and port) (true by default unless :host is specified)
+ # * :trailing_slash -- if true, adds a trailing slash, as in "/archive/2005/". Note that this
+ # is currently not recommended since it breaks caching.
+ # * :host -- overrides the default (current) host if provided
+ # * :protocol -- overrides the default (current) protocol if provided
+ # * :user -- Inline HTTP authentication (only plucked out if :password is also present)
+ # * :password -- Inline HTTP authentication (only plucked out if :user is also present)
+ # * :escape -- Determines whether the returned URL will be HTML escaped or not (true by default)
+ #
+ # ==== Relying on named routes
+ #
+ # If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter,
+ # you'll trigger the named route for that record. The lookup will happen on the name of the class. So passing
+ # a Workshop object will attempt to use the workshop_path route. If you have a nested route, such as
+ # admin_workshop_path you'll have to call that explicitly (it's impossible for url_for to guess that route).
+ #
+ # ==== Examples
+ # <%= url_for(:action => 'index') %>
+ # # => /blog/
+ #
+ # <%= url_for(:action => 'find', :controller => 'books') %>
+ # # => /books/find
+ #
+ # <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %>
+ # # => https://www.railsapplication.com/members/login/
+ #
+ # <%= url_for(:action => 'play', :anchor => 'player') %>
+ # # => /messages/play/#player
+ #
+ # <%= url_for(:action => 'checkout', :anchor => 'tax&ship') %>
+ # # => /testing/jump/#tax&ship
+ #
+ # <%= url_for(:action => 'checkout', :anchor => 'tax&ship', :escape => false) %>
+ # # => /testing/jump/#tax&ship
+ #
+ # <%= url_for(Workshop.new) %>
+ # # relies on Workshop answering a new_record? call (and in this case returning true)
+ # # => /workshops
+ #
+ # <%= url_for(@workshop) %>
+ # # calls @workshop.to_s
+ # # => /workshops/5
+ def url_for(options = {})
+ case options
+ when Hash
+ show_path = options[:host].nil? ? true : false
+ options = { :only_path => show_path }.update(options.symbolize_keys)
+ escape = options.key?(:escape) ? options.delete(:escape) : true
+ url = @controller.send(:url_for, options)
+ when String
escape = true
+ url = options
+ when NilClass
+ url = @controller.send(:url_for, nil)
+ else
+ escape = false
+ url = polymorphic_path(options)
end
- url = @controller.send(:url_for, options, *parameters_for_method_reference)
- escape ? html_escape(url) : url
+ escape ? escape_once(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.
+ # 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
+ # 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, or use +:back+ to link to the referrer - a JavaScript back
+ # link will be used in place of a referrer if none exists. 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
+ # ==== Options
+ # * :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
+ # * :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
+ # * :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
+ # Note that if the user has JavaScript disabled, the request will fall back
+ # to using GET. If you are relying on the POST behavior, you should check
+ # for it in your controller's action by using the request object's methods
# for post?, delete? or put?.
+ # * The +html_options+ will accept a hash of html attributes for the link tag.
+ #
+ # Note that if the user has JavaScript disabled, the request will fall back
+ # to using GET. If :href=>'#' is used and the user has JavaScript disabled
+ # clicking the link will have no effect. If you are relying on the POST
+ # behavior, your should check for it in your controller's action by using the
+ # request object's 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.
#
+ # ==== Examples
# link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
+ # # => Visit Other Site
+ #
# link_to "Help", { :action => "help" }, :popup => true
+ # # => Help
+ #
# link_to "View Image", { :action => "view" }, :popup => ['new_window_name', 'height=300,width=600']
+ # # => View Image
+ #
# 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)
+ # # => Delete Image
+ def link_to(name, options = {}, html_options = nil)
+ url = case options
+ when String
+ options
+ when :back
+ @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
+ else
+ self.url_for(options)
+ end
+
if html_options
html_options = html_options.stringify_keys
- convert_options_to_javascript!(html_options)
+ href = html_options['href']
+ convert_options_to_javascript!(html_options, url)
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} "
+
+ href_attr = "href=\"#{url}\"" unless href
+ "#{name || url} "
end
# Generates a form containing a single button that submits to the URL created
@@ -88,32 +169,36 @@ module ActionView
# 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
+ # 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.
#
+ # ==== Options
+ # The +options+ hash accepts the same options at url_for.
+ #
+ # There are a few special +html_options+:
+ # * :method -- specifies the anchor name to be appended to the path.
+ # * :disabled -- specifies the anchor name to be appended to the path.
+ # * :confirm -- 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.
+ #
+ # ==== Examples
+ # <%= button_to "New", :action => "new" %>
+ # # => ""
+ #
# 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 ))
@@ -124,7 +209,12 @@ module ActionView
end
form_method = method.to_s == 'get' ? 'get' : 'post'
-
+
+ request_token_tag = ''
+ if form_method == 'post' && protect_against_forgery?
+ request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
+ end
+
if confirm = html_options.delete("confirm")
html_options["onclick"] = "return #{confirm_javascript_function(confirm)};"
end
@@ -133,112 +223,118 @@ module ActionView
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
+ # +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.
+ # one exists). You can give link_to_unless_current a block which will
+ # specialize the default behavior (e.g., show a "Start Here" link rather
+ # than the link's text).
+ #
+ # ==== Examples
+ # Let's say you have a navigation menu...
#
#
# <%= 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:
+ # If in the "about" action, it will render...
#
#
- 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
+ #
+ # ...but if in the "home" action, it will render:
+ #
+ #
+ #
+ # The implicit block given to link_to_unless_current is evaluated if the current
+ # action is the action given. So, if we had a comments page and wanted to render a
+ # "Go Back" link instead of a link to the comments page, we could do something like this...
+ #
+ # <%=
+ # link_to_unless_current("Comment", { :controller => 'comments', :action => 'new}) do
+ # link_to("Go back", { :controller => 'posts', :action => 'index' })
+ # end
+ # %>
+ def link_to_unless_current(name, options = {}, html_options = {}, &block)
+ link_to_unless current_page?(options), name, options, html_options, &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).
+ # +options+ unless +condition+ is true, in which case only the name is
+ # returned. To specialize the default behavior (i.e., show a login link rather
+ # than just the plaintext link text), you can pass a block that
+ # accepts the name or the full argument list for link_to_unless.
#
+ # ==== Examples
# <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
+ # # If the user is logged in...
+ # # => 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)
+ # <%=
+ # link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
+ # link_to(name, { :controller => "accounts", :action => "signup" })
+ # end
+ # %>
+ # # If the user is logged in...
+ # # => Reply
+ # # If not...
+ # # => Reply
+ def link_to_unless(condition, name, options = {}, html_options = {}, &block)
if condition
if block_given?
- block.arity <= 1 ? yield(name) : yield(name, options, html_options, *parameters_for_method_reference)
+ block.arity <= 1 ? yield(name) : yield(name, options, html_options)
else
name
end
else
- link_to(name, options, html_options, *parameters_for_method_reference)
- end
+ link_to(name, options, html_options)
+ 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
+ # +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
+ #
+ # ==== Examples
+ # <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %>
+ # # If the user isn't logged in...
+ # # => Login
+ #
+ # <%=
+ # link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do
+ # link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user })
+ # end
+ # %>
+ # # If the user isn't logged in...
+ # # => Login
+ # # If they are logged in...
+ # # => my_username
+ def link_to_if(condition, name, options = {}, html_options = {}, &block)
+ link_to_unless !condition, name, options, html_options, &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+.
+ # HTML attributes for the link can be passed in +html_options+.
#
- # mail_to has several methods for hindering email harvestors and customizing
+ # mail_to has several methods for hindering email harvesters and customizing
# the email itself by passing special keys to +html_options+.
#
- # Special HTML Options:
- #
+ # ==== 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
@@ -257,23 +353,25 @@ module ActionView
# * :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" # =>
- #
+ # ==== Examples
+ # mail_to "me@domain.com"
+ # # => me@domain.com
#
- # mail_to "me@domain.com", "My email", :encode => "hex" # =>
- # My email
+ # mail_to "me@domain.com", "My email", :encode => "javascript"
+ # # =>
#
- # 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", :encode => "hex"
+ # # => My email
#
- # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com",
- # :subject => "This is an example email" # =>
- # 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")
+ encode = html_options.delete("encode").to_s
cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body")
string = ''
@@ -295,8 +393,16 @@ module ActionView
for i in 0...tmp.length
string << sprintf("%%%x",tmp[i])
end
- ""
+ ""
elsif encode == "hex"
+ email_address_encoded = ''
+ email_address_obfuscated.each_byte do |c|
+ email_address_encoded << sprintf("%d;", c)
+ end
+
+ protocol = 'mailto:'
+ protocol.each_byte { |c| string << sprintf("%d;", c) }
+
for i in 0...email_address.length
if email_address[i,1] =~ /\w/
string << sprintf("%%%x",email_address[i])
@@ -304,13 +410,28 @@ module ActionView
string << email_address[i,1]
end
end
- content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{string}#{extras}" })
+ content_tag "a", name || email_address_encoded, html_options.merge({ "href" => "#{string}#{extras}" })
else
content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
end
end
- # True if the current request uri was generated by the given +options+.
+ # True if the current request URI was generated by the given +options+.
+ #
+ # ==== Examples
+ # Let's say we're in the /shop/checkout action.
+ #
+ # current_page?(:action => 'process')
+ # # => false
+ #
+ # current_page?(:controller => 'shop', :action => 'checkout')
+ # # => true
+ #
+ # current_page?(:action => 'checkout')
+ # # => true
+ #
+ # current_page?(:controller => 'library', :action => 'checkout')
+ # # => false
def current_page?(options)
url_string = CGI.escapeHTML(url_for(options))
request = @controller.request
@@ -322,22 +443,14 @@ module ActionView
end
private
- def convert_options_to_javascript!(html_options)
+ def convert_options_to_javascript!(html_options, url = '')
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
-
+ method, href = html_options.delete("method"), html_options['href']
+
html_options["onclick"] = case
when popup && method
- raise ActionView::ActionViewError, "You can't use :popup and :post in the same link"
+ raise ActionView::ActionViewError, "You can't use :popup and :method in the same link"
when confirm && popup
"if (#{confirm_javascript_function(confirm)}) { #{popup_javascript_function(popup)} };return false;"
when confirm && method
@@ -345,32 +458,37 @@ module ActionView
when confirm
"return #{confirm_javascript_function(confirm)};"
when method
- "#{method_javascript_function(method)}return false;"
+ "#{method_javascript_function(method, url, href)}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 =
+
+ def method_javascript_function(method, url = '', href = nil)
+ action = (href && url.size > 0) ? "'#{url}'" : 'this.href'
+ submit_function =
"var f = document.createElement('form'); f.style.display = 'none'; " +
- "this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;"
-
+ "this.parentNode.appendChild(f); f.method = 'POST'; f.action = #{action};"
+
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
-
+
+ if protect_against_forgery?
+ submit_function << "var s = document.createElement('input'); s.setAttribute('type', 'hidden'); "
+ submit_function << "s.setAttribute('name', '#{request_forgery_protection_token}'); s.setAttribute('value', '#{escape_javascript form_authenticity_token}'); f.appendChild(s);"
+ end
submit_function << "f.submit();"
end
diff --git a/vendor/rails/actionpack/lib/action_view/partials.rb b/vendor/rails/actionpack/lib/action_view/partials.rb
index 063ff568..0795b8ec 100644
--- a/vendor/rails/actionpack/lib/action_view/partials.rb
+++ b/vendor/rails/actionpack/lib/action_view/partials.rb
@@ -1,13 +1,13 @@
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.
+ # 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
+ # This would render "advertiser/_account.erb" 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:
@@ -18,8 +18,8 @@ module ActionView
# <%= 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.
+ # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then render
+ # "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
#
# == Rendering a collection of partials
#
@@ -30,62 +30,131 @@ module ActionView
#
# <%= 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
+ # This will render "advertiser/_ad.erb" 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.
+ # This will render the partial "advertisement/_ad.erb" regardless of which controller this is being called from.
+ #
+ # == Rendering partials with layouts
+ #
+ # Partials can have their own layouts applied to them. These layouts are different than the ones that are specified globally
+ # for the entire action, but they work in a similar fashion. Imagine a list with two types of users:
+ #
+ # <%# app/views/users/index.html.erb &>
+ # Here's the administrator:
+ # <%= render :partial => "user", :layout => "administrator", :locals => { :user => administrator } %>
+ #
+ # Here's the editor:
+ # <%= render :partial => "user", :layout => "editor", :locals => { :user => editor } %>
+ #
+ # <%# app/views/users/_user.html.erb &>
+ # Name: <%= user.name %>
+ #
+ # <%# app/views/users/_administrator.html.erb &>
+ #
+ # Budget: $<%= user.budget %>
+ # <%= yield %>
+ #
+ #
+ # <%# app/views/users/_editor.html.erb &>
+ #
+ # Deadline: $<%= user.deadline %>
+ # <%= yield %>
+ #
+ #
+ # ...this will return:
+ #
+ # Here's the administrator:
+ #
+ # Budget: $<%= user.budget %>
+ # Name: <%= user.name %>
+ #
+ #
+ # Here's the editor:
+ #
+ # Deadline: $<%= user.deadline %>
+ # Name: <%= user.name %>
+ #
+ #
+ # You can also apply a layout to a block within any template:
+ #
+ # <%# app/views/users/_chief.html.erb &>
+ # <% render(:layout => "administrator", :locals => { :user => chief }) do %>
+ # Title: <%= chief.title %>
+ # <% end %>
+ #
+ # ...this will return:
+ #
+ #
+ # Budget: $<%= user.budget %>
+ # Title: <%= chief.name %>
+ #
+ #
+ # As you can see, the :locals hash is shared between both the partial and its layout.
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 render_partial(partial_path, object_assigns = nil, local_assigns = nil) #:nodoc:
+ case partial_path
+ when String, Symbol, NilClass
+ path, partial_name = partial_pieces(partial_path)
+ object = extracting_object(partial_name, object_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 && logger.debug?
+ 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
+ when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Associations::HasManyThroughAssociation
+ if partial_path.any?
+ path = ActionController::RecordIdentifier.partial_path(partial_path.first)
+ collection = partial_path
+ render_partial_collection(path, collection, nil, object_assigns.value)
+ else
+ ""
+ end
+ else
+ render_partial(
+ ActionController::RecordIdentifier.partial_path(partial_path),
+ object_assigns, local_assigns)
+ end
+ end
+
+ 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
+
def partial_pieces(partial_path)
if partial_path.include?('/')
return File.dirname(partial_path), File.basename(partial_path)
@@ -95,34 +164,37 @@ module ActionView
end
def partial_counter_name(partial_name)
- "#{partial_name.split('/').last}_counter".intern
+ "#{partial_variable_name(partial_name)}_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}")
+
+ def partial_variable_name(partial_name)
+ partial_name.split('/').last.split('.').first.intern
+ end
+
+ def extracting_object(partial_name, object_assigns)
+ variable_name = partial_variable_name(partial_name)
+ if object_assigns.nil?
+ controller.instance_variable_get("@#{variable_name}")
else
- # deprecated form where object could be passed in as second parameter
- local_assigns
+ object_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}")
+ variable_name = partial_variable_name(partial_name)
+
+ local_assigns[:object] ||=
+ local_assigns[variable_name] ||=
+ if object.is_a?(ActionView::Base::ObjectWrapper)
+ object.value
+ else
+ object
+ end || controller.instance_variable_get("@#{variable_name}")
end
end
end
diff --git a/vendor/rails/actionpack/lib/action_view/template_error.rb b/vendor/rails/actionpack/lib/action_view/template_error.rb
index 546db41f..01b4b155 100644
--- a/vendor/rails/actionpack/lib/action_view/template_error.rb
+++ b/vendor/rails/actionpack/lib/action_view/template_error.rb
@@ -10,8 +10,6 @@ module ActionView
@base_path, @assigns, @source, @original_exception =
base_path, assigns.dup, source, original_exception
@file_path = file_path
-
- remove_deprecated_assigns!
end
def message
@@ -82,16 +80,10 @@ module ActionView
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, "")
+ stripped_path = File.expand_path(path).gsub(@base_path, "")
+ stripped_path.gsub!(/^#{Regexp.escape File.expand_path(RAILS_ROOT)}/, '') if defined?(RAILS_ROOT)
+ stripped_path
end
def source_location
@@ -106,5 +98,5 @@ 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)
+ Exception::TraceSubstitutions << [%r{^\s*#{Regexp.escape RAILS_ROOT}/}, ''] if defined?(RAILS_ROOT)
end
diff --git a/vendor/rails/actionpack/lib/action_view/template_handler.rb b/vendor/rails/actionpack/lib/action_view/template_handler.rb
new file mode 100644
index 00000000..b9f4330a
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_view/template_handler.rb
@@ -0,0 +1,17 @@
+module ActionView
+ class TemplateHandler
+ def self.line_offset
+ 0
+ end
+
+ def initialize(view)
+ @view = view
+ end
+
+ def render(template, local_assigns)
+ end
+
+ def compile(template)
+ end
+ end
+end
diff --git a/vendor/rails/actionpack/lib/action_view/template_handlers/builder.rb b/vendor/rails/actionpack/lib/action_view/template_handlers/builder.rb
new file mode 100644
index 00000000..0f49d6ab
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_view/template_handlers/builder.rb
@@ -0,0 +1,19 @@
+require 'builder'
+
+module ActionView
+ module TemplateHandlers
+ class Builder < TemplateHandler
+ def self.line_offset
+ 2
+ end
+
+ def compile(template)
+ content_type_handler = (@view.send!(:controller).respond_to?(:response) ? "controller.response" : "controller")
+ "#{content_type_handler}.content_type ||= Mime::XML\n" +
+ "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
+ template +
+ "\nxml.target!\n"
+ end
+ end
+ end
+end
diff --git a/vendor/rails/actionpack/lib/action_view/template_handlers/erb.rb b/vendor/rails/actionpack/lib/action_view/template_handlers/erb.rb
new file mode 100644
index 00000000..022fc362
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_view/template_handlers/erb.rb
@@ -0,0 +1,21 @@
+require 'erb'
+
+class ERB
+ module Util
+ HTML_ESCAPE = { '&' => '&', '"' => '"', '>' => '>', '<' => '<' }
+
+ def html_escape(s)
+ s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }
+ end
+ end
+end
+
+module ActionView
+ module TemplateHandlers
+ class ERB < TemplateHandler
+ def compile(template)
+ ::ERB.new(template, nil, @view.erb_trim_mode).src
+ end
+ end
+ end
+end
diff --git a/vendor/rails/actionpack/lib/action_view/template_handlers/rjs.rb b/vendor/rails/actionpack/lib/action_view/template_handlers/rjs.rb
new file mode 100644
index 00000000..4ca9fc32
--- /dev/null
+++ b/vendor/rails/actionpack/lib/action_view/template_handlers/rjs.rb
@@ -0,0 +1,14 @@
+module ActionView
+ module TemplateHandlers
+ class RJS < TemplateHandler
+ def self.line_offset
+ 2
+ end
+
+ def compile(template)
+ "controller.response.content_type ||= Mime::JS\n" +
+ "update_page do |page|\n#{template}\nend"
+ end
+ end
+ end
+end
diff --git a/vendor/rails/actionpack/lib/actionpack.rb b/vendor/rails/actionpack/lib/actionpack.rb
new file mode 100644
index 00000000..2fe2832f
--- /dev/null
+++ b/vendor/rails/actionpack/lib/actionpack.rb
@@ -0,0 +1 @@
+require 'action_pack'
diff --git a/vendor/rails/actionpack/test/abstract_unit.rb b/vendor/rails/actionpack/test/abstract_unit.rb
index de4ca439..700bc1f5 100644
--- a/vendor/rails/actionpack/test/abstract_unit.rb
+++ b/vendor/rails/actionpack/test/abstract_unit.rb
@@ -3,14 +3,34 @@ $:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib/active_support')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
require 'yaml'
+require 'stringio'
require 'test/unit'
require 'action_controller'
-require 'breakpoint'
+require 'action_controller/cgi_ext'
require 'action_controller/test_process'
+begin
+ require 'ruby-debug'
+rescue LoadError
+ # Debugging disabled. `gem install ruby-debug` to enable.
+end
+
# 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
+
+
+# Wrap tests that use Mocha and skip if unavailable.
+def uses_mocha(test_name)
+ unless Object.const_defined?(:Mocha)
+ require 'mocha'
+ require 'stubba'
+ end
+ yield
+rescue LoadError => load_error
+ raise unless load_error.message =~ /mocha/i
+ $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again."
+end
diff --git a/vendor/rails/actionpack/test/action_view_test.rb b/vendor/rails/actionpack/test/action_view_test.rb
new file mode 100644
index 00000000..a69ff36f
--- /dev/null
+++ b/vendor/rails/actionpack/test/action_view_test.rb
@@ -0,0 +1,44 @@
+require File.dirname(__FILE__) + '/abstract_unit'
+require 'test/unit'
+
+class ActionViewTests < Test::Unit::TestCase
+ def test_find_template_extension_from_first_render
+ base = ActionView::Base.new
+
+ assert_nil base.send(:find_template_extension_from_first_render)
+
+ {
+ nil => nil,
+ '' => nil,
+ 'foo' => nil,
+ '/foo' => nil,
+ 'foo.rb' => 'rb',
+ 'foo.bar.rb' => 'bar.rb',
+ 'baz/foo.rb' => 'rb',
+ 'baz/foo.bar.rb' => 'bar.rb',
+ 'baz/foo.o/foo.rb' => 'rb',
+ 'baz/foo.o/foo.bar.rb' => 'bar.rb',
+ }.each do |input,expectation|
+ base.instance_variable_set('@first_render', input)
+ assert_equal expectation, base.send(:find_template_extension_from_first_render)
+ end
+ end
+
+ def test_should_report_file_exists_correctly
+ base = ActionView::Base.new
+
+ assert_nil base.send(:find_template_extension_from_first_render)
+
+ assert_equal false, base.send(:file_exists?, 'test.rhtml')
+ assert_equal false, base.send(:file_exists?, 'test.rb')
+
+ base.instance_variable_set('@first_render', 'foo.rb')
+
+ assert_equal 'rb', base.send(:find_template_extension_from_first_render)
+
+ assert_equal false, base.send(:file_exists?, 'baz')
+ assert_equal false, base.send(:file_exists?, 'baz.rb')
+
+ end
+
+end
diff --git a/vendor/rails/actionpack/test/active_record_unit.rb b/vendor/rails/actionpack/test/active_record_unit.rb
index 02cf3f70..5f2745b5 100644
--- a/vendor/rails/actionpack/test/active_record_unit.rb
+++ b/vendor/rails/actionpack/test/active_record_unit.rb
@@ -51,22 +51,23 @@ class ActiveRecordTestConnector
def setup_connection
if Object.const_defined?(:ActiveRecord)
+ defaults = { :database => ':memory:' }
begin
- connection_options = {:adapter => 'sqlite3', :dbfile => ':memory:'}
- ActiveRecord::Base.establish_connection(connection_options)
- ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => connection_options }
+ options = defaults.merge :adapter => 'sqlite3', :timeout => 500
+ ActiveRecord::Base.establish_connection(options)
+ ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => 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 }
+ $stderr.puts 'SQLite 3 unavailable; trying SQLite 2.'
+ options = defaults.merge :adapter => 'sqlite'
+ ActiveRecord::Base.establish_connection(options)
+ ActiveRecord::Base.configurations = { 'sqlite2_ar_integration' => 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."
+ raise "Can't setup connection since ActiveRecord isn't loaded."
end
end
@@ -83,7 +84,7 @@ class ActiveRecordTestConnector
end
end
-# Test case for inheiritance
+# Test case for inheritance
class ActiveRecordTestCase < Test::Unit::TestCase
# Set our fixture path
if ActiveRecordTestConnector.able_to_connect
@@ -95,22 +96,13 @@ class ActiveRecordTestCase < Test::Unit::TestCase
super if ActiveRecordTestConnector.connected
end
- def setup
- abort_tests unless ActiveRecordTestConnector.connected
+ def run(*args)
+ super if 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/vendor/rails/actionpack/test/activerecord/active_record_assertions_test.rb b/vendor/rails/actionpack/test/activerecord/active_record_assertions_test.rb
deleted file mode 100644
index 9a3c1127..00000000
--- a/vendor/rails/actionpack/test/activerecord/active_record_assertions_test.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-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/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb b/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb
index 9d92593a..707a0a75 100644
--- a/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb
+++ b/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb
@@ -53,7 +53,7 @@ class ActiveRecordStoreTest < ActiveRecordTestCase
@new_session['foo'] = 'bar'
end
-# this test only applies for eager sesssion saving
+# this test only applies for eager session 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
@@ -128,7 +128,7 @@ end
class SqlBypassActiveRecordStoreTest < ActiveRecordStoreTest
def session_class
- unless @session_class
+ unless defined? @session_class
@session_class = CGI::Session::ActiveRecordStore::SqlBypass
@session_class.connection = CGI::Session::ActiveRecordStore::Session.connection
end
diff --git a/vendor/rails/actionpack/test/activerecord/pagination_test.rb b/vendor/rails/actionpack/test/activerecord/pagination_test.rb
deleted file mode 100644
index 5e2fb8e7..00000000
--- a/vendor/rails/actionpack/test/activerecord/pagination_test.rb
+++ /dev/null
@@ -1,165 +0,0 @@
-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/"
-
- around_filter :silence_deprecation_warnings
-
- 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 silence_deprecation_warnings
- ActiveSupport::Deprecation.silence do
- yield
- end
- 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/vendor/rails/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/vendor/rails/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
new file mode 100644
index 00000000..ccebbefe
--- /dev/null
+++ b/vendor/rails/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
@@ -0,0 +1,74 @@
+require File.dirname(__FILE__) + '/../active_record_unit'
+
+class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
+ fixtures :developers, :projects, :developers_projects, :topics, :replies
+
+ class RenderPartialWithRecordIdentificationController < ActionController::Base
+ def render_with_has_many_and_belongs_to_association
+ @developer = Developer.find(1)
+ render :partial => @developer.projects
+ end
+
+ def render_with_has_many_association
+ @topic = Topic.find(1)
+ render :partial => @topic.replies
+ end
+
+ def render_with_has_many_through_association
+ @developer = Developer.find(:first)
+ render :partial => @developer.topics
+ end
+
+ def render_with_belongs_to_association
+ @reply = Reply.find(1)
+ render :partial => @reply.topic
+ end
+
+ def render_with_record
+ @developer = Developer.find(:first)
+ render :partial => @developer
+ end
+
+ def render_with_record_collection
+ @developers = Developer.find(:all)
+ render :partial => @developers
+ end
+ end
+
+ def setup
+ @controller = RenderPartialWithRecordIdentificationController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ super
+ end
+
+ def test_rendering_partial_with_has_many_and_belongs_to_association
+ get :render_with_has_many_and_belongs_to_association
+ assert_template 'projects/_project'
+ end
+
+ def test_rendering_partial_with_has_many_association
+ get :render_with_has_many_association
+ assert_template 'replies/_reply'
+ end
+
+ def test_rendering_partial_with_has_many_association
+ get :render_with_has_many_through_association
+ assert_template 'topics/_topic'
+ end
+
+ def test_rendering_partial_with_belongs_to_association
+ get :render_with_belongs_to_association
+ assert_template 'topics/_topic'
+ end
+
+ def test_render_with_record
+ get :render_with_record
+ assert_template 'developers/_developer'
+ end
+
+ def test_render_with_record_collection
+ get :render_with_record_collection
+ assert_template 'developers/_developer'
+ end
+end
diff --git a/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb b/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb
index e7c229df..1eb9610d 100644
--- a/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb
@@ -7,10 +7,10 @@ class ActionPackAssertionsController < ActionController::Base
def nothing() head :ok end
# a standard template
- def hello_world() render "test/hello_world"; end
+ def hello_world() render :template => "test/hello_world"; end
# a standard template
- def hello_xml_world() render "test/hello_xml_world"; end
+ def hello_xml_world() render :template => "test/hello_xml_world"; end
# a redirect to an internal location
def redirect_internal() redirect_to "/nothing"; end
@@ -26,7 +26,7 @@ class ActionPackAssertionsController < ActionController::Base
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
+ def redirect_external() redirect_to "http://www.rubyonrails.org"; end
# a 404
def response404() head '404 AWOL' end
@@ -40,13 +40,13 @@ class ActionPackAssertionsController < ActionController::Base
# putting stuff in the flash
def flash_me
flash['hello'] = 'my name is inigo montoya...'
- render_text "Inconceivable!"
+ render :text => "Inconceivable!"
end
# we have a flash, but nothing is in it
def flash_me_naked
flash.clear
- render_text "wow!"
+ render :text => "wow!"
end
# assign some template instance variables
@@ -56,11 +56,11 @@ class ActionPackAssertionsController < ActionController::Base
end
def render_based_on_parameters
- render_text "Mr. #{params[:name]}"
+ render :text => "Mr. #{params[:name]}"
end
def render_url
- render_text "#{url_for(:action => 'flash_me', :only_path => true)}
"
+ render :text => "#{url_for(:action => 'flash_me', :only_path => true)}
"
end
def render_text_with_custom_content_type
@@ -70,19 +70,19 @@ class ActionPackAssertionsController < ActionController::Base
# puts something in the session
def session_stuffing
session['xmas'] = 'turkey'
- render_text "ho ho ho"
+ 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']}"
+ 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']}"
+ render :text => "request method: #{request.env['REQUEST_METHOD']}"
end
def get_valid_record
@@ -141,7 +141,7 @@ module Admin
def redirect_to_fellow_controller
redirect_to :controller => 'user'
end
-
+
def redirect_to_top_level_named_route
redirect_to top_level_url(:id => "foo")
end
@@ -154,8 +154,7 @@ 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/"
-
+ActionPackAssertionsController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
# a test case to exercise the new capabilities TestRequest & TestResponse
class ActionPackAssertionsControllerTest < Test::Unit::TestCase
@@ -178,13 +177,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
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 }
@@ -212,49 +204,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
# 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|
@@ -262,7 +211,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing'
map.connect ':controller/:action/:id'
end
- set.named_routes.install
+ set.install_helpers
process :redirect_to_named_route
assert_redirected_to 'http://test.host/route_one'
@@ -306,7 +255,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
assert_redirected_to admin_inner_module_path
end
end
-
+
def test_assert_redirected_to_top_level_named_route_from_nested_controller
with_routing do |set|
set.draw do |map|
@@ -320,47 +269,8 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
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
@@ -374,11 +284,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
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
@@ -402,20 +307,12 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
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 }
+ process :hello_world
assert @response.rendered_with_file?
assert 'hello_world', @response.rendered_file
end
@@ -492,29 +389,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
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"
@@ -591,11 +465,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
rescue Test::Unit::AssertionFailedError => e
end
end
-
- protected
- def assert_deprecated_assertion(&block)
- assert_deprecated(/assert/, &block)
- end
end
class ActionPackHeaderTest < Test::Unit::TestCase
@@ -605,19 +474,19 @@ class ActionPackHeaderTest < Test::Unit::TestCase
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'])
+ process :hello_xml_world
+ assert_equal('application/xml; charset=utf-8', @response.headers['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'])
+ @response.headers['type'] = 'application/pdf'
+ process :hello_xml_world
+ assert_equal('application/pdf; charset=utf-8', @response.headers['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']
+ assert_equal 'application/rss+xml; charset=utf-8', @response.headers['type']
end
end
diff --git a/vendor/rails/actionpack/test/controller/addresses_render_test.rb b/vendor/rails/actionpack/test/controller/addresses_render_test.rb
index f329b2df..d1e9122d 100644
--- a/vendor/rails/actionpack/test/controller/addresses_render_test.rb
+++ b/vendor/rails/actionpack/test/controller/addresses_render_test.rb
@@ -16,13 +16,11 @@ class Address
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/"
+AddressesTestController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
class AddressesTest < Test::Unit::TestCase
def setup
@@ -39,10 +37,7 @@ class AddressesTest < Test::Unit::TestCase
end
def test_list
- # because pagination is deprecated
- ActiveSupport::Deprecation.silence do
- get :list
- end
+ get :list
assert_equal "We only need to get this far!", @response.body.chomp
end
end
diff --git a/vendor/rails/actionpack/test/controller/assert_select_test.rb b/vendor/rails/actionpack/test/controller/assert_select_test.rb
index 5c5da4b1..b0f3d6ce 100644
--- a/vendor/rails/actionpack/test/controller/assert_select_test.rb
+++ b/vendor/rails/actionpack/test/controller/assert_select_test.rb
@@ -3,8 +3,8 @@
# Under MIT and/or CC By license.
#++
-require File.dirname(__FILE__) + '/../abstract_unit'
-require File.dirname(__FILE__) + '/fake_controllers'
+require "#{File.dirname(__FILE__)}/../abstract_unit"
+require "#{File.dirname(__FILE__)}/fake_controllers"
unless defined?(ActionMailer)
@@ -73,7 +73,12 @@ class AssertSelectTest < Test::Unit::TestCase
def teardown
ActionMailer::Base.deliveries.clear
end
-
+
+ def assert_failure(message, &block)
+ e = assert_raises(AssertionFailedError, &block)
+ assert_match(message, e.message) if Regexp === message
+ assert_equal(message, e.message) if String === message
+ end
#
# Test assert select.
@@ -82,8 +87,8 @@ class AssertSelectTest < Test::Unit::TestCase
def test_assert_select
render_html %Q{
}
assert_select "div", 2
- assert_raises(AssertionFailedError) { assert_select "div", 3 }
- assert_raises(AssertionFailedError){ assert_select "p" }
+ assert_failure(/Expected at least 3 elements matching \"div\", found 2/) { assert_select "div", 3 }
+ assert_failure(/Expected at least 1 element matching \"p\", found 0/) { assert_select "p" }
end
@@ -131,22 +136,34 @@ class AssertSelectTest < Test::Unit::TestCase
end
- def test_equality_of_instances
+ def test_counts
render_html %Q{foo
foo
}
assert_nothing_raised { assert_select "div", 2 }
- assert_raises(AssertionFailedError) { assert_select "div", 3 }
+ assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do
+ assert_select "div", 3
+ end
assert_nothing_raised { assert_select "div", 1..2 }
- assert_raises(AssertionFailedError) { assert_select "div", 3..4 }
+ assert_failure(/Expected between 3 and 4 elements matching \"div\", found 2/) do
+ assert_select "div", 3..4
+ end
assert_nothing_raised { assert_select "div", :count=>2 }
- assert_raises(AssertionFailedError) { assert_select "div", :count=>3 }
+ assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do
+ assert_select "div", :count=>3
+ end
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_failure(/Expected at least 3 elements matching \"div\", found 2/) do
+ assert_select "div", :minimum=>3
+ end
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_failure(/Expected at most 1 element matching \"div\", found 2/) do
+ assert_select "div", :maximum=>1
+ end
assert_nothing_raised { assert_select "div", :minimum=>1, :maximum=>2 }
- assert_raises(AssertionFailedError) { assert_select "div", :minimum=>3, :maximum=>4 }
+ assert_failure(/Expected between 3 and 4 elements matching \"div\", found 2/) do
+ assert_select "div", :minimum=>3, :maximum=>4
+ end
end
@@ -183,6 +200,12 @@ class AssertSelectTest < Test::Unit::TestCase
assert_select "#3", false
end
end
+
+ assert_failure(/Expected at least 1 element matching \"#4\", found 0\./) do
+ assert_select "div" do
+ assert_select "#4"
+ end
+ end
end
@@ -408,6 +431,90 @@ class AssertSelectTest < Test::Unit::TestCase
assert_raises(AssertionFailedError) { assert_select_rjs :replace_html, "test1" }
end
+ # Simple remove
+ def test_assert_select_rjs_for_remove
+ render_rjs do |page|
+ page.remove "test1"
+ end
+
+ assert_select_rjs :remove, "test1"
+ end
+
+ def test_assert_select_rjs_for_remove_ignores_block
+ render_rjs do |page|
+ page.remove "test1"
+ end
+
+ assert_nothing_raised do
+ assert_select_rjs :remove, "test1" do
+ assert_select "p"
+ end
+ end
+ end
+
+ # Simple show
+ def test_assert_select_rjs_for_show
+ render_rjs do |page|
+ page.show "test1"
+ end
+
+ assert_select_rjs :show, "test1"
+ end
+
+ def test_assert_select_rjs_for_show_ignores_block
+ render_rjs do |page|
+ page.show "test1"
+ end
+
+ assert_nothing_raised do
+ assert_select_rjs :show, "test1" do
+ assert_select "p"
+ end
+ end
+ end
+
+ # Simple hide
+ def test_assert_select_rjs_for_hide
+ render_rjs do |page|
+ page.hide "test1"
+ end
+
+ assert_select_rjs :hide, "test1"
+ end
+
+ def test_assert_select_rjs_for_hide_ignores_block
+ render_rjs do |page|
+ page.hide "test1"
+ end
+
+ assert_nothing_raised do
+ assert_select_rjs :hide, "test1" do
+ assert_select "p"
+ end
+ end
+ end
+
+ # Simple toggle
+ def test_assert_select_rjs_for_toggle
+ render_rjs do |page|
+ page.toggle "test1"
+ end
+
+ assert_select_rjs :toggle, "test1"
+ end
+
+ def test_assert_select_rjs_for_toggle_ignores_block
+ render_rjs do |page|
+ page.toggle "test1"
+ end
+
+ assert_nothing_raised do
+ assert_select_rjs :toggle, "test1" do
+ assert_select "p"
+ end
+ end
+ end
+
# Non-positioned insert.
def test_assert_select_rjs_for_nonpositioned_insert
render_rjs do |page|
@@ -454,8 +561,7 @@ class AssertSelectTest < Test::Unit::TestCase
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|
diff --git a/vendor/rails/actionpack/test/controller/base_test.rb b/vendor/rails/actionpack/test/controller/base_test.rb
index 1d04b8bd..60e61b62 100644
--- a/vendor/rails/actionpack/test/controller/base_test.rb
+++ b/vendor/rails/actionpack/test/controller/base_test.rb
@@ -75,23 +75,21 @@ class ControllerInstanceTests < Test::Unit::TestCase
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!"
+ 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!"
+ assert_equal Set.new(%w(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, :__is_a__, :__metaclass__]
- controller.class.send(:hide_action, *mocha_methods)
- end
-
+ # Mocha adds some public instance methods to Object that would be
+ # considered actions, so explicitly hide_action them.
+ def hide_mocha_methods_from_controller(controller)
+ mocha_methods = [:expects, :metaclass, :mocha, :mocha_inspect, :reset_mocha, :stubba_object, :stubba_method, :stubs, :verify, :__metaclass__, :__is_a__]
+ controller.class.send!(:hide_action, *mocha_methods)
+ end
end
@@ -118,7 +116,7 @@ class PerformActionTest < Test::Unit::TestCase
def test_method_missing_is_not_an_action_name
use_controller MethodMissingController
- assert ! @controller.send(:action_methods).include?('method_missing')
+ assert ! @controller.send!(:action_methods).include?('method_missing')
get :method_missing
assert_response :success
@@ -133,4 +131,4 @@ class PerformActionTest < Test::Unit::TestCase
get :another_hidden_action
assert_response 404
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/actionpack/test/controller/caching_filestore.rb b/vendor/rails/actionpack/test/controller/caching_filestore.rb
deleted file mode 100644
index 389ebe02..00000000
--- a/vendor/rails/actionpack/test/controller/caching_filestore.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-require 'fileutils'
-require File.dirname(__FILE__) + '/../abstract_unit'
-
-class TestLogDevice < Logger::LogDevice
- attr :last_message, true
-
- def initialize
- @last_message=String.new
- end
-
- def write(message)
- @last_message << message
- end
-
- def clear
- @last_message = String.new
- end
-end
-
-#setup our really sophisticated logger
-TestLog = TestLogDevice.new
-RAILS_DEFAULT_LOGGER = Logger.new(TestLog)
-ActionController::Base.logger = RAILS_DEFAULT_LOGGER
-
-def use_store
- #generate a random key to ensure the cache is always in a different location
- RANDOM_KEY = rand(99999999).to_s
- FILE_STORE_PATH = File.dirname(__FILE__) + '/../temp/' + RANDOM_KEY
- ActionController::Base.perform_caching = true
- ActionController::Base.fragment_cache_store = :file_store, FILE_STORE_PATH
-end
-
-class TestController < ActionController::Base
- caches_action :render_to_cache, :index
-
- def render_to_cache
- render_text "Render Cached"
- end
- alias :index :render_to_cache
-end
-
-class FileStoreTest < Test::Unit::TestCase
- def setup
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- @controller = TestController.new
- @request.host = "hostname.com"
- end
-
- def teardown
- FileUtils.rm_rf(FILE_STORE_PATH)
- end
-
- def test_render_cached
- assert_fragment_cached { get :render_to_cache }
- assert_fragment_hit { get :render_to_cache }
- end
-
-
- private
- def assert_fragment_cached
- yield
- assert(TestLog.last_message.include?("Cached fragment:"), "--ERROR-- FileStore write failed ----")
- assert(!TestLog.last_message.include?("Couldn't create cache directory:"), "--ERROR-- FileStore create directory failed ----")
- TestLog.clear
- end
-
- def assert_fragment_hit
- yield
- assert(TestLog.last_message.include?("Fragment read:"), "--ERROR-- Fragment not found in FileStore ----")
- assert(!TestLog.last_message.include?("Cached fragment:"), "--ERROR-- Did cache ----")
- TestLog.clear
- end
-end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/controller/caching_test.rb b/vendor/rails/actionpack/test/controller/caching_test.rb
index 9cc6d0c1..d6982fbc 100644
--- a/vendor/rails/actionpack/test/controller/caching_test.rb
+++ b/vendor/rails/actionpack/test/controller/caching_test.rb
@@ -2,9 +2,8 @@ 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
+# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
-ActionController::Base.perform_caching = true
ActionController::Base.page_cache_directory = FILE_STORE_PATH
ActionController::Base.fragment_cache_store = :file_store, FILE_STORE_PATH
@@ -26,10 +25,26 @@ class PageCachingTestController < ActionController::Base
def not_found
head :not_found
end
+
+ def custom_path
+ render :text => "Super soaker"
+ cache_page("Super soaker", "/index.html")
+ end
+
+ def expire_custom_path
+ expire_page("/index.html")
+ head :ok
+ end
+
+ def trailing_slash
+ render :text => "Sneak attack"
+ end
end
class PageCachingTest < Test::Unit::TestCase
def setup
+ ActionController::Base.perform_caching = true
+
ActionController::Routing::Routes.draw do |map|
map.main '', :controller => 'posts'
map.resources :posts
@@ -51,6 +66,8 @@ class PageCachingTest < Test::Unit::TestCase
def teardown
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
+
+ ActionController::Base.perform_caching = false
end
def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
@@ -66,6 +83,38 @@ class PageCachingTest < Test::Unit::TestCase
assert_page_cached :ok, "get with ok status should have been cached"
end
+ def test_should_cache_with_custom_path
+ get :custom_path
+ assert File.exist?("#{FILE_STORE_PATH}/index.html")
+ end
+
+ def test_should_expire_cache_with_custom_path
+ get :custom_path
+ assert File.exist?("#{FILE_STORE_PATH}/index.html")
+
+ get :expire_custom_path
+ assert !File.exist?("#{FILE_STORE_PATH}/index.html")
+ end
+
+ def test_should_cache_without_trailing_slash_on_url
+ @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash'
+ assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
+ end
+
+ def test_should_cache_with_trailing_slash_on_url
+ @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash/'
+ assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
+ end
+
+ uses_mocha("should_cache_ok_at_custom_path") do
+ def test_should_cache_ok_at_custom_path
+ @request.expects(:path).returns("/index.html")
+ get :ok
+ assert_response :ok
+ assert File.exist?("#{FILE_STORE_PATH}/index.html")
+ end
+ end
+
[:ok, :no_content, :found, :not_found].each do |status|
[:get, :post, :put, :delete].each do |method|
unless method == :get and status == :ok
@@ -93,15 +142,29 @@ class PageCachingTest < Test::Unit::TestCase
end
end
+
class ActionCachingTestController < ActionController::Base
- caches_action :index
+ caches_action :index, :redirected, :forbidden
+ caches_action :show, :cache_path => 'http://test.host/custom/show'
+ caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
def index
- sleep 0.01
@cache_this = Time.now.to_f.to_s
render :text => @cache_this
end
+ def redirected
+ redirect_to :action => 'index'
+ end
+
+ def forbidden
+ render :text => "Forbidden"
+ headers["Status"] = "403 Forbidden"
+ end
+
+ alias_method :show, :index
+ alias_method :edit, :index
+
def expire
expire_action :controller => 'action_caching_test', :action => 'index'
render :nothing => true
@@ -146,11 +209,32 @@ class ActionCacheTest < Test::Unit::TestCase
get :index
cached_time = content_to_cache
assert_equal cached_time, @response.body
+ assert_cache_exists 'hostname.com/action_caching_test'
reset!
get :index
assert_equal cached_time, @response.body
end
+
+ def test_action_cache_with_custom_cache_path
+ get :show
+ cached_time = content_to_cache
+ assert_equal cached_time, @response.body
+ assert_cache_exists 'test.host/custom/show'
+ reset!
+
+ get :show
+ assert_equal cached_time, @response.body
+ end
+
+ def test_action_cache_with_custom_cache_path_in_block
+ get :edit
+ assert_cache_exists 'test.host/edit'
+ reset!
+
+ get :edit, :id => 1
+ assert_cache_exists 'test.host/1;edit'
+ end
def test_cache_expiration
get :index
@@ -178,21 +262,45 @@ class ActionCacheTest < Test::Unit::TestCase
@request.host = 'jamis.hostname.com'
get :index
jamis_cache = content_to_cache
-
+
+ reset!
+
@request.host = 'david.hostname.com'
get :index
david_cache = content_to_cache
assert_not_equal jamis_cache, @response.body
+ reset!
+
@request.host = 'jamis.hostname.com'
get :index
assert_equal jamis_cache, @response.body
+ reset!
+
@request.host = 'david.hostname.com'
get :index
assert_equal david_cache, @response.body
end
+ def test_redirect_is_not_cached
+ get :redirected
+ assert_response :redirect
+ reset!
+
+ get :redirected
+ assert_response :redirect
+ end
+
+ def test_forbidden_is_not_cached
+ get :forbidden
+ assert_response :forbidden
+ reset!
+
+ get :forbidden
+ assert_response :forbidden
+ 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'
@@ -200,6 +308,13 @@ class ActionCacheTest < Test::Unit::TestCase
assert_equal 'xml', path_object.extension
assert_equal 'example.org/posts/index.xml', path_object.path
end
+
+ def test_correct_content_type_is_returned_for_cache_hit
+ # run it twice to cache it the first time
+ get :index, :id => 'content-type.xml'
+ get :index, :id => 'content-type.xml'
+ assert_equal 'application/xml', @response.content_type
+ end
def test_empty_path_is_normalized
@mock_controller.mock_url_for = 'http://example.org/'
@@ -226,4 +341,9 @@ class ActionCacheTest < Test::Unit::TestCase
@controller = ActionCachingTestController.new
@request.host = 'hostname.com'
end
+
+ def assert_cache_exists(path)
+ full_path = File.join(FILE_STORE_PATH, path + '.cache')
+ assert File.exist?(full_path), "#{full_path.inspect} does not exist."
+ end
end
diff --git a/vendor/rails/actionpack/test/controller/capture_test.rb b/vendor/rails/actionpack/test/controller/capture_test.rb
index 43bf346e..7ec5f32a 100644
--- a/vendor/rails/actionpack/test/controller/capture_test.rb
+++ b/vendor/rails/actionpack/test/controller/capture_test.rb
@@ -8,6 +8,14 @@ class CaptureController < ActionController::Base
render :layout => "talk_from_action"
end
+ def content_for_with_parameter
+ render :layout => "talk_from_action"
+ end
+
+ def content_for_concatenated
+ render :layout => "talk_from_action"
+ end
+
def erb_content_for
render :layout => "talk_from_action"
end
@@ -23,7 +31,7 @@ class CaptureController < ActionController::Base
def rescue_action(e) raise end
end
-CaptureController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+CaptureController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
class CaptureTest < Test::Unit::TestCase
def setup
@@ -49,8 +57,18 @@ class CaptureTest < Test::Unit::TestCase
assert_equal expected_content_for_output, @response.body
end
+ def test_should_concatentate_content_for
+ get :content_for_concatenated
+ assert_equal expected_content_for_output, @response.body
+ end
+
def test_erb_content_for
- get :content_for
+ get :erb_content_for
+ assert_equal expected_content_for_output, @response.body
+ end
+
+ def test_should_set_content_for_with_parameter
+ get :content_for_with_parameter
assert_equal expected_content_for_output, @response.body
end
@@ -64,19 +82,8 @@ class CaptureTest < Test::Unit::TestCase
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
+ def expected_content_for_output
+ "Putting stuff in the title! \n\nGreat stuff!"
+ end
end
diff --git a/vendor/rails/actionpack/test/controller/cgi_test.rb b/vendor/rails/actionpack/test/controller/cgi_test.rb
index e7d44802..021781df 100755
--- a/vendor/rails/actionpack/test/controller/cgi_test.rb
+++ b/vendor/rails/actionpack/test/controller/cgi_test.rb
@@ -1,381 +1,7 @@
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
+class BaseCgiTest < 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)
@@ -383,20 +9,25 @@ class CGIRequestTest < Test::Unit::TestCase
@fake_cgi = Struct.new(:env_table).new(@request_hash)
@request = ActionController::CgiRequest.new(@fake_cgi)
end
-
+
+ def default_test; end
+end
+
+
+class CgiRequestTest < BaseCgiTest
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"
@@ -416,25 +47,69 @@ class CGIRequestTest < Test::Unit::TestCase
assert_equal "207.7.108.53:8007", @request.host_with_port
end
+ def test_host_with_port_if_http_standard_port_is_specified
+ @request_hash['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80"
+ assert_equal "glu.ttono.us", @request.host_with_port
+ end
+
+ def test_host_with_port_if_https_standard_port_is_specified
+ @request_hash['HTTP_X_FORWARDED_PROTO'] = "https"
+ @request_hash['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443"
+ assert_equal "glu.ttono.us", @request.host_with_port
+ end
+
+ def test_host_if_ipv6_reference
+ @request_hash.delete "HTTP_X_FORWARDED_HOST"
+ @request_hash['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]"
+ assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
+ end
+
+ def test_host_if_ipv6_reference_with_port
+ @request_hash.delete "HTTP_X_FORWARDED_HOST"
+ @request_hash['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008"
+ assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
+ 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"]
-
+ assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect
+ assert_equal ["yes"], cookies["is_admin"], cookies.inspect
+
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
-
- def test_unbalanced_query_string_with_array
- assert_equal(
- {'location' => ["1", "2"], 'age_group' => ["2"]},
- CGIMethods.parse_query_parameters("location[]=1&location[]=2&age_group[]=2")
- )
- assert_equal(
- {'location' => ["1", "2"], 'age_group' => ["2"]},
- CGIMethods.parse_request_parameters({'location[]' => ["1", "2"],
- 'age_group[]' => ["2"]})
- )
+ assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
+ assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect
+ end
+end
+
+
+class CgiRequestParamsParsingTest < BaseCgiTest
+ def test_doesnt_break_when_content_type_has_charset
+ data = 'flamenco=love'
+ @request.env['CONTENT_LENGTH'] = data.length
+ @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+ @request.env['RAW_POST_DATA'] = data
+ assert_equal({"flamenco"=> "love"}, @request.request_parameters)
+ end
+
+ def test_doesnt_interpret_request_uri_as_query_string_when_missing
+ @request.env['REQUEST_URI'] = 'foo'
+ assert_equal({}, @request.query_parameters)
+ end
+end
+
+
+class CgiRequestNeedsRewoundTest < BaseCgiTest
+ def test_body_should_be_rewound
+ data = 'foo'
+ fake_cgi = Struct.new(:env_table, :query_string, :stdinput).new(@request_hash, '', StringIO.new(data))
+ fake_cgi.env_table['CONTENT_LENGTH'] = data.length
+ fake_cgi.env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+
+ # Read the request body by parsing params.
+ request = ActionController::CgiRequest.new(fake_cgi)
+ request.request_parameters
+
+ # Should have rewound the body.
+ assert_equal 0, request.body.pos
end
end
diff --git a/vendor/rails/actionpack/test/controller/components_test.rb b/vendor/rails/actionpack/test/controller/components_test.rb
index fbe46375..debd8a27 100644
--- a/vendor/rails/actionpack/test/controller/components_test.rb
+++ b/vendor/rails/actionpack/test/controller/components_test.rb
@@ -14,15 +14,15 @@ class CallerController < ActionController::Base
end
def calling_from_template
- render_template "Ring, ring: <%= render_component(:controller => 'callee', :action => 'being_called') %>"
+ render :inline => "Ring, ring: <%= render_component(:controller => 'callee', :action => 'being_called') %>"
end
def internal_caller
- render_template "Are you there? <%= render_component(:action => 'internal_callee') %>"
+ render :inline => "Are you there? <%= render_component(:action => 'internal_callee') %>"
end
-
+
def internal_callee
- render_text "Yes, ma'am"
+ render :text => "Yes, ma'am"
end
def set_flash
@@ -32,13 +32,13 @@ class CallerController < ActionController::Base
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') %>"
+ render :inline => "<%= render_component(:controller => 'callee', :action => 'redirected') %>"
end
def rescue_action(e) raise end
@@ -46,22 +46,22 @@ end
class CalleeController < ActionController::Base
def being_called
- render_text "#{params[:name] || "Lady"} of the House, speaking"
+ 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"
+ render :text => "It's game over, man, just game over, man!", :status => 500
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
@@ -85,7 +85,7 @@ class ComponentsTest < Test::Unit::TestCase
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
@@ -95,12 +95,18 @@ class ComponentsTest < Test::Unit::TestCase
get :calling_from_template
assert_equal "Ring, ring: Lady of the House, speaking", @response.body
end
-
+
+ def test_etag_is_set_for_parent_template_when_calling_from_template
+ get :calling_from_template
+ expected_etag = etag_for("Ring, ring: Lady of the House, speaking")
+ assert_equal expected_etag, @response.headers['ETag']
+ 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]
@@ -109,43 +115,26 @@ class ComponentsTest < Test::Unit::TestCase
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
+
+ def test_component_as_string_redirect_renders_redirected_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
+ protected
+ def etag_for(text)
+ %("#{Digest::MD5.hexdigest(text)}")
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/vendor/rails/actionpack/test/controller/content_type_test.rb b/vendor/rails/actionpack/test/controller/content_type_test.rb
index 6f0618da..1841d37c 100644
--- a/vendor/rails/actionpack/test/controller/content_type_test.rb
+++ b/vendor/rails/actionpack/test/controller/content_type_test.rb
@@ -45,7 +45,7 @@ class ContentTypeController < ActionController::Base
def rescue_action(e) raise end
end
-ContentTypeController.template_root = File.dirname(__FILE__) + "/../fixtures/"
+ContentTypeController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
class ContentTypeTest < Test::Unit::TestCase
def setup
diff --git a/vendor/rails/actionpack/test/controller/cookie_test.rb b/vendor/rails/actionpack/test/controller/cookie_test.rb
index 44023b07..6a833fee 100644
--- a/vendor/rails/actionpack/test/controller/cookie_test.rb
+++ b/vendor/rails/actionpack/test/controller/cookie_test.rb
@@ -2,19 +2,15 @@ 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"
- end
-
def authenticate
cookies["user_name"] = "david"
end
- def authenticate_for_fourten_days
+ def authenticate_for_fourteen_days
cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) }
end
- def authenticate_for_fourten_days_with_symbols
+ def authenticate_for_fourteen_days_with_symbols
cookies[:user_name] = { :value => "david", :expires => Time.local(2005, 10, 10) }
end
@@ -22,7 +18,7 @@ class CookieTest < Test::Unit::TestCase
cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) }
cookies["login"] = "XJ-122"
end
-
+
def access_frozen_cookies
cookies["will"] = "work"
end
@@ -33,7 +29,11 @@ class CookieTest < Test::Unit::TestCase
def delete_cookie_with_path
cookies.delete("user_name", :path => '/beaten')
- render_text "hello world"
+ render :text => "hello world"
+ end
+
+ def authenticate_with_http_only
+ cookies["user_name"] = { :value => "david", :http_only => true }
end
def rescue_action(e)
@@ -49,26 +49,27 @@ class CookieTest < Test::Unit::TestCase
@request.host = "www.nextangle.com"
end
- def test_setting_cookie_with_deprecated_writer
- get :authenticate_with_deprecated_writer
- assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david") ], @response.headers["cookie"]
- end
-
def test_setting_cookie
get :authenticate
assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david") ], @response.headers["cookie"]
end
def test_setting_cookie_for_fourteen_days
- get :authenticate_for_fourten_days
+ get :authenticate_for_fourteen_days
assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
end
def test_setting_cookie_for_fourteen_days_with_symbols
- get :authenticate_for_fourten_days
+ get :authenticate_for_fourteen_days
assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
end
+ def test_setting_cookie_with_http_only
+ get :authenticate_with_http_only
+ assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "http_only" => true) ], @response.headers["cookie"]
+ assert_equal CGI::Cookie::new("name" => "user_name", "value" => "david", "path" => "/", "http_only" => true).to_s, @response.headers["cookie"][0].to_s
+ end
+
def test_multiple_cookies
get :set_multiple_cookies
assert_equal 2, @response.cookies.size
@@ -91,9 +92,44 @@ class CookieTest < Test::Unit::TestCase
assert_equal nil, jar["something_else"]
end
+ def test_cookiejar_accessor_with_array_value
+ a = %w{1 2 3}
+ @request.cookies["pages"] = CGI::Cookie.new("name" => "pages", "value" => a, "expires" => Time.local(2025, 10, 10))
+ @controller.request = @request
+ jar = ActionController::CookieJar.new(@controller)
+ assert_equal a, jar["pages"]
+ end
+
def test_delete_cookie_with_path
get :delete_cookie_with_path
assert_equal "/beaten", @response.headers["cookie"].first.path
assert_not_equal "/", @response.headers["cookie"].first.path
end
+
+ def test_cookie_to_s_simple_values
+ assert_equal 'myname=myvalue; path=', CGI::Cookie.new('myname', 'myvalue').to_s
+ end
+
+ def test_cookie_to_s_hash
+ cookie_str = CGI::Cookie.new(
+ 'name' => 'myname',
+ 'value' => 'myvalue',
+ 'domain' => 'mydomain',
+ 'path' => 'mypath',
+ 'expires' => Time.utc(2007, 10, 20),
+ 'secure' => true,
+ 'http_only' => true).to_s
+ assert_equal 'myname=myvalue; domain=mydomain; path=mypath; expires=Sat, 20 Oct 2007 00:00:00 GMT; secure; HttpOnly', cookie_str
+ end
+
+ def test_cookie_to_s_hash_default_not_secure_not_http_only
+ cookie_str = CGI::Cookie.new(
+ 'name' => 'myname',
+ 'value' => 'myvalue',
+ 'domain' => 'mydomain',
+ 'path' => 'mypath',
+ 'expires' => Time.utc(2007, 10, 20))
+ assert cookie_str !~ /secure/
+ assert cookie_str !~ /HttpOnly/
+ end
end
diff --git a/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb b/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
deleted file mode 100644
index 7865a69e..00000000
--- a/vendor/rails/actionpack/test/controller/deprecated_instance_variables_test.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-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/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb b/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
index 3b210742..6d7157e1 100644
--- a/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
+++ b/vendor/rails/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
@@ -1,20 +1,7 @@
require File.dirname(__FILE__) + '/../../abstract_unit'
class DeprecatedBaseMethodsTest < Test::Unit::TestCase
- # ActiveRecord model mock to test pagination deprecation
- class DummyModel
- def self.find(*args) [] end
- def self.count(*args) 0 end
- end
-
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}"
@@ -24,37 +11,15 @@ class DeprecatedBaseMethodsTest < Test::Unit::TestCase
this_method_doesnt_exist
end
- def pagination
- paginate :dummy_models, :class_name => 'DeprecatedBaseMethodsTest::DummyModel'
- render :nothing => true
- end
-
def rescue_action(e) raise e end
end
- Target.template_root = File.dirname(__FILE__) + "/../../fixtures"
+ Target.view_paths = [ File.dirname(__FILE__) + "/../../fixtures" ]
def setup
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@controller = Target.new
- @controller.logger = Logger.new(nil) unless @controller.logger
- 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
def test_log_error_silences_deprecation_warnings
@@ -69,10 +34,4 @@ class DeprecatedBaseMethodsTest < Test::Unit::TestCase
error = Test::Unit::Error.new('testing ur doodz', e)
assert_not_deprecated { error.message }
end
-
- def test_pagination_deprecation
- assert_deprecated('svn://errtheblog.com/svn/plugins/classic_pagination') do
- get :pagination
- end
- end
end
diff --git a/vendor/rails/actionpack/test/controller/dispatcher_test.rb b/vendor/rails/actionpack/test/controller/dispatcher_test.rb
new file mode 100644
index 00000000..ec937ebf
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/dispatcher_test.rb
@@ -0,0 +1,123 @@
+require "#{File.dirname(__FILE__)}/../abstract_unit"
+
+uses_mocha 'dispatcher tests' do
+
+require 'action_controller/dispatcher'
+
+class DispatcherTest < Test::Unit::TestCase
+ Dispatcher = ActionController::Dispatcher
+
+ def setup
+ @output = StringIO.new
+ ENV['REQUEST_METHOD'] = 'GET'
+
+ Dispatcher.callbacks[:prepare].clear
+ @dispatcher = Dispatcher.new(@output)
+ end
+
+ def teardown
+ ENV['REQUEST_METHOD'] = nil
+ end
+
+ def test_clears_dependencies_after_dispatch_if_in_loading_mode
+ Dependencies.stubs(:load?).returns(true)
+
+ ActionController::Routing::Routes.expects(:reload).once
+ Dependencies.expects(:clear).once
+
+ dispatch
+ end
+
+ def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
+ Dependencies.stubs(:load?).returns(false)
+
+ ActionController::Routing::Routes.expects(:reload).never
+ Dependencies.expects(:clear).never
+
+ dispatch
+ end
+
+ def test_failsafe_response
+ CGI.expects(:new).raises('some multipart parsing failure')
+
+ ActionController::Routing::Routes.stubs(:reload)
+ Dispatcher.stubs(:log_failsafe_exception)
+
+ assert_nothing_raised { dispatch }
+
+ assert_equal "Status: 400 Bad Request\r\nContent-Type: text/html\r\n\r\n400 Bad Request ", @output.string
+ end
+
+ def test_reload_application_sets_unprepared_if_loading_dependencies
+ Dependencies.stubs(:load?).returns(false)
+ ActionController::Routing::Routes.expects(:reload).never
+ @dispatcher.unprepared = false
+ @dispatcher.send!(:reload_application)
+ assert !@dispatcher.unprepared
+
+ Dependencies.stubs(:load?).returns(true)
+ ActionController::Routing::Routes.expects(:reload).once
+ @dispatcher.send!(:reload_application)
+ assert @dispatcher.unprepared
+ end
+
+ def test_prepare_application_runs_callbacks_if_unprepared
+ a = b = c = nil
+ Dispatcher.to_prepare { a = b = c = 1 }
+ Dispatcher.to_prepare { b = c = 2 }
+ Dispatcher.to_prepare { c = 3 }
+
+ # Skip the callbacks when already prepared.
+ @dispatcher.unprepared = false
+ @dispatcher.send! :prepare_application
+ assert_nil a || b || c
+
+ # Perform the callbacks when unprepared.
+ @dispatcher.unprepared = true
+ @dispatcher.send! :prepare_application
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+
+ # But when not :load, make sure they are only run once
+ a = b = c = nil
+ @dispatcher.send! :prepare_application
+ assert_nil a || b || c
+ end
+
+ def test_to_prepare_with_identifier_replaces
+ a = b = nil
+ Dispatcher.to_prepare(:unique_id) { a = b = 1 }
+ Dispatcher.to_prepare(:unique_id) { a = 2 }
+
+ @dispatcher.unprepared = true
+ @dispatcher.send! :prepare_application
+ assert_equal 2, a
+ assert_equal nil, b
+ end
+
+ def test_to_prepare_only_runs_once_if_not_loading_dependencies
+ Dependencies.stubs(:load?).returns(false)
+ called = 0
+ Dispatcher.to_prepare(:unprepared_test) { called += 1 }
+ 2.times { dispatch }
+ assert_equal 1, called
+ end
+
+ private
+ def dispatch(output = @output)
+ controller = mock
+ controller.stubs(:process).returns(controller)
+ controller.stubs(:out).with(output).returns('response')
+
+ ActionController::Routing::Routes.stubs(:recognize).returns(controller)
+
+ Dispatcher.dispatch(nil, {}, output)
+ end
+
+ def assert_subclasses(howmany, klass, message = klass.subclasses.inspect)
+ assert_equal howmany, klass.subclasses.size, message
+ end
+end
+
+end
diff --git a/vendor/rails/actionpack/test/controller/fake_models.rb b/vendor/rails/actionpack/test/controller/fake_models.rb
new file mode 100644
index 00000000..2761b09f
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/fake_models.rb
@@ -0,0 +1,5 @@
+class Customer < Struct.new(:name, :id)
+ def to_param
+ id.to_s
+ end
+end
diff --git a/vendor/rails/actionpack/test/controller/filters_test.rb b/vendor/rails/actionpack/test/controller/filters_test.rb
index 31330960..188e75af 100644
--- a/vendor/rails/actionpack/test/controller/filters_test.rb
+++ b/vendor/rails/actionpack/test/controller/filters_test.rb
@@ -1,5 +1,6 @@
require File.dirname(__FILE__) + '/../abstract_unit'
+# FIXME: crashes Ruby 1.9
class FilterTest < Test::Unit::TestCase
class TestController < ActionController::Base
before_filter :ensure_login
@@ -44,7 +45,9 @@ class FilterTest < Test::Unit::TestCase
(1..3).each do |i|
define_method "try_#{i}" do
instance_variable_set :@try, i
- action_name != "fail_#{i}"
+ if action_name == "fail_#{i}"
+ head(404)
+ end
end
end
end
@@ -234,7 +237,7 @@ class FilterTest < Test::Unit::TestCase
before_filter(AuditFilter)
def show
- render_text "hello"
+ render :text => "hello"
end
end
@@ -271,11 +274,11 @@ class FilterTest < Test::Unit::TestCase
before_filter :second, :only => :foo
def foo
- render_text 'foo'
+ render :text => 'foo'
end
def bar
- render_text 'bar'
+ render :text => 'bar'
end
protected
@@ -325,6 +328,31 @@ class FilterTest < Test::Unit::TestCase
end
end
+ class ErrorToRescue < Exception; end
+
+ class RescuingAroundFilterWithBlock
+ def filter(controller)
+ begin
+ yield
+ rescue ErrorToRescue => ex
+ controller.send! :render, :text => "I rescued this: #{ex.inspect}"
+ end
+ end
+ end
+
+ class RescuedController < ActionController::Base
+ around_filter RescuingAroundFilterWithBlock.new
+
+ def show
+ raise ErrorToRescue.new("Something made the bad noise.")
+ end
+
+ private
+ def rescue_action(exception)
+ raise exception
+ end
+ end
+
class NonYieldingAroundFilterController < ActionController::Base
before_filter :filter_one
@@ -558,6 +586,16 @@ class FilterTest < Test::Unit::TestCase
assert_equal nil, test_process(ChangingTheRequirementsController, "go_wild").template.assigns['ran_filter']
end
+ def test_a_rescuing_around_filter
+ response = nil
+ assert_nothing_raised do
+ response = test_process(RescuedController)
+ end
+
+ assert response.success?
+ assert_equal("I rescued this: #", response.body)
+ end
+
private
def test_process(controller, action = "show")
request = ActionController::TestRequest.new
@@ -788,7 +826,7 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
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 ' ', response.body
assert_equal 1, controller.instance_variable_get(:@try)
assert controller.instance_variable_get(:@before_filter_chain_aborted)
end
@@ -796,7 +834,7 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
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 ' ', response.body
assert_equal 2, controller.instance_variable_get(:@try)
assert controller.instance_variable_get(:@before_filter_chain_aborted)
end
@@ -804,7 +842,7 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
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 ' ', response.body
assert_equal 3, controller.instance_variable_get(:@try)
assert controller.instance_variable_get(:@before_filter_chain_aborted)
end
diff --git a/vendor/rails/actionpack/test/controller/flash_test.rb b/vendor/rails/actionpack/test/controller/flash_test.rb
index d12ced85..4a6f3c9e 100644
--- a/vendor/rails/actionpack/test/controller/flash_test.rb
+++ b/vendor/rails/actionpack/test/controller/flash_test.rb
@@ -31,7 +31,13 @@ class FlashTest < Test::Unit::TestCase
def use_flash_and_keep_it
@flash_copy = {}.update flash
@flashy = flash["that"]
- silence_warnings { keep_flash }
+ flash.keep
+ render :inline => "hello"
+ end
+
+ def use_flash_and_update_it
+ flash.update("this" => "hello again")
+ @flash_copy = {}.update flash
render :inline => "hello"
end
@@ -48,6 +54,23 @@ class FlashTest < Test::Unit::TestCase
def rescue_action(e)
raise unless ActionController::MissingTemplate === e
end
+
+ # methods for test_sweep_after_halted_filter_chain
+ before_filter :halt_and_redir, :only => "filter_halting_action"
+
+ def std_action
+ @flash_copy = {}.update(flash)
+ end
+
+ def filter_halting_action
+ @flash_copy = {}.update(flash)
+ end
+
+ def halt_and_redir
+ flash["foo"] = "bar"
+ redirect_to :action => "std_action"
+ @flash_copy = {}.update(flash)
+ end
end
def setup
@@ -70,7 +93,7 @@ class FlashTest < Test::Unit::TestCase
def test_keep_flash
get :set_flash
- assert_deprecated(/keep_flash/) { get :use_flash_and_keep_it }
+ get :use_flash_and_keep_it
assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
assert_equal "hello", @response.template.assigns["flashy"]
@@ -93,10 +116,31 @@ class FlashTest < Test::Unit::TestCase
assert_nil @response.template.assigns["flashy"]
end
+ def test_update_flash
+ get :set_flash
+ get :use_flash_and_update_it
+ assert_equal "hello", @response.template.assigns["flash_copy"]["that"]
+ assert_equal "hello again", @response.template.assigns["flash_copy"]["this"]
+ get :use_flash
+ assert_nil @response.template.assigns["flash_copy"]["that"], "On second flash"
+ assert_equal "hello again", @response.template.assigns["flash_copy"]["this"], "On second flash"
+ 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
+
+ def test_sweep_after_halted_filter_chain
+ get :std_action
+ assert_nil @response.template.assigns["flash_copy"]["foo"]
+ get :filter_halting_action
+ assert_equal "bar", @response.template.assigns["flash_copy"]["foo"]
+ get :std_action # follow redirection
+ assert_equal "bar", @response.template.assigns["flash_copy"]["foo"]
+ get :std_action
+ assert_nil @response.template.assigns["flash_copy"]["foo"]
+ end
end
diff --git a/vendor/rails/actionpack/test/controller/fragment_store_setting_test.rb b/vendor/rails/actionpack/test/controller/fragment_store_setting_test.rb
index cb872f65..3df6fd0b 100644
--- a/vendor/rails/actionpack/test/controller/fragment_store_setting_test.rb
+++ b/vendor/rails/actionpack/test/controller/fragment_store_setting_test.rb
@@ -24,14 +24,16 @@ class FragmentCacheStoreSettingTest < Test::Unit::TestCase
)
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
+
+ if defined? CGI::Session::MemCacheStore
+ 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
end
def test_object_assigned_fragment_cache_store
diff --git a/vendor/rails/actionpack/test/controller/helper_test.rb b/vendor/rails/actionpack/test/controller/helper_test.rb
index 98c8f7e7..117f73b7 100644
--- a/vendor/rails/actionpack/test/controller/helper_test.rb
+++ b/vendor/rails/actionpack/test/controller/helper_test.rb
@@ -1,5 +1,7 @@
require File.dirname(__FILE__) + '/../abstract_unit'
+ActionController::Helpers::HELPERS_DIR.replace File.dirname(__FILE__) + '/../fixtures/helpers'
+
class TestController < ActionController::Base
attr_accessor :delegate_attr
def delegate_method() end
@@ -15,7 +17,7 @@ module Fun
def rescue_action(e) raise end
end
- class PDFController < ActionController::Base
+ class PdfController < ActionController::Base
def test
render :inline => "test: <%= foobar %>"
end
@@ -24,6 +26,10 @@ module Fun
end
end
+class ApplicationController < ActionController::Base
+ helper :all
+end
+
module LocalAbcHelper
def a() end
def b() end
@@ -120,16 +126,29 @@ class HelperTest < Test::Unit::TestCase
response = ActionController::TestResponse.new
request.action = 'test'
- assert_equal 'test: baz', Fun::PDFController.process(request, response).body
+ assert_equal 'test: baz', Fun::PdfController.process(request, response).body
+ end
+
+ def test_all_helpers
+ methods = ApplicationController.master_helper_module.instance_methods.map(&:to_s)
+
+ # abc_helper.rb
+ assert methods.include?('bare_a')
+
+ # fun/games_helper.rb
+ assert methods.include?('stratego')
+
+ # fun/pdf_helper.rb
+ assert methods.include?('foobar')
end
private
def expected_helper_methods
- TestHelper.instance_methods
+ TestHelper.instance_methods.map(&:to_s)
end
def master_helper_methods
- @controller_class.master_helper_module.instance_methods
+ @controller_class.master_helper_module.instance_methods.map(&:to_s)
end
def missing_methods
diff --git a/vendor/rails/actionpack/test/controller/html-scanner/document_test.rb b/vendor/rails/actionpack/test/controller/html-scanner/document_test.rb
new file mode 100644
index 00000000..0719883f
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/html-scanner/document_test.rb
@@ -0,0 +1,124 @@
+require File.dirname(__FILE__) + '/../../abstract_unit'
+require 'test/unit'
+
+class DocumentTest < Test::Unit::TestCase
+ def test_handle_doctype
+ doc = nil
+ assert_nothing_raised do
+ doc = HTML::Document.new <<-HTML.strip
+
+
+
+ HTML
+ end
+ assert_equal 3, doc.root.children.length
+ assert_equal %{}, doc.root.children[0].content
+ assert_match %r{\s+}m, doc.root.children[1].content
+ assert_equal "html", doc.root.children[2].name
+ end
+
+ def test_find_img
+ doc = HTML::Document.new <<-HTML.strip
+
+
+
+
+
+ HTML
+ assert doc.find(:tag=>"img", :attributes=>{"src"=>"hello.gif"})
+ end
+
+ def test_find_all
+ doc = HTML::Document.new <<-HTML.strip
+
+
+
+
+
something
+
here is more
+
+
+
+ HTML
+ all = doc.find_all :attributes => { :class => "test" }
+ assert_equal 3, all.length
+ assert_equal [ "p", "p", "em" ], all.map { |n| n.name }
+ end
+
+ def test_find_with_text
+ doc = HTML::Document.new <<-HTML.strip
+
+
+ Some text
+
+
+ HTML
+ assert doc.find(:content => "Some text")
+ assert doc.find(:tag => "p", :child => { :content => "Some text" })
+ assert doc.find(:tag => "p", :child => "Some text")
+ assert doc.find(:tag => "p", :content => "Some text")
+ end
+
+ def test_parse_xml
+ assert_nothing_raised { HTML::Document.new(" ", true, true) }
+ assert_nothing_raised { HTML::Document.new(" something ", true, true) }
+ end
+
+ def test_parse_document
+ doc = HTML::Document.new(<<-HTML)
+
+ HTML
+ assert_not_nil doc.find(:tag => "div", :children => { :count => 1, :only => { :tag => "table" } })
+ end
+
+ def test_tag_nesting_nothing_to_s
+ doc = HTML::Document.new(" ")
+ assert_equal " ", doc.root.to_s
+ end
+
+ def test_tag_nesting_space_to_s
+ doc = HTML::Document.new(" ")
+ assert_equal " ", doc.root.to_s
+ end
+
+ def test_tag_nesting_text_to_s
+ doc = HTML::Document.new("text ")
+ assert_equal "text ", doc.root.to_s
+ end
+
+ def test_tag_nesting_tag_to_s
+ doc = HTML::Document.new(" ")
+ assert_equal " ", doc.root.to_s
+ end
+
+ def test_parse_cdata
+ doc = HTML::Document.new(<<-HTML)
+
+
+
+ ]]>
+
+
+ this document has <br> for a title
+
+
+HTML
+
+ assert_nil doc.find(:tag => "title", :descendant => { :tag => "br" })
+ assert doc.find(:tag => "title", :child => " ")
+ end
+
+ def test_find_empty_tag
+ doc = HTML::Document.new("
")
+ assert_nil doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /./)
+ assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /\A\Z/)
+ assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => /^$/)
+ assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => "")
+ assert doc.find(:tag => "div", :attributes => { :id => "map" }, :content => nil)
+ end
+end
diff --git a/vendor/rails/actionpack/test/controller/html-scanner/node_test.rb b/vendor/rails/actionpack/test/controller/html-scanner/node_test.rb
new file mode 100644
index 00000000..1cf0a4bb
--- /dev/null
+++ b/vendor/rails/actionpack/test/controller/html-scanner/node_test.rb
@@ -0,0 +1,69 @@
+require File.dirname(__FILE__) + '/../../abstract_unit'
+require 'test/unit'
+
+class NodeTest < Test::Unit::TestCase
+
+ class MockNode
+ def initialize(matched, value)
+ @matched = matched
+ @value = value
+ end
+
+ def find(conditions)
+ @matched && self
+ end
+
+ def to_s
+ @value.to_s
+ end
+ end
+
+ def setup
+ @node = HTML::Node.new("parent")
+ @node.children.concat [MockNode.new(false,1), MockNode.new(true,"two"), MockNode.new(false,:three)]
+ end
+
+ def test_match
+ assert !@node.match("foo")
+ end
+
+ def test_tag
+ assert !@node.tag?
+ end
+
+ def test_to_s
+ assert_equal "1twothree", @node.to_s
+ end
+
+ def test_find
+ assert_equal "two", @node.find('blah').to_s
+ end
+
+ def test_parse_strict
+ s = ""
+ assert_raise(RuntimeError) { HTML::Node.parse(nil,0,0,s) }
+ end
+
+ def test_parse_relaxed
+ s = ""
+ node = nil
+ assert_nothing_raised { node = HTML::Node.parse(nil,0,0,s,false) }
+ assert node.attributes.has_key?("foo")
+ assert !node.attributes.has_key?("bar")
+ end
+
+ def test_to_s_with_boolean_attrs
+ s = ""
+ node = HTML::Node.parse(nil,0,0,s)
+ assert node.attributes.has_key?("foo")
+ assert node.attributes.has_key?("bar")
+ assert "", node.to_s
+ end
+
+ def test_parse_with_unclosed_tag
+ s = ""))
+ assert_equal("Dont touch me", sanitizer.sanitize("Dont touch me"))
+ assert_equal("This is a test.", sanitizer.sanitize("This is a test .
"))
+ assert_equal("Weirdos", sanitizer.sanitize("Wei<a onclick='alert(document.cookie);' />rdos"))
+ assert_equal("This is a test.", sanitizer.sanitize("This is a test."))
+ assert_equal(
+ %{This is a test.\n\n\nIt no longer contains any HTML.\n}, sanitizer.sanitize(
+ %{This is a test . \n\n\n\nIt no longer contains any HTML .
\n}))
+ assert_equal "This has a here.", sanitizer.sanitize("This has a here.")
+ [nil, '', ' '].each { |blank| assert_equal blank, sanitizer.sanitize(blank) }
+ end
+
+ def test_strip_links
+ sanitizer = HTML::LinkSanitizer.new
+ assert_equal "Dont touch me", sanitizer.sanitize("Dont touch me")
+ assert_equal "on my mind\nall day long", sanitizer.sanitize("on my mind \nall day long ")
+ assert_equal "0wn3d", sanitizer.sanitize("0wn3d ")
+ assert_equal "Magic", sanitizer.sanitize("Mag ic")
+ assert_equal "FrrFox", sanitizer.sanitize("FrrFox ")
+ assert_equal "My mind\nall day long", sanitizer.sanitize("My mind \nall day long ")
+ assert_equal "all day long", sanitizer.sanitize("<a href='hello'>all day long< /a>")
+
+ assert_equal " ", ''
+ end
+
+ def test_sanitize_plaintext
+ raw = "foo "
+ assert_sanitized raw, "foo "
+ end
+
+ def test_sanitize_script
+ assert_sanitized "a b cd e f", "a b cd e f"
+ end
+
+ # fucked
+ def test_sanitize_js_handlers
+ raw = %{onthis="do that" hello }
+ assert_sanitized raw, %{onthis="do that" hello }
+ end
+
+ def test_sanitize_javascript_href
+ raw = %{href="javascript:bang" foo , bar }
+ assert_sanitized raw, %{href="javascript:bang" foo , bar }
+ end
+
+ def test_sanitize_image_src
+ raw = %{src="javascript:bang" foo, bar }
+ assert_sanitized raw, %{src="javascript:bang" foo, bar }
+ end
+
+ HTML::WhiteListSanitizer.allowed_tags.each do |tag_name|
+ define_method "test_should_allow_#{tag_name}_tag" do
+ assert_sanitized "start <#{tag_name} title=\"1\" onclick=\"foo\">foo bar baz#{tag_name}> end", %(start <#{tag_name} title="1">foo bar baz#{tag_name}> end)
+ end
+ end
+
+ def test_should_allow_anchors
+ assert_sanitized %( ), %( )
+ end
+
+ # RFC 3986, sec 4.2
+ def test_allow_colons_in_path_component
+ assert_sanitized("foo ")
+ end
+
+ %w(src width height alt).each do |img_attr|
+ define_method "test_should_allow_image_#{img_attr}_attribute" do
+ assert_sanitized %( ), %( )
+ end
+ end
+
+ def test_should_handle_non_html
+ assert_sanitized 'abc'
+ end
+
+ def test_should_handle_blank_text
+ assert_sanitized nil
+ assert_sanitized ''
+ end
+
+ def test_should_allow_custom_tags
+ text = "foo "
+ sanitizer = HTML::WhiteListSanitizer.new
+ assert_equal(text, sanitizer.sanitize(text, :tags => %w(u)))
+ end
+
+ def test_should_allow_only_custom_tags
+ text = "foo with bar "
+ sanitizer = HTML::WhiteListSanitizer.new
+ assert_equal("foo with bar", sanitizer.sanitize(text, :tags => %w(u)))
+ end
+
+ def test_should_allow_custom_tags_with_attributes
+ text = %(foo )
+ sanitizer = HTML::WhiteListSanitizer.new
+ assert_equal(text, sanitizer.sanitize(text))
+ end
+
+ def test_should_allow_custom_tags_with_custom_attributes
+ text = %(Lorem ipsum )
+ sanitizer = HTML::WhiteListSanitizer.new
+ assert_equal(text, sanitizer.sanitize(text, :attributes => ['foo']))
+ end
+
+ [%w(img src), %w(a href)].each do |(tag, attr)|
+ define_method "test_should_strip_#{attr}_attribute_in_#{tag}_with_bad_protocols" do
+ assert_sanitized %(<#{tag} #{attr}="javascript:bang" title="1">boo#{tag}>), %(<#{tag} title="1">boo#{tag}>)
+ end
+ end
+
+ def test_should_flag_bad_protocols
+ sanitizer = HTML::WhiteListSanitizer.new
+ %w(about chrome data disk hcp help javascript livescript lynxcgi lynxexec ms-help ms-its mhtml mocha opera res resource shell vbscript view-source vnd.ms.radio wysiwyg).each do |proto|
+ assert sanitizer.send(:contains_bad_protocols?, 'src', "#{proto}://bad")
+ end
+ end
+
+ def test_should_accept_good_protocols
+ sanitizer = HTML::WhiteListSanitizer.new
+ HTML::WhiteListSanitizer.allowed_protocols.each do |proto|
+ assert !sanitizer.send(:contains_bad_protocols?, 'src', "#{proto}://good")
+ end
+ end
+
+ def test_should_reject_hex_codes_in_protocol
+ assert_sanitized %(1 ), "1 "
+ assert @sanitizer.send(:contains_bad_protocols?, 'src', "%6A%61%76%61%73%63%72%69%70%74%3A%61%6C%65%72%74%28%22%58%53%53%22%29")
+ end
+
+ def test_should_block_script_tag
+ assert_sanitized %(), ""
+ end
+
+ [%( ),
+ %( ),
+ %( ),
+ %( ">),
+ %( ),
+ %( ),
+ %( ),
+ %( ),
+ %( ),
+ %( ),
+ %( ),
+ %( ),
+ %( ),
+ %( ),
+ %( )].each_with_index do |img_hack, i|
+ define_method "test_should_not_fall_for_xss_image_hack_#{i+1}" do
+ assert_sanitized img_hack, " "
+ end
+ end
+
+ def test_should_sanitize_tag_broken_up_by_null
+ assert_sanitized %(alert(\"XSS\") ), "alert(\"XSS\")"
+ end
+
+ def test_should_sanitize_invalid_script_tag
+ assert_sanitized %(), ""
+ end
+
+ def test_should_sanitize_script_tag_with_multiple_open_brackets
+ assert_sanitized %(<), "<"
+ assert_sanitized %( }
+ assert_end
+ end
+
+ def test_unterminated_comment
+ tokenize %{hello \n\n It no longer contains any HTML .
\n}))
+ assert_equal "This has a here.", strip_tags("This has a here.")
+ [nil, '', ' '].each { |blank| assert_equal blank, strip_tags(blank) }
+ end
+
+ def assert_sanitized(text, expected = nil)
+ assert_equal((expected || text), sanitize(text))
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/actionpack/test/template/scriptaculous_helper_test.rb b/vendor/rails/actionpack/test/template/scriptaculous_helper_test.rb
index 3538ee0c..04fbe33d 100644
--- a/vendor/rails/actionpack/test/template/scriptaculous_helper_test.rb
+++ b/vendor/rails/actionpack/test/template/scriptaculous_helper_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../abstract_unit'
+require "#{File.dirname(__FILE__)}/../abstract_unit"
class ScriptaculousHelperTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
@@ -13,7 +13,7 @@ class ScriptaculousHelperTest < Test::Unit::TestCase
def setup
@controller = Class.new do
- def url_for(options, *parameters_for_method_reference)
+ def url_for(options)
url = "http://www.example.com/"
url << options[:action].to_s if options and options[:action]
url
@@ -28,6 +28,8 @@ class ScriptaculousHelperTest < Test::Unit::TestCase
assert_equal "new Effect.Fade(\"fademe\",{duration:4.0});", visual_effect(:fade, "fademe", :duration => 4.0)
assert_equal "new Effect.Shake(element,{});", visual_effect(:shake)
assert_equal "new Effect.DropOut(\"dropme\",{queue:'end'});", visual_effect(:drop_out, 'dropme', :queue => :end)
+ assert_equal "new Effect.Highlight(\"status\",{endcolor:'#EEEEEE'});", visual_effect(:highlight, 'status', :endcolor => '#EEEEEE')
+ assert_equal "new Effect.Highlight(\"status\",{restorecolor:'#500000', startcolor:'#FEFEFE'});", visual_effect(:highlight, 'status', :restorecolor => '#500000', :startcolor => '#FEFEFE')
# chop the queue params into a comma separated list
beginning, ending = 'new Effect.DropOut("dropme",{queue:{', '}});'
@@ -87,4 +89,8 @@ class ScriptaculousHelperTest < Test::Unit::TestCase
assert_dom_equal %(),
drop_receiving_element("droptarget1", :accept => ['tshirts','mugs'], :update => 'infobox')
end
-end
\ No newline at end of file
+
+ def protect_against_forgery?
+ false
+ end
+end
diff --git a/vendor/rails/actionpack/test/template/tag_helper_test.rb b/vendor/rails/actionpack/test/template/tag_helper_test.rb
index 23992ee6..c7edc678 100644
--- a/vendor/rails/actionpack/test/template/tag_helper_test.rb
+++ b/vendor/rails/actionpack/test/template/tag_helper_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../abstract_unit'
+require "#{File.dirname(__FILE__)}/../abstract_unit"
class TagHelperTest < Test::Unit::TestCase
include ActionView::Helpers::TagHelper
@@ -13,7 +13,9 @@ class TagHelperTest < Test::Unit::TestCase
end
def test_tag_options
- assert_match /\A
\z/, tag("p", "class" => "show", :class => "elsewhere")
+ str = tag("p", "class" => "show", :class => "elsewhere")
+ assert_match /class="show"/, str
+ assert_match /class="elsewhere"/, str
end
def test_tag_options_rejects_nil_option
@@ -47,6 +49,11 @@ class TagHelperTest < Test::Unit::TestCase
assert_dom_equal %(Hello world!
), _erbout
end
+ def test_content_tag_with_block_and_options_outside_of_action_view
+ assert_equal content_tag("a", "Create", :href => "create"),
+ content_tag("a", "href" => "create") { "Create" }
+ end
+
def test_cdata_section
assert_equal "]]>", cdata_section("")
end
@@ -66,4 +73,8 @@ class TagHelperTest < Test::Unit::TestCase
assert_equal %( ), tag('a', :href => escaped)
end
end
+
+ def test_disable_escaping
+ assert_equal ' ', tag('a', { :href => '&' }, false, false)
+ end
end
diff --git a/vendor/rails/actionpack/test/template/text_helper_test.rb b/vendor/rails/actionpack/test/template/text_helper_test.rb
index 955375cd..92d6bc3a 100644
--- a/vendor/rails/actionpack/test/template/text_helper_test.rb
+++ b/vendor/rails/actionpack/test/template/text_helper_test.rb
@@ -1,11 +1,11 @@
-require File.dirname(__FILE__) + '/../abstract_unit'
+require "#{File.dirname(__FILE__)}/../abstract_unit"
require "#{File.dirname(__FILE__)}/../testing_sandbox"
class TextHelperTest < Test::Unit::TestCase
include ActionView::Helpers::TextHelper
include ActionView::Helpers::TagHelper
include TestingSandbox
-
+
def setup
# This simulates the fact that instance variables are reset every time
# a view is rendered. The cycle helper depends on this behavior.
@@ -31,6 +31,11 @@ class TextHelperTest < Test::Unit::TestCase
assert_equal "Hello Wor...", truncate("Hello World!!", 12)
end
+ def test_truncate_should_use_default_length_of_30
+ str = "This is a string that will go longer then the default truncate length of 30"
+ assert_equal str[0...27] + "...", truncate(str)
+ end
+
def test_truncate_multibyte
with_kcode 'none' do
assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", 10)
@@ -41,10 +46,6 @@ class TextHelperTest < Test::Unit::TestCase
end
end
- def test_strip_links
- assert_equal "on my mind\nall day long", strip_links("on my mind \nall day long ")
- end
-
def test_highlighter
assert_equal(
"This is a beautiful morning",
@@ -65,6 +66,8 @@ class TextHelperTest < Test::Unit::TestCase
"This text is not changed because we supplied an empty phrase",
highlight("This text is not changed because we supplied an empty phrase", nil)
)
+
+ assert_equal ' ', highlight(' ', 'blank text is returned verbatim')
end
def test_highlighter_with_regexp
@@ -84,6 +87,10 @@ class TextHelperTest < Test::Unit::TestCase
)
end
+ def test_highlighting_multiple_phrases_in_one_pass
+ assert_equal %(wow em ), highlight('wow em', %w(wow em), '\1 ')
+ end
+
def test_excerpt
assert_equal("...is a beautiful morni...", excerpt("This is a beautiful morning", "beautiful", 5))
assert_equal("This is a...", excerpt("This is a beautiful morning", "this", 5))
@@ -109,6 +116,10 @@ class TextHelperTest < Test::Unit::TestCase
assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", 15))
end
+ def test_word_wrap_with_extra_newlines
+ assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", 15))
+ end
+
def test_pluralization
assert_equal("1 count", pluralize(1, "count"))
assert_equal("2 counts", pluralize(2, "count"))
@@ -117,6 +128,20 @@ class TextHelperTest < Test::Unit::TestCase
assert_equal("1,066 counts", pluralize('1,066', "count"))
assert_equal("1.25 counts", pluralize('1.25', "count"))
assert_equal("2 counters", pluralize(2, "count", "counters"))
+ assert_equal("0 counters", pluralize(nil, "count", "counters"))
+ assert_equal("2 people", pluralize(2, "person"))
+ assert_equal("10 buffaloes", pluralize(10, "buffalo"))
+ end
+
+ uses_mocha("should_just_add_s_for_pluralize_without_inflector_loaded") do
+ def test_should_just_add_s_for_pluralize_without_inflector_loaded
+ Object.expects(:const_defined?).with("Inflector").times(4).returns(false)
+ assert_equal("1 count", pluralize(1, "count"))
+ assert_equal("2 persons", pluralize(2, "person"))
+ assert_equal("2 personss", pluralize("2", "persons"))
+ assert_equal("2 counts", pluralize(2, "count"))
+ assert_equal("10 buffalos", pluralize(10, "buffalo"))
+ end
end
def test_auto_link_parsing
@@ -132,6 +157,7 @@ class TextHelperTest < Test::Unit::TestCase
http://www.rubyonrails.com/contact;new?with=query&string=params
http://www.rubyonrails.com/~minam/contact;new?with=query&string=params
http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007
+ http://www.mail-archive.com/rails@lists.rubyonrails.org/
)
urls.each do |url|
@@ -142,6 +168,8 @@ class TextHelperTest < Test::Unit::TestCase
def test_auto_linking
email_raw = 'david@loudthinking.com'
email_result = %{#{email_raw} }
+ email2_raw = '+david@loudthinking.com'
+ email2_result = %{#{email2_raw} }
link_raw = 'http://www.rubyonrails.com'
link_result = %{#{link_raw} }
link_result_with_options = %{#{link_raw} }
@@ -161,6 +189,8 @@ class TextHelperTest < Test::Unit::TestCase
link8_result = %{#{link8_raw} }
link9_raw = 'http://business.timesonline.co.uk/article/0,,9065-2473189,00.html'
link9_result = %{#{link9_raw} }
+ link10_raw = 'http://www.mail-archive.com/ruby-talk@ruby-lang.org/'
+ link10_result = %{#{link10_raw} }
assert_equal %(hello #{email_result}), auto_link("hello #{email_raw}", :email_addresses)
assert_equal %(Go to #{link_result}), auto_link("Go to #{link_raw}", :urls)
@@ -200,6 +230,8 @@ class TextHelperTest < Test::Unit::TestCase
assert_equal %(#{link9_result} Link
), auto_link("#{link9_raw} Link
")
assert_equal %(Go to #{link9_result}.), auto_link(%(Go to #{link9_raw}.))
assert_equal %(Go to #{link9_result}. seriously, #{link9_result}? i think I'll say hello to #{email_result}. instead.
), auto_link(%(Go to #{link9_raw}. seriously, #{link9_raw}? i think I'll say hello to #{email_raw}. instead.
))
+ assert_equal %(#{link10_result} Link
), auto_link("#{link10_raw} Link
")
+ assert_equal email2_result, auto_link(email2_raw)
assert_equal '', auto_link(nil)
assert_equal '', auto_link('')
end
@@ -218,42 +250,6 @@ class TextHelperTest < Test::Unit::TestCase
assert_equal %(#{url[0...7]}... #{email[0...7]}...
), auto_link("#{url} #{email}
") { |url| truncate(url, 10) }
end
- def test_sanitize_form
- raw = ""
- result = sanitize(raw)
- assert_equal %(<form action="/foo/bar" method="post"> </form>), result
- end
-
- def test_sanitize_plaintext
- raw = "foo "
- result = sanitize(raw)
- assert_equal "<plaintext>foo </plaintext>", result
- end
-
- def test_sanitize_script
- raw = ""
- result = sanitize(raw)
- assert_equal %(<script language="Javascript">blah blah blah</script>), result
- end
-
- def test_sanitize_js_handlers
- raw = %{onthis="do that" hello }
- result = sanitize(raw)
- assert_equal %{onthis="do that" hello }, result
- end
-
- def test_sanitize_javascript_href
- raw = %{href="javascript:bang" foo , bar }
- result = sanitize(raw)
- assert_equal %{href="javascript:bang" foo , bar }, result
- end
-
- def test_sanitize_image_src
- raw = %{src="javascript:bang" foo, bar }
- result = sanitize(raw)
- assert_equal %{src="javascript:bang" foo, bar }, result
- end
-
def test_cycle_class
value = Cycle.new("one", 2, "3")
assert_equal("one", value.to_s)
@@ -336,14 +332,4 @@ class TextHelperTest < Test::Unit::TestCase
assert_equal("red", cycle("red", "blue"))
assert_equal(%w{Specialized Fuji Giant}, @cycles)
end
-
- def test_strip_tags
- assert_equal("This is a test.", strip_tags("This is a test .
"))
- assert_equal("This is a test.", strip_tags("This is a test."))
- assert_equal(
- %{This is a test.\n\n\nIt no longer contains any HTML.\n}, strip_tags(
- %{This is a test . \n\n\n\nIt no longer contains any HTML .
\n}))
- assert_equal "This has a here.", strip_tags("This has a here.")
- [nil, '', ' '].each { |blank| assert_equal blank, strip_tags(blank) }
- end
end
diff --git a/vendor/rails/actionpack/test/template/url_helper_test.rb b/vendor/rails/actionpack/test/template/url_helper_test.rb
index 4a772d50..ee75965b 100644
--- a/vendor/rails/actionpack/test/template/url_helper_test.rb
+++ b/vendor/rails/actionpack/test/template/url_helper_test.rb
@@ -1,10 +1,6 @@
-require File.dirname(__FILE__) + '/../abstract_unit'
+require "#{File.dirname(__FILE__)}/../abstract_unit"
-require File.dirname(__FILE__) + '/../../lib/action_view/helpers/url_helper'
-require File.dirname(__FILE__) + '/../../lib/action_view/helpers/asset_tag_helper'
-require File.dirname(__FILE__) + '/../../lib/action_view/helpers/tag_helper'
-
-RequestMock = Struct.new("Request", :request_uri, :protocol, :host_with_port)
+RequestMock = Struct.new("Request", :request_uri, :protocol, :host_with_port, :env)
class UrlHelperTest < Test::Unit::TestCase
include ActionView::Helpers::AssetTagHelper
@@ -14,14 +10,14 @@ class UrlHelperTest < Test::Unit::TestCase
def setup
@controller = Class.new do
attr_accessor :url, :request
- def url_for(options, *parameters_for_method_reference)
+ def url_for(options)
url
end
end
@controller = @controller.new
@controller.url = "http://www.example.com"
end
-
+
def test_url_for_escapes_urls
@controller.url = "http://www.example.com?a=b&c=d"
assert_equal "http://www.example.com?a=b&c=d", url_for(:a => 'b', :c => 'd')
@@ -29,6 +25,11 @@ class UrlHelperTest < Test::Unit::TestCase
assert_equal "http://www.example.com?a=b&c=d", url_for(:a => 'b', :c => 'd', :escape => false)
end
+ def test_url_for_escapes_url_once
+ @controller.url = "http://www.example.com?a=b&c=d"
+ assert_equal "http://www.example.com?a=b&c=d", url_for("http://www.example.com?a=b&c=d")
+ end
+
# todo: missing test cases
def test_button_to_with_straight_url
assert_dom_equal "", button_to("Hello", "http://www.example.com")
@@ -63,14 +64,14 @@ class UrlHelperTest < Test::Unit::TestCase
button_to("Hello", "http://www.example.com", :disabled => true)
)
end
-
+
def test_button_to_with_method_delete
assert_dom_equal(
"",
button_to("Hello", "http://www.example.com", :method => :delete)
)
end
-
+
def test_button_to_with_method_get
assert_dom_equal(
"",
@@ -81,6 +82,24 @@ class UrlHelperTest < Test::Unit::TestCase
def test_link_tag_with_straight_url
assert_dom_equal "Hello ", link_to("Hello", "http://www.example.com")
end
+
+ def test_link_tag_without_host_option
+ ActionController::Base.class_eval { attr_accessor :url }
+ url = {:controller => 'weblog', :action => 'show'}
+ @controller = ActionController::Base.new
+ @controller.request = ActionController::TestRequest.new
+ @controller.url = ActionController::UrlRewriter.new(@controller.request, url)
+ assert_dom_equal(%q{Test Link }, link_to('Test Link', url))
+ end
+
+ def test_link_tag_with_host_option
+ ActionController::Base.class_eval { attr_accessor :url }
+ url = {:controller => 'weblog', :action => 'show', :host => 'www.example.com'}
+ @controller = ActionController::Base.new
+ @controller.request = ActionController::TestRequest.new
+ @controller.url = ActionController::UrlRewriter.new(@controller.request, url)
+ assert_dom_equal(%q{Test Link }, link_to('Test Link', url))
+ end
def test_link_tag_with_query
assert_dom_equal "Hello ", link_to("Hello", "http://www.example.com?q1=v1&q2=v2")
@@ -90,6 +109,26 @@ class UrlHelperTest < Test::Unit::TestCase
assert_dom_equal "http://www.example.com?q1=v1&q2=v2 ", link_to(nil, "http://www.example.com?q1=v1&q2=v2")
end
+ def test_link_tag_with_back
+ @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+ assert_dom_equal "go back ", link_to('go back', :back)
+ end
+
+ def test_link_tag_with_back_and_no_referer
+ @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+ assert_dom_equal "go back ", link_to('go back', :back)
+ end
+
+ def test_link_tag_with_back
+ @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+ assert_dom_equal "go back ", link_to('go back', :back)
+ end
+
+ def test_link_tag_with_back_and_no_referer
+ @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+ assert_dom_equal "go back ", link_to('go back', :back)
+ end
+
def test_link_tag_with_img
assert_dom_equal " ", link_to(" ", "http://www.example.com")
end
@@ -101,18 +140,18 @@ class UrlHelperTest < Test::Unit::TestCase
def test_link_tag_with_custom_onclick
assert_dom_equal "Hello ", link_to("Hello", "http://www.example.com", :onclick => "alert('yay!')")
end
-
+
def test_link_tag_with_javascript_confirm
assert_dom_equal(
"Hello ",
link_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
)
assert_dom_equal(
- "Hello ",
+ "Hello ",
link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure, can you?")
)
assert_dom_equal(
- "Hello ",
+ "Hello ",
link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure,\n can you?")
)
end
@@ -123,15 +162,15 @@ class UrlHelperTest < Test::Unit::TestCase
link_to("Hello", "http://www.example.com", :popup => true)
)
assert_dom_equal(
- "Hello ",
+ "Hello ",
link_to("Hello", "http://www.example.com", :popup => 'true')
)
assert_dom_equal(
- "Hello ",
+ "Hello ",
link_to("Hello", "http://www.example.com", :popup => ['window_name', 'width=300,height=300'])
)
end
-
+
def test_link_tag_with_popup_and_javascript_confirm
assert_dom_equal(
"Hello ",
@@ -142,16 +181,7 @@ class UrlHelperTest < Test::Unit::TestCase
link_to("Hello", "http://www.example.com", { :popup => ['window_name', 'width=300,height=300'], :confirm => "Are you serious?" })
)
end
-
- def test_link_tag_with_post_is_deprecated
- assert_deprecated 'post' do
- assert_dom_equal(
- "Hello ",
- link_to("Hello", "http://www.example.com", :post => true)
- )
- end
- end
-
+
def test_link_tag_using_post_javascript
assert_dom_equal(
"Hello ",
@@ -165,14 +195,21 @@ class UrlHelperTest < Test::Unit::TestCase
link_to("Destroy", "http://www.example.com", :method => :delete)
)
end
-
+
+ def test_link_tag_using_delete_javascript_and_href
+ assert_dom_equal(
+ "Destroy ",
+ link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#')
+ )
+ end
+
def test_link_tag_using_post_javascript_and_confirm
assert_dom_equal(
"Hello ",
link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?")
- )
+ )
end
-
+
def test_link_tag_using_post_javascript_and_popup
assert_raises(ActionView::ActionViewError) { link_to("Hello", "http://www.example.com", :popup => true, :method => :post, :confirm => "Are you serious?") }
end
@@ -181,17 +218,17 @@ class UrlHelperTest < Test::Unit::TestCase
assert_equal "Showing", link_to_unless(true, "Showing", :action => "show", :controller => "weblog")
assert_dom_equal "Listing ", link_to_unless(false, "Listing", :action => "list", :controller => "weblog")
assert_equal "Showing", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1)
- assert_equal "Showing ", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1) { |name, options, html_options, *parameters_for_method_reference|
+ assert_equal "Showing ", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1) { |name, options, html_options|
"#{name} "
}
assert_equal "Showing ", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1) { |name|
"#{name} "
- }
+ }
assert_equal "test", link_to_unless(true, "Showing", :action => "show", :controller => "weblog", :id => 1) {
"test"
- }
+ }
end
-
+
def test_link_to_if
assert_equal "Showing", link_to_if(false, "Showing", :action => "show", :controller => "weblog")
assert_dom_equal "Listing ", link_to_if(true, "Listing", :action => "list", :controller => "weblog")
@@ -226,41 +263,46 @@ class UrlHelperTest < Test::Unit::TestCase
def test_mail_to_with_javascript
assert_dom_equal "", mail_to("me@domain.com", "My email", :encode => "javascript")
end
-
+
def test_mail_with_options
assert_dom_equal(
%(My email ),
mail_to("me@example.com", "My email", :cc => "ccaddress@example.com", :bcc => "bccaddress@example.com", :subject => "This is an example email", :body => "This is the body of the message.")
)
end
-
+
def test_mail_to_with_img
assert_dom_equal %( ), mail_to('feedback@example.com', ' ')
end
def test_mail_to_with_hex
- assert_dom_equal "My email ", mail_to("me@domain.com", "My email", :encode => "hex")
+ assert_dom_equal "My email ", mail_to("me@domain.com", "My email", :encode => "hex")
+ assert_dom_equal "me@domain.com ", mail_to("me@domain.com", nil, :encode => "hex")
end
def test_mail_to_with_replace_options
assert_dom_equal "wolfgang(at)stufenlos(dot)net ", mail_to("wolfgang@stufenlos.net", nil, :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "me(at)domain.com ", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
- assert_dom_equal "My email ", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
- assert_dom_equal "me(at)domain(dot)com ", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
+ assert_dom_equal "me(at)domain.com ", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
+ assert_dom_equal "My email ", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
+ assert_dom_equal "me(at)domain(dot)com ", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
assert_dom_equal "", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
end
+
+ def protect_against_forgery?
+ false
+ end
end
class UrlHelperWithControllerTest < Test::Unit::TestCase
class UrlHelperController < ActionController::Base
- self.template_root = "#{File.dirname(__FILE__)}/../fixtures/"
+ self.view_paths = [ "#{File.dirname(__FILE__)}/../fixtures/" ]
def self.controller_path; 'url_helper_with_controller' end
def show_url_for
render :inline => "<%= url_for :controller => 'url_helper_with_controller', :action => 'show_url_for' %>"
end
-
+
def show_named_route
render :inline => "<%= show_named_route_#{params[:kind]} %>"
end
@@ -275,26 +317,26 @@ class UrlHelperWithControllerTest < Test::Unit::TestCase
@response = ActionController::TestResponse.new
@controller = UrlHelperController.new
end
-
+
def test_url_for_shows_only_path
get :show_url_for
assert_equal '/url_helper_with_controller/show_url_for', @response.body
end
-
+
def test_named_route_shows_host_and_path
with_url_helper_routing do
get :show_named_route, :kind => 'url'
assert_equal 'http://test.host/url_helper_with_controller/show_named_route', @response.body
end
end
-
+
def test_named_route_path_shows_only_path
with_url_helper_routing do
get :show_named_route, :kind => 'path'
assert_equal '/url_helper_with_controller/show_named_route', @response.body
end
end
-
+
protected
def with_url_helper_routing
with_routing do |set|
@@ -308,7 +350,7 @@ end
class LinkToUnlessCurrentWithControllerTest < Test::Unit::TestCase
class TasksController < ActionController::Base
- self.template_root = "#{File.dirname(__FILE__)}/../fixtures/"
+ self.view_paths = ["#{File.dirname(__FILE__)}/../fixtures/"]
def self.controller_path; 'tasks' end
@@ -364,3 +406,131 @@ class LinkToUnlessCurrentWithControllerTest < Test::Unit::TestCase
end
end
end
+
+
+class Workshop
+ attr_accessor :id, :new_record
+
+ def initialize(id, new_record)
+ @id, @new_record = id, new_record
+ end
+
+ def new_record?
+ @new_record
+ end
+
+ def to_s
+ id.to_s
+ end
+end
+
+class Session
+ attr_accessor :id, :workshop_id, :new_record
+
+ def initialize(id, new_record)
+ @id, @new_record = id, new_record
+ end
+
+ def new_record?
+ @new_record
+ end
+
+ def to_s
+ id.to_s
+ end
+end
+
+class PolymorphicControllerTest < Test::Unit::TestCase
+ class WorkshopsController < ActionController::Base
+ self.view_paths = ["#{File.dirname(__FILE__)}/../fixtures/"]
+
+ def self.controller_path; 'workshops' end
+
+ def index
+ @workshop = Workshop.new(1, true)
+ render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
+ end
+
+ def show
+ @workshop = Workshop.new(params[:id], false)
+ render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
+ end
+
+ def rescue_action(e) raise e end
+ end
+
+ class SessionsController < ActionController::Base
+ self.view_paths = ["#{File.dirname(__FILE__)}/../fixtures/"]
+
+ def self.controller_path; 'sessions' end
+
+ def index
+ @workshop = Workshop.new(params[:workshop_id], false)
+ @session = Session.new(1, true)
+ render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
+ end
+
+ def show
+ @workshop = Workshop.new(params[:workshop_id], false)
+ @session = Session.new(params[:id], false)
+ render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
+ end
+
+ def rescue_action(e) raise e end
+ end
+
+ include ActionView::Helpers::UrlHelper
+
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_new_resource
+ @controller = WorkshopsController.new
+
+ with_restful_routing do
+ get :index
+ assert_equal "/workshops\nWorkshop ", @response.body
+ end
+ end
+
+ def test_existing_resource
+ @controller = WorkshopsController.new
+
+ with_restful_routing do
+ get :show, :id => 1
+ assert_equal "/workshops/1\nWorkshop ", @response.body
+ end
+ end
+
+ def test_new_nested_resource
+ @controller = SessionsController.new
+
+ with_restful_routing do
+ get :index, :workshop_id => 1
+ assert_equal "/workshops/1/sessions\nSession ", @response.body
+ end
+ end
+
+ def test_existing_nested_resource
+ @controller = SessionsController.new
+
+ with_restful_routing do
+ get :show, :workshop_id => 1, :id => 1
+ assert_equal "/workshops/1/sessions/1\nSession ", @response.body
+ end
+ end
+
+ protected
+ def with_restful_routing
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :workshops do |w|
+ w.resources :sessions
+ end
+ end
+ yield
+ end
+ end
+end
diff --git a/vendor/rails/actionwebservice/CHANGELOG b/vendor/rails/actionwebservice/CHANGELOG
deleted file mode 100644
index 99134271..00000000
--- a/vendor/rails/actionwebservice/CHANGELOG
+++ /dev/null
@@ -1,293 +0,0 @@
-*1.2.5* (October 12th, 2007)
-
-* Depend on Action Pack 1.13.5
-
-* Depend on Active Record 1.15.5
-
-
-*1.2.4* (October 4th, 2007)
-
-* Depend on Action Pack 1.13.4
-
-* Depend on Active Record 1.15.4
-
-
-*1.2.3* (March 12th, 2007)
-
-* Depend on Action Pack 1.13.3
-
-
-*1.2.2* (Feburary 4th, 2007)
-
-* Depend on Action Pack 1.13.2
-
-
-*1.2.1* (January 16th, 2007)
-
-* Depend on Action Pack 1.13.1
-
-
-*1.2.0* (January 16th, 2007)
-
-* Removed invocation of deprecated before_action and around_action filter methods. Corresponding before_invocation and after_invocation methods should be used instead. #6275 [Kent Sibilev]
-
-* Provide access to the underlying SOAP driver. #6212 [bmilekic, Kent Sibilev]
-
-* ActionWebService WSDL generation ignores HTTP_X_FORWARDED_HOST [Paul Butcher ]
-
-* Tighten rescue clauses. #5985 [james@grayproductions.net]
-
-* Fixed XMLRPC multicall when one of the called methods returns a struct object. [Kent Sibilev]
-
-* Fix invoke_layered since api_method didn't declare :expects. Closes #4720. [Kevin Ballard , Kent Sibilev]
-
-
-*1.1.6* (August 10th, 2006)
-
-* Rely on Action Pack 1.12.5
-
-
-*1.1.5* (August 8th, 2006)
-
-* Rely on Action Pack 1.12.4 and Active Record 1.14.4
-
-
-*1.1.4* (June 29th, 2006)
-
-* Rely on Action Pack 1.12.3
-
-
-*1.1.3* (June 27th, 2006)
-
-* Rely on Action Pack 1.12.2 and Active Record 1.14.3
-
-
-*1.1.2* (April 9th, 2006)
-
-* Rely on Active Record 1.14.2
-
-
-*1.1.1* (April 6th, 2006)
-
-* Do not convert driver options to strings (#4499)
-
-
-*1.1.0* (March 27th, 2006)
-
-* Make ActiveWebService::Struct type reloadable
-
-* Fix scaffolding action when one of the members of a structural type has date or time type
-
-* Remove extra index hash when generating scaffold html for parameters of structural type #4374 [joe@mjg2.com]
-
-* Fix Scaffold Fails with Struct as a Parameter #4363 [joe@mjg2.com]
-
-* Fix soap type registration of multidimensional arrays (#4232)
-
-* Fix that marshaler couldn't handle ActiveRecord models defined in a different namespace (#2392).
-
-* Fix that marshaler couldn't handle structs with members of ActiveRecord type (#1889).
-
-* Fix that marshaler couldn't handle nil values for inner structs (#3576).
-
-* Fix that changes to ActiveWebService::API::Base required restarting of the server (#2390).
-
-* Fix scaffolding for signatures with :date, :time and :base64 types (#3321, #2769, #2078).
-
-* Fix for incorrect casting of TrueClass/FalseClass instances (#2633, #3421).
-
-* Fix for incompatibility problems with SOAP4R 1.5.5 (#2553) [Kent Sibilev]
-
-
-*1.0.0* (December 13th, 2005)
-
-* Become part of Rails 1.0
-
-*0.9.4* (December 7th, 2005)
-
-* Update from LGPL to MIT license as per Minero Aoki's permission. [Marcel Molina Jr.]
-
-* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
-
-* Fix that XML-RPC date/time values did not have well-defined behaviour (#2516, #2534). This fix has one caveat, in that we can't support pre-1970 dates from XML-RPC clients.
-
-*0.9.3* (November 7th, 2005)
-
-* Upgraded to Action Pack 1.11.0 and Active Record 1.13.0
-
-
-*0.9.2* (October 26th, 2005)
-
-* Upgraded to Action Pack 1.10.2 and Active Record 1.12.2
-
-
-*0.9.1* (October 19th, 2005)
-
-* Upgraded to Action Pack 1.10.1 and Active Record 1.12.1
-
-
-*0.9.0* (October 16th, 2005)
-
-* Fix invalid XML request generation bug in test_invoke [Ken Barker]
-
-* Add XML-RPC 'system.multicall' support #1941 [jbonnar]
-
-* Fix duplicate XSD entries for custom types shared across delegated/layered services #1729 [Tyler Kovacs]
-
-* Allow multiple invocations in the same test method #1720 [dkhawk]
-
-* Added ActionWebService::API::Base.soap_client and ActionWebService::API::Base.xmlrpc_client helper methods to create the internal clients for an API, useful for testing from ./script/console
-
-* ActionWebService now always returns UTF-8 responses.
-
-
-*0.8.1* (11 July, 2005)
-
-* Fix scaffolding for Action Pack controller changes
-
-
-*0.8.0* (6 July, 2005)
-
-* Fix WSDL generation by aliasing #inherited instead of trying to overwrite it, or the WSDL action may end up not being defined in the controller
-
-* Add ActionController::Base.wsdl_namespace option, to allow overriding of the namespace used in generated WSDL and SOAP messages. This is equivalent to the [WebService(Namespace = "Value")] attribute in .NET.
-
-* Add workaround for Ruby 1.8.3's SOAP4R changing the return value of SOAP::Mapping::Registry#find_mapped_soap_class #1414 [Shugo Maeda]
-
-* Fix moduled controller URLs in WSDL, and add unit test to verify the generated URL #1428
-
-* Fix scaffolding template paths, it was broken on Win32
-
-* Fix that functional testing of :layered controllers failed when using the SOAP protocol
-
-* Allow invocation filters in :direct controllers as well, as they have access to more information regarding the web service request than ActionPack filters
-
-* Add support for a :base64 signature type #1272 [Shugo Maeda]
-
-* Fix that boolean fields were not rendered correctly in scaffolding
-
-* Fix that scaffolding was not working for :delegated dispatching
-
-* Add support for structured types as input parameters to scaffolding, this should let one test the blogging APIs using scaffolding as well
-
-* Fix that generated WSDL was not using relative_url_root for base URI #1210 [Shugo Maeda]
-
-* Use UTF-8 encoding by default for SOAP responses, but if an encoding is supplied by caller, use that for the response #1211 [Shugo Maeda, NAKAMURA Hiroshi]
-
-* If the WSDL was retrieved over HTTPS, use HTTPS URLs in the WSDL too
-
-* Fix that casting change in 0.7.0 would convert nil values to the default value for the type instead of leaving it as nil
-
-
-*0.7.1* (20th April, 2005)
-
-* Depend on Active Record 1.10.1 and Action Pack 1.8.1
-
-
-*0.7.0* (19th April, 2005)
-
-* When casting structured types, don't try to send obj.name= unless obj responds to it, causes casting to be less likely to fail for XML-RPC
-
-* Add scaffolding via ActionController::Base.web_service_scaffold for quick testing using a web browser
-
-* ActionWebService::API::Base#api_methods now returns a hash containing ActionWebService::API::Method objects instead of hashes. However, ActionWebService::API::Method defines a #[]() backwards compatibility method so any existing code utilizing this will still work.
-
-* The :layered dispatching mode can now be used with SOAP as well, allowing you to support SOAP and XML-RPC clients for APIs like the metaWeblog API
-
-* Remove ActiveRecordSoapMarshallable workaround, see #912 for details
-
-* Generalize casting code to be used by both SOAP and XML-RPC (previously, it was only XML-RPC)
-
-* Ensure return value is properly cast as well, fixes XML-RPC interoperability with Ecto and possibly other clients
-
-* Include backtraces in 500 error responses for failed request parsing, and remove "rescue nil" statements obscuring real errors for XML-RPC
-
-* Perform casting of struct members even if the structure is already of the correct type, so that the type we specify for the struct member is always the type of the value seen by the API implementation
-
-
-*0.6.2* (27th March, 2005)
-
-* Allow method declarations for direct dispatching to declare parameters as well. We treat an arity of < 0 or > 0 as an indication that we should send through parameters. Closes #939.
-
-
-*0.6.1* (22th March, 2005)
-
-* Fix that method response QNames mismatched with that declared in the WSDL, makes SOAP::WSDLDriverFactory work against AWS again
-
-* Fix that @request.env was being modified, instead, dup the value gotten from env
-
-* Fix XML-RPC example to use :layered mode, so it works again
-
-* Support casting '0' or 0 into false, and '1' or 1 into true, when expecting a boolean value
-
-* Fix that SOAP fault response fault code values were not QName's #804
-
-
-*0.6.0* (7th March, 2005)
-
-* Add action_controller/test_invoke, used for integrating AWS with the Rails testing infrastructure
-
-* Allow passing through options to the SOAP RPC driver for the SOAP client
-
-* Make the SOAP WS marshaler use #columns to decide which fields to marshal as well, avoids providing attributes brought in by associations
-
-* Add ActionWebService::API::Base.allow_active_record_expects option, with a default of false. Setting this to true will allow specifying ActiveRecord::Base model classes in :expects . API writers should take care to validate the received ActiveRecord model objects when turning it on, and/or have an authentication mechanism in place to reduce the security risk.
-
-* Improve error message reporting. Bugs in either AWS or the web service itself will send back a protocol-specific error report message if possible, otherwise, provide as much detail as possible.
-
-* Removed type checking of received parameters, and perform casting for XML-RPC if possible, but fallback to the received parameters if casting fails, closes #677
-
-* Refactored SOAP and XML-RPC marshaling and encoding into a small library devoted exclusively to protocol specifics, also cleaned up the SOAP marshaling approach, so that array and custom type marshaling should be a bit faster.
-
-* Add namespaced XML-RPC method name support, closes #678
-
-* Replace '::' with '..' in fully qualified type names for marshaling and WSDL. This improves interoperability with .NET, and closes #676.
-
-
-*0.5.0* (24th February, 2005)
-
- * lib/action_service/dispatcher*: replace "router" fragments with
- one file for Action Controllers, moves dispatching work out of
- the container
- * lib/*,test/*,examples/*: rename project to
- ActionWebService. prefix all generic "service" type names with web_.
- update all using code as well as the RDoc.
- * lib/action_service/router/wsdl.rb: ensure that #wsdl is
- defined in the final container class, or the new ActionPack
- filtering will exclude it
- * lib/action_service/struct.rb,test/struct_test.rb: create a
- default #initialize on inherit that accepts a Hash containing
- the default member values
- * lib/action_service/api/action_controller.rb: add support and
- tests for #client_api in controller
- * test/router_wsdl_test.rb: add tests to ensure declared
- service names don't contain ':', as ':' causes interoperability
- issues
- * lib/*, test/*: rename "interface" concept to "api", and change all
- related uses to reflect this change. update all uses of Inflector
- to call the method on String instead.
- * test/api_test.rb: add test to ensure API definition not
- instantiatable
- * lib/action_service/invocation.rb: change @invocation_params to
- @method_params
- * lib/*: update RDoc
- * lib/action_service/struct.rb: update to support base types
- * lib/action_service/support/signature.rb: support the notion of
- "base types" in signatures, with well-known unambiguous names such as :int,
- :bool, etc, which map to the correct Ruby class. accept the same names
- used by ActiveRecord as well as longer versions of each, as aliases.
- * examples/*: update for seperate API definition updates
- * lib/action_service/*, test/*: extensive refactoring: define API methods in
- a seperate class, and specify it wherever used with 'service_api'.
- this makes writing a client API for accessing defined API methods
- with ActionWebService really easy.
- * lib/action_service/container.rb: fix a bug in default call
- handling for direct dispatching, and add ActionController filter
- support for direct dispatching.
- * test/router_action_controller_test.rb: add tests to ensure
- ActionController filters are actually called.
- * test/protocol_soap_test.rb: add more tests for direct dispatching.
-
-0.3.0
-
- * First public release
diff --git a/vendor/rails/actionwebservice/README b/vendor/rails/actionwebservice/README
deleted file mode 100644
index 78b91f08..00000000
--- a/vendor/rails/actionwebservice/README
+++ /dev/null
@@ -1,364 +0,0 @@
-= Action Web Service -- Serving APIs on rails
-
-Action Web Service provides a way to publish interoperable web service APIs with
-Rails without spending a lot of time delving into protocol details.
-
-
-== Features
-
-* SOAP RPC protocol support
-* Dynamic WSDL generation for APIs
-* XML-RPC protocol support
-* Clients that use the same API definitions as the server for
- easy interoperability with other Action Web Service based applications
-* Type signature hints to improve interoperability with static languages
-* Active Record model class support in signatures
-
-
-== Defining your APIs
-
-You specify the methods you want to make available as API methods in an
-ActionWebService::API::Base derivative, and then specify this API
-definition class wherever you want to use that API.
-
-The implementation of the methods is done separately from the API
-specification.
-
-
-==== Method name inflection
-
-Action Web Service will camelcase the method names according to Rails Inflector
-rules for the API visible to public callers. What this means, for example,
-is that the method names in generated WSDL will be camelcased, and callers will
-have to supply the camelcased name in their requests for the request to
-succeed.
-
-If you do not desire this behaviour, you can turn it off with the
-ActionWebService::API::Base +inflect_names+ option.
-
-
-==== Inflection examples
-
- :add => Add
- :find_all => FindAll
-
-
-==== Disabling inflection
-
- class PersonAPI < ActionWebService::API::Base
- inflect_names false
- end
-
-
-==== API definition example
-
- class PersonAPI < ActionWebService::API::Base
- api_method :add, :expects => [:string, :string, :bool], :returns => [:int]
- api_method :remove, :expects => [:int], :returns => [:bool]
- end
-
-==== API usage example
-
- class PersonController < ActionController::Base
- web_service_api PersonAPI
-
- def add
- end
-
- def remove
- end
- end
-
-
-== Publishing your APIs
-
-Action Web Service uses Action Pack to process protocol requests. There are two
-modes of dispatching protocol requests, _Direct_, and _Delegated_.
-
-
-=== Direct dispatching
-
-This is the default mode. In this mode, public controller instance methods
-implement the API methods, and parameters are passed through to the methods in
-accordance with the API specification.
-
-The return value of the method is sent back as the return value to the
-caller.
-
-In this mode, a special api action is generated in the target
-controller to unwrap the protocol request, forward it on to the relevant method
-and send back the wrapped return value. This action must not be
-overridden.
-
-==== Direct dispatching example
-
- class PersonController < ApplicationController
- web_service_api PersonAPI
-
- def add
- end
-
- def remove
- end
- end
-
- class PersonAPI < ActionWebService::API::Base
- ...
- end
-
-
-For this example, protocol requests for +Add+ and +Remove+ methods sent to
-/person/api will be routed to the controller methods +add+ and +remove+.
-
-
-=== Delegated dispatching
-
-This mode can be turned on by setting the +web_service_dispatching_mode+ option
-in a controller to :delegated .
-
-In this mode, the controller contains one or more web service objects (objects
-that implement an ActionWebService::API::Base definition). These web service
-objects are each mapped onto one controller action only.
-
-==== Delegated dispatching example
-
- class ApiController < ApplicationController
- web_service_dispatching_mode :delegated
-
- web_service :person, PersonService.new
- end
-
- class PersonService < ActionWebService::Base
- web_service_api PersonAPI
-
- def add
- end
-
- def remove
- end
- end
-
- class PersonAPI < ActionWebService::API::Base
- ...
- end
-
-
-For this example, all protocol requests for +PersonService+ are
-sent to the /api/person action.
-
-The /api/person action is generated when the +web_service+
-method is called. This action must not be overridden.
-
-Other controller actions (actions that aren't the target of a +web_service+ call)
-are ignored for ActionWebService purposes, and can do normal action tasks.
-
-
-=== Layered dispatching
-
-This mode can be turned on by setting the +web_service_dispatching_mode+ option
-in a controller to :layered .
-
-This mode is similar to _delegated_ mode, in that multiple web service objects
-can be attached to one controller, however, all protocol requests are sent to a
-single endpoint.
-
-Use this mode when you want to share code between XML-RPC and SOAP clients,
-for APIs where the XML-RPC method names have prefixes. An example of such
-a method name would be blogger.newPost .
-
-
-==== Layered dispatching example
-
-
- class ApiController < ApplicationController
- web_service_dispatching_mode :layered
-
- web_service :mt, MovableTypeService.new
- web_service :blogger, BloggerService.new
- web_service :metaWeblog, MetaWeblogService.new
- end
-
- class MovableTypeService < ActionWebService::Base
- ...
- end
-
- class BloggerService < ActionWebService::Base
- ...
- end
-
- class MetaWeblogService < ActionWebService::API::Base
- ...
- end
-
-
-For this example, an XML-RPC call for a method with a name like
-mt.getCategories will be sent to the getCategories
-method on the :mt service.
-
-
-== Customizing WSDL generation
-
-You can customize the names used for the SOAP bindings in the generated
-WSDL by using the wsdl_service_name option in a controller:
-
- class WsController < ApplicationController
- wsdl_service_name 'MyApp'
- end
-
-You can also customize the namespace used in the generated WSDL for
-custom types and message definition types:
-
- class WsController < ApplicationController
- wsdl_namespace 'http://my.company.com/app/wsapi'
- end
-
-The default namespace used is 'urn:ActionWebService', if you don't supply
-one.
-
-
-== ActionWebService and UTF-8
-
-If you're going to be sending back strings containing non-ASCII UTF-8
-characters using the :string data type, you need to make sure that
-Ruby is using UTF-8 as the default encoding for its strings.
-
-The default in Ruby is to use US-ASCII encoding for strings, which causes a string
-validation check in the Ruby SOAP library to fail and your string to be sent
-back as a Base-64 value, which may confuse clients that expected strings
-because of the WSDL.
-
-Two ways of setting the default string encoding are:
-
-* Start Ruby using the -Ku command-line option to the Ruby executable
-* Set the $KCODE flag in config/environment.rb to the
- string 'UTF8'
-
-
-== Testing your APIs
-
-
-=== Functional testing
-
-You can perform testing of your APIs by creating a functional test for the
-controller dispatching the API, and calling #invoke in the test case to
-perform the invocation.
-
-Example:
-
- class PersonApiControllerTest < Test::Unit::TestCase
- def setup
- @controller = PersonController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
-
- def test_add
- result = invoke :remove, 1
- assert_equal true, result
- end
- end
-
-This example invokes the API method test , defined on
-the PersonController, and returns the result.
-
-
-=== Scaffolding
-
-You can also test your APIs with a web browser by attaching scaffolding
-to the controller.
-
-Example:
-
- class PersonController
- web_service_scaffold :invocation
- end
-
-This creates an action named invocation on the PersonController.
-
-Navigating to this action lets you select the method to invoke, supply the parameters,
-and view the result of the invocation.
-
-
-== Using the client support
-
-Action Web Service includes client classes that can use the same API
-definition as the server. The advantage of this approach is that your client
-will have the same support for Active Record and structured types as the
-server, and can just use them directly, and rely on the marshaling to Do The
-Right Thing.
-
-*Note*: The client support is intended for communication between Ruby on Rails
-applications that both use Action Web Service. It may work with other servers, but
-that is not its intended use, and interoperability can't be guaranteed, especially
-not for .NET web services.
-
-Web services protocol specifications are complex, and Action Web Service client
-support can only be guaranteed to work with a subset.
-
-
-==== Factory created client example
-
- class BlogManagerController < ApplicationController
- web_client_api :blogger, :xmlrpc, 'http://url/to/blog/api/RPC2', :handler_name => 'blogger'
- end
-
- class SearchingController < ApplicationController
- web_client_api :google, :soap, 'http://url/to/blog/api/beta', :service_name => 'GoogleSearch'
- end
-
-See ActionWebService::API::ActionController::ClassMethods for more details.
-
-==== Manually created client example
-
- class PersonAPI < ActionWebService::API::Base
- api_method :find_all, :returns => [[Person]]
- end
-
- soap_client = ActionWebService::Client::Soap.new(PersonAPI, "http://...")
- persons = soap_client.find_all
-
- class BloggerAPI < ActionWebService::API::Base
- inflect_names false
- api_method :getRecentPosts, :returns => [[Blog::Post]]
- end
-
- blog = ActionWebService::Client::XmlRpc.new(BloggerAPI, "http://.../xmlrpc", :handler_name => "blogger")
- posts = blog.getRecentPosts
-
-
-See ActionWebService::Client::Soap and ActionWebService::Client::XmlRpc for more details.
-
-== Dependencies
-
-Action Web Service requires that the Action Pack and Active Record are either
-available to be required immediately or are accessible as GEMs.
-
-It also requires a version of Ruby that includes SOAP support in the standard
-library. At least version 1.8.2 final (2004-12-25) of Ruby is recommended; this
-is the version tested against.
-
-
-== Download
-
-The latest Action Web Service version can be downloaded from
-http://rubyforge.org/projects/actionservice
-
-
-== Installation
-
-You can install Action Web Service with the following command.
-
- % [sudo] ruby setup.rb
-
-
-== License
-
-Action Web Service is released under the MIT license.
-
-
-== Support
-
-The Ruby on Rails mailing list
-
-Or, to contact the author, send mail to bitserf@gmail.com
-
diff --git a/vendor/rails/actionwebservice/Rakefile b/vendor/rails/actionwebservice/Rakefile
deleted file mode 100644
index 33d24d6a..00000000
--- a/vendor/rails/actionwebservice/Rakefile
+++ /dev/null
@@ -1,171 +0,0 @@
-require 'rubygems'
-require 'rake'
-require 'rake/testtask'
-require 'rake/rdoctask'
-require 'rake/packagetask'
-require 'rake/gempackagetask'
-require 'rake/contrib/rubyforgepublisher'
-require 'fileutils'
-require File.join(File.dirname(__FILE__), 'lib', 'action_web_service', 'version')
-
-PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
-PKG_NAME = 'actionwebservice'
-PKG_VERSION = ActionWebService::VERSION::STRING + PKG_BUILD
-PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
-PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}"
-
-RELEASE_NAME = "REL #{PKG_VERSION}"
-
-RUBY_FORGE_PROJECT = "aws"
-RUBY_FORGE_USER = "webster132"
-
-desc "Default Task"
-task :default => [ :test ]
-
-
-# Run the unit tests
-Rake::TestTask.new { |t|
- t.libs << "test"
- t.test_files = Dir['test/*_test.rb']
- t.verbose = true
-}
-
-SCHEMA_PATH = File.join(File.dirname(__FILE__), *%w(test fixtures db_definitions))
-
-desc 'Build the MySQL test database'
-task :build_database do
- %x( mysqladmin create actionwebservice_unittest )
- %x( mysql actionwebservice_unittest < #{File.join(SCHEMA_PATH, 'mysql.sql')} )
-end
-
-
-# Generate the RDoc documentation
-Rake::RDocTask.new { |rdoc|
- rdoc.rdoc_dir = 'doc'
- rdoc.title = "Action Web Service -- Web services for Action Pack"
- rdoc.options << '--line-numbers' << '--inline-source'
- rdoc.template = "#{ENV['template']}.rb" if ENV['template']
- rdoc.rdoc_files.include('README')
- rdoc.rdoc_files.include('CHANGELOG')
- rdoc.rdoc_files.include('lib/action_web_service.rb')
- rdoc.rdoc_files.include('lib/action_web_service/*.rb')
- rdoc.rdoc_files.include('lib/action_web_service/api/*.rb')
- rdoc.rdoc_files.include('lib/action_web_service/client/*.rb')
- rdoc.rdoc_files.include('lib/action_web_service/container/*.rb')
- rdoc.rdoc_files.include('lib/action_web_service/dispatcher/*.rb')
- rdoc.rdoc_files.include('lib/action_web_service/protocol/*.rb')
- rdoc.rdoc_files.include('lib/action_web_service/support/*.rb')
-}
-
-
-# Create compressed packages
-spec = Gem::Specification.new do |s|
- s.platform = Gem::Platform::RUBY
- s.name = PKG_NAME
- s.summary = "Web service support for Action Pack."
- s.description = %q{Adds WSDL/SOAP and XML-RPC web service support to Action Pack}
- s.version = PKG_VERSION
-
- s.author = "Leon Breedt"
- s.email = "bitserf@gmail.com"
- s.rubyforge_project = "aws"
- s.homepage = "http://www.rubyonrails.org"
-
- s.add_dependency('actionpack', '= 1.13.5' + PKG_BUILD)
- s.add_dependency('activerecord', '= 1.15.5' + PKG_BUILD)
-
- s.has_rdoc = true
- s.requirements << 'none'
- s.require_path = 'lib'
- s.autorequire = 'action_web_service'
-
- s.files = [ "Rakefile", "setup.rb", "README", "TODO", "CHANGELOG", "MIT-LICENSE" ]
- s.files = s.files + Dir.glob( "examples/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
- s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
- s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
-end
-Rake::GemPackageTask.new(spec) do |p|
- p.gem_spec = spec
- p.need_tar = true
- p.need_zip = true
-end
-
-
-# Publish beta gem
-desc "Publish the API documentation"
-task :pgem => [:package] do
- Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
- `ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
-end
-
-# Publish documentation
-desc "Publish the API documentation"
-task :pdoc => [:rdoc] do
- Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/aws", "doc").upload
-end
-
-
-def each_source_file(*args)
- prefix, includes, excludes, open_file = args
- prefix ||= File.dirname(__FILE__)
- open_file = true if open_file.nil?
- includes ||= %w[lib\/action_web_service\.rb$ lib\/action_web_service\/.*\.rb$]
- excludes ||= %w[lib\/action_web_service\/vendor]
- Find.find(prefix) do |file_name|
- next if file_name =~ /\.svn/
- file_name.gsub!(/^\.\//, '')
- continue = false
- includes.each do |inc|
- if file_name.match(/#{inc}/)
- continue = true
- break
- end
- end
- next unless continue
- excludes.each do |exc|
- if file_name.match(/#{exc}/)
- continue = false
- break
- end
- end
- next unless continue
- if open_file
- File.open(file_name) do |f|
- yield file_name, f
- end
- else
- yield file_name
- end
- end
-end
-
-desc "Count lines of the AWS source code"
-task :lines do
- total_lines = total_loc = 0
- puts "Per File:"
- each_source_file do |file_name, f|
- file_lines = file_loc = 0
- while line = f.gets
- file_lines += 1
- next if line =~ /^\s*$/
- next if line =~ /^\s*#/
- file_loc += 1
- end
- puts " #{file_name}: Lines #{file_lines}, LOC #{file_loc}"
- total_lines += file_lines
- total_loc += file_loc
- end
- puts "Total:"
- puts " Lines #{total_lines}, LOC #{total_loc}"
-end
-
-desc "Publish the release files to RubyForge."
-task :release => [ :package ] do
- require 'rubyforge'
-
- packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
-
- rubyforge = RubyForge.new
- rubyforge.login
- rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
-end
\ No newline at end of file
diff --git a/vendor/rails/actionwebservice/TODO b/vendor/rails/actionwebservice/TODO
deleted file mode 100644
index 7c022c14..00000000
--- a/vendor/rails/actionwebservice/TODO
+++ /dev/null
@@ -1,32 +0,0 @@
-= Post-1.0
- - Document/Literal SOAP support
- - URL-based dispatching, URL identifies method
-
- - Add :rest dispatching mode, a.l.a. Backpack API. Clean up dispatching
- in general. Support vanilla XML-format as a "Rails" protocol?
- XML::Simple deserialization into params?
-
- web_service_dispatching_mode :rest
-
- def method1(params)
- end
-
- def method2(params)
- end
-
-
- /ws/method1
-
- /ws/method2
-
-
- - Allow locking down a controller to only accept messages for a particular
- protocol. This will allow us to generate fully conformant error messages
- in cases where we currently fudge it if we don't know the protocol.
-
- - Allow AWS user to participate in typecasting, so they can centralize
- workarounds for buggy input in one place
-
-= Refactoring
- - Don't have clean way to go from SOAP Class object to the xsd:NAME type
- string -- NaHi possibly looking at remedying this situation
diff --git a/vendor/rails/actionwebservice/examples/googlesearch/README b/vendor/rails/actionwebservice/examples/googlesearch/README
deleted file mode 100644
index 25ccbd23..00000000
--- a/vendor/rails/actionwebservice/examples/googlesearch/README
+++ /dev/null
@@ -1,143 +0,0 @@
-= Google Service example
-
-This example shows how one would implement an API like Google
-Search that uses lots of structured types.
-
-There are examples for "Direct" and "Delegated" dispatching
-modes.
-
-There is also an example for API definition file autoloading.
-
-
-= Running the examples
-
- 1. Add the files to an Action Web Service enabled Rails project.
-
- "Direct" example:
-
- * Copy direct/search_controller.rb to "app/controllers"
- in a Rails project.
- * Copy direct/google_search_api.rb to "app/apis"
- in a Rails project
-
- "Delegated" example:
-
- * Copy delegated/search_controller.rb to "app/controllers"
- in a Rails project.
- * Copy delegated/google_search_service.rb to "lib"
- in a Rails project.
-
- "Autoloading" example:
-
- * Copy autoloading/google_search_api.rb to "app/apis" (create the directory
- if it doesn't exist) in a Rails project.
-
- * Copy autoloading/google_search_controller.rb "app/controllers"
- in a Rails project.
-
-
- 2. Go to the WSDL url in a browser, and check that it looks correct.
-
- "Direct" and "Delegated" examples:
- http://url_to_project/search/wsdl
-
- "Autoloading" example:
- http://url_to_project/google_search/wsdl
-
- You can compare it to Google's hand-coded WSDL at http://api.google.com/GoogleSearch.wsdl
- and see how close (or not) the generated version is.
-
- Note that I used GoogleSearch as the canonical "best practice"
- interoperable example when implementing WSDL/SOAP support, which might
- explain extreme similarities :)
-
-
- 3. Test that it works with .NET (Mono in this example):
-
- $ wget WSDL_URL
- $ mv wsdl GoogleSearch.wsdl
- $ wsdl -out:GoogleSearch.cs GoogleSearch.wsdl
-
- Add these lines to the GoogleSearchService class body (be mindful of the
- wrapping):
-
- public static void Main(string[] args)
- {
- GoogleSearchResult result;
- GoogleSearchService service;
-
- service = new GoogleSearchService();
- result = service.doGoogleSearch("myApiKey", "my query", 10, 30, true, "restrict", false, "lr", "ie", "oe");
- System.Console.WriteLine("documentFiltering: {0}", result.documentFiltering);
- System.Console.WriteLine("searchComments: {0}", result.searchComments);
- System.Console.WriteLine("estimatedTotalResultsCount: {0}", result.estimatedTotalResultsCount);
- System.Console.WriteLine("estimateIsExact: {0}", result.estimateIsExact);
- System.Console.WriteLine("resultElements:");
- foreach (ResultElement element in result.resultElements) {
- System.Console.WriteLine("\tsummary: {0}", element.summary);
- System.Console.WriteLine("\tURL: {0}", element.URL);
- System.Console.WriteLine("\tsnippet: {0}", element.snippet);
- System.Console.WriteLine("\ttitle: {0}", element.title);
- System.Console.WriteLine("\tcachedSize: {0}", element.cachedSize);
- System.Console.WriteLine("\trelatedInformationPresent: {0}", element.relatedInformationPresent);
- System.Console.WriteLine("\thostName: {0}", element.hostName);
- System.Console.WriteLine("\tdirectoryCategory: {0}", element.directoryCategory.fullViewableName);
- System.Console.WriteLine("\tdirectoryTitle: {0}", element.directoryTitle);
- }
- System.Console.WriteLine("searchQuery: {0}", result.searchQuery);
- System.Console.WriteLine("startIndex: {0}", result.startIndex);
- System.Console.WriteLine("endIndex: {0}", result.endIndex);
- System.Console.WriteLine("searchTips: {0}", result.searchTips);
- System.Console.WriteLine("directoryCategories:");
- foreach (DirectoryCategory cat in result.directoryCategories) {
- System.Console.WriteLine("\t{0} ({1})", cat.fullViewableName, cat.specialEncoding);
- }
- System.Console.WriteLine("searchTime: {0}", result.searchTime);
- }
-
- Now compile and run:
-
- $ mcs -reference:System.Web.Services GoogleSearch.cs
- $ mono GoogleSearch.exe
-
-
- If you had the application running (on the same host you got
- the WSDL from), you should see something like this:
-
-
- documentFiltering: True
- searchComments:
- estimatedTotalResultsCount: 322000
- estimateIsExact: False
- resultElements:
- summary: ONlamp.com: Rolling with Ruby on Rails
- URL: http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html
- snippet: Curt Hibbs shows off Ruby on Rails by building a simple ...
- title: Teh Railz0r
- cachedSize: Almost no lines of code!
- relatedInformationPresent: True
- hostName: rubyonrails.com
- directoryCategory: Web Development
- directoryTitle:
- searchQuery: http://www.google.com/search?q=ruby+on+rails
- startIndex: 10
- endIndex: 40
- searchTips: "on" is a very common word and was not included in your search [details]
- directoryCategories:
- Web Development (UTF-8)
- Programming (US-ASCII)
- searchTime: 1E-06
-
-
- Also, if an API method throws an exception, it will be sent back to the
- caller in the protocol's exception format, so they should get an exception
- thrown on their side with a meaningful error message.
-
- If you don't like this behaviour, you can do:
-
- class MyController < ActionController::Base
- web_service_exception_reporting false
- end
-
- 4. Crack open a beer. Publishing APIs for working with the same model as
- your Rails web app should be easy from now on :)
diff --git a/vendor/rails/actionwebservice/examples/googlesearch/autoloading/google_search_api.rb b/vendor/rails/actionwebservice/examples/googlesearch/autoloading/google_search_api.rb
deleted file mode 100644
index ed69fed7..00000000
--- a/vendor/rails/actionwebservice/examples/googlesearch/autoloading/google_search_api.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-class DirectoryCategory < ActionWebService::Struct
- member :fullViewableName, :string
- member :specialEncoding, :string
-end
-
-class ResultElement < ActionWebService::Struct
- member :summary, :string
- member :URL, :string
- member :snippet, :string
- member :title, :string
- member :cachedSize, :string
- member :relatedInformationPresent, :bool
- member :hostName, :string
- member :directoryCategory, DirectoryCategory
- member :directoryTitle, :string
-end
-
-class GoogleSearchResult < ActionWebService::Struct
- member :documentFiltering, :bool
- member :searchComments, :string
- member :estimatedTotalResultsCount, :int
- member :estimateIsExact, :bool
- member :resultElements, [ResultElement]
- member :searchQuery, :string
- member :startIndex, :int
- member :endIndex, :int
- member :searchTips, :string
- member :directoryCategories, [DirectoryCategory]
- member :searchTime, :float
-end
-
-class GoogleSearchAPI < ActionWebService::API::Base
- inflect_names false
-
- api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
- api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
-
- api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
- {:key=>:string},
- {:q=>:string},
- {:start=>:int},
- {:maxResults=>:int},
- {:filter=>:bool},
- {:restrict=>:string},
- {:safeSearch=>:bool},
- {:lr=>:string},
- {:ie=>:string},
- {:oe=>:string}
- ]
-end
diff --git a/vendor/rails/actionwebservice/examples/googlesearch/autoloading/google_search_controller.rb b/vendor/rails/actionwebservice/examples/googlesearch/autoloading/google_search_controller.rb
deleted file mode 100644
index c62e869d..00000000
--- a/vendor/rails/actionwebservice/examples/googlesearch/autoloading/google_search_controller.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-class GoogleSearchController < ApplicationController
- wsdl_service_name 'GoogleSearch'
-
- def doGetCachedPage
- "i am a cached page. my key was %s, url was %s" % [@params['key'], @params['url']]
- end
-
- def doSpellingSuggestion
- "%s: Did you mean '%s'?" % [@params['key'], @params['phrase']]
- end
-
- def doGoogleSearch
- resultElement = ResultElement.new
- resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
- resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
- resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
- "almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
- resultElement.title = "Teh Railz0r"
- resultElement.cachedSize = "Almost no lines of code!"
- resultElement.relatedInformationPresent = true
- resultElement.hostName = "rubyonrails.com"
- resultElement.directoryCategory = category("Web Development", "UTF-8")
-
- result = GoogleSearchResult.new
- result.documentFiltering = @params['filter']
- result.searchComments = ""
- result.estimatedTotalResultsCount = 322000
- result.estimateIsExact = false
- result.resultElements = [resultElement]
- result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
- result.startIndex = @params['start']
- result.endIndex = @params['start'] + @params['maxResults']
- result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
- result.searchTime = 0.000001
-
- # For Mono, we have to clone objects if they're referenced by more than one place, otherwise
- # the Ruby SOAP collapses them into one instance and uses references all over the
- # place, confusing Mono.
- #
- # This has recently been fixed:
- # http://bugzilla.ximian.com/show_bug.cgi?id=72265
- result.directoryCategories = [
- category("Web Development", "UTF-8"),
- category("Programming", "US-ASCII"),
- ]
-
- result
- end
-
- private
- def category(name, encoding)
- cat = DirectoryCategory.new
- cat.fullViewableName = name.dup
- cat.specialEncoding = encoding.dup
- cat
- end
-end
diff --git a/vendor/rails/actionwebservice/examples/googlesearch/delegated/google_search_service.rb b/vendor/rails/actionwebservice/examples/googlesearch/delegated/google_search_service.rb
deleted file mode 100644
index ade354d8..00000000
--- a/vendor/rails/actionwebservice/examples/googlesearch/delegated/google_search_service.rb
+++ /dev/null
@@ -1,108 +0,0 @@
-class DirectoryCategory < ActionWebService::Struct
- member :fullViewableName, :string
- member :specialEncoding, :string
-end
-
-class ResultElement < ActionWebService::Struct
- member :summary, :string
- member :URL, :string
- member :snippet, :string
- member :title, :string
- member :cachedSize, :string
- member :relatedInformationPresent, :bool
- member :hostName, :string
- member :directoryCategory, DirectoryCategory
- member :directoryTitle, :string
-end
-
-class GoogleSearchResult < ActionWebService::Struct
- member :documentFiltering, :bool
- member :searchComments, :string
- member :estimatedTotalResultsCount, :int
- member :estimateIsExact, :bool
- member :resultElements, [ResultElement]
- member :searchQuery, :string
- member :startIndex, :int
- member :endIndex, :int
- member :searchTips, :string
- member :directoryCategories, [DirectoryCategory]
- member :searchTime, :float
-end
-
-class GoogleSearchAPI < ActionWebService::API::Base
- inflect_names false
-
- api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
- api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
-
- api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
- {:key=>:string},
- {:q=>:string},
- {:start=>:int},
- {:maxResults=>:int},
- {:filter=>:bool},
- {:restrict=>:string},
- {:safeSearch=>:bool},
- {:lr=>:string},
- {:ie=>:string},
- {:oe=>:string}
- ]
-end
-
-class GoogleSearchService < ActionWebService::Base
- web_service_api GoogleSearchAPI
-
- def doGetCachedPage(key, url)
- "i am a cached page"
- end
-
- def doSpellingSuggestion(key, phrase)
- "Did you mean 'teh'?"
- end
-
- def doGoogleSearch(key, q, start, maxResults, filter, restrict, safeSearch, lr, ie, oe)
- resultElement = ResultElement.new
- resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
- resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
- resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
- "almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
- resultElement.title = "Teh Railz0r"
- resultElement.cachedSize = "Almost no lines of code!"
- resultElement.relatedInformationPresent = true
- resultElement.hostName = "rubyonrails.com"
- resultElement.directoryCategory = category("Web Development", "UTF-8")
-
- result = GoogleSearchResult.new
- result.documentFiltering = filter
- result.searchComments = ""
- result.estimatedTotalResultsCount = 322000
- result.estimateIsExact = false
- result.resultElements = [resultElement]
- result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
- result.startIndex = start
- result.endIndex = start + maxResults
- result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
- result.searchTime = 0.000001
-
- # For Mono, we have to clone objects if they're referenced by more than one place, otherwise
- # the Ruby SOAP collapses them into one instance and uses references all over the
- # place, confusing Mono.
- #
- # This has recently been fixed:
- # http://bugzilla.ximian.com/show_bug.cgi?id=72265
- result.directoryCategories = [
- category("Web Development", "UTF-8"),
- category("Programming", "US-ASCII"),
- ]
-
- result
- end
-
- private
- def category(name, encoding)
- cat = DirectoryCategory.new
- cat.fullViewableName = name.dup
- cat.specialEncoding = encoding.dup
- cat
- end
-end
diff --git a/vendor/rails/actionwebservice/examples/googlesearch/delegated/search_controller.rb b/vendor/rails/actionwebservice/examples/googlesearch/delegated/search_controller.rb
deleted file mode 100644
index 6525921b..00000000
--- a/vendor/rails/actionwebservice/examples/googlesearch/delegated/search_controller.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require 'google_search_service'
-
-class SearchController < ApplicationController
- wsdl_service_name 'GoogleSearch'
- web_service_dispatching_mode :delegated
- web_service :beta3, GoogleSearchService.new
-end
diff --git a/vendor/rails/actionwebservice/examples/googlesearch/direct/google_search_api.rb b/vendor/rails/actionwebservice/examples/googlesearch/direct/google_search_api.rb
deleted file mode 100644
index ed69fed7..00000000
--- a/vendor/rails/actionwebservice/examples/googlesearch/direct/google_search_api.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-class DirectoryCategory < ActionWebService::Struct
- member :fullViewableName, :string
- member :specialEncoding, :string
-end
-
-class ResultElement < ActionWebService::Struct
- member :summary, :string
- member :URL, :string
- member :snippet, :string
- member :title, :string
- member :cachedSize, :string
- member :relatedInformationPresent, :bool
- member :hostName, :string
- member :directoryCategory, DirectoryCategory
- member :directoryTitle, :string
-end
-
-class GoogleSearchResult < ActionWebService::Struct
- member :documentFiltering, :bool
- member :searchComments, :string
- member :estimatedTotalResultsCount, :int
- member :estimateIsExact, :bool
- member :resultElements, [ResultElement]
- member :searchQuery, :string
- member :startIndex, :int
- member :endIndex, :int
- member :searchTips, :string
- member :directoryCategories, [DirectoryCategory]
- member :searchTime, :float
-end
-
-class GoogleSearchAPI < ActionWebService::API::Base
- inflect_names false
-
- api_method :doGetCachedPage, :returns => [:string], :expects => [{:key=>:string}, {:url=>:string}]
- api_method :doGetSpellingSuggestion, :returns => [:string], :expects => [{:key=>:string}, {:phrase=>:string}]
-
- api_method :doGoogleSearch, :returns => [GoogleSearchResult], :expects => [
- {:key=>:string},
- {:q=>:string},
- {:start=>:int},
- {:maxResults=>:int},
- {:filter=>:bool},
- {:restrict=>:string},
- {:safeSearch=>:bool},
- {:lr=>:string},
- {:ie=>:string},
- {:oe=>:string}
- ]
-end
diff --git a/vendor/rails/actionwebservice/examples/googlesearch/direct/search_controller.rb b/vendor/rails/actionwebservice/examples/googlesearch/direct/search_controller.rb
deleted file mode 100644
index 7c69f022..00000000
--- a/vendor/rails/actionwebservice/examples/googlesearch/direct/search_controller.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-class SearchController < ApplicationController
- web_service_api :google_search
- wsdl_service_name 'GoogleSearch'
-
- def doGetCachedPage
- "i am a cached page. my key was %s, url was %s" % [@params['key'], @params['url']]
- end
-
- def doSpellingSuggestion
- "%s: Did you mean '%s'?" % [@params['key'], @params['phrase']]
- end
-
- def doGoogleSearch
- resultElement = ResultElement.new
- resultElement.summary = "ONlamp.com: Rolling with Ruby on Rails"
- resultElement.URL = "http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html"
- resultElement.snippet = "Curt Hibbs shows off Ruby on Rails by building a simple application that requires " +
- "almost no Ruby experience. ... Rolling with Ruby on Rails. ..."
- resultElement.title = "Teh Railz0r"
- resultElement.cachedSize = "Almost no lines of code!"
- resultElement.relatedInformationPresent = true
- resultElement.hostName = "rubyonrails.com"
- resultElement.directoryCategory = category("Web Development", "UTF-8")
-
- result = GoogleSearchResult.new
- result.documentFiltering = @params['filter']
- result.searchComments = ""
- result.estimatedTotalResultsCount = 322000
- result.estimateIsExact = false
- result.resultElements = [resultElement]
- result.searchQuery = "http://www.google.com/search?q=ruby+on+rails"
- result.startIndex = @params['start']
- result.endIndex = @params['start'] + @params['maxResults']
- result.searchTips = "\"on\" is a very common word and was not included in your search [details]"
- result.searchTime = 0.000001
-
- # For Mono, we have to clone objects if they're referenced by more than one place, otherwise
- # the Ruby SOAP collapses them into one instance and uses references all over the
- # place, confusing Mono.
- #
- # This has recently been fixed:
- # http://bugzilla.ximian.com/show_bug.cgi?id=72265
- result.directoryCategories = [
- category("Web Development", "UTF-8"),
- category("Programming", "US-ASCII"),
- ]
-
- result
- end
-
- private
- def category(name, encoding)
- cat = DirectoryCategory.new
- cat.fullViewableName = name.dup
- cat.specialEncoding = encoding.dup
- cat
- end
-end
diff --git a/vendor/rails/actionwebservice/examples/metaWeblog/README b/vendor/rails/actionwebservice/examples/metaWeblog/README
deleted file mode 100644
index f66f5677..00000000
--- a/vendor/rails/actionwebservice/examples/metaWeblog/README
+++ /dev/null
@@ -1,17 +0,0 @@
-= metaWeblog example
-
-This example shows how one might begin to go about adding metaWeblog
-(http://www.xmlrpc.com/metaWeblogApi) API support to a Rails-based
-blogging application.
-
-The example APIs are more verbose than you may want to make them, for documentation
-reasons.
-
-= Running
-
- 1. Copy the "apis" directory and its files into "app" in a Rails project.
-
- 2. Copy the "controllers" directory and its files into "app" in a Rails project
-
- 3. Fire up a desktop blogging application (such as w.bloggar, MarsEdit, or BloGTK),
- point it at http://localhost:3000/xmlrpc/api, and try creating or editing blog posts.
diff --git a/vendor/rails/actionwebservice/examples/metaWeblog/apis/blogger_api.rb b/vendor/rails/actionwebservice/examples/metaWeblog/apis/blogger_api.rb
deleted file mode 100644
index 9f85a239..00000000
--- a/vendor/rails/actionwebservice/examples/metaWeblog/apis/blogger_api.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-#
-# see the blogger API spec at http://www.blogger.com/developers/api/1_docs/
-# note that the method signatures are subtly different to metaWeblog, they
-# are not identical. take care to ensure you handle the different semantics
-# properly if you want to support blogger API too, to get maximum compatibility.
-#
-
-module Blog
- class Blog < ActionWebService::Struct
- member :url, :string
- member :blogid, :string
- member :blogName, :string
- end
-
- class User < ActionWebService::Struct
- member :nickname, :string
- member :userid, :string
- member :url, :string
- member :email, :string
- member :lastname, :string
- member :firstname, :string
- end
-end
-
-#
-# blogger
-#
-class BloggerAPI < ActionWebService::API::Base
- inflect_names false
-
- api_method :newPost, :returns => [:string], :expects => [
- {:appkey=>:string},
- {:blogid=>:string},
- {:username=>:string},
- {:password=>:string},
- {:content=>:string},
- {:publish=>:bool}
- ]
-
- api_method :editPost, :returns => [:bool], :expects => [
- {:appkey=>:string},
- {:postid=>:string},
- {:username=>:string},
- {:password=>:string},
- {:content=>:string},
- {:publish=>:bool}
- ]
-
- api_method :getUsersBlogs, :returns => [[Blog::Blog]], :expects => [
- {:appkey=>:string},
- {:username=>:string},
- {:password=>:string}
- ]
-
- api_method :getUserInfo, :returns => [Blog::User], :expects => [
- {:appkey=>:string},
- {:username=>:string},
- {:password=>:string}
- ]
-end
diff --git a/vendor/rails/actionwebservice/examples/metaWeblog/apis/blogger_service.rb b/vendor/rails/actionwebservice/examples/metaWeblog/apis/blogger_service.rb
deleted file mode 100644
index b79b53e6..00000000
--- a/vendor/rails/actionwebservice/examples/metaWeblog/apis/blogger_service.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'blogger_api'
-
-class BloggerService < ActionWebService::Base
- web_service_api BloggerAPI
-
- def initialize
- @postid = 0
- end
-
- def newPost(key, id, user, pw, content, publish)
- $stderr.puts "id=#{id} user=#{user} pw=#{pw}, content=#{content.inspect} [#{publish}]"
- (@postid += 1).to_s
- end
-
- def editPost(key, post_id, user, pw, content, publish)
- $stderr.puts "id=#{post_id} user=#{user} pw=#{pw} content=#{content.inspect} [#{publish}]"
- true
- end
-
- def getUsersBlogs(key, user, pw)
- $stderr.puts "getting blogs for #{user}"
- blog = Blog::Blog.new(
- :url =>'http://blog',
- :blogid => 'myblog',
- :blogName => 'My Blog'
- )
- [blog]
- end
-
- def getUserInfo(key, user, pw)
- $stderr.puts "getting user info for #{user}"
- Blog::User.new(:nickname => 'user', :email => 'user@test.com')
- end
-end
diff --git a/vendor/rails/actionwebservice/examples/metaWeblog/apis/meta_weblog_api.rb b/vendor/rails/actionwebservice/examples/metaWeblog/apis/meta_weblog_api.rb
deleted file mode 100644
index adef12a2..00000000
--- a/vendor/rails/actionwebservice/examples/metaWeblog/apis/meta_weblog_api.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-#
-# here lie structures, cousins of those on http://www.xmlrpc.com/metaWeblog
-# but they don't necessarily the real world reflect
-# so if you do, find that your client complains:
-# please tell, of problems you suffered through
-#
-
-module Blog
- class Post < ActionWebService::Struct
- member :title, :string
- member :link, :string
- member :description, :string
- member :author, :string
- member :category, :string
- member :comments, :string
- member :guid, :string
- member :pubDate, :string
- end
-
- class Category < ActionWebService::Struct
- member :description, :string
- member :htmlUrl, :string
- member :rssUrl, :string
- end
-end
-
-#
-# metaWeblog
-#
-class MetaWeblogAPI < ActionWebService::API::Base
- inflect_names false
-
- api_method :newPost, :returns => [:string], :expects => [
- {:blogid=>:string},
- {:username=>:string},
- {:password=>:string},
- {:struct=>Blog::Post},
- {:publish=>:bool}
- ]
-
- api_method :editPost, :returns => [:bool], :expects => [
- {:postid=>:string},
- {:username=>:string},
- {:password=>:string},
- {:struct=>Blog::Post},
- {:publish=>:bool},
- ]
-
- api_method :getPost, :returns => [Blog::Post], :expects => [
- {:postid=>:string},
- {:username=>:string},
- {:password=>:string},
- ]
-
- api_method :getCategories, :returns => [[Blog::Category]], :expects => [
- {:blogid=>:string},
- {:username=>:string},
- {:password=>:string},
- ]
-
- api_method :getRecentPosts, :returns => [[Blog::Post]], :expects => [
- {:blogid=>:string},
- {:username=>:string},
- {:password=>:string},
- {:numberOfPosts=>:int},
- ]
-end
diff --git a/vendor/rails/actionwebservice/examples/metaWeblog/apis/meta_weblog_service.rb b/vendor/rails/actionwebservice/examples/metaWeblog/apis/meta_weblog_service.rb
deleted file mode 100644
index 9c66558f..00000000
--- a/vendor/rails/actionwebservice/examples/metaWeblog/apis/meta_weblog_service.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require 'meta_weblog_api'
-
-class MetaWeblogService < ActionWebService::Base
- web_service_api MetaWeblogAPI
-
- def initialize
- @postid = 0
- end
-
- def newPost(id, user, pw, struct, publish)
- $stderr.puts "id=#{id} user=#{user} pw=#{pw}, struct=#{struct.inspect} [#{publish}]"
- (@postid += 1).to_s
- end
-
- def editPost(post_id, user, pw, struct, publish)
- $stderr.puts "id=#{post_id} user=#{user} pw=#{pw} struct=#{struct.inspect} [#{publish}]"
- true
- end
-
- def getPost(post_id, user, pw)
- $stderr.puts "get post #{post_id}"
- Blog::Post.new(:title => 'hello world', :description => 'first post!')
- end
-
- def getCategories(id, user, pw)
- $stderr.puts "categories for #{user}"
- cat = Blog::Category.new(
- :description => 'Tech',
- :htmlUrl => 'http://blog/tech',
- :rssUrl => 'http://blog/tech.rss')
- [cat]
- end
-
- def getRecentPosts(id, user, pw, num)
- $stderr.puts "recent #{num} posts for #{user} on blog #{id}"
- post1 = Blog::Post.new(
- :title => 'first post!',
- :link => 'http://blog.xeraph.org/testOne.html',
- :description => 'this is the first post'
- )
- post2 = Blog::Post.new(
- :title => 'second post!',
- :link => 'http://blog.xeraph.org/testTwo.html',
- :description => 'this is the second post'
- )
- [post1, post2]
- end
-end
diff --git a/vendor/rails/actionwebservice/examples/metaWeblog/controllers/xmlrpc_controller.rb b/vendor/rails/actionwebservice/examples/metaWeblog/controllers/xmlrpc_controller.rb
deleted file mode 100644
index 7486402d..00000000
--- a/vendor/rails/actionwebservice/examples/metaWeblog/controllers/xmlrpc_controller.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# example controller implementing both blogger and metaWeblog APIs
-# in a way that should be compatible with clients supporting both/either.
-#
-# test by pointing your client at http://URL/xmlrpc/api
-#
-
-require 'meta_weblog_service'
-require 'blogger_service'
-
-class XmlrpcController < ApplicationController
- web_service_dispatching_mode :layered
-
- web_service :metaWeblog, MetaWeblogService.new
- web_service :blogger, BloggerService.new
-end
diff --git a/vendor/rails/actionwebservice/install.rb b/vendor/rails/actionwebservice/install.rb
deleted file mode 100644
index e2a90480..00000000
--- a/vendor/rails/actionwebservice/install.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require 'rbconfig'
-require 'find'
-require 'ftools'
-
-include Config
-
-# this was adapted from rdoc's install.rb by way of Log4r
-
-$sitedir = CONFIG["sitelibdir"]
-unless $sitedir
- version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
- $libdir = File.join(CONFIG["libdir"], "ruby", version)
- $sitedir = $:.find {|x| x =~ /site_ruby/ }
- if !$sitedir
- $sitedir = File.join($libdir, "site_ruby")
- elsif $sitedir !~ Regexp.quote(version)
- $sitedir = File.join($sitedir, version)
- end
-end
-
-# the acual gruntwork
-Dir.chdir("lib")
-
-Find.find("action_web_service", "action_web_service.rb") { |f|
- if f[-3..-1] == ".rb"
- File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
- else
- File::makedirs(File.join($sitedir, *f.split(/\//)))
- end
-}
diff --git a/vendor/rails/actionwebservice/lib/action_web_service.rb b/vendor/rails/actionwebservice/lib/action_web_service.rb
deleted file mode 100644
index 0632dd1e..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-#--
-# Copyright (C) 2005 Leon Breedt
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#++
-
-begin
- require 'active_support'
- require 'action_controller'
- require 'active_record'
-rescue LoadError
- require 'rubygems'
- gem 'activesupport', '>= 1.0.2'
- gem 'actionpack', '>= 1.6.0'
- gem 'activerecord', '>= 1.9.0'
-end
-
-$:.unshift(File.dirname(__FILE__) + "/action_web_service/vendor/")
-
-require 'action_web_service/support/class_inheritable_options'
-require 'action_web_service/support/signature_types'
-require 'action_web_service/base'
-require 'action_web_service/client'
-require 'action_web_service/invocation'
-require 'action_web_service/api'
-require 'action_web_service/casting'
-require 'action_web_service/struct'
-require 'action_web_service/container'
-require 'action_web_service/protocol'
-require 'action_web_service/dispatcher'
-require 'action_web_service/scaffolding'
-
-ActionWebService::Base.class_eval do
- include ActionWebService::Container::Direct
- include ActionWebService::Invocation
-end
-
-ActionController::Base.class_eval do
- include ActionWebService::Protocol::Discovery
- include ActionWebService::Protocol::Soap
- include ActionWebService::Protocol::XmlRpc
- include ActionWebService::Container::Direct
- include ActionWebService::Container::Delegated
- include ActionWebService::Container::ActionController
- include ActionWebService::Invocation
- include ActionWebService::Dispatcher
- include ActionWebService::Dispatcher::ActionController
- include ActionWebService::Scaffolding
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/api.rb b/vendor/rails/actionwebservice/lib/action_web_service/api.rb
deleted file mode 100644
index 73fb886e..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/api.rb
+++ /dev/null
@@ -1,249 +0,0 @@
-module ActionWebService # :nodoc:
- module API # :nodoc:
- # A web service API class specifies the methods that will be available for
- # invocation for an API. It also contains metadata such as the method type
- # signature hints.
- #
- # It is not intended to be instantiated.
- #
- # It is attached to web service implementation classes like
- # ActionWebService::Base and ActionController::Base derivatives by using
- # container.web_service_api , where container is an
- # ActionController::Base or a ActionWebService::Base.
- #
- # See ActionWebService::Container::Direct::ClassMethods for an example
- # of use.
- class Base
- # Action WebService API subclasses should be reloaded by the dispatcher in Rails
- # when Dependencies.mechanism = :load.
- include Reloadable::Deprecated
-
- # Whether to transform the public API method names into camel-cased names
- class_inheritable_option :inflect_names, true
-
- # Whether to allow ActiveRecord::Base models in :expects .
- # The default is +false+; you should be aware of the security implications
- # of allowing this, and ensure that you don't allow remote callers to
- # easily overwrite data they should not have access to.
- class_inheritable_option :allow_active_record_expects, false
-
- # If present, the name of a method to call when the remote caller
- # tried to call a nonexistent method. Semantically equivalent to
- # +method_missing+.
- class_inheritable_option :default_api_method
-
- # Disallow instantiation
- private_class_method :new, :allocate
-
- class << self
- include ActionWebService::SignatureTypes
-
- # API methods have a +name+, which must be the Ruby method name to use when
- # performing the invocation on the web service object.
- #
- # The signatures for the method input parameters and return value can
- # by specified in +options+.
- #
- # A signature is an array of one or more parameter specifiers.
- # A parameter specifier can be one of the following:
- #
- # * A symbol or string representing one of the Action Web Service base types.
- # See ActionWebService::SignatureTypes for a canonical list of the base types.
- # * The Class object of the parameter type
- # * A single-element Array containing one of the two preceding items. This
- # will cause Action Web Service to treat the parameter at that position
- # as an array containing only values of the given type.
- # * A Hash containing as key the name of the parameter, and as value
- # one of the three preceding items
- #
- # If no method input parameter or method return value signatures are given,
- # the method is assumed to take no parameters and/or return no values of
- # interest, and any values that are received by the server will be
- # discarded and ignored.
- #
- # Valid options:
- # [:expects ] Signature for the method input parameters
- # [:returns ] Signature for the method return value
- # [:expects_and_returns ] Signature for both input parameters and return value
- def api_method(name, options={})
- unless options.is_a?(Hash)
- raise(ActionWebServiceError, "Expected a Hash for options")
- end
- validate_options([:expects, :returns, :expects_and_returns], options.keys)
- if options[:expects_and_returns]
- expects = options[:expects_and_returns]
- returns = options[:expects_and_returns]
- else
- expects = options[:expects]
- returns = options[:returns]
- end
- expects = canonical_signature(expects)
- returns = canonical_signature(returns)
- if expects
- expects.each do |type|
- type = type.element_type if type.is_a?(ArrayType)
- if type.type_class.ancestors.include?(ActiveRecord::Base) && !allow_active_record_expects
- raise(ActionWebServiceError, "ActiveRecord model classes not allowed in :expects")
- end
- end
- end
- name = name.to_sym
- public_name = public_api_method_name(name)
- method = Method.new(name, public_name, expects, returns)
- write_inheritable_hash("api_methods", name => method)
- write_inheritable_hash("api_public_method_names", public_name => name)
- end
-
- # Whether the given method name is a service method on this API
- def has_api_method?(name)
- api_methods.has_key?(name)
- end
-
- # Whether the given public method name has a corresponding service method
- # on this API
- def has_public_api_method?(public_name)
- api_public_method_names.has_key?(public_name)
- end
-
- # The corresponding public method name for the given service method name
- def public_api_method_name(name)
- if inflect_names
- name.to_s.camelize
- else
- name.to_s
- end
- end
-
- # The corresponding service method name for the given public method name
- def api_method_name(public_name)
- api_public_method_names[public_name]
- end
-
- # A Hash containing all service methods on this API, and their
- # associated metadata.
- def api_methods
- read_inheritable_attribute("api_methods") || {}
- end
-
- # The Method instance for the given public API method name, if any
- def public_api_method_instance(public_method_name)
- api_method_instance(api_method_name(public_method_name))
- end
-
- # The Method instance for the given API method name, if any
- def api_method_instance(method_name)
- api_methods[method_name]
- end
-
- # The Method instance for the default API method, if any
- def default_api_method_instance
- return nil unless name = default_api_method
- instance = read_inheritable_attribute("default_api_method_instance")
- if instance && instance.name == name
- return instance
- end
- instance = Method.new(name, public_api_method_name(name), nil, nil)
- write_inheritable_attribute("default_api_method_instance", instance)
- instance
- end
-
- private
- def api_public_method_names
- read_inheritable_attribute("api_public_method_names") || {}
- end
-
- def validate_options(valid_option_keys, supplied_option_keys)
- unknown_option_keys = supplied_option_keys - valid_option_keys
- unless unknown_option_keys.empty?
- raise(ActionWebServiceError, "Unknown options: #{unknown_option_keys}")
- end
- end
- end
- end
-
- # Represents an API method and its associated metadata, and provides functionality
- # to assist in commonly performed API method tasks.
- class Method
- attr :name
- attr :public_name
- attr :expects
- attr :returns
-
- def initialize(name, public_name, expects, returns)
- @name = name
- @public_name = public_name
- @expects = expects
- @returns = returns
- @caster = ActionWebService::Casting::BaseCaster.new(self)
- end
-
- # The list of parameter names for this method
- def param_names
- return [] unless @expects
- @expects.map{ |type| type.name }
- end
-
- # Casts a set of Ruby values into the expected Ruby values
- def cast_expects(params)
- @caster.cast_expects(params)
- end
-
- # Cast a Ruby return value into the expected Ruby value
- def cast_returns(return_value)
- @caster.cast_returns(return_value)
- end
-
- # Returns the index of the first expected parameter
- # with the given name
- def expects_index_of(param_name)
- return -1 if @expects.nil?
- (0..(@expects.length-1)).each do |i|
- return i if @expects[i].name.to_s == param_name.to_s
- end
- -1
- end
-
- # Returns a hash keyed by parameter name for the given
- # parameter list
- def expects_to_hash(params)
- return {} if @expects.nil?
- h = {}
- @expects.zip(params){ |type, param| h[type.name] = param }
- h
- end
-
- # Backwards compatibility with previous API
- def [](sig_type)
- case sig_type
- when :expects
- @expects.map{|x| compat_signature_entry(x)}
- when :returns
- @returns.map{|x| compat_signature_entry(x)}
- end
- end
-
- # String representation of this method
- def to_s
- fqn = ""
- fqn << (@returns ? (@returns[0].human_name(false) + " ") : "void ")
- fqn << "#{@public_name}("
- fqn << @expects.map{ |p| p.human_name }.join(", ") if @expects
- fqn << ")"
- fqn
- end
-
- private
- def compat_signature_entry(entry)
- if entry.array?
- [compat_signature_entry(entry.element_type)]
- else
- if entry.spec.is_a?(Hash)
- {entry.spec.keys.first => entry.type_class}
- else
- entry.type_class
- end
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/base.rb b/vendor/rails/actionwebservice/lib/action_web_service/base.rb
deleted file mode 100644
index b409377b..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/base.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-module ActionWebService # :nodoc:
- class ActionWebServiceError < StandardError # :nodoc:
- end
-
- # An Action Web Service object implements a specified API.
- #
- # Used by controllers operating in _Delegated_ dispatching mode.
- #
- # ==== Example
- #
- # class PersonService < ActionWebService::Base
- # web_service_api PersonAPI
- #
- # def find_person(criteria)
- # Person.find(:all) [...]
- # end
- #
- # def delete_person(id)
- # Person.find_by_id(id).destroy
- # end
- # end
- #
- # class PersonAPI < ActionWebService::API::Base
- # api_method :find_person, :expects => [SearchCriteria], :returns => [[Person]]
- # api_method :delete_person, :expects => [:int]
- # end
- #
- # class SearchCriteria < ActionWebService::Struct
- # member :firstname, :string
- # member :lastname, :string
- # member :email, :string
- # end
- class Base
- # Action WebService subclasses should be reloaded by the dispatcher in Rails
- # when Dependencies.mechanism = :load.
- include Reloadable::Deprecated
-
- # Whether to report exceptions back to the caller in the protocol's exception
- # format
- class_inheritable_option :web_service_exception_reporting, true
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/casting.rb b/vendor/rails/actionwebservice/lib/action_web_service/casting.rb
deleted file mode 100644
index 71cdf4e0..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/casting.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-require 'time'
-require 'date'
-require 'xmlrpc/datetime'
-
-module ActionWebService # :nodoc:
- module Casting # :nodoc:
- class CastingError < ActionWebServiceError # :nodoc:
- end
-
- # Performs casting of arbitrary values into the correct types for the signature
- class BaseCaster # :nodoc:
- def initialize(api_method)
- @api_method = api_method
- end
-
- # Coerces the parameters in +params+ (an Enumerable) into the types
- # this method expects
- def cast_expects(params)
- self.class.cast_expects(@api_method, params)
- end
-
- # Coerces the given +return_value+ into the type returned by this
- # method
- def cast_returns(return_value)
- self.class.cast_returns(@api_method, return_value)
- end
-
- class << self
- include ActionWebService::SignatureTypes
-
- def cast_expects(api_method, params) # :nodoc:
- return [] if api_method.expects.nil?
- api_method.expects.zip(params).map{ |type, param| cast(param, type) }
- end
-
- def cast_returns(api_method, return_value) # :nodoc:
- return nil if api_method.returns.nil?
- cast(return_value, api_method.returns[0])
- end
-
- def cast(value, signature_type) # :nodoc:
- return value if signature_type.nil? # signature.length != params.length
- return nil if value.nil?
- # XMLRPC protocol doesn't support nil values. It uses false instead.
- # It should never happen for SOAP.
- if signature_type.structured? && value.equal?(false)
- return nil
- end
- unless signature_type.array? || signature_type.structured?
- return value if canonical_type(value.class) == signature_type.type
- end
- if signature_type.array?
- unless value.respond_to?(:entries) && !value.is_a?(String)
- raise CastingError, "Don't know how to cast #{value.class} into #{signature_type.type.inspect}"
- end
- value.entries.map do |entry|
- cast(entry, signature_type.element_type)
- end
- elsif signature_type.structured?
- cast_to_structured_type(value, signature_type)
- elsif !signature_type.custom?
- cast_base_type(value, signature_type)
- end
- end
-
- def cast_base_type(value, signature_type) # :nodoc:
- # This is a work-around for the fact that XML-RPC special-cases DateTime values into its own DateTime type
- # in order to support iso8601 dates. This doesn't work too well for us, so we'll convert it into a Time,
- # with the caveat that we won't be able to handle pre-1970 dates that are sent to us.
- #
- # See http://dev.rubyonrails.com/ticket/2516
- value = value.to_time if value.is_a?(XMLRPC::DateTime)
-
- case signature_type.type
- when :int
- Integer(value)
- when :string
- value.to_s
- when :base64
- if value.is_a?(ActionWebService::Base64)
- value
- else
- ActionWebService::Base64.new(value.to_s)
- end
- when :bool
- return false if value.nil?
- return value if value == true || value == false
- case value.to_s.downcase
- when '1', 'true', 'y', 'yes'
- true
- when '0', 'false', 'n', 'no'
- false
- else
- raise CastingError, "Don't know how to cast #{value.class} into Boolean"
- end
- when :float
- Float(value)
- when :time
- value = "%s/%s/%s %s:%s:%s" % value.values_at(*%w[2 3 1 4 5 6]) if value.kind_of?(Hash)
- value.kind_of?(Time) ? value : Time.parse(value.to_s)
- when :date
- value = "%s/%s/%s" % value.values_at(*%w[2 3 1]) if value.kind_of?(Hash)
- value.kind_of?(Date) ? value : Date.parse(value.to_s)
- when :datetime
- value = "%s/%s/%s %s:%s:%s" % value.values_at(*%w[2 3 1 4 5 6]) if value.kind_of?(Hash)
- value.kind_of?(DateTime) ? value : DateTime.parse(value.to_s)
- end
- end
-
- def cast_to_structured_type(value, signature_type) # :nodoc:
- obj = nil
- obj = value if canonical_type(value.class) == canonical_type(signature_type.type)
- obj ||= signature_type.type_class.new
- if value.respond_to?(:each_pair)
- klass = signature_type.type_class
- value.each_pair do |name, val|
- type = klass.respond_to?(:member_type) ? klass.member_type(name) : nil
- val = cast(val, type) if type
- # See http://dev.rubyonrails.com/ticket/3567
- val = val.to_time if val.is_a?(XMLRPC::DateTime)
- obj.__send__("#{name}=", val) if obj.respond_to?(name)
- end
- elsif value.respond_to?(:attributes)
- signature_type.each_member do |name, type|
- val = value.__send__(name)
- obj.__send__("#{name}=", cast(val, type)) if obj.respond_to?(name)
- end
- else
- raise CastingError, "Don't know how to cast #{value.class} to #{signature_type.type_class}"
- end
- obj
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/client.rb b/vendor/rails/actionwebservice/lib/action_web_service/client.rb
deleted file mode 100644
index 2a1e3305..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/client.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-require 'action_web_service/client/base'
-require 'action_web_service/client/soap_client'
-require 'action_web_service/client/xmlrpc_client'
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/client/base.rb b/vendor/rails/actionwebservice/lib/action_web_service/client/base.rb
deleted file mode 100644
index 9dada7bf..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/client/base.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-module ActionWebService # :nodoc:
- module Client # :nodoc:
- class ClientError < StandardError # :nodoc:
- end
-
- class Base # :nodoc:
- def initialize(api, endpoint_uri)
- @api = api
- @endpoint_uri = endpoint_uri
- end
-
- def method_missing(name, *args) # :nodoc:
- call_name = method_name(name)
- return super(name, *args) if call_name.nil?
- self.perform_invocation(call_name, args)
- end
-
- private
- def method_name(name)
- if @api.has_api_method?(name.to_sym)
- name.to_s
- elsif @api.has_public_api_method?(name.to_s)
- @api.api_method_name(name.to_s).to_s
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/client/soap_client.rb b/vendor/rails/actionwebservice/lib/action_web_service/client/soap_client.rb
deleted file mode 100644
index ebabd8ea..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/client/soap_client.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-require 'soap/rpc/driver'
-require 'uri'
-
-module ActionWebService # :nodoc:
- module Client # :nodoc:
-
- # Implements SOAP client support (using RPC encoding for the messages).
- #
- # ==== Example Usage
- #
- # class PersonAPI < ActionWebService::API::Base
- # api_method :find_all, :returns => [[Person]]
- # end
- #
- # soap_client = ActionWebService::Client::Soap.new(PersonAPI, "http://...")
- # persons = soap_client.find_all
- #
- class Soap < Base
- # provides access to the underlying soap driver
- attr_reader :driver
-
- # Creates a new web service client using the SOAP RPC protocol.
- #
- # +api+ must be an ActionWebService::API::Base derivative, and
- # +endpoint_uri+ must point at the relevant URL to which protocol requests
- # will be sent with HTTP POST.
- #
- # Valid options:
- # [:namespace ] If the remote server has used a custom namespace to
- # declare its custom types, you can specify it here. This would
- # be the namespace declared with a [WebService(Namespace = "http://namespace")] attribute
- # in .NET, for example.
- # [:driver_options ] If you want to supply any custom SOAP RPC driver
- # options, you can provide them as a Hash here
- #
- # The :driver_options option can be used to configure the backend SOAP
- # RPC driver. An example of configuring the SOAP backend to do
- # client-certificate authenticated SSL connections to the server:
- #
- # opts = {}
- # opts['protocol.http.ssl_config.verify_mode'] = 'OpenSSL::SSL::VERIFY_PEER'
- # opts['protocol.http.ssl_config.client_cert'] = client_cert_file_path
- # opts['protocol.http.ssl_config.client_key'] = client_key_file_path
- # opts['protocol.http.ssl_config.ca_file'] = ca_cert_file_path
- # client = ActionWebService::Client::Soap.new(api, 'https://some/service', :driver_options => opts)
- def initialize(api, endpoint_uri, options={})
- super(api, endpoint_uri)
- @namespace = options[:namespace] || 'urn:ActionWebService'
- @driver_options = options[:driver_options] || {}
- @protocol = ActionWebService::Protocol::Soap::SoapProtocol.new @namespace
- @soap_action_base = options[:soap_action_base]
- @soap_action_base ||= URI.parse(endpoint_uri).path
- @driver = create_soap_rpc_driver(api, endpoint_uri)
- @driver_options.each do |name, value|
- @driver.options[name.to_s] = value
- end
- end
-
- protected
- def perform_invocation(method_name, args)
- method = @api.api_methods[method_name.to_sym]
- args = method.cast_expects(args.dup) rescue args
- return_value = @driver.send(method_name, *args)
- method.cast_returns(return_value.dup) rescue return_value
- end
-
- def soap_action(method_name)
- "#{@soap_action_base}/#{method_name}"
- end
-
- private
- def create_soap_rpc_driver(api, endpoint_uri)
- @protocol.register_api(api)
- driver = SoapDriver.new(endpoint_uri, nil)
- driver.mapping_registry = @protocol.marshaler.registry
- api.api_methods.each do |name, method|
- qname = XSD::QName.new(@namespace, method.public_name)
- action = soap_action(method.public_name)
- expects = method.expects
- returns = method.returns
- param_def = []
- if expects
- expects.each do |type|
- type_binding = @protocol.marshaler.lookup_type(type)
- if SOAP::Version >= "1.5.5"
- param_def << ['in', type.name.to_s, [type_binding.type.type_class.to_s]]
- else
- param_def << ['in', type.name, type_binding.mapping]
- end
- end
- end
- if returns
- type_binding = @protocol.marshaler.lookup_type(returns[0])
- if SOAP::Version >= "1.5.5"
- param_def << ['retval', 'return', [type_binding.type.type_class.to_s]]
- else
- param_def << ['retval', 'return', type_binding.mapping]
- end
- end
- driver.add_method(qname, action, method.name.to_s, param_def)
- end
- driver
- end
-
- class SoapDriver < SOAP::RPC::Driver # :nodoc:
- def add_method(qname, soapaction, name, param_def)
- @proxy.add_rpc_method(qname, soapaction, name, param_def)
- add_rpc_method_interface(name, param_def)
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb b/vendor/rails/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
deleted file mode 100644
index 42b5c5d4..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require 'uri'
-require 'xmlrpc/client'
-
-module ActionWebService # :nodoc:
- module Client # :nodoc:
-
- # Implements XML-RPC client support
- #
- # ==== Example Usage
- #
- # class BloggerAPI < ActionWebService::API::Base
- # inflect_names false
- # api_method :getRecentPosts, :returns => [[Blog::Post]]
- # end
- #
- # blog = ActionWebService::Client::XmlRpc.new(BloggerAPI, "http://.../RPC", :handler_name => "blogger")
- # posts = blog.getRecentPosts
- class XmlRpc < Base
-
- # Creates a new web service client using the XML-RPC protocol.
- #
- # +api+ must be an ActionWebService::API::Base derivative, and
- # +endpoint_uri+ must point at the relevant URL to which protocol requests
- # will be sent with HTTP POST.
- #
- # Valid options:
- # [:handler_name ] If the remote server defines its services inside special
- # handler (the Blogger API uses a "blogger" handler name for example),
- # provide it here, or your method calls will fail
- def initialize(api, endpoint_uri, options={})
- @api = api
- @handler_name = options[:handler_name]
- @protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.new
- @client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
- end
-
- protected
- def perform_invocation(method_name, args)
- method = @api.api_methods[method_name.to_sym]
- if method.expects && method.expects.length != args.length
- raise(ArgumentError, "#{method.public_name}: wrong number of arguments (#{args.length} for #{method.expects.length})")
- end
- args = method.cast_expects(args.dup) rescue args
- if method.expects
- method.expects.each_with_index{ |type, i| args[i] = @protocol.value_to_xmlrpc_wire_format(args[i], type) }
- end
- ok, return_value = @client.call2(public_name(method_name), *args)
- return (method.cast_returns(return_value.dup) rescue return_value) if ok
- raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
- end
-
- def public_name(method_name)
- public_name = @api.public_api_method_name(method_name)
- @handler_name ? "#{@handler_name}.#{public_name}" : public_name
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/container.rb b/vendor/rails/actionwebservice/lib/action_web_service/container.rb
deleted file mode 100644
index 13d9d8ab..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/container.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-require 'action_web_service/container/direct_container'
-require 'action_web_service/container/delegated_container'
-require 'action_web_service/container/action_controller_container'
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/container/action_controller_container.rb b/vendor/rails/actionwebservice/lib/action_web_service/container/action_controller_container.rb
deleted file mode 100644
index bbc28083..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/container/action_controller_container.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-module ActionWebService # :nodoc:
- module Container # :nodoc:
- module ActionController # :nodoc:
- def self.included(base) # :nodoc:
- class << base
- include ClassMethods
- alias_method_chain :inherited, :api
- alias_method_chain :web_service_api, :require
- end
- end
-
- module ClassMethods
- # Creates a client for accessing remote web services, using the
- # given +protocol+ to communicate with the +endpoint_uri+.
- #
- # ==== Example
- #
- # class MyController < ActionController::Base
- # web_client_api :blogger, :xmlrpc, "http://blogger.com/myblog/api/RPC2", :handler_name => 'blogger'
- # end
- #
- # In this example, a protected method named blogger will
- # now exist on the controller, and calling it will return the
- # XML-RPC client object for working with that remote service.
- #
- # +options+ is the set of protocol client specific options (see
- # a protocol client class for details).
- #
- # If your API definition does not exist on the load path with the
- # correct rules for it to be found using +name+, you can pass in
- # the API definition class via +options+, using a key of :api
- def web_client_api(name, protocol, endpoint_uri, options={})
- unless method_defined?(name)
- api_klass = options.delete(:api) || require_web_service_api(name)
- class_eval do
- define_method(name) do
- create_web_service_client(api_klass, protocol, endpoint_uri, options)
- end
- protected name
- end
- end
- end
-
- def web_service_api_with_require(definition=nil) # :nodoc:
- return web_service_api_without_require if definition.nil?
- case definition
- when String, Symbol
- klass = require_web_service_api(definition)
- else
- klass = definition
- end
- web_service_api_without_require(klass)
- end
-
- def require_web_service_api(name) # :nodoc:
- case name
- when String, Symbol
- file_name = name.to_s.underscore + "_api"
- class_name = file_name.camelize
- class_names = [class_name, class_name.sub(/Api$/, 'API')]
- begin
- require_dependency(file_name)
- rescue LoadError => load_error
- requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
- msg = requiree == file_name ? "Missing API definition file in apis/#{file_name}.rb" : "Can't load file: #{requiree}"
- raise LoadError.new(msg).copy_blame!(load_error)
- end
- klass = nil
- class_names.each do |name|
- klass = name.constantize rescue nil
- break unless klass.nil?
- end
- unless klass
- raise(NameError, "neither #{class_names[0]} or #{class_names[1]} found")
- end
- klass
- else
- raise(ArgumentError, "expected String or Symbol argument")
- end
- end
-
- private
- def inherited_with_api(child)
- inherited_without_api(child)
- begin child.web_service_api(child.controller_path)
- rescue MissingSourceFile => e
- raise unless e.is_missing?("apis/#{child.controller_path}_api")
- end
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/container/delegated_container.rb b/vendor/rails/actionwebservice/lib/action_web_service/container/delegated_container.rb
deleted file mode 100644
index 5477f8d1..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/container/delegated_container.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-module ActionWebService # :nodoc:
- module Container # :nodoc:
- module Delegated # :nodoc:
- class ContainerError < ActionWebServiceError # :nodoc:
- end
-
- def self.included(base) # :nodoc:
- base.extend(ClassMethods)
- base.send(:include, ActionWebService::Container::Delegated::InstanceMethods)
- end
-
- module ClassMethods
- # Declares a web service that will provide access to the API of the given
- # +object+. +object+ must be an ActionWebService::Base derivative.
- #
- # Web service object creation can either be _immediate_, where the object
- # instance is given at class definition time, or _deferred_, where
- # object instantiation is delayed until request time.
- #
- # ==== Immediate web service object example
- #
- # class ApiController < ApplicationController
- # web_service_dispatching_mode :delegated
- #
- # web_service :person, PersonService.new
- # end
- #
- # For deferred instantiation, a block should be given instead of an
- # object instance. This block will be executed in controller instance
- # context, so it can rely on controller instance variables being present.
- #
- # ==== Deferred web service object example
- #
- # class ApiController < ApplicationController
- # web_service_dispatching_mode :delegated
- #
- # web_service(:person) { PersonService.new(request.env) }
- # end
- def web_service(name, object=nil, &block)
- if (object && block_given?) || (object.nil? && block.nil?)
- raise(ContainerError, "either service, or a block must be given")
- end
- name = name.to_sym
- if block_given?
- info = { name => { :block => block } }
- else
- info = { name => { :object => object } }
- end
- write_inheritable_hash("web_services", info)
- call_web_service_definition_callbacks(self, name, info)
- end
-
- # Whether this service contains a service with the given +name+
- def has_web_service?(name)
- web_services.has_key?(name.to_sym)
- end
-
- def web_services # :nodoc:
- read_inheritable_attribute("web_services") || {}
- end
-
- def add_web_service_definition_callback(&block) # :nodoc:
- write_inheritable_array("web_service_definition_callbacks", [block])
- end
-
- private
- def call_web_service_definition_callbacks(container_class, web_service_name, service_info)
- (read_inheritable_attribute("web_service_definition_callbacks") || []).each do |block|
- block.call(container_class, web_service_name, service_info)
- end
- end
- end
-
- module InstanceMethods # :nodoc:
- def web_service_object(web_service_name)
- info = self.class.web_services[web_service_name.to_sym]
- unless info
- raise(ContainerError, "no such web service '#{web_service_name}'")
- end
- service = info[:block]
- service ? self.instance_eval(&service) : info[:object]
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/container/direct_container.rb b/vendor/rails/actionwebservice/lib/action_web_service/container/direct_container.rb
deleted file mode 100644
index 8818d8f4..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/container/direct_container.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-module ActionWebService # :nodoc:
- module Container # :nodoc:
- module Direct # :nodoc:
- class ContainerError < ActionWebServiceError # :nodoc:
- end
-
- def self.included(base) # :nodoc:
- base.extend(ClassMethods)
- end
-
- module ClassMethods
- # Attaches ActionWebService API +definition+ to the calling class.
- #
- # Action Controllers can have a default associated API, removing the need
- # to call this method if you follow the Action Web Service naming conventions.
- #
- # A controller with a class name of GoogleSearchController will
- # implicitly load app/apis/google_search_api.rb , and expect the
- # API definition class to be named GoogleSearchAPI or
- # GoogleSearchApi .
- #
- # ==== Service class example
- #
- # class MyService < ActionWebService::Base
- # web_service_api MyAPI
- # end
- #
- # class MyAPI < ActionWebService::API::Base
- # ...
- # end
- #
- # ==== Controller class example
- #
- # class MyController < ActionController::Base
- # web_service_api MyAPI
- # end
- #
- # class MyAPI < ActionWebService::API::Base
- # ...
- # end
- def web_service_api(definition=nil)
- if definition.nil?
- read_inheritable_attribute("web_service_api")
- else
- if definition.is_a?(Symbol)
- raise(ContainerError, "symbols can only be used for #web_service_api inside of a controller")
- end
- unless definition.respond_to?(:ancestors) && definition.ancestors.include?(ActionWebService::API::Base)
- raise(ContainerError, "#{definition.to_s} is not a valid API definition")
- end
- write_inheritable_attribute("web_service_api", definition)
- call_web_service_api_callbacks(self, definition)
- end
- end
-
- def add_web_service_api_callback(&block) # :nodoc:
- write_inheritable_array("web_service_api_callbacks", [block])
- end
-
- private
- def call_web_service_api_callbacks(container_class, definition)
- (read_inheritable_attribute("web_service_api_callbacks") || []).each do |block|
- block.call(container_class, definition)
- end
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/dispatcher.rb b/vendor/rails/actionwebservice/lib/action_web_service/dispatcher.rb
deleted file mode 100644
index 601d8313..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/dispatcher.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-require 'action_web_service/dispatcher/abstract'
-require 'action_web_service/dispatcher/action_controller_dispatcher'
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/dispatcher/abstract.rb b/vendor/rails/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
deleted file mode 100644
index 9c981e4e..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
+++ /dev/null
@@ -1,203 +0,0 @@
-require 'benchmark'
-
-module ActionWebService # :nodoc:
- module Dispatcher # :nodoc:
- class DispatcherError < ActionWebService::ActionWebServiceError # :nodoc:
- end
-
- def self.included(base) # :nodoc:
- base.class_inheritable_option(:web_service_dispatching_mode, :direct)
- base.class_inheritable_option(:web_service_exception_reporting, true)
- base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
- end
-
- module InstanceMethods # :nodoc:
- private
- def invoke_web_service_request(protocol_request)
- invocation = web_service_invocation(protocol_request)
- if invocation.is_a?(Array) && protocol_request.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
- xmlrpc_multicall_invoke(invocation)
- else
- web_service_invoke(invocation)
- end
- end
-
- def web_service_direct_invoke(invocation)
- @method_params = invocation.method_ordered_params
- arity = method(invocation.api_method.name).arity rescue 0
- if arity < 0 || arity > 0
- params = @method_params
- else
- params = []
- end
- web_service_filtered_invoke(invocation, params)
- end
-
- def web_service_delegated_invoke(invocation)
- web_service_filtered_invoke(invocation, invocation.method_ordered_params)
- end
-
- def web_service_filtered_invoke(invocation, params)
- cancellation_reason = nil
- return_value = invocation.service.perform_invocation(invocation.api_method.name, params) do |x|
- cancellation_reason = x
- end
- if cancellation_reason
- raise(DispatcherError, "request canceled: #{cancellation_reason}")
- end
- return_value
- end
-
- def web_service_invoke(invocation)
- case web_service_dispatching_mode
- when :direct
- return_value = web_service_direct_invoke(invocation)
- when :delegated, :layered
- return_value = web_service_delegated_invoke(invocation)
- end
- web_service_create_response(invocation.protocol, invocation.protocol_options, invocation.api, invocation.api_method, return_value)
- end
-
- def xmlrpc_multicall_invoke(invocations)
- responses = []
- invocations.each do |invocation|
- if invocation.is_a?(Hash)
- responses << [invocation, nil]
- next
- end
- begin
- case web_service_dispatching_mode
- when :direct
- return_value = web_service_direct_invoke(invocation)
- when :delegated, :layered
- return_value = web_service_delegated_invoke(invocation)
- end
- api_method = invocation.api_method
- if invocation.api.has_api_method?(api_method.name)
- response_type = (api_method.returns ? api_method.returns[0] : nil)
- return_value = api_method.cast_returns(return_value)
- else
- response_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
- end
- responses << [return_value, response_type]
- rescue Exception => e
- responses << [{ 'faultCode' => 3, 'faultString' => e.message }, nil]
- end
- end
- invocation = invocations[0]
- invocation.protocol.encode_multicall_response(responses, invocation.protocol_options)
- end
-
- def web_service_invocation(request, level = 0)
- public_method_name = request.method_name
- invocation = Invocation.new
- invocation.protocol = request.protocol
- invocation.protocol_options = request.protocol_options
- invocation.service_name = request.service_name
- if web_service_dispatching_mode == :layered
- case invocation.protocol
- when Protocol::Soap::SoapProtocol
- soap_action = request.protocol_options[:soap_action]
- if soap_action && soap_action =~ /^\/\w+\/(\w+)\//
- invocation.service_name = $1
- end
- when Protocol::XmlRpc::XmlRpcProtocol
- if request.method_name =~ /^([^\.]+)\.(.*)$/
- public_method_name = $2
- invocation.service_name = $1
- end
- end
- end
- if invocation.protocol.is_a? Protocol::XmlRpc::XmlRpcProtocol
- if public_method_name == 'multicall' && invocation.service_name == 'system'
- if level > 0
- raise(DispatcherError, "Recursive system.multicall invocations not allowed")
- end
- multicall = request.method_params.dup
- unless multicall.is_a?(Array) && multicall[0].is_a?(Array)
- raise(DispatcherError, "Malformed multicall (expected array of Hash elements)")
- end
- multicall = multicall[0]
- return multicall.map do |item|
- raise(DispatcherError, "Multicall elements must be Hash") unless item.is_a?(Hash)
- raise(DispatcherError, "Multicall elements must contain a 'methodName' key") unless item.has_key?('methodName')
- method_name = item['methodName']
- params = item.has_key?('params') ? item['params'] : []
- multicall_request = request.dup
- multicall_request.method_name = method_name
- multicall_request.method_params = params
- begin
- web_service_invocation(multicall_request, level + 1)
- rescue Exception => e
- {'faultCode' => 4, 'faultMessage' => e.message}
- end
- end
- end
- end
- case web_service_dispatching_mode
- when :direct
- invocation.api = self.class.web_service_api
- invocation.service = self
- when :delegated, :layered
- invocation.service = web_service_object(invocation.service_name)
- invocation.api = invocation.service.class.web_service_api
- end
- if invocation.api.nil?
- raise(DispatcherError, "no API attached to #{invocation.service.class}")
- end
- invocation.protocol.register_api(invocation.api)
- request.api = invocation.api
- if invocation.api.has_public_api_method?(public_method_name)
- invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
- else
- if invocation.api.default_api_method.nil?
- raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
- else
- invocation.api_method = invocation.api.default_api_method_instance
- end
- end
- if invocation.service.nil?
- raise(DispatcherError, "no service available for service name #{invocation.service_name}")
- end
- unless invocation.service.respond_to?(invocation.api_method.name)
- raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
- end
- request.api_method = invocation.api_method
- begin
- invocation.method_ordered_params = invocation.api_method.cast_expects(request.method_params.dup)
- rescue
- logger.warn "Casting of method parameters failed" unless logger.nil?
- invocation.method_ordered_params = request.method_params
- end
- request.method_params = invocation.method_ordered_params
- invocation.method_named_params = {}
- invocation.api_method.param_names.inject(0) do |m, n|
- invocation.method_named_params[n] = invocation.method_ordered_params[m]
- m + 1
- end
- invocation
- end
-
- def web_service_create_response(protocol, protocol_options, api, api_method, return_value)
- if api.has_api_method?(api_method.name)
- return_type = api_method.returns ? api_method.returns[0] : nil
- return_value = api_method.cast_returns(return_value)
- else
- return_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
- end
- protocol.encode_response(api_method.public_name + 'Response', return_value, return_type, protocol_options)
- end
-
- class Invocation # :nodoc:
- attr_accessor :protocol
- attr_accessor :protocol_options
- attr_accessor :service_name
- attr_accessor :api
- attr_accessor :api_method
- attr_accessor :method_ordered_params
- attr_accessor :method_named_params
- attr_accessor :service
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb b/vendor/rails/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
deleted file mode 100644
index 85773a61..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb
+++ /dev/null
@@ -1,376 +0,0 @@
-require 'benchmark'
-require 'builder/xmlmarkup'
-
-module ActionWebService # :nodoc:
- module Dispatcher # :nodoc:
- module ActionController # :nodoc:
- def self.included(base) # :nodoc:
- class << base
- include ClassMethods
- alias_method_chain :inherited, :action_controller
- end
- base.class_eval do
- alias_method :web_service_direct_invoke_without_controller, :web_service_direct_invoke
- end
- base.add_web_service_api_callback do |klass, api|
- if klass.web_service_dispatching_mode == :direct
- klass.class_eval 'def api; dispatch_web_service_request; end'
- end
- end
- base.add_web_service_definition_callback do |klass, name, info|
- if klass.web_service_dispatching_mode == :delegated
- klass.class_eval "def #{name}; dispatch_web_service_request; end"
- elsif klass.web_service_dispatching_mode == :layered
- klass.class_eval 'def api; dispatch_web_service_request; end'
- end
- end
- base.send(:include, ActionWebService::Dispatcher::ActionController::InstanceMethods)
- end
-
- module ClassMethods # :nodoc:
- def inherited_with_action_controller(child)
- inherited_without_action_controller(child)
- child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlAction)
- end
- end
-
- module InstanceMethods # :nodoc:
- private
- def dispatch_web_service_request
- if request.get?
- render_text('GET not supported', '500 GET not supported')
- return
- end
- exception = nil
- begin
- ws_request = discover_web_service_request(request)
- rescue Exception => e
- exception = e
- end
- if ws_request
- ws_response = nil
- exception = nil
- bm = Benchmark.measure do
- begin
- ws_response = invoke_web_service_request(ws_request)
- rescue Exception => e
- exception = e
- end
- end
- log_request(ws_request, request.raw_post)
- if exception
- log_error(exception) unless logger.nil?
- send_web_service_error_response(ws_request, exception)
- else
- send_web_service_response(ws_response, bm.real)
- end
- else
- exception ||= DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
- log_error(exception) unless logger.nil?
- send_web_service_error_response(ws_request, exception)
- end
- rescue Exception => e
- log_error(e) unless logger.nil?
- send_web_service_error_response(ws_request, e)
- end
-
- def send_web_service_response(ws_response, elapsed=nil)
- log_response(ws_response, elapsed)
- options = { :type => ws_response.content_type, :disposition => 'inline' }
- send_data(ws_response.body, options)
- end
-
- def send_web_service_error_response(ws_request, exception)
- if ws_request
- unless self.class.web_service_exception_reporting
- exception = DispatcherError.new("Internal server error (exception raised)")
- end
- api_method = ws_request.api_method
- public_method_name = api_method ? api_method.public_name : ws_request.method_name
- return_type = ActionWebService::SignatureTypes.canonical_signature_entry(Exception, 0)
- ws_response = ws_request.protocol.encode_response(public_method_name + 'Response', exception, return_type, ws_request.protocol_options)
- send_web_service_response(ws_response)
- else
- if self.class.web_service_exception_reporting
- message = exception.message
- backtrace = "\nBacktrace:\n#{exception.backtrace.join("\n")}"
- else
- message = "Exception raised"
- backtrace = ""
- end
- render_text("Internal protocol error: #{message}#{backtrace}", "500 Internal Protocol Error")
- end
- end
-
- def web_service_direct_invoke(invocation)
- invocation.method_named_params.each do |name, value|
- params[name] = value
- end
- web_service_direct_invoke_without_controller(invocation)
- end
-
- def log_request(ws_request, body)
- unless logger.nil?
- name = ws_request.method_name
- api_method = ws_request.api_method
- params = ws_request.method_params
- if api_method && api_method.expects
- params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
- else
- params = params.map{ |param| param.inspect }
- end
- service = ws_request.service_name
- logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
- logger.debug(indent(body))
- end
- end
-
- def log_response(ws_response, elapsed=nil)
- unless logger.nil?
- elapsed = (elapsed ? " (%f):" % elapsed : ":")
- logger.debug("\nWeb Service Response" + elapsed + " => #{ws_response.return_value.inspect}")
- logger.debug(indent(ws_response.body))
- end
- end
-
- def indent(body)
- body.split(/\n/).map{|x| " #{x}"}.join("\n")
- end
- end
-
- module WsdlAction # :nodoc:
- XsdNs = 'http://www.w3.org/2001/XMLSchema'
- WsdlNs = 'http://schemas.xmlsoap.org/wsdl/'
- SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/'
- SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/'
- SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http'
-
- def wsdl
- case request.method
- when :get
- begin
- options = { :type => 'text/xml', :disposition => 'inline' }
- send_data(to_wsdl, options)
- rescue Exception => e
- log_error(e) unless logger.nil?
- end
- when :post
- render_text('POST not supported', '500 POST not supported')
- end
- end
-
- private
- def base_uri
- host = request.host_with_port
- relative_url_root = request.relative_url_root
- scheme = request.ssl? ? 'https' : 'http'
- '%s://%s%s/%s/' % [scheme, host, relative_url_root, self.class.controller_path]
- end
-
- def to_wsdl
- xml = ''
- dispatching_mode = web_service_dispatching_mode
- global_service_name = wsdl_service_name
- namespace = wsdl_namespace || 'urn:ActionWebService'
- soap_action_base = "/#{controller_name}"
-
- marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
- apis = {}
- case dispatching_mode
- when :direct
- api = self.class.web_service_api
- web_service_name = controller_class_name.sub(/Controller$/, '').underscore
- apis[web_service_name] = [api, register_api(api, marshaler)]
- when :delegated, :layered
- self.class.web_services.each do |web_service_name, info|
- service = web_service_object(web_service_name)
- api = service.class.web_service_api
- apis[web_service_name] = [api, register_api(api, marshaler)]
- end
- end
- custom_types = []
- apis.values.each do |api, bindings|
- bindings.each do |b|
- custom_types << b unless custom_types.include?(b)
- end
- end
-
- xm = Builder::XmlMarkup.new(:target => xml, :indent => 2)
- xm.instruct!
- xm.definitions('name' => wsdl_service_name,
- 'targetNamespace' => namespace,
- 'xmlns:typens' => namespace,
- 'xmlns:xsd' => XsdNs,
- 'xmlns:soap' => SoapNs,
- 'xmlns:soapenc' => SoapEncodingNs,
- 'xmlns:wsdl' => WsdlNs,
- 'xmlns' => WsdlNs) do
- # Generate XSD
- if custom_types.size > 0
- xm.types do
- xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
- custom_types.each do |binding|
- case
- when binding.type.array?
- xm.xsd(:complexType, 'name' => binding.type_name) do
- xm.xsd(:complexContent) do
- xm.xsd(:restriction, 'base' => 'soapenc:Array') do
- xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
- 'wsdl:arrayType' => binding.element_binding.qualified_type_name('typens') + '[]')
- end
- end
- end
- when binding.type.structured?
- xm.xsd(:complexType, 'name' => binding.type_name) do
- xm.xsd(:all) do
- binding.type.each_member do |name, type|
- b = marshaler.register_type(type)
- xm.xsd(:element, 'name' => name, 'type' => b.qualified_type_name('typens'))
- end
- end
- end
- end
- end
- end
- end
- end
-
- # APIs
- apis.each do |api_name, values|
- api = values[0]
- api.api_methods.each do |name, method|
- gen = lambda do |msg_name, direction|
- xm.message('name' => message_name_for(api_name, msg_name)) do
- sym = nil
- if direction == :out
- returns = method.returns
- if returns
- binding = marshaler.register_type(returns[0])
- xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
- end
- else
- expects = method.expects
- expects.each do |type|
- binding = marshaler.register_type(type)
- xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
- end if expects
- end
- end
- end
- public_name = method.public_name
- gen.call(public_name, :in)
- gen.call("#{public_name}Response", :out)
- end
-
- # Port
- port_name = port_name_for(global_service_name, api_name)
- xm.portType('name' => port_name) do
- api.api_methods.each do |name, method|
- xm.operation('name' => method.public_name) do
- xm.input('message' => "typens:" + message_name_for(api_name, method.public_name))
- xm.output('message' => "typens:" + message_name_for(api_name, "#{method.public_name}Response"))
- end
- end
- end
-
- # Bind it
- binding_name = binding_name_for(global_service_name, api_name)
- xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
- xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
- api.api_methods.each do |name, method|
- xm.operation('name' => method.public_name) do
- case web_service_dispatching_mode
- when :direct
- soap_action = soap_action_base + "/api/" + method.public_name
- when :delegated, :layered
- soap_action = soap_action_base \
- + "/" + api_name.to_s \
- + "/" + method.public_name
- end
- xm.soap(:operation, 'soapAction' => soap_action)
- xm.input do
- xm.soap(:body,
- 'use' => 'encoded',
- 'namespace' => namespace,
- 'encodingStyle' => SoapEncodingNs)
- end
- xm.output do
- xm.soap(:body,
- 'use' => 'encoded',
- 'namespace' => namespace,
- 'encodingStyle' => SoapEncodingNs)
- end
- end
- end
- end
- end
-
- # Define it
- xm.service('name' => "#{global_service_name}Service") do
- apis.each do |api_name, values|
- port_name = port_name_for(global_service_name, api_name)
- binding_name = binding_name_for(global_service_name, api_name)
- case web_service_dispatching_mode
- when :direct, :layered
- binding_target = 'api'
- when :delegated
- binding_target = api_name.to_s
- end
- xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
- xm.soap(:address, 'location' => "#{base_uri}#{binding_target}")
- end
- end
- end
- end
- end
-
- def port_name_for(global_service, service)
- "#{global_service}#{service.to_s.camelize}Port"
- end
-
- def binding_name_for(global_service, service)
- "#{global_service}#{service.to_s.camelize}Binding"
- end
-
- def message_name_for(api_name, message_name)
- mode = web_service_dispatching_mode
- if mode == :layered || mode == :delegated
- api_name.to_s + '-' + message_name
- else
- message_name
- end
- end
-
- def register_api(api, marshaler)
- bindings = {}
- traverse_custom_types(api, marshaler, bindings) do |binding|
- bindings[binding] = nil unless bindings.has_key?(binding)
- element_binding = binding.element_binding
- bindings[element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
- end
- bindings.keys
- end
-
- def traverse_custom_types(api, marshaler, bindings, &block)
- api.api_methods.each do |name, method|
- expects, returns = method.expects, method.returns
- expects.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if expects
- returns.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if returns
- end
- end
-
- def traverse_type(marshaler, type, bindings, &block)
- binding = marshaler.register_type(type)
- return if bindings.has_key?(binding)
- bindings[binding] = nil
- yield binding
- if type.array?
- yield marshaler.register_type(type.element_type)
- type = type.element_type
- end
- type.each_member{ |name, type| traverse_type(marshaler, type, bindings, &block) } if type.structured?
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/invocation.rb b/vendor/rails/actionwebservice/lib/action_web_service/invocation.rb
deleted file mode 100644
index 2a9121ee..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/invocation.rb
+++ /dev/null
@@ -1,202 +0,0 @@
-module ActionWebService # :nodoc:
- module Invocation # :nodoc:
- class InvocationError < ActionWebService::ActionWebServiceError # :nodoc:
- end
-
- def self.included(base) # :nodoc:
- base.extend(ClassMethods)
- base.send(:include, ActionWebService::Invocation::InstanceMethods)
- end
-
- # Invocation interceptors provide a means to execute custom code before
- # and after method invocations on ActionWebService::Base objects.
- #
- # When running in _Direct_ dispatching mode, ActionController filters
- # should be used for this functionality instead.
- #
- # The semantics of invocation interceptors are the same as ActionController
- # filters, and accept the same parameters and options.
- #
- # A _before_ interceptor can also cancel execution by returning +false+,
- # or returning a [false, "cancel reason"] array if it wishes to supply
- # a reason for canceling the request.
- #
- # === Example
- #
- # class CustomService < ActionWebService::Base
- # before_invocation :intercept_add, :only => [:add]
- #
- # def add(a, b)
- # a + b
- # end
- #
- # private
- # def intercept_add
- # return [false, "permission denied"] # cancel it
- # end
- # end
- #
- # Options:
- # [:except ] A list of methods for which the interceptor will NOT be called
- # [:only ] A list of methods for which the interceptor WILL be called
- module ClassMethods
- # Appends the given +interceptors+ to be called
- # _before_ method invocation.
- def append_before_invocation(*interceptors, &block)
- conditions = extract_conditions!(interceptors)
- interceptors << block if block_given?
- add_interception_conditions(interceptors, conditions)
- append_interceptors_to_chain("before", interceptors)
- end
-
- # Prepends the given +interceptors+ to be called
- # _before_ method invocation.
- def prepend_before_invocation(*interceptors, &block)
- conditions = extract_conditions!(interceptors)
- interceptors << block if block_given?
- add_interception_conditions(interceptors, conditions)
- prepend_interceptors_to_chain("before", interceptors)
- end
-
- alias :before_invocation :append_before_invocation
-
- # Appends the given +interceptors+ to be called
- # _after_ method invocation.
- def append_after_invocation(*interceptors, &block)
- conditions = extract_conditions!(interceptors)
- interceptors << block if block_given?
- add_interception_conditions(interceptors, conditions)
- append_interceptors_to_chain("after", interceptors)
- end
-
- # Prepends the given +interceptors+ to be called
- # _after_ method invocation.
- def prepend_after_invocation(*interceptors, &block)
- conditions = extract_conditions!(interceptors)
- interceptors << block if block_given?
- add_interception_conditions(interceptors, conditions)
- prepend_interceptors_to_chain("after", interceptors)
- end
-
- alias :after_invocation :append_after_invocation
-
- def before_invocation_interceptors # :nodoc:
- read_inheritable_attribute("before_invocation_interceptors")
- end
-
- def after_invocation_interceptors # :nodoc:
- read_inheritable_attribute("after_invocation_interceptors")
- end
-
- def included_intercepted_methods # :nodoc:
- read_inheritable_attribute("included_intercepted_methods") || {}
- end
-
- def excluded_intercepted_methods # :nodoc:
- read_inheritable_attribute("excluded_intercepted_methods") || {}
- end
-
- private
- def append_interceptors_to_chain(condition, interceptors)
- write_inheritable_array("#{condition}_invocation_interceptors", interceptors)
- end
-
- def prepend_interceptors_to_chain(condition, interceptors)
- interceptors = interceptors + read_inheritable_attribute("#{condition}_invocation_interceptors")
- write_inheritable_attribute("#{condition}_invocation_interceptors", interceptors)
- end
-
- def extract_conditions!(interceptors)
- return nil unless interceptors.last.is_a? Hash
- interceptors.pop
- end
-
- def add_interception_conditions(interceptors, conditions)
- return unless conditions
- included, excluded = conditions[:only], conditions[:except]
- write_inheritable_hash("included_intercepted_methods", condition_hash(interceptors, included)) && return if included
- write_inheritable_hash("excluded_intercepted_methods", condition_hash(interceptors, excluded)) if excluded
- end
-
- def condition_hash(interceptors, *methods)
- interceptors.inject({}) {|hash, interceptor| hash.merge(interceptor => methods.flatten.map {|method| method.to_s})}
- end
- end
-
- module InstanceMethods # :nodoc:
- def self.included(base)
- base.class_eval do
- alias_method_chain :perform_invocation, :interception
- end
- end
-
- def perform_invocation_with_interception(method_name, params, &block)
- return if before_invocation(method_name, params, &block) == false
- return_value = perform_invocation_without_interception(method_name, params)
- after_invocation(method_name, params, return_value)
- return_value
- end
-
- def perform_invocation(method_name, params)
- send(method_name, *params)
- end
-
- def before_invocation(name, args, &block)
- call_interceptors(self.class.before_invocation_interceptors, [name, args], &block)
- end
-
- def after_invocation(name, args, result)
- call_interceptors(self.class.after_invocation_interceptors, [name, args, result])
- end
-
- private
-
- def call_interceptors(interceptors, interceptor_args, &block)
- if interceptors and not interceptors.empty?
- interceptors.each do |interceptor|
- next if method_exempted?(interceptor, interceptor_args[0].to_s)
- result = case
- when interceptor.is_a?(Symbol)
- self.send(interceptor, *interceptor_args)
- when interceptor_block?(interceptor)
- interceptor.call(self, *interceptor_args)
- when interceptor_class?(interceptor)
- interceptor.intercept(self, *interceptor_args)
- else
- raise(
- InvocationError,
- "Interceptors need to be either a symbol, proc/method, or a class implementing a static intercept method"
- )
- end
- reason = nil
- if result.is_a?(Array)
- reason = result[1] if result[1]
- result = result[0]
- end
- if result == false
- block.call(reason) if block && reason
- return false
- end
- end
- end
- end
-
- def interceptor_block?(interceptor)
- interceptor.respond_to?("call") && (interceptor.arity == 3 || interceptor.arity == -1)
- end
-
- def interceptor_class?(interceptor)
- interceptor.respond_to?("intercept")
- end
-
- def method_exempted?(interceptor, method_name)
- case
- when self.class.included_intercepted_methods[interceptor]
- !self.class.included_intercepted_methods[interceptor].include?(method_name)
- when self.class.excluded_intercepted_methods[interceptor]
- self.class.excluded_intercepted_methods[interceptor].include?(method_name)
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/protocol.rb b/vendor/rails/actionwebservice/lib/action_web_service/protocol.rb
deleted file mode 100644
index 053e9cb4..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/protocol.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'action_web_service/protocol/abstract'
-require 'action_web_service/protocol/discovery'
-require 'action_web_service/protocol/soap_protocol'
-require 'action_web_service/protocol/xmlrpc_protocol'
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/protocol/abstract.rb b/vendor/rails/actionwebservice/lib/action_web_service/protocol/abstract.rb
deleted file mode 100644
index fff5f622..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/protocol/abstract.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-module ActionWebService # :nodoc:
- module Protocol # :nodoc:
- class ProtocolError < ActionWebServiceError # :nodoc:
- end
-
- class AbstractProtocol # :nodoc:
- def setup(controller)
- end
-
- def decode_action_pack_request(action_pack_request)
- end
-
- def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
- klass = options[:request_class] || SimpleActionPackRequest
- request = klass.new
- request.request_parameters['action'] = service_name.to_s
- request.env['RAW_POST_DATA'] = raw_body
- request.env['REQUEST_METHOD'] = 'POST'
- request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
- request
- end
-
- def decode_request(raw_request, service_name, protocol_options={})
- end
-
- def encode_request(method_name, params, param_types)
- end
-
- def decode_response(raw_response)
- end
-
- def encode_response(method_name, return_value, return_type, protocol_options={})
- end
-
- def protocol_client(api, protocol_name, endpoint_uri, options)
- end
-
- def register_api(api)
- end
- end
-
- class Request # :nodoc:
- attr :protocol
- attr_accessor :method_name
- attr_accessor :method_params
- attr :service_name
- attr_accessor :api
- attr_accessor :api_method
- attr :protocol_options
-
- def initialize(protocol, method_name, method_params, service_name, api=nil, api_method=nil, protocol_options=nil)
- @protocol = protocol
- @method_name = method_name
- @method_params = method_params
- @service_name = service_name
- @api = api
- @api_method = api_method
- @protocol_options = protocol_options || {}
- end
- end
-
- class Response # :nodoc:
- attr :body
- attr :content_type
- attr :return_value
-
- def initialize(body, content_type, return_value)
- @body = body
- @content_type = content_type
- @return_value = return_value
- end
- end
-
- class SimpleActionPackRequest < ActionController::AbstractRequest # :nodoc:
- def initialize
- @env = {}
- @qparams = {}
- @rparams = {}
- @cookies = {}
- reset_session
- end
-
- def query_parameters
- @qparams
- end
-
- def request_parameters
- @rparams
- end
-
- def env
- @env
- end
-
- def host
- ''
- end
-
- def cookies
- @cookies
- end
-
- def session
- @session
- end
-
- def reset_session
- @session = {}
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/protocol/discovery.rb b/vendor/rails/actionwebservice/lib/action_web_service/protocol/discovery.rb
deleted file mode 100644
index 3d4e0818..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/protocol/discovery.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-module ActionWebService # :nodoc:
- module Protocol # :nodoc:
- module Discovery # :nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- base.send(:include, ActionWebService::Protocol::Discovery::InstanceMethods)
- end
-
- module ClassMethods # :nodoc:
- def register_protocol(klass)
- write_inheritable_array("web_service_protocols", [klass])
- end
- end
-
- module InstanceMethods # :nodoc:
- private
- def discover_web_service_request(action_pack_request)
- (self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
- protocol = protocol.create(self)
- request = protocol.decode_action_pack_request(action_pack_request)
- return request unless request.nil?
- end
- nil
- end
-
- def create_web_service_client(api, protocol_name, endpoint_uri, options)
- (self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
- protocol = protocol.create(self)
- client = protocol.protocol_client(api, protocol_name, endpoint_uri, options)
- return client unless client.nil?
- end
- nil
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb b/vendor/rails/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
deleted file mode 100644
index 1bce496a..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb
+++ /dev/null
@@ -1,176 +0,0 @@
-require 'action_web_service/protocol/soap_protocol/marshaler'
-require 'soap/streamHandler'
-require 'action_web_service/client/soap_client'
-
-module ActionWebService # :nodoc:
- module API # :nodoc:
- class Base # :nodoc:
- def self.soap_client(endpoint_uri, options={})
- ActionWebService::Client::Soap.new self, endpoint_uri, options
- end
- end
- end
-
- module Protocol # :nodoc:
- module Soap # :nodoc:
- def self.included(base)
- base.register_protocol(SoapProtocol)
- base.class_inheritable_option(:wsdl_service_name)
- base.class_inheritable_option(:wsdl_namespace)
- end
-
- class SoapProtocol < AbstractProtocol # :nodoc:
- AWSEncoding = 'UTF-8'
- XSDEncoding = 'UTF8'
-
- attr :marshaler
-
- def initialize(namespace=nil)
- namespace ||= 'urn:ActionWebService'
- @marshaler = SoapMarshaler.new namespace
- end
-
- def self.create(controller)
- SoapProtocol.new(controller.wsdl_namespace)
- end
-
- def decode_action_pack_request(action_pack_request)
- return nil unless soap_action = has_valid_soap_action?(action_pack_request)
- service_name = action_pack_request.parameters['action']
- input_encoding = parse_charset(action_pack_request.env['HTTP_CONTENT_TYPE'])
- protocol_options = {
- :soap_action => soap_action,
- :charset => input_encoding
- }
- decode_request(action_pack_request.raw_post, service_name, protocol_options)
- end
-
- def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
- request = super
- request.env['HTTP_SOAPACTION'] = '/soap/%s/%s' % [service_name, public_method_name]
- request
- end
-
- def decode_request(raw_request, service_name, protocol_options={})
- envelope = SOAP::Processor.unmarshal(raw_request, :charset => protocol_options[:charset])
- unless envelope
- raise ProtocolError, "Failed to parse SOAP request message"
- end
- request = envelope.body.request
- method_name = request.elename.name
- params = request.collect{ |k, v| marshaler.soap_to_ruby(request[k]) }
- Request.new(self, method_name, params, service_name, nil, nil, protocol_options)
- end
-
- def encode_request(method_name, params, param_types)
- param_types.each{ |type| marshaler.register_type(type) } if param_types
- qname = XSD::QName.new(marshaler.namespace, method_name)
- param_def = []
- if param_types
- params = param_types.zip(params).map do |type, param|
- param_def << ['in', type.name, marshaler.lookup_type(type).mapping]
- [type.name, marshaler.ruby_to_soap(param)]
- end
- else
- params = []
- end
- request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
- request.set_param(params)
- envelope = create_soap_envelope(request)
- SOAP::Processor.marshal(envelope)
- end
-
- def decode_response(raw_response)
- envelope = SOAP::Processor.unmarshal(raw_response)
- unless envelope
- raise ProtocolError, "Failed to parse SOAP request message"
- end
- method_name = envelope.body.request.elename.name
- return_value = envelope.body.response
- return_value = marshaler.soap_to_ruby(return_value) unless return_value.nil?
- [method_name, return_value]
- end
-
- def encode_response(method_name, return_value, return_type, protocol_options={})
- if return_type
- return_binding = marshaler.register_type(return_type)
- marshaler.annotate_arrays(return_binding, return_value)
- end
- qname = XSD::QName.new(marshaler.namespace, method_name)
- if return_value.nil?
- response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
- else
- if return_value.is_a?(Exception)
- detail = SOAP::Mapping::SOAPException.new(return_value)
- response = SOAP::SOAPFault.new(
- SOAP::SOAPQName.new('%s:%s' % [SOAP::SOAPNamespaceTag, 'Server']),
- SOAP::SOAPString.new(return_value.to_s),
- SOAP::SOAPString.new(self.class.name),
- marshaler.ruby_to_soap(detail))
- else
- if return_type
- param_def = [['retval', 'return', marshaler.lookup_type(return_type).mapping]]
- response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
- response.retval = marshaler.ruby_to_soap(return_value)
- else
- response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
- end
- end
- end
- envelope = create_soap_envelope(response)
-
- # FIXME: This is not thread-safe, but StringFactory_ in SOAP4R only
- # reads target encoding from the XSD::Charset.encoding variable.
- # This is required to ensure $KCODE strings are converted
- # correctly to UTF-8 for any values of $KCODE.
- previous_encoding = XSD::Charset.encoding
- XSD::Charset.encoding = XSDEncoding
- response_body = SOAP::Processor.marshal(envelope, :charset => AWSEncoding)
- XSD::Charset.encoding = previous_encoding
-
- Response.new(response_body, "text/xml; charset=#{AWSEncoding}", return_value)
- end
-
- def protocol_client(api, protocol_name, endpoint_uri, options={})
- return nil unless protocol_name == :soap
- ActionWebService::Client::Soap.new(api, endpoint_uri, options)
- end
-
- def register_api(api)
- api.api_methods.each do |name, method|
- method.expects.each{ |type| marshaler.register_type(type) } if method.expects
- method.returns.each{ |type| marshaler.register_type(type) } if method.returns
- end
- end
-
- private
- def has_valid_soap_action?(request)
- return nil unless request.method == :post
- soap_action = request.env['HTTP_SOAPACTION']
- return nil unless soap_action
- soap_action = soap_action.dup
- soap_action.gsub!(/^"/, '')
- soap_action.gsub!(/"$/, '')
- soap_action.strip!
- return nil if soap_action.empty?
- soap_action
- end
-
- def create_soap_envelope(body)
- header = SOAP::SOAPHeader.new
- body = SOAP::SOAPBody.new(body)
- SOAP::SOAPEnvelope.new(header, body)
- end
-
- def parse_charset(content_type)
- return AWSEncoding if content_type.nil?
- if /^text\/xml(?:\s*;\s*charset=([^"]+|"[^"]+"))$/i =~ content_type
- $1
- else
- AWSEncoding
- end
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/protocol/soap_protocol/marshaler.rb b/vendor/rails/actionwebservice/lib/action_web_service/protocol/soap_protocol/marshaler.rb
deleted file mode 100644
index 351c9da1..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/protocol/soap_protocol/marshaler.rb
+++ /dev/null
@@ -1,241 +0,0 @@
-require 'soap/mapping'
-
-module ActionWebService
- module Protocol
- module Soap
- # Workaround for SOAP4R return values changing
- class Registry < SOAP::Mapping::Registry
- if SOAP::Version >= "1.5.4"
- def find_mapped_soap_class(obj_class)
- return @map.instance_eval { @obj2soap[obj_class][0] }
- end
-
- def find_mapped_obj_class(soap_class)
- return @map.instance_eval { @soap2obj[soap_class][0] }
- end
- end
- end
-
- class SoapMarshaler
- attr :namespace
- attr :registry
-
- def initialize(namespace=nil)
- @namespace = namespace || 'urn:ActionWebService'
- @registry = Registry.new
- @type2binding = {}
- register_static_factories
- end
-
- def soap_to_ruby(obj)
- SOAP::Mapping.soap2obj(obj, @registry)
- end
-
- def ruby_to_soap(obj)
- soap = SOAP::Mapping.obj2soap(obj, @registry)
- soap.elename = XSD::QName.new if SOAP::Version >= "1.5.5" && soap.elename == XSD::QName::EMPTY
- soap
- end
-
- def register_type(type)
- return @type2binding[type] if @type2binding.has_key?(type)
-
- if type.array?
- array_mapping = @registry.find_mapped_soap_class(Array)
- qname = XSD::QName.new(@namespace, soap_type_name(type.element_type.type_class.name) + 'Array')
- element_type_binding = register_type(type.element_type)
- @type2binding[type] = SoapBinding.new(self, qname, type, array_mapping, element_type_binding)
- elsif (mapping = @registry.find_mapped_soap_class(type.type_class) rescue nil)
- qname = mapping[2] ? mapping[2][:type] : nil
- qname ||= soap_base_type_name(mapping[0])
- @type2binding[type] = SoapBinding.new(self, qname, type, mapping)
- else
- qname = XSD::QName.new(@namespace, soap_type_name(type.type_class.name))
- @registry.add(type.type_class,
- SOAP::SOAPStruct,
- typed_struct_factory(type.type_class),
- { :type => qname })
- mapping = @registry.find_mapped_soap_class(type.type_class)
- @type2binding[type] = SoapBinding.new(self, qname, type, mapping)
- end
-
- if type.structured?
- type.each_member do |m_name, m_type|
- register_type(m_type)
- end
- end
-
- @type2binding[type]
- end
- alias :lookup_type :register_type
-
- def annotate_arrays(binding, value)
- if value.nil?
- return
- elsif binding.type.array?
- mark_typed_array(value, binding.element_binding.qname)
- if binding.element_binding.type.custom?
- value.each do |element|
- annotate_arrays(binding.element_binding, element)
- end
- end
- elsif binding.type.structured?
- binding.type.each_member do |name, type|
- member_binding = register_type(type)
- member_value = value.respond_to?('[]') ? value[name] : value.send(name)
- annotate_arrays(member_binding, member_value) if type.custom?
- end
- end
- end
-
- private
- def typed_struct_factory(type_class)
- if Object.const_defined?('ActiveRecord')
- if type_class.ancestors.include?(ActiveRecord::Base)
- qname = XSD::QName.new(@namespace, soap_type_name(type_class.name))
- type_class.instance_variable_set('@qname', qname)
- return SoapActiveRecordStructFactory.new
- end
- end
- SOAP::Mapping::Registry::TypedStructFactory
- end
-
- def mark_typed_array(array, qname)
- (class << array; self; end).class_eval do
- define_method(:arytype) do
- qname
- end
- end
- end
-
- def soap_base_type_name(type)
- xsd_type = type.ancestors.find{ |c| c.const_defined? 'Type' }
- xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
- end
-
- def soap_type_name(type_name)
- type_name.gsub(/::/, '..')
- end
-
- def register_static_factories
- @registry.add(ActionWebService::Base64,
- SOAP::SOAPBase64,
- SoapBase64Factory.new,
- nil)
- mapping = @registry.find_mapped_soap_class(ActionWebService::Base64)
- @type2binding[ActionWebService::Base64] =
- SoapBinding.new(self, SOAP::SOAPBase64::Type,
- ActionWebService::Base64, mapping)
- @registry.add(Array,
- SOAP::SOAPArray,
- SoapTypedArrayFactory.new,
- nil)
- end
- end
-
- class SoapBinding
- attr :qname
- attr :type
- attr :mapping
- attr :element_binding
-
- def initialize(marshaler, qname, type, mapping, element_binding=nil)
- @marshaler = marshaler
- @qname = qname
- @type = type
- @mapping = mapping
- @element_binding = element_binding
- end
-
- def type_name
- @type.custom? ? @qname.name : nil
- end
-
- def qualified_type_name(ns=nil)
- if @type.custom?
- "#{ns ? ns : @qname.namespace}:#{@qname.name}"
- else
- ns = XSD::NS.new
- ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
- ns.assign(SOAP::EncodingNamespace, "soapenc")
- xsd_klass = mapping[0].ancestors.find{|c| c.const_defined?('Type')}
- return ns.name(XSD::AnyTypeName) unless xsd_klass
- ns.name(xsd_klass.const_get('Type'))
- end
- end
-
- def eql?(other)
- @qname == other.qname
- end
- alias :== :eql?
-
- def hash
- @qname.hash
- end
- end
-
- class SoapActiveRecordStructFactory < SOAP::Mapping::Factory
- def obj2soap(soap_class, obj, info, map)
- unless obj.is_a?(ActiveRecord::Base)
- return nil
- end
- soap_obj = soap_class.new(obj.class.instance_variable_get('@qname'))
- obj.class.columns.each do |column|
- key = column.name.to_s
- value = obj.send(key)
- soap_obj[key] = SOAP::Mapping._obj2soap(value, map)
- end
- soap_obj
- end
-
- def soap2obj(obj_class, node, info, map)
- unless node.type == obj_class.instance_variable_get('@qname')
- return false
- end
- obj = obj_class.new
- node.each do |key, value|
- obj[key] = value.data
- end
- obj.instance_variable_set('@new_record', false)
- return true, obj
- end
- end
-
- class SoapTypedArrayFactory < SOAP::Mapping::Factory
- def obj2soap(soap_class, obj, info, map)
- unless obj.respond_to?(:arytype)
- return nil
- end
- soap_obj = soap_class.new(SOAP::ValueArrayName, 1, obj.arytype)
- mark_marshalled_obj(obj, soap_obj)
- obj.each do |item|
- child = SOAP::Mapping._obj2soap(item, map)
- soap_obj.add(child)
- end
- soap_obj
- end
-
- def soap2obj(obj_class, node, info, map)
- return false
- end
- end
-
- class SoapBase64Factory < SOAP::Mapping::Factory
- def obj2soap(soap_class, obj, info, map)
- unless obj.is_a?(ActionWebService::Base64)
- return nil
- end
- return soap_class.new(obj)
- end
-
- def soap2obj(obj_class, node, info, map)
- unless node.type == SOAP::SOAPBase64::Type
- return false
- end
- return true, obj_class.new(node.string)
- end
- end
-
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb b/vendor/rails/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
deleted file mode 100644
index a3abc6a2..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-require 'xmlrpc/marshal'
-require 'action_web_service/client/xmlrpc_client'
-
-module XMLRPC # :nodoc:
- class FaultException # :nodoc:
- alias :message :faultString
- end
-end
-
-module ActionWebService # :nodoc:
- module API # :nodoc:
- class Base # :nodoc:
- def self.xmlrpc_client(endpoint_uri, options={})
- ActionWebService::Client::XmlRpc.new self, endpoint_uri, options
- end
- end
- end
-
- module Protocol # :nodoc:
- module XmlRpc # :nodoc:
- def self.included(base)
- base.register_protocol(XmlRpcProtocol)
- end
-
- class XmlRpcProtocol < AbstractProtocol # :nodoc:
- def self.create(controller)
- XmlRpcProtocol.new
- end
-
- def decode_action_pack_request(action_pack_request)
- service_name = action_pack_request.parameters['action']
- decode_request(action_pack_request.raw_post, service_name)
- end
-
- def decode_request(raw_request, service_name)
- method_name, params = XMLRPC::Marshal.load_call(raw_request)
- Request.new(self, method_name, params, service_name)
- end
-
- def encode_request(method_name, params, param_types)
- if param_types
- params = params.dup
- param_types.each_with_index{ |type, i| params[i] = value_to_xmlrpc_wire_format(params[i], type) }
- end
- XMLRPC::Marshal.dump_call(method_name, *params)
- end
-
- def decode_response(raw_response)
- [nil, XMLRPC::Marshal.load_response(raw_response)]
- end
-
- def encode_response(method_name, return_value, return_type, protocol_options={})
- if return_value && return_type
- return_value = value_to_xmlrpc_wire_format(return_value, return_type)
- end
- return_value = false if return_value.nil?
- raw_response = XMLRPC::Marshal.dump_response(return_value)
- Response.new(raw_response, 'text/xml', return_value)
- end
-
- def encode_multicall_response(responses, protocol_options={})
- result = responses.map do |return_value, return_type|
- if return_value && return_type
- return_value = value_to_xmlrpc_wire_format(return_value, return_type)
- return_value = [return_value] unless return_value.nil?
- end
- return_value = false if return_value.nil?
- return_value
- end
- raw_response = XMLRPC::Marshal.dump_response(result)
- Response.new(raw_response, 'text/xml', result)
- end
-
- def protocol_client(api, protocol_name, endpoint_uri, options={})
- return nil unless protocol_name == :xmlrpc
- ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
- end
-
- def value_to_xmlrpc_wire_format(value, value_type)
- if value_type.array?
- value.map{ |val| value_to_xmlrpc_wire_format(val, value_type.element_type) }
- else
- if value.is_a?(ActionWebService::Struct)
- struct = {}
- value.class.members.each do |name, type|
- member_value = value[name]
- next if member_value.nil?
- struct[name.to_s] = value_to_xmlrpc_wire_format(member_value, type)
- end
- struct
- elsif value.is_a?(ActiveRecord::Base)
- struct = {}
- value.attributes.each do |key, member_value|
- next if member_value.nil?
- struct[key.to_s] = member_value
- end
- struct
- elsif value.is_a?(ActionWebService::Base64)
- XMLRPC::Base64.new(value)
- elsif value.is_a?(Exception) && !value.is_a?(XMLRPC::FaultException)
- XMLRPC::FaultException.new(2, value.message)
- else
- value
- end
- end
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/scaffolding.rb b/vendor/rails/actionwebservice/lib/action_web_service/scaffolding.rb
deleted file mode 100644
index e1098577..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/scaffolding.rb
+++ /dev/null
@@ -1,283 +0,0 @@
-require 'benchmark'
-require 'pathname'
-
-module ActionWebService
- module Scaffolding # :nodoc:
- class ScaffoldingError < ActionWebServiceError # :nodoc:
- end
-
- def self.included(base)
- base.extend(ClassMethods)
- end
-
- # Web service invocation scaffolding provides a way to quickly invoke web service methods in a controller. The
- # generated scaffold actions have default views to let you enter the method parameters and view the
- # results.
- #
- # Example:
- #
- # class ApiController < ActionController
- # web_service_scaffold :invoke
- # end
- #
- # This example generates an +invoke+ action in the +ApiController+ that you can navigate to from
- # your browser, select the API method, enter its parameters, and perform the invocation.
- #
- # If you want to customize the default views, create the following views in "app/views":
- #
- # * action_name/methods.rhtml
- # * action_name/parameters.rhtml
- # * action_name/result.rhtml
- # * action_name/layout.rhtml
- #
- # Where action_name is the name of the action you gave to ClassMethods#web_service_scaffold.
- #
- # You can use the default views in RAILS_DIR/lib/action_web_service/templates/scaffolds as
- # a guide.
- module ClassMethods
- # Generates web service invocation scaffolding for the current controller. The given action name
- # can then be used as the entry point for invoking API methods from a web browser.
- def web_service_scaffold(action_name)
- add_template_helper(Helpers)
- module_eval <<-"end_eval", __FILE__, __LINE__ + 1
- def #{action_name}
- if request.method == :get
- setup_invocation_assigns
- render_invocation_scaffold 'methods'
- end
- end
-
- def #{action_name}_method_params
- if request.method == :get
- setup_invocation_assigns
- render_invocation_scaffold 'parameters'
- end
- end
-
- def #{action_name}_submit
- if request.method == :post
- setup_invocation_assigns
- protocol_name = params['protocol'] ? params['protocol'].to_sym : :soap
- case protocol_name
- when :soap
- @protocol = Protocol::Soap::SoapProtocol.create(self)
- when :xmlrpc
- @protocol = Protocol::XmlRpc::XmlRpcProtocol.create(self)
- end
- bm = Benchmark.measure do
- @protocol.register_api(@scaffold_service.api)
- post_params = params['method_params'] ? params['method_params'].dup : nil
- params = []
- @scaffold_method.expects.each_with_index do |spec, i|
- params << post_params[i.to_s]
- end if @scaffold_method.expects
- params = @scaffold_method.cast_expects(params)
- method_name = public_method_name(@scaffold_service.name, @scaffold_method.public_name)
- @method_request_xml = @protocol.encode_request(method_name, params, @scaffold_method.expects)
- new_request = @protocol.encode_action_pack_request(@scaffold_service.name, @scaffold_method.public_name, @method_request_xml)
- prepare_request(new_request, @scaffold_service.name, @scaffold_method.public_name)
- self.request = new_request
- if @scaffold_container.dispatching_mode != :direct
- request.parameters['action'] = @scaffold_service.name
- end
- dispatch_web_service_request
- @method_response_xml = response.body
- method_name, obj = @protocol.decode_response(@method_response_xml)
- return if handle_invocation_exception(obj)
- @method_return_value = @scaffold_method.cast_returns(obj)
- end
- @method_elapsed = bm.real
- add_instance_variables_to_assigns
- reset_invocation_response
- render_invocation_scaffold 'result'
- end
- end
-
- private
- def setup_invocation_assigns
- @scaffold_class = self.class
- @scaffold_action_name = "#{action_name}"
- @scaffold_container = WebServiceModel::Container.new(self)
- if params['service'] && params['method']
- @scaffold_service = @scaffold_container.services.find{ |x| x.name == params['service'] }
- @scaffold_method = @scaffold_service.api_methods[params['method']]
- end
- add_instance_variables_to_assigns
- end
-
- def render_invocation_scaffold(action)
- customized_template = "\#{self.class.controller_path}/#{action_name}/\#{action}"
- default_template = scaffold_path(action)
- if template_exists?(customized_template)
- content = @template.render_file(customized_template)
- else
- content = @template.render_file(default_template, false)
- end
- @template.instance_variable_set("@content_for_layout", content)
- if self.active_layout.nil?
- render_file(scaffold_path("layout"))
- else
- render_file(self.active_layout, "200 OK", true)
- end
- end
-
- def scaffold_path(template_name)
- File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".rhtml"
- end
-
- def reset_invocation_response
- erase_render_results
- response.headers = ::ActionController::AbstractResponse::DEFAULT_HEADERS.merge("cookie" => [])
- end
-
- def public_method_name(service_name, method_name)
- if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::XmlRpc::XmlRpcProtocol)
- service_name + '.' + method_name
- else
- method_name
- end
- end
-
- def prepare_request(new_request, service_name, method_name)
- new_request.parameters.update(request.parameters)
- request.env.each{ |k, v| new_request.env[k] = v unless new_request.env.has_key?(k) }
- if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::Soap::SoapProtocol)
- new_request.env['HTTP_SOAPACTION'] = "/\#{controller_name()}/\#{service_name}/\#{method_name}"
- end
- end
-
- def handle_invocation_exception(obj)
- exception = nil
- if obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && obj.detail.cause.is_a?(Exception)
- exception = obj.detail.cause
- elsif obj.is_a?(XMLRPC::FaultException)
- exception = obj
- end
- return unless exception
- reset_invocation_response
- rescue_action(exception)
- true
- end
- end_eval
- end
- end
-
- module Helpers # :nodoc:
- def method_parameter_input_fields(method, type, field_name_base, idx, was_structured=false)
- if type.array?
- return content_tag('em', "Typed array input fields not supported yet (#{type.name})")
- end
- if type.structured?
- return content_tag('em', "Nested structural types not supported yet (#{type.name})") if was_structured
- parameters = ""
- type.each_member do |member_name, member_type|
- label = method_parameter_label(member_name, member_type)
- nested_content = method_parameter_input_fields(
- method,
- member_type,
- "#{field_name_base}[#{idx}][#{member_name}]",
- idx,
- true)
- if member_type.custom?
- parameters << content_tag('li', label)
- parameters << content_tag('ul', nested_content)
- else
- parameters << content_tag('li', label + ' ' + nested_content)
- end
- end
- content_tag('ul', parameters)
- else
- # If the data source was structured previously we already have the index set
- field_name_base = "#{field_name_base}[#{idx}]" unless was_structured
-
- case type.type
- when :int
- text_field_tag "#{field_name_base}"
- when :string
- text_field_tag "#{field_name_base}"
- when :base64
- text_area_tag "#{field_name_base}", nil, :size => "40x5"
- when :bool
- radio_button_tag("#{field_name_base}", "true") + " True" +
- radio_button_tag("#{field_name_base}", "false") + "False"
- when :float
- text_field_tag "#{field_name_base}"
- when :time, :datetime
- time = Time.now
- i = 0
- %w|year month day hour minute second|.map do |name|
- i += 1
- send("select_#{name}", time, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
- end.join
- when :date
- date = Date.today
- i = 0
- %w|year month day|.map do |name|
- i += 1
- send("select_#{name}", date, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
- end.join
- end
- end
- end
-
- def method_parameter_label(name, type)
- name.to_s.capitalize + ' (' + type.human_name(false) + ')'
- end
-
- def service_method_list(service)
- action = @scaffold_action_name + '_method_params'
- methods = service.api_methods_full.map do |desc, name|
- content_tag("li", link_to(desc, :action => action, :service => service.name, :method => name))
- end
- content_tag("ul", methods.join("\n"))
- end
- end
-
- module WebServiceModel # :nodoc:
- class Container # :nodoc:
- attr :services
- attr :dispatching_mode
-
- def initialize(real_container)
- @real_container = real_container
- @dispatching_mode = @real_container.class.web_service_dispatching_mode
- @services = []
- if @dispatching_mode == :direct
- @services << Service.new(@real_container.controller_name, @real_container)
- else
- @real_container.class.web_services.each do |name, obj|
- @services << Service.new(name, @real_container.instance_eval{ web_service_object(name) })
- end
- end
- end
- end
-
- class Service # :nodoc:
- attr :name
- attr :object
- attr :api
- attr :api_methods
- attr :api_methods_full
-
- def initialize(name, real_service)
- @name = name.to_s
- @object = real_service
- @api = @object.class.web_service_api
- if @api.nil?
- raise ScaffoldingError, "No web service API attached to #{object.class}"
- end
- @api_methods = {}
- @api_methods_full = []
- @api.api_methods.each do |name, method|
- @api_methods[method.public_name.to_s] = method
- @api_methods_full << [method.to_s, method.public_name.to_s]
- end
- end
-
- def to_s
- self.name.camelize
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/struct.rb b/vendor/rails/actionwebservice/lib/action_web_service/struct.rb
deleted file mode 100644
index af93f65a..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/struct.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-module ActionWebService
- # To send structured types across the wire, derive from ActionWebService::Struct,
- # and use +member+ to declare structure members.
- #
- # ActionWebService::Struct should be used in method signatures when you want to accept or return
- # structured types that have no Active Record model class representations, or you don't
- # want to expose your entire Active Record model to remote callers.
- #
- # === Example
- #
- # class Person < ActionWebService::Struct
- # member :id, :int
- # member :firstnames, [:string]
- # member :lastname, :string
- # member :email, :string
- # end
- # person = Person.new(:id => 5, :firstname => 'john', :lastname => 'doe')
- #
- # Active Record model classes are already implicitly supported in method
- # signatures.
- class Struct
- # Action WebService Struct subclasses should be reloaded by the dispatcher in Rails
- # when Dependencies.mechanism = :load.
- include Reloadable::Deprecated
-
- # If a Hash is given as argument to an ActionWebService::Struct constructor,
- # it can contain initial values for the structure member.
- def initialize(values={})
- if values.is_a?(Hash)
- values.map{|k,v| __send__('%s=' % k.to_s, v)}
- end
- end
-
- # The member with the given name
- def [](name)
- send(name.to_s)
- end
-
- # Iterates through each member
- def each_pair(&block)
- self.class.members.each do |name, type|
- yield name, self.__send__(name)
- end
- end
-
- class << self
- # Creates a structure member with the specified +name+ and +type+. Generates
- # accessor methods for reading and writing the member value.
- def member(name, type)
- name = name.to_sym
- type = ActionWebService::SignatureTypes.canonical_signature_entry({ name => type }, 0)
- write_inheritable_hash("struct_members", name => type)
- class_eval <<-END
- def #{name}; @#{name}; end
- def #{name}=(value); @#{name} = value; end
- END
- end
-
- def members # :nodoc:
- read_inheritable_attribute("struct_members") || {}
- end
-
- def member_type(name) # :nodoc:
- members[name.to_sym]
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/support/class_inheritable_options.rb b/vendor/rails/actionwebservice/lib/action_web_service/support/class_inheritable_options.rb
deleted file mode 100644
index 4d1c2ed4..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/support/class_inheritable_options.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-class Class # :nodoc:
- def class_inheritable_option(sym, default_value=nil)
- write_inheritable_attribute sym, default_value
- class_eval <<-EOS
- def self.#{sym}(value=nil)
- if !value.nil?
- write_inheritable_attribute(:#{sym}, value)
- else
- read_inheritable_attribute(:#{sym})
- end
- end
-
- def self.#{sym}=(value)
- write_inheritable_attribute(:#{sym}, value)
- end
-
- def #{sym}
- self.class.#{sym}
- end
-
- def #{sym}=(value)
- self.class.#{sym} = value
- end
- EOS
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/support/signature_types.rb b/vendor/rails/actionwebservice/lib/action_web_service/support/signature_types.rb
deleted file mode 100644
index 36224c64..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/support/signature_types.rb
+++ /dev/null
@@ -1,222 +0,0 @@
-module ActionWebService # :nodoc:
- # Action Web Service supports the following base types in a signature:
- #
- # [:int ] Represents an integer value, will be cast to an integer using Integer(value)
- # [:string ] Represents a string value, will be cast to an string using the to_s method on an object
- # [:base64 ] Represents a Base 64 value, will contain the binary bytes of a Base 64 value sent by the caller
- # [:bool ] Represents a boolean value, whatever is passed will be cast to boolean (true , '1', 'true', 'y', 'yes' are taken to represent true; false , '0', 'false', 'n', 'no' and nil represent false)
- # [:float ] Represents a floating point value, will be cast to a float using Float(value)
- # [:time ] Represents a timestamp, will be cast to a Time object
- # [:datetime ] Represents a timestamp, will be cast to a DateTime object
- # [:date ] Represents a date, will be cast to a Date object
- #
- # For structured types, you'll need to pass in the Class objects of
- # ActionWebService::Struct and ActiveRecord::Base derivatives.
- module SignatureTypes
- def canonical_signature(signature) # :nodoc:
- return nil if signature.nil?
- unless signature.is_a?(Array)
- raise(ActionWebServiceError, "Expected signature to be an Array")
- end
- i = -1
- signature.map{ |spec| canonical_signature_entry(spec, i += 1) }
- end
-
- def canonical_signature_entry(spec, i) # :nodoc:
- orig_spec = spec
- name = "param#{i}"
- if spec.is_a?(Hash)
- name, spec = spec.keys.first, spec.values.first
- end
- type = spec
- if spec.is_a?(Array)
- ArrayType.new(orig_spec, canonical_signature_entry(spec[0], 0), name)
- else
- type = canonical_type(type)
- if type.is_a?(Symbol)
- BaseType.new(orig_spec, type, name)
- else
- StructuredType.new(orig_spec, type, name)
- end
- end
- end
-
- def canonical_type(type) # :nodoc:
- type_name = symbol_name(type) || class_to_type_name(type)
- type = type_name || type
- return canonical_type_name(type) if type.is_a?(Symbol)
- type
- end
-
- def canonical_type_name(name) # :nodoc:
- name = name.to_sym
- case name
- when :int, :integer, :fixnum, :bignum
- :int
- when :string, :text
- :string
- when :base64, :binary
- :base64
- when :bool, :boolean
- :bool
- when :float, :double
- :float
- when :time, :timestamp
- :time
- when :datetime
- :datetime
- when :date
- :date
- else
- raise(TypeError, "#{name} is not a valid base type")
- end
- end
-
- def canonical_type_class(type) # :nodoc:
- type = canonical_type(type)
- type.is_a?(Symbol) ? type_name_to_class(type) : type
- end
-
- def symbol_name(name) # :nodoc:
- return name.to_sym if name.is_a?(Symbol) || name.is_a?(String)
- nil
- end
-
- def class_to_type_name(klass) # :nodoc:
- klass = klass.class unless klass.is_a?(Class)
- if derived_from?(Integer, klass) || derived_from?(Fixnum, klass) || derived_from?(Bignum, klass)
- :int
- elsif klass == String
- :string
- elsif klass == Base64
- :base64
- elsif klass == TrueClass || klass == FalseClass
- :bool
- elsif derived_from?(Float, klass) || derived_from?(Precision, klass) || derived_from?(Numeric, klass)
- :float
- elsif klass == Time
- :time
- elsif klass == DateTime
- :datetime
- elsif klass == Date
- :date
- else
- nil
- end
- end
-
- def type_name_to_class(name) # :nodoc:
- case canonical_type_name(name)
- when :int
- Integer
- when :string
- String
- when :base64
- Base64
- when :bool
- TrueClass
- when :float
- Float
- when :time
- Time
- when :date
- Date
- when :datetime
- DateTime
- else
- nil
- end
- end
-
- def derived_from?(ancestor, child) # :nodoc:
- child.ancestors.include?(ancestor)
- end
-
- module_function :type_name_to_class
- module_function :class_to_type_name
- module_function :symbol_name
- module_function :canonical_type_class
- module_function :canonical_type_name
- module_function :canonical_type
- module_function :canonical_signature_entry
- module_function :canonical_signature
- module_function :derived_from?
- end
-
- class BaseType # :nodoc:
- include SignatureTypes
-
- attr :spec
- attr :type
- attr :type_class
- attr :name
-
- def initialize(spec, type, name)
- @spec = spec
- @type = canonical_type(type)
- @type_class = canonical_type_class(@type)
- @name = name
- end
-
- def custom?
- false
- end
-
- def array?
- false
- end
-
- def structured?
- false
- end
-
- def human_name(show_name=true)
- type_type = array? ? element_type.type.to_s : self.type.to_s
- str = array? ? (type_type + '[]') : type_type
- show_name ? (str + " " + name.to_s) : str
- end
- end
-
- class ArrayType < BaseType # :nodoc:
- attr :element_type
-
- def initialize(spec, element_type, name)
- super(spec, Array, name)
- @element_type = element_type
- end
-
- def custom?
- true
- end
-
- def array?
- true
- end
- end
-
- class StructuredType < BaseType # :nodoc:
- def each_member
- if @type_class.respond_to?(:members)
- @type_class.members.each do |name, type|
- yield name, type
- end
- elsif @type_class.respond_to?(:columns)
- i = -1
- @type_class.columns.each do |column|
- yield column.name, canonical_signature_entry(column.type, i += 1)
- end
- end
- end
-
- def custom?
- true
- end
-
- def structured?
- true
- end
- end
-
- class Base64 < String # :nodoc:
- end
-end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/layout.rhtml b/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/layout.rhtml
deleted file mode 100644
index 167613f6..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/layout.rhtml
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
- <%= @scaffold_class.wsdl_service_name %> Web Service
-
-
-
-
-<%= @content_for_layout %>
-
-
-
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/methods.rhtml b/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/methods.rhtml
deleted file mode 100644
index 60dfe23f..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/methods.rhtml
+++ /dev/null
@@ -1,6 +0,0 @@
-<% @scaffold_container.services.each do |service| %>
-
- API Methods for <%= service %>
- <%= service_method_list(service) %>
-
-<% end %>
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/parameters.rhtml b/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/parameters.rhtml
deleted file mode 100644
index 767284e0..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/parameters.rhtml
+++ /dev/null
@@ -1,29 +0,0 @@
-Method Invocation Details for <%= @scaffold_service %>#<%= @scaffold_method.public_name %>
-
-<% form_tag(:action => @scaffold_action_name + '_submit') do -%>
-<%= hidden_field_tag "service", @scaffold_service.name %>
-<%= hidden_field_tag "method", @scaffold_method.public_name %>
-
-
-Protocol:
-<%= select_tag 'protocol', options_for_select([['SOAP', 'soap'], ['XML-RPC', 'xmlrpc']], params['protocol']) %>
-
-
-<% if @scaffold_method.expects %>
-
-Method Parameters:
-<% @scaffold_method.expects.each_with_index do |type, i| %>
-
- <%= method_parameter_label(type.name, type) %>
- <%= method_parameter_input_fields(@scaffold_method, type, "method_params", i) %>
-
-<% end %>
-
-<% end %>
-
-<%= submit_tag "Invoke" %>
-<% end -%>
-
-
-<%= link_to "Back", :action => @scaffold_action_name %>
-
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/result.rhtml b/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/result.rhtml
deleted file mode 100644
index 5317688f..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/templates/scaffolds/result.rhtml
+++ /dev/null
@@ -1,30 +0,0 @@
-Method Invocation Result for <%= @scaffold_service %>#<%= @scaffold_method.public_name %>
-
-
-Invocation took <%= '%f' % @method_elapsed %> seconds
-
-
-
-Return Value:
-
-<%= h @method_return_value.inspect %>
-
-
-
-
-Request XML:
-
-<%= h @method_request_xml %>
-
-
-
-
-Response XML:
-
-<%= h @method_response_xml %>
-
-
-
-
-<%= link_to "Back", :action => @scaffold_action_name + '_method_params', :method => @scaffold_method.public_name, :service => @scaffold_service.name %>
-
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/test_invoke.rb b/vendor/rails/actionwebservice/lib/action_web_service/test_invoke.rb
deleted file mode 100644
index 7e714c94..00000000
--- a/vendor/rails/actionwebservice/lib/action_web_service/test_invoke.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-require 'test/unit'
-
-module Test # :nodoc:
- module Unit # :nodoc:
- class TestCase # :nodoc:
- private
- # invoke the specified API method
- def invoke_direct(method_name, *args)
- prepare_request('api', 'api', method_name, *args)
- @controller.process(@request, @response)
- decode_rpc_response
- end
- alias_method :invoke, :invoke_direct
-
- # invoke the specified API method on the specified service
- def invoke_delegated(service_name, method_name, *args)
- prepare_request(service_name.to_s, service_name, method_name, *args)
- @controller.process(@request, @response)
- decode_rpc_response
- end
-
- # invoke the specified layered API method on the correct service
- def invoke_layered(service_name, method_name, *args)
- prepare_request('api', service_name, method_name, *args)
- @controller.process(@request, @response)
- decode_rpc_response
- end
-
- # ---------------------- internal ---------------------------
-
- def prepare_request(action, service_name, api_method_name, *args)
- @request.recycle!
- @request.request_parameters['action'] = action
- @request.env['REQUEST_METHOD'] = 'POST'
- @request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
- @request.env['RAW_POST_DATA'] = encode_rpc_call(service_name, api_method_name, *args)
- case protocol
- when ActionWebService::Protocol::Soap::SoapProtocol
- soap_action = "/#{@controller.controller_name}/#{service_name}/#{public_method_name(service_name, api_method_name)}"
- @request.env['HTTP_SOAPACTION'] = soap_action
- when ActionWebService::Protocol::XmlRpc::XmlRpcProtocol
- @request.env.delete('HTTP_SOAPACTION')
- end
- end
-
- def encode_rpc_call(service_name, api_method_name, *args)
- case @controller.web_service_dispatching_mode
- when :direct
- api = @controller.class.web_service_api
- when :delegated, :layered
- api = @controller.web_service_object(service_name.to_sym).class.web_service_api
- end
- protocol.register_api(api)
- method = api.api_methods[api_method_name.to_sym]
- raise ArgumentError, "wrong number of arguments for rpc call (#{args.length} for #{method.expects.length})" if method && method.expects && args.length != method.expects.length
- protocol.encode_request(public_method_name(service_name, api_method_name), args.dup, method.expects)
- end
-
- def decode_rpc_response
- public_method_name, return_value = protocol.decode_response(@response.body)
- exception = is_exception?(return_value)
- raise exception if exception
- return_value
- end
-
- def public_method_name(service_name, api_method_name)
- public_name = service_api(service_name).public_api_method_name(api_method_name)
- if @controller.web_service_dispatching_mode == :layered && protocol.is_a?(ActionWebService::Protocol::XmlRpc::XmlRpcProtocol)
- '%s.%s' % [service_name.to_s, public_name]
- else
- public_name
- end
- end
-
- def service_api(service_name)
- case @controller.web_service_dispatching_mode
- when :direct
- @controller.class.web_service_api
- when :delegated, :layered
- @controller.web_service_object(service_name.to_sym).class.web_service_api
- end
- end
-
- def protocol
- if @protocol.nil?
- @protocol ||= ActionWebService::Protocol::Soap::SoapProtocol.create(@controller)
- else
- case @protocol
- when :xmlrpc
- @protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.create(@controller)
- when :soap
- @protocol = ActionWebService::Protocol::Soap::SoapProtocol.create(@controller)
- else
- @protocol
- end
- end
- end
-
- def is_exception?(obj)
- case protocol
- when :soap, ActionWebService::Protocol::Soap::SoapProtocol
- (obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && \
- obj.detail.cause.is_a?(Exception)) ? obj.detail.cause : nil
- when :xmlrpc, ActionWebService::Protocol::XmlRpc::XmlRpcProtocol
- obj.is_a?(XMLRPC::FaultException) ? obj : nil
- end
- end
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/setup.rb b/vendor/rails/actionwebservice/setup.rb
deleted file mode 100644
index 9ab880d3..00000000
--- a/vendor/rails/actionwebservice/setup.rb
+++ /dev/null
@@ -1,1379 +0,0 @@
-#
-# setup.rb
-#
-# Copyright (c) 2000-2004 Minero Aoki
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
-# with permission of Minero Aoki.
-
-#
-
-unless Enumerable.method_defined?(:map) # Ruby 1.4.6
- module Enumerable
- alias map collect
- end
-end
-
-unless File.respond_to?(:read) # Ruby 1.6
- def File.read(fname)
- open(fname) {|f|
- return f.read
- }
- end
-end
-
-def File.binread(fname)
- open(fname, 'rb') {|f|
- return f.read
- }
-end
-
-# for corrupted windows stat(2)
-def File.dir?(path)
- File.directory?((path[-1,1] == '/') ? path : path + '/')
-end
-
-
-class SetupError < StandardError; end
-
-def setup_rb_error(msg)
- raise SetupError, msg
-end
-
-#
-# Config
-#
-
-if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
- ARGV.delete(arg)
- require arg.split(/=/, 2)[1]
- $".push 'rbconfig.rb'
-else
- require 'rbconfig'
-end
-
-def multipackage_install?
- FileTest.directory?(File.dirname($0) + '/packages')
-end
-
-
-class ConfigItem
- def initialize(name, template, default, desc)
- @name = name.freeze
- @template = template
- @value = default
- @default = default.dup.freeze
- @description = desc
- end
-
- attr_reader :name
- attr_reader :description
-
- attr_accessor :default
- alias help_default default
-
- def help_opt
- "--#{@name}=#{@template}"
- end
-
- def value
- @value
- end
-
- def eval(table)
- @value.gsub(%r<\$([^/]+)>) { table[$1] }
- end
-
- def set(val)
- @value = check(val)
- end
-
- private
-
- def check(val)
- setup_rb_error "config: --#{name} requires argument" unless val
- val
- end
-end
-
-class BoolItem < ConfigItem
- def config_type
- 'bool'
- end
-
- def help_opt
- "--#{@name}"
- end
-
- private
-
- def check(val)
- return 'yes' unless val
- unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
- setup_rb_error "config: --#{@name} accepts only yes/no for argument"
- end
- (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
- end
-end
-
-class PathItem < ConfigItem
- def config_type
- 'path'
- end
-
- private
-
- def check(path)
- setup_rb_error "config: --#{@name} requires argument" unless path
- path[0,1] == '$' ? path : File.expand_path(path)
- end
-end
-
-class ProgramItem < ConfigItem
- def config_type
- 'program'
- end
-end
-
-class SelectItem < ConfigItem
- def initialize(name, template, default, desc)
- super
- @ok = template.split('/')
- end
-
- def config_type
- 'select'
- end
-
- private
-
- def check(val)
- unless @ok.include?(val.strip)
- setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
- end
- val.strip
- end
-end
-
-class PackageSelectionItem < ConfigItem
- def initialize(name, template, default, help_default, desc)
- super name, template, default, desc
- @help_default = help_default
- end
-
- attr_reader :help_default
-
- def config_type
- 'package'
- end
-
- private
-
- def check(val)
- unless File.dir?("packages/#{val}")
- setup_rb_error "config: no such package: #{val}"
- end
- val
- end
-end
-
-class ConfigTable_class
-
- def initialize(items)
- @items = items
- @table = {}
- items.each do |i|
- @table[i.name] = i
- end
- ALIASES.each do |ali, name|
- @table[ali] = @table[name]
- end
- end
-
- include Enumerable
-
- def each(&block)
- @items.each(&block)
- end
-
- def key?(name)
- @table.key?(name)
- end
-
- def lookup(name)
- @table[name] or raise ArgumentError, "no such config item: #{name}"
- end
-
- def add(item)
- @items.push item
- @table[item.name] = item
- end
-
- def remove(name)
- item = lookup(name)
- @items.delete_if {|i| i.name == name }
- @table.delete_if {|name, i| i.name == name }
- item
- end
-
- def new
- dup()
- end
-
- def savefile
- '.config'
- end
-
- def load
- begin
- t = dup()
- File.foreach(savefile()) do |line|
- k, v = *line.split(/=/, 2)
- t[k] = v.strip
- end
- t
- rescue Errno::ENOENT
- setup_rb_error $!.message + "#{File.basename($0)} config first"
- end
- end
-
- def save
- @items.each {|i| i.value }
- File.open(savefile(), 'w') {|f|
- @items.each do |i|
- f.printf "%s=%s\n", i.name, i.value if i.value
- end
- }
- end
-
- def [](key)
- lookup(key).eval(self)
- end
-
- def []=(key, val)
- lookup(key).set val
- end
-
-end
-
-c = ::Config::CONFIG
-
-rubypath = c['bindir'] + '/' + c['ruby_install_name']
-
-major = c['MAJOR'].to_i
-minor = c['MINOR'].to_i
-teeny = c['TEENY'].to_i
-version = "#{major}.#{minor}"
-
-# ruby ver. >= 1.4.4?
-newpath_p = ((major >= 2) or
- ((major == 1) and
- ((minor >= 5) or
- ((minor == 4) and (teeny >= 4)))))
-
-if c['rubylibdir']
- # V < 1.6.3
- _stdruby = c['rubylibdir']
- _siteruby = c['sitedir']
- _siterubyver = c['sitelibdir']
- _siterubyverarch = c['sitearchdir']
-elsif newpath_p
- # 1.4.4 <= V <= 1.6.3
- _stdruby = "$prefix/lib/ruby/#{version}"
- _siteruby = c['sitedir']
- _siterubyver = "$siteruby/#{version}"
- _siterubyverarch = "$siterubyver/#{c['arch']}"
-else
- # V < 1.4.4
- _stdruby = "$prefix/lib/ruby/#{version}"
- _siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
- _siterubyver = _siteruby
- _siterubyverarch = "$siterubyver/#{c['arch']}"
-end
-libdir = '-* dummy libdir *-'
-stdruby = '-* dummy rubylibdir *-'
-siteruby = '-* dummy site_ruby *-'
-siterubyver = '-* dummy site_ruby version *-'
-parameterize = lambda {|path|
- path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
- .sub(/\A#{Regexp.quote(libdir)}/, '$libdir')\
- .sub(/\A#{Regexp.quote(stdruby)}/, '$stdruby')\
- .sub(/\A#{Regexp.quote(siteruby)}/, '$siteruby')\
- .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
-}
-libdir = parameterize.call(c['libdir'])
-stdruby = parameterize.call(_stdruby)
-siteruby = parameterize.call(_siteruby)
-siterubyver = parameterize.call(_siterubyver)
-siterubyverarch = parameterize.call(_siterubyverarch)
-
-if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
- makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
-else
- makeprog = 'make'
-end
-
-common_conf = [
- PathItem.new('prefix', 'path', c['prefix'],
- 'path prefix of target environment'),
- PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
- 'the directory for commands'),
- PathItem.new('libdir', 'path', libdir,
- 'the directory for libraries'),
- PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
- 'the directory for shared data'),
- PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
- 'the directory for man pages'),
- PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
- 'the directory for man pages'),
- PathItem.new('stdruby', 'path', stdruby,
- 'the directory for standard ruby libraries'),
- PathItem.new('siteruby', 'path', siteruby,
- 'the directory for version-independent aux ruby libraries'),
- PathItem.new('siterubyver', 'path', siterubyver,
- 'the directory for aux ruby libraries'),
- PathItem.new('siterubyverarch', 'path', siterubyverarch,
- 'the directory for aux ruby binaries'),
- PathItem.new('rbdir', 'path', '$siterubyver',
- 'the directory for ruby scripts'),
- PathItem.new('sodir', 'path', '$siterubyverarch',
- 'the directory for ruby extentions'),
- PathItem.new('rubypath', 'path', rubypath,
- 'the path to set to #! line'),
- ProgramItem.new('rubyprog', 'name', rubypath,
- 'the ruby program using for installation'),
- ProgramItem.new('makeprog', 'name', makeprog,
- 'the make program to compile ruby extentions'),
- SelectItem.new('shebang', 'all/ruby/never', 'ruby',
- 'shebang line (#!) editing mode'),
- BoolItem.new('without-ext', 'yes/no', 'no',
- 'does not compile/install ruby extentions')
-]
-class ConfigTable_class # open again
- ALIASES = {
- 'std-ruby' => 'stdruby',
- 'site-ruby-common' => 'siteruby', # For backward compatibility
- 'site-ruby' => 'siterubyver', # For backward compatibility
- 'bin-dir' => 'bindir',
- 'bin-dir' => 'bindir',
- 'rb-dir' => 'rbdir',
- 'so-dir' => 'sodir',
- 'data-dir' => 'datadir',
- 'ruby-path' => 'rubypath',
- 'ruby-prog' => 'rubyprog',
- 'ruby' => 'rubyprog',
- 'make-prog' => 'makeprog',
- 'make' => 'makeprog'
- }
-end
-multipackage_conf = [
- PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
- 'package names that you want to install'),
- PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
- 'package names that you do not want to install')
-]
-if multipackage_install?
- ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
-else
- ConfigTable = ConfigTable_class.new(common_conf)
-end
-
-
-module MetaConfigAPI
-
- def eval_file_ifexist(fname)
- instance_eval File.read(fname), fname, 1 if File.file?(fname)
- end
-
- def config_names
- ConfigTable.map {|i| i.name }
- end
-
- def config?(name)
- ConfigTable.key?(name)
- end
-
- def bool_config?(name)
- ConfigTable.lookup(name).config_type == 'bool'
- end
-
- def path_config?(name)
- ConfigTable.lookup(name).config_type == 'path'
- end
-
- def value_config?(name)
- case ConfigTable.lookup(name).config_type
- when 'bool', 'path'
- true
- else
- false
- end
- end
-
- def add_config(item)
- ConfigTable.add item
- end
-
- def add_bool_config(name, default, desc)
- ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
- end
-
- def add_path_config(name, default, desc)
- ConfigTable.add PathItem.new(name, 'path', default, desc)
- end
-
- def set_config_default(name, default)
- ConfigTable.lookup(name).default = default
- end
-
- def remove_config(name)
- ConfigTable.remove(name)
- end
-
-end
-
-
-#
-# File Operations
-#
-
-module FileOperations
-
- def mkdir_p(dirname, prefix = nil)
- dirname = prefix + File.expand_path(dirname) if prefix
- $stderr.puts "mkdir -p #{dirname}" if verbose?
- return if no_harm?
-
- # does not check '/'... it's too abnormal case
- dirs = File.expand_path(dirname).split(%r<(?=/)>)
- if /\A[a-z]:\z/i =~ dirs[0]
- disk = dirs.shift
- dirs[0] = disk + dirs[0]
- end
- dirs.each_index do |idx|
- path = dirs[0..idx].join('')
- Dir.mkdir path unless File.dir?(path)
- end
- end
-
- def rm_f(fname)
- $stderr.puts "rm -f #{fname}" if verbose?
- return if no_harm?
-
- if File.exist?(fname) or File.symlink?(fname)
- File.chmod 0777, fname
- File.unlink fname
- end
- end
-
- def rm_rf(dn)
- $stderr.puts "rm -rf #{dn}" if verbose?
- return if no_harm?
-
- Dir.chdir dn
- Dir.foreach('.') do |fn|
- next if fn == '.'
- next if fn == '..'
- if File.dir?(fn)
- verbose_off {
- rm_rf fn
- }
- else
- verbose_off {
- rm_f fn
- }
- end
- end
- Dir.chdir '..'
- Dir.rmdir dn
- end
-
- def move_file(src, dest)
- File.unlink dest if File.exist?(dest)
- begin
- File.rename src, dest
- rescue
- File.open(dest, 'wb') {|f| f.write File.binread(src) }
- File.chmod File.stat(src).mode, dest
- File.unlink src
- end
- end
-
- def install(from, dest, mode, prefix = nil)
- $stderr.puts "install #{from} #{dest}" if verbose?
- return if no_harm?
-
- realdest = prefix ? prefix + File.expand_path(dest) : dest
- realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
- str = File.binread(from)
- if diff?(str, realdest)
- verbose_off {
- rm_f realdest if File.exist?(realdest)
- }
- File.open(realdest, 'wb') {|f|
- f.write str
- }
- File.chmod mode, realdest
-
- File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
- if prefix
- f.puts realdest.sub(prefix, '')
- else
- f.puts realdest
- end
- }
- end
- end
-
- def diff?(new_content, path)
- return true unless File.exist?(path)
- new_content != File.binread(path)
- end
-
- def command(str)
- $stderr.puts str if verbose?
- system str or raise RuntimeError, "'system #{str}' failed"
- end
-
- def ruby(str)
- command config('rubyprog') + ' ' + str
- end
-
- def make(task = '')
- command config('makeprog') + ' ' + task
- end
-
- def extdir?(dir)
- File.exist?(dir + '/MANIFEST')
- end
-
- def all_files_in(dirname)
- Dir.open(dirname) {|d|
- return d.select {|ent| File.file?("#{dirname}/#{ent}") }
- }
- end
-
- REJECT_DIRS = %w(
- CVS SCCS RCS CVS.adm .svn
- )
-
- def all_dirs_in(dirname)
- Dir.open(dirname) {|d|
- return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
- }
- end
-
-end
-
-
-#
-# Main Installer
-#
-
-module HookUtils
-
- def run_hook(name)
- try_run_hook "#{curr_srcdir()}/#{name}" or
- try_run_hook "#{curr_srcdir()}/#{name}.rb"
- end
-
- def try_run_hook(fname)
- return false unless File.file?(fname)
- begin
- instance_eval File.read(fname), fname, 1
- rescue
- setup_rb_error "hook #{fname} failed:\n" + $!.message
- end
- true
- end
-
-end
-
-
-module HookScriptAPI
-
- def get_config(key)
- @config[key]
- end
-
- alias config get_config
-
- def set_config(key, val)
- @config[key] = val
- end
-
- #
- # srcdir/objdir (works only in the package directory)
- #
-
- #abstract srcdir_root
- #abstract objdir_root
- #abstract relpath
-
- def curr_srcdir
- "#{srcdir_root()}/#{relpath()}"
- end
-
- def curr_objdir
- "#{objdir_root()}/#{relpath()}"
- end
-
- def srcfile(path)
- "#{curr_srcdir()}/#{path}"
- end
-
- def srcexist?(path)
- File.exist?(srcfile(path))
- end
-
- def srcdirectory?(path)
- File.dir?(srcfile(path))
- end
-
- def srcfile?(path)
- File.file? srcfile(path)
- end
-
- def srcentries(path = '.')
- Dir.open("#{curr_srcdir()}/#{path}") {|d|
- return d.to_a - %w(. ..)
- }
- end
-
- def srcfiles(path = '.')
- srcentries(path).select {|fname|
- File.file?(File.join(curr_srcdir(), path, fname))
- }
- end
-
- def srcdirectories(path = '.')
- srcentries(path).select {|fname|
- File.dir?(File.join(curr_srcdir(), path, fname))
- }
- end
-
-end
-
-
-class ToplevelInstaller
-
- Version = '3.3.1'
- Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
-
- TASKS = [
- [ 'all', 'do config, setup, then install' ],
- [ 'config', 'saves your configurations' ],
- [ 'show', 'shows current configuration' ],
- [ 'setup', 'compiles ruby extentions and others' ],
- [ 'install', 'installs files' ],
- [ 'clean', "does `make clean' for each extention" ],
- [ 'distclean',"does `make distclean' for each extention" ]
- ]
-
- def ToplevelInstaller.invoke
- instance().invoke
- end
-
- @singleton = nil
-
- def ToplevelInstaller.instance
- @singleton ||= new(File.dirname($0))
- @singleton
- end
-
- include MetaConfigAPI
-
- def initialize(ardir_root)
- @config = nil
- @options = { 'verbose' => true }
- @ardir = File.expand_path(ardir_root)
- end
-
- def inspect
- "#<#{self.class} #{__id__()}>"
- end
-
- def invoke
- run_metaconfigs
- case task = parsearg_global()
- when nil, 'all'
- @config = load_config('config')
- parsearg_config
- init_installers
- exec_config
- exec_setup
- exec_install
- else
- @config = load_config(task)
- __send__ "parsearg_#{task}"
- init_installers
- __send__ "exec_#{task}"
- end
- end
-
- def run_metaconfigs
- eval_file_ifexist "#{@ardir}/metaconfig"
- end
-
- def load_config(task)
- case task
- when 'config'
- ConfigTable.new
- when 'clean', 'distclean'
- if File.exist?(ConfigTable.savefile)
- then ConfigTable.load
- else ConfigTable.new
- end
- else
- ConfigTable.load
- end
- end
-
- def init_installers
- @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
- end
-
- #
- # Hook Script API bases
- #
-
- def srcdir_root
- @ardir
- end
-
- def objdir_root
- '.'
- end
-
- def relpath
- '.'
- end
-
- #
- # Option Parsing
- #
-
- def parsearg_global
- valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
-
- while arg = ARGV.shift
- case arg
- when /\A\w+\z/
- setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
- return arg
-
- when '-q', '--quiet'
- @options['verbose'] = false
-
- when '--verbose'
- @options['verbose'] = true
-
- when '-h', '--help'
- print_usage $stdout
- exit 0
-
- when '-v', '--version'
- puts "#{File.basename($0)} version #{Version}"
- exit 0
-
- when '--copyright'
- puts Copyright
- exit 0
-
- else
- setup_rb_error "unknown global option '#{arg}'"
- end
- end
-
- nil
- end
-
-
- def parsearg_no_options
- unless ARGV.empty?
- setup_rb_error "#{task}: unknown options: #{ARGV.join ' '}"
- end
- end
-
- alias parsearg_show parsearg_no_options
- alias parsearg_setup parsearg_no_options
- alias parsearg_clean parsearg_no_options
- alias parsearg_distclean parsearg_no_options
-
- def parsearg_config
- re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
- @options['config-opt'] = []
-
- while i = ARGV.shift
- if /\A--?\z/ =~ i
- @options['config-opt'] = ARGV.dup
- break
- end
- m = re.match(i) or setup_rb_error "config: unknown option #{i}"
- name, value = *m.to_a[1,2]
- @config[name] = value
- end
- end
-
- def parsearg_install
- @options['no-harm'] = false
- @options['install-prefix'] = ''
- while a = ARGV.shift
- case a
- when /\A--no-harm\z/
- @options['no-harm'] = true
- when /\A--prefix=(.*)\z/
- path = $1
- path = File.expand_path(path) unless path[0,1] == '/'
- @options['install-prefix'] = path
- else
- setup_rb_error "install: unknown option #{a}"
- end
- end
- end
-
- def print_usage(out)
- out.puts 'Typical Installation Procedure:'
- out.puts " $ ruby #{File.basename $0} config"
- out.puts " $ ruby #{File.basename $0} setup"
- out.puts " # ruby #{File.basename $0} install (may require root privilege)"
- out.puts
- out.puts 'Detailed Usage:'
- out.puts " ruby #{File.basename $0} "
- out.puts " ruby #{File.basename $0} [] []"
-
- fmt = " %-24s %s\n"
- out.puts
- out.puts 'Global options:'
- out.printf fmt, '-q,--quiet', 'suppress message outputs'
- out.printf fmt, ' --verbose', 'output messages verbosely'
- out.printf fmt, '-h,--help', 'print this message'
- out.printf fmt, '-v,--version', 'print version and quit'
- out.printf fmt, ' --copyright', 'print copyright and quit'
- out.puts
- out.puts 'Tasks:'
- TASKS.each do |name, desc|
- out.printf fmt, name, desc
- end
-
- fmt = " %-24s %s [%s]\n"
- out.puts
- out.puts 'Options for CONFIG or ALL:'
- ConfigTable.each do |item|
- out.printf fmt, item.help_opt, item.description, item.help_default
- end
- out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
- out.puts
- out.puts 'Options for INSTALL:'
- out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
- out.printf fmt, '--prefix=path', 'install path prefix', '$prefix'
- out.puts
- end
-
- #
- # Task Handlers
- #
-
- def exec_config
- @installer.exec_config
- @config.save # must be final
- end
-
- def exec_setup
- @installer.exec_setup
- end
-
- def exec_install
- @installer.exec_install
- end
-
- def exec_show
- ConfigTable.each do |i|
- printf "%-20s %s\n", i.name, i.value
- end
- end
-
- def exec_clean
- @installer.exec_clean
- end
-
- def exec_distclean
- @installer.exec_distclean
- end
-
-end
-
-
-class ToplevelInstallerMulti < ToplevelInstaller
-
- include HookUtils
- include HookScriptAPI
- include FileOperations
-
- def initialize(ardir)
- super
- @packages = all_dirs_in("#{@ardir}/packages")
- raise 'no package exists' if @packages.empty?
- end
-
- def run_metaconfigs
- eval_file_ifexist "#{@ardir}/metaconfig"
- @packages.each do |name|
- eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
- end
- end
-
- def init_installers
- @installers = {}
- @packages.each do |pack|
- @installers[pack] = Installer.new(@config, @options,
- "#{@ardir}/packages/#{pack}",
- "packages/#{pack}")
- end
-
- with = extract_selection(config('with'))
- without = extract_selection(config('without'))
- @selected = @installers.keys.select {|name|
- (with.empty? or with.include?(name)) \
- and not without.include?(name)
- }
- end
-
- def extract_selection(list)
- a = list.split(/,/)
- a.each do |name|
- setup_rb_error "no such package: #{name}" unless @installers.key?(name)
- end
- a
- end
-
- def print_usage(f)
- super
- f.puts 'Inluded packages:'
- f.puts ' ' + @packages.sort.join(' ')
- f.puts
- end
-
- #
- # multi-package metaconfig API
- #
-
- attr_reader :packages
-
- def declare_packages(list)
- raise 'package list is empty' if list.empty?
- list.each do |name|
- raise "directory packages/#{name} does not exist"\
- unless File.dir?("#{@ardir}/packages/#{name}")
- end
- @packages = list
- end
-
- #
- # Task Handlers
- #
-
- def exec_config
- run_hook 'pre-config'
- each_selected_installers {|inst| inst.exec_config }
- run_hook 'post-config'
- @config.save # must be final
- end
-
- def exec_setup
- run_hook 'pre-setup'
- each_selected_installers {|inst| inst.exec_setup }
- run_hook 'post-setup'
- end
-
- def exec_install
- run_hook 'pre-install'
- each_selected_installers {|inst| inst.exec_install }
- run_hook 'post-install'
- end
-
- def exec_clean
- rm_f ConfigTable.savefile
- run_hook 'pre-clean'
- each_selected_installers {|inst| inst.exec_clean }
- run_hook 'post-clean'
- end
-
- def exec_distclean
- rm_f ConfigTable.savefile
- run_hook 'pre-distclean'
- each_selected_installers {|inst| inst.exec_distclean }
- run_hook 'post-distclean'
- end
-
- #
- # lib
- #
-
- def each_selected_installers
- Dir.mkdir 'packages' unless File.dir?('packages')
- @selected.each do |pack|
- $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
- Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
- Dir.chdir "packages/#{pack}"
- yield @installers[pack]
- Dir.chdir '../..'
- end
- end
-
- def verbose?
- @options['verbose']
- end
-
- def no_harm?
- @options['no-harm']
- end
-
-end
-
-
-class Installer
-
- FILETYPES = %w( bin lib ext data )
-
- include HookScriptAPI
- include HookUtils
- include FileOperations
-
- def initialize(config, opt, srcroot, objroot)
- @config = config
- @options = opt
- @srcdir = File.expand_path(srcroot)
- @objdir = File.expand_path(objroot)
- @currdir = '.'
- end
-
- def inspect
- "#<#{self.class} #{File.basename(@srcdir)}>"
- end
-
- #
- # Hook Script API base methods
- #
-
- def srcdir_root
- @srcdir
- end
-
- def objdir_root
- @objdir
- end
-
- def relpath
- @currdir
- end
-
- #
- # configs/options
- #
-
- def no_harm?
- @options['no-harm']
- end
-
- def verbose?
- @options['verbose']
- end
-
- def verbose_off
- begin
- save, @options['verbose'] = @options['verbose'], false
- yield
- ensure
- @options['verbose'] = save
- end
- end
-
- #
- # TASK config
- #
-
- def exec_config
- exec_task_traverse 'config'
- end
-
- def config_dir_bin(rel)
- end
-
- def config_dir_lib(rel)
- end
-
- def config_dir_ext(rel)
- extconf if extdir?(curr_srcdir())
- end
-
- def extconf
- opt = @options['config-opt'].join(' ')
- command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}"
- end
-
- def config_dir_data(rel)
- end
-
- #
- # TASK setup
- #
-
- def exec_setup
- exec_task_traverse 'setup'
- end
-
- def setup_dir_bin(rel)
- all_files_in(curr_srcdir()).each do |fname|
- adjust_shebang "#{curr_srcdir()}/#{fname}"
- end
- end
-
- def adjust_shebang(path)
- return if no_harm?
- tmpfile = File.basename(path) + '.tmp'
- begin
- File.open(path, 'rb') {|r|
- first = r.gets
- return unless File.basename(config('rubypath')) == 'ruby'
- return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
- $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
- File.open(tmpfile, 'wb') {|w|
- w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
- w.write r.read
- }
- move_file tmpfile, File.basename(path)
- }
- ensure
- File.unlink tmpfile if File.exist?(tmpfile)
- end
- end
-
- def setup_dir_lib(rel)
- end
-
- def setup_dir_ext(rel)
- make if extdir?(curr_srcdir())
- end
-
- def setup_dir_data(rel)
- end
-
- #
- # TASK install
- #
-
- def exec_install
- rm_f 'InstalledFiles'
- exec_task_traverse 'install'
- end
-
- def install_dir_bin(rel)
- install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
- end
-
- def install_dir_lib(rel)
- install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
- end
-
- def install_dir_ext(rel)
- return unless extdir?(curr_srcdir())
- install_files ruby_extentions('.'),
- "#{config('sodir')}/#{File.dirname(rel)}",
- 0555
- end
-
- def install_dir_data(rel)
- install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
- end
-
- def install_files(list, dest, mode)
- mkdir_p dest, @options['install-prefix']
- list.each do |fname|
- install fname, dest, mode, @options['install-prefix']
- end
- end
-
- def ruby_scripts
- collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
- end
-
- # picked up many entries from cvs-1.11.1/src/ignore.c
- reject_patterns = %w(
- core RCSLOG tags TAGS .make.state
- .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
- *~ *.old *.bak *.BAK *.orig *.rej _$* *$
-
- *.org *.in .*
- )
- mapping = {
- '.' => '\.',
- '$' => '\$',
- '#' => '\#',
- '*' => '.*'
- }
- REJECT_PATTERNS = Regexp.new('\A(?:' +
- reject_patterns.map {|pat|
- pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
- }.join('|') +
- ')\z')
-
- def collect_filenames_auto
- mapdir((existfiles() - hookfiles()).reject {|fname|
- REJECT_PATTERNS =~ fname
- })
- end
-
- def existfiles
- all_files_in(curr_srcdir()) | all_files_in('.')
- end
-
- def hookfiles
- %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
- %w( config setup install clean ).map {|t| sprintf(fmt, t) }
- }.flatten
- end
-
- def mapdir(filelist)
- filelist.map {|fname|
- if File.exist?(fname) # objdir
- fname
- else # srcdir
- File.join(curr_srcdir(), fname)
- end
- }
- end
-
- def ruby_extentions(dir)
- Dir.open(dir) {|d|
- ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
- if ents.empty?
- setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
- end
- return ents
- }
- end
-
- #
- # TASK clean
- #
-
- def exec_clean
- exec_task_traverse 'clean'
- rm_f ConfigTable.savefile
- rm_f 'InstalledFiles'
- end
-
- def clean_dir_bin(rel)
- end
-
- def clean_dir_lib(rel)
- end
-
- def clean_dir_ext(rel)
- return unless extdir?(curr_srcdir())
- make 'clean' if File.file?('Makefile')
- end
-
- def clean_dir_data(rel)
- end
-
- #
- # TASK distclean
- #
-
- def exec_distclean
- exec_task_traverse 'distclean'
- rm_f ConfigTable.savefile
- rm_f 'InstalledFiles'
- end
-
- def distclean_dir_bin(rel)
- end
-
- def distclean_dir_lib(rel)
- end
-
- def distclean_dir_ext(rel)
- return unless extdir?(curr_srcdir())
- make 'distclean' if File.file?('Makefile')
- end
-
- #
- # lib
- #
-
- def exec_task_traverse(task)
- run_hook "pre-#{task}"
- FILETYPES.each do |type|
- if config('without-ext') == 'yes' and type == 'ext'
- $stderr.puts 'skipping ext/* by user option' if verbose?
- next
- end
- traverse task, type, "#{task}_dir_#{type}"
- end
- run_hook "post-#{task}"
- end
-
- def traverse(task, rel, mid)
- dive_into(rel) {
- run_hook "pre-#{task}"
- __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
- all_dirs_in(curr_srcdir()).each do |d|
- traverse task, "#{rel}/#{d}", mid
- end
- run_hook "post-#{task}"
- }
- end
-
- def dive_into(rel)
- return unless File.dir?("#{@srcdir}/#{rel}")
-
- dir = File.basename(rel)
- Dir.mkdir dir unless File.dir?(dir)
- prevdir = Dir.pwd
- Dir.chdir dir
- $stderr.puts '---> ' + rel if verbose?
- @currdir = rel
- yield
- Dir.chdir prevdir
- $stderr.puts '<--- ' + rel if verbose?
- @currdir = File.dirname(rel)
- end
-
-end
-
-
-if $0 == __FILE__
- begin
- if multipackage_install?
- ToplevelInstallerMulti.invoke
- else
- ToplevelInstaller.invoke
- end
- rescue SetupError
- raise if $DEBUG
- $stderr.puts $!.message
- $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
- exit 1
- end
-end
diff --git a/vendor/rails/actionwebservice/test/abstract_client.rb b/vendor/rails/actionwebservice/test/abstract_client.rb
deleted file mode 100644
index 467c4e0d..00000000
--- a/vendor/rails/actionwebservice/test/abstract_client.rb
+++ /dev/null
@@ -1,183 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-require 'webrick'
-require 'webrick/log'
-require 'singleton'
-
-module ClientTest
- class Person < ActionWebService::Struct
- member :firstnames, [:string]
- member :lastname, :string
-
- def ==(other)
- firstnames == other.firstnames && lastname == other.lastname
- end
- end
-
- class Inner < ActionWebService::Struct
- member :name, :string
- end
-
- class Outer < ActionWebService::Struct
- member :name, :string
- member :inner, Inner
- end
-
- class User < ActiveRecord::Base
- end
-
- module Accounting
- class User < ActiveRecord::Base
- end
- end
-
- class WithModel < ActionWebService::Struct
- member :user, User
- member :users, [User]
- end
-
- class WithMultiDimArray < ActionWebService::Struct
- member :pref, [[:string]]
- end
-
- class API < ActionWebService::API::Base
- api_method :void
- api_method :normal, :expects => [:int, :int], :returns => [:int]
- api_method :array_return, :returns => [[Person]]
- api_method :struct_pass, :expects => [[Person]], :returns => [:bool]
- api_method :nil_struct_return, :returns => [Person]
- api_method :inner_nil, :returns => [Outer]
- api_method :client_container, :returns => [:int]
- api_method :named_parameters, :expects => [{:key=>:string}, {:id=>:int}]
- api_method :thrower
- api_method :user_return, :returns => [User]
- api_method :with_model_return, :returns => [WithModel]
- api_method :scoped_model_return, :returns => [Accounting::User]
- api_method :multi_dim_return, :returns => [WithMultiDimArray]
- end
-
- class NullLogOut
- def <<(*args); end
- end
-
- class Container < ActionController::Base
- web_service_api API
-
- attr_accessor :value_void
- attr_accessor :value_normal
- attr_accessor :value_array_return
- attr_accessor :value_struct_pass
- attr_accessor :value_named_parameters
-
- def initialize
- @value_void = nil
- @value_normal = nil
- @value_array_return = nil
- @value_struct_pass = nil
- @value_named_parameters = nil
- end
-
- def void
- @value_void = @method_params
- end
-
- def normal
- @value_normal = @method_params
- 5
- end
-
- def array_return
- person = Person.new
- person.firstnames = ["one", "two"]
- person.lastname = "last"
- @value_array_return = [person]
- end
-
- def struct_pass
- @value_struct_pass = @method_params
- true
- end
-
- def nil_struct_return
- nil
- end
-
- def inner_nil
- Outer.new :name => 'outer', :inner => nil
- end
-
- def client_container
- 50
- end
-
- def named_parameters
- @value_named_parameters = @method_params
- end
-
- def thrower
- raise "Hi"
- end
-
- def user_return
- User.find(1)
- end
-
- def with_model_return
- WithModel.new :user => User.find(1), :users => User.find(:all)
- end
-
- def scoped_model_return
- Accounting::User.find(1)
- end
-
- def multi_dim_return
- WithMultiDimArray.new :pref => [%w{pref1 value1}, %w{pref2 value2}]
- end
- end
-
- class AbstractClientLet < WEBrick::HTTPServlet::AbstractServlet
- def initialize(controller)
- @controller = controller
- end
-
- def get_instance(*args)
- self
- end
-
- def require_path_info?
- false
- end
-
- def do_GET(req, res)
- raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed."
- end
-
- def do_POST(req, res)
- raise NotImplementedError
- end
- end
-
- class AbstractServer
- include ClientTest
- include Singleton
- attr :container
- def initialize
- @container = Container.new
- @clientlet = create_clientlet(@container)
- log = WEBrick::BasicLog.new(NullLogOut.new)
- @server = WEBrick::HTTPServer.new(:Port => server_port, :Logger => log, :AccessLog => log)
- @server.mount('/', @clientlet)
- @thr = Thread.new { @server.start }
- until @server.status == :Running; end
- at_exit { @server.stop; @thr.join }
- end
-
- protected
- def create_clientlet
- raise NotImplementedError
- end
-
- def server_port
- raise NotImplementedError
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/test/abstract_dispatcher.rb b/vendor/rails/actionwebservice/test/abstract_dispatcher.rb
deleted file mode 100644
index 8d7c98a9..00000000
--- a/vendor/rails/actionwebservice/test/abstract_dispatcher.rb
+++ /dev/null
@@ -1,505 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-require 'stringio'
-
-class ActionController::Base; def rescue_action(e) raise e end; end
-
-module DispatcherTest
- Utf8String = "One World Caf\303\251"
- WsdlNamespace = 'http://rubyonrails.com/some/namespace'
-
- class Node < ActiveRecord::Base
- def initialize(*args)
- super(*args)
- @new_record = false
- end
-
- class << self
- def name
- "DispatcherTest::Node"
- end
-
- def columns(*args)
- [
- ActiveRecord::ConnectionAdapters::Column.new('id', 0, 'int'),
- ActiveRecord::ConnectionAdapters::Column.new('name', nil, 'string'),
- ActiveRecord::ConnectionAdapters::Column.new('description', nil, 'string'),
- ]
- end
-
- def connection
- self
- end
- end
- end
-
- class Person < ActionWebService::Struct
- member :id, :int
- member :name, :string
-
- def ==(other)
- self.id == other.id && self.name == other.name
- end
- end
-
- class API < ActionWebService::API::Base
- api_method :add, :expects => [:int, :int], :returns => [:int]
- api_method :interceptee
- api_method :struct_return, :returns => [[Node]]
- api_method :void
- end
-
- class DirectAPI < ActionWebService::API::Base
- api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int]
- api_method :add2, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int]
- api_method :before_filtered
- api_method :after_filtered, :returns => [[:int]]
- api_method :struct_return, :returns => [[Node]]
- api_method :struct_pass, :expects => [{:person => Person}]
- api_method :base_struct_return, :returns => [[Person]]
- api_method :hash_struct_return, :returns => [[Person]]
- api_method :thrower
- api_method :void
- api_method :test_utf8, :returns => [:string]
- api_method :hex, :expects => [:base64], :returns => [:string]
- api_method :unhex, :expects => [:string], :returns => [:base64]
- api_method :time, :expects => [:time], :returns => [:time]
- end
-
- class VirtualAPI < ActionWebService::API::Base
- default_api_method :fallback
- end
-
- class Service < ActionWebService::Base
- web_service_api API
-
- before_invocation :do_intercept, :only => [:interceptee]
-
- attr :added
- attr :intercepted
- attr :void_called
-
- def initialize
- @void_called = false
- end
-
- def add(a, b)
- @added = a + b
- end
-
- def interceptee
- @intercepted = false
- end
-
- def struct_return
- n1 = Node.new('id' => 1, 'name' => 'node1', 'description' => 'Node 1')
- n2 = Node.new('id' => 2, 'name' => 'node2', 'description' => 'Node 2')
- [n1, n2]
- end
-
- def void(*args)
- @void_called = args
- end
-
- def do_intercept(name, args)
- [false, "permission denied"]
- end
- end
-
- class MTAPI < ActionWebService::API::Base
- inflect_names false
- api_method :getCategories, :returns => [[:string]]
- api_method :bool, :returns => [:bool]
- api_method :alwaysFail
- api_method :person, :returns => [Person]
- end
-
- class BloggerAPI < ActionWebService::API::Base
- inflect_names false
- api_method :getCategories, :returns => [[:string]]
- api_method :str, :expects => [:int], :returns => [:string]
- api_method :alwaysFail
- end
-
- class MTService < ActionWebService::Base
- web_service_api MTAPI
-
- def getCategories
- ["mtCat1", "mtCat2"]
- end
-
- def bool
- 'y'
- end
-
- def alwaysFail
- raise "MT AlwaysFail"
- end
-
- def person
- Person.new('id' => 1, 'name' => 'person1')
- end
- end
-
- class BloggerService < ActionWebService::Base
- web_service_api BloggerAPI
-
- def getCategories
- ["bloggerCat1", "bloggerCat2"]
- end
-
- def str(int)
- unless int.is_a?(Integer)
- raise "Not an integer!"
- end
- 500 + int
- end
-
- def alwaysFail
- raise "Blogger AlwaysFail"
- end
- end
-
- class AbstractController < ActionController::Base
- def generate_wsdl
- self.request ||= ::ActionController::TestRequest.new
- to_wsdl
- end
- end
-
- class DelegatedController < AbstractController
- web_service_dispatching_mode :delegated
- wsdl_namespace WsdlNamespace
-
- web_service(:test_service) { @service ||= Service.new; @service }
- end
-
- class LayeredController < AbstractController
- web_service_dispatching_mode :layered
- wsdl_namespace WsdlNamespace
-
- web_service(:mt) { @mt_service ||= MTService.new; @mt_service }
- web_service(:blogger) { @blogger_service ||= BloggerService.new; @blogger_service }
- end
-
- class DirectController < AbstractController
- web_service_api DirectAPI
- web_service_dispatching_mode :direct
- wsdl_namespace WsdlNamespace
-
- before_invocation :alwaysfail, :only => [:before_filtered]
- after_invocation :alwaysok, :only => [:after_filtered]
-
- attr :added
- attr :added2
- attr :before_filter_called
- attr :before_filter_target_called
- attr :after_filter_called
- attr :after_filter_target_called
- attr :void_called
- attr :struct_pass_value
-
- def initialize
- @before_filter_called = false
- @before_filter_target_called = false
- @after_filter_called = false
- @after_filter_target_called = false
- @void_called = false
- @struct_pass_value = false
- end
-
- def add
- @added = params['a'] + params['b']
- end
-
- def add2(a, b)
- @added2 = a + b
- end
-
- def before_filtered
- @before_filter_target_called = true
- end
-
- def after_filtered
- @after_filter_target_called = true
- [5, 6, 7]
- end
-
- def thrower
- raise "Hi, I'm an exception"
- end
-
- def struct_return
- n1 = Node.new('id' => 1, 'name' => 'node1', 'description' => 'Node 1')
- n2 = Node.new('id' => 2, 'name' => 'node2', 'description' => 'Node 2')
- [n1, n2]
- end
-
- def struct_pass(person)
- @struct_pass_value = person
- end
-
- def base_struct_return
- p1 = Person.new('id' => 1, 'name' => 'person1')
- p2 = Person.new('id' => 2, 'name' => 'person2')
- [p1, p2]
- end
-
- def hash_struct_return
- p1 = { :id => '1', 'name' => 'test' }
- p2 = { 'id' => '2', :name => 'person2' }
- [p1, p2]
- end
-
- def void
- @void_called = @method_params
- end
-
- def test_utf8
- Utf8String
- end
-
- def hex(s)
- return s.unpack("H*")[0]
- end
-
- def unhex(s)
- return [s].pack("H*")
- end
-
- def time(t)
- t
- end
-
- protected
- def alwaysfail(method_name, params)
- @before_filter_called = true
- false
- end
-
- def alwaysok(method_name, params, return_value)
- @after_filter_called = true
- end
- end
-
- class VirtualController < AbstractController
- web_service_api VirtualAPI
- wsdl_namespace WsdlNamespace
-
- def fallback
- "fallback!"
- end
- end
-end
-
-module DispatcherCommonTests
- def test_direct_dispatching
- assert_equal(70, do_method_call(@direct_controller, 'Add', 20, 50))
- assert_equal(70, @direct_controller.added)
- assert_equal(50, do_method_call(@direct_controller, 'Add2', 25, 25))
- assert_equal(50, @direct_controller.added2)
- assert(@direct_controller.void_called == false)
- assert(do_method_call(@direct_controller, 'Void', 3, 4, 5).nil?)
- assert(@direct_controller.void_called == [])
- result = do_method_call(@direct_controller, 'BaseStructReturn')
- assert(result[0].is_a?(DispatcherTest::Person))
- assert(result[1].is_a?(DispatcherTest::Person))
- assert_equal("cafe", do_method_call(@direct_controller, 'Hex', "\xca\xfe"))
- assert_equal("\xca\xfe", do_method_call(@direct_controller, 'Unhex', "cafe"))
- time = Time.gm(1998, "Feb", 02, 15, 12, 01)
- assert_equal(time, do_method_call(@direct_controller, 'Time', time))
- end
-
- def test_direct_entrypoint
- assert(@direct_controller.respond_to?(:api))
- end
-
- def test_virtual_dispatching
- assert_equal("fallback!", do_method_call(@virtual_controller, 'VirtualOne'))
- assert_equal("fallback!", do_method_call(@virtual_controller, 'VirtualTwo'))
- end
-
- def test_direct_filtering
- assert_equal(false, @direct_controller.before_filter_called)
- assert_equal(false, @direct_controller.before_filter_target_called)
- do_method_call(@direct_controller, 'BeforeFiltered')
- assert_equal(true, @direct_controller.before_filter_called)
- assert_equal(false, @direct_controller.before_filter_target_called)
- assert_equal(false, @direct_controller.after_filter_called)
- assert_equal(false, @direct_controller.after_filter_target_called)
- assert_equal([5, 6, 7], do_method_call(@direct_controller, 'AfterFiltered'))
- assert_equal(true, @direct_controller.after_filter_called)
- assert_equal(true, @direct_controller.after_filter_target_called)
- end
-
- def test_delegated_dispatching
- assert_equal(130, do_method_call(@delegated_controller, 'Add', 50, 80))
- service = @delegated_controller.web_service_object(:test_service)
- assert_equal(130, service.added)
- @delegated_controller.web_service_exception_reporting = true
- assert(service.intercepted.nil?)
- result = do_method_call(@delegated_controller, 'Interceptee')
- assert(service.intercepted.nil?)
- assert(is_exception?(result))
- assert_match(/permission denied/, exception_message(result))
- result = do_method_call(@delegated_controller, 'NonExistentMethod')
- assert(is_exception?(result))
- assert_match(/NonExistentMethod/, exception_message(result))
- assert(service.void_called == false)
- assert(do_method_call(@delegated_controller, 'Void', 3, 4, 5).nil?)
- assert(service.void_called == [])
- end
-
- def test_garbage_request
- [@direct_controller, @delegated_controller].each do |controller|
- controller.class.web_service_exception_reporting = true
- send_garbage_request = lambda do
- service_name = service_name(controller)
- request = protocol.encode_action_pack_request(service_name, 'broken, method, name!', 'broken request body', :request_class => ActionController::TestRequest)
- response = ActionController::TestResponse.new
- controller.process(request, response)
- # puts response.body
- assert(response.headers['Status'] =~ /^500/)
- end
- send_garbage_request.call
- controller.class.web_service_exception_reporting = false
- send_garbage_request.call
- end
- end
-
- def test_exception_marshaling
- @direct_controller.web_service_exception_reporting = true
- result = do_method_call(@direct_controller, 'Thrower')
- assert(is_exception?(result))
- assert_equal("Hi, I'm an exception", exception_message(result))
- @direct_controller.web_service_exception_reporting = false
- result = do_method_call(@direct_controller, 'Thrower')
- assert(exception_message(result) != "Hi, I'm an exception")
- end
-
- def test_ar_struct_return
- [@direct_controller, @delegated_controller].each do |controller|
- result = do_method_call(controller, 'StructReturn')
- assert(result[0].is_a?(DispatcherTest::Node))
- assert(result[1].is_a?(DispatcherTest::Node))
- assert_equal('node1', result[0].name)
- assert_equal('node2', result[1].name)
- end
- end
-
- def test_casting
- assert_equal 70, do_method_call(@direct_controller, 'Add', "50", "20")
- assert_equal false, @direct_controller.struct_pass_value
- person = DispatcherTest::Person.new(:id => 1, :name => 'test')
- result = do_method_call(@direct_controller, 'StructPass', person)
- assert(nil == result || true == result)
- assert_equal person, @direct_controller.struct_pass_value
- assert !person.equal?(@direct_controller.struct_pass_value)
- result = do_method_call(@direct_controller, 'StructPass', {'id' => '1', 'name' => 'test'})
- case
- when soap?
- assert_equal(person, @direct_controller.struct_pass_value)
- assert !person.equal?(@direct_controller.struct_pass_value)
- when xmlrpc?
- assert_equal(person, @direct_controller.struct_pass_value)
- assert !person.equal?(@direct_controller.struct_pass_value)
- end
- assert_equal person, do_method_call(@direct_controller, 'HashStructReturn')[0]
- result = do_method_call(@direct_controller, 'StructPass', {'id' => '1', 'name' => 'test', 'nonexistent_attribute' => 'value'})
- case
- when soap?
- assert_equal(person, @direct_controller.struct_pass_value)
- assert !person.equal?(@direct_controller.struct_pass_value)
- when xmlrpc?
- assert_equal(person, @direct_controller.struct_pass_value)
- assert !person.equal?(@direct_controller.struct_pass_value)
- end
- end
-
- def test_logging
- buf = ""
- ActionController::Base.logger = Logger.new(StringIO.new(buf))
- test_casting
- test_garbage_request
- test_exception_marshaling
- ActionController::Base.logger = nil
- assert_match /Web Service Response/, buf
- assert_match /Web Service Request/, buf
- end
-
- protected
- def service_name(container)
- raise NotImplementedError
- end
-
- def exception_message(obj)
- raise NotImplementedError
- end
-
- def is_exception?(obj)
- raise NotImplementedError
- end
-
- def protocol
- @protocol
- end
-
- def soap?
- protocol.is_a? ActionWebService::Protocol::Soap::SoapProtocol
- end
-
- def xmlrpc?
- protocol.is_a? ActionWebService::Protocol::XmlRpc::XmlRpcProtocol
- end
-
- def do_method_call(container, public_method_name, *params)
- request_env = {}
- mode = container.web_service_dispatching_mode
- case mode
- when :direct
- service_name = service_name(container)
- api = container.class.web_service_api
- method = api.public_api_method_instance(public_method_name)
- when :delegated
- service_name = service_name(container)
- api = container.web_service_object(service_name).class.web_service_api
- method = api.public_api_method_instance(public_method_name)
- when :layered
- service_name = nil
- real_method_name = nil
- if public_method_name =~ /^([^\.]+)\.(.*)$/
- service_name = $1
- real_method_name = $2
- end
- if soap?
- public_method_name = real_method_name
- request_env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{real_method_name}"
- end
- api = container.web_service_object(service_name.to_sym).class.web_service_api rescue nil
- method = api.public_api_method_instance(real_method_name) rescue nil
- service_name = self.service_name(container)
- end
- protocol.register_api(api)
- virtual = false
- unless method
- virtual = true
- method ||= ActionWebService::API::Method.new(public_method_name.underscore.to_sym, public_method_name, nil, nil)
- end
- body = protocol.encode_request(public_method_name, params.dup, method.expects)
- # puts body
- ap_request = protocol.encode_action_pack_request(service_name, public_method_name, body, :request_class => ActionController::TestRequest)
- ap_request.env.update(request_env)
- ap_response = ActionController::TestResponse.new
- container.process(ap_request, ap_response)
- # puts ap_response.body
- @response_body = ap_response.body
- public_method_name, return_value = protocol.decode_response(ap_response.body)
- unless is_exception?(return_value) || virtual
- return_value = method.cast_returns(return_value)
- end
- if soap?
- # http://dev.rubyonrails.com/changeset/920
- assert_match(/Response$/, public_method_name) unless public_method_name == "fault"
- end
- return_value
- end
-end
diff --git a/vendor/rails/actionwebservice/test/abstract_unit.rb b/vendor/rails/actionwebservice/test/abstract_unit.rb
deleted file mode 100644
index b7088c43..00000000
--- a/vendor/rails/actionwebservice/test/abstract_unit.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-ENV["RAILS_ENV"] = "test"
-$:.unshift(File.dirname(__FILE__) + '/../lib')
-$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
-$:.unshift(File.dirname(__FILE__) + '/../../actionpack/lib')
-$:.unshift(File.dirname(__FILE__) + '/../../activerecord/lib')
-
-require 'test/unit'
-require 'action_web_service'
-require 'action_controller'
-require 'action_controller/test_process'
-
-# Show backtraces for deprecated behavior for quicker cleanup.
-ActiveSupport::Deprecation.debug = true
-
-
-ActionController::Base.logger = Logger.new("debug.log")
-ActionController::Base.ignore_missing_templates = true
-
-begin
- PATH_TO_AR = File.dirname(__FILE__) + '/../../activerecord'
- require "#{PATH_TO_AR}/lib/active_record" unless Object.const_defined?(:ActiveRecord)
- require "#{PATH_TO_AR}/lib/active_record/fixtures" unless Object.const_defined?(:Fixtures)
-rescue LoadError => e
- fail "\nFailed to load activerecord: #{e}"
-end
-
-ActiveRecord::Base.configurations = {
- 'mysql' => {
- :adapter => "mysql",
- :username => "rails",
- :encoding => "utf8",
- :database => "actionwebservice_unittest"
- }
-}
-
-ActiveRecord::Base.establish_connection 'mysql'
-
-Test::Unit::TestCase.fixture_path = "#{File.dirname(__FILE__)}/fixtures/"
-
-# restore default raw_post functionality
-class ActionController::TestRequest
- def raw_post
- super
- end
-end
diff --git a/vendor/rails/actionwebservice/test/api_test.rb b/vendor/rails/actionwebservice/test/api_test.rb
deleted file mode 100644
index 0e58d848..00000000
--- a/vendor/rails/actionwebservice/test/api_test.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-module APITest
- class API < ActionWebService::API::Base
- api_method :void
- api_method :expects_and_returns, :expects_and_returns => [:string]
- api_method :expects, :expects => [:int, :bool]
- api_method :returns, :returns => [:int, [:string]]
- api_method :named_signature, :expects => [{:appkey=>:int}, {:publish=>:bool}]
- api_method :string_types, :expects => ['int', 'string', 'bool', 'base64']
- api_method :class_types, :expects => [TrueClass, Bignum, String]
- end
-end
-
-class TC_API < Test::Unit::TestCase
- API = APITest::API
-
- def test_api_method_declaration
- %w(
- void
- expects_and_returns
- expects
- returns
- named_signature
- string_types
- class_types
- ).each do |name|
- name = name.to_sym
- public_name = API.public_api_method_name(name)
- assert(API.has_api_method?(name))
- assert(API.has_public_api_method?(public_name))
- assert(API.api_method_name(public_name) == name)
- assert(API.api_methods.has_key?(name))
- end
- end
-
- def test_signature_canonicalization
- assert_equal(nil, API.api_methods[:void].expects)
- assert_equal(nil, API.api_methods[:void].returns)
- assert_equal([String], API.api_methods[:expects_and_returns].expects.map{|x| x.type_class})
- assert_equal([String], API.api_methods[:expects_and_returns].returns.map{|x| x.type_class})
- assert_equal([Integer, TrueClass], API.api_methods[:expects].expects.map{|x| x.type_class})
- assert_equal(nil, API.api_methods[:expects].returns)
- assert_equal(nil, API.api_methods[:returns].expects)
- assert_equal([Integer, [String]], API.api_methods[:returns].returns.map{|x| x.array?? [x.element_type.type_class] : x.type_class})
- assert_equal([[:appkey, Integer], [:publish, TrueClass]], API.api_methods[:named_signature].expects.map{|x| [x.name, x.type_class]})
- assert_equal(nil, API.api_methods[:named_signature].returns)
- assert_equal([Integer, String, TrueClass, ActionWebService::Base64], API.api_methods[:string_types].expects.map{|x| x.type_class})
- assert_equal(nil, API.api_methods[:string_types].returns)
- assert_equal([TrueClass, Integer, String], API.api_methods[:class_types].expects.map{|x| x.type_class})
- assert_equal(nil, API.api_methods[:class_types].returns)
- end
-
- def test_not_instantiable
- assert_raises(NoMethodError) do
- API.new
- end
- end
-
- def test_api_errors
- assert_raises(ActionWebService::ActionWebServiceError) do
- klass = Class.new(ActionWebService::API::Base) do
- api_method :test, :expects => [ActiveRecord::Base]
- end
- end
- klass = Class.new(ActionWebService::API::Base) do
- allow_active_record_expects true
- api_method :test2, :expects => [ActiveRecord::Base]
- end
- assert_raises(ActionWebService::ActionWebServiceError) do
- klass = Class.new(ActionWebService::API::Base) do
- api_method :test, :invalid => [:int]
- end
- end
- end
-
- def test_parameter_names
- method = API.api_methods[:named_signature]
- assert_equal 0, method.expects_index_of(:appkey)
- assert_equal 1, method.expects_index_of(:publish)
- assert_equal 1, method.expects_index_of('publish')
- assert_equal 0, method.expects_index_of('appkey')
- assert_equal -1, method.expects_index_of('blah')
- assert_equal -1, method.expects_index_of(:missing)
- assert_equal -1, API.api_methods[:void].expects_index_of('test')
- end
-
- def test_parameter_hash
- method = API.api_methods[:named_signature]
- hash = method.expects_to_hash([5, false])
- assert_equal({:appkey => 5, :publish => false}, hash)
- end
-
- def test_api_methods_compat
- sig = API.api_methods[:named_signature][:expects]
- assert_equal [{:appkey=>Integer}, {:publish=>TrueClass}], sig
- end
-
- def test_to_s
- assert_equal 'void Expects(int param0, bool param1)', APITest::API.api_methods[:expects].to_s
- end
-end
diff --git a/vendor/rails/actionwebservice/test/apis/auto_load_api.rb b/vendor/rails/actionwebservice/test/apis/auto_load_api.rb
deleted file mode 100644
index a35bbe3f..00000000
--- a/vendor/rails/actionwebservice/test/apis/auto_load_api.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class AutoLoadAPI < ActionWebService::API::Base
- api_method :void
-end
diff --git a/vendor/rails/actionwebservice/test/apis/broken_auto_load_api.rb b/vendor/rails/actionwebservice/test/apis/broken_auto_load_api.rb
deleted file mode 100644
index 139597f9..00000000
--- a/vendor/rails/actionwebservice/test/apis/broken_auto_load_api.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/vendor/rails/actionwebservice/test/base_test.rb b/vendor/rails/actionwebservice/test/base_test.rb
deleted file mode 100644
index 55a112a0..00000000
--- a/vendor/rails/actionwebservice/test/base_test.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-module BaseTest
- class API < ActionWebService::API::Base
- api_method :add, :expects => [:int, :int], :returns => [:int]
- api_method :void
- end
-
- class PristineAPI < ActionWebService::API::Base
- inflect_names false
-
- api_method :add
- api_method :under_score
- end
-
- class Service < ActionWebService::Base
- web_service_api API
-
- def add(a, b)
- end
-
- def void
- end
- end
-
- class PristineService < ActionWebService::Base
- web_service_api PristineAPI
-
- def add
- end
-
- def under_score
- end
- end
-end
-
-class TC_Base < Test::Unit::TestCase
- def test_options
- assert(BaseTest::PristineService.web_service_api.inflect_names == false)
- assert(BaseTest::Service.web_service_api.inflect_names == true)
- end
-end
diff --git a/vendor/rails/actionwebservice/test/casting_test.rb b/vendor/rails/actionwebservice/test/casting_test.rb
deleted file mode 100644
index 34bad07d..00000000
--- a/vendor/rails/actionwebservice/test/casting_test.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-module CastingTest
- class API < ActionWebService::API::Base
- api_method :int, :expects => [:int]
- api_method :str, :expects => [:string]
- api_method :base64, :expects => [:base64]
- api_method :bool, :expects => [:bool]
- api_method :float, :expects => [:float]
- api_method :time, :expects => [:time]
- api_method :datetime, :expects => [:datetime]
- api_method :date, :expects => [:date]
-
- api_method :int_array, :expects => [[:int]]
- api_method :str_array, :expects => [[:string]]
- api_method :bool_array, :expects => [[:bool]]
- end
-end
-
-class TC_Casting < Test::Unit::TestCase
- include CastingTest
-
- def test_base_type_casting_valid
- assert_equal 10000, cast_expects(:int, '10000')[0]
- assert_equal '10000', cast_expects(:str, 10000)[0]
- base64 = cast_expects(:base64, 10000)[0]
- assert_equal '10000', base64
- assert_instance_of ActionWebService::Base64, base64
- [1, '1', 'true', 'y', 'yes'].each do |val|
- assert_equal true, cast_expects(:bool, val)[0]
- end
- [0, '0', 'false', 'n', 'no'].each do |val|
- assert_equal false, cast_expects(:bool, val)[0]
- end
- assert_equal 3.14159, cast_expects(:float, '3.14159')[0]
- now = Time.at(Time.now.tv_sec)
- casted = cast_expects(:time, now.to_s)[0]
- assert_equal now, casted
- now = DateTime.now
- assert_equal now.to_s, cast_expects(:datetime, now.to_s)[0].to_s
- today = Date.today
- assert_equal today, cast_expects(:date, today.to_s)[0]
- end
-
- def test_base_type_casting_invalid
- assert_raises ArgumentError do
- cast_expects(:int, 'this is not a number')
- end
- assert_raises ActionWebService::Casting::CastingError do
- # neither true or false ;)
- cast_expects(:bool, 'i always lie')
- end
- assert_raises ArgumentError do
- cast_expects(:float, 'not a float')
- end
- assert_raises ArgumentError do
- cast_expects(:time, '111111111111111111111111111111111')
- end
- assert_raises ArgumentError do
- cast_expects(:datetime, '-1')
- end
- assert_raises ArgumentError do
- cast_expects(:date, '')
- end
- end
-
- def test_array_type_casting
- assert_equal [1, 2, 3213992, 4], cast_expects(:int_array, ['1', '2', '3213992', '4'])[0]
- assert_equal ['one', 'two', '5.0', '200', nil, 'true'], cast_expects(:str_array, [:one, 'two', 5.0, 200, nil, true])[0]
- assert_equal [true, nil, true, true, false], cast_expects(:bool_array, ['1', nil, 'y', true, 'false'])[0]
- end
-
- def test_array_type_casting_failure
- assert_raises ActionWebService::Casting::CastingError do
- cast_expects(:bool_array, ['false', 'blahblah'])
- end
- assert_raises ArgumentError do
- cast_expects(:int_array, ['1', '2.021', '4'])
- end
- end
-
- private
- def cast_expects(method_name, *args)
- API.api_method_instance(method_name.to_sym).cast_expects([*args])
- end
-end
diff --git a/vendor/rails/actionwebservice/test/client_soap_test.rb b/vendor/rails/actionwebservice/test/client_soap_test.rb
deleted file mode 100644
index c03c2414..00000000
--- a/vendor/rails/actionwebservice/test/client_soap_test.rb
+++ /dev/null
@@ -1,152 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_client'
-
-
-module ClientSoapTest
- PORT = 8998
-
- class SoapClientLet < ClientTest::AbstractClientLet
- def do_POST(req, res)
- test_request = ActionController::TestRequest.new
- test_request.request_parameters['action'] = req.path.gsub(/^\//, '').split(/\//)[1]
- test_request.env['REQUEST_METHOD'] = "POST"
- test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
- test_request.env['HTTP_SOAPACTION'] = req.header['soapaction'][0]
- test_request.env['RAW_POST_DATA'] = req.body
- response = ActionController::TestResponse.new
- @controller.process(test_request, response)
- res.header['content-type'] = 'text/xml'
- res.body = response.body
- rescue Exception => e
- $stderr.puts e.message
- $stderr.puts e.backtrace.join("\n")
- end
- end
-
- class ClientContainer < ActionController::Base
- web_client_api :client, :soap, "http://localhost:#{PORT}/client/api", :api => ClientTest::API
- web_client_api :invalid, :null, "", :api => true
-
- def get_client
- client
- end
-
- def get_invalid
- invalid
- end
- end
-
- class SoapServer < ClientTest::AbstractServer
- def create_clientlet(controller)
- SoapClientLet.new(controller)
- end
-
- def server_port
- PORT
- end
- end
-end
-
-class TC_ClientSoap < Test::Unit::TestCase
- include ClientTest
- include ClientSoapTest
-
- fixtures :users
-
- def setup
- @server = SoapServer.instance
- @container = @server.container
- @client = ActionWebService::Client::Soap.new(API, "http://localhost:#{@server.server_port}/client/api")
- end
-
- def test_void
- assert(@container.value_void.nil?)
- @client.void
- assert(!@container.value_void.nil?)
- end
-
- def test_normal
- assert(@container.value_normal.nil?)
- assert_equal(5, @client.normal(5, 6))
- assert_equal([5, 6], @container.value_normal)
- assert_equal(5, @client.normal("7", "8"))
- assert_equal([7, 8], @container.value_normal)
- assert_equal(5, @client.normal(true, false))
- end
-
- def test_array_return
- assert(@container.value_array_return.nil?)
- new_person = Person.new
- new_person.firstnames = ["one", "two"]
- new_person.lastname = "last"
- assert_equal([new_person], @client.array_return)
- assert_equal([new_person], @container.value_array_return)
- end
-
- def test_struct_pass
- assert(@container.value_struct_pass.nil?)
- new_person = Person.new
- new_person.firstnames = ["one", "two"]
- new_person.lastname = "last"
- assert_equal(true, @client.struct_pass([new_person]))
- assert_equal([[new_person]], @container.value_struct_pass)
- end
-
- def test_nil_struct_return
- assert_nil @client.nil_struct_return
- end
-
- def test_inner_nil
- outer = @client.inner_nil
- assert_equal 'outer', outer.name
- assert_nil outer.inner
- end
-
- def test_client_container
- assert_equal(50, ClientContainer.new.get_client.client_container)
- assert(ClientContainer.new.get_invalid.nil?)
- end
-
- def test_named_parameters
- assert(@container.value_named_parameters.nil?)
- assert(@client.named_parameters("key", 5).nil?)
- assert_equal(["key", 5], @container.value_named_parameters)
- end
-
- def test_capitalized_method_name
- @container.value_normal = nil
- assert_equal(5, @client.Normal(5, 6))
- assert_equal([5, 6], @container.value_normal)
- @container.value_normal = nil
- end
-
- def test_model_return
- user = @client.user_return
- assert_equal 1, user.id
- assert_equal 'Kent', user.name
- assert user.active?
- assert_kind_of Date, user.created_on
- assert_equal Date.today, user.created_on
- end
-
- def test_with_model
- with_model = @client.with_model_return
- assert_equal 'Kent', with_model.user.name
- assert_equal 2, with_model.users.size
- with_model.users.each do |user|
- assert_kind_of User, user
- end
- end
-
- def test_scoped_model_return
- scoped_model = @client.scoped_model_return
- assert_kind_of Accounting::User, scoped_model
- assert_equal 'Kent', scoped_model.name
- end
-
- def test_multi_dim_return
- md_struct = @client.multi_dim_return
- assert_kind_of Array, md_struct.pref
- assert_equal 2, md_struct.pref.size
- assert_kind_of Array, md_struct.pref[0]
- end
-end
diff --git a/vendor/rails/actionwebservice/test/client_xmlrpc_test.rb b/vendor/rails/actionwebservice/test/client_xmlrpc_test.rb
deleted file mode 100644
index 0abd5898..00000000
--- a/vendor/rails/actionwebservice/test/client_xmlrpc_test.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_client'
-
-
-module ClientXmlRpcTest
- PORT = 8999
-
- class XmlRpcClientLet < ClientTest::AbstractClientLet
- def do_POST(req, res)
- test_request = ActionController::TestRequest.new
- test_request.request_parameters['action'] = req.path.gsub(/^\//, '').split(/\//)[1]
- test_request.env['REQUEST_METHOD'] = "POST"
- test_request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
- test_request.env['RAW_POST_DATA'] = req.body
- response = ActionController::TestResponse.new
- @controller.process(test_request, response)
- res.header['content-type'] = 'text/xml'
- res.body = response.body
- # puts res.body
- rescue Exception => e
- $stderr.puts e.message
- $stderr.puts e.backtrace.join("\n")
- end
- end
-
- class ClientContainer < ActionController::Base
- web_client_api :client, :xmlrpc, "http://localhost:#{PORT}/client/api", :api => ClientTest::API
-
- def get_client
- client
- end
- end
-
- class XmlRpcServer < ClientTest::AbstractServer
- def create_clientlet(controller)
- XmlRpcClientLet.new(controller)
- end
-
- def server_port
- PORT
- end
- end
-end
-
-class TC_ClientXmlRpc < Test::Unit::TestCase
- include ClientTest
- include ClientXmlRpcTest
-
- fixtures :users
-
- def setup
- @server = XmlRpcServer.instance
- @container = @server.container
- @client = ActionWebService::Client::XmlRpc.new(API, "http://localhost:#{@server.server_port}/client/api")
- end
-
- def test_void
- assert(@container.value_void.nil?)
- @client.void
- assert(!@container.value_void.nil?)
- end
-
- def test_normal
- assert(@container.value_normal.nil?)
- assert_equal(5, @client.normal(5, 6))
- assert_equal([5, 6], @container.value_normal)
- assert_equal(5, @client.normal("7", "8"))
- assert_equal([7, 8], @container.value_normal)
- assert_equal(5, @client.normal(true, false))
- end
-
- def test_array_return
- assert(@container.value_array_return.nil?)
- new_person = Person.new
- new_person.firstnames = ["one", "two"]
- new_person.lastname = "last"
- assert_equal([new_person], @client.array_return)
- assert_equal([new_person], @container.value_array_return)
- end
-
- def test_struct_pass
- assert(@container.value_struct_pass.nil?)
- new_person = Person.new
- new_person.firstnames = ["one", "two"]
- new_person.lastname = "last"
- assert_equal(true, @client.struct_pass([new_person]))
- assert_equal([[new_person]], @container.value_struct_pass)
- end
-
- def test_nil_struct_return
- assert_equal false, @client.nil_struct_return
- end
-
- def test_inner_nil
- outer = @client.inner_nil
- assert_equal 'outer', outer.name
- assert_nil outer.inner
- end
-
- def test_client_container
- assert_equal(50, ClientContainer.new.get_client.client_container)
- end
-
- def test_named_parameters
- assert(@container.value_named_parameters.nil?)
- assert_equal(false, @client.named_parameters("xxx", 7))
- assert_equal(["xxx", 7], @container.value_named_parameters)
- end
-
- def test_exception
- assert_raises(ActionWebService::Client::ClientError) do
- assert(@client.thrower)
- end
- end
-
- def test_invalid_signature
- assert_raises(ArgumentError) do
- @client.normal
- end
- end
-
- def test_model_return
- user = @client.user_return
- assert_equal 1, user.id
- assert_equal 'Kent', user.name
- assert user.active?
- assert_kind_of Time, user.created_on
- assert_equal Time.utc(Time.now.year, Time.now.month, Time.now.day), user.created_on
- end
-
- def test_with_model
- with_model = @client.with_model_return
- assert_equal 'Kent', with_model.user.name
- assert_equal 2, with_model.users.size
- with_model.users.each do |user|
- assert_kind_of User, user
- end
- end
-
- def test_scoped_model_return
- scoped_model = @client.scoped_model_return
- assert_kind_of Accounting::User, scoped_model
- assert_equal 'Kent', scoped_model.name
- end
-
- def test_multi_dim_return
- md_struct = @client.multi_dim_return
- assert_kind_of Array, md_struct.pref
- assert_equal 2, md_struct.pref.size
- assert_kind_of Array, md_struct.pref[0]
- end
-end
diff --git a/vendor/rails/actionwebservice/test/container_test.rb b/vendor/rails/actionwebservice/test/container_test.rb
deleted file mode 100644
index 325d420f..00000000
--- a/vendor/rails/actionwebservice/test/container_test.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-module ContainerTest
- $immediate_service = Object.new
- $deferred_service = Object.new
-
- class DelegateContainer < ActionController::Base
- web_service_dispatching_mode :delegated
-
- attr :flag
- attr :previous_flag
-
- def initialize
- @previous_flag = nil
- @flag = true
- end
-
- web_service :immediate_service, $immediate_service
- web_service(:deferred_service) { @previous_flag = @flag; @flag = false; $deferred_service }
- end
-
- class DirectContainer < ActionController::Base
- web_service_dispatching_mode :direct
- end
-
- class InvalidContainer
- include ActionWebService::Container::Direct
- end
-end
-
-class TC_Container < Test::Unit::TestCase
- include ContainerTest
-
- def setup
- @delegate_container = DelegateContainer.new
- @direct_container = DirectContainer.new
- end
-
- def test_registration
- assert(DelegateContainer.has_web_service?(:immediate_service))
- assert(DelegateContainer.has_web_service?(:deferred_service))
- assert(!DelegateContainer.has_web_service?(:fake_service))
- assert_raises(ActionWebService::Container::Delegated::ContainerError) do
- DelegateContainer.web_service('invalid')
- end
- end
-
- def test_service_object
- assert_raises(ActionWebService::Container::Delegated::ContainerError) do
- @delegate_container.web_service_object(:nonexistent)
- end
- assert(@delegate_container.flag == true)
- assert(@delegate_container.web_service_object(:immediate_service) == $immediate_service)
- assert(@delegate_container.previous_flag.nil?)
- assert(@delegate_container.flag == true)
- assert(@delegate_container.web_service_object(:deferred_service) == $deferred_service)
- assert(@delegate_container.previous_flag == true)
- assert(@delegate_container.flag == false)
- end
-
- def test_direct_container
- assert(DirectContainer.web_service_dispatching_mode == :direct)
- end
-
- def test_validity
- assert_raises(ActionWebService::Container::Direct::ContainerError) do
- InvalidContainer.web_service_api :test
- end
- assert_raises(ActionWebService::Container::Direct::ContainerError) do
- InvalidContainer.web_service_api 50.0
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/test/dispatcher_action_controller_soap_test.rb b/vendor/rails/actionwebservice/test/dispatcher_action_controller_soap_test.rb
deleted file mode 100644
index 0b58af30..00000000
--- a/vendor/rails/actionwebservice/test/dispatcher_action_controller_soap_test.rb
+++ /dev/null
@@ -1,137 +0,0 @@
-$:.unshift(File.dirname(__FILE__) + '/apis')
-require File.dirname(__FILE__) + '/abstract_dispatcher'
-require 'wsdl/parser'
-
-class ActionController::Base
- class << self
- alias :inherited_without_name_error :inherited
- def inherited(child)
- begin
- inherited_without_name_error(child)
- rescue NameError => e
- end
- end
- end
-end
-
-class AutoLoadController < ActionController::Base; end
-class FailingAutoLoadController < ActionController::Base; end
-class BrokenAutoLoadController < ActionController::Base; end
-
-class TC_DispatcherActionControllerSoap < Test::Unit::TestCase
- include DispatcherTest
- include DispatcherCommonTests
-
- def setup
- @direct_controller = DirectController.new
- @delegated_controller = DelegatedController.new
- @virtual_controller = VirtualController.new
- @layered_controller = LayeredController.new
- @protocol = ActionWebService::Protocol::Soap::SoapProtocol.create(@direct_controller)
- end
-
- def test_wsdl_generation
- ensure_valid_wsdl_generation DelegatedController.new, DispatcherTest::WsdlNamespace
- ensure_valid_wsdl_generation DirectController.new, DispatcherTest::WsdlNamespace
- end
-
- def test_wsdl_action
- delegated_types = ensure_valid_wsdl_action DelegatedController.new
- delegated_names = delegated_types.map{|x| x.name.name}
- assert(delegated_names.include?('DispatcherTest..NodeArray'))
- assert(delegated_names.include?('DispatcherTest..Node'))
- direct_types = ensure_valid_wsdl_action DirectController.new
- direct_names = direct_types.map{|x| x.name.name}
- assert(direct_names.include?('DispatcherTest..NodeArray'))
- assert(direct_names.include?('DispatcherTest..Node'))
- assert(direct_names.include?('IntegerArray'))
- end
-
- def test_autoloading
- assert(!AutoLoadController.web_service_api.nil?)
- assert(AutoLoadController.web_service_api.has_public_api_method?('Void'))
- assert(FailingAutoLoadController.web_service_api.nil?)
- assert_raises(MissingSourceFile) do
- FailingAutoLoadController.require_web_service_api :blah
- end
- assert_raises(ArgumentError) do
- FailingAutoLoadController.require_web_service_api 50.0
- end
- assert(BrokenAutoLoadController.web_service_api.nil?)
- end
-
- def test_layered_dispatching
- mt_cats = do_method_call(@layered_controller, 'mt.getCategories')
- assert_equal(["mtCat1", "mtCat2"], mt_cats)
- blogger_cats = do_method_call(@layered_controller, 'blogger.getCategories')
- assert_equal(["bloggerCat1", "bloggerCat2"], blogger_cats)
- end
-
- def test_utf8
- @direct_controller.web_service_exception_reporting = true
- $KCODE = 'u'
- assert_equal(Utf8String, do_method_call(@direct_controller, 'TestUtf8'))
- retval = SOAP::Processor.unmarshal(@response_body).body.response
- assert retval.is_a?(SOAP::SOAPString)
-
- # If $KCODE is not set to UTF-8, any strings with non-ASCII UTF-8 data
- # will be sent back as base64 by SOAP4R. By the time we get it here though,
- # it will be decoded back into a string. So lets read the base64 value
- # from the message body directly.
- $KCODE = 'NONE'
- do_method_call(@direct_controller, 'TestUtf8')
- retval = SOAP::Processor.unmarshal(@response_body).body.response
- assert retval.is_a?(SOAP::SOAPBase64)
- assert_equal "T25lIFdvcmxkIENhZsOp", retval.data.to_s
- end
-
- protected
- def exception_message(soap_fault_exception)
- soap_fault_exception.detail.cause.message
- end
-
- def is_exception?(obj)
- obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && \
- obj.detail.cause.is_a?(Exception)
- end
-
- def service_name(container)
- container.is_a?(DelegatedController) ? 'test_service' : 'api'
- end
-
- def ensure_valid_wsdl_generation(controller, expected_namespace)
- wsdl = controller.generate_wsdl
- ensure_valid_wsdl(controller, wsdl, expected_namespace)
- end
-
- def ensure_valid_wsdl(controller, wsdl, expected_namespace)
- definitions = WSDL::Parser.new.parse(wsdl)
- assert(definitions.is_a?(WSDL::Definitions))
- definitions.bindings.each do |binding|
- assert(binding.name.name.index(':').nil?)
- end
- definitions.services.each do |service|
- service.ports.each do |port|
- assert(port.name.name.index(':').nil?)
- end
- end
- types = definitions.collect_complextypes.map{|x| x.name}
- types.each do |type|
- assert(type.namespace == expected_namespace)
- end
- location = definitions.services[0].ports[0].soap_address.location
- if controller.is_a?(DelegatedController)
- assert_match %r{http://test.host/dispatcher_test/delegated/test_service$}, location
- elsif controller.is_a?(DirectController)
- assert_match %r{http://test.host/dispatcher_test/direct/api$}, location
- end
- definitions.collect_complextypes
- end
-
- def ensure_valid_wsdl_action(controller)
- test_request = ActionController::TestRequest.new({ 'action' => 'wsdl' })
- test_response = ActionController::TestResponse.new
- wsdl = controller.process(test_request, test_response).body
- ensure_valid_wsdl(controller, wsdl, DispatcherTest::WsdlNamespace)
- end
-end
diff --git a/vendor/rails/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb b/vendor/rails/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb
deleted file mode 100644
index 8add5766..00000000
--- a/vendor/rails/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_dispatcher'
-
-class TC_DispatcherActionControllerXmlRpc < Test::Unit::TestCase
- include DispatcherTest
- include DispatcherCommonTests
-
- def setup
- @direct_controller = DirectController.new
- @delegated_controller = DelegatedController.new
- @layered_controller = LayeredController.new
- @virtual_controller = VirtualController.new
- @protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.create(@direct_controller)
- end
-
- def test_layered_dispatching
- mt_cats = do_method_call(@layered_controller, 'mt.getCategories')
- assert_equal(["mtCat1", "mtCat2"], mt_cats)
- blogger_cats = do_method_call(@layered_controller, 'blogger.getCategories')
- assert_equal(["bloggerCat1", "bloggerCat2"], blogger_cats)
- end
-
- def test_multicall
- response = do_method_call(@layered_controller, 'system.multicall', [
- {'methodName' => 'mt.getCategories'},
- {'methodName' => 'blogger.getCategories'},
- {'methodName' => 'mt.bool'},
- {'methodName' => 'blogger.str', 'params' => ['2000']},
- {'methodName' => 'mt.alwaysFail'},
- {'methodName' => 'blogger.alwaysFail'},
- {'methodName' => 'mt.blah'},
- {'methodName' => 'blah.blah'},
- {'methodName' => 'mt.person'}
- ])
- assert_equal [
- [["mtCat1", "mtCat2"]],
- [["bloggerCat1", "bloggerCat2"]],
- [true],
- ["2500"],
- {"faultCode" => 3, "faultString" => "MT AlwaysFail"},
- {"faultCode" => 3, "faultString" => "Blogger AlwaysFail"},
- {"faultCode" => 4, "faultMessage" => "no such method 'blah' on API DispatcherTest::MTAPI"},
- {"faultCode" => 4, "faultMessage" => "no such web service 'blah'"},
- [{"name"=>"person1", "id"=>1}]
- ], response
- end
-
- protected
- def exception_message(xmlrpc_fault_exception)
- xmlrpc_fault_exception.faultString
- end
-
- def is_exception?(obj)
- obj.is_a?(XMLRPC::FaultException)
- end
-
- def service_name(container)
- container.is_a?(DelegatedController) ? 'test_service' : 'api'
- end
-end
diff --git a/vendor/rails/actionwebservice/test/fixtures/db_definitions/mysql.sql b/vendor/rails/actionwebservice/test/fixtures/db_definitions/mysql.sql
deleted file mode 100644
index 026f2a2c..00000000
--- a/vendor/rails/actionwebservice/test/fixtures/db_definitions/mysql.sql
+++ /dev/null
@@ -1,7 +0,0 @@
-CREATE TABLE `users` (
- `id` int(11) NOT NULL auto_increment,
- `name` varchar(30) default NULL,
- `active` tinyint(4) default NULL,
- `created_on` date default NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1;
diff --git a/vendor/rails/actionwebservice/test/fixtures/users.yml b/vendor/rails/actionwebservice/test/fixtures/users.yml
deleted file mode 100644
index a97d8c84..00000000
--- a/vendor/rails/actionwebservice/test/fixtures/users.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-user1:
- id: 1
- name: Kent
- active: 1
- created_on: <%= Date.today %>
-user2:
- id: 2
- name: David
- active: 1
- created_on: <%= Date.today %>
diff --git a/vendor/rails/actionwebservice/test/gencov b/vendor/rails/actionwebservice/test/gencov
deleted file mode 100755
index 1faab34c..00000000
--- a/vendor/rails/actionwebservice/test/gencov
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-rcov -x '.*_test\.rb,rubygems,abstract_,/run,/apis' ./run
diff --git a/vendor/rails/actionwebservice/test/invocation_test.rb b/vendor/rails/actionwebservice/test/invocation_test.rb
deleted file mode 100644
index 3ef22faf..00000000
--- a/vendor/rails/actionwebservice/test/invocation_test.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-module InvocationTest
- class API < ActionWebService::API::Base
- api_method :add, :expects => [:int, :int], :returns => [:int]
- api_method :transmogrify, :expects_and_returns => [:string]
- api_method :fail_with_reason
- api_method :fail_generic
- api_method :no_before
- api_method :no_after
- api_method :only_one
- api_method :only_two
- end
-
- class Interceptor
- attr :args
-
- def initialize
- @args = nil
- end
-
- def intercept(*args)
- @args = args
- end
- end
-
- InterceptorClass = Interceptor.new
-
- class Service < ActionController::Base
- web_service_api API
-
- before_invocation :intercept_before, :except => [:no_before]
- after_invocation :intercept_after, :except => [:no_after]
- prepend_after_invocation :intercept_after_first, :except => [:no_after]
- prepend_before_invocation :intercept_only, :only => [:only_one, :only_two]
- after_invocation(:only => [:only_one]) do |*args|
- args[0].instance_variable_set('@block_invoked', args[1])
- end
- after_invocation InterceptorClass, :only => [:only_one]
-
- attr_accessor :before_invoked
- attr_accessor :after_invoked
- attr_accessor :after_first_invoked
- attr_accessor :only_invoked
- attr_accessor :block_invoked
- attr_accessor :invocation_result
-
- def initialize
- @before_invoked = nil
- @after_invoked = nil
- @after_first_invoked = nil
- @only_invoked = nil
- @invocation_result = nil
- @block_invoked = nil
- end
-
- def add(a, b)
- a + b
- end
-
- def transmogrify(str)
- str.upcase
- end
-
- def fail_with_reason
- end
-
- def fail_generic
- end
-
- def no_before
- 5
- end
-
- def no_after
- end
-
- def only_one
- end
-
- def only_two
- end
-
- protected
- def intercept_before(name, args)
- @before_invoked = name
- return [false, "permission denied"] if name == :fail_with_reason
- return false if name == :fail_generic
- end
-
- def intercept_after(name, args, result)
- @after_invoked = name
- @invocation_result = result
- end
-
- def intercept_after_first(name, args, result)
- @after_first_invoked = name
- end
-
- def intercept_only(name, args)
- raise "Interception error" unless name == :only_one || name == :only_two
- @only_invoked = name
- end
- end
-end
-
-class TC_Invocation < Test::Unit::TestCase
- include ActionWebService::Invocation
-
- def setup
- @service = InvocationTest::Service.new
- end
-
- def test_invocation
- assert(perform_invocation(:add, 5, 10) == 15)
- assert(perform_invocation(:transmogrify, "hello") == "HELLO")
- assert_raises(NoMethodError) do
- perform_invocation(:nonexistent_method_xyzzy)
- end
- end
-
- def test_interceptor_registration
- assert(InvocationTest::Service.before_invocation_interceptors.length == 2)
- assert(InvocationTest::Service.after_invocation_interceptors.length == 4)
- assert_equal(:intercept_only, InvocationTest::Service.before_invocation_interceptors[0])
- assert_equal(:intercept_after_first, InvocationTest::Service.after_invocation_interceptors[0])
- end
-
- def test_interception
- assert(@service.before_invoked.nil?)
- assert(@service.after_invoked.nil?)
- assert(@service.only_invoked.nil?)
- assert(@service.block_invoked.nil?)
- assert(@service.invocation_result.nil?)
- perform_invocation(:add, 20, 50)
- assert(@service.before_invoked == :add)
- assert(@service.after_invoked == :add)
- assert(@service.invocation_result == 70)
- end
-
- def test_interception_canceling
- reason = nil
- perform_invocation(:fail_with_reason){|r| reason = r}
- assert(@service.before_invoked == :fail_with_reason)
- assert(@service.after_invoked.nil?)
- assert(@service.invocation_result.nil?)
- assert(reason == "permission denied")
- reason = true
- @service.before_invoked = @service.after_invoked = @service.invocation_result = nil
- perform_invocation(:fail_generic){|r| reason = r}
- assert(@service.before_invoked == :fail_generic)
- assert(@service.after_invoked.nil?)
- assert(@service.invocation_result.nil?)
- assert(reason == true)
- end
-
- def test_interception_except_conditions
- perform_invocation(:no_before)
- assert(@service.before_invoked.nil?)
- assert(@service.after_first_invoked == :no_before)
- assert(@service.after_invoked == :no_before)
- assert(@service.invocation_result == 5)
- @service.before_invoked = @service.after_invoked = @service.invocation_result = nil
- perform_invocation(:no_after)
- assert(@service.before_invoked == :no_after)
- assert(@service.after_invoked.nil?)
- assert(@service.invocation_result.nil?)
- end
-
- def test_interception_only_conditions
- assert(@service.only_invoked.nil?)
- perform_invocation(:only_one)
- assert(@service.only_invoked == :only_one)
- assert(@service.block_invoked == :only_one)
- assert(InvocationTest::InterceptorClass.args[1] == :only_one)
- @service.only_invoked = nil
- perform_invocation(:only_two)
- assert(@service.only_invoked == :only_two)
- end
-
- private
- def perform_invocation(method_name, *args, &block)
- @service.perform_invocation(method_name, args, &block)
- end
-end
diff --git a/vendor/rails/actionwebservice/test/run b/vendor/rails/actionwebservice/test/run
deleted file mode 100755
index c8c03727..00000000
--- a/vendor/rails/actionwebservice/test/run
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env ruby
-require 'test/unit'
-$:.unshift(File.dirname(__FILE__) + '/../lib')
-args = Dir[File.join(File.dirname(__FILE__), '*_test.rb')] + Dir[File.join(File.dirname(__FILE__), 'ws/*_test.rb')]
-(r = Test::Unit::AutoRunner.new(true)).process_args(args)
-exit r.run
diff --git a/vendor/rails/actionwebservice/test/scaffolded_controller_test.rb b/vendor/rails/actionwebservice/test/scaffolded_controller_test.rb
deleted file mode 100644
index 09c40a86..00000000
--- a/vendor/rails/actionwebservice/test/scaffolded_controller_test.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-ActionController::Routing::Routes.draw do |map|
- map.connect '', :controller => 'scaffolded'
- map.connect ':controller/:action/:id'
-end
-
-ActionController::Base.template_root = '.'
-
-class ScaffoldPerson < ActionWebService::Struct
- member :id, :int
- member :name, :string
- member :birth, :date
-
- def ==(other)
- self.id == other.id && self.name == other.name
- end
-end
-
-class ScaffoldedControllerTestAPI < ActionWebService::API::Base
- api_method :hello, :expects => [{:integer=>:int}, :string], :returns => [:bool]
- api_method :hello_struct_param, :expects => [{:person => ScaffoldPerson}], :returns => [:bool]
- api_method :date_of_birth, :expects => [ScaffoldPerson], :returns => [:string]
- api_method :bye, :returns => [[ScaffoldPerson]]
- api_method :date_diff, :expects => [{:start_date => :date}, {:end_date => :date}], :returns => [:int]
- api_method :time_diff, :expects => [{:start_time => :time}, {:end_time => :time}], :returns => [:int]
- api_method :base64_upcase, :expects => [:base64], :returns => [:base64]
-end
-
-class ScaffoldedController < ActionController::Base
- web_service_api ScaffoldedControllerTestAPI
- web_service_scaffold :scaffold_invoke
-
- def hello(int, string)
- 0
- end
-
- def hello_struct_param(person)
- 0
- end
-
- def date_of_birth(person)
- person.birth.to_s
- end
-
- def bye
- [ScaffoldPerson.new(:id => 1, :name => "leon"), ScaffoldPerson.new(:id => 2, :name => "paul")]
- end
-
- def rescue_action(e)
- raise e
- end
-
- def date_diff(start_date, end_date)
- end_date - start_date
- end
-
- def time_diff(start_time, end_time)
- end_time - start_time
- end
-
- def base64_upcase(data)
- data.upcase
- end
-end
-
-class ScaffoldedControllerTest < Test::Unit::TestCase
- def setup
- @controller = ScaffoldedController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
-
- def test_scaffold_invoke
- get :scaffold_invoke
- assert_template 'methods.rhtml'
- end
-
- def test_scaffold_invoke_method_params
- get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'Hello'
- assert_template 'parameters.rhtml'
- end
-
- def test_scaffold_invoke_method_params_with_struct
- get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'HelloStructParam'
- assert_template 'parameters.rhtml'
- assert_tag :tag => 'form'
- assert_tag :tag => 'input', :attributes => {:name => "method_params[0][name]"}
- end
-
- def test_scaffold_invoke_submit_hello
- post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'Hello', :method_params => {'0' => '5', '1' => 'hello world'}
- assert_template 'result.rhtml'
- assert_equal false, @controller.instance_eval{ @method_return_value }
- end
-
- def test_scaffold_invoke_submit_bye
- post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'Bye'
- assert_template 'result.rhtml'
- persons = [ScaffoldPerson.new(:id => 1, :name => "leon"), ScaffoldPerson.new(:id => 2, :name => "paul")]
- assert_equal persons, @controller.instance_eval{ @method_return_value }
- end
-
- def test_scaffold_date_params
- get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'DateDiff'
- (0..1).each do |param|
- (1..3).each do |date_part|
- assert_tag :tag => 'select', :attributes => {:name => "method_params[#{param}][#{date_part}]"},
- :children => {:greater_than => 1, :only => {:tag => 'option'}}
- end
- end
-
- post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'DateDiff',
- :method_params => {'0' => {'1' => '2006', '2' => '2', '3' => '1'}, '1' => {'1' => '2006', '2' => '2', '3' => '2'}}
- assert_equal 1, @controller.instance_eval{ @method_return_value }
- end
-
- def test_scaffold_struct_date_params
- post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'DateOfBirth',
- :method_params => {'0' => {'birth' => {'1' => '2006', '2' => '2', '3' => '1'}, 'id' => '1', 'name' => 'person'}}
- assert_equal '2006-02-01', @controller.instance_eval{ @method_return_value }
- end
-
- def test_scaffold_time_params
- get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'TimeDiff'
- (0..1).each do |param|
- (1..6).each do |date_part|
- assert_tag :tag => 'select', :attributes => {:name => "method_params[#{param}][#{date_part}]"},
- :children => {:greater_than => 1, :only => {:tag => 'option'}}
- end
- end
-
- post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'TimeDiff',
- :method_params => {'0' => {'1' => '2006', '2' => '2', '3' => '1', '4' => '1', '5' => '1', '6' => '1'},
- '1' => {'1' => '2006', '2' => '2', '3' => '2', '4' => '1', '5' => '1', '6' => '1'}}
- assert_equal 86400, @controller.instance_eval{ @method_return_value }
- end
-
- def test_scaffold_base64
- get :scaffold_invoke_method_params, :service => 'scaffolded', :method => 'Base64Upcase'
- assert_tag :tag => 'textarea', :attributes => {:name => 'method_params[0]'}
-
- post :scaffold_invoke_submit, :service => 'scaffolded', :method => 'Base64Upcase', :method_params => {'0' => 'scaffold'}
- assert_equal 'SCAFFOLD', @controller.instance_eval{ @method_return_value }
- end
-end
diff --git a/vendor/rails/actionwebservice/test/struct_test.rb b/vendor/rails/actionwebservice/test/struct_test.rb
deleted file mode 100644
index f689746e..00000000
--- a/vendor/rails/actionwebservice/test/struct_test.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-module StructTest
- class Struct < ActionWebService::Struct
- member :id, Integer
- member :name, String
- member :items, [String]
- member :deleted, :bool
- member :emails, [:string]
- end
-end
-
-class TC_Struct < Test::Unit::TestCase
- include StructTest
-
- def setup
- @struct = Struct.new(:id => 5,
- :name => 'hello',
- :items => ['one', 'two'],
- :deleted => true,
- :emails => ['test@test.com'])
- end
-
- def test_members
- assert_equal(5, Struct.members.size)
- assert_equal(Integer, Struct.members[:id].type_class)
- assert_equal(String, Struct.members[:name].type_class)
- assert_equal(String, Struct.members[:items].element_type.type_class)
- assert_equal(TrueClass, Struct.members[:deleted].type_class)
- assert_equal(String, Struct.members[:emails].element_type.type_class)
- end
-
- def test_initializer_and_lookup
- assert_equal(5, @struct.id)
- assert_equal('hello', @struct.name)
- assert_equal(['one', 'two'], @struct.items)
- assert_equal(true, @struct.deleted)
- assert_equal(['test@test.com'], @struct.emails)
- assert_equal(5, @struct['id'])
- assert_equal('hello', @struct['name'])
- assert_equal(['one', 'two'], @struct['items'])
- assert_equal(true, @struct['deleted'])
- assert_equal(['test@test.com'], @struct['emails'])
- end
-
- def test_each_pair
- @struct.each_pair do |name, value|
- assert_equal @struct.__send__(name), value
- assert_equal @struct[name], value
- end
- end
-end
diff --git a/vendor/rails/actionwebservice/test/test_invoke_test.rb b/vendor/rails/actionwebservice/test/test_invoke_test.rb
deleted file mode 100644
index 72ebc719..00000000
--- a/vendor/rails/actionwebservice/test/test_invoke_test.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-require 'action_web_service/test_invoke'
-
-class TestInvokeAPI < ActionWebService::API::Base
- api_method :null
- api_method :add, :expects => [:int, :int], :returns => [:int]
-end
-
-class TestInvokeService < ActionWebService::Base
- web_service_api TestInvokeAPI
-
- attr :invoked
-
- def add(a, b)
- @invoked = true
- a + b
- end
-
- def null
- end
-end
-
-class TestController < ActionController::Base
- def rescue_action(e); raise e; end
-end
-
-class TestInvokeDirectController < TestController
- web_service_api TestInvokeAPI
-
- attr :invoked
-
- def add
- @invoked = true
- @method_params[0] + @method_params[1]
- end
-
- def null
- end
-end
-
-class TestInvokeDelegatedController < TestController
- web_service_dispatching_mode :delegated
- web_service :service, TestInvokeService.new
-end
-
-class TestInvokeLayeredController < TestController
- web_service_dispatching_mode :layered
- web_service(:one) { @service_one ||= TestInvokeService.new }
- web_service(:two) { @service_two ||= TestInvokeService.new }
-end
-
-class TestInvokeTest < Test::Unit::TestCase
- def setup
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
-
- def test_direct_add
- @controller = TestInvokeDirectController.new
- assert_equal nil, @controller.invoked
- result = invoke :add, 25, 25
- assert_equal 50, result
- assert_equal true, @controller.invoked
- end
-
- def test_delegated_add
- @controller = TestInvokeDelegatedController.new
- assert_equal nil, @controller.web_service_object(:service).invoked
- result = invoke_delegated :service, :add, 100, 50
- assert_equal 150, result
- assert_equal true, @controller.web_service_object(:service).invoked
- end
-
- def test_layered_add
- [:soap, :xmlrpc].each do |protocol|
- @protocol = protocol
- [:one, :two].each do |service|
- @controller = TestInvokeLayeredController.new
- assert_equal nil, @controller.web_service_object(service).invoked
- result = invoke_layered service, :add, 200, -50
- assert_equal 150, result
- assert_equal true, @controller.web_service_object(service).invoked
- end
- end
- end
-
- def test_layered_fail_with_wrong_number_of_arguments
- [:soap, :xmlrpc].each do |protocol|
- @protocol = protocol
- [:one, :two].each do |service|
- @controller = TestInvokeLayeredController.new
- assert_raise(ArgumentError) { invoke_layered service, :add, 1 }
- end
- end
- end
-
- def test_delegated_fail_with_wrong_number_of_arguments
- @controller = TestInvokeDelegatedController.new
- assert_raise(ArgumentError) { invoke_delegated :service, :add, 1 }
- end
-
- def test_direct_fail_with_wrong_number_of_arguments
- @controller = TestInvokeDirectController.new
- assert_raise(ArgumentError) { invoke :add, 1 }
- end
-
- def test_with_no_parameters_declared
- @controller = TestInvokeDirectController.new
- assert_nil invoke(:null)
- end
-
-end
diff --git a/vendor/rails/activerecord/CHANGELOG b/vendor/rails/activerecord/CHANGELOG
index 07ed4d74..fe44b1b6 100644
--- a/vendor/rails/activerecord/CHANGELOG
+++ b/vendor/rails/activerecord/CHANGELOG
@@ -1,21 +1,352 @@
-*1.15.5* (October 12th, 2007)
+*2.0.2* (December 16th, 2007)
-* Depend on Action Pack 1.4.4
+* Ensure optimistic locking handles nil #lock_version values properly. Closes #10510 [rick]
+
+* Make the Fixtures Test::Unit enhancements more supporting for double-loaded test cases. Closes #10379 [brynary]
+
+* Fix that validates_acceptance_of still works for non-existent tables (useful for bootstrapping new databases). Closes #10474 [hasmanyjosh]
+
+* Ensure that the :uniq option for has_many :through associations retains the order. #10463 [remvee]
+
+* Base.exists? doesn't rescue exceptions to avoid hiding SQL errors. #10458 [Michael Klishin]
+
+* Documentation: Active Record exceptions, destroy_all and delete_all. #10444, #10447 [Michael Klishin]
-*1.15.4* (October 4th, 2007)
+*2.0.1* (December 7th, 2007)
-* Fix #count on a has_many :through association so that it recognizes the :uniq option. Closes #8801 [lifofifo]
+* Removed query cache rescue as it could cause code to be run twice (closes #10408) [DHH]
+
+
+*2.0.0* (December 6th, 2007)
+
+* Anchor DateTimeTest to fixed DateTime instead of a variable value based on Time.now#advance#to_datetime, so that this test passes on 64-bit platforms running Ruby 1.8.6+ [Geoff Buesing]
+
+* Fixed that the Query Cache should just be ignored if the database is misconfigured (so that the "About your applications environment" works even before the database has been created) [DHH]
+
+* Fixed that the truncation of strings longer than 50 chars should use inspect
+so newlines etc are escaped #10385 [Norbert Crombach]
+
+* Fixed that habtm associations should be able to set :select as part of their definition and have that honored [DHH]
+
+* Document how the :include option can be used in Calculations::calculate. Closes #7446 [adamwiggins, ultimoamore]
+
+* Fix typo in documentation for polymorphic associations w/STI. Closes #7461 [johnjosephbachir]
+
+* Reveal that the type option in migrations can be any supported column type for your database but also include caveat about agnosticism. Closes #7531 [adamwiggins, mikong]
+
+* More complete documentation for find_by_sql. Closes #7912 [fearoffish]
+
+* Added ActiveRecord::Base#becomes to turn a record into one of another class (mostly relevant for STIs) [DHH]. Example:
+
+ render :partial => @client.becomes(Company) # renders companies/company instead of clients/client
+
+* Fixed that to_xml should not automatically pass :procs to associations included with :include #10162 [Cheah Chu Yeow]
+
+* Fix documentation typo introduced in [8250]. Closes #10339 [Henrik N]
+
+* Foxy fixtures: support single-table inheritance. #10234 [tom]
+
+* Foxy fixtures: allow mixed usage to make migration easier and more attractive. #10004 [lotswholetime]
+
+* Make the record_timestamps class-inheritable so it can be set per model. #10004 [tmacedo]
+
+* Allow validates_acceptance_of to use a real attribute instead of only virtual (so you can record that the acceptance occured) #7457 [ambethia]
+
+* DateTimes use Ruby's default calendar reform setting. #10201 [Geoff Buesing]
+
+* Dynamic finders on association collections respect association :order and :limit. #10211, #10227 [Patrick Joyce, Rick Olson, Jack Danger Canty]
+
+* Add 'foxy' support for fixtures of polymorphic associations. #10183 [jbarnette, David Lowenfels]
+
+* validates_inclusion_of and validates_exclusion_of allow formatted :message strings. #8132 [devrieda, Mike Naberezny]
+
+* attr_readonly behaves well with optimistic locking. #10188 [Nick Bugajski]
+
+* Base#to_xml supports the nil="true" attribute like Hash#to_xml. #8268 [Catfish]
+
+* Change plings to the more conventional quotes in the documentation. Closes #10104 [danger]
+
+* Fix HasManyThrough Association so it uses :conditions on the HasMany Association. Closes #9729 [danger]
+
+* Ensure that column names are quoted. Closes #10134 [wesley.moxam]
+
+* Smattering of grammatical fixes to documentation. Closes #10083 [BobSilva]
+
+* Enhance explanation with more examples for attr_accessible macro. Closes #8095 [fearoffish, Marcel Molina]
+
+* Update association/method mapping table to refected latest collection methods for has_many :through. Closes #8772 [Pratik Naik]
+
+* Explain semantics of having several different AR instances in a transaction block. Closes #9036 [jacobat, Marcel Molina]
+
+* Update Schema documentation to use updated sexy migration notation. Closes #10086 [sjgman9]
+
+* Make fixtures work with the new test subclasses. [Tarmo Tänav, Koz]
+
+* Introduce finder :joins with associations. Same :include syntax but with inner rather than outer joins. #10012 [RubyRedRick]
+ # Find users with an avatar
+ User.find(:all, :joins => :avatar)
+
+ # Find posts with a high-rated comment.
+ Post.find(:all, :joins => :comments, :conditions => 'comments.rating > 3')
+
+* Associations: speedup duplicate record check. #10011 [Pratik Naik]
+
+* Make sure that << works on has_many associations on unsaved records. Closes #9989 [hasmanyjosh]
+
+* Allow association redefinition in subclasses. #9346 [wildchild]
+
+* Fix has_many :through delete with custom foreign keys. #6466 [naffis]
+
+* Foxy fixtures, from rathole (http://svn.geeksomnia.com/rathole/trunk/README)
+ - stable, autogenerated IDs
+ - specify associations (belongs_to, has_one, has_many) by label, not ID
+ - specify HABTM associations as inline lists
+ - autofill timestamp columns
+ - support YAML defaults
+ - fixture label interpolation
+ Enabled for fixtures that correspond to a model class and don't specify a primary key value. #9981 [jbarnette]
+
+* Add docs explaining how to protect all attributes using attr_accessible with no arguments. Closes #9631 [boone, rmm5t]
+
+* Update add_index documentation to use new options api. Closes #9787 [Kamal Fariz Mahyuddin]
+
+* Allow find on a has_many association defined with :finder_sql to accept id arguments as strings like regular find does. Closes #9916 [krishna]
+
+* Use VALID_FIND_OPTIONS when resolving :find scoping rather than hard coding the list of valid find options. Closes #9443 [sur]
+
+* Limited eager loading no longer ignores scoped :order. Closes #9561 [danger, Josh Peek]
+
+* Assigning an instance of a foreign class to a composed_of aggregate calls an optional conversion block. Refactor and simplify composed_of implementation. #6322 [brandon, Chris Cruft]
+
+* Assigning nil to a composed_of aggregate also sets its immediate value to nil. #9843 [Chris Cruft]
+
+* Ensure that mysql quotes table names with database names correctly. Closes #9911 [crayz]
+
+ "foo.bar" => "`foo`.`bar`"
+
+* Complete the assimilation of Sexy Migrations from ErrFree [Chris Wanstrath, PJ Hyett]
+ http://errtheblog.com/post/2381
+
+* Qualified column names work in hash conditions, like :conditions => { 'comments.created_at' => ... }. #9733 [danger]
+
+* Fix regression where the association would not construct new finder SQL on save causing bogus queries for "WHERE owner_id = NULL" even after owner was saved. #8713 [Bryan Helmkamp]
+
+* Refactor association create and build so before & after callbacks behave consistently. #8854 [Pratik Naik, mortent]
+
+* Quote table names. Defaults to column quoting. #4593 [Justin Lynn, gwcoffey, eadz, Dmitry V. Sabanin, Jeremy Kemper]
+
+* Alias association #build to #new so it behaves predictably. #8787 [Pratik Naik]
+
+* Add notes to documentation regarding attr_readonly behavior with counter caches and polymorphic associations. Closes #9835 [saimonmoore, rick]
+
+* Observers can observe model names as symbols properly now. Closes #9869 [queso]
+
+* find_and_(initialize|create)_by methods can now properly initialize protected attributes [Tobias Luetke]
+
+* belongs_to infers the foreign key from the association name instead of from the class name. [Jeremy Kemper]
+
+* PostgreSQL: support multiline default values. #7533 [Carl Lerche, aguynamedryan, Rein Henrichs, Tarmo Tänav]
+
+* MySQL: fix change_column on not-null columns that don't accept dfeault values of ''. #6663 [Jonathan Viney, Tarmo Tänav]
+
+* validates_uniqueness_of behaves well with abstract superclasses and
+single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Beausoleil, Josh Peek, Tarmo Tänav, pat]
+
+* Warn about protected attribute assigments in development and test environments when mass-assigning to an attr_protected attribute. #9802 [Henrik N]
+
+* Speedup database date/time parsing. [Jeremy Kemper, Tarmo Tänav]
+
+* Fix calling .clear on a has_many :dependent=>:delete_all association. [Tarmo Tänav]
+
+* Allow change_column to set NOT NULL in the PostgreSQL adapter [Tarmo Tänav]
+
+* Fix that ActiveRecord would create attribute methods and override custom attribute getters if the method is also defined in Kernel.methods. [Rick]
+
+* Don't call attr_readonly on polymorphic belongs_to associations, in case it matches the name of some other non-ActiveRecord class/module. [Rick]
+
+* Try loading activerecord--adapter gem before trying a plain require so you can use custom gems for the bundled adapters. Also stops gems from requiring an adapter from an old Active Record gem. [Jeremy Kemper, Derrick Spell]
+
+
+*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.14.2 - 1.15.3]
+
+* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [dcmanges]
+
+ class Comment < ActiveRecord::Base
+ # Automatically sets Article#comments_count as readonly.
+ belongs_to :article, :counter_cache => :comments_count
+ end
+
+ class Article < ActiveRecord::Base
+ attr_readonly :approved_comments_count
+ end
+
+* Make size for has_many :through use counter cache if it exists. Closes #9734 [xaviershay]
+
+* Remove DB2 adapter since IBM chooses to maintain their own adapter instead. [Jeremy Kemper]
+
+* Extract Oracle, SQLServer, and Sybase adapters into gems. [Jeremy Kemper]
+
+* Added fixture caching that'll speed up a normal fixture-powered test suite between 50% and 100% #9682 [Frederick Cheung]
+
+* Correctly quote id list for limited eager loading. #7482 [tmacedo]
+
+* Fixed that using version-targetted migrates would fail on loggers other than the default one #7430 [valeksenko]
+
+* Fixed rename_column for SQLite when using symbols for the column names #8616 [drodriguez]
+
+* Added the possibility of using symbols in addition to concrete classes with ActiveRecord::Observer#observe. #3998 [Robby Russell, Tarmo Tänav]
+
+* Added ActiveRecord::Base#to_json/from_json [DHH, Cheah Chu Yeow]
+
+* Added ActiveRecord::Base#from_xml [DHH]. Example:
+
+ xml = "David "
+ Person.new.from_xml(xml).name # => "David"
+
+* Define dynamic finders as real methods after first usage. [bscofield]
+
+* Deprecation: remove deprecated threaded_connections methods. Use allow_concurrency instead. [Jeremy Kemper]
+
+* Associations macros accept extension blocks alongside modules. #9346 [Josh Peek]
+
+* Speed up and simplify query caching. [Jeremy Kemper]
+
+* connection.select_rows 'sql' returns an array (rows) of arrays (field values). #2329 [Michael Schuerig]
+
+* Eager loading respects explicit :joins. #9496 [dasil003]
+
+* Extract Firebird, FrontBase, and OpenBase adapters into gems. #9508, #9509, #9510 [Jeremy Kemper]
+
+* RubyGem database adapters: expects a gem named activerecord--adapter with active_record/connection_adapters/_adapter.rb in its load path. [Jeremy Kemper]
+
+* Fixed that altering join tables in migrations would fail w/ sqlite3 #7453 [TimoMihaljov/brandon]
+
+* Fix association writer with :dependent => :nullify. #7314 [Jonathan Viney]
+
+* OpenBase: update for new lib and latest Rails. Support migrations. #8748 [dcsesq]
+
+* Moved acts_as_tree into a plugin of the same name on the official Rails svn. #9514 [Pratik Naik]
+
+* Moved acts_as_nested_set into a plugin of the same name on the official Rails svn. #9516 [Josh Peek]
+
+* Moved acts_as_list into a plugin of the same name on the official Rails svn. [Josh Peek]
+
+* Explicitly require active_record/query_cache before using it. [Jeremy Kemper]
+
+* Fix bug where unserializing an attribute attempts to modify a frozen @attributes hash for a deleted record. [Rick, marclove]
+
+* Performance: absorb instantiate and initialize_with_callbacks into the Base methods. [Jeremy Kemper]
+
+* Fixed that eager loading queries and with_scope should respect the :group option [DHH]
+
+* Improve performance and functionality of the postgresql adapter. Closes #8049 [roderickvd]
+
+ For more information see: http://dev.rubyonrails.org/ticket/8049
* Don't clobber includes passed to has_many.count [danger]
* Make sure has_many uses :include when counting [danger]
+* Change the implementation of ActiveRecord's attribute reader and writer methods [nzkoz]
+ - Generate Reader and Writer methods which cache attribute values in hashes. This is to avoid repeatedly parsing the same date or integer columns.
+ - Change exception raised when users use find with :select then try to access a skipped column. Plugins could override missing_attribute() to lazily load the columns.
+ - Move method definition to the class, instead of the instance
+ - Always generate the readers, writers and predicate methods.
+
+* Perform a deep #dup on query cache results so that modifying activerecord attributes does not modify the cached attributes. [Rick]
+
+# Ensure that has_many :through associations use a count query instead of loading the target when #size is called. Closes #8800 [Pratik Naik]
+
+* Added :unless clause to validations #8003 [monki]. Example:
+
+ def using_open_id?
+ !identity_url.blank?
+ end
+
+ validates_presence_of :identity_url, :if => using_open_id?
+ validates_presence_of :username, :unless => using_open_id?
+ validates_presence_of :password, :unless => using_open_id?
+
+* Fix #count on a has_many :through association so that it recognizes the :uniq option. Closes #8801 [Pratik Naik]
+
+* Fix and properly document/test count(column_name) usage. Closes #8999 [Pratik Naik]
+
+* Remove deprecated count(conditions=nil, joins=nil) usage. Closes #8993 [Pratik Naik]
+
+* Change belongs_to so that the foreign_key assumption is taken from the association name, not the class name. Closes #8992 [hasmanyjosh]
+
+ OLD
+ belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is user_id
+
+ NEW
+ belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is visitor_id
+
+* Remove spurious tests from deprecated_associations_test, most of these aren't deprecated, and are duplicated in associations_test. Closes #8987 [Pratik Naik]
+
+* Make create! on a has_many :through association return the association object. Not the collection. Closes #8786 [Pratik Naik]
+
+* Move from select * to select tablename.* to avoid clobbering IDs. Closes #8889 [dasil003]
+
+* Don't call unsupported methods on associated objects when using :include, :method with to_xml #7307, [manfred, jwilger]
+
+* Define collection singular ids method for has_many :through associations. #8763 [Pratik Naik]
+
+* Array attribute conditions work with proxied association collections. #8318 [Kamal Fariz Mahyuddin, theamazingrando]
+
+* Fix polymorphic has_one associations declared in an abstract class. #8638 [Pratik Naik, Dax Huiberts]
+
+* Fixed validates_associated should not stop on the first error. #4276 [mrj, Manfred Stienstra, Josh Peek]
+
+* Rollback if commit raises an exception. #8642 [kik, Jeremy Kemper]
+
+* Update tests' use of fixtures for the new collections api. #8726 [Kamal Fariz Mahyuddin]
+
* Save associated records only if the association is already loaded. #8713 [blaine]
-* Changing the :default Date format doesn't break date quoting. #6312 [bshand, Elias]
+* MySQL: fix show_variable. #8448 [matt, Jeremy Kemper]
-* Allow nil serialized attributes with a set class constraint. #7293 [sandofsky]
+* Fixtures: correctly delete and insert fixtures in a single transaction. #8553 [Michael Schuerig]
+
+* Fixtures: people(:technomancy, :josh) returns both fixtures. #7880 [technomancy, Josh Peek]
+
+* Calculations support non-numeric foreign keys. #8154 [Kamal Fariz Mahyuddin]
+
+* with_scope is protected. #8524 [Josh Peek]
+
+* Quickref for association methods. #7723 [marclove, Mindsweeper]
+
+* Calculations: return nil average instead of 0 when there are no rows to average. #8298 [davidw]
+
+* acts_as_nested_set: direct_children is sorted correctly. #4761 [Josh Peek, rails@33lc0.net]
+
+* Raise an exception if both attr_protected and attr_accessible are declared. #8507 [stellsmi]
+
+* SQLite, MySQL, PostgreSQL, Oracle: quote column names in column migration SQL statements. #8466 [marclove, lorenjohnson]
+
+* Allow nil serialized attributes with a set class constraint. #7293 [sandofsky]
+
+* Oracle: support binary fixtures. #7987 [Michael Schoen]
+
+* Fixtures: pull fixture insertion into the database adapters. #7987 [Michael Schoen]
+
+* Announce migration versions as they're performed. [Jeremy Kemper]
+
+* find gracefully copes with blank :conditions. #7599 [Dan Manges, johnnyb]
+
+* validates_numericality_of takes :greater_than, :greater_than_or_equal_to, :equal_to, :less_than, :less_than_or_equal_to, :odd, and :even options. #3952 [Bob Silva, Dan Kubb, Josh Peek]
+
+* MySQL: create_database takes :charset and :collation options. Charset defaults to utf8. #8448 [matt]
+
+* Find with a list of ids supports limit/offset. #8437 [hrudududu]
+
+* Optimistic locking: revert the lock version when an update fails. #7840 [plang]
+
+* Migrations: add_column supports custom column types. #7742 [jsgarvin, Theory]
+
+* Load database adapters on demand. Eliminates config.connection_adapters and RAILS_CONNECTION_ADAPTERS. Add your lib directory to the $LOAD_PATH and put your custom adapter in lib/active_record/connection_adapters/adaptername_adapter.rb. This way you can provide custom adapters as plugins or gems without modifying Rails. [Jeremy Kemper]
+
+* Ensure that associations with :dependent => :delete_all respect :conditions option. Closes #8034 [danger, Josh Peek, Rick]
* belongs_to assignment creates a new proxy rather than modifying its target in-place. #8412 [mmangino@elevatedrails.com]
@@ -23,25 +354,629 @@
* Document deep eager includes. #6267 [Josh Susser, Dan Manges]
+* Document warning that associations names shouldn't be reserved words. #4378 [murphy@cYcnus.de, Josh Susser]
+
+* Sanitize Base#inspect. #8392, #8623 [Nik Wakelin, jnoon]
+
+* Replace the transaction {|transaction|..} semantics with a new Exception ActiveRecord::Rollback. [Koz]
+
* Oracle: extract column length for CHAR also. #7866 [ymendel]
-* Small additions and fixes for ActiveRecord documentation. Closes #7342 [jeremymcanally]
+* Document :allow_nil option for validates_acceptance_of since it defaults to true. [tzaharia]
-* SQLite: binary escaping works with $KCODE='u'. #7862 [tsuka]
+* Update documentation for :dependent declaration so that it explicitly uses the non-deprecated API. [danger]
+
+* Add documentation caveat about when to use count_by_sql. [fearoffish]
+
+* Enhance documentation for increment_counter and decrement_counter. [fearoffish]
+
+* Provide brief introduction to what optimistic locking is. [fearoffish]
+
+* Add documentation for :encoding option to mysql adapter. [marclove]
+
+* Added short-hand declaration style to migrations (inspiration from Sexy Migrations, http://errtheblog.com/post/2381) [DHH]. Example:
+
+ create_table "products" do |t|
+ t.column "shop_id", :integer
+ t.column "creator_id", :integer
+ t.column "name", :string, :default => "Untitled"
+ t.column "value", :string, :default => "Untitled"
+ t.column "created_at", :datetime
+ t.column "updated_at", :datetime
+ end
+
+ ...can now be written as:
+
+ create_table :products do |t|
+ t.integer :shop_id, :creator_id
+ t.string :name, :value, :default => "Untitled"
+ t.timestamps
+ end
+
+* Use association name for the wrapper element when using .to_xml. Previous behavior lead to non-deterministic situations with STI and polymorphic associations. [Koz, jstrachan]
+
+* Improve performance of calling .create on has_many :through associations. [evan]
* Improved cloning performance by relying less on exception raising #8159 [Blaine]
+* Added ActiveRecord::Base.inspect to return a column-view like # [DHH]
-*1.15.3* (March 12th, 2007)
+* Added yielding of Builder instance for ActiveRecord::Base#to_xml calls [DHH]
-* Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
+* Small additions and fixes for ActiveRecord documentation. Closes #7342 [jeremymcanally]
+
+* Add helpful debugging info to the ActiveRecord::StatementInvalid exception in ActiveRecord::ConnectionAdapters::SqliteAdapter#table_structure. Closes #7925. [court3nay]
+
+* SQLite: binary escaping works with $KCODE='u'. #7862 [tsuka]
+
+* Base#to_xml supports serialized attributes. #7502 [jonathan]
+
+* Base.update_all :order and :limit options. Useful for MySQL updates that must be ordered to avoid violating unique constraints. [Jeremy Kemper]
+
+* Remove deprecated object transactions. People relying on this functionality should install the object_transactions plugin at http://code.bitsweat.net/svn/object_transactions. Closes #5637 [Koz, Jeremy Kemper]
+
+* PostgreSQL: remove DateTime -> Time downcast. Warning: do not enable translate_results for the C bindings if you have timestamps outside Time's domain. [Jeremy Kemper]
+
+* find_or_create_by_* takes a hash so you can create with more attributes than are in the method name. For example, Person.find_or_create_by_name(:name => 'Henry', :comments => 'Hi new user!') is equivalent to Person.find_by_name('Henry') || Person.create(:name => 'Henry', :comments => 'Hi new user!'). #7368 [Josh Susser]
+
+* Make sure with_scope takes both :select and :joins into account when setting :readonly. Allows you to save records you retrieve using method_missing on a has_many :through associations. [Koz]
+
+* Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
+
+* Consistent public/protected/private visibility for chained methods. #7813 [Dan Manges]
+
+* Oracle: fix quoted primary keys and datetime overflow. #7798 [Michael Schoen]
* Consistently quote primary key column names. #7763 [toolmantim]
* Fixtures: fix YAML ordered map support. #2665 [Manuel Holtgrewe, nfbuckley]
+* DateTimes assume the default timezone. #7764 [Geoff Buesing]
+
+* Sybase: hide timestamp columns since they're inherently read-only. #7716 [Mike Joyce]
+
+* Oracle: overflow Time to DateTime. #7718 [Michael Schoen]
+
+* PostgreSQL: don't use async_exec and async_query with postgres-pr. #7727, #7762 [flowdelic, toolmantim]
+
* Fix has_many :through << with custom foreign keys. #6466, #7153 [naffis, Rich Collins]
+* Test DateTime native type in migrations, including an edge case with dates
+during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
+
+* SQLServer: correctly schema-dump tables with no indexes or descending indexes. #7333, #7703 [Jakob S, Tom Ward]
+
+* SQLServer: recognize real column type as Ruby float. #7057 [sethladd, Tom Ward]
+
+* Added fixtures :all as a way of loading all fixtures in the fixture directory at once #7214 [manfred]
+
+* Added database connection as a yield parameter to ActiveRecord::Base.transaction so you can manually rollback [DHH]. Example:
+
+ transaction do |transaction|
+ david.withdrawal(100)
+ mary.deposit(100)
+ transaction.rollback! # rolls back the transaction that was otherwise going to be successful
+ end
+
+* Made increment_counter/decrement_counter play nicely with optimistic locking, and added a more general update_counters method [Jamis Buck]
+
+* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Luetke]
+ Task.cache { Task.find(1); Task.find(1) } #=> 1 query
+
+* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
+
+* Oracle: fix lob and text default handling. #7344 [gfriedrich, Michael Schoen]
+
+* SQLServer: don't choke on strings containing 'null'. #7083 [Jakob S]
+
+* MySQL: blob and text columns may not have defaults in 5.x. Update fixtures schema for strict mode. #6695 [Dan Kubb]
+
+* update_all can take a Hash argument. sanitize_sql splits into two methods for conditions and assignment since NULL values and delimiters are handled differently. #6583, #7365 [sandofsky, Assaf]
+
+* MySQL: SET SQL_AUTO_IS_NULL=0 so 'where id is null' doesn't select the last inserted id. #6778 [Jonathan Viney, timc]
+
+* Use Date#to_s(:db) for quoted dates. #7411 [Michael Schoen]
+
+* Don't create instance writer methods for class attributes. Closes #7401 [Rick]
+
+* Docs: validations examples. #7343 [zackchandler]
+
+* Add missing tests ensuring callbacks work with class inheritance. Closes #7339 [sandofsky]
+
+* Fixtures use the table name and connection from set_fixture_class. #7330 [Anthony Eden]
+
+* Remove useless code in #attribute_present? since 0 != blank?. Closes #7249 [Josh Susser]
+
+* Fix minor doc typos. Closes #7157 [Josh Susser]
+
+* Fix incorrect usage of #classify when creating the eager loading join statement. Closes #7044 [Josh Susser]
+
+* SQLServer: quote table name in indexes query. #2928 [keithm@infused.org]
+
+* Subclasses of an abstract class work with single-table inheritance. #5704, #7284 [BertG, nick+rails@ag.arizona.edu]
+
+* Make sure sqlite3 driver closes open connections on disconnect [Rob Rasmussen]
+
+* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 [jeremymcanally]
+
+* change_column accepts :default => nil. Skip column options for primary keys. #6956, #7048 [Dan Manges, Jeremy Kemper]
+
+* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 [Jonathan Viney, manfred, altano@bigfoot.com]
+
+* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default. #7000 [Michael Schoen]
+
+* MySQL: retain SSL settings on reconnect. #6976 [randyv2]
+
+* Apply scoping during initialize instead of create. Fixes setting of foreign key when using find_or_initialize_by with scoping. [Cody Fauser]
+
+* SQLServer: handle [quoted] table names. #6635 [rrich]
+
+* acts_as_nested_set works with single-table inheritance. #6030 [Josh Susser]
+
+* PostgreSQL, Oracle: correctly perform eager finds with :limit and :order. #4668, #7021 [eventualbuddha, Michael Schoen]
+
+* Pass a range in :conditions to use the SQL BETWEEN operator. #6974 [Dan Manges]
+ Student.find(:all, :conditions => { :grade => 9..12 })
+
+* Fix the Oracle adapter for serialized attributes stored in CLOBs. Closes #6825 [mschoen, tdfowler]
+
+* [DOCS] Apply more documentation for ActiveRecord Reflection. Closes #4055 [Robby Russell]
+
+* [DOCS] Document :allow_nil option of #validate_uniqueness_of. Closes #3143 [Caio Chassot]
+
+* Bring the sybase adapter up to scratch for 1.2 release. [jsheets]
+
+* Rollback new_record? and id when an exception is raised in a save callback. #6910 [Ben Curren, outerim]
+
+* Pushing a record on an association collection doesn't unnecessarily load all the associated records. [Obie Fernandez, Jeremy Kemper]
+
+* Oracle: fix connection reset failure. #6846 [leonlleslie]
+
+* Subclass instantiation doesn't try to explicitly require the corresponding subclass. #6840 [leei, Jeremy Kemper]
+
+* fix faulty inheritance tests and that eager loading grabs the wrong inheritance column when the class of your association is an STI subclass. Closes #6859 [protocool]
+
+* Consolidated different create and create! versions to call through to the base class with scope. This fixes inconsistencies, especially related to protected attribtues. Closes #5847 [Alexander Dymo, Tobias Luetke]
+
+* find supports :lock with :include. Check whether your database allows SELECT ... FOR UPDATE with outer joins before using. #6764 [vitaly, Jeremy Kemper]
+
+* Add AssociationCollection#create! to be consistent with AssociationCollection#create when dealing with a foreign key that is a protected attribute [Cody Fauser]
+
+* Added counter optimization for AssociationCollection#any? so person.friends.any? won't actually load the full association if we have the count in a cheaper form [DHH]
+
+* Change fixture_path to a class inheritable accessor allowing test cases to have their own custom set of fixtures. #6672 [zdennis]
+
+* Quote ActiveSupport::Multibyte::Chars. #6653 [Julian Tarkhanov]
+
+* Simplify query_attribute by typecasting the attribute value and checking whether it's nil, false, zero or blank. #6659 [Jonathan Viney]
+
+* validates_numericality_of uses \A \Z to ensure the entire string matches rather than ^ $ which may match one valid line of a multiline string. #5716 [Andreas Schwarz]
+
+* Run validations in the order they were declared. #6657 [obrie]
+
+* MySQL: detect when a NOT NULL column without a default value is misreported as default ''. Can't detect for string, text, and binary columns since '' is a legitimate default. #6156 [simon@redhillconsulting.com.au, obrie, Jonathan Viney, Jeremy Kemper]
+
+* Simplify association proxy implementation by factoring construct_scope out of method_missing. #6643 [martin]
+
+* Oracle: automatically detect the primary key. #6594 [vesaria, Michael Schoen]
+
+* Oracle: to increase performance, prefetch 100 rows and enable similar cursor sharing. Both are configurable in database.yml. #6607 [philbogle@gmail.com, ray.fortna@jobster.com, Michael Schoen]
+
+* Don't inspect unloaded associations. #2905 [lmarlow]
+
+* SQLite: use AUTOINCREMENT primary key in >= 3.1.0. #6588, #6616 [careo, lukfugl]
+
+* Cache inheritance_column. #6592 [Stefan Kaes]
+
+* Firebird: decimal/numeric support. #6408 [macrnic]
+
+* make add_order a tad faster. #6567 [Stefan Kaes]
+
+* Find with :include respects scoped :order. #5850
+
+* Support nil and Array in :conditions => { attr => value } hashes. #6548 [Assaf, Jeremy Kemper]
+ find(:all, :conditions => { :topic_id => [1, 2, 3], :last_read => nil }
+
+* Consistently use LOWER() for uniqueness validations (rather than mixing with UPPER()) so the database can always use a functional index on the lowercased column. #6495 [Si]
+
+* SQLite: fix calculations workaround, remove count(distinct) query rewrite, cleanup test connection scripts. [Jeremy Kemper]
+
+* SQLite: count(distinct) queries supported in >= 3.2.6. #6544 [Bob Silva]
+
+* Dynamically generate reader methods for serialized attributes. #6362 [Stefan Kaes]
+
+* Deprecation: object transactions warning. [Jeremy Kemper]
+
+* has_one :dependent => :nullify ignores nil associates. #4848, #6528 [bellis@deepthought.org, janovetz, Jeremy Kemper]
+
+* Oracle: resolve test failures, use prefetched primary key for inserts, check for null defaults, fix limited id selection for eager loading. Factor out some common methods from all adapters. #6515 [Michael Schoen]
+
+* Make add_column use the options hash with the Sqlite Adapter. Closes #6464 [obrie]
+
+* Document other options available to migration's add_column. #6419 [grg]
+
+* MySQL: all_hashes compatibility with old MysqlRes class. #6429, #6601 [Jeremy Kemper]
+
+* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. [Jonathan Viney]
+
+* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick]
+
+* Restore eager condition interpolation, document it's differences [Rick]
+
+* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started. #6282 [Jacob Fugal, Jeremy Kemper]
+
+* Add #delete support to has_many :through associations. Closes #6049 [Martin Landers]
+
+* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 [Rick]
+
+* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 [turnip@turnipspatch.com]
+
+* Added timeout option to SQLite3 configurations to deal more gracefully with SQLite3::BusyException, now the connection can instead retry for x seconds to see if the db clears up before throwing that exception #6126 [wreese@gmail.com]
+
+* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 [jonathan]
+
+* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [BobSilva]
+
+* The has_many create method works with polymorphic associations. #6361 [Dan Peterson]
+
+* MySQL: introduce Mysql::Result#all_hashes to support further optimization. #5581 [Stefan Kaes]
+
+* save! shouldn't validate twice. #6324 [maiha, Bob Silva]
+
+* Association collections have an _ids reader method to match the existing writer for collection_select convenience (e.g. employee.task_ids). The writer method skips blank ids so you can safely do @employee.task_ids = params[:tasks] without checking every time for an empty list or blank values. #1887, #5780 [Michael Schuerig]
+
+* Add an attribute reader method for ActiveRecord::Base.observers [Rick Olson]
+
+* Deprecation: count class method should be called with an options hash rather than two args for conditions and joins. #6287 [Bob Silva]
+
+* has_one associations with a nil target may be safely marshaled. #6279 [norbauer, Jeremy Kemper]
+
+* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Koz]
+
+* Add a :namespace option to AR::Base#to_xml [Koz]
+
+* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. [Jeremy Kemper]
+
+* Mock Time.now for more accurate Touch mixin tests. #6213 [Dan Peterson]
+
+* Improve yaml fixtures error reporting. #6205 [Bruce Williams]
+
+* Rename AR::Base#quote so people can use that name in their models. #3628 [Koz]
+
+* Add deprecation warning for inferred foreign key. #6029 [Josh Susser]
+
+* Fixed the Ruby/MySQL adapter we ship with Active Record to work with the new authentication handshake that was introduced in MySQL 4.1, along with the other protocol changes made at that time #5723 [jimw@mysql.com]
+
+* Deprecation: use :dependent => :delete_all rather than :exclusively_dependent => true. #6024 [Josh Susser]
+
+* Document validates_presences_of behavior with booleans: you probably want validates_inclusion_of :attr, :in => [true, false]. #2253 [Bob Silva]
+
+* Optimistic locking: gracefully handle nil versions, treat as zero. #5908 [Tom Ward]
+
+* to_xml: the :methods option works on arrays of records. #5845 [Josh Starcher]
+
+* Deprecation: update docs. #5998 [jakob@mentalized.net, Kevin Clark]
+
+* Add some XmlSerialization tests for ActiveRecord [Rick Olson]
+
+* has_many :through conditions are sanitized by the associating class. #5971 [martin.emde@gmail.com]
+
+* Tighten rescue clauses. #5985 [james@grayproductions.net]
+
+* Fix spurious newlines and spaces in AR::Base#to_xml output [Jamis Buck]
+
+* has_one supports the :dependent => :delete option which skips the typical callback chain and deletes the associated object directly from the database. #5927 [Chris Mear, Jonathan Viney]
+
+* Nested subclasses are not prefixed with the parent class' table_name since they should always use the base class' table_name. #5911 [Jonathan Viney]
+
+* SQLServer: work around bug where some unambiguous date formats are not correctly identified if the session language is set to german. #5894 [Tom Ward, kruth@bfpi]
+
+* SQLServer: fix eager association test. #5901 [Tom Ward]
+
+* Clashing type columns due to a sloppy join shouldn't wreck single-table inheritance. #5838 [Kevin Clark]
+
+* Fixtures: correct escaping of \n and \r. #5859 [evgeny.zislis@gmail.com]
+
+* Migrations: gracefully handle missing migration files. #5857 [eli.gordon@gmail.com]
+
+* MySQL: update test schema for MySQL 5 strict mode. #5861 [Tom Ward]
+
+* to_xml: correct naming of included associations. #5831 [josh.starcher@gmail.com]
+
+* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 [josh@hasmanythrough.com]
+
+* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. [Jeremy Kemper]
+
+ # Create a tagging to associate the post and tag.
+ post.tags << Tag.find_by_name('old')
+ post.tags.create! :name => 'general'
+
+ # Would have been:
+ post.taggings.create!(:tag => Tag.find_by_name('finally')
+ transaction do
+ post.taggings.create!(:tag => Tag.create!(:name => 'general'))
+ end
+
+* Cache nil results for :included has_one associations also. #5787 [Michael Schoen]
+
+* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Luetke]
+
+* Nested classes are given table names prefixed by the singular form of the parent's table name. [Jeremy Kemper]
+ Example: Invoice::Lineitem is given table name invoice_lineitems
+
+* Migrations: uniquely name multicolumn indexes so you don't have to. [Jeremy Kemper]
+ # people_active_last_name_index, people_active_deactivated_at_index
+ add_index :people, [:active, :last_name]
+ add_index :people, [:active, :deactivated_at]
+ remove_index :people, [:active, :last_name]
+ remove_index :people, [:active, :deactivated_at]
+
+ WARNING: backward-incompatibility. Multicolumn indexes created before this
+ revision were named using the first column name only. Now they're uniquely
+ named using all indexed columns.
+
+ To remove an old multicolumn index, remove_index :table_name, :first_column
+
+* Fix for deep includes on the same association. [richcollins@gmail.com]
+
+* Tweak fixtures so they don't try to use a non-ActiveRecord class. [Kevin Clark]
+
+* Remove ActiveRecord::Base.reset since Dispatcher doesn't use it anymore. [Rick Olson]
+
+* Document find's :from option. Closes #5762. [andrew@redlinesoftware.com]
+
+* PostgreSQL: autodetected sequences work correctly with multiple schemas. Rely on the schema search_path instead of explicitly qualifying the sequence name with its schema. #5280 [guy.naor@famundo.com]
+
+* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
+
+* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael A. Schoen]
+
+* Add documentation for how to disable timestamps on a per model basis. Closes #5684. [matt@mattmargolis.net Marcel Molina Jr.]
+
+* Don't save has_one associations unnecessarily. #5735 [Jonathan Viney]
+
+* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting. [Rick Olson]
+
+* Formally deprecate the deprecated finders. [Koz]
+
+* Formally deprecate rich associations. [Koz]
+
+* Fixed that default timezones for new / initialize should uphold utc setting #5709 [daniluk@yahoo.com]
+
+* Fix announcement of very long migration names. #5722 [blake@near-time.com]
+
+* The exists? class method should treat a string argument as an id rather than as conditions. #5698 [jeremy@planetargon.com]
+
+* Fixed to_xml with :include misbehaviors when invoked on array of model instances #5690 [alexkwolfe@gmail.com]
+
+* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
+
+ assert (Topic.exists?(:author_name => "David"))
+ assert (Topic.exists?(:author_name => "Mary", :approved => true))
+ assert (Topic.exists?(["parent_id = ?", 1]))
+
+* Schema dumper quotes date :default values. [Dave Thomas]
+
+* Calculate sum with SQL, not Enumerable on HasManyThrough Associations. [Dan Peterson]
+
+* Factor the attribute#{suffix} methods out of method_missing for easier extension. [Jeremy Kemper]
+
+* Patch sql injection vulnerability when using integer or float columns. [Jamis Buck]
+
+* Allow #count through a has_many association to accept :include. [Dan Peterson]
+
+* create_table rdoc: suggest :id => false for habtm join tables. [Zed Shaw]
+
+* PostgreSQL: return array fields as strings. #4664 [Robby Russell]
+
+* SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. #5670 [Tom Ward]
+
+* SQLServer: fix db:schema:dump case-sensitivity. #4684 [Will Rogers]
+
+* Oracle: BigDecimal support. #5667 [schoenm@earthlink.net]
+
+* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 [robbat2@gentoo.org, work@ashleymoran.me.uk]
+
+* Firebird migrations support. #5337 [Ken Kunz ]
+
+* PostgreSQL: create/drop as postgres user. #4790 [mail@matthewpainter.co.uk, mlaster@metavillage.com]
+
+* Update callbacks documentation. #3970 [Robby Russell ]
+
+* PostgreSQL: correctly quote the ' in pk_and_sequence_for. #5462 [tietew@tietew.net]
+
+* PostgreSQL: correctly quote microseconds in timestamps. #5641 [rick@rickbradley.com]
+
+* Clearer has_one/belongs_to model names (account has_one :user). #5632 [matt@mattmargolis.net]
+
+* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [schoenm@earthlink.net]
+
+* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [schoenm@earthlink.net]
+
+* Added :group to available options for finds done on associations #5516 [mike@michaeldewey.org]
+
+* Minor tweak to improve performance of ActiveRecord::Base#to_param.
+
+* Observers also watch subclasses created after they are declared. #5535 [daniels@pronto.com.au]
+
+* Removed deprecated timestamps_gmt class methods. [Jeremy Kemper]
+
+* rake build_mysql_database grants permissions to rails@localhost. #5501 [brianegge@yahoo.com]
+
+* PostgreSQL: support microsecond time resolution. #5492 [alex@msgpad.com]
+
+* Add AssociationCollection#sum since the method_missing invokation has been shadowed by Enumerable#sum.
+
+* Added find_or_initialize_by_X which works like find_or_create_by_X but doesn't save the newly instantiated record. [Sam Stephenson]
+
+* Row locking. Provide a locking clause with the :lock finder option or true for the default "FOR UPDATE". Use the #lock! method to obtain a row lock on a single record (reloads the record with :lock => true). [Shugo Maeda]
+ # Obtain an exclusive lock on person 1 so we can safely increment visits.
+ Person.transaction do
+ # select * from people where id=1 for update
+ person = Person.find(1, :lock => true)
+ person.visits += 1
+ person.save!
+ end
+
+* PostgreSQL: introduce allow_concurrency option which determines whether to use blocking or asynchronous #execute. Adapters with blocking #execute will deadlock Ruby threads. The default value is ActiveRecord::Base.allow_concurrency. [Jeremy Kemper]
+
+* Use a per-thread (rather than global) transaction mutex so you may execute concurrent transactions on separate connections. [Jeremy Kemper]
+
+* Change AR::Base#to_param to return a String instead of a Fixnum. Closes #5320. [Nicholas Seckar]
+
+* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes@web.de)
+
+* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [DHH]
+
+* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [hcatlin@gmail.com]. Example:
+
+ Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
+
+...is the same as:
+
+ Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
+
+ This makes it easier to pass in the options from a form or otherwise outside.
+
+
+* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz@gmail.com]
+
+* Fixed usage of :limit and with_scope when the association in scope is a 1:m #5208 [alex@purefiction.net]
+
+* Fixed migration trouble with SQLite when NOT NULL is used in the new definition #5215 [greg@lapcominc.com]
+
+* Fixed problems with eager loading and counting on SQL Server #5212 [kajism@yahoo.com]
+
+* Fixed that count distinct should use the selected column even when using :include #5251 [anna@wota.jp]
+
+* Fixed that :includes merged from with_scope won't cause the same association to be loaded more than once if repetition occurs in the clauses #5253 [alex@purefiction.net]
+
+* Allow models to override to_xml. #4989 [Blair Zajac ]
+
+* PostgreSQL: don't ignore port when host is nil since it's often used to label the domain socket. #5247 [shimbo@is.naist.jp]
+
+* Records and arrays of records are bound as quoted ids. [Jeremy Kemper]
+ Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
+ Foo.find(:first, :conditions => ['bar_id = ?', bar])
+
+* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [DHH]
+
+* Add a list of regexes assert_queries skips in the ActiveRecord test suite. [Rick]
+
+* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [josh@hasmanythrough.com]
+
+* Provide Association Extensions access to the instance that the association is being accessed from.
+ Closes #4433 [josh@hasmanythrough.com]
+
+* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
+
+* Add a quick note about :select and eagerly included associations. [Rick]
+
+* Add docs for the :as option in has_one associations. Closes #5144 [cdcarter@gmail.com]
+
+* Fixed that has_many collections shouldn't load the entire association to do build or create [DHH]
+
+* Added :allow_nil option for aggregations #5091 [ian.w.white@gmail.com]
+
+* Fix Oracle boolean support and tests. Closes #5139. [schoenm@earthlink.net]
+
+* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) [Jeremy Kemper]
+
+* Call Inflector#demodulize on the class name when eagerly including an STI model. Closes #5077 [info@loobmedia.com]
+
+* Preserve MySQL boolean column defaults when changing a column in a migration. Closes #5015. [pdcawley@bofh.org.uk]
+
+* PostgreSQL: migrations support :limit with :integer columns by mapping limit < 4 to smallint, > 4 to bigint, and anything else to integer. #2900 [keegan@thebasement.org]
+
+* Dates and times interpret empty strings as nil rather than 2000-01-01. #4830 [kajism@yahoo.com]
+
+* Allow :uniq => true with has_many :through associations. [Jeremy Kemper]
+
+* Ensure that StringIO is always available for the Schema dumper. [Marcel Molina Jr.]
+
+* Allow AR::Base#to_xml to include methods too. Closes #4921. [johan@textdrive.com]
+
+* Replace superfluous name_to_class_name variant with camelize. [Marcel Molina Jr.]
+
+* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
+
+* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
+
+* Remove duplicate fixture entry in comments.yml. Closes #4923. [Blair Zajac ]
+
+* Update FrontBase adapter to check binding version. Closes #4920. [mlaster@metavillage.com]
+
+* New Frontbase connections don't start in auto-commit mode. Closes #4922. [mlaster@metavillage.com]
+
+* When grouping, use the appropriate option key. [Marcel Molina Jr.]
+
+* Only modify the sequence name in the FrontBase adapter if the FrontBase adapter is actually being used. [Marcel Molina Jr.]
+
+* Add support for FrontBase (http://www.frontbase.com/) with a new adapter thanks to the hard work of one Mike Laster. Closes #4093. [mlaster@metavillage.com]
+
+* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil ]
+
+* Fix syntax error in documentation. Closes #4679. [mislav@nippur.irb.hr]
+
+* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
+
+* Various fixes for sqlserver_adapter (odbc statement finishing, ado schema dumper, drop index). Closes #4831. [kajism@yahoo.com]
+
+* Add support for :order option to with_scope. Closes #3887. [eric.daspet@survol.net]
+
+* Prettify output of schema_dumper by making things line up. Closes #4241 [Caio Chassot ]
+
+* Make build_postgresql_databases task make databases owned by the postgres user. Closes #4790. [mlaster@metavillage.com]
+
+* Sybase Adapter type conversion cleanup. Closes #4736. [dev@metacasa.net]
+
+* Fix bug where calculations with long alias names return null. [Rick]
+
+* Raise error when trying to add to a has_many :through association. Use the Join Model instead. [Rick]
+
+ @post.tags << @tag # BAD
+ @post.taggings.create(:tag => @tag) # GOOD
+
+* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick]
+
+* Update inconsistent migrations documentation. #4683 [machomagna@gmail.com]
+
+* Add ActiveRecord::Errors#to_xml [Jamis Buck]
+
+* Properly quote index names in migrations (closes #4764) [John Long]
+
+* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. [Rick]
+
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick]
+
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick]
+
+* DRY up association collection reader method generation. [Marcel Molina Jr.]
+
+* DRY up and tweak style of the validation error object. [Marcel Molina Jr.]
+
+* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick]
+
+ class Account < ActiveRecord::Base
+ validates_uniqueness_of :email, :case_sensitive => false
+ end
+
+* Allow multiple association extensions with :extend option (closes #4666) [Josh Susser]
+
+ class Account < ActiveRecord::Base
+ has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
+ end
+
+ *1.15.3* (March 12th, 2007)
+
+ * Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
+
+ * Consistently quote primary key column names. #7763 [toolmantim]
+
+ * Fixtures: fix YAML ordered map support. #2665 [Manuel Holtgrewe, nfbuckley]
+
+ * Fix has_many :through << with custom foreign keys. #6466, #7153 [naffis, Rich Collins]
+
*1.15.2* (February 5th, 2007)
@@ -271,7 +1206,7 @@
* Fixed to_xml with :include misbehaviors when invoked on array of model instances #5690 [alexkwolfe@gmail.com]
-* Added support for conditions on Base.exists? #5689 [josh@joshpeek.com]. Examples:
+* Added support for conditions on Base.exists? #5689 [Josh Peek]. Examples:
assert (Topic.exists?(:author_name => "David"))
assert (Topic.exists?(:author_name => "Mary", :approved => true))
@@ -353,9 +1288,9 @@
...is the same as:
Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2)
-
+
This makes it easier to pass in the options from a form or otherwise outside.
-
+
* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz@gmail.com]
@@ -495,6 +1430,1521 @@
* Fixed calculations for the Oracle Adapter (closes #4626) [Michael Schoen]
+*1.14.1* (April 6th, 2006)
+
+* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar]
+
+* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) [Lars Pind]
+
+* Enable Limit/Offset in Calculations (closes #4558) [lmarlow@yahoo.com]
+
+* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick]
+
+* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 [lagroue@free.fr]
+
+* Allow AR::Base#respond_to? to behave when @attributes is nil [zenspider]
+
+* Support eager includes when going through a polymorphic has_many association. [Rick]
+
+* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick]
+
+ class Post < ActiveRecord::Base
+ has_one :tagging, :as => :taggable
+ end
+
+ Post.find :all, :include => :tagging
+
+* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick]
+
+* Added support for going through a polymorphic has_many association: (closes #4401) [Rick]
+
+ class PhotoCollection < ActiveRecord::Base
+ has_many :photos, :as => :photographic
+ belongs_to :firm
+ end
+
+ class Firm < ActiveRecord::Base
+ has_many :photo_collections
+ has_many :photos, :through => :photo_collections
+ end
+
+* Multiple fixes and optimizations in PostgreSQL adapter, allowing ruby-postgres gem to work properly. [ruben.nine@gmail.com]
+
+* Fixed that AssociationCollection#delete_all should work even if the records of the association are not loaded yet. [Florian Weber]
+
+* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations. (closes #4456) [Stefan]
+
+* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick]
+
+* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [DHH]
+
+* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [DHH]
+
+* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). [Marcel Mollina Jr.]
+
+* Fixed broken OCIAdapter #4457 [schoenm@earthlink.net]
+
+
+*1.14.0* (March 27th, 2006)
+
+* Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
+
+* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick]
+
+* Add support for :include to with_scope [andrew@redlinesoftware.com]
+
+* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [schoenm@earthlink.net]
+
+* Change periods (.) in table aliases to _'s. Closes #4251 [jeff@ministrycentered.com]
+
+* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 [Rick]
+
+* Fixed issue that kept :select options from being scoped [Rick]
+
+* Fixed db_schema_import when binary types are present #3101 [DHH]
+
+* Fixed that MySQL enums should always be returned as strings #3501 [DHH]
+
+* Change has_many :through to use the :source option to specify the source association. :class_name is now ignored. [Rick Olson]
+
+ class Connection < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :channel
+ end
+
+ class Channel < ActiveRecord::Base
+ has_many :connections
+ has_many :contacts, :through => :connections, :class_name => 'User' # OLD
+ has_many :contacts, :through => :connections, :source => :user # NEW
+ end
+
+* Fixed DB2 adapter so nullable columns will be determines correctly now and quotes from column default values will be removed #4350 [contact@maik-schmidt.de]
+
+* Allow overriding of find parameters in scoped has_many :through calls [Rick Olson]
+
+ In this example, :include => false disables the default eager association from loading. :select changes the standard
+ select clause. :joins specifies a join that is added to the end of the has_many :through query.
+
+ class Post < ActiveRecord::Base
+ has_many :tags, :through => :taggings, :include => :tagging do
+ def add_joins_and_select
+ find :all, :select => 'tags.*, authors.id as author_id', :include => false,
+ :joins => 'left outer join posts on taggings.taggable_id = posts.id left outer join authors on posts.author_id = authors.id'
+ end
+ end
+ end
+
+* Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [DHH]
+
+* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [jonathan@bluewire.net.nz]
+
+* Quit ignoring default :include options in has_many :through calls [Mark James]
+
+* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [jonathan@bluewire.net.nz]
+
+* Eager Loading support added for has_many :through => :has_many associations (see below). [Rick Olson]
+
+* Allow has_many :through to work on has_many associations (closes #3864) [sco@scottraymond.net] Example:
+
+ class Firm < ActiveRecord::Base
+ has_many :clients
+ has_many :invoices, :through => :clients
+ end
+
+ class Client < ActiveRecord::Base
+ belongs_to :firm
+ has_many :invoices
+ end
+
+ class Invoice < ActiveRecord::Base
+ belongs_to :client
+ end
+
+* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [josh@hasmanythrough.com]
+
+* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [jonathan@bluewire.net.nz]
+
+* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien@gmail.com / Rick]
+
+* SQL Server adapter gets some love #4298 [rtomayko@gmail.com]
+
+* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 [derrickspell@cdmplus.com]
+
+* Rework table aliasing to account for truncated table aliases. Add smarter table aliasing when doing eager loading of STI associations. This allows you to use the association name in the order/where clause. [Jonathan Viney / Rick Olson] #4108 Example (SpecialComment is using STI):
+
+ Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
+
+* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick]
+
+* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Koz]
+
+* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Koz]
+
+* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom ward]
+
+* Fixed that Migration#execute would have the table name prefix appended to its query #4110 [mark.imbriaco@pobox.com]
+
+* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) [Jamis Buck]
+
+* Use association's :conditions when eager loading. [jeremyevans0@gmail.com] #4144
+
+* Alias the has_and_belongs_to_many join table on eager includes. #4106 [jeremyevans0@gmail.com]
+
+ This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
+
+ Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
+
+* Oracle adapter gets some love #4230 [schoenm@earthlink.net]
+
+ * Changes :text to CLOB rather than BLOB [Moses Hohman]
+ * Fixes an issue with nil numeric length/scales (several)
+ * Implements support for XMLTYPE columns [wilig / Kubo Takehiro]
+ * Tweaks a unit test to get it all green again
+ * Adds support for #current_database
+
+* Added Base.abstract_class? that marks which classes are not part of the Active Record hierarchy #3704 [Rick Olson]
+
+ class CachedModel < ActiveRecord::Base
+ self.abstract_class = true
+ end
+
+ class Post < CachedModel
+ end
+
+ CachedModel.abstract_class?
+ => true
+
+ Post.abstract_class?
+ => false
+
+ Post.base_class
+ => Post
+
+ Post.table_name
+ => 'posts'
+
+* Allow :dependent options to be used with polymorphic joins. #3820 [Rick Olson]
+
+ class Foo < ActiveRecord::Base
+ has_many :attachments, :as => :attachable, :dependent => :delete_all
+ end
+
+* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay@gmail.com]
+
+* Upgrade to Transaction::Simple 1.3 [Jamis Buck]
+
+* Catch FixtureClassNotFound when using instantiated fixtures on a fixture that has no ActiveRecord model [Rick Olson]
+
+* Allow ordering of calculated results and/or grouped fields in calculations [solo@gatelys.com]
+
+* Make ActiveRecord::Base#save! return true instead of nil on success. #4173 [johan@johansorensen.com]
+
+* Dynamically set allow_concurrency. #4044 [Stefan Kaes]
+
+* Added Base#to_xml that'll turn the current record into a XML representation [DHH]. Example:
+
+ topic.to_xml
+
+ ...returns:
+
+
+
+ The First Topic
+ David
+ 1
+ false
+ 0
+ 2000-01-01 08:28:00
+ 2003-07-16 09:28:00
+ Have a nice day
+ david@loudthinking.com
+
+ 2004-04-15
+
+
+ ...and you can configure with:
+
+ topic.to_xml(:skip_instruct => true, :except => [ :id, bonus_time, :written_on, replies_count ])
+
+ ...that'll return:
+
+
+ The First Topic
+ David
+ false
+ Have a nice day
+ david@loudthinking.com
+
+ 2004-04-15
+
+
+ You can even do load first-level associations as part of the document:
+
+ firm.to_xml :include => [ :account, :clients ]
+
+ ...that'll return something like:
+
+
+
+ 1
+ 1
+ 37signals
+
+
+ 1
+ Summit
+
+
+ 1
+ Microsoft
+
+
+
+ 1
+ 50
+
+
+
+* Allow :counter_cache to take a column name for custom counter cache columns [Jamis Buck]
+
+* Documentation fixes for :dependent [robby@planetargon.com]
+
+* Stop the MySQL adapter crashing when views are present. #3782 [Jonathan Viney]
+
+* Don't classify the belongs_to class, it is already singular #4117 [keithm@infused.org]
+
+* Allow set_fixture_class to take Classes instead of strings for a class in a module. Raise FixtureClassNotFound if a fixture can't load. [Rick Olson]
+
+* Fix quoting of inheritance column for STI eager loading #4098 [Jonathan Viney ]
+
+* Added smarter table aliasing for eager associations for multiple self joins #3580 [Rick Olson]
+
+ * The first time a table is referenced in a join, no alias is used.
+ * After that, the parent class name and the reflection name are used.
+
+ Tree.find(:all, :include => :children) # LEFT OUTER JOIN trees AS tree_children ...
+
+ * Any additional join references get a numerical suffix like '_2', '_3', etc.
+
+* Fixed eager loading problems with single-table inheritance #3580 [Rick Olson]. Post.find(:all, :include => :special_comments) now returns all posts, and any special comments that the posts may have. And made STI work with has_many :through and polymorphic belongs_to.
+
+* Added cascading eager loading that allows for queries like Author.find(:all, :include=> { :posts=> :comments }), which will fetch all authors, their posts, and the comments belonging to those posts in a single query (using LEFT OUTER JOIN) #3913 [anna@wota.jp]. Examples:
+
+ # cascaded in two levels
+ >> Author.find(:all, :include=>{:posts=>:comments})
+ => authors
+ +- posts
+ +- comments
+
+ # cascaded in two levels and normal association
+ >> Author.find(:all, :include=>[{:posts=>:comments}, :categorizations])
+ => authors
+ +- posts
+ +- comments
+ +- categorizations
+
+ # cascaded in two levels with two has_many associations
+ >> Author.find(:all, :include=>{:posts=>[:comments, :categorizations]})
+ => authors
+ +- posts
+ +- comments
+ +- categorizations
+
+ # cascaded in three levels
+ >> Company.find(:all, :include=>{:groups=>{:members=>{:favorites}}})
+ => companies
+ +- groups
+ +- members
+ +- favorites
+
+* Make counter cache work when replacing an association #3245 [eugenol@gmail.com]
+
+* Make migrations verbose [Jamis Buck]
+
+* Make counter_cache work with polymorphic belongs_to [Jamis Buck]
+
+* Fixed that calling HasOneProxy#build_model repeatedly would cause saving to happen #4058 [anna@wota.jp]
+
+* Added Sybase database adapter that relies on the Sybase Open Client bindings (see http://raa.ruby-lang.org/project/sybase-ctlib) #3765 [John Sheets]. It's almost completely Active Record compliant (including migrations), but has the following caveats:
+
+ * Does not support DATE SQL column types; use DATETIME instead.
+ * Date columns on HABTM join tables are returned as String, not Time.
+ * Insertions are potentially broken for :polymorphic join tables
+ * BLOB column access not yet fully supported
+
+* Clear stale, cached connections left behind by defunct threads. [Jeremy Kemper]
+
+* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. Most AR usage is in single-threaded applications. [Jeremy Kemper]
+
+* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [schoenm@earthlink.net]
+
+* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [DHH]
+
+* Speed up class -> connection caching and stale connection verification. #3979 [Stefan Kaes]
+
+* Add set_fixture_class to allow the use of table name accessors with models which use set_table_name. [Kevin Clark]
+
+* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 [dblack@wobblini.net]
+
+* Define attribute query methods to avoid method_missing calls. #3677 [jonathan@bluewire.net.nz]
+
+* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome. #3591 [Simon Stapleton, Tom Ward]
+
+* Added support for nested scopes #3407 [anna@wota.jp]. Examples:
+
+ Developer.with_scope(:find => { :conditions => "salary > 10000", :limit => 10 }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (salary > 10000) LIMIT 10
+
+ # inner rule is used. (all previous parameters are ignored)
+ Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (name = 'Jamis')
+ end
+
+ # parameters are merged
+ Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
+ Developer.find(:all) # => SELECT * FROM developers WHERE (( salary > 10000 ) AND ( name = 'Jamis' )) LIMIT 10
+ end
+ end
+
+* Fixed db2 connection with empty user_name and auth options #3622 [phurley@gmail.com]
+
+* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 [Masao Mutoh]
+
+* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [lars@pind.com]
+
+* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
+
+ Person.average :age
+ Person.minimum :age
+ Person.maximum :age
+ Person.sum :salary, :group => :last_name
+
+* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [contact@lukeredpath.co.uk]
+
+* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. [Jeremy Kemper]
+
+* Improved the Oracle OCI Adapter with better performance for column reflection (from #3210), fixes to migrations (from #3476 and #3742), tweaks to unit tests (from #3610), and improved documentation (from #2446) #3879 [Aggregated by schoenm@earthlink.net]
+
+* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 [rubyonrails@atyp.de]
+
+* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [skaes]
+
+* Correct syntax error in mysql DDL, and make AAACreateTablesTest run first [Bob Silva]
+
+* Allow :include to be used with has_many :through associations #3611 [Michael Schoen]
+
+* PostgreSQL: smarter schema dumps using pk_and_sequence_for(table). #2920 [Blair Zajac]
+
+* SQLServer: more compatible limit/offset emulation. #3779 [Tom Ward]
+
+* Polymorphic join support for has_one associations (has_one :foo, :as => :bar) #3785 [Rick Olson]
+
+* PostgreSQL: correctly parse negative integer column defaults. #3776 [bellis@deepthought.org]
+
+* Fix problems with count when used with :include [Jeremy Hopple and Kevin Clark]
+
+* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Luetke]
+
+* Using AssociationCollection#build with arrays of hashes should call build, not create [DHH]
+
+* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. [Nicholas Seckar]
+
+* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 [Maik Schmidt]
+
+* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [shugo@ruby-lang.org]
+
+* Add documentation for add_index and remove_index. #3600 [Manfred Stienstra ]
+
+* If the OCI library is not available, raise an exception indicating as much. #3593 [schoenm@earthlink.net]
+
+* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. [Rick Olson]
+
+* Make dynamic finders honor additional passed in :conditions. #3569 [Oleg Pudeyev , Marcel Molina Jr.]
+
+* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. [Nicholas Seckar]
+
+* Make .count work for has_many associations with multi line finder sql [schoenm@earthlink.net]
+
+* Add AR::Base.base_class for querying the ancestor AR::Base subclass [Jamis Buck]
+
+* Allow configuration of the column used for optimistic locking [wilsonb@gmail.com]
+
+* Don't hardcode 'id' in acts as list. [ror@philippeapril.com]
+
+* Fix date errors for SQLServer in association tests. #3406 [kevin.clark@gmal.com]
+
+* Escape database name in MySQL adapter when creating and dropping databases. #3409 [anna@wota.jp]
+
+* Disambiguate table names for columns in validates_uniquness_of's WHERE clause. #3423 [alex.borovsky@gmail.com]
+
+* .with_scope imposed create parameters now bypass attr_protected [Tobias Luetke]
+
+* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. [Marcel Molina Jr.]
+
+* Multiple enhancements and adjustments to DB2 adaptor. #3377 [contact@maik-schmidt.de]
+
+* Sanitize scoped conditions. [Marcel Molina Jr.]
+
+* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [DHH]
+
+* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [TobiasLuetke]
+
+* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [TobiasLuetke]
+
+* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber]
+
+* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
+
+* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Luetke]
+
+* made method missing delegation to class methods on relation target work on :through associations. [Tobias Luetke]
+
+* made .find() work on :through relations. [Tobias Luetke]
+
+* Fix typo in association docs. #3296. [Blair Zajac]
+
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Luetke]
+
+*1.13.2* (December 13th, 2005)
+
+* Become part of Rails 1.0
+
+* MySQL: allow encoding option for mysql.rb driver. [Jeremy Kemper]
+
+* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [DHH]. Example:
+
+ class Post
+ has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
+ end
+
+ post.recent_comments.find(:all) # Uses LIMIT 10 and includes authors
+ post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
+ post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
+
+* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [DHH]
+
+* MySQL: fixes for the bundled mysql.rb driver. #3160 [Justin Forder]
+
+* SQLServer: fix obscure optimistic locking bug. #3068 [kajism@yahoo.com]
+
+* SQLServer: support uniqueidentifier columns. #2930 [keithm@infused.org]
+
+* SQLServer: cope with tables names qualified by owner. #3067 [jeff@ministrycentered.com]
+
+* SQLServer: cope with columns with "desc" in the name. #1950 [Ron Lusk, Ryan Tomayko]
+
+* SQLServer: cope with primary keys with "select" in the name. #3057 [rdifrango@captechventures.com]
+
+* Oracle: active? performs a select instead of a commit. #3133 [Michael Schoen]
+
+* MySQL: more robust test for nullified result hashes. #3124 [Stefan Kaes]
+
+* Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausoleil]
+
+* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
+
+* PostgreSQL: more robust sequence name discovery. #3087 [Rick Olson]
+
+* Oracle: use syntax compatible with Oracle 8. #3131 [Michael Schoen]
+
+* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
+
+* Added preliminary support for polymorphic associations [DHH]
+
+* Added preliminary support for join models [DHH]
+
+* Allow validate_uniqueness_of to be scoped by more than just one column. #1559. [jeremy@jthopple.com, Marcel Molina Jr.]
+
+* Firebird: active? and reconnect! methods for handling stale connections. #428 [Ken Kunz ]
+
+* Firebird: updated for FireRuby 0.4.0. #3009 [Ken Kunz ]
+
+* MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 [Jeremy Kemper]
+
+* Oracle: active? check pings the database rather than testing the last command status. #428 [Michael Schoen]
+
+* SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 [kajism@yahoo.com]
+
+* Reloading a model doesn't lose track of its connection. #2996 [junk@miriamtech.com, Jeremy Kemper]
+
+* Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman@rominato.com, Florian Weber, Michael Schoen]
+
+* MySQL, PostgreSQL: reconnect! also reconfigures the connection. Otherwise, the connection 'loses' its settings if it times out and is reconnected. #2978 [Shugo Maeda]
+
+* has_and_belongs_to_many: use JOIN instead of LEFT JOIN. [Jeremy Kemper]
+
+* MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 [Shugo Maeda]
+
+* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
+
+* Correct boolean handling in generated reader methods. #2945 [don.park@gmail.com, Stefan Kaes]
+
+* Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
+
+* Document :force option to create_table. #2921 [Blair Zajac ]
+
+* Don't add the same conditions twice in has_one finder sql. #2916 [Jeremy Evans]
+
+* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
+
+* Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 [Ken Kunz ]
+
+* SQLServer: active? and reconnect! methods for handling stale connections. #428 [kajism@yahoo.com, Tom Ward ]
+
+* Associations handle case-equality more consistently: item.parts.is_a?(Array) and item.parts === Array. #1345 [MarkusQ@reality.com]
+
+* SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 [kajism@yahoo.com, Tom Ward ]
+
+* Oracle: active? and reconnect! methods for handling stale connections. Optionally retry queries after reconnect. #428 [Michael Schoen ]
+
+* Correct documentation for Base.delete_all. #1568 [Newhydra]
+
+* Oracle: test case for column default parsing. #2788 [Michael Schoen ]
+
+* Update documentation for Migrations. #2861 [Tom Werner ]
+
+* When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? #428 [Jeremy Kemper]
+
+* Oracle: Much faster column reflection. #2848 [Michael Schoen ]
+
+* Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. [Jeremy Kemper]
+
+* PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. [Jeremy Kemper]
+
+* PostgreSQL: correctly discover custom primary key sequences. #2594 [Blair Zajac , meadow.nnick@gmail.com, Jeremy Kemper]
+
+* SQLServer: don't report limits for unsupported field types. #2835 [Ryan Tomayko]
+
+* Include the Enumerable module in ActiveRecord::Errors. [Rick Bradley ]
+
+* Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 [rubyonrails@atyp.de]
+
+* Don't cast nil or empty strings to a dummy date. #2789 [Rick Bradley ]
+
+* acts_as_list plays nicely with inheritance by remembering the class which declared it. #2811 [rephorm@rephorm.com]
+
+* Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
+
+* Fixed acts_as_list for definitions without an explicit :order #2803 [jonathan@bluewire.net.nz]
+
+* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. [tommy@tmtm.org, akuroda@gmail.com, Doug Fales , Jeremy Kemper]
+
+* Correct handling of complex order clauses with SQL Server limit emulation. #2770 [Tom Ward , Matt B.]
+
+* Correct whitespace problem in Oracle default column value parsing. #2788 [rick@rickbradley.com]
+
+* Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. #2065 [larrywilliams1@gmail.com, sam.kirchmeier@gmail.com, elliot@townx.org, Jeremy Kemper]
+
+* Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all. [Jeremy Kemper]
+
+* More compatible Oracle column reflection. #2771 [Ryan Davis , Michael Schoen ]
+
+
+*1.13.0* (November 7th, 2005)
+
+* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
+
+* Added :include as an option for association declarations [DHH]. Example:
+
+ has_many :posts, :include => [ :author, :comments ]
+
+* Rename Base.constrain to Base.with_scope so it doesn't conflict with existing concept of database constraints. Make scoping more robust: uniform method => parameters, validated method names and supported finder parameters, raise exception on nested scopes. [Jeremy Kemper] Example:
+
+ Comment.with_scope(:find => { :conditions => 'active=true' }, :create => { :post_id => 5 }) do
+ # Find where name = ? and active=true
+ Comment.find :all, :conditions => ['name = ?', name]
+ # Create comment associated with :post_id
+ Comment.create :body => "Hello world"
+ end
+
+* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 [Ryan Tomayko]
+
+* Added constrain scoping for creates using a hash of attributes bound to the :creation key [DHH]. Example:
+
+ Comment.constrain(:creation => { :post_id => 5 }) do
+ # Associated with :post_id
+ Comment.create :body => "Hello world"
+ end
+
+ This is rarely used directly, but allows for find_or_create on associations. So you can do:
+
+ # If the tag doesn't exist, a new one is created that's associated with the person
+ person.tags.find_or_create_by_name("Summer")
+
+* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [DHH]. Example:
+
+ # No 'Summer' tag exists
+ Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
+
+ # Now the 'Summer' tag does exist
+ Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
+
+* Added extension capabilities to has_many and has_and_belongs_to_many proxies [DHH]. Example:
+
+ class Account < ActiveRecord::Base
+ has_many :people do
+ def find_or_create_by_name(name)
+ first_name, *last_name = name.split
+ last_name = last_name.join " "
+
+ find_or_create_by_first_name_and_last_name(first_name, last_name)
+ end
+ end
+ end
+
+ person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
+ person.first_name # => "David"
+ person.last_name # => "Heinemeier Hansson"
+
+ Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
+
+* Omit internal dtproperties table from SQLServer table list. #2729 [rtomayko@gmail.com]
+
+* Quote column names in generated SQL. #2728 [rtomayko@gmail.com]
+
+* Correct the pure-Ruby MySQL 4.1.1 shim's version test. #2718 [Jeremy Kemper]
+
+* Add Model.create! to match existing model.save! method. When save! raises RecordInvalid, you can catch the exception, retrieve the invalid record (invalid_exception.record), and see its errors (invalid_exception.record.errors). [Jeremy Kemper]
+
+* Correct fixture behavior when table name pluralization is off. #2719 [Rick Bradley ]
+
+* Changed :dbfile to :database for SQLite adapter for consistency (old key still works as an alias) #2644 [Dan Peterson]
+
+* Added migration support for Oracle #2647 [Michael Schoen]
+
+* Worked around that connection can't be reset if allow_concurrency is off. #2648 [Michael Schoen ]
+
+* Fixed SQL Server adapter to pass even more tests and do even better #2634 [rtomayko@gmail.com]
+
+* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 [Tom Ward]
+
+* Added migration support to SQL Server adapter (please someone do the same for Oracle and DB2) #2625 [Tom Ward]
+
+* Use AR::Base.silence rather than AR::Base.logger.silence in fixtures to preserve Log4r compatibility. #2618 [dansketcher@gmail.com]
+
+* Constraints are cloned so they can't be inadvertently modified while they're
+in effect. Added :readonly finder constraint. Calling an association collection's class method (Part.foobar via item.parts.foobar) constrains :readonly => false since the collection's :joins constraint would otherwise force it to true. [Jeremy Kemper ]
+
+* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 [duane.johnson@gmail.com]
+
+* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [schoenm@earthlink.net]
+
+* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 [maik schmidt]
+
+
+*1.12.2* (October 26th, 2005)
+
+* Allow symbols to rename columns when using SQLite adapter. #2531 [kevin.clark@gmail.com]
+
+* Map Active Record time to SQL TIME. #2575, #2576 [Robby Russell ]
+
+* Clarify semantics of ActiveRecord::Base#respond_to? #2560 [skaes@web.de]
+
+* Fixed Association#clear for associations which have not yet been accessed. #2524 [Patrick Lenz ]
+
+* HABTM finders shouldn't return readonly records. #2525 [Patrick Lenz ]
+
+* Make all tests runnable on their own. #2521. [Blair Zajac ]
+
+
+*1.12.1* (October 19th, 2005)
+
+* Always parenthesize :conditions options so they may be safely combined with STI and constraints.
+
+* Correct PostgreSQL primary key sequence detection. #2507 [tmornini@infomania.com]
+
+* Added support for using limits in eager loads that involve has_many and has_and_belongs_to_many associations
+
+
+*1.12.0* (October 16th, 2005)
+
+* Update/clean up documentation (rdoc)
+
+* PostgreSQL sequence support. Use set_sequence_name in your model class to specify its primary key sequence. #2292 [Rick Olson , Robby Russell ]
+
+* Change default logging colors to work on both white and black backgrounds. [Sam Stephenson]
+
+* YAML fixtures support ordered hashes for fixtures with foreign key dependencies in the same table. #1896 [purestorm@ggnore.net]
+
+* :dependent now accepts :nullify option. Sets the foreign key of the related objects to NULL instead of deleting them. #2015 [Robby Russell ]
+
+* Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail. Use find_by_sql to work around.
+
+* Avoid memleak in dev mode when using fcgi
+
+* Simplified .clear on active record associations by using the existing delete_records method. #1906 [Caleb ]
+
+* Delegate access to a customized primary key to the conventional id method. #2444. [Blair Zajac ]
+
+* Fix errors caused by assigning a has-one or belongs-to property to itself
+
+* Add ActiveRecord::Base.schema_format setting which specifies how databases should be dumped [Sam Stephenson]
+
+* Update DB2 adapter. #2206. [contact@maik-schmidt.de]
+
+* Corrections to SQLServer native data types. #2267. [rails.20.clarry@spamgourmet.com]
+
+* Deprecated ActiveRecord::Base.threaded_connection in favor of ActiveRecord::Base.allow_concurrency.
+
+* Protect id attribute from mass assigment even when the primary key is set to something else. #2438. [Blair Zajac ]
+
+* Misc doc fixes (typos/grammar/etc.). #2430. [coffee2code]
+
+* Add test coverage for content_columns. #2432. [coffee2code]
+
+* Speed up for unthreaded environments. #2431. [skaes@web.de]
+
+* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. [skaes@web.de]
+
+* Speed up the setting of table_name. #2428. [skaes@web.de]
+
+* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [skaes@web.de]
+
+* Fix typo of 'constrains' to 'contraints'. #2069. [Michael Schuerig ]
+
+* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [skaes@web.de]
+
+* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. [Michael Schuerig ]
+
+* Add geometric type for postgresql adapter. #2233 [akaspick@gmail.com]
+
+* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [skaes@web.de]
+
+* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [skaes@web.de]
+
+* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363 [Chad Fowler , Nicholas Seckar]
+
+* Added :force option to create_table that'll try to drop the table if it already exists before creating
+
+* Fix transactions so that calling return while inside a transaction will not leave an open transaction on the connection. [Nicholas Seckar]
+
+* Use foreign_key inflection uniformly. #2156 [Blair Zajac ]
+
+* model.association.clear should destroy associated objects if :dependent => true instead of nullifying their foreign keys. #2221 [joergd@pobox.com, ObieFernandez ]
+
+* Returning false from before_destroy should cancel the action. #1829 [Jeremy Huffman]
+
+* Recognize PostgreSQL NOW() default as equivalent to CURRENT_TIMESTAMP or CURRENT_DATE, depending on the column's type. #2256 [mat ]
+
+* Extensive documentation for the abstract database adapter. #2250 [François Beausoleil ]
+
+* Clean up Fixtures.reset_sequences for PostgreSQL. Handle tables with no rows and models with custom primary keys. #2174, #2183 [jay@jay.fm, Blair Zajac ]
+
+* Improve error message when nil is assigned to an attr which validates_size_of within a range. #2022 [Manuel Holtgrewe ]
+
+* Make update_attribute use the same writer method that update_attributes uses.
+ #2237 [trevor@protocool.com]
+
+* Make migrations honor table name prefixes and suffixes. #2298 [Jakob S, Marcel Molina]
+
+* Correct and optimize PostgreSQL bytea escaping. #1745, #1837 [dave@cherryville.org, ken@miriamtech.com, bellis@deepthought.org]
+
+* Fixtures should only reset a PostgreSQL sequence if it corresponds to an integer primary key named id. #1749 [chris@chrisbrinker.com]
+
+* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
+
+* Added new symbol-driven approach to activating observers with Base#observers= [DHH]. Example:
+
+ ActiveRecord::Base.observers = :cacher, :garbage_collector
+
+* Added AbstractAdapter#select_value and AbstractAdapter#select_values as convenience methods for selecting single values, instead of hashes, of the first column in a SELECT #2283 [solo@gatelys.com]
+
+* Wrap :conditions in parentheses to prevent problems with OR's #1871 [Jamis Buck]
+
+* Allow the postgresql adapter to work with the SchemaDumper. [Jamis Buck]
+
+* Add ActiveRecord::SchemaDumper for dumping a DB schema to a pure-ruby file, making it easier to consolidate large migration lists and port database schemas between databases. [Jamis Buck]
+
+* Fixed migrations for Windows when using more than 10 [David Naseby]
+
+* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 [Florian Weber]
+
+* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Luetke]
+
+* Improved migrations' behavior when the schema_info table is empty. [Nicholas Seckar]
+
+* Fixed that Observers didn't observe sub-classes #627 [Florian Weber]
+
+* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina]
+
+* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 [skae]
+
+* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 [Florian Weber]
+
+* Added better exception error when unknown column types are used with migrations #1814 [fbeausoleil@ftml.net]
+
+* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 [kajism@yahoo.com]
+
+* Fixed comparison of Active Record objects so two new objects are not equal #2099 [deberg]
+
+* Fixed that the SQL Server adapter would sometimes return DBI::Timestamp objects instead of Time #2127 [Tom Ward]
+
+* Added the instance methods #root and #ancestors on acts_as_tree and fixed siblings to not include the current node #2142, #2140 [coffee2code]
+
+* Fixed that Active Record would call SHOW FIELDS twice (or more) for the same model when the cached results were available #1947 [sd@notso.net]
+
+* Added log_level and use_silence parameter to ActiveRecord::Base.benchmark. The first controls at what level the benchmark statement will be logged (now as debug, instead of info) and the second that can be passed false to include all logging statements during the benchmark block/
+
+* Make sure the schema_info table is created before querying the current version #1903
+
+* Fixtures ignore table name prefix and suffix #1987 [Jakob S]
+
+* Add documentation for index_type argument to add_index method for migrations #2005 [blaine@odeo.com]
+
+* Modify read_attribute to allow a symbol argument #2024 [Ken Kunz]
+
+* Make destroy return self #1913 [sebastian.kanthak@muehlheim.de]
+
+* Fix typo in validations documentation #1938 [court3nay]
+
+* Make acts_as_list work for insert_at(1) #1966 [hensleyl@papermountain.org]
+
+* Fix typo in count_by_sql documentation #1969 [Alexey Verkhovsky]
+
+* Allow add_column and create_table to specify NOT NULL #1712 [emptysands@gmail.com]
+
+* Fix create_table so that id column is implicitly added [Rick Olson]
+
+* Default sequence names for Oracle changed to #{table_name}_seq, which is the most commonly used standard. In addition, a new method ActiveRecord::Base#set_sequence_name allows the developer to set the sequence name per model. This is a non-backwards-compatible change -- anyone using the old-style "rails_sequence" will need to either create new sequences, or set: ActiveRecord::Base.set_sequence_name = "rails_sequence" #1798
+
+* OCIAdapter now properly handles synonyms, which are commonly used to separate out the schema owner from the application user #1798
+
+* Fixed the handling of camelCase columns names in Oracle #1798
+
+* Implemented for OCI the Rakefile tasks of :clone_structure_to_test, :db_structure_dump, and :purge_test_database, which enable Oracle folks to enjoy all the agile goodness of Rails for testing. Note that the current implementation is fairly limited -- only tables and sequences are cloned, not constraints or indexes. A full clone in Oracle generally requires some manual effort, and is version-specific. Post 9i, Oracle recommends the use of the DBMS_METADATA package, though that approach requires editing of the physical characteristics generated #1798
+
+* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
+
+* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Luetke]
+
+ class Comment < AR:B
+ def self.search(q)
+ find(:all, :conditions => ["body = ?", q])
+ end
+ end
+
+ class Post < AR:B
+ has_many :comments
+ end
+
+ Post.find(1).comments.search('hi') # => SELECT * from comments WHERE post_id = 1 AND body = 'hi'
+
+ NOTICE: This patch changes the underlying SQL generated by has_and_belongs_to_many queries. If your relying on that, such as
+ by explicitly referencing the old t and j aliases, you'll need to update your code. Of course, you _shouldn't_ be relying on
+ details like that no less than you should be diving in to touch private variables. But just in case you do, consider yourself
+ noticed :)
+
+* Added migration support for SQLite (using temporary tables to simulate ALTER TABLE) #1771 [Sam Stephenson]
+
+* Remove extra definition of supports_migrations? from abstract_adaptor.rb [Nicholas Seckar]
+
+* Fix acts_as_list so that moving next-to-last item to the bottom does not result in duplicate item positions
+
+* Fixed incompatibility in DB2 adapter with the new limit/offset approach #1718 [Maik Schmidt]
+
+* Added :select option to find which can specify a different value than the default *, like find(:all, :select => "first_name, last_name"), if you either only want to select part of the columns or exclude columns otherwise included from a join #1338 [Stefan Kaes]
+
+
+*1.11.1* (11 July, 2005)
+
+* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olsen]
+
+* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 [tyler@kianta.com]
+
+* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 [Sam Stephenson]
+
+* Correct reflected table name for singular associations. #1688 [court3nay@gmail.com]
+
+* Fixed optimistic locking with SQL Server #1660 [tom@popdog.net]
+
+* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
+
+* Added better error message for "packets out of order" #1630 [courtenay]
+
+* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
+
+
+*1.11.0* (6 July, 2005)
+
+* Fixed that Yaml error message in fixtures hid the real error #1623 [Nicholas Seckar]
+
+* Changed logging of SQL statements to use the DEBUG level instead of INFO
+
+* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Luetke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
+
+* Added callback hooks to association collections #1549 [Florian Weber]. Example:
+
+ class Project
+ has_and_belongs_to_many :developers, :before_add => :evaluate_velocity
+
+ def evaluate_velocity(developer)
+ ...
+ end
+ end
+
+ ..raising an exception will cause the object not to be added (or removed, with before_remove).
+
+
+* Fixed Base.content_columns call for SQL Server adapter #1450 [DeLynn Berry]
+
+* Fixed Base#write_attribute to work with both symbols and strings #1190 [Paul Legato]
+
+* Fixed that has_and_belongs_to_many didn't respect single table inheritance types #1081 [Florian Weber]
+
+* Speed up ActiveRecord#method_missing for the common case (read_attribute).
+
+* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 [skaes@web.de]
+
+* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 [Chris McGrath]
+
+* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 [raidel@onemail.at]
+
+* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [michael@schuerig.de]
+
+* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]
+
+* Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 [Mark Imbriaco/DeLynn Berry]
+
+* Fixed that multiparameter posts ignored attr_protected #1532 [alec+rails@veryclever.net]
+
+* Fixed problem with eager loading when using a has_and_belongs_to_many association using :association_foreign_key #1504 [flash@vanklinkenbergsoftware.nl]
+
+* Fixed Base#find to honor the documentation on how :joins work and make them consistent with Base#count #1405 [pritchie@gmail.com]. What used to be:
+
+ Developer.find :all, :joins => 'developers_projects', :conditions => 'id=developer_id AND project_id=1'
+
+ ...should instead be:
+
+ Developer.find(
+ :all,
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+ :conditions => 'project_id=1'
+ )
+
+* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina]
+
+* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 [cluon]
+
+* Fixed that calling Model.find([]) returns [] and doesn't throw an exception #1379
+
+* Fixed that adding a record to a has_and_belongs_to collection would always save it -- now it only saves if its a new record #1203 [Alisdair McDiarmid]
+
+* Fixed saving of in-memory association structures to happen as a after_create/after_update callback instead of after_save -- that way you can add new associations in after_create/after_update callbacks without getting them saved twice
+
+* Allow any Enumerable, not just Array, to work as bind variables #1344 [Jeremy Kemper]
+
+* Added actual database-changing behavior to collection assigment for has_many and has_and_belongs_to_many #1425 [Sebastian Kanthak].
+ Example:
+
+ david.projects = [Project.find(1), Project.new("name" => "ActionWebSearch")]
+ david.save
+
+ If david.projects already contain the project with ID 1, this is left unchanged. Any other projects are dropped. And the new
+ project is saved when david.save is called.
+
+ Also included is a way to do assignments through IDs, which is perfect for checkbox updating, so you get to do:
+
+ david.project_ids = [1, 5, 7]
+
+* Corrected typo in find SQL for has_and_belongs_to_many. #1312 [ben@bensinclair.com]
+
+* Fixed sanitized conditions for has_many finder method. #1281 [jackc@hylesanderson.com, pragdave, Tobias Luetke]
+
+* Comprehensive PostgreSQL schema support. Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables. This allows you, for example, to have tables in a shared schema without having to use a custom table name. See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more. #827 [dave@cherryville.org]
+
+* Corrected @@configurations typo #1410 [david@ruppconsulting.com]
+
+* Return PostgreSQL columns in the order they were declared #1374 [perlguy@gmail.com]
+
+* Allow before/after update hooks to work on models using optimistic locking
+
+* Eager loading of dependent has_one associations won't delete the association #1212
+
+* Added a second parameter to the build and create method for has_one that controls whether the existing association should be replaced (which means nullifying its foreign key as well). By default this is true, but false can be passed to prevent it.
+
+* Using transactional fixtures now causes the data to be loaded only once.
+
+* Added fixture accessor methods that can be used when instantiated fixtures are disabled.
+
+ fixtures :web_sites
+
+ def test_something
+ assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
+ end
+
+* Added DoubleRenderError exception that'll be raised if render* is called twice #518 [Nicholas Seckar]
+
+* Fixed exceptions occuring after render has been called #1096 [Nicholas Seckar]
+
+* CHANGED: validates_presence_of now uses Errors#add_on_blank, which will make " " fail the validation where it didn't before #1309
+
+* Added Errors#add_on_blank which works like Errors#add_on_empty, but uses Object#blank? instead
+
+* Added the :if option to all validations that can either use a block or a method pointer to determine whether the validation should be run or not. #1324 [Duane Johnson/jhosteny]. Examples:
+
+ Conditional validations such as the following are made possible:
+ validates_numericality_of :income, :if => :employed?
+
+ Conditional validations can also solve the salted login generator problem:
+ validates_confirmation_of :password, :if => :new_password?
+
+ Using blocks:
+ validates_presence_of :username, :if => Proc.new { |user| user.signup_step > 1 }
+
+* Fixed use of construct_finder_sql when using :join #1288 [dwlt@dwlt.net]
+
+* Fixed that :delete_sql in has_and_belongs_to_many associations couldn't access record properties #1299 [Rick Olson]
+
+* Fixed that clone would break when an aggregate had the same name as one of its attributes #1307 [Jeremy Kemper]
+
+* Changed that destroying an object will only freeze the attributes hash, which keeps the object from having attributes changed (as that wouldn't make sense), but allows for the querying of associations after it has been destroyed.
+
+* Changed the callbacks such that observers are notified before the in-object callbacks are triggered. Without this change, it wasn't possible to act on the whole object in something like a before_destroy observer without having the objects own callbacks (like deleting associations) called first.
+
+* Added option for passing an array to the find_all version of the dynamic finders and have it evaluated as an IN fragment. Example:
+
+ # SELECT * FROM topics WHERE title IN ('First', 'Second')
+ Topic.find_all_by_title(["First", "Second"])
+
+* Added compatibility with camelCase column names for dynamic finders #533 [Dee.Zsombor]
+
+* Fixed extraneous comma in count() function that made it not work with joins #1156 [jarkko/Dee.Zsombor]
+
+* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 [Alisdair McDiarmid]
+
+* Fixed that validate_length_of lost :on option when :within was specified #1195 [jhosteny@mac.com]
+
+* Added encoding and min_messages options for PostgreSQL #1205 [shugo]. Configuration example:
+
+ development:
+ adapter: postgresql
+ database: rails_development
+ host: localhost
+ username: postgres
+ password:
+ encoding: UTF8
+ min_messages: ERROR
+
+* Fixed acts_as_list where deleting an item that was removed from the list would ruin the positioning of other list items #1197 [Jamis Buck]
+
+* Added validates_exclusion_of as a negative of validates_inclusion_of
+
+* Optimized counting of has_many associations by setting the association to empty if the count is 0 so repeated calls doesn't trigger database calls
+
+
+*1.10.1* (20th April, 2005)
+
+* Fixed frivilous database queries being triggered with eager loading on empty associations and other things
+
+* Fixed order of loading in eager associations
+
+* Fixed stray comma when using eager loading and ordering together from has_many associations #1143
+
+
+*1.10.0* (19th April, 2005)
+
+* Added eager loading of associations as a way to solve the N+1 problem more gracefully without piggy-back queries. Example:
+
+ for post in Post.find(:all, :limit => 100)
+ puts "Post: " + post.title
+ puts "Written by: " + post.author.name
+ puts "Last comment on: " + post.comments.first.created_on
+ end
+
+ This used to generate 301 database queries if all 100 posts had both author and comments. It can now be written as:
+
+ for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
+
+ ...and the number of database queries needed is now 1.
+
+* Added new unified Base.find API and deprecated the use of find_first and find_all. See the documentation for Base.find. Examples:
+
+ Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
+ Person.find(1, 5, 6, :conditions => "administrator = 1", :order => "created_on DESC")
+ Person.find(:first, :order => "created_on DESC", :offset => 5)
+ Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
+ Person.find(:all, :offset => 10, :limit => 10)
+
+* Added acts_as_nested_set #1000 [wschenk]. Introduction:
+
+ This acts provides Nested Set functionality. Nested Set is similiar to Tree, but with
+ the added feature that you can select the children and all of it's descendants with
+ a single query. A good use case for this is a threaded post system, where you want
+ to display every reply to a comment without multiple selects.
+
+* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [After much pestering from Dave Thomas]
+
+* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 [gnuman1@gmail.com]
+
+* Fixed that fixtures were being deleted in the same order as inserts causing FK errors #890 [andrew.john.peters@gmail.com]
+
+* Fixed loading of fixtures in to be in the right order (or PostgreSQL would bark) #1047 [stephenh@chase3000.com]
+
+* Fixed page caching for non-vhost applications living underneath the root #1004 [Ben Schumacher]
+
+* Fixes a problem with the SQL Adapter which was resulting in IDENTITY_INSERT not being set to ON when it should be #1104 [adelle]
+
+* Added the option to specify the acceptance string in validates_acceptance_of #1106 [caleb@aei-tech.com]
+
+* Added insert_at(position) to acts_as_list #1083 [DeLynnB]
+
+* Removed the default order by id on has_and_belongs_to_many queries as it could kill performance on large sets (you can still specify by hand with :order)
+
+* Fixed that Base.silence should restore the old logger level when done, not just set it to DEBUG #1084 [yon@milliped.com]
+
+* Fixed boolean saving on Oracle #1093 [mparrish@pearware.org]
+
+* Moved build_association and create_association for has_one and belongs_to out of deprecation as they work when the association is nil unlike association.build and association.create, which require the association to be already in place #864
+
+* Added rollbacks of transactions if they're active as the dispatcher is killed gracefully (TERM signal) #1054 [Leon Bredt]
+
+* Added quoting of column names for fixtures #997 [jcfischer@gmail.com]
+
+* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 [Caleb Tennis]
+
+* Fixed that benchmarking times for rendering included db runtimes #987 [skaes@web.de]
+
+* Fixed boolean queries for t/f fields in PostgreSQL #995 [dave@cherryville.org]
+
+* Added that model.items.delete(child) will delete the child, not just set the foreign key to nil, if the child is dependent on the model #978 [Jeremy Kemper]
+
+* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 [dave@cherryville.org]
+
+* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [skaes@web.de]
+
+* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [skaes@web.de]
+
+* Fixed bug in Base#hash method that would treat records with the same string-based id as different [Dave Thomas]
+
+* Renamed DateHelper#distance_of_time_in_words_to_now to DateHelper#time_ago_in_words (old method name is still available as a deprecated alias)
+
+
+*1.9.1* (27th March, 2005)
+
+* Fixed that Active Record objects with float attribute could not be cloned #808
+
+* Fixed that MissingSourceFile's wasn't properly detected in production mode #925 [Nicholas Seckar]
+
+* Fixed that :counter_cache option would look for a line_items_count column for a LineItem object instead of lineitems_count
+
+* Fixed that AR exists?() would explode on postgresql if the passed id did not match the PK type #900 [Scott Barron]
+
+* Fixed the MS SQL adapter to work with the new limit/offset approach and with binary data (still suffering from 7KB limit, though) #901 [delynnb]
+
+
+*1.9.0* (22th March, 2005)
+
+* Added adapter independent limit clause as a two-element array with the first being the limit, the second being the offset #795 [Sam Stephenson]. Example:
+
+ Developer.find_all nil, 'id ASC', 5 # return the first five developers
+ Developer.find_all nil, 'id ASC', [3, 8] # return three developers, starting from #8 and forward
+
+ This doesn't yet work with the DB2 or MS SQL adapters. Patches to make that happen are encouraged.
+
+* Added alias_method :to_param, :id to Base, such that Active Record objects to be used as URL parameters in Action Pack automatically #812 [Nicholas Seckar/Sam Stephenson]
+
+* Improved the performance of the OCI8 adapter for Oracle #723 [pilx/gjenkins]
+
+* Added type conversion before saving a record, so string-based values like "10.0" aren't left for the database to convert #820 [dave@cherryville.org]
+
+* Added with additional settings for working with transactional fixtures and pre-loaded test databases #865 [mindel]
+
+* Fixed acts_as_list to trigger remove_from_list on destroy after the fact, not before, so a unique position can be maintained #871 [Alisdair McDiarmid]
+
+* Added the possibility of specifying fixtures in multiple calls #816 [kim@tinker.com]
+
+* Added Base.exists?(id) that'll return true if an object of the class with the given id exists #854 [stian@grytoyr.net]
+
+* Added optionally allow for nil or empty strings with validates_numericality_of #801 [Sebastian Kanthak]
+
+* Fixed problem with using slashes in validates_format_of regular expressions #801 [Sebastian Kanthak]
+
+* Fixed that SQLite3 exceptions are caught and reported properly #823 [yerejm]
+
+* Added that all types of after_find/after_initialized callbacks are triggered if the explicit implementation is present, not only the explicit implementation itself
+
+* Fixed that symbols can be used on attribute assignment, like page.emails.create(:subject => data.subject, :body => data.body)
+
+
+*1.8.0* (7th March, 2005)
+
+* Added ActiveRecord::Base.colorize_logging to control whether to use colors in logs or not (on by default)
+
+* Added support for timestamp with time zone in PostgreSQL #560 [Scott Barron]
+
+* Added MultiparameterAssignmentErrors and AttributeAssignmentError exceptions #777 [demetrius]. Documentation:
+
+ * +MultiparameterAssignmentErrors+ -- collection of errors that occurred during a mass assignment using the
+ +attributes=+ method. The +errors+ property of this exception contains an array of +AttributeAssignmentError+
+ objects that should be inspected to determine which attributes triggered the errors.
+ * +AttributeAssignmentError+ -- an error occurred while doing a mass assignment through the +attributes=+ method.
+ You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
+
+* Fixed that postgresql adapter would fails when reading bytea fields with null value #771 [rodrigo k]
+
+* Added transactional fixtures that uses rollback to undo changes to fixtures instead of DELETE/INSERT -- it's much faster. See documentation under Fixtures #760 [Jeremy Kemper]
+
+* Added destruction of dependent objects in has_one associations when a new assignment happens #742 [mindel]. Example:
+
+ class Account < ActiveRecord::Base
+ has_one :credit_card, :dependent => true
+ end
+ class CreditCard < ActiveRecord::Base
+ belongs_to :account
+ end
+
+ account.credit_card # => returns existing credit card, lets say id = 12
+ account.credit_card = CreditCard.create("number" => "123")
+ account.save # => CC with id = 12 is destroyed
+
+
+* Added validates_numericality_of #716 [skanthak/c.r.mcgrath]. Docuemntation:
+
+ Validates whether the value of the specified attribute is numeric by trying to convert it to
+ a float with Kernel.Float (if integer is false) or applying it to the regular expression
+ /^[\+\-]?\d+$/ (if integer is set to true).
+
+ class Person < ActiveRecord::Base
+ validates_numericality_of :value, :on => :create
+ end
+
+ Configuration options:
+ * message - A custom error message (default is: "is not a number")
+ * on Specifies when this validation is active (default is :save, other options :create, :update)
+ * only_integer Specifies whether the value has to be an integer, e.g. an integral value (default is false)
+
+
+* Fixed that HasManyAssociation#count was using :finder_sql rather than :counter_sql if it was available #445 [Scott Barron]
+
+* Added better defaults for composed_of, so statements like composed_of :time_zone, :mapping => %w( time_zone time_zone ) can be written without the mapping part (it's now assumed)
+
+* Added MacroReflection#macro which will return a symbol describing the macro used (like :composed_of or :has_many) #718, #248 [james@slashetc.com]
+
+
+*1.7.0* (24th February, 2005)
+
+* Changed the auto-timestamping feature to use ActiveRecord::Base.default_timezone instead of entertaining the parallel ActiveRecord::Base.timestamps_gmt method. The latter is now deprecated and will throw a warning on use (but still work) #710 [Jamis Buck]
+
+* Added a OCI8-based Oracle adapter that has been verified to work with Oracle 8 and 9 #629 [Graham Jenkins]. Usage notes:
+
+ 1. Key generation uses a sequence "rails_sequence" for all tables. (I couldn't find a simple
+ and safe way of passing table-specific sequence information to the adapter.)
+ 2. Oracle uses DATE or TIMESTAMP datatypes for both dates and times. Consequently I have had to
+ resort to some hacks to get data converted to Date or Time in Ruby.
+ If the column_name ends in _at (like created_at, updated_at) it's created as a Ruby Time. Else if the
+ hours/minutes/seconds are 0, I make it a Ruby Date. Else it's a Ruby Time.
+ This is nasty - but if you use Duck Typing you'll probably not care very much.
+ In 9i it's tempting to map DATE to Date and TIMESTAMP to Time but I don't think that is
+ valid - too many databases use DATE for both.
+ Timezones and sub-second precision on timestamps are not supported.
+ 3. Default values that are functions (such as "SYSDATE") are not supported. This is a
+ restriction of the way active record supports default values.
+ 4. Referential integrity constraints are not fully supported. Under at least
+ some circumstances, active record appears to delete parent and child records out of
+ sequence and out of transaction scope. (Or this may just be a problem of test setup.)
+
+ The OCI8 driver can be retrieved from http://rubyforge.org/projects/ruby-oci8/
+
+* Added option :schema_order to the PostgreSQL adapter to support the use of multiple schemas per database #697 [YuriSchimke]
+
+* Optimized the SQL used to generate has_and_belongs_to_many queries by listing the join table first #693 [yerejm]
+
+* Fixed that when using validation macros with a custom message, if you happened to use single quotes in the message string you would get a parsing error #657 [tonka]
+
+* Fixed that Active Record would throw Broken Pipe errors with FCGI when the MySQL connection timed out instead of reconnecting #428 [Nicholas Seckar]
+
+* Added options to specify an SSL connection for MySQL. Define the following attributes in the connection config (config/database.yml in Rails) to use it: sslkey, sslcert, sslca, sslcapath, sslcipher. To use SSL with no client certs, just set :sslca = '/dev/null'. http://dev.mysql.com/doc/mysql/en/secure-connections.html #604 [daniel@nightrunner.com]
+
+* Added automatic dropping/creating of test tables for running the unit tests on all databases #587 [adelle@bullet.net.au]
+
+* Fixed that find_by_* would fail when column names had numbers #670 [demetrius]
+
+* Fixed the SQL Server adapter on a bunch of issues #667 [DeLynn]
+
+ 1. Created a new columns method that is much cleaner.
+ 2. Corrected a problem with the select and select_all methods
+ that didn't account for the LIMIT clause being passed into raw SQL statements.
+ 3. Implemented the string_to_time method in order to create proper instances of the time class.
+ 4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
+ 5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
+
+* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow@yahoo.com]
+
+* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
+
+ class Person < ActiveRecord::Base
+ validates_each :first_name, :last_name do |record, attr|
+ record.errors.add attr, 'starts with z.' if attr[0] == ?z
+ end
+ end
+
+* Added :allow_nil as an explicit option for validates_length_of, so unless that's set to true having the attribute as nil will also return an error if a range is specified as :within #610 [Jeremy Kemper]
+
+* Added that validates_* now accept blocks to perform validations #618 [Tim Bates]. Example:
+
+ class Person < ActiveRecord::Base
+ validate { |person| person.errors.add("title", "will never be valid") if SHOULD_NEVER_BE_VALID }
+ end
+
+* Addded validation for validate all the associated objects before declaring failure with validates_associated #618 [Tim Bates]
+
+* Added keyword-style approach to defining the custom relational bindings #545 [Jamis Buck]. Example:
+
+ class Project < ActiveRecord::Base
+ primary_key "sysid"
+ table_name "XYZ_PROJECT"
+ inheritance_column { original_inheritance_column + "_id" }
+ end
+
+* Fixed Base#clone for use with PostgreSQL #565 [hanson@surgery.wisc.edu]
+
+
+*1.6.0* (January 25th, 2005)
+
+* Added that has_many association build and create methods can take arrays of record data like Base#create and Base#build to build/create multiple records at once.
+
+* Added that Base#delete and Base#destroy both can take an array of ids to delete/destroy #336
+
+* Added the option of supplying an array of attributes to Base#create, so that multiple records can be created at once.
+
+* Added the option of supplying an array of ids and attributes to Base#update, so that multiple records can be updated at once (inspired by #526/Duane Johnson). Example
+
+ people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
+ Person.update(people.keys, people.values)
+
+* Added ActiveRecord::Base.timestamps_gmt that can be set to true to make the automated timestamping use GMT instead of local time #520 [Scott Baron]
+
+* Added that update_all calls sanitize_sql on its updates argument, so stuff like MyRecord.update_all(['time = ?', Time.now]) works #519 [notahat]
+
+* Fixed that the dynamic finders didn't treat nil as a "IS NULL" but rather "= NULL" case #515 [Demetrius]
+
+* Added bind-named arrays for interpolating a group of ids or strings in conditions #528 [Jeremy Kemper]
+
+* Added that has_and_belongs_to_many associations with additional attributes also can be created between unsaved objects and only committed to the database when Base#save is called on the associator #524 [Eric Anderson]
+
+* Fixed that records fetched with piggy-back attributes or through rich has_and_belongs_to_many associations couldn't be saved due to the extra attributes not part of the table #522 [Eric Anderson]
+
+* Added mass-assignment protection for the inheritance column -- regardless of a custom column is used or not
+
+* Fixed that association proxies would fail === tests like PremiumSubscription === @account.subscription
+
+* Fixed that column aliases didn't work as expected with the new MySql411 driver #507 [Demetrius]
+
+* Fixed that find_all would produce invalid sql when called sequentialy #490 [Scott Baron]
+
+
+*1.5.1* (January 18th, 2005)
+
+* Fixed that the belongs_to and has_one proxy would fail a test like 'if project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 [Tim Bates]
+
+* Fixed that the Ruby/MySQL adapter wouldn't connect if the password was empty #503 [Pelle]
+
+
+*1.5.0* (January 17th, 2005)
+
+* Fixed that unit tests for MySQL are now run as the "rails" user instead of root #455 [Eric Hodel]
+
+* Added validates_associated that enables validation of objects in an unsaved association #398 [Tim Bates]. Example:
+
+ class Book < ActiveRecord::Base
+ has_many :pages
+ belongs_to :library
+
+ validates_associated :pages, :library
+ end
+
+* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
+
+ == Unsaved objects and associations
+
+ You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
+ aware of, mostly involving the saving of associated objects.
+
+ === One-to-one associations
+
+ * Assigning an object to a has_one association automatically saves that object, and the object being replaced (if there is one), in
+ order to update their primary keys - except if the parent object is unsaved (new_record? == true).
+ * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
+ is cancelled.
+ * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
+ * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
+ not save the parent either.
+
+ === Collections
+
+ * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
+ (the owner of the collection) is not yet stored in the database.
+ * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
+ * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
+ * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
+
+* Added replace to associations, so you can do project.manager.replace(new_manager) or project.milestones.replace(new_milestones) #402 [Tim Bates]
+
+* Added build and create methods to has_one and belongs_to associations, so you can now do project.manager.build(attributes) #402 [Tim Bates]
+
+* Added that if a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last. #402 [Tim Bates]
+
+* Fixed that Base#== wouldn't work for multiple references to the same unsaved object #402 [Tim Bates]
+
+* Fixed binary support for PostgreSQL #444 [alex@byzantine.no]
+
+* Added a differenciation between AssociationCollection#size and -length. Now AssociationCollection#size returns the size of the
+ collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and calling collection.size if it has. If
+ it's more likely than not that the collection does have a size larger than zero and you need to fetch that collection afterwards,
+ it'll take one less SELECT query if you use length.
+
+* Added Base#attributes that returns a hash of all the attributes with their names as keys and clones of their objects as values #433 [atyp.de]
+
+* Fixed that foreign keys named the same as the association would cause stack overflow #437 [Eric Anderson]
+
+* Fixed default scope of acts_as_list from "1" to "1 = 1", so it'll work in PostgreSQL (among other places) #427 [Alexey]
+
+* Added Base#reload that reloads the attributes of an object from the database #422 [Andreas Schwarz]
+
+* Added SQLite3 compatibility through the sqlite3-ruby adapter by Jamis Buck #381 [Jeremy Kemper]
+
+* Added support for the new protocol spoken by MySQL 4.1.1+ servers for the Ruby/MySQL adapter that ships with Rails #440 [Matt Mower]
+
+* Added that Observers can use the observes class method instead of overwriting self.observed_class().
+
+ Before:
+ class ListSweeper < ActiveRecord::Base
+ def self.observed_class() [ List, Item ]
+ end
+
+ After:
+ class ListSweeper < ActiveRecord::Base
+ observes List, Item
+ end
+
+* Fixed that conditions in has_many and has_and_belongs_to_many should be interpolated just like the finder_sql is
+
+* Fixed Base#update_attribute to be indifferent to whether a string or symbol is used to describe the name
+
+* Added Base#toggle(attribute) and Base#toggle!(attribute) that makes it easier to flip a switch or flag.
+
+ Before: topic.update_attribute(:approved, !approved?)
+ After : topic.toggle!(:approved)
+
+* Added Base#increment!(attribute) and Base#decrement!(attribute) that also saves the records. Example:
+
+ page.views # => 1
+ page.increment!(:views) # executes an UPDATE statement
+ page.views # => 2
+
+ page.increment(:views).increment!(:views)
+ page.views # => 4
+
+* Added Base#increment(attribute) and Base#decrement(attribute) that encapsulates the += 1 and -= 1 patterns.
+
+
+
+
+*1.14.2* (April 9th, 2005)
+
+* Fixed calculations for the Oracle Adapter (closes #4626) [Michael Schoen]
+
+
*1.14.1* (April 6th, 2006)
* Fix type_name_with_module to handle type names that begin with '::'. Closes #4614. [Nicholas Seckar]
diff --git a/vendor/rails/activerecord/MIT-LICENSE b/vendor/rails/activerecord/MIT-LICENSE
index 530150e2..5fee6e10 100644
--- a/vendor/rails/activerecord/MIT-LICENSE
+++ b/vendor/rails/activerecord/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2006 David Heinemeier Hansson
+Copyright (c) 2004-2007 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/vendor/rails/activerecord/README b/vendor/rails/activerecord/README
index c938c66f..44253018 100755
--- a/vendor/rails/activerecord/README
+++ b/vendor/rails/activerecord/README
@@ -65,20 +65,6 @@ A short rundown of the major features:
end
{Learn more}[link:classes/ActiveRecord/Validations.html]
-
-
-* Acts that can make records work as lists or trees:
-
- class Item < ActiveRecord::Base
- belongs_to :list
- acts_as_list :scope => :list
- end
-
- item.move_higher
- item.move_to_bottom
-
- Learn about {acts_as_list}[link:classes/ActiveRecord/Acts/List/ClassMethods.html], {the instance methods acts_as_list provides}[link:classes/ActiveRecord/Acts/List/InstanceMethods.html], and
- {acts_as_tree}[link:classes/ActiveRecord/Acts/Tree/ClassMethods.html]
* Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc).
@@ -176,7 +162,7 @@ A short rundown of the major features:
)
{Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for
- MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OCIAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html].
+ MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OracleAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html].
* Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc]
diff --git a/vendor/rails/activerecord/RUNNING_UNIT_TESTS b/vendor/rails/activerecord/RUNNING_UNIT_TESTS
index 80cfd8a6..cbf702d6 100644
--- a/vendor/rails/activerecord/RUNNING_UNIT_TESTS
+++ b/vendor/rails/activerecord/RUNNING_UNIT_TESTS
@@ -23,42 +23,11 @@ Rake can be found at http://rake.rubyforge.org
== Running by hand
Unit tests are located in test directory. If you only want to run a single test suite,
-or don't want to bother with Rake, you can do so with something like:
+you can do so with:
- cd test; ruby -I "connections/native_mysql" base_test.rb
+ rake test_mysql TEST=base_test.rb
-That'll run the base suite using the MySQL-Ruby adapter. Change the adapter
-and test suite name as needed.
+That'll run the base suite using the MySQL-Ruby adapter.
-You can also run all the suites on a specific adapter with:
- cd test; all.sh "connections/native_mysql"
-
-== Faster tests
-
-If you are using a database that supports transactions, you can set the
-"AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
-This gives a very large speed boost. With rake:
-
- rake AR_TX_FIXTURES=yes
-
-Or, by hand:
-
- AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb
-
-== Testing with Oracle
-
-In order to allow for testing against Oracle using an "arunit" schema within an existing
-Oracle database, the database name and tns connection string must be set in environment
-variables prior to running the unit tests.
-
- $ export ARUNIT_DB_NAME=MYDB
- $ export ARUNIT_DB=MYDB
-
-The ARUNIT_DB_NAME variable should be set to the name by which the database knows
-itself, ie., what will be returned by the query:
-
- select sys_context('userenv','db_name') db from dual
-
-And the ARUNIT_DB variable should be set to the tns connection string.
diff --git a/vendor/rails/activerecord/Rakefile b/vendor/rails/activerecord/Rakefile
index e7fd12c4..c22934e1 100755
--- a/vendor/rails/activerecord/Rakefile
+++ b/vendor/rails/activerecord/Rakefile
@@ -22,106 +22,127 @@ PKG_FILES = FileList[
].exclude(/\bCVS\b|~$/)
-desc "Default Task"
-task :default => [ :test_mysql, :test_sqlite, :test_postgresql ]
+desc 'Run mysql, sqlite, and postgresql tests by default'
+task :default => :test
-# Run the unit tests
+desc 'Run mysql, sqlite, and postgresql tests'
+task :test => %w(test_mysql test_sqlite test_sqlite3 test_postgresql)
-for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oracle sybase openbase frontbase )
+for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase )
Rake::TestTask.new("test_#{adapter}") { |t|
t.libs << "test" << "test/connections/native_#{adapter}"
- if adapter =~ /^sqlserver/
- t.pattern = "test/**/*_test{,_sqlserver}.rb"
- else
- t.pattern = "test/**/*_test{,_#{adapter}}.rb"
- end
+ adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/]
+ t.pattern = "test/**/*_test{,_#{adapter_short}}.rb"
t.verbose = true
}
+
+ namespace adapter do
+ task :test => "test_#{adapter}"
+ end
end
SCHEMA_PATH = File.join(File.dirname(__FILE__), *%w(test fixtures db_definitions))
-desc 'Build the MySQL test databases'
-task :build_mysql_databases do
- %x( mysqladmin create activerecord_unittest )
- %x( mysqladmin create activerecord_unittest2 )
- %x( mysql -e "grant all on activerecord_unittest.* to rails@localhost" )
- %x( mysql -e "grant all on activerecord_unittest2.* to rails@localhost" )
- %x( mysql activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql.sql')} )
- %x( mysql activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql2.sql')} )
-end
-
-desc 'Drop the MySQL test databases'
-task :drop_mysql_databases do
- %x( mysqladmin -f drop activerecord_unittest )
- %x( mysqladmin -f drop activerecord_unittest2 )
-end
-
-desc 'Rebuild the MySQL test databases'
-task :rebuild_mysql_databases => [:drop_mysql_databases, :build_mysql_databases]
-
-desc 'Build the PostgreSQL test databases'
-task :build_postgresql_databases do
- %x( createdb -U postgres activerecord_unittest )
- %x( createdb -U postgres activerecord_unittest2 )
- %x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} postgres )
- %x( psql activerecord_unittest2 -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} postgres )
-end
-
-desc 'Drop the PostgreSQL test databases'
-task :drop_postgresql_databases do
- %x( dropdb -U postgres activerecord_unittest )
- %x( dropdb -U postgres activerecord_unittest2 )
-end
-
-desc 'Rebuild the PostgreSQL test databases'
-task :rebuild_postgresql_databases => [:drop_postgresql_databases, :build_postgresql_databases]
-
-desc 'Build the FrontBase test databases'
-task :build_frontbase_databases => :rebuild_frontbase_databases
-
-desc 'Rebuild the FrontBase test databases'
-task :rebuild_frontbase_databases do
- build_frontbase_database = Proc.new do |db_name, sql_definition_file|
- %(
- STOP DATABASE #{db_name};
- DELETE DATABASE #{db_name};
- CREATE DATABASE #{db_name};
-
- CONNECT TO #{db_name} AS SESSION_NAME USER _SYSTEM;
- SET COMMIT FALSE;
-
- CREATE USER RAILS;
- CREATE SCHEMA RAILS AUTHORIZATION RAILS;
- COMMIT;
-
- SET SESSION AUTHORIZATION RAILS;
- SCRIPT '#{sql_definition_file}';
-
- COMMIT;
-
- DISCONNECT ALL;
- )
+namespace :mysql do
+ desc 'Build the MySQL test databases'
+ task :build_databases do
+ %x( mysqladmin create activerecord_unittest )
+ %x( mysqladmin create activerecord_unittest2 )
+ %x( mysql -e "grant all on activerecord_unittest.* to rails@localhost" )
+ %x( mysql -e "grant all on activerecord_unittest2.* to rails@localhost" )
end
- create_activerecord_unittest = build_frontbase_database['activerecord_unittest', File.join(SCHEMA_PATH, 'frontbase.sql')]
- create_activerecord_unittest2 = build_frontbase_database['activerecord_unittest2', File.join(SCHEMA_PATH, 'frontbase2.sql')]
- execute_frontbase_sql = Proc.new do |sql|
- system(<<-SHELL)
- /Library/FrontBase/bin/sql92 <<-SQL
- #{sql}
- SQL
- SHELL
+
+ desc 'Drop the MySQL test databases'
+ task :drop_databases do
+ %x( mysqladmin -f drop activerecord_unittest )
+ %x( mysqladmin -f drop activerecord_unittest2 )
end
- execute_frontbase_sql[create_activerecord_unittest]
- execute_frontbase_sql[create_activerecord_unittest2]
+
+ desc 'Rebuild the MySQL test databases'
+ task :rebuild_databases => [:drop_databases, :build_databases]
end
+task :build_mysql_databases => 'mysql:build_databases'
+task :drop_mysql_databases => 'mysql:drop_databases'
+task :rebuild_mysql_databases => 'mysql:rebuild_databases'
+
+
+namespace :postgresql do
+ desc 'Build the PostgreSQL test databases'
+ task :build_databases do
+ %x( createdb -U postgres activerecord_unittest )
+ %x( createdb -U postgres activerecord_unittest2 )
+ %x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} postgres )
+ %x( psql activerecord_unittest2 -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} postgres )
+ end
+
+ desc 'Drop the PostgreSQL test databases'
+ task :drop_databases do
+ %x( dropdb -U postgres activerecord_unittest )
+ %x( dropdb -U postgres activerecord_unittest2 )
+ end
+
+ desc 'Rebuild the PostgreSQL test databases'
+ task :rebuild_databases => [:drop_databases, :build_databases]
+end
+
+task :build_postgresql_databases => 'postgresql:build_databases'
+task :drop_postgresql_databases => 'postgresql:drop_databases'
+task :rebuild_postgresql_databases => 'postgresql:rebuild_databases'
+
+
+namespace :frontbase do
+ desc 'Build the FrontBase test databases'
+ task :build_databases => :rebuild_frontbase_databases
+
+ desc 'Rebuild the FrontBase test databases'
+ task :rebuild_databases do
+ build_frontbase_database = Proc.new do |db_name, sql_definition_file|
+ %(
+ STOP DATABASE #{db_name};
+ DELETE DATABASE #{db_name};
+ CREATE DATABASE #{db_name};
+
+ CONNECT TO #{db_name} AS SESSION_NAME USER _SYSTEM;
+ SET COMMIT FALSE;
+
+ CREATE USER RAILS;
+ CREATE SCHEMA RAILS AUTHORIZATION RAILS;
+ COMMIT;
+
+ SET SESSION AUTHORIZATION RAILS;
+ SCRIPT '#{sql_definition_file}';
+
+ COMMIT;
+
+ DISCONNECT ALL;
+ )
+ end
+ create_activerecord_unittest = build_frontbase_database['activerecord_unittest', File.join(SCHEMA_PATH, 'frontbase.sql')]
+ create_activerecord_unittest2 = build_frontbase_database['activerecord_unittest2', File.join(SCHEMA_PATH, 'frontbase2.sql')]
+ execute_frontbase_sql = Proc.new do |sql|
+ system(<<-SHELL)
+ /Library/FrontBase/bin/sql92 <<-SQL
+ #{sql}
+ SQL
+ SHELL
+ end
+ execute_frontbase_sql[create_activerecord_unittest]
+ execute_frontbase_sql[create_activerecord_unittest2]
+ end
+end
+
+task :build_frontbase_databases => 'frontbase:build_databases'
+task :rebuild_frontbase_databases => 'frontbase:rebuild_databases'
+
+
# Generate the RDoc documentation
Rake::RDocTask.new { |rdoc|
rdoc.rdoc_dir = 'doc'
rdoc.title = "Active Record -- Object-relation mapping put on rails"
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+ rdoc.options << '--charset' << 'utf-8'
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
rdoc.rdoc_files.include('lib/**/*.rb')
@@ -138,7 +159,7 @@ end
# Create compressed packages
-dist_dirs = [ "lib", "test", "examples", "dev-utils" ]
+dist_dirs = [ "lib", "test", "examples" ]
spec = Gem::Specification.new do |s|
s.name = PKG_NAME
@@ -150,8 +171,8 @@ spec = Gem::Specification.new do |s|
dist_dirs.each do |dir|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end
-
- s.add_dependency('activesupport', '= 1.4.4' + PKG_BUILD)
+
+ s.add_dependency('activesupport', '= 2.0.2' + PKG_BUILD)
s.files.delete "test/fixtures/fixture_database.sqlite"
s.files.delete "test/fixtures/fixture_database_2.sqlite"
@@ -163,13 +184,13 @@ spec = Gem::Specification.new do |s|
s.has_rdoc = true
s.extra_rdoc_files = %w( README )
s.rdoc_options.concat ['--main', 'README']
-
+
s.author = "David Heinemeier Hansson"
s.email = "david@loudthinking.com"
s.homepage = "http://www.rubyonrails.org"
s.rubyforge_project = "activerecord"
end
-
+
Rake::GemPackageTask.new(spec) do |p|
p.gem_spec = spec
p.need_tar = true
@@ -190,10 +211,10 @@ task :lines do
codelines += 1
end
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
-
+
total_lines += lines
total_codelines += codelines
-
+
lines, codelines = 0, 0
end
@@ -204,13 +225,13 @@ end
# Publishing ------------------------------------------------------
desc "Publish the beta gem"
-task :pgem => [:package] do
+task :pgem => [:package] do
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
end
desc "Publish the API documentation"
-task :pdoc => [:rdoc] do
+task :pdoc => [:rdoc] do
Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/ar", "doc").upload
end
@@ -223,4 +244,4 @@ task :release => [ :package ] do
rubyforge = RubyForge.new
rubyforge.login
rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activerecord/benchmarks/benchmark.rb b/vendor/rails/activerecord/benchmarks/benchmark.rb
deleted file mode 100644
index de390c11..00000000
--- a/vendor/rails/activerecord/benchmarks/benchmark.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-$:.unshift(File.dirname(__FILE__) + '/../lib')
-if ARGV[2]
- require 'rubygems'
- gem 'activerecord', ARGV[2]
-else
- require 'active_record'
-end
-
-ActiveRecord::Base.establish_connection(:adapter => "mysql", :database => "basecamp")
-
-class Post < ActiveRecord::Base; end
-
-require 'benchmark'
-
-RUNS = ARGV[0].to_i
-if ARGV[1] == "profile" then require 'profile' end
-
-runtime = Benchmark::measure {
- RUNS.times {
- Post.find_all(nil,nil,100).each { |p| p.title }
- }
-}
-
-puts "Runs: #{RUNS}"
-puts "Avg. runtime: #{runtime.real / RUNS}"
-puts "Requests/second: #{RUNS / runtime.real}"
diff --git a/vendor/rails/activerecord/benchmarks/mysql_benchmark.rb b/vendor/rails/activerecord/benchmarks/mysql_benchmark.rb
deleted file mode 100644
index 2f9e0e69..00000000
--- a/vendor/rails/activerecord/benchmarks/mysql_benchmark.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require 'mysql'
-
-conn = Mysql::real_connect("localhost", "root", "", "basecamp")
-
-require 'benchmark'
-
-require 'profile' if ARGV[1] == "profile"
-RUNS = ARGV[0].to_i
-
-runtime = Benchmark::measure {
- RUNS.times {
- result = conn.query("SELECT * FROM posts LIMIT 100")
- result.each_hash { |p| p["title"] }
- }
-}
-
-puts "Runs: #{RUNS}"
-puts "Avg. runtime: #{runtime.real / RUNS}"
-puts "Requests/second: #{RUNS / runtime.real}"
\ No newline at end of file
diff --git a/vendor/rails/activerecord/examples/associations.rb b/vendor/rails/activerecord/examples/associations.rb
deleted file mode 100644
index b0df3673..00000000
--- a/vendor/rails/activerecord/examples/associations.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-require File.dirname(__FILE__) + '/shared_setup'
-
-logger = Logger.new(STDOUT)
-
-# Database setup ---------------
-
-logger.info "\nCreate tables"
-
-[ "DROP TABLE companies", "DROP TABLE people", "DROP TABLE people_companies",
- "CREATE TABLE companies (id int(11) auto_increment, client_of int(11), name varchar(255), type varchar(100), PRIMARY KEY (id))",
- "CREATE TABLE people (id int(11) auto_increment, name varchar(100), PRIMARY KEY (id))",
- "CREATE TABLE people_companies (person_id int(11), company_id int(11), PRIMARY KEY (person_id, company_id))",
-].each { |statement|
- # Tables doesn't necessarily already exist
- begin; ActiveRecord::Base.connection.execute(statement); rescue ActiveRecord::StatementInvalid; end
-}
-
-
-# Class setup ---------------
-
-class Company < ActiveRecord::Base
- has_and_belongs_to_many :people, :class_name => "Person", :join_table => "people_companies", :table_name => "people"
-end
-
-class Firm < Company
- has_many :clients, :foreign_key => "client_of"
-
- def people_with_all_clients
- clients.inject([]) { |people, client| people + client.people }
- end
-end
-
-class Client < Company
- belongs_to :firm, :foreign_key => "client_of"
-end
-
-class Person < ActiveRecord::Base
- has_and_belongs_to_many :companies, :join_table => "people_companies"
- def self.table_name() "people" end
-end
-
-
-# Usage ---------------
-
-logger.info "\nCreate fixtures"
-
-Firm.new("name" => "Next Angle").save
-Client.new("name" => "37signals", "client_of" => 1).save
-Person.new("name" => "David").save
-
-
-logger.info "\nUsing Finders"
-
-next_angle = Company.find(1)
-next_angle = Firm.find(1)
-next_angle = Company.find_first "name = 'Next Angle'"
-next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first
-
-Firm === next_angle
-
-
-logger.info "\nUsing has_many association"
-
-next_angle.has_clients?
-next_angle.clients_count
-all_clients = next_angle.clients
-
-thirty_seven_signals = next_angle.find_in_clients(2)
-
-
-logger.info "\nUsing belongs_to association"
-
-thirty_seven_signals.has_firm?
-thirty_seven_signals.firm?(next_angle)
-
-
-logger.info "\nUsing has_and_belongs_to_many association"
-
-david = Person.find(1)
-david.add_companies(thirty_seven_signals, next_angle)
-david.companies.include?(next_angle)
-david.companies_count == 2
-
-david.remove_companies(next_angle)
-david.companies_count == 1
-
-thirty_seven_signals.people.include?(david)
\ No newline at end of file
diff --git a/vendor/rails/activerecord/examples/shared_setup.rb b/vendor/rails/activerecord/examples/shared_setup.rb
deleted file mode 100644
index 6ede4b1d..00000000
--- a/vendor/rails/activerecord/examples/shared_setup.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# Be sure to change the mysql_connection details and create a database for the example
-
-$: << File.dirname(__FILE__) + '/../lib'
-
-require 'active_record'
-require 'logger'; class Logger; def format_message(severity, timestamp, msg, progname) "#{msg}\n" end; end
-
-ActiveRecord::Base.logger = Logger.new(STDOUT)
-ActiveRecord::Base.establish_connection(
- :adapter => "mysql",
- :host => "localhost",
- :username => "root",
- :password => "",
- :database => "activerecord_examples"
-)
diff --git a/vendor/rails/activerecord/examples/validation.rb b/vendor/rails/activerecord/examples/validation.rb
deleted file mode 100644
index e6a448af..00000000
--- a/vendor/rails/activerecord/examples/validation.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-require File.dirname(__FILE__) + '/shared_setup'
-
-logger = Logger.new(STDOUT)
-
-# Database setup ---------------
-
-logger.info "\nCreate tables"
-
-[ "DROP TABLE people",
- "CREATE TABLE people (id int(11) auto_increment, name varchar(100), pass varchar(100), email varchar(100), PRIMARY KEY (id))"
-].each { |statement|
- begin; ActiveRecord::Base.connection.execute(statement); rescue ActiveRecord::StatementInvalid; end # Tables doesn't necessarily already exist
-}
-
-
-# Class setup ---------------
-
-class Person < ActiveRecord::Base
- # Using
- def self.authenticate(name, pass)
- # find_first "name = '#{name}' AND pass = '#{pass}'" would be open to sql-injection (in a web-app scenario)
- find_first [ "name = '%s' AND pass = '%s'", name, pass ]
- end
-
- def self.name_exists?(name, id = nil)
- if id.nil?
- condition = [ "name = '%s'", name ]
- else
- # Check if anyone else than the person identified by person_id has that user_name
- condition = [ "name = '%s' AND id <> %d", name, id ]
- end
-
- !find_first(condition).nil?
- end
-
- def email_address_with_name
- "\"#{name}\" <#{email}>"
- end
-
- protected
- def validate
- errors.add_on_empty(%w(name pass email))
- errors.add("email", "must be valid") unless email_address_valid?
- end
-
- def validate_on_create
- if attribute_present?("name") && Person.name_exists?(name)
- errors.add("name", "is already taken by another person")
- end
- end
-
- def validate_on_update
- if attribute_present?("name") && Person.name_exists?(name, id)
- errors.add("name", "is already taken by another person")
- end
- end
-
- private
- def email_address_valid?() email =~ /\w[-.\w]*\@[-\w]+[-.\w]*\.\w+/ end
-end
-
-# Usage ---------------
-
-logger.info "\nCreate fixtures"
-david = Person.new("name" => "David Heinemeier Hansson", "pass" => "", "email" => "")
-unless david.save
- puts "There was #{david.errors.count} error(s)"
- david.errors.each_full { |error| puts error }
-end
-
-david.pass = "something"
-david.email = "invalid_address"
-unless david.save
- puts "There was #{david.errors.count} error(s)"
- puts "It was email with: " + david.errors.on("email")
-end
-
-david.email = "david@loudthinking.com"
-if david.save then puts "David finally made it!" end
-
-
-another_david = Person.new("name" => "David Heinemeier Hansson", "pass" => "xc", "email" => "david@loudthinking")
-unless another_david.save
- puts "Error on name: " + another_david.errors.on("name")
-end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/install.rb b/vendor/rails/activerecord/install.rb
index 592c4b9d..c87398b1 100644
--- a/vendor/rails/activerecord/install.rb
+++ b/vendor/rails/activerecord/install.rb
@@ -18,7 +18,7 @@ unless $sitedir
end
end
-# the acual gruntwork
+# the actual gruntwork
Dir.chdir("lib")
Find.find("active_record", "active_record.rb") { |f|
diff --git a/vendor/rails/activerecord/lib/active_record.rb b/vendor/rails/activerecord/lib/active_record.rb
index c4a22805..36c1a2bd 100755
--- a/vendor/rails/activerecord/lib/active_record.rb
+++ b/vendor/rails/activerecord/lib/active_record.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2006 David Heinemeier Hansson
+# Copyright (c) 2004-2007 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -24,18 +24,21 @@
$:.unshift(File.dirname(__FILE__)) unless
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
-unless defined?(ActiveSupport)
- begin
- $:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
- require 'active_support'
- rescue LoadError
+unless defined? ActiveSupport
+ active_support_path = File.dirname(__FILE__) + "/../../activesupport/lib"
+ if File.exist?(active_support_path)
+ $:.unshift active_support_path
+ require 'active_support'
+ else
require 'rubygems'
gem 'activesupport'
+ require 'active_support'
end
end
require 'active_record/base'
require 'active_record/observer'
+require 'active_record/query_cache'
require 'active_record/validations'
require 'active_record/callbacks'
require 'active_record/reflection'
@@ -43,18 +46,16 @@ require 'active_record/associations'
require 'active_record/aggregations'
require 'active_record/transactions'
require 'active_record/timestamp'
-require 'active_record/acts/list'
-require 'active_record/acts/tree'
-require 'active_record/acts/nested_set'
require 'active_record/locking/optimistic'
require 'active_record/locking/pessimistic'
require 'active_record/migration'
require 'active_record/schema'
require 'active_record/calculations'
-require 'active_record/xml_serialization'
+require 'active_record/serialization'
require 'active_record/attribute_methods'
ActiveRecord::Base.class_eval do
+ extend ActiveRecord::QueryCache
include ActiveRecord::Validations
include ActiveRecord::Locking::Optimistic
include ActiveRecord::Locking::Pessimistic
@@ -65,21 +66,11 @@ ActiveRecord::Base.class_eval do
include ActiveRecord::Aggregations
include ActiveRecord::Transactions
include ActiveRecord::Reflection
- include ActiveRecord::Acts::Tree
- include ActiveRecord::Acts::List
- include ActiveRecord::Acts::NestedSet
include ActiveRecord::Calculations
- include ActiveRecord::XmlSerialization
+ include ActiveRecord::Serialization
include ActiveRecord::AttributeMethods
end
-unless defined?(RAILS_CONNECTION_ADAPTERS)
- RAILS_CONNECTION_ADAPTERS = %w( mysql postgresql sqlite firebird sqlserver db2 oracle sybase openbase frontbase )
-end
+require 'active_record/connection_adapters/abstract_adapter'
-RAILS_CONNECTION_ADAPTERS.each do |adapter|
- require "active_record/connection_adapters/" + adapter + "_adapter"
-end
-
-require 'active_record/query_cache'
require 'active_record/schema_dumper'
diff --git a/vendor/rails/activerecord/lib/active_record/acts/list.rb b/vendor/rails/activerecord/lib/active_record/acts/list.rb
deleted file mode 100644
index fdf1c14c..00000000
--- a/vendor/rails/activerecord/lib/active_record/acts/list.rb
+++ /dev/null
@@ -1,256 +0,0 @@
-module ActiveRecord
- module Acts #:nodoc:
- module List #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
-
- # This act provides the capabilities for sorting and reordering a number of objects in a list.
- # The class that has this specified needs to have a "position" column defined as an integer on
- # the mapped database table.
- #
- # Todo list example:
- #
- # class TodoList < ActiveRecord::Base
- # has_many :todo_items, :order => "position"
- # end
- #
- # class TodoItem < ActiveRecord::Base
- # belongs_to :todo_list
- # acts_as_list :scope => :todo_list
- # end
- #
- # todo_list.first.move_to_bottom
- # todo_list.last.move_higher
- module ClassMethods
- # Configuration options are:
- #
- # * +column+ - specifies the column name to use for keeping the position integer (default: position)
- # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach "_id"
- # (if that hasn't been already) and use that as the foreign key restriction. It's also possible
- # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
- # Example: acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'
- def acts_as_list(options = {})
- configuration = { :column => "position", :scope => "1 = 1" }
- configuration.update(options) if options.is_a?(Hash)
-
- configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
-
- if configuration[:scope].is_a?(Symbol)
- scope_condition_method = %(
- def scope_condition
- if #{configuration[:scope].to_s}.nil?
- "#{configuration[:scope].to_s} IS NULL"
- else
- "#{configuration[:scope].to_s} = \#{#{configuration[:scope].to_s}}"
- end
- end
- )
- else
- scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end"
- end
-
- class_eval <<-EOV
- include ActiveRecord::Acts::List::InstanceMethods
-
- def acts_as_list_class
- ::#{self.name}
- end
-
- def position_column
- '#{configuration[:column]}'
- end
-
- #{scope_condition_method}
-
- before_destroy :remove_from_list
- before_create :add_to_list_bottom
- EOV
- end
- end
-
- # All the methods available to a record that has had acts_as_list specified. Each method works
- # by assuming the object to be the item in the list, so chapter.move_lower would move that chapter
- # lower in the list of all chapters. Likewise, chapter.first? would return true if that chapter is
- # the first in the list of all chapters.
- module InstanceMethods
- # Insert the item at the given position (defaults to the top position of 1).
- def insert_at(position = 1)
- insert_at_position(position)
- end
-
- # Swap positions with the next lower item, if one exists.
- def move_lower
- return unless lower_item
-
- acts_as_list_class.transaction do
- lower_item.decrement_position
- increment_position
- end
- end
-
- # Swap positions with the next higher item, if one exists.
- def move_higher
- return unless higher_item
-
- acts_as_list_class.transaction do
- higher_item.increment_position
- decrement_position
- end
- end
-
- # Move to the bottom of the list. If the item is already in the list, the items below it have their
- # position adjusted accordingly.
- def move_to_bottom
- return unless in_list?
- acts_as_list_class.transaction do
- decrement_positions_on_lower_items
- assume_bottom_position
- end
- end
-
- # Move to the top of the list. If the item is already in the list, the items above it have their
- # position adjusted accordingly.
- def move_to_top
- return unless in_list?
- acts_as_list_class.transaction do
- increment_positions_on_higher_items
- assume_top_position
- end
- end
-
- # Removes the item from the list.
- def remove_from_list
- if in_list?
- decrement_positions_on_lower_items
- update_attribute position_column, nil
- end
- end
-
- # Increase the position of this item without adjusting the rest of the list.
- def increment_position
- return unless in_list?
- update_attribute position_column, self.send(position_column).to_i + 1
- end
-
- # Decrease the position of this item without adjusting the rest of the list.
- def decrement_position
- return unless in_list?
- update_attribute position_column, self.send(position_column).to_i - 1
- end
-
- # Return true if this object is the first in the list.
- def first?
- return false unless in_list?
- self.send(position_column) == 1
- end
-
- # Return true if this object is the last in the list.
- def last?
- return false unless in_list?
- self.send(position_column) == bottom_position_in_list
- end
-
- # Return the next higher item in the list.
- def higher_item
- return nil unless in_list?
- acts_as_list_class.find(:first, :conditions =>
- "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
- )
- end
-
- # Return the next lower item in the list.
- def lower_item
- return nil unless in_list?
- acts_as_list_class.find(:first, :conditions =>
- "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
- )
- end
-
- # Test if this record is in a list
- def in_list?
- !send(position_column).nil?
- end
-
- private
- def add_to_list_top
- increment_positions_on_all_items
- end
-
- def add_to_list_bottom
- self[position_column] = bottom_position_in_list.to_i + 1
- end
-
- # Overwrite this method to define the scope of the list changes
- def scope_condition() "1" end
-
- # Returns the bottom position number in the list.
- # bottom_position_in_list # => 2
- def bottom_position_in_list(except = nil)
- item = bottom_item(except)
- item ? item.send(position_column) : 0
- end
-
- # Returns the bottom item
- def bottom_item(except = nil)
- conditions = scope_condition
- conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
- acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
- end
-
- # Forces item to assume the bottom position in the list.
- def assume_bottom_position
- update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
- end
-
- # Forces item to assume the top position in the list.
- def assume_top_position
- update_attribute(position_column, 1)
- end
-
- # This has the effect of moving all the higher items up one.
- def decrement_positions_on_higher_items(position)
- acts_as_list_class.update_all(
- "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
- )
- end
-
- # This has the effect of moving all the lower items up one.
- def decrement_positions_on_lower_items
- return unless in_list?
- acts_as_list_class.update_all(
- "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
- )
- end
-
- # This has the effect of moving all the higher items down one.
- def increment_positions_on_higher_items
- return unless in_list?
- acts_as_list_class.update_all(
- "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
- )
- end
-
- # This has the effect of moving all the lower items down one.
- def increment_positions_on_lower_items(position)
- acts_as_list_class.update_all(
- "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
- )
- end
-
- # Increments position (position_column ) of all items in the list.
- def increment_positions_on_all_items
- acts_as_list_class.update_all(
- "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
- )
- end
-
- def insert_at_position(position)
- remove_from_list
- increment_positions_on_lower_items(position)
- self.update_attribute(position_column, position)
- end
- end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/acts/nested_set.rb b/vendor/rails/activerecord/lib/active_record/acts/nested_set.rb
deleted file mode 100644
index 09bf1c39..00000000
--- a/vendor/rails/activerecord/lib/active_record/acts/nested_set.rb
+++ /dev/null
@@ -1,211 +0,0 @@
-module ActiveRecord
- module Acts #:nodoc:
- module NestedSet #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
-
- # This acts provides Nested Set functionality. Nested Set is similiar to Tree, but with
- # the added feature that you can select the children and all of their descendents with
- # a single query. A good use case for this is a threaded post system, where you want
- # to display every reply to a comment without multiple selects.
- #
- # A google search for "Nested Set" should point you in the direction to explain the
- # database theory. I figured out a bunch of this from
- # http://threebit.net/tutorials/nestedset/tutorial1.html
- #
- # Instead of picturing a leaf node structure with children pointing back to their parent,
- # the best way to imagine how this works is to think of the parent entity surrounding all
- # of its children, and its parent surrounding it, etc. Assuming that they are lined up
- # horizontally, we store the left and right boundries in the database.
- #
- # Imagine:
- # root
- # |_ Child 1
- # |_ Child 1.1
- # |_ Child 1.2
- # |_ Child 2
- # |_ Child 2.1
- # |_ Child 2.2
- #
- # If my cirlces in circles description didn't make sense, check out this sweet
- # ASCII art:
- #
- # ___________________________________________________________________
- # | Root |
- # | ____________________________ ____________________________ |
- # | | Child 1 | | Child 2 | |
- # | | __________ _________ | | __________ _________ | |
- # | | | C 1.1 | | C 1.2 | | | | C 2.1 | | C 2.2 | | |
- # 1 2 3_________4 5________6 7 8 9_________10 11_______12 13 14
- # | |___________________________| |___________________________| |
- # |___________________________________________________________________|
- #
- # The numbers represent the left and right boundries. The table then might
- # look like this:
- # ID | PARENT | LEFT | RIGHT | DATA
- # 1 | 0 | 1 | 14 | root
- # 2 | 1 | 2 | 7 | Child 1
- # 3 | 2 | 3 | 4 | Child 1.1
- # 4 | 2 | 5 | 6 | Child 1.2
- # 5 | 1 | 8 | 13 | Child 2
- # 6 | 5 | 9 | 10 | Child 2.1
- # 7 | 5 | 11 | 12 | Child 2.2
- #
- # So, to get all children of an entry, you
- # SELECT * WHERE CHILD.LEFT IS BETWEEN PARENT.LEFT AND PARENT.RIGHT
- #
- # To get the count, it's (LEFT - RIGHT + 1)/2, etc.
- #
- # To get the direct parent, it falls back to using the PARENT_ID field.
- #
- # There are instance methods for all of these.
- #
- # The structure is good if you need to group things together; the downside is that
- # keeping data integrity is a pain, and both adding and removing an entry
- # require a full table write.
- #
- # This sets up a before_destroy trigger to prune the tree correctly if one of its
- # elements gets deleted.
- #
- module ClassMethods
- # Configuration options are:
- #
- # * +parent_column+ - specifies the column name to use for keeping the position integer (default: parent_id)
- # * +left_column+ - column name for left boundry data, default "lft"
- # * +right_column+ - column name for right boundry data, default "rgt"
- # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach "_id"
- # (if that hasn't been already) and use that as the foreign key restriction. It's also possible
- # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
- # Example: acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'
- def acts_as_nested_set(options = {})
- configuration = { :parent_column => "parent_id", :left_column => "lft", :right_column => "rgt", :scope => "1 = 1" }
-
- configuration.update(options) if options.is_a?(Hash)
-
- configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
-
- if configuration[:scope].is_a?(Symbol)
- scope_condition_method = %(
- def scope_condition
- if #{configuration[:scope].to_s}.nil?
- "#{configuration[:scope].to_s} IS NULL"
- else
- "#{configuration[:scope].to_s} = \#{#{configuration[:scope].to_s}}"
- end
- end
- )
- else
- scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end"
- end
-
- class_eval <<-EOV
- include ActiveRecord::Acts::NestedSet::InstanceMethods
-
- #{scope_condition_method}
-
- def left_col_name() "#{configuration[:left_column]}" end
-
- def right_col_name() "#{configuration[:right_column]}" end
-
- def parent_column() "#{configuration[:parent_column]}" end
-
- EOV
- end
- end
-
- module InstanceMethods
- # Returns true is this is a root node.
- def root?
- parent_id = self[parent_column]
- (parent_id == 0 || parent_id.nil?) && (self[left_col_name] == 1) && (self[right_col_name] > self[left_col_name])
- end
-
- # Returns true is this is a child node
- def child?
- parent_id = self[parent_column]
- !(parent_id == 0 || parent_id.nil?) && (self[left_col_name] > 1) && (self[right_col_name] > self[left_col_name])
- end
-
- # Returns true if we have no idea what this is
- def unknown?
- !root? && !child?
- end
-
-
- # Adds a child to this object in the tree. If this object hasn't been initialized,
- # it gets set up as a root node. Otherwise, this method will update all of the
- # other elements in the tree and shift them to the right, keeping everything
- # balanced.
- def add_child( child )
- self.reload
- child.reload
-
- if child.root?
- raise "Adding sub-tree isn\'t currently supported"
- else
- if ( (self[left_col_name] == nil) || (self[right_col_name] == nil) )
- # Looks like we're now the root node! Woo
- self[left_col_name] = 1
- self[right_col_name] = 4
-
- # What do to do about validation?
- return nil unless self.save
-
- child[parent_column] = self.id
- child[left_col_name] = 2
- child[right_col_name]= 3
- return child.save
- else
- # OK, we need to add and shift everything else to the right
- child[parent_column] = self.id
- right_bound = self[right_col_name]
- child[left_col_name] = right_bound
- child[right_col_name] = right_bound + 1
- self[right_col_name] += 2
- self.class.base_class.transaction {
- self.class.base_class.update_all( "#{left_col_name} = (#{left_col_name} + 2)", "#{scope_condition} AND #{left_col_name} >= #{right_bound}" )
- self.class.base_class.update_all( "#{right_col_name} = (#{right_col_name} + 2)", "#{scope_condition} AND #{right_col_name} >= #{right_bound}" )
- self.save
- child.save
- }
- end
- end
- end
-
- # Returns the number of nested children of this object.
- def children_count
- return (self[right_col_name] - self[left_col_name] - 1)/2
- end
-
- # Returns a set of itself and all of its nested children
- def full_set
- self.class.base_class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} BETWEEN #{self[left_col_name]} and #{self[right_col_name]})" )
- end
-
- # Returns a set of all of its children and nested children
- def all_children
- self.class.base_class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} > #{self[left_col_name]}) and (#{right_col_name} < #{self[right_col_name]})" )
- end
-
- # Returns a set of only this entry's immediate children
- def direct_children
- self.class.base_class.find(:all, :conditions => "#{scope_condition} and #{parent_column} = #{self.id}")
- end
-
- # Prunes a branch off of the tree, shifting all of the elements on the right
- # back to the left so the counts still work.
- def before_destroy
- return if self[right_col_name].nil? || self[left_col_name].nil?
- dif = self[right_col_name] - self[left_col_name] + 1
-
- self.class.base_class.transaction {
- self.class.base_class.delete_all( "#{scope_condition} and #{left_col_name} > #{self[left_col_name]} and #{right_col_name} < #{self[right_col_name]}" )
- self.class.base_class.update_all( "#{left_col_name} = (#{left_col_name} - #{dif})", "#{scope_condition} AND #{left_col_name} >= #{self[right_col_name]}" )
- self.class.base_class.update_all( "#{right_col_name} = (#{right_col_name} - #{dif} )", "#{scope_condition} AND #{right_col_name} >= #{self[right_col_name]}" )
- }
- end
- end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/acts/tree.rb b/vendor/rails/activerecord/lib/active_record/acts/tree.rb
deleted file mode 100644
index b92587e5..00000000
--- a/vendor/rails/activerecord/lib/active_record/acts/tree.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-module ActiveRecord
- module Acts #:nodoc:
- module Tree #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
-
- # Specify this act if you want to model a tree structure by providing a parent association and a children
- # association. This act requires that you have a foreign key column, which by default is called parent_id.
- #
- # class Category < ActiveRecord::Base
- # acts_as_tree :order => "name"
- # end
- #
- # Example:
- # root
- # \_ child1
- # \_ subchild1
- # \_ subchild2
- #
- # root = Category.create("name" => "root")
- # child1 = root.children.create("name" => "child1")
- # subchild1 = child1.children.create("name" => "subchild1")
- #
- # root.parent # => nil
- # child1.parent # => root
- # root.children # => [child1]
- # root.children.first.children.first # => subchild1
- #
- # In addition to the parent and children associations, the following instance methods are added to the class
- # after specifying the act:
- # * siblings : Returns all the children of the parent, excluding the current node ([ subchild2 ] when called from subchild1)
- # * self_and_siblings : Returns all the children of the parent, including the current node ([ subchild1, subchild2 ] when called from subchild1)
- # * ancestors : Returns all the ancestors of the current node ([child1, root] when called from subchild2)
- # * root : Returns the root of the current node (root when called from subchild2)
- module ClassMethods
- # Configuration options are:
- #
- # * foreign_key - specifies the column name to use for tracking of the tree (default: parent_id)
- # * order - makes it possible to sort the children according to this SQL snippet.
- # * counter_cache - keeps a count in a children_count column if set to true (default: false).
- def acts_as_tree(options = {})
- configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil }
- configuration.update(options) if options.is_a?(Hash)
-
- belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
- has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy
-
- class_eval <<-EOV
- include ActiveRecord::Acts::Tree::InstanceMethods
-
- def self.roots
- find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
- end
-
- def self.root
- find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
- end
- EOV
- end
- end
-
- module InstanceMethods
- # Returns list of ancestors, starting from parent until root.
- #
- # subchild1.ancestors # => [child1, root]
- def ancestors
- node, nodes = self, []
- nodes << node = node.parent while node.parent
- nodes
- end
-
- # Returns the root node of the tree.
- def root
- node = self
- node = node.parent while node.parent
- node
- end
-
- # Returns all siblings of the current node.
- #
- # subchild1.siblings # => [subchild2]
- def siblings
- self_and_siblings - [self]
- end
-
- # Returns all siblings and a reference to the current node.
- #
- # subchild1.self_and_siblings # => [subchild1, subchild2]
- def self_and_siblings
- parent ? parent.children : self.class.roots
- end
- end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/aggregations.rb b/vendor/rails/activerecord/lib/active_record/aggregations.rb
index a137a11c..6fef668e 100644
--- a/vendor/rails/activerecord/lib/active_record/aggregations.rb
+++ b/vendor/rails/activerecord/lib/active_record/aggregations.rb
@@ -70,7 +70,7 @@ module ActiveRecord
# end
#
# Now it's possible to access attributes from the database through the value objects instead. If you choose to name the
- # composition the same as the attributes name, it will be the only way to access that attribute. That's the case with our
+ # composition the same as the attribute's name, it will be the only way to access that attribute. That's the case with our
# +balance+ attribute. You interact with the value objects just like you would any other attribute, though:
#
# customer.balance = Money.new(20) # sets the Money value object and the attribute
@@ -92,19 +92,19 @@ module ActiveRecord
#
# == Writing value objects
#
- # Value objects are immutable and interchangeable objects that represent a given value, such as a Money object representing
- # $5. Two Money objects both representing $5 should be equal (through methods such as == and <=> from Comparable if ranking
- # makes sense). This is unlike entity objects where equality is determined by identity. An entity class such as Customer can
+ # Value objects are immutable and interchangeable objects that represent a given value, such as a +Money+ object representing
+ # $5. Two +Money+ objects both representing $5 should be equal (through methods such as == and <=> from +Comparable+ if ranking
+ # makes sense). This is unlike entity objects where equality is determined by identity. An entity class such as +Customer+ can
# easily have two different objects that both have an address on Hyancintvej. Entity identity is determined by object or
- # relational unique identifiers (such as primary keys). Normal ActiveRecord::Base classes are entity objects.
+ # relational unique identifiers (such as primary keys). Normal ActiveRecord::Base classes are entity objects.
#
- # It's also important to treat the value objects as immutable. Don't allow the Money object to have its amount changed after
- # creation. Create a new money object with the new value instead. This is exemplified by the Money#exchanged_to method that
+ # It's also important to treat the value objects as immutable. Don't allow the +Money+ object to have its amount changed after
+ # creation. Create a new +Money+ object with the new value instead. This is exemplified by the Money#exchanged_to method that
# returns a new value object instead of changing its own values. Active Record won't persist value objects that have been
- # changed through other means than the writer method.
+ # changed through means other than the writer method.
#
# The immutable requirement is enforced by Active Record by freezing any object assigned as a value object. Attempting to
- # change it afterwards will result in a TypeError.
+ # change it afterwards will result in a TypeError .
#
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not keeping value objects
# immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
@@ -119,71 +119,60 @@ module ActiveRecord
# * :mapping - specifies a number of mapping arrays (attribute, parameter) that bind an attribute name
# to a constructor parameter on the value class.
# * :allow_nil - specifies that the aggregate object will not be instantiated when all mapped
- # attributes are nil. Setting the aggregate class to nil has the effect of writing nil to all mapped attributes.
- # This defaults to false.
+ # attributes are +nil+. Setting the aggregate class to +nil+ has the effect of writing +nil+ to all mapped attributes.
+ # This defaults to +false+.
+ #
+ # An optional block can be passed to convert the argument that is passed to the writer method into an instance of
+ # :class_name . The block will only be called if the argument is not already an instance of :class_name .
#
# Option examples:
# composed_of :temperature, :mapping => %w(reading celsius)
- # composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
+ # composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) {|balance| balance.to_money }
# composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
# composed_of :gps_location
# composed_of :gps_location, :allow_nil => true
#
- def composed_of(part_id, options = {})
+ def composed_of(part_id, options = {}, &block)
options.assert_valid_keys(:class_name, :mapping, :allow_nil)
name = part_id.id2name
class_name = options[:class_name] || name.camelize
mapping = options[:mapping] || [ name, name ]
+ mapping = [ mapping ] unless mapping.first.is_a?(Array)
allow_nil = options[:allow_nil] || false
reader_method(name, class_name, mapping, allow_nil)
- writer_method(name, class_name, mapping, allow_nil)
+ writer_method(name, class_name, mapping, allow_nil, block)
create_reflection(:composed_of, part_id, options, self)
end
private
def reader_method(name, class_name, mapping, allow_nil)
- mapping = (Array === mapping.first ? mapping : [ mapping ])
-
- allow_nil_condition = if allow_nil
- mapping.collect { |pair| "!read_attribute(\"#{pair.first}\").nil?"}.join(" && ")
- else
- "true"
+ module_eval do
+ define_method(name) do |*args|
+ force_reload = args.first || false
+ if (instance_variable_get("@#{name}").nil? || force_reload) && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
+ instance_variable_set("@#{name}", class_name.constantize.new(*mapping.collect {|pair| read_attribute(pair.first)}))
+ end
+ return instance_variable_get("@#{name}")
+ end
end
- module_eval <<-end_eval
- def #{name}(force_reload = false)
- if (@#{name}.nil? || force_reload) && #{allow_nil_condition}
- @#{name} = #{class_name}.new(#{mapping.collect { |pair| "read_attribute(\"#{pair.first}\")"}.join(", ")})
- end
- return @#{name}
- end
- end_eval
- end
-
- def writer_method(name, class_name, mapping, allow_nil)
- mapping = (Array === mapping.first ? mapping : [ mapping ])
+ end
- if allow_nil
- module_eval <<-end_eval
- def #{name}=(part)
- if part.nil?
- #{mapping.collect { |pair| "@attributes[\"#{pair.first}\"] = nil" }.join("\n")}
- else
- @#{name} = part.freeze
- #{mapping.collect { |pair| "@attributes[\"#{pair.first}\"] = part.#{pair.last}" }.join("\n")}
- end
+ def writer_method(name, class_name, mapping, allow_nil, conversion)
+ module_eval do
+ define_method("#{name}=") do |part|
+ if part.nil? && allow_nil
+ mapping.each { |pair| @attributes[pair.first] = nil }
+ instance_variable_set("@#{name}", nil)
+ else
+ part = conversion.call(part) unless part.is_a?(class_name.constantize) || conversion.nil?
+ mapping.each { |pair| @attributes[pair.first] = part.send(pair.last) }
+ instance_variable_set("@#{name}", part.freeze)
end
- end_eval
- else
- module_eval <<-end_eval
- def #{name}=(part)
- @#{name} = part.freeze
- #{mapping.collect{ |pair| "@attributes[\"#{pair.first}\"] = part.#{pair.last}" }.join("\n")}
- end
- end_eval
+ end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/associations.rb b/vendor/rails/activerecord/lib/active_record/associations.rb
index 8cdf748a..434612fd 100755
--- a/vendor/rails/activerecord/lib/active_record/associations.rb
+++ b/vendor/rails/activerecord/lib/active_record/associations.rb
@@ -6,7 +6,6 @@ require 'active_record/associations/has_one_association'
require 'active_record/associations/has_many_association'
require 'active_record/associations/has_many_through_association'
require 'active_record/associations/has_and_belongs_to_many_association'
-require 'active_record/deprecated_associations'
module ActiveRecord
class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
@@ -50,6 +49,12 @@ module ActiveRecord
end
end
+ class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
+ def initialize(owner, reflection)
+ super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
+ end
+ end
+
class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
def initialize(reflection)
super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
@@ -76,7 +81,7 @@ module ActiveRecord
# Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like
# "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are
- # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own attr*
+ # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own attr*
# methods. Example:
#
# class Project < ActiveRecord::Base
@@ -95,14 +100,121 @@ module ActiveRecord
# * Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),
# Project#categories.delete(category1)
#
- # == Example
+ # === A word of warning
#
- # link:files/examples/associations.png
+ # Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association
+ # adds a method with that name to its model, it will override the inherited method and break things.
+ # For instance, #attributes and #connection would be bad choices for association names.
#
- # == Is it belongs_to or has_one?
+ # == Auto-generated methods
#
- # Both express a 1-1 relationship, the difference is mostly where to place the foreign key, which goes on the table for the class
- # saying belongs_to. Example:
+ # ===Singular associations (one-to-one)
+ # | | belongs_to |
+ # generated methods | belongs_to | :polymorphic | has_one
+ # ----------------------------------+------------+--------------+---------
+ # #other | X | X | X
+ # #other=(other) | X | X | X
+ # #build_other(attributes={}) | X | | X
+ # #create_other(attributes={}) | X | | X
+ # #other.create!(attributes={}) | | | X
+ # #other.nil? | X | X |
+ #
+ # ===Collection associations (one-to-many / many-to-many)
+ # | | | has_many
+ # generated methods | habtm | has_many | :through
+ # ----------------------------------+-------+----------+----------
+ # #others | X | X | X
+ # #others=(other,other,...) | X | X |
+ # #other_ids | X | X | X
+ # #other_ids=(id,id,...) | X | X |
+ # #others<< | X | X | X
+ # #others.push | X | X | X
+ # #others.concat | X | X | X
+ # #others.build(attributes={}) | X | X | X
+ # #others.create(attributes={}) | X | X |
+ # #others.create!(attributes={}) | X | X | X
+ # #others.size | X | X | X
+ # #others.length | X | X | X
+ # #others.count | | X | X
+ # #others.sum(args*,&block) | X | X | X
+ # #others.empty? | X | X | X
+ # #others.clear | X | X |
+ # #others.delete(other,other,...) | X | X | X
+ # #others.delete_all | X | X |
+ # #others.destroy_all | X | X | X
+ # #others.find(*args) | X | X | X
+ # #others.find_first | X | |
+ # #others.uniq | X | X |
+ # #others.reset | X | X | X
+ #
+ # == Cardinality and associations
+ #
+ # ActiveRecord associations can be used to describe relations with one-to-one, one-to-many
+ # and many-to-many cardinality. Each model uses an association to describe its role in
+ # the relation. In each case, the +belongs_to+ association is used in the model that has
+ # the foreign key.
+ #
+ # === One-to-one
+ #
+ # Use +has_one+ in the base, and +belongs_to+ in the associated model.
+ #
+ # class Employee < ActiveRecord::Base
+ # has_one :office
+ # end
+ # class Office < ActiveRecord::Base
+ # belongs_to :employee # foreign key - employee_id
+ # end
+ #
+ # === One-to-many
+ #
+ # Use +has_many+ in the base, and +belongs_to+ in the associated model.
+ #
+ # class Manager < ActiveRecord::Base
+ # has_many :employees
+ # end
+ # class Employee < ActiveRecord::Base
+ # belongs_to :manager # foreign key - manager_id
+ # end
+ #
+ # === Many-to-many
+ #
+ # There are two ways to build a many-to-many relationship.
+ #
+ # The first way uses a +has_many+ association with the :through option and a join model, so
+ # there are two stages of associations.
+ #
+ # class Assignment < ActiveRecord::Base
+ # belongs_to :programmer # foreign key - programmer_id
+ # belongs_to :project # foreign key - project_id
+ # end
+ # class Programmer < ActiveRecord::Base
+ # has_many :assignments
+ # has_many :projects, :through => :assignments
+ # end
+ # class Project < ActiveRecord::Base
+ # has_many :assignments
+ # has_many :programmers, :through => :assignments
+ # end
+ #
+ # For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table
+ # that has no corresponding model or primary key.
+ #
+ # class Programmer < ActiveRecord::Base
+ # has_and_belongs_to_many :projects # foreign keys in the join table
+ # end
+ # class Project < ActiveRecord::Base
+ # has_and_belongs_to_many :programmers # foreign keys in the join table
+ # end
+ #
+ # Choosing which way to build a many-to-many relationship is not always simple.
+ # If you need to work with the relationship model as its own entity,
+ # use has_many :through . Use +has_and_belongs_to_many+ when working with legacy schemas or when
+ # you never work directly with the relationship itself.
+ #
+ # == Is it a +belongs_to+ or +has_one+ association?
+ #
+ # Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class
+ # declaring the +belongs_to+ relationship. Example:
#
# class User < ActiveRecord::Base
# # I reference an account.
@@ -131,31 +243,31 @@ module ActiveRecord
#
# == Unsaved objects and associations
#
- # You can manipulate objects and associations before they are saved to the database, but there is some special behaviour you should be
+ # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
# aware of, mostly involving the saving of associated objects.
#
# === One-to-one associations
#
- # * Assigning an object to a has_one association automatically saves that object and the object being replaced (if there is one), in
- # order to update their primary keys - except if the parent object is unsaved (new_record? == true).
- # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns false and the assignment
+ # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in
+ # order to update their primary keys - except if the parent object is unsaved (new_record? == true ).
+ # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment
# is cancelled.
- # * If you wish to assign an object to a has_one association without saving it, use the #association.build method (documented below).
- # * Assigning an object to a belongs_to association does not save the object, since the foreign key field belongs on the parent. It does
- # not save the parent either.
+ # * If you wish to assign an object to a +has_one+ association without saving it, use the #association.build method (documented below).
+ # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It
+ # does not save the parent either.
#
# === Collections
#
- # * Adding an object to a collection (has_many or has_and_belongs_to_many) automatically saves that object, except if the parent object
+ # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object
# (the owner of the collection) is not yet stored in the database.
- # * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns false.
- # * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
- # * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
+ # * If saving any of the objects being added to a collection (via #push or similar) fails, then #push returns +false+.
+ # * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
+ # * All unsaved (new_record? == true ) members of the collection are automatically saved when the parent is saved.
#
# === Association callbacks
#
- # Similiar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get
- # trigged when you add an object to or removing an object from a association collection. Example:
+ # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get
+ # triggered when you add an object to or remove an object from an association collection. Example:
#
# class Project
# has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
@@ -171,14 +283,14 @@ module ActiveRecord
# has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
# end
#
- # Possible callbacks are: before_add, after_add, before_remove and after_remove.
+ # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
#
- # Should any of the before_add callbacks throw an exception, the object does not get added to the collection. Same with
- # the before_remove callbacks, if an exception is thrown the object doesn't get removed.
+ # Should any of the +before_add+ callbacks throw an exception, the object does not get added to the collection. Same with
+ # the +before_remove+ callbacks; if an exception is thrown the object doesn't get removed.
#
# === Association extensions
#
- # The proxy objects that controls the access to associations can be extended through anonymous modules. This is especially
+ # The proxy objects that control the access to associations can be extended through anonymous modules. This is especially
# beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association.
# Example:
#
@@ -212,7 +324,7 @@ module ActiveRecord
# has_many :people, :extend => FindOrCreateByNameExtension
# end
#
- # If you need to use multiple named extension modules, you can specify an array of modules with the :extend option.
+ # If you need to use multiple named extension modules, you can specify an array of modules with the :extend option.
# In the case of name conflicts between methods in the modules, methods in modules later in the array supercede
# those earlier in the array. Example:
#
@@ -225,12 +337,12 @@ module ActiveRecord
#
# * +proxy_owner+ - Returns the object the association is part of.
# * +proxy_reflection+ - Returns the reflection object that describes the association.
- # * +proxy_target+ - Returns the associated object for belongs_to and has_one, or the collection of associated objects for has_many and has_and_belongs_to_many.
+ # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
#
# === Association Join Models
#
- # Has Many associations can be configured with the :through option to use an explicit join model to retrieve the data. This
- # operates similarly to a has_and_belongs_to_many association. The advantage is that you're able to add validations,
+ # Has Many associations can be configured with the :through option to use an explicit join model to retrieve the data. This
+ # operates similarly to a +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
# callbacks, and extra attributes on the join model. Consider the following schema:
#
# class Author < ActiveRecord::Base
@@ -247,7 +359,7 @@ module ActiveRecord
# @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to.
# @author.books # selects all books by using the Authorship join model
#
- # You can also go through a has_many association on the join model:
+ # You can also go through a +has_many+ association on the join model:
#
# class Firm < ActiveRecord::Base
# has_many :clients
@@ -270,25 +382,25 @@ module ActiveRecord
# === Polymorphic Associations
#
# Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they
- # specify an interface that a has_many association must adhere to.
+ # specify an interface that a +has_many+ association must adhere to.
#
# class Asset < ActiveRecord::Base
# belongs_to :attachable, :polymorphic => true
# end
#
# class Post < ActiveRecord::Base
- # has_many :assets, :as => :attachable # The :as option specifies the polymorphic interface to use.
+ # has_many :assets, :as => :attachable # The :as option specifies the polymorphic interface to use.
# end
#
# @asset.attachable = @post
#
# This works by using a type column in addition to a foreign key to specify the associated record. In the Asset example, you'd need
- # an attachable_id integer column and an attachable_type string column.
+ # an +attachable_id+ integer column and an +attachable_type+ string column.
#
# Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order
# for the associations to work as expected, ensure that you store the base model for the STI models in the
# type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts
- # and member posts that use the posts table for STI. So there will be an additional 'type' column in the posts table.
+ # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table.
#
# class Asset < ActiveRecord::Base
# belongs_to :attachable, :polymorphic => true
@@ -303,10 +415,10 @@ module ActiveRecord
# has_many :assets, :as => :attachable, :dependent => :destroy
# end
#
- # class GuestPost < ActiveRecord::Base
+ # class GuestPost < Post
# end
#
- # class MemberPost < ActiveRecord::Base
+ # class MemberPost < Post
# end
#
# == Caching
@@ -324,7 +436,7 @@ module ActiveRecord
# == Eager loading of associations
#
# Eager loading is a way to find objects of a certain class and a number of named associations along with it in a single SQL call. This is
- # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each needs to display their author
+ # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author
# triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 1. Example:
#
# class Post < ActiveRecord::Base
@@ -344,16 +456,16 @@ module ActiveRecord
#
# for post in Post.find(:all, :include => :author)
#
- # This references the name of the belongs_to association that also used the :author symbol, so the find will now weave in a join something
- # like this: LEFT OUTER JOIN authors ON authors.id = posts.author_id. Doing so will cut down the number of queries from 201 to 101.
+ # This references the name of the +belongs_to+ association that also used the :author symbol, so the find will now weave in a join something
+ # like this: LEFT OUTER JOIN authors ON authors.id = posts.author_id . Doing so will cut down the number of queries from 201 to 101.
#
# We can improve upon the situation further by referencing both associations in the finder with:
#
# for post in Post.find(:all, :include => [ :author, :comments ])
#
- # That'll add another join along the lines of: LEFT OUTER JOIN comments ON comments.post_id = posts.id. And we'll be down to 1 query.
+ # That'll add another join along the lines of: LEFT OUTER JOIN comments ON comments.post_id = posts.id . And we'll be down to 1 query.
#
- # To include a deep hierarchy of associations, using a hash:
+ # To include a deep hierarchy of associations, use a hash:
#
# for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
#
@@ -365,12 +477,12 @@ module ActiveRecord
# catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above.
#
# Since the eager loading pulls from multiple tables, you'll have to disambiguate any column references in both conditions and orders. So
- # :order => "posts.id DESC" will work while :order => "id DESC" will not. Because eager loading generates the SELECT statement too, the
- # :select option is ignored.
+ # :order => "posts.id DESC" will work while :order => "id DESC" will not. Because eager loading generates the +SELECT+ statement too, the
+ # :select option is ignored.
#
# You can use eager loading on multiple associations from the same table, but you cannot use those associations in orders and conditions
# as there is currently not any way to disambiguate them. Eager loading will not pull additional attributes on join tables, so "rich
- # associations" with has_and_belongs_to_many are not a good fit for eager loading.
+ # associations" with +has_and_belongs_to_many+ are not a good fit for eager loading.
#
# When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
# before the actual model exists.
@@ -378,7 +490,7 @@ module ActiveRecord
# == Table Aliasing
#
# ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
- # the standard table name is used. The second time, the table is aliased as #{reflection_name}_#{parent_table_name}. Indexes are appended
+ # the standard table name is used. The second time, the table is aliased as #{reflection_name}_#{parent_table_name} . Indexes are appended
# for any more successive uses of the table name.
#
# Post.find :all, :include => :comments
@@ -398,9 +510,9 @@ module ActiveRecord
# TreeMixin.find :all, :include => {:children => {:parent => :children}}
# # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
# LEFT OUTER JOIN parents_mixins ...
- # LEFT OUTER JOIN mixins childrens_mixins_2
+ # LEFT OUTER JOIN mixins childrens_mixins_2
#
- # Has and Belongs to Many join tables use the same idea, but add a _join suffix:
+ # Has and Belongs to Many join tables use the same idea, but add a _join suffix:
#
# Post.find :all, :include => :categories
# # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
@@ -412,7 +524,7 @@ module ActiveRecord
# LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories
# LEFT OUTER JOIN categories_posts categories_posts_join LEFT OUTER JOIN categories categories_posts
#
- # If you wish to specify your own custom joins using a :joins option, those table names will take precedence over the eager associations..
+ # If you wish to specify your own custom joins using a :joins option, those table names will take precedence over the eager associations:
#
# Post.find :all, :include => :comments, :joins => "inner join comments ..."
# # => SELECT ... FROM posts LEFT OUTER JOIN comments_posts ON ... INNER JOIN comments ...
@@ -437,8 +549,8 @@ module ActiveRecord
# end
# end
#
- # When Firm#clients is called, it'll in turn call MyApplication::Business::Company.find(firm.id) . If you want to associate
- # with a class in another module scope this can be done by specifying the complete class name, such as:
+ # When Firm#clients is called, it will in turn call MyApplication::Business::Company.find(firm.id) . If you want to associate
+ # with a class in another module scope, this can be done by specifying the complete class name. Example:
#
# module MyApplication
# module Business
@@ -452,41 +564,41 @@ module ActiveRecord
# end
# end
#
- # == Type safety with ActiveRecord::AssociationTypeMismatch
+ # == Type safety with ActiveRecord::AssociationTypeMismatch
#
# If you attempt to assign an object to an association that doesn't match the inferred or specified :class_name , you'll
- # get a ActiveRecord::AssociationTypeMismatch.
+ # get an ActiveRecord::AssociationTypeMismatch .
#
# == Options
#
- # All of the association macros can be specialized through options which makes more complex cases than the simple and guessable ones
+ # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones
# possible.
module ClassMethods
- # Adds the following methods for retrieval and query of collections of associated objects.
+ # Adds the following methods for retrieval and query of collections of associated objects:
# +collection+ is replaced with the symbol passed as the first argument, so
# has_many :clients would add among others clients.empty? .
# * collection(force_reload = false) - returns an array of all the associated objects.
# An empty array is returned if none are found.
# * collection<<(object, ...) - adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
# * collection.delete(object, ...) - removes one or more objects from the collection by setting their foreign keys to NULL.
- # This will also destroy the objects if they're declared as belongs_to and dependent on this model.
+ # This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
# * collection=objects - replaces the collections content by deleting and adding objects as appropriate.
- # * collection_singular_ids - returns an array of the associated objects ids
- # * collection_singular_ids=ids - replace the collection by the objects identified by the primary keys in +ids+
+ # * collection_singular_ids - returns an array of the associated objects' ids
+ # * collection_singular_ids=ids - replace the collection with the objects identified by the primary keys in +ids+
# * collection.clear - removes every object from the collection. This destroys the associated objects if they
- # are :dependent , deletes them directly from the database if they are :dependent => :delete_all ,
- # and sets their foreign keys to NULL otherwise.
- # * collection.empty? - returns true if there are no associated objects.
+ # are associated with :dependent => :destroy , deletes them directly from the database if :dependent => :delete_all ,
+ # otherwise sets their foreign keys to NULL.
+ # * collection.empty? - returns +true+ if there are no associated objects.
# * collection.size - returns the number of associated objects.
# * collection.find - finds an associated object according to the same rules as Base.find.
- # * collection.build(attributes = {}) - returns a new object of the collection type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key but has not yet been saved. *Note:* This only works if an
- # associated object already exists, not if it's nil!
+ # * collection.build(attributes = {}, ...) - returns one or more new objects of the collection type that have been instantiated
+ # with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an
+ # associated object already exists, not if it's +nil+!
# * collection.create(attributes = {}) - returns a new object of the collection type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key and that has already been saved (if it passed the validation).
- # *Note:* This only works if an associated object already exists, not if it's nil!
+ # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
+ # *Note:* This only works if an associated object already exists, not if it's +nil+!
#
- # Example: A Firm class declares has_many :clients , which will add:
+ # Example: A +Firm+ class declares has_many :clients , which will add:
# * Firm#clients (similar to Clients.find :all, :conditions => "firm_id = #{id}" )
# * Firm#clients<<
# * Firm#clients.delete
@@ -505,47 +617,38 @@ module ActiveRecord
# * :class_name - specify the class name of the association. Use it only if that name can't be inferred
# from the association name. So has_many :products will by default be linked to the +Product+ class, but
# if the real class name is +SpecialProduct+, you'll have to specify it with this option.
- # * :conditions - specify the conditions that the associated objects must meet in order to be included as a "WHERE"
- # sql fragment, such as "price > 5 AND name LIKE 'B%'".
- # * :order - specify the order in which the associated objects are returned as a "ORDER BY" sql fragment,
- # such as "last_name, first_name DESC"
- # * :group - specify the attribute by which the associated objects are returned as a "GROUP BY" sql fragment,
- # such as "category"
+ # * :conditions - specify the conditions that the associated objects must meet in order to be included as a +WHERE+
+ # SQL fragment, such as price > 5 AND name LIKE 'B%' .
+ # * :order - specify the order in which the associated objects are returned as an ORDER BY SQL fragment,
+ # such as last_name, first_name DESC
# * :foreign_key - specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and "_id" suffixed. So a +Person+ class that makes a has_many association will use "person_id"
- # as the default foreign_key.
- # * :dependent - if set to :destroy all the associated objects are destroyed
- # alongside this object by calling their destroy method. If set to :delete_all all associated
- # objects are deleted *without* calling their destroy method. If set to :nullify all associated
- # objects' foreign keys are set to NULL *without* calling their save callbacks.
- # NOTE: :dependent => true is deprecated and has been replaced with :dependent => :destroy.
- # May not be set if :exclusively_dependent is also set.
- # * :exclusively_dependent - Deprecated; equivalent to :dependent => :delete_all. If set to true all
- # the associated object are deleted in one SQL statement without having their
- # before_destroy callback run. This should only be used on associations that depend solely on this class and don't need to do any
- # clean-up in before_destroy. The upside is that it's much faster, especially if there's a counter_cache involved.
- # May not be set if :dependent is also set.
+ # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_many+ association will use +person_id+
+ # as the default +foreign_key+.
+ # * :dependent - if set to :destroy all the associated objects are destroyed
+ # alongside this object by calling their destroy method. If set to :delete_all all associated
+ # objects are deleted *without* calling their destroy method. If set to :nullify all associated
+ # objects' foreign keys are set to +NULL+ *without* calling their save callbacks.
# * :finder_sql - specify a complete SQL statement to fetch the association. This is a good way to go for complex
# associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
- # * :counter_sql - specify a complete SQL statement to fetch the size of the association. If +:finder_sql+ is
- # specified but +:counter_sql+, +:counter_sql+ will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM.
- # * :extend - specify a named module for extending the proxy, see "Association extensions".
+ # * :counter_sql - specify a complete SQL statement to fetch the size of the association. If :finder_sql is
+ # specified but not :counter_sql , :counter_sql will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM .
+ # * :extend - specify a named module for extending the proxy. See "Association extensions".
# * :include - specify second-order associations that should be eager loaded when the collection is loaded.
- # * :group : An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
+ # * :group : An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
# * :limit : An integer determining the limit on the number of rows that should be returned.
# * :offset : An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
- # * :select : By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
- # include the joined columns.
- # * :as : Specifies a polymorphic interface (See #belongs_to).
- # * :through : Specifies a Join Model to perform the query through. Options for :class_name and :foreign_key
- # are ignored, as the association uses the source reflection. You can only use a :through query through a belongs_to
- # or has_many association.
+ # * :select : By default, this is * as in SELECT * FROM , but can be changed if you, for example, want to do a join
+ # but not include the joined columns.
+ # * :as : Specifies a polymorphic interface (See #belongs_to ).
+ # * :through : Specifies a Join Model through which to perform the query. Options for :class_name and :foreign_key
+ # are ignored, as the association uses the source reflection. You can only use a :through query through a belongs_to
+ # or has_many association on the join model.
# * :source : Specifies the source association name used by has_many :through queries. Only use it if the name cannot be
- # inferred from the association. has_many :subscribers, :through => :subscriptions will look for either +:subscribers+ or
- # +:subscriber+ on +Subscription+, unless a +:source+ is given.
- # * :source_type : Specifies type of the source association used by has_many :through queries where the source association
- # is a polymorphic belongs_to.
- # * :uniq - if set to true, duplicates will be omitted from the collection. Useful in conjunction with :through.
+ # inferred from the association. has_many :subscribers, :through => :subscriptions will look for either :subscribers or
+ # :subscriber on +Subscription+, unless a :source is given.
+ # * :source_type : Specifies type of the source association used by has_many :through queries where the source
+ # association is a polymorphic +belongs_to+.
+ # * :uniq - if set to +true+, duplicates will be omitted from the collection. Useful in conjunction with :through .
#
# Option examples:
# has_many :comments, :order => "posted_on"
@@ -567,27 +670,26 @@ module ActiveRecord
if options[:through]
collection_reader_method(reflection, HasManyThroughAssociation)
+ collection_accessor_methods(reflection, HasManyThroughAssociation, false)
else
add_multiple_associated_save_callbacks(reflection.name)
add_association_callbacks(reflection.name, reflection.options)
collection_accessor_methods(reflection, HasManyAssociation)
end
-
- add_deprecated_api_for_has_many(reflection.name)
end
- # Adds the following methods for retrieval and query of a single associated object.
+ # Adds the following methods for retrieval and query of a single associated object:
# +association+ is replaced with the symbol passed as the first argument, so
# has_one :manager would add among others manager.nil? .
- # * association(force_reload = false) - returns the associated object. Nil is returned if none is found.
+ # * association(force_reload = false) - returns the associated object. +nil+ is returned if none is found.
# * association=(associate) - assigns the associate object, extracts the primary key, sets it as the foreign key,
# and saves the associate object.
- # * association.nil? - returns true if there is no associated object.
+ # * association.nil? - returns +true+ if there is no associated object.
# * build_association(attributes = {}) - returns a new object of the associated type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key but has not yet been saved. Note: This ONLY works if
- # an association already exists. It will NOT work if the association is nil.
+ # with +attributes+ and linked to this object through a foreign key, but has not yet been saved. Note: This ONLY works if
+ # an association already exists. It will NOT work if the association is +nil+.
# * create_association(attributes = {}) - returns a new object of the associated type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key and that has already been saved (if it passed the validation).
+ # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
#
# Example: An Account class declares has_one :beneficiary , which will add:
# * Account#beneficiary (similar to Beneficiary.find(:first, :conditions => "account_id = #{id}") )
@@ -602,22 +704,22 @@ module ActiveRecord
# * :class_name - specify the class name of the association. Use it only if that name can't be inferred
# from the association name. So has_one :manager will by default be linked to the +Manager+ class, but
# if the real class name is +Person+, you'll have to specify it with this option.
- # * :conditions - specify the conditions that the associated object must meet in order to be included as a "WHERE"
- # sql fragment, such as "rank = 5".
- # * :order - specify the order from which the associated object will be picked at the top. Specified as
- # an "ORDER BY" sql fragment, such as "last_name, first_name DESC"
- # * :dependent - if set to :destroy (or true) the associated object is destroyed when this object is. If set to
- # :delete the associated object is deleted *without* calling its destroy method. If set to :nullify the associated
- # object's foreign key is set to NULL. Also, association is assigned.
+ # * :conditions - specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # SQL fragment, such as rank = 5 .
+ # * :order - specify the order in which the associated objects are returned as an ORDER BY SQL fragment,
+ # such as last_name, first_name DESC
+ # * :dependent - if set to :destroy , the associated object is destroyed when this object is. If set to
+ # :delete , the associated object is deleted *without* calling its destroy method. If set to :nullify , the associated
+ # object's foreign key is set to +NULL+. Also, association is assigned.
# * :foreign_key - specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and "_id" suffixed. So a +Person+ class that makes a has_one association will use "person_id"
- # as the default foreign_key.
+ # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_one+ association will use +person_id+
+ # as the default +foreign_key+.
# * :include - specify second-order associations that should be eager loaded when this object is loaded.
- # * :as : Specifies a polymorphic interface (See #belongs_to).
+ # * :as : Specifies a polymorphic interface (See #belongs_to ).
#
# Option examples:
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
- # has_one :credit_card, :dependent => :nullify # updates the associated records foriegn key value to null rather than destroying it
+ # has_one :credit_card, :dependent => :nullify # updates the associated records foreign key value to NULL rather than destroying it
# has_one :last_comment, :class_name => "Comment", :order => "posted_on"
# has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'"
# has_one :attachment, :as => :attachable
@@ -639,22 +741,18 @@ module ActiveRecord
association_constructor_method(:create, reflection, HasOneAssociation)
configure_dependency_for_has_one(reflection)
-
- # deprecated api
- deprecated_has_association_method(reflection.name)
- deprecated_association_comparison_method(reflection.name, reflection.class_name)
end
- # Adds the following methods for retrieval and query for a single associated object that this object holds an id to.
+ # Adds the following methods for retrieval and query for a single associated object for which this object holds an id:
# +association+ is replaced with the symbol passed as the first argument, so
# belongs_to :author would add among others author.nil? .
- # * association(force_reload = false) - returns the associated object. Nil is returned if none is found.
+ # * association(force_reload = false) - returns the associated object. +nil+ is returned if none is found.
# * association=(associate) - assigns the associate object, extracts the primary key, and sets it as the foreign key.
- # * association.nil? - returns true if there is no associated object.
+ # * association.nil? - returns +true+ if there is no associated object.
# * build_association(attributes = {}) - returns a new object of the associated type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key but has not yet been saved.
+ # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
# * create_association(attributes = {}) - returns a new object of the associated type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key and that has already been saved (if it passed the validation).
+ # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
#
# Example: A Post class declares belongs_to :author , which will add:
# * Post#author (similar to Author.find(author_id) )
@@ -669,20 +767,23 @@ module ActiveRecord
# * :class_name - specify the class name of the association. Use it only if that name can't be inferred
# from the association name. So has_one :author will by default be linked to the +Author+ class, but
# if the real class name is +Person+, you'll have to specify it with this option.
- # * :conditions - specify the conditions that the associated object must meet in order to be included as a "WHERE"
- # sql fragment, such as "authorized = 1".
- # * :order - specify the order from which the associated object will be picked at the top. Specified as
- # an "ORDER BY" sql fragment, such as "last_name, first_name DESC"
+ # * :conditions - specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # SQL fragment, such as authorized = 1 .
+ # * :order - specify the order in which the associated objects are returned as an ORDER BY SQL fragment,
+ # such as last_name, first_name DESC
# * :foreign_key - specify the foreign key used for the association. By default this is guessed to be the name
- # of the associated class in lower-case and "_id" suffixed. So a +Person+ class that makes a belongs_to association to a
- # +Boss+ class will use "boss_id" as the default foreign_key.
- # * :counter_cache - caches the number of belonging objects on the associate class through use of increment_counter
- # and decrement_counter. The counter cache is incremented when an object of this class is created and decremented when it's
- # destroyed. This requires that a column named "#{table_name}_count" (such as comments_count for a belonging Comment class)
- # is used on the associate class (such as a Post class). You can also specify a custom counter cache column by given that
- # name instead of a true/false value to this option (e.g., :counter_cache => :my_custom_counter .)
+ # of the associated class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +belongs_to+ association to a
+ # +Boss+ class will use +boss_id+ as the default +foreign_key+.
+ # * :counter_cache - caches the number of belonging objects on the associate class through the use of +increment_counter+
+ # and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's
+ # destroyed. This requires that a column named #{table_name}_count (such as +comments_count+ for a belonging +Comment+ class)
+ # is used on the associate class (such as a +Post+ class). You can also specify a custom counter cache column by providing
+ # a column name instead of a +true+/+false+ value to this option (e.g., :counter_cache => :my_custom_counter .)
+ # Note: Specifying a counter_cache will add it to that model's list of readonly attributes using #attr_readonly.
# * :include - specify second-order associations that should be eager loaded when this object is loaded.
- # * :polymorphic - specify this association is a polymorphic association by passing true.
+ # * :polymorphic - specify this association is a polymorphic association by passing +true+.
+ # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
+ # to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end).
#
# Option examples:
# belongs_to :firm, :foreign_key => "client_of"
@@ -691,12 +792,6 @@ module ActiveRecord
# :conditions => 'discounts > #{payments_count}'
# belongs_to :attachable, :polymorphic => true
def belongs_to(association_id, options = {})
- if options.include?(:class_name) && !options.include?(:foreign_key)
- ::ActiveSupport::Deprecation.warn(
- "The inferred foreign_key name will change in Rails 2.0 to use the association name instead of its class name when they differ. When using :class_name in belongs_to, use the :foreign_key option to explicitly set the key name to avoid problems in the transition.",
- caller)
- end
-
reflection = create_belongs_to_reflection(association_id, options)
if reflection.options[:polymorphic]
@@ -736,10 +831,6 @@ module ActiveRecord
end
EOF
end
-
- # deprecated api
- deprecated_has_association_method(reflection.name)
- deprecated_association_comparison_method(reflection.name, reflection.class_name)
end
# Create the callbacks to update counter cache
@@ -756,13 +847,17 @@ module ActiveRecord
module_eval(
"before_destroy '#{reflection.name}.class.decrement_counter(\"#{cache_column}\", #{reflection.primary_key_name})" +
" unless #{reflection.name}.nil?'"
- )
+ )
+
+ module_eval(
+ "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
+ )
end
end
# Associates two classes via an intermediate join table. Unless the join table is explicitly specified as
- # an option, it is guessed using the lexical order of the class names. So a join between Developer and Project
- # will give the default join table name of "developers_projects" because "D" outranks "P". Note that this precedence
+ # an option, it is guessed using the lexical order of the class names. So a join between +Developer+ and +Project+
+ # will give the default join table name of +developers_projects+ because "D" outranks "P". Note that this precedence
# is calculated using the < operator for String . This means that if the strings are of different lengths,
# and the strings are equal when compared up to the shortest length, then the longer string is considered of higher
# lexical precedence than the shorter one. For example, one would expect the tables paper_boxes and papers
@@ -771,37 +866,33 @@ module ActiveRecord
# custom join_table option if you need to.
#
# Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
- # has_and_belongs_to_many associations. Records returned from join tables with additional attributes will be marked as
- # ReadOnly (because we can't save changes to the additional attrbutes). It's strongly recommended that you upgrade any
+ # +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as
+ # +ReadOnly+ (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any
# associations with attributes to a real join model (see introduction).
#
- # Adds the following methods for retrieval and query.
+ # Adds the following methods for retrieval and query:
# +collection+ is replaced with the symbol passed as the first argument, so
# has_and_belongs_to_many :categories would add among others categories.empty? .
# * collection(force_reload = false) - returns an array of all the associated objects.
- # An empty array is returned if none is found.
+ # An empty array is returned if none are found.
# * collection<<(object, ...) - adds one or more objects to the collection by creating associations in the join table
- # (collection.push and collection.concat are aliases to this method).
- # * collection.push_with_attributes(object, join_attributes) - adds one to the collection by creating an association in the join table that
- # also holds the attributes from join_attributes (should be a hash with the column names as keys). This can be used to have additional
- # attributes on the join, which will be injected into the associated objects when they are retrieved through the collection.
- # (collection.concat_with_attributes is an alias to this method). This method is now deprecated.
+ # (collection.push and collection.concat are aliases to this method).
# * collection.delete(object, ...) - removes one or more objects from the collection by removing their associations from the join table.
# This does not destroy the objects.
- # * collection=objects - replaces the collections content by deleting and adding objects as appropriate.
- # * collection_singular_ids - returns an array of the associated objects ids
+ # * collection=objects - replaces the collection's content by deleting and adding objects as appropriate.
+ # * collection_singular_ids - returns an array of the associated objects' ids
# * collection_singular_ids=ids - replace the collection by the objects identified by the primary keys in +ids+
# * collection.clear - removes every object from the collection. This does not destroy the objects.
- # * collection.empty? - returns true if there are no associated objects.
+ # * collection.empty? - returns +true+ if there are no associated objects.
# * collection.size - returns the number of associated objects.
# * collection.find(id) - finds an associated object responding to the +id+ and that
# meets the condition that it has to be associated with this object.
# * collection.build(attributes = {}) - returns a new object of the collection type that has been instantiated
- # with +attributes+ and linked to this object through the join table but has not yet been saved.
+ # with +attributes+ and linked to this object through the join table, but has not yet been saved.
# * collection.create(attributes = {}) - returns a new object of the collection type that has been instantiated
- # with +attributes+ and linked to this object through the join table and that has already been saved (if it passed the validation).
+ # with +attributes+, linked to this object through the join table, and that has already been saved (if it passed the validation).
#
- # Example: An Developer class declares has_and_belongs_to_many :projects , which will add:
+ # Example: A Developer class declares has_and_belongs_to_many :projects , which will add:
# * Developer#projects
# * Developer#projects<<
# * Developer#projects.delete
@@ -821,30 +912,31 @@ module ActiveRecord
# from the association name. So has_and_belongs_to_many :projects will by default be linked to the
# +Project+ class, but if the real class name is +SuperProject+, you'll have to specify it with this option.
# * :join_table - specify the name of the join table if the default based on lexical order isn't what you want.
- # WARNING: If you're overwriting the table name of either class, the table_name method MUST be declared underneath any
- # has_and_belongs_to_many declaration in order to work.
+ # WARNING: If you're overwriting the table name of either class, the +table_name+ method MUST be declared underneath any
+ # +has_and_belongs_to_many+ declaration in order to work.
# * :foreign_key - specify the foreign key used for the association. By default this is guessed to be the name
- # of this class in lower-case and "_id" suffixed. So a +Person+ class that makes a has_and_belongs_to_many association
- # will use "person_id" as the default foreign_key.
+ # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_and_belongs_to_many+ association
+ # will use +person_id+ as the default +foreign_key+.
# * :association_foreign_key - specify the association foreign key used for the association. By default this is
- # guessed to be the name of the associated class in lower-case and "_id" suffixed. So if the associated class is +Project+,
- # the has_and_belongs_to_many association will use "project_id" as the default association foreign_key.
- # * :conditions - specify the conditions that the associated object must meet in order to be included as a "WHERE"
- # sql fragment, such as "authorized = 1".
- # * :order - specify the order in which the associated objects are returned as a "ORDER BY" sql fragment, such as "last_name, first_name DESC"
- # * :uniq - if set to true, duplicate associated objects will be ignored by accessors and query methods
- # * :finder_sql - overwrite the default generated SQL used to fetch the association with a manual one
- # * :delete_sql - overwrite the default generated SQL used to remove links between the associated
- # classes with a manual one
- # * :insert_sql - overwrite the default generated SQL used to add links between the associated classes
- # with a manual one
+ # guessed to be the name of the associated class in lower-case and +_id+ suffixed. So if the associated class is +Project+,
+ # the +has_and_belongs_to_many+ association will use +project_id+ as the default association +foreign_key+.
+ # * :conditions - specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # SQL fragment, such as authorized = 1 .
+ # * :order - specify the order in which the associated objects are returned as an ORDER BY SQL fragment,
+ # such as last_name, first_name DESC
+ # * :uniq - if set to +true+, duplicate associated objects will be ignored by accessors and query methods
+ # * :finder_sql - overwrite the default generated SQL statement used to fetch the association with a manual statement
+ # * :delete_sql - overwrite the default generated SQL statement used to remove links between the associated
+ # classes with a manual statement
+ # * :insert_sql - overwrite the default generated SQL statement used to add links between the associated classes
+ # with a manual statement
# * :extend - anonymous module for extending the proxy, see "Association extensions".
# * :include - specify second-order associations that should be eager loaded when the collection is loaded.
- # * :group : An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
+ # * :group : An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
# * :limit : An integer determining the limit on the number of rows that should be returned.
# * :offset : An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
- # * :select : By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
- # include the joined columns.
+ # * :select : By default, this is * as in SELECT * FROM , but can be changed if, for example, you want to do a join
+ # but not include the joined columns.
#
# Option examples:
# has_and_belongs_to_many :projects
@@ -862,7 +954,7 @@ module ActiveRecord
# Don't use a before_destroy callback since users' before_destroy
# callbacks will be executed after the association is wiped out.
old_method = "destroy_without_habtm_shim_for_#{reflection.name}"
- class_eval <<-end_eval
+ class_eval <<-end_eval unless method_defined?(old_method)
alias_method :#{old_method}, :destroy_without_callbacks
def destroy_without_callbacks
#{reflection.name}.clear
@@ -871,12 +963,6 @@ module ActiveRecord
end_eval
add_association_callbacks(reflection.name, options)
-
- # deprecated api
- deprecated_collection_count_method(reflection.name)
- deprecated_add_association_relation(reflection.name)
- deprecated_remove_association_relation(reflection.name)
- deprecated_has_collection_method(reflection.name)
end
private
@@ -953,7 +1039,7 @@ module ActiveRecord
end
end
- def collection_accessor_methods(reflection, association_proxy_class)
+ def collection_accessor_methods(reflection, association_proxy_class, writer = true)
collection_reader_method(reflection, association_proxy_class)
define_method("#{reflection.name}=") do |new_value|
@@ -970,7 +1056,7 @@ module ActiveRecord
define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
ids = (new_value || []).reject { |nid| nid.blank? }
send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
- end
+ end if writer
end
def add_multiple_associated_save_callbacks(association_name)
@@ -1002,10 +1088,10 @@ module ActiveRecord
[]
end
- if !records_to_save.blank?
- records_to_save.each { |record| association.send(:insert_record, record) }
- association.send(:construct_sql) # reconstruct the SQL queries now that we know the owner's id
- end
+ records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank?
+
+ # reconstruct the SQL queries now that we know the owner's id
+ association.send(:construct_sql) if association.respond_to?(:construct_sql)
end_eval
# Doesn't use after_save as that would save associations added in after_create/after_update twice
@@ -1041,74 +1127,49 @@ module ActiveRecord
[]
end
+ # See HasManyAssociation#delete_records. Dependent associations
+ # delete children, otherwise foreign key is set to NULL.
def configure_dependency_for_has_many(reflection)
- if reflection.options[:dependent] == true
- ::ActiveSupport::Deprecation.warn("The :dependent => true option is deprecated and will be removed from Rails 2.0. Please use :dependent => :destroy instead. See http://www.rubyonrails.org/deprecation for details.", caller)
- end
+ if reflection.options.include?(:dependent)
+ # Add polymorphic type if the :as option is present
+ dependent_conditions = []
+ dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
+ dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
+ dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
+ dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
- if reflection.options[:dependent] && reflection.options[:exclusively_dependent]
- raise ArgumentError, ':dependent and :exclusively_dependent are mutually exclusive options. You may specify one or the other.'
- end
-
- if reflection.options[:exclusively_dependent]
- reflection.options[:dependent] = :delete_all
- ::ActiveSupport::Deprecation.warn("The :exclusively_dependent option is deprecated and will be removed from Rails 2.0. Please use :dependent => :delete_all instead. See http://www.rubyonrails.org/deprecation for details.", caller)
- end
-
- # See HasManyAssociation#delete_records. Dependent associations
- # delete children, otherwise foreign key is set to NULL.
-
- # Add polymorphic type if the :as option is present
- dependent_conditions = %(#{reflection.primary_key_name} = \#{record.quoted_id})
- if reflection.options[:as]
- dependent_conditions += " AND #{reflection.options[:as]}_type = '#{base_class.name}'"
- end
-
- case reflection.options[:dependent]
- when :destroy, true
- module_eval "before_destroy '#{reflection.name}.each { |o| o.destroy }'"
- when :delete_all
- module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{dependent_conditions})) }"
- when :nullify
- module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL), %(#{dependent_conditions})) }"
- when nil, false
- # pass
- else
- raise ArgumentError, 'The :dependent option expects either :destroy, :delete_all, or :nullify'
+ case reflection.options[:dependent]
+ when :destroy
+ module_eval "before_destroy '#{reflection.name}.each { |o| o.destroy }'"
+ when :delete_all
+ module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{dependent_conditions})) }"
+ when :nullify
+ module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL), %(#{dependent_conditions})) }"
+ else
+ raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
+ end
end
end
def configure_dependency_for_has_one(reflection)
- case reflection.options[:dependent]
- when :destroy, true
- module_eval "before_destroy '#{reflection.name}.destroy unless #{reflection.name}.nil?'"
- when :delete
- module_eval "before_destroy '#{reflection.class_name}.delete(#{reflection.name}.id) unless #{reflection.name}.nil?'"
- when :nullify
- module_eval "before_destroy '#{reflection.name}.update_attribute(\"#{reflection.primary_key_name}\", nil) unless #{reflection.name}.nil?'"
- when nil, false
- # pass
- else
- raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify."
+ if reflection.options.include?(:dependent)
+ case reflection.options[:dependent]
+ when :destroy
+ module_eval "before_destroy '#{reflection.name}.destroy unless #{reflection.name}.nil?'"
+ when :delete
+ module_eval "before_destroy '#{reflection.class_name}.delete(#{reflection.name}.id) unless #{reflection.name}.nil?'"
+ when :nullify
+ module_eval "before_destroy '#{reflection.name}.update_attribute(\"#{reflection.primary_key_name}\", nil) unless #{reflection.name}.nil?'"
+ else
+ raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
+ end
end
end
-
-
- def add_deprecated_api_for_has_many(association_name)
- deprecated_collection_count_method(association_name)
- deprecated_add_association_relation(association_name)
- deprecated_remove_association_relation(association_name)
- deprecated_has_collection_method(association_name)
- deprecated_find_in_collection_method(association_name)
- deprecated_find_all_in_collection_method(association_name)
- deprecated_collection_create_method(association_name)
- deprecated_collection_build_method(association_name)
- end
def create_has_many_reflection(association_id, options, &extension)
options.assert_valid_keys(
:class_name, :table_name, :foreign_key,
- :exclusively_dependent, :dependent,
+ :dependent,
:select, :conditions, :include, :order, :group, :limit, :offset,
:as, :through, :source, :source_type,
:uniq,
@@ -1117,7 +1178,7 @@ module ActiveRecord
:extend
)
- options[:extend] = create_extension_module(association_id, extension) if block_given?
+ options[:extend] = create_extension_modules(association_id, extension, options[:extend]) if block_given?
create_reflection(:has_many, association_id, options, self)
end
@@ -1155,7 +1216,7 @@ module ActiveRecord
:extend
)
- options[:extend] = create_extension_module(association_id, extension) if block_given?
+ options[:extend] = create_extension_modules(association_id, extension, options[:extend]) if block_given?
reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
@@ -1186,15 +1247,14 @@ module ActiveRecord
def construct_finder_sql_with_included_associations(options, join_dependency)
scope = scope(:find)
- sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || table_name} "
+ sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
add_joins!(sql, options, scope)
add_conditions!(sql, options[:conditions], scope)
add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
- sql << "GROUP BY #{options[:group]} " if options[:group]
-
+ add_group!(sql, options[:group], scope)
add_order!(sql, options[:order], scope)
add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
add_lock!(sql, options, scope)
@@ -1204,29 +1264,31 @@ module ActiveRecord
def add_limited_ids_condition!(sql, options, join_dependency)
unless (id_list = select_limited_ids_list(options, join_dependency)).empty?
- sql << "#{condition_word(sql)} #{table_name}.#{primary_key} IN (#{id_list}) "
+ sql << "#{condition_word(sql)} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) "
else
throw :invalid_query
end
end
-
+
def select_limited_ids_list(options, join_dependency)
+ pk = columns_hash[primary_key]
+
connection.select_all(
construct_finder_sql_for_association_limiting(options, join_dependency),
"#{name} Load IDs For Limited Eager Loading"
- ).collect { |row| connection.quote(row[primary_key]) }.join(", ")
+ ).collect { |row| connection.quote(row[primary_key], pk) }.join(", ")
end
def construct_finder_sql_for_association_limiting(options, join_dependency)
scope = scope(:find)
- is_distinct = include_eager_conditions?(options) || include_eager_order?(options)
+ is_distinct = !options[:joins].blank? || include_eager_conditions?(options) || include_eager_order?(options)
sql = "SELECT "
if is_distinct
- sql << connection.distinct("#{table_name}.#{primary_key}", options[:order])
+ sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", options[:order])
else
sql << primary_key
end
- sql << " FROM #{table_name} "
+ sql << " FROM #{connection.quote_table_name table_name} "
if is_distinct
sql << join_dependency.join_associations.collect(&:association_join).join
@@ -1234,14 +1296,16 @@ module ActiveRecord
end
add_conditions!(sql, options[:conditions], scope)
- if options[:order]
- if is_distinct
- connection.add_order_by_for_association_limiting!(sql, options)
- else
- sql << "ORDER BY #{options[:order]}"
- end
+ add_group!(sql, options[:group], scope)
+
+ if options[:order] && is_distinct
+ connection.add_order_by_for_association_limiting!(sql, options)
+ else
+ add_order!(sql, options[:order], scope)
end
+
add_limit!(sql, options, scope)
+
return sanitize_sql(sql)
end
@@ -1276,7 +1340,7 @@ module ActiveRecord
def column_aliases(join_dependency)
join_dependency.joins.collect{|join| join.column_names_with_alias.collect{|column_name, aliased_name|
- "#{join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ")
+ "#{connection.quote_table_name join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ")
end
def add_association_callbacks(association_name, options)
@@ -1286,7 +1350,9 @@ module ActiveRecord
defined_callbacks = options[callback_name.to_sym]
if options.has_key?(callback_name.to_sym)
class_inheritable_reader full_callback_name.to_sym
- write_inheritable_array(full_callback_name.to_sym, [defined_callbacks].flatten)
+ write_inheritable_attribute(full_callback_name.to_sym, [defined_callbacks].flatten)
+ else
+ write_inheritable_attribute(full_callback_name.to_sym, [])
end
end
end
@@ -1295,14 +1361,14 @@ module ActiveRecord
sql =~ /where/i ? " AND " : "WHERE "
end
- def create_extension_module(association_id, extension)
+ def create_extension_modules(association_id, block_extension, extensions)
extension_module_name = "#{self.to_s}#{association_id.to_s.camelize}AssociationExtension"
silence_warnings do
- Object.const_set(extension_module_name, Module.new(&extension))
+ Object.const_set(extension_module_name, Module.new(&block_extension))
end
-
- extension_module_name.constantize
+
+ Array(extensions).push(extension_module_name.constantize)
end
class JoinDependency # :nodoc:
@@ -1335,11 +1401,34 @@ module ActiveRecord
end
construct(@base_records_hash[primary_id], @associations, join_associations.dup, row)
end
+ remove_duplicate_results!(join_base.active_record, @base_records_in_order, @associations)
return @base_records_in_order
end
- def aliased_table_names_for(table_name)
- joins.select{|join| join.table_name == table_name }.collect{|join| join.aliased_table_name}
+ def remove_duplicate_results!(base, records, associations)
+ case associations
+ when Symbol, String
+ reflection = base.reflections[associations]
+ if reflection && [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
+ records.each { |record| record.send(reflection.name).target.uniq! }
+ end
+ when Array
+ associations.each do |association|
+ remove_duplicate_results!(base, records, association)
+ end
+ when Hash
+ associations.keys.each do |name|
+ reflection = base.reflections[name]
+ is_collection = [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
+
+ parent_records = records.map do |record|
+ next unless record.send(reflection.name)
+ is_collection ? record.send(reflection.name).target.uniq! : record.send(reflection.name)
+ end.flatten.compact
+
+ remove_duplicate_results!(reflection.class_name.constantize, parent_records, associations[name]) unless parent_records.empty?
+ end
+ end
end
protected
@@ -1350,7 +1439,7 @@ module ActiveRecord
reflection = parent.reflections[associations.to_s.intern] or
raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
@reflections << reflection
- @joins << JoinAssociation.new(reflection, self, parent)
+ @joins << build_join_association(reflection, parent)
when Array
associations.each do |association|
build(association, parent)
@@ -1365,6 +1454,11 @@ module ActiveRecord
end
end
+ # overridden in InnerJoinDependency subclass
+ def build_join_association(reflection, parent)
+ JoinAssociation.new(reflection, self, parent)
+ end
+
def construct(parent, associations, joins, row)
case associations
when Symbol, String
@@ -1394,7 +1488,7 @@ module ActiveRecord
return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
association = join.instantiate(row)
- collection.target.push(association) unless collection.target.include?(association)
+ collection.target.push(association)
when :has_one
return if record.id.to_s != join.parent.record_id(row).to_s
association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
@@ -1450,7 +1544,7 @@ module ActiveRecord
end
def instantiate(row)
- @cached_record[record_id(row)] ||= active_record.instantiate(extract_record(row))
+ @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
end
end
@@ -1499,53 +1593,60 @@ module ActiveRecord
end
def association_join
+ connection = reflection.active_record.connection
join = case reflection.macro
when :has_and_belongs_to_many
- " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
+ " #{join_type} %s ON %s.%s = %s.%s " % [
table_alias_for(options[:join_table], aliased_join_table_name),
- aliased_join_table_name,
- options[:foreign_key] || reflection.active_record.to_s.classify.foreign_key,
- parent.aliased_table_name, reflection.active_record.primary_key] +
- " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
- table_name_and_alias, aliased_table_name, klass.primary_key,
- aliased_join_table_name, options[:association_foreign_key] || klass.table_name.classify.foreign_key
+ connection.quote_table_name(aliased_join_table_name),
+ options[:foreign_key] || reflection.active_record.to_s.foreign_key,
+ connection.quote_table_name(parent.aliased_table_name),
+ reflection.active_record.primary_key] +
+ " #{join_type} %s ON %s.%s = %s.%s " % [
+ table_name_and_alias,
+ connection.quote_table_name(aliased_table_name),
+ klass.primary_key,
+ connection.quote_table_name(aliased_join_table_name),
+ options[:association_foreign_key] || klass.to_s.foreign_key
]
when :has_many, :has_one
case
when reflection.macro == :has_many && reflection.options[:through]
through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
-
- jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
- first_key = second_key = as_extra = nil
+
+ jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
+ first_key = second_key = as_extra = nil
if through_reflection.options[:as] # has_many :through against a polymorphic join
jt_foreign_key = through_reflection.options[:as].to_s + '_id'
jt_as_extra = " AND %s.%s = %s" % [
- aliased_join_table_name, reflection.active_record.connection.quote_column_name(through_reflection.options[:as].to_s + '_type'),
- klass.quote_value(parent.active_record.base_class.name)
+ connection.quote_table_name(aliased_join_table_name),
+ connection.quote_column_name(through_reflection.options[:as].to_s + '_type'),
+ klass.quote_value(parent.active_record.base_class.name)
]
else
- jt_foreign_key = through_reflection.primary_key_name
+ jt_foreign_key = through_reflection.primary_key_name
end
-
+
case source_reflection.macro
when :has_many
- if source_reflection.options[:as]
- first_key = "#{source_reflection.options[:as]}_id"
- second_key = options[:foreign_key] || primary_key
+ if source_reflection.options[:as]
+ first_key = "#{source_reflection.options[:as]}_id"
+ second_key = options[:foreign_key] || primary_key
as_extra = " AND %s.%s = %s" % [
- aliased_table_name, reflection.active_record.connection.quote_column_name("#{source_reflection.options[:as]}_type"),
- klass.quote_value(source_reflection.active_record.base_class.name)
+ connection.quote_table_name(aliased_table_name),
+ connection.quote_column_name("#{source_reflection.options[:as]}_type"),
+ klass.quote_value(source_reflection.active_record.base_class.name)
]
else
- first_key = through_reflection.klass.base_class.to_s.classify.foreign_key
+ first_key = through_reflection.klass.base_class.to_s.foreign_key
second_key = options[:foreign_key] || primary_key
end
unless through_reflection.klass.descends_from_active_record?
jt_sti_extra = " AND %s.%s = %s" % [
- aliased_join_table_name,
- reflection.active_record.connection.quote_column_name(through_reflection.active_record.inheritance_column),
+ connection.quote_table_name(aliased_join_table_name),
+ connection.quote_column_name(through_reflection.active_record.inheritance_column),
through_reflection.klass.quote_value(through_reflection.klass.name.demodulize)]
end
when :belongs_to
@@ -1553,62 +1654,67 @@ module ActiveRecord
if reflection.options[:source_type]
second_key = source_reflection.association_foreign_key
jt_source_extra = " AND %s.%s = %s" % [
- aliased_join_table_name, reflection.active_record.connection.quote_column_name(reflection.source_reflection.options[:foreign_type]),
- klass.quote_value(reflection.options[:source_type])
+ connection.quote_table_name(aliased_join_table_name),
+ connection.quote_column_name(reflection.source_reflection.options[:foreign_type]),
+ klass.quote_value(reflection.options[:source_type])
]
else
- second_key = source_reflection.options[:foreign_key] || klass.to_s.classify.foreign_key
+ second_key = source_reflection.primary_key_name
end
end
-
- " LEFT OUTER JOIN %s ON (%s.%s = %s.%s%s%s%s) " % [
+
+ " #{join_type} %s ON (%s.%s = %s.%s%s%s%s) " % [
table_alias_for(through_reflection.klass.table_name, aliased_join_table_name),
- parent.aliased_table_name, reflection.active_record.connection.quote_column_name(parent.primary_key),
- aliased_join_table_name, reflection.active_record.connection.quote_column_name(jt_foreign_key),
+ connection.quote_table_name(parent.aliased_table_name),
+ connection.quote_column_name(parent.primary_key),
+ connection.quote_table_name(aliased_join_table_name),
+ connection.quote_column_name(jt_foreign_key),
jt_as_extra, jt_source_extra, jt_sti_extra
] +
- " LEFT OUTER JOIN %s ON (%s.%s = %s.%s%s) " % [
+ " #{join_type} %s ON (%s.%s = %s.%s%s) " % [
table_name_and_alias,
- aliased_table_name, reflection.active_record.connection.quote_column_name(first_key),
- aliased_join_table_name, reflection.active_record.connection.quote_column_name(second_key),
+ connection.quote_table_name(aliased_table_name),
+ connection.quote_column_name(first_key),
+ connection.quote_table_name(aliased_join_table_name),
+ connection.quote_column_name(second_key),
as_extra
]
-
- when reflection.macro == :has_many && reflection.options[:as]
- " LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s" % [
+
+ when reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro)
+ " #{join_type} %s ON %s.%s = %s.%s AND %s.%s = %s" % [
table_name_and_alias,
- aliased_table_name, "#{reflection.options[:as]}_id",
- parent.aliased_table_name, parent.primary_key,
- aliased_table_name, "#{reflection.options[:as]}_type",
+ connection.quote_table_name(aliased_table_name),
+ "#{reflection.options[:as]}_id",
+ connection.quote_table_name(parent.aliased_table_name),
+ parent.primary_key,
+ connection.quote_table_name(aliased_table_name),
+ "#{reflection.options[:as]}_type",
klass.quote_value(parent.active_record.base_class.name)
]
- when reflection.macro == :has_one && reflection.options[:as]
- " LEFT OUTER JOIN %s ON %s.%s = %s.%s AND %s.%s = %s " % [
- table_name_and_alias,
- aliased_table_name, "#{reflection.options[:as]}_id",
- parent.aliased_table_name, parent.primary_key,
- aliased_table_name, "#{reflection.options[:as]}_type",
- klass.quote_value(reflection.active_record.base_class.name)
- ]
else
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
- " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
+ " #{join_type} %s ON %s.%s = %s.%s " % [
table_name_and_alias,
- aliased_table_name, foreign_key,
- parent.aliased_table_name, parent.primary_key
+ aliased_table_name,
+ foreign_key,
+ parent.aliased_table_name,
+ parent.primary_key
]
end
when :belongs_to
- " LEFT OUTER JOIN %s ON %s.%s = %s.%s " % [
- table_name_and_alias, aliased_table_name, reflection.klass.primary_key,
- parent.aliased_table_name, options[:foreign_key] || klass.to_s.foreign_key
+ " #{join_type} %s ON %s.%s = %s.%s " % [
+ table_name_and_alias,
+ connection.quote_table_name(aliased_table_name),
+ reflection.klass.primary_key,
+ connection.quote_table_name(parent.aliased_table_name),
+ options[:foreign_key] || klass.to_s.foreign_key
]
else
""
end || ''
join << %(AND %s.%s = %s ) % [
- aliased_table_name,
- reflection.active_record.connection.quote_column_name(klass.inheritance_column),
+ connection.quote_table_name(aliased_table_name),
+ connection.quote_column_name(klass.inheritance_column),
klass.quote_value(klass.name.demodulize)] unless klass.descends_from_active_record?
[through_reflection, reflection].each do |ref|
@@ -1625,7 +1731,7 @@ module ActiveRecord
end
def table_alias_for(table_name, table_alias)
- "#{table_name} #{table_alias if table_name != table_alias}".strip
+ "#{reflection.active_record.connection.quote_table_name(table_name)} #{table_alias if table_name != table_alias}".strip
end
def table_name_and_alias
@@ -1635,8 +1741,29 @@ module ActiveRecord
def interpolate_sql(sql)
instance_eval("%@#{sql.gsub('@', '\@')}@")
end
+
+ private
+
+ def join_type
+ "LEFT OUTER JOIN"
+ end
end
end
+
+ class InnerJoinDependency < JoinDependency # :nodoc:
+ protected
+ def build_join_association(reflection, parent)
+ InnerJoinAssociation.new(reflection, self, parent)
+ end
+
+ class InnerJoinAssociation < JoinAssociation
+ private
+ def join_type
+ "INNER JOIN"
+ end
+ end
+ end
+
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb b/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb
index e5bf157e..79cd227c 100644
--- a/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb
+++ b/vendor/rails/activerecord/lib/active_record/associations/association_collection.rb
@@ -7,7 +7,7 @@ module ActiveRecord
load_target
@target.to_ary
end
-
+
def reset
reset_target!
@loaded = false
@@ -17,7 +17,7 @@ module ActiveRecord
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
def <<(*records)
result = true
- load_target
+ load_target if @owner.new_record?
@owner.transaction do
flatten_deeper(records).each do |record|
@@ -34,7 +34,7 @@ module ActiveRecord
alias_method :push, :<<
alias_method :concat, :<<
-
+
# Remove all records from this association
def delete_all
load_target
@@ -66,9 +66,9 @@ module ActiveRecord
# Removes all records from this association. Returns +self+ so method calls may be chained.
def clear
- return self if length.zero? # forces load_target if hasn't happened already
+ return self if length.zero? # forces load_target if it hasn't happened already
- if @reflection.options[:dependent] && @reflection.options[:dependent] == :delete_all
+ if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy
destroy_all
else
delete_all
@@ -84,27 +84,24 @@ module ActiveRecord
reset_target!
end
-
- def create(attributes = {})
- # Can't use Base.create since the foreign key may be a protected attribute.
- if attributes.is_a?(Array)
- attributes.collect { |attr| create(attr) }
+
+ def create(attrs = {})
+ if attrs.is_a?(Array)
+ attrs.collect { |attr| create(attr) }
else
- record = build(attributes)
- if @owner.new_record?
- ActiveSupport::Deprecation.warn("Calling .create on a has_many association without saving its owner will not work in rails 2.0, you probably want .build instead")
- else
- record.save
- end
- record
+ create_record(attrs) { |record| record.save }
end
end
+ def create!(attrs = {})
+ create_record(attrs) { |record| record.save! }
+ end
+
# Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
# calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero
# and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length.
def size
- if loaded? && !@reflection.options[:uniq]
+ if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
@target.size
elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
unsaved_records = Array(@target.detect { |r| r.new_record? })
@@ -124,6 +121,14 @@ module ActiveRecord
size.zero?
end
+ def any?(&block)
+ if block_given?
+ method_missing(:any?, &block)
+ else
+ !empty?
+ end
+ end
+
def uniq(collection = self)
seen = Set.new
collection.inject([]) do |kept, record|
@@ -150,7 +155,21 @@ module ActiveRecord
end
end
+
protected
+ def method_missing(method, *args, &block)
+ if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
+ super
+ else
+ @reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.send(method, *args, &block) }
+ end
+ end
+
+ # overloaded in derived Association classes to provide useful scoping depending on association type.
+ def construct_scope
+ {}
+ end
+
def reset_target!
@target = Array.new
end
@@ -167,6 +186,27 @@ module ActiveRecord
end
private
+
+ def create_record(attrs, &block)
+ ensure_owner_is_not_new
+ record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.new(attrs) }
+ add_record_to_target_with_callbacks(record, &block)
+ end
+
+ def build_record(attrs, &block)
+ record = @reflection.klass.new(attrs)
+ add_record_to_target_with_callbacks(record, &block)
+ end
+
+ def add_record_to_target_with_callbacks(record)
+ callback(:before_add, record)
+ yield(record) if block_given?
+ @target ||= [] unless loaded?
+ @target << record
+ callback(:after_add, record)
+ record
+ end
+
def callback(method, record)
callbacks_for(method).each do |callback|
case callback
@@ -187,8 +227,14 @@ module ActiveRecord
def callbacks_for(callback_name)
full_callback_name = "#{callback_name}_for_#{@reflection.name}"
@owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
- end
+ end
+ def ensure_owner_is_not_new
+ if @owner.new_record?
+ raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
+ end
+ end
+
end
end
-end
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/lib/active_record/associations/association_proxy.rb b/vendor/rails/activerecord/lib/active_record/associations/association_proxy.rb
index 1b929f28..ed1dd36f 100644
--- a/vendor/rails/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/vendor/rails/activerecord/lib/active_record/associations/association_proxy.rb
@@ -12,15 +12,15 @@ module ActiveRecord
Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
reset
end
-
+
def proxy_owner
@owner
end
-
+
def proxy_reflection
@reflection
end
-
+
def proxy_target
@target
end
@@ -28,55 +28,61 @@ module ActiveRecord
def respond_to?(symbol, include_priv = false)
proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
end
-
+
# Explicitly proxy === because the instance method removal above
# doesn't catch it.
def ===(other)
load_target
other === @target
end
-
+
def aliased_table_name
@reflection.klass.table_name
end
-
+
def conditions
@conditions ||= interpolate_sql(sanitize_sql(@reflection.options[:conditions])) if @reflection.options[:conditions]
end
alias :sql_conditions :conditions
-
+
def reset
- @target = nil
@loaded = false
+ @target = nil
end
def reload
reset
load_target
+ self unless @target.nil?
end
def loaded?
@loaded
end
-
+
def loaded
@loaded = true
end
-
+
def target
@target
end
-
+
def target=(target)
@target = target
loaded
end
-
+
+ def inspect
+ reload unless loaded?
+ @target.inspect
+ end
+
protected
def dependent?
- @reflection.options[:dependent] || false
+ @reflection.options[:dependent]
end
-
+
def quoted_record_ids(records)
records.map { |record| record.quoted_id }.join(',')
end
@@ -93,10 +99,6 @@ module ActiveRecord
@reflection.klass.send(:sanitize_sql, sql)
end
- def extract_options_from_args!(args)
- @owner.send(:extract_options_from_args!, args)
- end
-
def set_belongs_to_association_for(record)
if @reflection.options[:as]
record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
@@ -116,10 +118,10 @@ module ActiveRecord
:select => @reflection.options[:select]
)
end
-
+
private
def method_missing(method, *args, &block)
- if load_target
+ if load_target
@target.send(method, *args, &block)
end
end
@@ -138,14 +140,14 @@ module ActiveRecord
end
# Can be overwritten by associations that might have the foreign key available for an association without
- # having the object itself (and still being a new record). Currently, only belongs_to present this scenario.
+ # having the object itself (and still being a new record). Currently, only belongs_to presents this scenario.
def foreign_key_present
false
end
def raise_on_type_mismatch(record)
unless record.is_a?(@reflection.klass)
- raise ActiveRecord::AssociationTypeMismatch, "#{@reflection.class_name} expected, got #{record.class}"
+ raise ActiveRecord::AssociationTypeMismatch, "#{@reflection.klass} expected, got #{record.class}"
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/vendor/rails/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index 1eea798a..b26ea586 100644
--- a/vendor/rails/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/vendor/rails/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -5,31 +5,26 @@ module ActiveRecord
super
construct_sql
end
-
+
def build(attributes = {})
load_target
- record = @reflection.klass.new(attributes)
- @target << record
- record
+ build_record(attributes)
end
def create(attributes = {})
- # Can't use Base.create since the foreign key may be a protected attribute.
- if attributes.is_a?(Array)
- attributes.collect { |attr| create(attr) }
- else
- record = build(attributes)
- insert_record(record) unless @owner.new_record?
- record
- end
+ create_record(attributes) { |record| insert_record(record) }
+ end
+
+ def create!(attributes = {})
+ create_record(attributes) { |record| insert_record(record, true) }
end
def find_first
load_target.first
end
-
+
def find(*args)
- options = Base.send(:extract_options_from_args!, args)
+ options = args.extract_options!
# If using a custom finder_sql, scan the entire collection.
if @reflection.options[:finder_sql]
@@ -52,7 +47,7 @@ module ActiveRecord
options[:conditions] = conditions
options[:joins] = @join_sql
- options[:readonly] = finding_with_ambigious_select?(options[:select])
+ options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
if options[:order] && @reflection.options[:order]
options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
@@ -62,47 +57,26 @@ module ActiveRecord
merge_options_from_reflection!(options)
+ options[:select] ||= (@reflection.options[:select] || '*')
+
# Pass through args exactly as we received them.
args << options
@reflection.klass.find(*args)
end
- end
-
- # Deprecated as of Rails 1.2. If your associations require attributes
- # you should be using has_many :through
- def push_with_attributes(record, join_attributes = {})
- raise_on_type_mismatch(record)
- join_attributes.each { |key, value| record[key.to_s] = value }
-
- callback(:before_add, record)
- insert_record(record) unless @owner.new_record?
- @target << record
- callback(:after_add, record)
-
- self
end
- deprecate :push_with_attributes => "consider using has_many :through instead"
-
- alias :concat_with_attributes :push_with_attributes
protected
- def method_missing(method, *args, &block)
- if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
- super
- else
- @reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
- @reflection.klass.send(method, *args, &block)
- end
- end
- end
-
def count_records
load_target.size
end
- def insert_record(record)
+ def insert_record(record, force=true)
if record.new_record?
- return false unless record.save
+ if force
+ record.save!
+ else
+ return false unless record.save
+ end
end
if @reflection.options[:insert_sql]
@@ -131,10 +105,10 @@ module ActiveRecord
@owner.connection.execute(sql)
end
-
+
return true
end
-
+
def delete_records(records)
if sql = @reflection.options[:delete_sql]
records.each { |record| @owner.connection.execute(interpolate_sql(sql, record)) }
@@ -144,7 +118,7 @@ module ActiveRecord
@owner.connection.execute(sql)
end
end
-
+
def construct_sql
interpolate_sql_options!(@reflection.options, :finder_sql)
@@ -158,12 +132,33 @@ module ActiveRecord
@join_sql = "INNER JOIN #{@reflection.options[:join_table]} ON #{@reflection.klass.table_name}.#{@reflection.klass.primary_key} = #{@reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
end
- # Join tables with additional columns on top of the two foreign keys must be considered ambigious unless a select
- # clause has been explicitly defined. Otherwise you can get broken records back, if, say, the join column also has
- # and id column, which will then overwrite the id column of the records coming back.
- def finding_with_ambigious_select?(select_clause)
+ def construct_scope
+ { :find => { :conditions => @finder_sql,
+ :joins => @join_sql,
+ :readonly => false,
+ :order => @reflection.options[:order],
+ :limit => @reflection.options[:limit] } }
+ end
+
+ # Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select
+ # clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has
+ # an id column. This will then overwrite the id column of the records coming back.
+ def finding_with_ambiguous_select?(select_clause)
!select_clause && @owner.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2
end
+
+ private
+ def create_record(attributes)
+ # Can't use Base.create because the foreign key may be a protected attribute.
+ ensure_owner_is_not_new
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create(attr) }
+ else
+ record = build(attributes)
+ yield(record)
+ record
+ end
+ end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/associations/has_many_association.rb b/vendor/rails/activerecord/lib/active_record/associations/has_many_association.rb
index 5460c63e..ead447ba 100644
--- a/vendor/rails/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/vendor/rails/activerecord/lib/active_record/associations/has_many_association.rb
@@ -10,35 +10,10 @@ module ActiveRecord
if attributes.is_a?(Array)
attributes.collect { |attr| build(attr) }
else
- record = @reflection.klass.new(attributes)
- set_belongs_to_association_for(record)
-
- @target ||= [] unless loaded?
- @target << record
-
- record
+ build_record(attributes) { |record| set_belongs_to_association_for(record) }
end
end
- # DEPRECATED.
- def find_all(runtime_conditions = nil, orderings = nil, limit = nil, joins = nil)
- if @reflection.options[:finder_sql]
- @reflection.klass.find_by_sql(@finder_sql)
- else
- conditions = @finder_sql
- conditions += " AND (#{sanitize_sql(runtime_conditions)})" if runtime_conditions
- orderings ||= @reflection.options[:order]
- @reflection.klass.find_all(conditions, orderings, limit, joins)
- end
- end
- deprecate :find_all => "use find(:all, ...) instead"
-
- # DEPRECATED. Find the first associated record. All arguments are optional.
- def find_first(conditions = nil, orderings = nil)
- find_all(conditions, orderings, 1).first
- end
- deprecate :find_first => "use find(:first, ...) instead"
-
# Count the number of associated records. All arguments are optional.
def count(*args)
if @reflection.options[:counter_sql]
@@ -46,7 +21,7 @@ module ActiveRecord
elsif @reflection.options[:finder_sql]
@reflection.klass.count_by_sql(@finder_sql)
else
- column_name, options = @reflection.klass.send(:construct_count_options_from_legacy_args, *args)
+ column_name, options = @reflection.klass.send(:construct_count_options_from_args, *args)
options[:conditions] = options[:conditions].nil? ?
@finder_sql :
@finder_sql + " AND (#{sanitize_sql(options[:conditions])})"
@@ -57,12 +32,12 @@ module ActiveRecord
end
def find(*args)
- options = Base.send(:extract_options_from_args!, args)
+ options = args.extract_options!
# If using a custom finder_sql, scan the entire collection.
if @reflection.options[:finder_sql]
expects_array = args.first.kind_of?(Array)
- ids = args.flatten.compact.uniq
+ ids = args.flatten.compact.uniq.map(&:to_i)
if ids.size == 1
id = ids.first
@@ -93,26 +68,6 @@ module ActiveRecord
end
protected
- def method_missing(method, *args, &block)
- if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
- super
- else
- create_scoping = {}
- set_belongs_to_association_for(create_scoping)
-
- @reflection.klass.with_scope(
- :create => create_scoping,
- :find => {
- :conditions => @finder_sql,
- :joins => @join_sql,
- :readonly => false
- }
- ) do
- @reflection.klass.send(method, *args, &block)
- end
- end
- end
-
def load_target
if !@owner.new_record? || foreign_key_present
begin
@@ -164,14 +119,17 @@ module ActiveRecord
end
def delete_records(records)
- if @reflection.options[:dependent]
- records.each { |r| r.destroy }
- else
- ids = quoted_record_ids(records)
- @reflection.klass.update_all(
- "#{@reflection.primary_key_name} = NULL",
- "#{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
- )
+ case @reflection.options[:dependent]
+ when :destroy
+ records.each(&:destroy)
+ when :delete_all
+ @reflection.klass.delete(records.map(&:id))
+ else
+ ids = quoted_record_ids(records)
+ @reflection.klass.update_all(
+ "#{@reflection.primary_key_name} = NULL",
+ "#{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
+ )
end
end
@@ -205,6 +163,12 @@ module ActiveRecord
@counter_sql = @finder_sql
end
end
+
+ def construct_scope
+ create_scoping = {}
+ set_belongs_to_association_for(create_scoping)
+ { :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit] }, :create => create_scoping }
+ end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/associations/has_many_through_association.rb b/vendor/rails/activerecord/lib/active_record/associations/has_many_through_association.rb
index 61e6020b..14f752ab 100644
--- a/vendor/rails/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/vendor/rails/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -9,7 +9,7 @@ module ActiveRecord
end
def find(*args)
- options = Base.send(:extract_options_from_args!, args)
+ options = args.extract_options!
conditions = "#{@finder_sql}"
if sanitized_conditions = sanitize_sql(options[:conditions])
@@ -51,16 +51,14 @@ module ActiveRecord
through = @reflection.through_reflection
raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, through) if @owner.new_record?
- load_target
-
klass = through.klass
klass.transaction do
flatten_deeper(records).each do |associate|
raise_on_type_mismatch(associate)
raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, through) unless associate.respond_to?(:new_record?) && !associate.new_record?
- @owner.send(@reflection.through_reflection.name).proxy_target << klass.with_scope(:create => construct_join_attributes(associate)) { klass.create! }
- @target << associate
+ @owner.send(@reflection.through_reflection.name).proxy_target << klass.send(:with_scope, :create => construct_join_attributes(associate)) { klass.create! }
+ @target << associate if loaded?
end
end
@@ -69,43 +67,60 @@ module ActiveRecord
[:push, :concat].each { |method| alias_method method, :<< }
- # Remove +records+ from this association. Does not destroy +records+.
+ # Removes +records+ from this association. Does not destroy +records+.
def delete(*records)
records = flatten_deeper(records)
records.each { |associate| raise_on_type_mismatch(associate) }
- records.reject! { |associate| @target.delete(associate) if associate.new_record? }
- return if records.empty?
-
- @delete_join_finder ||= "find_all_by_#{@reflection.source_reflection.association_foreign_key}"
+
through = @reflection.through_reflection
- through.klass.transaction do
- records.each do |associate|
- joins = @owner.send(through.name).send(@delete_join_finder, associate.id)
- @owner.send(through.name).delete(joins)
+ raise ActiveRecord::HasManyThroughCantDissociateNewRecords.new(@owner, through) if @owner.new_record?
+
+ load_target
+
+ klass = through.klass
+ klass.transaction do
+ flatten_deeper(records).each do |associate|
+ raise_on_type_mismatch(associate)
+ raise ActiveRecord::HasManyThroughCantDissociateNewRecords.new(@owner, through) unless associate.respond_to?(:new_record?) && !associate.new_record?
+
+ @owner.send(through.name).proxy_target.delete(klass.delete_all(construct_join_attributes(associate)))
@target.delete(associate)
end
end
+
+ self
end
def build(attrs = nil)
raise ActiveRecord::HasManyThroughCantAssociateNewRecords.new(@owner, @reflection.through_reflection)
end
+ alias_method :new, :build
def create!(attrs = nil)
@reflection.klass.transaction do
- self << @reflection.klass.with_scope(:create => attrs) { @reflection.klass.create! }
+ self << (object = @reflection.klass.send(:with_scope, :create => attrs) { @reflection.klass.create! })
+ object
end
end
+ # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
+ # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero
+ # and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length.
+ def size
+ return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter?
+ return @target.size if loaded?
+ return count
+ end
+
# Calculate sum using SQL, not Enumerable
def sum(*args, &block)
calculate(:sum, *args, &block)
end
def count(*args)
- column_name, options = @reflection.klass.send(:construct_count_options_from_legacy_args, *args)
+ column_name, options = @reflection.klass.send(:construct_count_options_from_args, *args)
if @reflection.options[:uniq]
- # This is needed becase 'SELECT count(DISTINCT *)..' is not valid sql statement.
+ # This is needed because 'SELECT count(DISTINCT *)..' is not valid sql statement.
column_name = "#{@reflection.klass.table_name}.#{@reflection.klass.primary_key}" if column_name == :all
options.merge!(:distinct => true)
end
@@ -117,7 +132,7 @@ module ActiveRecord
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
super
else
- @reflection.klass.with_scope(construct_scope) { @reflection.klass.send(method, *args, &block) }
+ @reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.send(method, *args, &block) }
end
end
@@ -133,7 +148,8 @@ module ActiveRecord
:include => @reflection.options[:include] || @reflection.source_reflection.options[:include]
)
- @reflection.options[:uniq] ? records.to_set.to_a : records
+ records.uniq! if @reflection.options[:uniq]
+ records
end
# Construct attributes for associate pointing to owner.
@@ -148,11 +164,11 @@ module ActiveRecord
# Construct attributes for :through pointing to owner and associate.
def construct_join_attributes(associate)
- returning construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) do |join_attributes|
- if @reflection.options[:source_type]
- join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s)
- end
+ join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id)
+ if @reflection.options[:source_type]
+ join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s)
end
+ join_attributes
end
# Associate attributes pointing to owner, quoted.
@@ -220,7 +236,9 @@ module ActiveRecord
:find => { :from => construct_from,
:conditions => construct_conditions,
:joins => construct_joins,
- :select => construct_select } }
+ :select => construct_select,
+ :order => @reflection.options[:order],
+ :limit => @reflection.options[:limit] } }
end
def construct_sql
@@ -247,11 +265,20 @@ module ActiveRecord
@conditions ||= [
(interpolate_sql(@reflection.klass.send(:sanitize_sql, @reflection.options[:conditions])) if @reflection.options[:conditions]),
(interpolate_sql(@reflection.active_record.send(:sanitize_sql, @reflection.through_reflection.options[:conditions])) if @reflection.through_reflection.options[:conditions]),
+ (interpolate_sql(@reflection.active_record.send(:sanitize_sql, @reflection.source_reflection.options[:conditions])) if @reflection.source_reflection.options[:conditions]),
("#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.name.demodulize)}" unless @reflection.through_reflection.klass.descends_from_active_record?)
- ].compact.collect { |condition| "(#{condition})" }.join(' AND ') unless (!@reflection.options[:conditions] && !@reflection.through_reflection.options[:conditions] && @reflection.through_reflection.klass.descends_from_active_record?)
+ ].compact.collect { |condition| "(#{condition})" }.join(' AND ') unless (!@reflection.options[:conditions] && !@reflection.through_reflection.options[:conditions] && !@reflection.source_reflection.options[:conditions] && @reflection.through_reflection.klass.descends_from_active_record?)
end
alias_method :sql_conditions, :conditions
+
+ def has_cached_counter?
+ @owner.attribute_present?(cached_counter_attribute_name)
+ end
+
+ def cached_counter_attribute_name
+ "#{@reflection.name}_count"
+ end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/associations/has_one_association.rb b/vendor/rails/activerecord/lib/active_record/associations/has_one_association.rb
index 4ccd725e..085de27c 100644
--- a/vendor/rails/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/vendor/rails/activerecord/lib/active_record/associations/has_one_association.rb
@@ -6,23 +6,16 @@ module ActiveRecord
construct_sql
end
- def create(attributes = {}, replace_existing = true)
- record = build(attributes, replace_existing)
- record.save
- record
+ def create(attrs = {}, replace_existing = true)
+ new_record(replace_existing) { |klass| klass.create(attrs) }
end
- def build(attributes = {}, replace_existing = true)
- record = @reflection.klass.new(attributes)
+ def create!(attrs = {}, replace_existing = true)
+ new_record(replace_existing) { |klass| klass.create!(attrs) }
+ end
- if replace_existing
- replace(record, true)
- else
- record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
- self.target = record
- end
-
- record
+ def build(attrs = {}, replace_existing = true)
+ new_record(replace_existing) { |klass| klass.new(attrs) }
end
def replace(obj, dont_save = false)
@@ -75,6 +68,29 @@ module ActiveRecord
end
@finder_sql << " AND (#{conditions})" if conditions
end
+
+ def construct_scope
+ create_scoping = {}
+ set_belongs_to_association_for(create_scoping)
+ { :create => create_scoping }
+ end
+
+ def new_record(replace_existing)
+ # Make sure we load the target first, if we plan on replacing the existing
+ # instance. Otherwise, if the target has not previously been loaded
+ # elsewhere, the instance we create will get orphaned.
+ load_target if replace_existing
+ record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { yield @reflection.klass }
+
+ if replace_existing
+ replace(record, true)
+ else
+ record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
+ self.target = record
+ end
+
+ record
+ end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/attribute_methods.rb b/vendor/rails/activerecord/lib/active_record/attribute_methods.rb
index adc6eb65..dd26c1f7 100644
--- a/vendor/rails/activerecord/lib/active_record/attribute_methods.rb
+++ b/vendor/rails/activerecord/lib/active_record/attribute_methods.rb
@@ -1,10 +1,13 @@
module ActiveRecord
module AttributeMethods #:nodoc:
DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
+ ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
def self.included(base)
base.extend ClassMethods
base.attribute_method_suffix *DEFAULT_SUFFIXES
+ base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
+ base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
end
# Declare and check for suffixed attribute methods.
@@ -43,6 +46,68 @@ module ActiveRecord
@@attribute_method_regexp.match(method_name)
end
+
+ # Contains the names of the generated attribute methods.
+ def generated_methods #:nodoc:
+ @generated_methods ||= Set.new
+ end
+
+ def generated_methods?
+ !generated_methods.empty?
+ end
+
+ # generates all the attribute related methods for columns in the database
+ # accessors, mutators and query methods
+ def define_attribute_methods
+ return if generated_methods?
+ columns_hash.each do |name, column|
+ unless instance_method_already_implemented?(name)
+ if self.serialized_attributes[name]
+ define_read_method_for_serialized_attribute(name)
+ else
+ define_read_method(name.to_sym, name, column)
+ end
+ end
+
+ unless instance_method_already_implemented?("#{name}=")
+ define_write_method(name.to_sym)
+ end
+
+ unless instance_method_already_implemented?("#{name}?")
+ define_question_method(name)
+ end
+ end
+ end
+
+ # Check to see if the method is defined in the model or any of its subclasses that also derive from ActiveRecord.
+ # Raise DangerousAttributeError if the method is defined by ActiveRecord though.
+ def instance_method_already_implemented?(method_name)
+ return true if method_name =~ /^id(=$|\?$|$)/
+ @_defined_class_methods ||= Set.new(ancestors.first(ancestors.index(ActiveRecord::Base)).collect! { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.flatten)
+ @@_defined_activerecord_methods ||= Set.new(ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false))
+ raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
+ @_defined_class_methods.include?(method_name)
+ end
+
+ alias :define_read_methods :define_attribute_methods
+
+ # +cache_attributes+ allows you to declare which converted attribute values should
+ # be cached. Usually caching only pays off for attributes with expensive conversion
+ # methods, like date columns (e.g. created_at, updated_at).
+ def cache_attributes(*attribute_names)
+ attribute_names.each {|attr| cached_attributes << attr.to_s}
+ end
+
+ # returns the attributes where
+ def cached_attributes
+ @cached_attributes ||=
+ columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map(&:name).to_set
+ end
+
+ def cache_attribute?(attr_name)
+ cached_attributes.include?(attr_name)
+ end
+
private
# Suffixes a, ?, c become regexp /(a|\?|c)$/
def rebuild_attribute_method_regexp
@@ -54,9 +119,197 @@ module ActiveRecord
def attribute_method_suffixes
@@attribute_method_suffixes ||= []
end
+
+ # Define an attribute reader method. Cope with nil column.
+ def define_read_method(symbol, attr_name, column)
+ cast_code = column.type_cast_code('v') if column
+ access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
+
+ unless attr_name.to_s == self.primary_key.to_s
+ access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
+ end
+
+ if cache_attribute?(attr_name)
+ access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
+ end
+ evaluate_attribute_method attr_name, "def #{symbol}; #{access_code}; end"
+ end
+
+ # Define read method for serialized attribute.
+ def define_read_method_for_serialized_attribute(attr_name)
+ evaluate_attribute_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end"
+ end
+
+ # Define an attribute ? method.
+ def define_question_method(attr_name)
+ evaluate_attribute_method attr_name, "def #{attr_name}?; query_attribute('#{attr_name}'); end", "#{attr_name}?"
+ end
+
+ def define_write_method(attr_name)
+ evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}="
+ end
+
+ # Evaluate the definition for an attribute related method
+ def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
+
+ unless method_name.to_s == primary_key.to_s
+ generated_methods << method_name
+ end
+
+ begin
+ class_eval(method_definition, __FILE__, __LINE__)
+ rescue SyntaxError => err
+ generated_methods.delete(attr_name)
+ if logger
+ logger.warn "Exception occurred during reader method compilation."
+ logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
+ logger.warn "#{err.message}"
+ end
+ end
+ end
+ end # ClassMethods
+
+
+ # Allows access to the object attributes, which are held in the @attributes hash, as though they
+ # were first-class methods. So a Person class with a name attribute can use Person#name and
+ # Person#name= and never directly use the attributes hash -- except for multiple assigns with
+ # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
+ # the completed attribute is not nil or 0.
+ #
+ # It's also possible to instantiate related objects, so a Client class belonging to the clients
+ # table with a master_id foreign key can instantiate master through Client#master.
+ def method_missing(method_id, *args, &block)
+ method_name = method_id.to_s
+
+ # If we haven't generated any methods yet, generate them, then
+ # see if we've created the method we're looking for.
+ if !self.class.generated_methods?
+ self.class.define_attribute_methods
+ if self.class.generated_methods.include?(method_name)
+ return self.send(method_id, *args, &block)
+ end
+ end
+
+ if self.class.primary_key.to_s == method_name
+ id
+ elsif md = self.class.match_attribute_method?(method_name)
+ attribute_name, method_type = md.pre_match, md.to_s
+ if @attributes.include?(attribute_name)
+ __send__("attribute#{method_type}", attribute_name, *args, &block)
+ else
+ super
+ end
+ elsif @attributes.include?(method_name)
+ read_attribute(method_name)
+ else
+ super
+ end
end
+ # Returns the value of the attribute identified by attr_name after it has been typecast (for example,
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
+ def read_attribute(attr_name)
+ attr_name = attr_name.to_s
+ if !(value = @attributes[attr_name]).nil?
+ if column = column_for_attribute(attr_name)
+ if unserializable_attribute?(attr_name, column)
+ unserialize_attribute(attr_name)
+ else
+ column.type_cast(value)
+ end
+ else
+ value
+ end
+ else
+ nil
+ end
+ end
+
+ def read_attribute_before_type_cast(attr_name)
+ @attributes[attr_name]
+ end
+
+ # Returns true if the attribute is of a text column and marked for serialization.
+ def unserializable_attribute?(attr_name, column)
+ column.text? && self.class.serialized_attributes[attr_name]
+ end
+
+ # Returns the unserialized object of the attribute.
+ def unserialize_attribute(attr_name)
+ unserialized_object = object_from_yaml(@attributes[attr_name])
+
+ if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
+ @attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
+ else
+ raise SerializationTypeMismatch,
+ "#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
+ end
+ end
+
+
+ # Updates the attribute identified by attr_name with the specified +value+. Empty strings for fixnum and float
+ # columns are turned into nil.
+ def write_attribute(attr_name, value)
+ attr_name = attr_name.to_s
+ @attributes_cache.delete(attr_name)
+ if (column = column_for_attribute(attr_name)) && column.number?
+ @attributes[attr_name] = convert_number_column_value(value)
+ else
+ @attributes[attr_name] = value
+ end
+ end
+
+
+ def query_attribute(attr_name)
+ unless value = read_attribute(attr_name)
+ false
+ else
+ column = self.class.columns_hash[attr_name]
+ if column.nil?
+ if Numeric === value || value !~ /[^0-9]/
+ !value.to_i.zero?
+ else
+ !value.blank?
+ end
+ elsif column.number?
+ !value.zero?
+ else
+ !value.blank?
+ end
+ end
+ end
+
+ # A Person object with a name attribute can ask person.respond_to?("name"), person.respond_to?("name="), and
+ # person.respond_to?("name?") which will all return true.
+ alias :respond_to_without_attributes? :respond_to?
+ def respond_to?(method, include_priv = false)
+ method_name = method.to_s
+ if super
+ return true
+ elsif !self.class.generated_methods?
+ self.class.define_attribute_methods
+ if self.class.generated_methods.include?(method_name)
+ return true
+ end
+ end
+
+ if @attributes.nil?
+ return super
+ elsif @attributes.include?(method_name)
+ return true
+ elsif md = self.class.match_attribute_method?(method_name)
+ return true if @attributes.include?(md.pre_match)
+ end
+ super
+ end
+
+
private
+
+ def missing_attribute(attr_name, stack)
+ raise ActiveRecord::MissingAttributeError, "missing attribute: #{attr_name}", stack
+ end
+
# Handle *? for method_missing.
def attribute?(attribute_name)
query_attribute(attribute_name)
diff --git a/vendor/rails/activerecord/lib/active_record/base.rb b/vendor/rails/activerecord/lib/active_record/base.rb
index 6ddb1c7a..9941697f 100755
--- a/vendor/rails/activerecord/lib/active_record/base.rb
+++ b/vendor/rails/activerecord/lib/active_record/base.rb
@@ -1,40 +1,108 @@
require 'base64'
require 'yaml'
require 'set'
-require 'active_record/deprecated_finders'
module ActiveRecord #:nodoc:
- class ActiveRecordError < StandardError #:nodoc:
+ # Generic ActiveRecord exception class.
+ class ActiveRecordError < StandardError
end
+
+ # Raised when the single-table inheritance mechanism failes to locate the subclass
+ # (for example due to improper usage of column that +inheritance_column+ points to).
class SubclassNotFound < ActiveRecordError #:nodoc:
end
- class AssociationTypeMismatch < ActiveRecordError #:nodoc:
+
+ # Raised when object assigned to association is of incorrect type.
+ #
+ # Example:
+ #
+ # class Ticket < ActiveRecord::Base
+ # has_many :patches
+ # end
+ #
+ # class Patch < ActiveRecord::Base
+ # belongs_to :ticket
+ # end
+ #
+ # and somewhere in the code:
+ #
+ # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
+ # @ticket.save
+ class AssociationTypeMismatch < ActiveRecordError
end
- class SerializationTypeMismatch < ActiveRecordError #:nodoc:
+
+ # Raised when unserialized object's type mismatches one specified for serializable field.
+ class SerializationTypeMismatch < ActiveRecordError
end
- class AdapterNotSpecified < ActiveRecordError # :nodoc:
+
+ # Raised when adapter not specified on connection (or configuration file config/database.yml misses adapter field).
+ class AdapterNotSpecified < ActiveRecordError
end
- class AdapterNotFound < ActiveRecordError # :nodoc:
+
+ # Raised when ActiveRecord cannot find database adapter specified in config/database.yml or programmatically.
+ class AdapterNotFound < ActiveRecordError
end
- class ConnectionNotEstablished < ActiveRecordError #:nodoc:
+
+ # Raised when connection to the database could not been established (for example when connection= is given a nil object).
+ class ConnectionNotEstablished < ActiveRecordError
end
- class ConnectionFailed < ActiveRecordError #:nodoc:
+
+ # Raised when ActiveRecord cannot find record by given id or set of ids.
+ class RecordNotFound < ActiveRecordError
end
- class RecordNotFound < ActiveRecordError #:nodoc:
+
+ # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
+ # saved because record is invalid.
+ class RecordNotSaved < ActiveRecordError
end
- class RecordNotSaved < ActiveRecordError #:nodoc:
+
+ # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
+ class StatementInvalid < ActiveRecordError
end
- class StatementInvalid < ActiveRecordError #:nodoc:
+
+ # Raised when number of bind variables in statement given to :condition key (for example, when using +find+ method)
+ # does not match number of expected variables.
+ #
+ # Example:
+ #
+ # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
+ #
+ # in example above two placeholders are given but only one variable to fill them.
+ class PreparedStatementInvalid < ActiveRecordError
end
- class PreparedStatementInvalid < ActiveRecordError #:nodoc:
+
+ # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
+ # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
+ # the page before the other.
+ #
+ # Read more about optimistic locking in +ActiveRecord::Locking+ module RDoc.
+ class StaleObjectError < ActiveRecordError
end
- class StaleObjectError < ActiveRecordError #:nodoc:
+
+ # Raised when association is being configured improperly or
+ # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
+ class ConfigurationError < ActiveRecordError
end
- class ConfigurationError < StandardError #:nodoc:
+
+ # Raised on attempt to update record that is instantiated as read only.
+ class ReadOnlyRecord < ActiveRecordError
end
- class ReadOnlyRecord < StandardError #:nodoc:
+
+ # Used by ActiveRecord transaction mechanism to distinguish rollback from other exceptional situations.
+ # You can use it to roll your transaction back explicitly in the block passed to +transaction+ method.
+ class Rollback < ActiveRecordError
end
-
+
+ # Raised when attribute has a name reserved by ActiveRecord (when attribute has name of one of ActiveRecord instance methods).
+ class DangerousAttributeError < ActiveRecordError
+ end
+
+ # Raised when you've tried to access a column which wasn't
+ # loaded by your finder. Typically this is because :select
+ # has been specified
+ class MissingAttributeError < NoMethodError
+ end
+
class AttributeAssignmentError < ActiveRecordError #:nodoc:
attr_reader :exception, :attribute
def initialize(message, exception, attribute)
@@ -61,7 +129,7 @@ module ActiveRecord #:nodoc:
# == Creation
#
# Active Records accept constructor parameters either in a hash or as a block. The hash method is especially useful when
- # you're receiving the data from somewhere else, like a HTTP request. It works like this:
+ # you're receiving the data from somewhere else, like an HTTP request. It works like this:
#
# user = User.new(:name => "David", :occupation => "Code Artist")
# user.name # => "David"
@@ -101,15 +169,15 @@ module ActiveRecord #:nodoc:
# end
#
# The authenticate_unsafely method inserts the parameters directly into the query and is thus susceptible to SQL-injection
- # attacks if the user_name and +password+ parameters come directly from a HTTP request. The authenticate_safely and
- # authenticate_safely_simply both will sanitize the user_name and +password+ before inserting them in the query,
+ # attacks if the user_name and +password+ parameters come directly from an HTTP request. The authenticate_safely and
+ # authenticate_safely_simply both will sanitize the user_name and +password+ before inserting them in the query,
# which will ensure that an attacker can't escape the query and fake the login (or worse).
#
# When using multiple parameters in the conditions, it can easily become hard to read exactly what the fourth or fifth
# question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
# the question marks with symbols and supplying a hash with values for the matching symbol keys:
#
- # Company.find(:first, [
+ # Company.find(:first, :conditions => [
# "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
# { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
# ])
@@ -126,9 +194,9 @@ module ActiveRecord #:nodoc:
#
# == Overwriting default accessors
#
- # All column values are automatically available through basic accessors on the Active Record object, but some times you
- # want to specialize this behavior. This can be done by either by overwriting the default accessors (using the same
- # name as the attribute) calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things.
+ # All column values are automatically available through basic accessors on the Active Record object, but sometimes you
+ # want to specialize this behavior. This can be done by overwriting the default accessors (using the same
+ # name as the attribute) and calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things.
# Example:
#
# class Song < ActiveRecord::Base
@@ -143,9 +211,23 @@ module ActiveRecord #:nodoc:
# end
# end
#
- # You can alternatively use self[:attribute]=(value) and self[:attribute] instead of write_attribute(:attribute, vaule) and
+ # You can alternatively use self[:attribute]=(value) and self[:attribute] instead of write_attribute(:attribute, value) and
# read_attribute(:attribute) as a shorter form.
#
+ # == Attribute query methods
+ #
+ # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
+ # Query methods allow you to test whether an attribute value is present.
+ #
+ # For example, an Active Record User with the name attribute has a name? method that you can call
+ # to determine whether the user has a name:
+ #
+ # user = User.new(:name => "David")
+ # user.name? # => true
+ #
+ # anonymous = User.new(:name => "")
+ # anonymous.name? # => false
+ #
# == Accessing attributes before they have been typecasted
#
# Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first.
@@ -161,12 +243,12 @@ module ActiveRecord #:nodoc:
# Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
# appending the name of an attribute to find_by_ or find_all_by_ , so you get finders like Person.find_by_user_name,
# Person.find_all_by_last_name, Payment.find_by_transaction_id. So instead of writing
- # Person.find(:first, ["user_name = ?", user_name]) , you just do Person.find_by_user_name(user_name) .
- # And instead of writing Person.find(:all, ["last_name = ?", last_name]) , you just do Person.find_all_by_last_name(last_name) .
+ # Person.find(:first, :conditions => ["user_name = ?", user_name]) , you just do Person.find_by_user_name(user_name) .
+ # And instead of writing Person.find(:all, :conditions => ["last_name = ?", last_name]) , you just do Person.find_all_by_last_name(last_name) .
#
# It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
# Person.find_by_user_name_and_password or even Payment.find_by_purchaser_and_state_and_country . So instead of writing
- # Person.find(:first, ["user_name = ? AND password = ?", user_name, password]) , you just do
+ # Person.find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password]) , you just do
# Person.find_by_user_name_and_password(user_name, password) .
#
# It's even possible to use all the additional parameters to find. For example, the full interface for Payment.find_all_by_amount
@@ -178,7 +260,7 @@ module ActiveRecord #:nodoc:
#
# # No 'Summer' tag exists
# Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
- #
+ #
# # Now the 'Summer' tag does exist
# Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
#
@@ -188,6 +270,13 @@ module ActiveRecord #:nodoc:
# winter = Tag.find_or_initialize_by_name("Winter")
# winter.new_record? # true
#
+ # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
+ # a list of parameters. For example:
+ #
+ # Tag.find_or_create_by_name(:name => "rails", :creator => current_user)
+ #
+ # That will either find an existing tag named "rails", or create a new one while setting the user that created it.
+ #
# == Saving arrays, hashes, and other non-mappable objects in text columns
#
# Active Record can serialize any object in text columns using YAML. To do so, you must specify this with a call to the class method +serialize+.
@@ -212,7 +301,7 @@ module ActiveRecord #:nodoc:
#
# == Single table inheritance
#
- # Active Record allows inheritance by storing the name of the class in a column that by default is called "type" (can be changed
+ # Active Record allows inheritance by storing the name of the class in a column that by default is named "type" (can be changed
# by overwriting Base.inheritance_column ). This means that an inheritance looking like this:
#
# class Company < ActiveRecord::Base; end
@@ -233,7 +322,7 @@ module ActiveRecord #:nodoc:
#
# Connections are usually created through ActiveRecord::Base.establish_connection and retrieved by ActiveRecord::Base.connection.
# All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection.
- # For example, if Course is a ActiveRecord::Base, but resides in a different database you can just say Course.establish_connection
+ # For example, if Course is an ActiveRecord::Base, but resides in a different database, you can just say Course.establish_connection
# and Course *and all its subclasses* will use this connection instead.
#
# This feature is implemented by keeping a connection pool in ActiveRecord::Base that is a Hash indexed by the class. If a connection is
@@ -242,12 +331,12 @@ module ActiveRecord #:nodoc:
# == Exceptions
#
# * +ActiveRecordError+ -- generic error class and superclass of all other errors raised by Active Record
- # * +AdapterNotSpecified+ -- the configuration hash used in establish_connection didn't include a
+ # * +AdapterNotSpecified+ -- the configuration hash used in establish_connection didn't include an
# :adapter key.
- # * +AdapterNotFound+ -- the :adapter key used in establish_connection specified an non-existent adapter
+ # * +AdapterNotFound+ -- the :adapter key used in establish_connection specified a non-existent adapter
# (or a bad spelling of an existing one).
# * +AssociationTypeMismatch+ -- the object assigned to the association wasn't of the type specified in the association definition.
- # * +SerializationTypeMismatch+ -- the object serialized wasn't of the class specified as the second parameter.
+ # * +SerializationTypeMismatch+ -- the serialized object wasn't of the class specified as the second parameter.
# * +ConnectionNotEstablished+ -- no connection has been established. Use establish_connection before querying.
# * +RecordNotFound+ -- no record responded to the find* method.
# Either the row with the given ID doesn't exist or the row didn't meet the additional restrictions.
@@ -266,15 +355,13 @@ module ActiveRecord #:nodoc:
# Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
# on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
cattr_accessor :logger, :instance_writer => false
-
- include Reloadable::Deprecated
-
+
def self.inherited(child) #:nodoc:
@@subclasses[self] ||= []
@@subclasses[self] << child
super
end
-
+
def self.reset_subclasses #:nodoc:
nonreloadables = []
subclasses.each do |klass|
@@ -312,13 +399,13 @@ module ActiveRecord #:nodoc:
cattr_accessor :table_name_suffix, :instance_writer => false
@@table_name_suffix = ""
- # Indicates whether or not table names should be the pluralized versions of the corresponding class names.
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
# If true, the default table name for a +Product+ class will be +products+. If false, it would just be +product+.
# See table_name for the full rules on table/class naming. This is true, by default.
cattr_accessor :pluralize_table_names, :instance_writer => false
@@pluralize_table_names = true
- # Determines whether or not to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors
+ # Determines whether to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors
# make it much easier to overview things during debugging (when used through a reader like +tail+ and on a black background), but
# may complicate matters if you use software like syslog. This is true, by default.
cattr_accessor :colorize_logging, :instance_writer => false
@@ -329,21 +416,14 @@ module ActiveRecord #:nodoc:
cattr_accessor :default_timezone, :instance_writer => false
@@default_timezone = :local
- # Determines whether or not to use a connection for each thread, or a single shared connection for all threads.
+ # Determines whether to use a connection for each thread, or a single shared connection for all threads.
# Defaults to false. Set to true if you're writing a threaded application.
cattr_accessor :allow_concurrency, :instance_writer => false
@@allow_concurrency = false
- # Determines whether to speed up access by generating optimized reader
- # methods to avoid expensive calls to method_missing when accessing
- # attributes by name. You might want to set this to false in development
- # mode, because the methods would be regenerated on each request.
- cattr_accessor :generate_read_methods, :instance_writer => false
- @@generate_read_methods = true
-
# Specifies the format to use when dumping the database schema with Rails'
# Rakefile. If :sql, the schema is dumped as (potentially database-
- # specific) SQL statements. If :ruby, the schema is dumped as an
+ # specific) SQL statements. If :ruby, the schema is dumped as an
# ActiveRecord::Schema file which can be loaded into any database that
# supports migrations. Use :ruby if you want to have different database
# adapters for, e.g., your development and test environments.
@@ -356,25 +436,26 @@ module ActiveRecord #:nodoc:
# * Find by id: This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
# If no record can be found for all of the listed ids, then RecordNotFound will be raised.
# * Find first: This will return the first record matched by the options used. These options can either be specific
- # conditions or merely an order. If no record can matched, nil is returned.
+ # conditions or merely an order. If no record can be matched, nil is returned.
# * Find all: This will return all the records matched by the options used. If no records are found, an empty array is returned.
#
- # All approaches accept an option hash as their last parameter. The options are:
+ # All approaches accept an options hash as their last parameter. The options are:
#
# * :conditions : An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
# * :order : An SQL fragment like "created_at DESC, name".
# * :group : An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
# * :limit : An integer determining the limit on the number of rows that should be returned.
- # * :offset : An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
- # * :joins : An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
- # The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
+ # * :offset : An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4.
+ # * :joins : Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
+ # or named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s).
+ # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
# Pass :readonly => false to override.
# * :include : Names associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
# to already defined associations. See eager loading under Associations.
- # * :select : By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
+ # * :select : By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
# include the joined columns.
# * :from : By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
- # of a database view).
+ # of a database view).
# * :readonly : Mark the returned records read-only so they cannot be saved or updated.
# * :lock : An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
# :lock => true gives connection's default exclusive lock, usually "FOR UPDATE".
@@ -383,9 +464,13 @@ module ActiveRecord #:nodoc:
# Person.find(1) # returns the object for ID = 1
# Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
# Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
- # Person.find([1]) # returns an array for objects the object with ID = 1
+ # Person.find([1]) # returns an array for the object with ID = 1
# Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
#
+ # Note that returned records may not be in the same order as the ids you
+ # provide since database rows are unordered. Give an explicit :order
+ # to ensure the results are sorted.
+ #
# Examples for find first:
# Person.find(:first) # returns the first object fetched by SELECT * FROM people
# Person.find(:first, :conditions => [ "user_name = ?", user_name])
@@ -409,7 +494,7 @@ module ActiveRecord #:nodoc:
# person.save!
# end
def find(*args)
- options = extract_options_from_args!(args)
+ options = args.extract_options!
validate_find_options(options)
set_readonly_option!(options)
@@ -419,51 +504,91 @@ module ActiveRecord #:nodoc:
else find_from_ids(args, options)
end
end
-
- # Works like find(:all), but requires a complete SQL string. Examples:
- # Post.find_by_sql "SELECT p.*, c.author FROM posts p, comments c WHERE p.id = c.post_id"
- # Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
+
+ #
+ # Executes a custom sql query against your database and returns all the results. The results will
+ # be returned as an array with columns requested encapsulated as attributes of the model you call
+ # this method from. If you call +Product.find_by_sql+ then the results will be returned in a Product
+ # object with the attributes you specified in the SQL query.
+ #
+ # If you call a complicated SQL query which spans multiple tables the columns specified by the
+ # SELECT will be attributes of the model, whether or not they are columns of the corresponding
+ # table.
+ #
+ # The +sql+ parameter is a full sql query as a string. It will be called as is, there will be
+ # no database agnostic conversions performed. This should be a last resort because using, for example,
+ # MySQL specific terms will lock you to using that particular database engine or require you to
+ # change your call if you switch engines
+ #
+ # ==== Examples
+ # # A simple sql query spanning multiple tables
+ # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
+ # > [#"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
+ #
+ # # You can use the same string replacement techniques as you can with ActiveRecord#find
+ # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
+ # > [#"The Cheap Man Buys Twice"}>, ...]
def find_by_sql(sql)
connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
end
- # Returns true if the given +id+ represents the primary key of a record in the database, false otherwise.
- # You can also pass a set of SQL conditions.
- # Example:
+ # Checks whether a record exists in the database that matches conditions given. These conditions
+ # can either be a single integer representing a primary key id to be found, or a condition to be
+ # matched like using ActiveRecord#find.
+ #
+ # The +id_or_conditions+ parameter can be an Integer or a String if you want to search the primary key
+ # column of the table for a matching id, or if you're looking to match against a condition you can use
+ # an Array or a Hash.
+ #
+ # Possible gotcha: You can't pass in a condition as a string e.g. "name = 'Jamie'", this would be
+ # sanitized and then queried against the primary key column as "id = 'name = \'Jamie"
+ #
+ # ==== Examples
# Person.exists?(5)
# Person.exists?('5')
# Person.exists?(:name => "David")
# Person.exists?(['name LIKE ?', "%#{query}%"])
def exists?(id_or_conditions)
- !find(:first, :conditions => expand_id_conditions(id_or_conditions)).nil?
- rescue ActiveRecord::ActiveRecordError
- false
+ !find(:first, :select => "#{quoted_table_name}.#{primary_key}",
+ :conditions => expand_id_conditions(id_or_conditions)).nil?
end
- # Creates an object, instantly saves it as a record (if the validation permits it), and returns it. If the save
- # fails under validations, the unsaved object is still returned.
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
+ # The resulting object is returned whether the object was saved successfully to the database or not.
+ #
+ # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
+ # attributes on the objects that are to be created.
+ #
+ # ==== Examples
+ # # Create a single new object
+ # User.create(:first_name => 'Jamie')
+ # # Create an Array of new objects
+ # User.create([{:first_name => 'Jamie'}, {:first_name => 'Jeremy'}])
def create(attributes = nil)
if attributes.is_a?(Array)
attributes.collect { |attr| create(attr) }
else
object = new(attributes)
- scope(:create).each { |att,value| object.send("#{att}=", value) } if scoped?(:create)
object.save
object
end
end
- # Finds the record from the passed +id+, instantly saves it with the passed +attributes+ (if the validation permits it),
- # and returns it. If the save fails under validations, the unsaved object is still returned.
+ # Updates an object (or multiple objects) and saves it to the database, if validations pass.
+ # The resulting object is returned whether the object was saved successfully to the database or not.
#
- # The arguments may also be given as arrays in which case the update method is called for each pair of +id+ and
- # +attributes+ and an array of objects is returned.
+ # ==== Options
#
- # Example of updating one record:
+ # +id+ This should be the id or an array of ids to be updated
+ # +attributes+ This should be a Hash of attributes to be set on the object, or an array of Hashes.
+ #
+ # ==== Examples
+ #
+ # # Updating one record:
# Person.update(15, {:user_name => 'Samuel', :group => 'expert'})
- #
- # Example of updating multiple records:
- # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
+ #
+ # # Updating multiple records:
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
# Person.update(people.keys, people.values)
def update(id, attributes)
if id.is_a?(Array)
@@ -476,62 +601,201 @@ module ActiveRecord #:nodoc:
end
end
- # Deletes the record with the given +id+ without instantiating an object first. If an array of ids is provided, all of them
- # are deleted.
+ # Delete an object (or multiple objects) where the +id+ given matches the primary_key. A SQL +DELETE+ command
+ # is executed on the database which means that no callbacks are fired off running this. This is an efficient method
+ # of deleting records that don't need cleaning up after or other actions to be taken.
+ #
+ # Objects are _not_ instantiated with this method.
+ #
+ # ==== Options
+ #
+ # +id+ Can be either an Integer or an Array of Integers
+ #
+ # ==== Examples
+ #
+ # # Delete a single object
+ # Todo.delete(1)
+ #
+ # # Delete multiple objects
+ # todos = [1,2,3]
+ # Todo.delete(todos)
def delete(id)
delete_all([ "#{connection.quote_column_name(primary_key)} IN (?)", id ])
end
- # Destroys the record with the given +id+ by instantiating the object and calling #destroy (all the callbacks are the triggered).
- # If an array of ids is provided, all of them are destroyed.
+ # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
+ # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
+ #
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
+ # from the attributes, and then calls destroy on it.
+ #
+ # ==== Options
+ #
+ # +id+ Can be either an Integer or an Array of Integers
+ #
+ # ==== Examples
+ #
+ # # Destroy a single object
+ # Todo.destroy(1)
+ #
+ # # Destroy multiple objects
+ # todos = [1,2,3]
+ # Todo.destroy(todos)
def destroy(id)
id.is_a?(Array) ? id.each { |id| destroy(id) } : find(id).destroy
end
- # Updates all records with the SET-part of an SQL update statement in +updates+ and returns an integer with the number of rows updated.
- # A subset of the records can be selected by specifying +conditions+. Example:
- # Billing.update_all "category = 'authorized', approved = 1", "author = 'David'"
- def update_all(updates, conditions = nil)
- sql = "UPDATE #{table_name} SET #{sanitize_sql(updates)} "
- add_conditions!(sql, conditions, scope(:find))
+ # Updates all records with details given if they match a set of conditions supplied, limits and order can
+ # also be supplied.
+ #
+ # ==== Options
+ #
+ # +updates+ A String of column and value pairs that will be set on any records that match conditions
+ # +conditions+ An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
+ # See conditions in the intro for more info.
+ # +options+ Additional options are :limit and/or :order, see the examples for usage.
+ #
+ # ==== Examples
+ #
+ # # Update all billing objects with the 3 different attributes given
+ # Billing.update_all( "category = 'authorized', approved = 1, author = 'David'" )
+ #
+ # # Update records that match our conditions
+ # Billing.update_all( "author = 'David'", "title LIKE '%Rails%'" )
+ #
+ # # Update records that match our conditions but limit it to 5 ordered by date
+ # Billing.update_all( "author = 'David'", "title LIKE '%Rails%'",
+ # :order => 'created_at', :limit => 5 )
+ def update_all(updates, conditions = nil, options = {})
+ sql = "UPDATE #{table_name} SET #{sanitize_sql_for_assignment(updates)} "
+ scope = scope(:find)
+ add_conditions!(sql, conditions, scope)
+ add_order!(sql, options[:order], scope)
+ add_limit!(sql, options, scope)
connection.update(sql, "#{name} Update")
end
- # Destroys the objects for all the records that match the +condition+ by instantiating each object and calling
- # the destroy method. Example:
+ # Destroys the records matching +conditions+ by instantiating each record and calling the destroy method.
+ # This means at least 2*N database queries to destroy N records, so avoid destroy_all if you are deleting
+ # many records. If you want to simply delete records without worrying about dependent associations or
+ # callbacks, use the much faster +delete_all+ method instead.
+ #
+ # ==== Options
+ #
+ # +conditions+ Conditions are specified the same way as with +find+ method.
+ #
+ # ==== Example
+ #
# Person.destroy_all "last_login < '2004-04-04'"
+ #
+ # This loads and destroys each person one by one, including its dependent associations and before_ and
+ # after_destroy callbacks.
def destroy_all(conditions = nil)
find(:all, :conditions => conditions).each { |object| object.destroy }
end
- # Deletes all the records that match the +condition+ without instantiating the objects first (and hence not
- # calling the destroy method). Example:
+ # Deletes the records matching +conditions+ without instantiating the records first, and hence not
+ # calling the destroy method and invoking callbacks. This is a single SQL query, much more efficient
+ # than destroy_all.
+ #
+ # ==== Options
+ #
+ # +conditions+ Conditions are specified the same way as with +find+ method.
+ #
+ # ==== Example
+ #
# Post.delete_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
+ #
+ # This deletes the affected posts all at once with a single DELETE query. If you need to destroy dependent
+ # associations or call your before_ or after_destroy callbacks, use the +destroy_all+ method instead.
def delete_all(conditions = nil)
- sql = "DELETE FROM #{table_name} "
+ sql = "DELETE FROM #{quoted_table_name} "
add_conditions!(sql, conditions, scope(:find))
connection.delete(sql, "#{name} Delete all")
end
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
+ # The use of this method should be restricted to complicated SQL queries that can't be executed
+ # using the ActiveRecord::Calculations class methods. Look into those before using this.
+ #
+ # ==== Options
+ #
+ # +sql+: An SQL statement which should return a count query from the database, see the example below
+ #
+ # ==== Examples
+ #
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
def count_by_sql(sql)
sql = sanitize_conditions(sql)
connection.select_value(sql, "#{name} Count").to_i
end
- # Increments the specified counter by one. So DiscussionBoard.increment_counter("post_count",
- # discussion_board_id) would increment the "post_count" counter on the board responding to discussion_board_id.
- # This is used for caching aggregate values, so that they don't need to be computed every time. Especially important
- # for looping over a collection where each element require a number of aggregate values. Like the DiscussionBoard
- # that needs to list both the number of posts and comments.
- def increment_counter(counter_name, id)
- update_all "#{connection.quote_column_name(counter_name)} = #{connection.quote_column_name(counter_name)} + 1", "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}"
+ # A generic "counter updater" implementation, intended primarily to be
+ # used by increment_counter and decrement_counter, but which may also
+ # be useful on its own. It simply does a direct SQL update for the record
+ # with the given ID, altering the given hash of counters by the amount
+ # given by the corresponding value:
+ #
+ # ==== Options
+ #
+ # +id+ The id of the object you wish to update a counter on
+ # +counters+ An Array of Hashes containing the names of the fields
+ # to update as keys and the amount to update the field by as
+ # values
+ #
+ # ==== Examples
+ #
+ # # For the Post with id of 5, decrement the comment_count by 1, and
+ # # increment the action_count by 1
+ # Post.update_counters 5, :comment_count => -1, :action_count => 1
+ # # Executes the following SQL:
+ # # UPDATE posts
+ # # SET comment_count = comment_count - 1,
+ # # action_count = action_count + 1
+ # # WHERE id = 5
+ def update_counters(id, counters)
+ updates = counters.inject([]) { |list, (counter_name, increment)|
+ sign = increment < 0 ? "-" : "+"
+ list << "#{connection.quote_column_name(counter_name)} = #{connection.quote_column_name(counter_name)} #{sign} #{increment.abs}"
+ }.join(", ")
+ update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
end
- # Works like increment_counter, but decrements instead.
+ # Increment a number field by one, usually representing a count.
+ #
+ # This is used for caching aggregate values, so that they don't need to be computed every time.
+ # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
+ # shown it would have to run an SQL query to find how many posts and comments there are.
+ #
+ # ==== Options
+ #
+ # +counter_name+ The name of the field that should be incremented
+ # +id+ The id of the object that should be incremented
+ #
+ # ==== Examples
+ #
+ # # Increment the post_count column for the record with an id of 5
+ # DiscussionBoard.increment_counter(:post_count, 5)
+ def increment_counter(counter_name, id)
+ update_counters(id, counter_name => 1)
+ end
+
+ # Decrement a number field by one, usually representing a count.
+ #
+ # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
+ #
+ # ==== Options
+ #
+ # +counter_name+ The name of the field that should be decremented
+ # +id+ The id of the object that should be decremented
+ #
+ # ==== Examples
+ #
+ # # Decrement the post_count column for the record with an id of 5
+ # DiscussionBoard.decrement_counter(:post_count, 5)
def decrement_counter(counter_name, id)
- update_all "#{connection.quote_column_name(counter_name)} = #{connection.quote_column_name(counter_name)} - 1", "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}"
+ update_counters(id, counter_name => -1)
end
@@ -550,8 +814,10 @@ module ActiveRecord #:nodoc:
#
# customer.credit_rating = "Average"
# customer.credit_rating # => "Average"
+ #
+ # To start from an all-closed default and enable attributes as needed, have a look at attr_accessible.
def attr_protected(*attributes)
- write_inheritable_array("attr_protected", attributes - (protected_attributes || []))
+ write_inheritable_attribute("attr_protected", Set.new(attributes.map(&:to_s)) + (protected_attributes || []))
end
# Returns an array of all the attributes that have been protected from mass-assignment.
@@ -559,12 +825,33 @@ module ActiveRecord #:nodoc:
read_inheritable_attribute("attr_protected")
end
- # If this macro is used, only those attributes named in it will be accessible for mass-assignment, such as
- # new(attributes) and attributes=(attributes) . This is the more conservative choice for mass-assignment
- # protection. If you'd rather start from an all-open default and restrict attributes as needed, have a look at
- # attr_protected.
+ # Similar to the attr_protected macro, this protects attributes of your model from mass-assignment,
+ # such as new(attributes) and attributes=(attributes)
+ # however, it does it in the opposite way. This locks all attributes and only allows access to the
+ # attributes specified. Assignment to attributes not in this list will be ignored and need to be set
+ # using the direct writer methods instead. This is meant to protect sensitive attributes from being
+ # overwritten by URL/form hackers. If you'd rather start from an all-open default and restrict
+ # attributes as needed, have a look at attr_protected.
+ #
+ # ==== Options
+ #
+ # *attributes A comma separated list of symbols that represent columns _not_ to be protected
+ #
+ # ==== Examples
+ #
+ # class Customer < ActiveRecord::Base
+ # attr_accessible :name, :nickname
+ # end
+ #
+ # customer = Customer.new(:name => "David", :nickname => "Dave", :credit_rating => "Excellent")
+ # customer.credit_rating # => nil
+ # customer.attributes = { :name => "Jolly fellow", :credit_rating => "Superb" }
+ # customer.credit_rating # => nil
+ #
+ # customer.credit_rating = "Average"
+ # customer.credit_rating # => "Average"
def attr_accessible(*attributes)
- write_inheritable_array("attr_accessible", attributes - (accessible_attributes || []))
+ write_inheritable_attribute("attr_accessible", Set.new(attributes.map(&:to_s)) + (accessible_attributes || []))
end
# Returns an array of all the attributes that have been made accessible to mass-assignment.
@@ -572,10 +859,31 @@ module ActiveRecord #:nodoc:
read_inheritable_attribute("attr_accessible")
end
+ # Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.
+ def attr_readonly(*attributes)
+ write_inheritable_attribute("attr_readonly", Set.new(attributes.map(&:to_s)) + (readonly_attributes || []))
+ end
- # Specifies that the attribute by the name of +attr_name+ should be serialized before saving to the database and unserialized
- # after loading from the database. The serialization is done through YAML. If +class_name+ is specified, the serialized
- # object must be of that class on retrieval, or nil. Otherwise, +SerializationTypeMismatch+ will be raised.
+ # Returns an array of all the attributes that have been specified as readonly.
+ def readonly_attributes
+ read_inheritable_attribute("attr_readonly")
+ end
+
+ # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
+ # then specify the name of that attribute using this method and it will be handled automatically.
+ # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
+ # class on retrieval or +SerializationTypeMismatch+ will be raised.
+ #
+ # ==== Options
+ #
+ # +attr_name+ The field name that should be serialized
+ # +class_name+ Optional, class name that the object type should be equal to
+ #
+ # ==== Example
+ # # Serialize a preferences attribute
+ # class User
+ # serialize :preferences
+ # end
def serialize(attr_name, class_name = Object)
serialized_attributes[attr_name.to_s] = class_name
end
@@ -588,14 +896,23 @@ module ActiveRecord #:nodoc:
# Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
# directly from ActiveRecord. So if the hierarchy looks like: Reply < Message < ActiveRecord, then Message is used
- # to guess the table name from even when called on Reply. The rules used to do the guess are handled by the Inflector class
- # in Active Support, which knows almost all common English inflections (report a bug if your inflection isn't covered).
+ # to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class
+ # in Active Support, which knows almost all common English inflections. You can add new inflections in config/initializers/inflections.rb.
#
# Nested classes are given table names prefixed by the singular form of
- # the parent's table name. Example:
+ # the parent's table name. Enclosing modules are not considered. Examples:
+ #
+ # class Invoice < ActiveRecord::Base; end;
# file class table_name
# invoice.rb Invoice invoices
- # invoice/lineitem.rb Invoice::Lineitem invoice_lineitems
+ #
+ # class Invoice < ActiveRecord::Base; class Lineitem < ActiveRecord::Base; end; end;
+ # file class table_name
+ # invoice.rb Invoice::Lineitem invoice_lineitems
+ #
+ # module Invoice; class Lineitem < ActiveRecord::Base; end; end;
+ # file class table_name
+ # invoice/lineitem.rb Invoice::Lineitem lineitems
#
# Additionally, the class-level table_name_prefix is prepended and the
# table_name_suffix is appended. So if you have "myapp_" as a prefix,
@@ -753,7 +1070,7 @@ module ActiveRecord #:nodoc:
columns.size > 0
rescue ActiveRecord::StatementInvalid
false
- end
+ end
end
end
@@ -796,15 +1113,10 @@ module ActiveRecord #:nodoc:
end
end
- # Contains the names of the generated reader methods.
- def read_methods #:nodoc:
- @read_methods ||= Set.new
- end
-
# Resets all the cached information about columns, which will cause them to be reloaded on the next request.
def reset_column_information
- read_methods.each { |name| undef_method(name) }
- @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = @inheritance_column = nil
+ generated_methods.each { |name| undef_method(name) }
+ @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @generated_methods = @inheritance_column = nil
end
def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
@@ -818,8 +1130,32 @@ module ActiveRecord #:nodoc:
attribute_key_name.humanize
end
- def descends_from_active_record? # :nodoc:
- superclass == Base || !columns_hash.include?(inheritance_column)
+ # True if this isn't a concrete subclass needing a STI type condition.
+ def descends_from_active_record?
+ if superclass.abstract_class?
+ superclass.descends_from_active_record?
+ else
+ superclass == Base || !columns_hash.include?(inheritance_column)
+ end
+ end
+
+ def finder_needs_type_condition? #:nodoc:
+ # This is like this because benchmarking justifies the strange :false stuff
+ :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
+ end
+
+ # Returns a string like 'Post id:integer, title:string, body:text'
+ def inspect
+ if self == Base
+ super
+ elsif abstract_class?
+ "#{super}(abstract)"
+ elsif table_exists?
+ attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
+ "#{super}(#{attr_list})"
+ else
+ "#{super}(Table doesn't exist)"
+ end
end
@@ -827,12 +1163,7 @@ module ActiveRecord #:nodoc:
connection.quote(value,column)
end
- def quote(value, column = nil) #:nodoc:
- connection.quote(value, column)
- end
- deprecate :quote => :quote_value
-
- # Used to sanitize objects before they're used in an SELECT SQL-statement. Delegates to connection.quote .
+ # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to connection.quote .
def sanitize(object) #:nodoc:
connection.quote(object)
end
@@ -869,104 +1200,9 @@ module ActiveRecord #:nodoc:
logger.level = old_logger_level if logger
end
- # Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
- # method_name may be :find or :create. :find parameters may include the :conditions , :joins ,
- # :include , :offset , :limit , and :readonly options. :create parameters are an attributes hash.
- #
- # Article.with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
- # Article.find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
- # a = Article.create(1)
- # a.blog_id # => 1
- # end
- #
- # In nested scopings, all previous parameters are overwritten by inner rule
- # except :conditions in :find, that are merged as hash.
- #
- # Article.with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
- # Article.with_scope(:find => { :limit => 10})
- # Article.find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
- # end
- # Article.with_scope(:find => { :conditions => "author_id = 3" })
- # Article.find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
- # end
- # end
- #
- # You can ignore any previous scopings by using with_exclusive_scope method.
- #
- # Article.with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
- # Article.with_exclusive_scope(:find => { :limit => 10 })
- # Article.find(:all) # => SELECT * from articles LIMIT 10
- # end
- # end
- def with_scope(method_scoping = {}, action = :merge, &block)
- method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
-
- # Dup first and second level of hash (method and params).
- method_scoping = method_scoping.inject({}) do |hash, (method, params)|
- hash[method] = (params == true) ? params : params.dup
- hash
- end
-
- method_scoping.assert_valid_keys([ :find, :create ])
-
- if f = method_scoping[:find]
- f.assert_valid_keys([ :conditions, :joins, :select, :include, :from, :offset, :limit, :order, :readonly, :lock ])
- f[:readonly] = true if !f[:joins].blank? && !f.has_key?(:readonly)
- end
-
- # Merge scopings
- if action == :merge && current_scoped_methods
- method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
- case hash[method]
- when Hash
- if method == :find
- (hash[method].keys + params.keys).uniq.each do |key|
- merge = hash[method][key] && params[key] # merge if both scopes have the same key
- if key == :conditions && merge
- hash[method][key] = [params[key], hash[method][key]].collect{ |sql| "( %s )" % sanitize_sql(sql) }.join(" AND ")
- elsif key == :include && merge
- hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
- else
- hash[method][key] = hash[method][key] || params[key]
- end
- end
- else
- hash[method] = params.merge(hash[method])
- end
- else
- hash[method] = params
- end
- hash
- end
- end
-
- self.scoped_methods << method_scoping
-
- begin
- yield
- ensure
- self.scoped_methods.pop
- end
- end
-
- # Works like with_scope, but discards any nested properties.
- def with_exclusive_scope(method_scoping = {}, &block)
- with_scope(method_scoping, :overwrite, &block)
- end
-
# Overwrite the default class equality method to provide support for association proxies.
def ===(object)
object.is_a?(self)
- end
-
- # Deprecated
- def threaded_connections #:nodoc:
- allow_concurrency
- end
-
- # Deprecated
- def threaded_connections=(value) #:nodoc:
- self.allow_concurrency = value
end
# Returns the base AR subclass that this class descends from. If A
@@ -993,7 +1229,7 @@ module ActiveRecord #:nodoc:
def find_every(options)
records = scoped?(:find, :include) || options[:include] ?
- find_with_associations(options) :
+ find_with_associations(options) :
find_by_sql(construct_finder_sql(options))
records.each { |record| record.readonly! } if options[:readonly]
@@ -1017,10 +1253,10 @@ module ActiveRecord #:nodoc:
find_some(ids, options)
end
end
-
+
def find_one(id, options)
conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
- options.update :conditions => "#{table_name}.#{connection.quote_column_name(primary_key)} = #{quote_value(id,columns_hash[primary_key])}#{conditions}"
+ options.update :conditions => "#{quoted_table_name}.#{connection.quote_column_name(primary_key)} = #{quote_value(id,columns_hash[primary_key])}#{conditions}"
# Use find_every(options).first since the primary key condition
# already ensures we have a single record. Using find_initial adds
@@ -1031,18 +1267,31 @@ module ActiveRecord #:nodoc:
raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}"
end
end
-
+
def find_some(ids, options)
conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
ids_list = ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')
- options.update :conditions => "#{table_name}.#{connection.quote_column_name(primary_key)} IN (#{ids_list})#{conditions}"
+ options.update :conditions => "#{quoted_table_name}.#{connection.quote_column_name(primary_key)} IN (#{ids_list})#{conditions}"
result = find_every(options)
- if result.size == ids.size
+ # Determine expected size from limit and offset, not just ids.size.
+ expected_size =
+ if options[:limit] && ids.size > options[:limit]
+ options[:limit]
+ else
+ ids.size
+ end
+
+ # 11 ids with limit 3, offset 9 should give 2 results.
+ if options[:offset] && (ids.size - options[:offset] < expected_size)
+ expected_size = ids.size - options[:offset]
+ end
+
+ if result.size == expected_size
result
else
- raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions}"
+ raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
end
end
@@ -1052,9 +1301,10 @@ module ActiveRecord #:nodoc:
def instantiate(record)
object =
if subclass_name = record[inheritance_column]
+ # No type given.
if subclass_name.empty?
- # No type given.
allocate
+
else
# Ignore type if no column is present since it was probably
# pulled in from a sloppy join.
@@ -1078,6 +1328,16 @@ module ActiveRecord #:nodoc:
end
object.instance_variable_set("@attributes", record)
+ object.instance_variable_set("@attributes_cache", Hash.new)
+
+ if object.respond_to_without_attributes?(:after_find)
+ object.send(:callback, :after_find)
+ end
+
+ if object.respond_to_without_attributes?(:after_initialize)
+ object.send(:callback, :after_initialize)
+ end
+
object
end
@@ -1089,14 +1349,13 @@ module ActiveRecord #:nodoc:
def construct_finder_sql(options)
scope = scope(:find)
- sql = "SELECT #{(scope && scope[:select]) || options[:select] || '*'} "
- sql << "FROM #{(scope && scope[:from]) || options[:from] || table_name} "
+ sql = "SELECT #{(scope && scope[:select]) || options[:select] || (options[:joins] && quoted_table_name + '.*') || '*'} "
+ sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
add_joins!(sql, options, scope)
add_conditions!(sql, options[:conditions], scope)
- sql << " GROUP BY #{options[:group]} " if options[:group]
-
+ add_group!(sql, options[:group], scope)
add_order!(sql, options[:order], scope)
add_limit!(sql, options, scope)
add_lock!(sql, options, scope)
@@ -1132,10 +1391,26 @@ module ActiveRecord #:nodoc:
end
end
+ def add_group!(sql, group, scope = :auto)
+ if group
+ sql << " GROUP BY #{group}"
+ else
+ scope = scope(:find) if :auto == scope
+ if scope && (scoped_group = scope[:group])
+ sql << " GROUP BY #{scoped_group}"
+ end
+ end
+ end
+
# The optional scope argument is for the current :find scope.
def add_limit!(sql, options, scope = :auto)
scope = scope(:find) if :auto == scope
- options = options.reverse_merge(:limit => scope[:limit], :offset => scope[:offset]) if scope
+
+ if scope
+ options[:limit] ||= scope[:limit]
+ options[:offset] ||= scope[:offset]
+ end
+
connection.add_limit_offset!(sql, options)
end
@@ -1151,7 +1426,13 @@ module ActiveRecord #:nodoc:
def add_joins!(sql, options, scope = :auto)
scope = scope(:find) if :auto == scope
join = (scope && scope[:joins]) || options[:joins]
- sql << " #{join} " if join
+ case join
+ when Symbol, Hash, Array
+ join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, join, nil)
+ sql << " #{join_dependency.join_associations.collect{|join| join.association_join }.join} "
+ else
+ sql << " #{join} "
+ end
end
# Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
@@ -1159,17 +1440,17 @@ module ActiveRecord #:nodoc:
def add_conditions!(sql, conditions, scope = :auto)
scope = scope(:find) if :auto == scope
segments = []
- segments << sanitize_sql(scope[:conditions]) if scope && scope[:conditions]
- segments << sanitize_sql(conditions) unless conditions.nil?
- segments << type_condition unless descends_from_active_record?
- segments.compact!
+ segments << sanitize_sql(scope[:conditions]) if scope && !scope[:conditions].blank?
+ segments << sanitize_sql(conditions) unless conditions.blank?
+ segments << type_condition if finder_needs_type_condition?
+ segments.delete_if{|s| s.blank?}
sql << "WHERE (#{segments.join(") AND (")}) " unless segments.empty?
end
def type_condition
quoted_inheritance_column = connection.quote_column_name(inheritance_column)
- type_condition = subclasses.inject("#{table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
- condition << "OR #{table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' "
+ type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
+ condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' "
end
" (#{type_condition}) "
@@ -1184,56 +1465,69 @@ module ActiveRecord #:nodoc:
# Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
# find(:first, :conditions => ["user_name = ?", user_name]) and find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])
- # respectively. Also works for find(:all), but using find_all_by_amount(50) that are turned into find(:all, :conditions => ["amount = ?", 50]).
+ # respectively. Also works for find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions => ["amount = ?", 50]).
#
# It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
# is actually find_all_by_amount(amount, options).
#
- # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
+ # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
# or find_or_create_by_user_and_password(user, password).
+ #
+ # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
+ # attempts to use it do not run through method_missing.
def method_missing(method_id, *arguments)
if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(method_id.to_s)
- finder, deprecated_finder = determine_finder(match), determine_deprecated_finder(match)
+ finder = determine_finder(match)
attribute_names = extract_attribute_names_from_match(match)
super unless all_attributes_exists?(attribute_names)
- attributes = construct_attributes_from_arguments(attribute_names, arguments)
-
- case extra_options = arguments[attribute_names.size]
- when nil
- options = { :conditions => attributes }
+ self.class_eval %{
+ def self.#{method_id}(*args)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
+ finder_options = { :conditions => attributes }
+ validate_find_options(options)
set_readonly_option!(options)
- ActiveSupport::Deprecation.silence { send(finder, options) }
- when Hash
- finder_options = extra_options.merge(:conditions => attributes)
- validate_find_options(finder_options)
- set_readonly_option!(finder_options)
-
- if extra_options[:conditions]
- with_scope(:find => { :conditions => extra_options[:conditions] }) do
- ActiveSupport::Deprecation.silence { send(finder, finder_options) }
+ if options[:conditions]
+ with_scope(:find => finder_options) do
+ ActiveSupport::Deprecation.silence { send(:#{finder}, options) }
end
else
- ActiveSupport::Deprecation.silence { send(finder, finder_options) }
+ ActiveSupport::Deprecation.silence { send(:#{finder}, options.merge(finder_options)) }
end
-
- else
- ActiveSupport::Deprecation.silence do
- send(deprecated_finder, sanitize_sql(attributes), *arguments[attribute_names.length..-1])
- end
- end
+ end
+ }, __FILE__, __LINE__
+ send(method_id, *arguments)
elsif match = /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/.match(method_id.to_s)
instantiator = determine_instantiator(match)
attribute_names = extract_attribute_names_from_match(match)
super unless all_attributes_exists?(attribute_names)
- attributes = construct_attributes_from_arguments(attribute_names, arguments)
- options = { :conditions => attributes }
- set_readonly_option!(options)
+ self.class_eval %{
+ def self.#{method_id}(*args)
+ if args[0].is_a?(Hash)
+ attributes = args[0].with_indifferent_access
+ find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}])
+ else
+ find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
+ end
- find_initial(options) || send(instantiator, attributes)
+ options = { :conditions => find_attributes }
+ set_readonly_option!(options)
+
+ record = find_initial(options)
+ if record.nil?
+ record = self.new { |r| r.send(:attributes=, attributes, false) }
+ #{'record.save' if instantiator == :create}
+ record
+ else
+ record
+ end
+ end
+ }, __FILE__, __LINE__
+ send(method_id, *arguments)
else
super
end
@@ -1243,10 +1537,6 @@ module ActiveRecord #:nodoc:
match.captures.first == 'all_by' ? :find_every : :find_initial
end
- def determine_deprecated_finder(match)
- match.captures.first == 'all_by' ? :find_all : :find_first
- end
-
def determine_instantiator(match)
match.captures.first == 'initialize' ? :new : :create
end
@@ -1268,7 +1558,7 @@ module ActiveRecord #:nodoc:
def attribute_condition(argument)
case argument
when nil then "IS ?"
- when Array then "IN (?)"
+ when Array, ActiveRecord::Associations::AssociationCollection then "IN (?)"
when Range then "BETWEEN ? AND ?"
else "= ?"
end
@@ -1314,6 +1604,103 @@ module ActiveRecord #:nodoc:
end
protected
+ # Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
+ # method_name may be :find or :create. :find parameters may include the :conditions , :joins ,
+ # :include , :offset , :limit , and :readonly options. :create parameters are an attributes hash.
+ #
+ # class Article < ActiveRecord::Base
+ # def self.create_with_scope
+ # with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
+ # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
+ # a = create(1)
+ # a.blog_id # => 1
+ # end
+ # end
+ # end
+ #
+ # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
+ # :conditions and :include options in :find, which are merged.
+ #
+ # class Article < ActiveRecord::Base
+ # def self.find_with_scope
+ # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
+ # with_scope(:find => { :limit => 10})
+ # find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
+ # end
+ # with_scope(:find => { :conditions => "author_id = 3" })
+ # find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
+ # end
+ # end
+ # end
+ # end
+ #
+ # You can ignore any previous scopings by using the with_exclusive_scope method.
+ #
+ # class Article < ActiveRecord::Base
+ # def self.find_with_exclusive_scope
+ # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
+ # with_exclusive_scope(:find => { :limit => 10 })
+ # find(:all) # => SELECT * from articles LIMIT 10
+ # end
+ # end
+ # end
+ # end
+ def with_scope(method_scoping = {}, action = :merge, &block)
+ method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
+
+ # Dup first and second level of hash (method and params).
+ method_scoping = method_scoping.inject({}) do |hash, (method, params)|
+ hash[method] = (params == true) ? params : params.dup
+ hash
+ end
+
+ method_scoping.assert_valid_keys([ :find, :create ])
+
+ if f = method_scoping[:find]
+ f.assert_valid_keys(VALID_FIND_OPTIONS)
+ set_readonly_option! f
+ end
+
+ # Merge scopings
+ if action == :merge && current_scoped_methods
+ method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
+ case hash[method]
+ when Hash
+ if method == :find
+ (hash[method].keys + params.keys).uniq.each do |key|
+ merge = hash[method][key] && params[key] # merge if both scopes have the same key
+ if key == :conditions && merge
+ hash[method][key] = [params[key], hash[method][key]].collect{ |sql| "( %s )" % sanitize_sql(sql) }.join(" AND ")
+ elsif key == :include && merge
+ hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
+ else
+ hash[method][key] = hash[method][key] || params[key]
+ end
+ end
+ else
+ hash[method] = params.merge(hash[method])
+ end
+ else
+ hash[method] = params
+ end
+ hash
+ end
+ end
+
+ self.scoped_methods << method_scoping
+
+ begin
+ yield
+ ensure
+ self.scoped_methods.pop
+ end
+ end
+
+ # Works like with_scope, but discards any nested properties.
+ def with_exclusive_scope(method_scoping = {}, &block)
+ with_scope(method_scoping, :overwrite, &block)
+ end
+
def subclasses #:nodoc:
@@subclasses[self] ||= []
@@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
@@ -1337,18 +1724,18 @@ module ActiveRecord #:nodoc:
scoped_methods = (Thread.current[:scoped_methods] ||= {})
scoped_methods[self] ||= []
end
-
+
def single_threaded_scoped_methods #:nodoc:
@scoped_methods ||= []
end
-
+
# pick up the correct scoped_methods version from @@allow_concurrency
if @@allow_concurrency
alias_method :scoped_methods, :thread_safe_scoped_methods
else
alias_method :scoped_methods, :single_threaded_scoped_methods
end
-
+
def current_scoped_methods #:nodoc:
scoped_methods.last
end
@@ -1381,32 +1768,66 @@ module ActiveRecord #:nodoc:
end
# Accepts an array, hash, or string of sql conditions and sanitizes
- # them into a valid SQL fragment.
+ # them into a valid SQL fragment for a WHERE clause.
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
# { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
# "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
- def sanitize_sql(condition)
+ def sanitize_sql_for_conditions(condition)
case condition
when Array; sanitize_sql_array(condition)
- when Hash; sanitize_sql_hash(condition)
+ when Hash; sanitize_sql_hash_for_conditions(condition)
else condition
end
end
+ alias_method :sanitize_sql, :sanitize_sql_for_conditions
- # Sanitizes a hash of attribute/value pairs into SQL conditions.
+ # Accepts an array, hash, or string of sql conditions and sanitizes
+ # them into a valid SQL fragment for a SET clause.
+ # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
+ def sanitize_sql_for_assignment(assignments)
+ case assignments
+ when Array; sanitize_sql_array(assignments)
+ when Hash; sanitize_sql_hash_for_assignment(assignments)
+ else assignments
+ end
+ end
+
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
# { :name => "foo'bar", :group_id => 4 }
# # => "name='foo''bar' and group_id= 4"
# { :status => nil, :group_id => [1,2,3] }
# # => "status IS NULL and group_id IN (1,2,3)"
# { :age => 13..18 }
# # => "age BETWEEN 13 AND 18"
- def sanitize_sql_hash(attrs)
+ # { 'other_records.id' => 7 }
+ # # => "`other_records`.`id` = 7"
+ def sanitize_sql_hash_for_conditions(attrs)
conditions = attrs.map do |attr, value|
+ attr = attr.to_s
+
+ # Extract table name from qualified attribute names.
+ if attr.include?('.')
+ table_name, attr = attr.split('.', 2)
+ table_name = connection.quote_table_name(table_name)
+ else
+ table_name = quoted_table_name
+ end
+
"#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"
end.join(' AND ')
replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
end
+ alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
+
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
+ # { :status => nil, :group_id => 1 }
+ # # => "status = NULL , group_id = 1"
+ def sanitize_sql_hash_for_assignment(attrs)
+ conditions = attrs.map do |attr, value|
+ "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
+ end.join(', ')
+ end
# Accepts an array of conditions. The array has each value
# sanitized and interpolated into the sql statement.
@@ -1466,10 +1887,6 @@ module ActiveRecord #:nodoc:
end
end
- def extract_options_from_args!(args) #:nodoc:
- args.last.is_a?(Hash) ? args.pop : {}
- end
-
VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
:order, :select, :readonly, :group, :from, :lock ]
@@ -1481,8 +1898,8 @@ module ActiveRecord #:nodoc:
# Inherit :readonly from finder scope if set. Otherwise,
# if :joins is not blank then :readonly defaults to true.
unless options.has_key?(:readonly)
- if scoped?(:find, :readonly)
- options[:readonly] = scope(:find, :readonly)
+ if scoped_readonly = scope(:find, :readonly)
+ options[:readonly] = scoped_readonly
elsif !options[:joins].blank? && !options[:select]
options[:readonly] = true
end
@@ -1491,8 +1908,8 @@ module ActiveRecord #:nodoc:
def encode_quoted_value(value) #:nodoc:
quoted_value = connection.quote(value)
- quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
- quoted_value
+ quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
+ quoted_value
end
end
@@ -1503,10 +1920,14 @@ module ActiveRecord #:nodoc:
# hence you can't have attributes that aren't part of the table columns.
def initialize(attributes = nil)
@attributes = attributes_from_column_definition
+ @attributes_cache = {}
@new_record = true
ensure_proper_type
self.attributes = attributes unless attributes.nil?
- yield self if block_given?
+ self.class.send(:scope, :create).each { |att,value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create)
+ result = yield self if block_given?
+ callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
+ result
end
# A model instance's primary key is always available as model.id
@@ -1514,8 +1935,11 @@ module ActiveRecord #:nodoc:
def id
attr_name = self.class.primary_key
column = column_for_attribute(attr_name)
- define_read_method(:id, attr_name, column) if self.class.generate_read_methods
- read_attribute(attr_name)
+
+ self.class.send(:define_read_method, :id, attr_name, column)
+ # now that the method exists, call it
+ self.send attr_name.to_sym
+
end
# Enables Active Record objects to be used as URL parameters in Action Pack automatically.
@@ -1547,8 +1971,8 @@ module ActiveRecord #:nodoc:
def save
create_or_update
end
-
- # Attempts to save the record, but instead of just returning false if it couldn't happen, it raises a
+
+ # Attempts to save the record, but instead of just returning false if it couldn't happen, it raises a
# RecordNotSaved exception
def save!
create_or_update || raise(RecordNotSaved)
@@ -1559,7 +1983,7 @@ module ActiveRecord #:nodoc:
def destroy
unless new_record?
connection.delete <<-end_sql, "#{self.class.name} Destroy"
- DELETE FROM #{self.class.table_name}
+ DELETE FROM #{self.class.quoted_table_name}
WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}
end_sql
end
@@ -1575,14 +1999,29 @@ module ActiveRecord #:nodoc:
def clone
attrs = self.attributes_before_type_cast
attrs.delete(self.class.primary_key)
- self.class.new do |record|
- record.send :instance_variable_set, '@attributes', attrs
+ record = self.class.new
+ record.send :instance_variable_set, '@attributes', attrs
+ record
+ end
+
+ # Returns an instance of the specified klass with the attributes of the current record. This is mostly useful in relation to
+ # single-table inheritance structures where you want a subclass to appear as the superclass. This can be used along with record
+ # identification in Action Pack to allow, say, Client < Company to do something like render :partial => @client.becomes(Company)
+ # to render that instance using the companies/company partial instead of clients/client.
+ #
+ # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
+ # instance will affect the other.
+ def becomes(klass)
+ returning klass.new do |became|
+ became.instance_variable_set("@attributes", @attributes)
+ became.instance_variable_set("@attributes_cache", @attributes_cache)
+ became.instance_variable_set("@new_record", new_record?)
end
end
# Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records.
# Note: This method is overwritten by the Validation module that'll make sure that updates made with this method
- # doesn't get subjected to validation checks. Hence, attributes can be updated even if the full object isn't valid.
+ # aren't subjected to validation checks. Hence, attributes can be updated even if the full object isn't valid.
def update_attribute(name, value)
send(name.to_s + '=', value)
save
@@ -1594,7 +2033,7 @@ module ActiveRecord #:nodoc:
self.attributes = attributes
save
end
-
+
# Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.
def update_attributes!(attributes)
self.attributes = attributes
@@ -1644,6 +2083,7 @@ module ActiveRecord #:nodoc:
clear_aggregation_cache
clear_association_cache
@attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
+ @attributes_cache = {}
self
end
@@ -1663,15 +2103,17 @@ module ActiveRecord #:nodoc:
# Allows you to set all the attributes at once by passing in a hash with keys
# matching the attribute names (which again matches the column names). Sensitive attributes can be protected
# from this form of mass-assignment by using the +attr_protected+ macro. Or you can alternatively
- # specify which attributes *can* be accessed in with the +attr_accessible+ macro. Then all the
+ # specify which attributes *can* be accessed with the +attr_accessible+ macro. Then all the
# attributes not included in that won't be allowed to be mass-assigned.
- def attributes=(new_attributes)
+ def attributes=(new_attributes, guard_protected_attributes = true)
return if new_attributes.nil?
attributes = new_attributes.dup
attributes.stringify_keys!
multi_parameter_attributes = []
- remove_attributes_protected_from_mass_assignment(attributes).each do |k, v|
+ attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes
+
+ attributes.each do |k, v|
k.include?("(") ? multi_parameter_attributes << [ k, v ] : send(k + "=", v)
end
@@ -1682,7 +2124,7 @@ module ActiveRecord #:nodoc:
# Returns a hash of all the attributes with their names as keys and clones of their objects as values.
def attributes(options = nil)
attributes = clone_attributes :read_attribute
-
+
if options.nil?
attributes
else
@@ -1705,11 +2147,24 @@ module ActiveRecord #:nodoc:
clone_attributes :read_attribute_before_type_cast
end
+ # Format attributes nicely for inspect.
+ def attribute_for_inspect(attr_name)
+ value = read_attribute(attr_name)
+
+ if value.is_a?(String) && value.length > 50
+ "#{value[0..50]}...".inspect
+ elsif value.is_a?(Date) || value.is_a?(Time)
+ %("#{value.to_s(:db)}")
+ else
+ value.inspect
+ end
+ end
+
# Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
# nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
def attribute_present?(attribute)
value = read_attribute(attribute)
- !value.blank? or value == 0
+ !value.blank?
end
# Returns true if the given attribute is in the attributes hash
@@ -1730,8 +2185,8 @@ module ActiveRecord #:nodoc:
# Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
def ==(comparison_object)
comparison_object.equal?(self) ||
- (comparison_object.instance_of?(self.class) &&
- comparison_object.id == id &&
+ (comparison_object.instance_of?(self.class) &&
+ comparison_object.id == id &&
!comparison_object.new_record?)
end
@@ -1746,45 +2201,36 @@ module ActiveRecord #:nodoc:
id.hash
end
- # For checking respond_to? without searching the attributes (which is faster).
- alias_method :respond_to_without_attributes?, :respond_to?
-
- # A Person object with a name attribute can ask person.respond_to?("name"), person.respond_to?("name="), and
- # person.respond_to?("name?") which will all return true.
- def respond_to?(method, include_priv = false)
- if @attributes.nil?
- return super
- elsif attr_name = self.class.column_methods_hash[method.to_sym]
- return true if @attributes.include?(attr_name) || attr_name == self.class.primary_key
- return false if self.class.read_methods.include?(attr_name)
- elsif @attributes.include?(method_name = method.to_s)
- return true
- elsif md = self.class.match_attribute_method?(method.to_s)
- return true if @attributes.include?(md.pre_match)
- end
- # super must be called at the end of the method, because the inherited respond_to?
- # would return true for generated readers, even if the attribute wasn't present
- super
- end
-
- # Just freeze the attributes hash, such that associations are still accessible even on destroyed records.
+ # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
def freeze
@attributes.freeze; self
end
+ # Returns +true+ if the attributes hash has been frozen.
def frozen?
@attributes.frozen?
end
- # Records loaded through joins with piggy-back attributes will be marked as read only as they cannot be saved and return true to this query.
+ # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
+ # attributes will be marked as read only since they cannot be saved.
def readonly?
@readonly == true
end
- def readonly! #:nodoc:
+ # Marks this record as read only.
+ def readonly!
@readonly = true
end
+ # Returns the contents of the record as a nicely formatted string.
+ def inspect
+ attributes_as_nice_string = self.class.column_names.collect { |name|
+ if has_attribute?(name) || new_record?
+ "#{name}: #{attribute_for_inspect(name)}"
+ end
+ }.compact.join(", ")
+ "#<#{self.class} #{attributes_as_nice_string}>"
+ end
private
def create_or_update
@@ -1796,9 +2242,11 @@ module ActiveRecord #:nodoc:
# Updates the associated record with values matching those of the instance attributes.
# Returns the number of affected rows.
def update
+ quoted_attributes = attributes_with_quotes(false, false)
+ return 0 if quoted_attributes.empty?
connection.update(
- "UPDATE #{self.class.table_name} " +
- "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " +
+ "UPDATE #{self.class.quoted_table_name} " +
+ "SET #{quoted_comma_pair_list(connection, quoted_attributes)} " +
"WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}",
"#{self.class.name} Update"
)
@@ -1811,13 +2259,18 @@ module ActiveRecord #:nodoc:
self.id = connection.next_sequence_value(self.class.sequence_name)
end
- self.id = connection.insert(
- "INSERT INTO #{self.class.table_name} " +
+ quoted_attributes = attributes_with_quotes
+
+ statement = if quoted_attributes.empty?
+ connection.empty_insert_statement(self.class.table_name)
+ else
+ "INSERT INTO #{self.class.quoted_table_name} " +
"(#{quoted_column_names.join(', ')}) " +
- "VALUES(#{attributes_with_quotes.values.join(', ')})",
- "#{self.class.name} Create",
- self.class.primary_key, self.id, self.class.sequence_name
- )
+ "VALUES(#{quoted_attributes.values.join(', ')})"
+ end
+
+ self.id = connection.insert(statement, "#{self.class.name} Create",
+ self.class.primary_key, self.id, self.class.sequence_name)
@new_record = false
id
@@ -1833,189 +2286,42 @@ module ActiveRecord #:nodoc:
end
end
-
- # Allows access to the object attributes, which are held in the @attributes hash, as were
- # they first-class methods. So a Person class with a name attribute can use Person#name and
- # Person#name= and never directly use the attributes hash -- except for multiple assigns with
- # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
- # the completed attribute is not nil or 0.
- #
- # It's also possible to instantiate related objects, so a Client class belonging to the clients
- # table with a master_id foreign key can instantiate master through Client#master.
- def method_missing(method_id, *args, &block)
- method_name = method_id.to_s
- if @attributes.include?(method_name) or
- (md = /\?$/.match(method_name) and
- @attributes.include?(query_method_name = md.pre_match) and
- method_name = query_method_name)
- define_read_methods if self.class.read_methods.empty? && self.class.generate_read_methods
- md ? query_attribute(method_name) : read_attribute(method_name)
- elsif self.class.primary_key.to_s == method_name
- id
- elsif md = self.class.match_attribute_method?(method_name)
- attribute_name, method_type = md.pre_match, md.to_s
- if @attributes.include?(attribute_name)
- __send__("attribute#{method_type}", attribute_name, *args, &block)
- else
- super
- end
- else
- super
- end
- end
-
- # Returns the value of the attribute identified by attr_name after it has been typecast (for example,
- # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
- def read_attribute(attr_name)
- attr_name = attr_name.to_s
- if !(value = @attributes[attr_name]).nil?
- if column = column_for_attribute(attr_name)
- if unserializable_attribute?(attr_name, column)
- unserialize_attribute(attr_name)
- else
- column.type_cast(value)
- end
- else
- value
- end
- else
- nil
- end
- end
-
- def read_attribute_before_type_cast(attr_name)
- @attributes[attr_name]
- end
-
- # Called on first read access to any given column and generates reader
- # methods for all columns in the columns_hash if
- # ActiveRecord::Base.generate_read_methods is set to true.
- def define_read_methods
- self.class.columns_hash.each do |name, column|
- unless respond_to_without_attributes?(name)
- if self.class.serialized_attributes[name]
- define_read_method_for_serialized_attribute(name)
- else
- define_read_method(name.to_sym, name, column)
- end
- end
-
- unless respond_to_without_attributes?("#{name}?")
- define_question_method(name)
- end
- end
- end
-
- # Define an attribute reader method. Cope with nil column.
- def define_read_method(symbol, attr_name, column)
- cast_code = column.type_cast_code('v') if column
- access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
-
- unless attr_name.to_s == self.class.primary_key.to_s
- access_code = access_code.insert(0, "raise NoMethodError, 'missing attribute: #{attr_name}', caller unless @attributes.has_key?('#{attr_name}'); ")
- self.class.read_methods << attr_name
- end
-
- evaluate_read_method attr_name, "def #{symbol}; #{access_code}; end"
- end
-
- # Define read method for serialized attribute.
- def define_read_method_for_serialized_attribute(attr_name)
- unless attr_name.to_s == self.class.primary_key.to_s
- self.class.read_methods << attr_name
- end
-
- evaluate_read_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end"
- end
-
- # Define an attribute ? method.
- def define_question_method(attr_name)
- unless attr_name.to_s == self.class.primary_key.to_s
- self.class.read_methods << "#{attr_name}?"
- end
-
- evaluate_read_method attr_name, "def #{attr_name}?; query_attribute('#{attr_name}'); end"
- end
-
- # Evaluate the definition for an attribute reader or ? method
- def evaluate_read_method(attr_name, method_definition)
- begin
- self.class.class_eval(method_definition)
- rescue SyntaxError => err
- self.class.read_methods.delete(attr_name)
- if logger
- logger.warn "Exception occurred during reader method compilation."
- logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
- logger.warn "#{err.message}"
- end
- end
- end
-
- # Returns true if the attribute is of a text column and marked for serialization.
- def unserializable_attribute?(attr_name, column)
- column.text? && self.class.serialized_attributes[attr_name]
- end
-
- # Returns the unserialized object of the attribute.
- def unserialize_attribute(attr_name)
- unserialized_object = object_from_yaml(@attributes[attr_name])
-
- if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
- @attributes[attr_name] = unserialized_object
- else
- raise SerializationTypeMismatch,
- "#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
- end
- end
-
- # Updates the attribute identified by attr_name with the specified +value+. Empty strings for fixnum and float
- # columns are turned into nil.
- def write_attribute(attr_name, value)
- attr_name = attr_name.to_s
- if (column = column_for_attribute(attr_name)) && column.number?
- @attributes[attr_name] = convert_number_column_value(value)
- else
- @attributes[attr_name] = value
- end
- end
-
def convert_number_column_value(value)
case value
- when FalseClass: 0
- when TrueClass: 1
- when '': nil
+ when FalseClass; 0
+ when TrueClass; 1
+ when ''; nil
else value
end
end
- def query_attribute(attr_name)
- attribute = @attributes[attr_name]
- if attribute.kind_of?(Fixnum) && attribute == 0
- false
- elsif attribute.kind_of?(String) && attribute == "0"
- false
- elsif attribute.kind_of?(String) && attribute.empty?
- false
- elsif attribute.nil?
- false
- elsif attribute == false
- false
- elsif attribute == "f"
- false
- elsif attribute == "false"
- false
- else
- true
+ def remove_attributes_protected_from_mass_assignment(attributes)
+ safe_attributes =
+ if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
+ attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
+ elsif self.class.protected_attributes.nil?
+ attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
+ elsif self.class.accessible_attributes.nil?
+ attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
+ else
+ raise "Declare either attr_protected or attr_accessible for #{self.class}, but not both."
+ end
+
+ removed_attributes = attributes.keys - safe_attributes.keys
+
+ if removed_attributes.any?
+ logger.debug "WARNING: Can't mass-assign these protected attributes: #{removed_attributes.join(', ')}"
end
+
+ safe_attributes
end
- def remove_attributes_protected_from_mass_assignment(attributes)
- if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
- attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
- elsif self.class.protected_attributes.nil?
- attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "").intern) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
- elsif self.class.accessible_attributes.nil?
- attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"").intern) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
+ # Removes attributes which have been marked as readonly.
+ def remove_readonly_attributes(attributes)
+ unless self.class.readonly_attributes.nil?
+ attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,"")) }
+ else
+ attributes
end
end
@@ -2026,15 +2332,16 @@ module ActiveRecord #:nodoc:
default
end
- # Returns copy of the attributes hash where all the values have been safely quoted for use in
+ # Returns a copy of the attributes hash where all the values have been safely quoted for use in
# an SQL statement.
- def attributes_with_quotes(include_primary_key = true)
- attributes.inject({}) do |quoted, (name, value)|
+ def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true)
+ quoted = attributes.inject({}) do |quoted, (name, value)|
if column = column_for_attribute(name)
quoted[name] = quote_value(value, column) unless !include_primary_key && column.primary
end
quoted
end
+ include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
end
# Quote strings appropriately for SQL statements.
@@ -2042,13 +2349,6 @@ module ActiveRecord #:nodoc:
self.class.connection.quote(value, column)
end
- # Deprecated, use quote_value
- def quote(value, column = nil)
- self.class.connection.quote(value, column)
- end
- deprecate :quote => :quote_value
-
-
# Interpolate custom sql string in instance context.
# Optional record argument is meant for custom insert_sql.
def interpolate_sql(sql, record = nil)
@@ -2071,7 +2371,7 @@ module ActiveRecord #:nodoc:
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
- # s for String, and a for Array. If all the values for a given attribute is empty, the attribute will be set to nil.
+ # s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.
def assign_multiparameter_attributes(pairs)
execute_callstack_for_multiparameter_attributes(
extract_callstack_for_multiparameter_attributes(pairs)
@@ -2134,6 +2434,10 @@ module ActiveRecord #:nodoc:
end
end
+ def self.quoted_table_name
+ self.connection.quote_table_name(self.table_name)
+ end
+
def quote_columns(quoter, hash)
hash.inject({}) do |quoted, (name, value)|
quoted[quoter.quote_column_name(name)] = value
@@ -2159,13 +2463,7 @@ module ActiveRecord #:nodoc:
def clone_attribute_value(reader_method, attribute_name)
value = send(reader_method, attribute_name)
-
- case value
- when nil, Fixnum, true, false
- value
- else
- value.clone
- end
+ value.duplicable? ? value.clone : value
rescue TypeError, NoMethodError
value
end
diff --git a/vendor/rails/activerecord/lib/active_record/calculations.rb b/vendor/rails/activerecord/lib/active_record/calculations.rb
index 1d9ac4b1..e08520f1 100644
--- a/vendor/rails/activerecord/lib/active_record/calculations.rb
+++ b/vendor/rails/activerecord/lib/active_record/calculations.rb
@@ -6,79 +6,80 @@ module ActiveRecord
end
module ClassMethods
- # Count operates using three different approaches.
+ # Count operates using three different approaches.
#
# * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
- # * Count by conditions or joins: This API has been deprecated and will be removed in Rails 2.0
+ # * Count using column : By passing a column name to count, it will return a count of all the rows for the model with supplied column present
# * Count using options will find the row count matched by the options used.
#
- # The last approach, count using options, accepts an option hash as the only parameter. The options are:
+ # The third approach, count using options, accepts an option hash as the only parameter. The options are:
#
# * :conditions : An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
- # * :joins : An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
- # The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
+ # * :joins : Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
+ # or named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s).
+ # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
+ # Pass :readonly => false to override.
# * :include : Named associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
- # to already defined associations. When using named associations count returns the number DISTINCT items for the model you're counting.
+ # to already defined associations. When using named associations, count returns the number of DISTINCT items for the model you're counting.
# See eager loading under Associations.
# * :order : An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
# * :group : An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
- # * :select : By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
+ # * :select : By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
# include the joined columns.
# * :distinct : Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
#
# Examples for counting all:
# Person.count # returns the total count of all people
#
- # Examples for count by +conditions+ and +joins+ (this has been deprecated):
- # Person.count("age > 26") # returns the number of people older than 26
- # Person.find("age > 26 AND job.salary > 60000", "LEFT JOIN jobs on jobs.person_id = person.id") # returns the total number of rows matching the conditions and joins fetched by SELECT COUNT(*).
+ # Examples for counting by column:
+ # Person.count(:age) # returns the total count of all people whose age is present in database
#
# Examples for count with options:
# Person.count(:conditions => "age > 26")
# Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job) # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
- # Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins.
+ # Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins.
# Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
# Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
#
# Note: Person.count(:all) will not work because it will use :all as the condition. Use Person.count instead.
def count(*args)
- calculate(:count, *construct_count_options_from_legacy_args(*args))
+ calculate(:count, *construct_count_options_from_args(*args))
end
- # Calculates average value on a given column. The value is returned as a float. See #calculate for examples with options.
+ # Calculates the average value on a given column. The value is returned as a float. See #calculate for examples with options.
#
# Person.average('age')
def average(column_name, options = {})
calculate(:avg, column_name, options)
end
- # Calculates the minimum value on a given column. The value is returned with the same data type of the column.. See #calculate for examples with options.
+ # Calculates the minimum value on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
#
# Person.minimum('age')
def minimum(column_name, options = {})
calculate(:min, column_name, options)
end
- # Calculates the maximum value on a given column. The value is returned with the same data type of the column.. See #calculate for examples with options.
+ # Calculates the maximum value on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
#
# Person.maximum('age')
def maximum(column_name, options = {})
calculate(:max, column_name, options)
end
- # Calculates the sum value on a given column. The value is returned with the same data type of the column.. See #calculate for examples with options.
+ # Calculates the sum of values on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
#
# Person.sum('age')
def sum(column_name, options = {})
calculate(:sum, column_name, options)
end
- # This calculates aggregate values in the given column: Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
- # Options such as :conditions, :order, :group, :having, and :joins can be passed to customize the query.
+ # This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
+ # Options such as :conditions, :order, :group, :having, and :joins can be passed to customize the query.
#
# There are two basic forms of output:
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float for AVG, and the given column's type for everything else.
- # * Grouped values: This returns an ordered hash of the values and groups them by the :group option. It takes either a column name, or the name
+ # * Grouped values: This returns an ordered hash of the values and groups them by the :group option. It takes either a column name, or the name
# of a belongs_to association.
#
# values = Person.maximum(:age, :group => 'last_name')
@@ -95,14 +96,15 @@ module ActiveRecord
# end
#
# Options:
- # * :conditions : An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
- # * :joins : An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
+ # * :conditions - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
+ # * :include : Eager loading, see Associations for details. Since calculations don't load anything, the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
+ # * :joins - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
# The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
- # * :order : An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
- # * :group : An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
- # * :select : By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
+ # * :order - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
+ # * :group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
+ # * :select - By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
# include the joined columns.
- # * :distinct : Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
+ # * :distinct - Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
#
# Examples:
# Person.calculate(:count, :all) # The same as Person.count
@@ -125,60 +127,54 @@ module ActiveRecord
end
protected
- def construct_count_options_from_legacy_args(*args)
+ def construct_count_options_from_args(*args)
options = {}
column_name = :all
-
+
# We need to handle
# count()
+ # count(:column_name=:all)
# count(options={})
# count(column_name=:all, options={})
- # count(conditions=nil, joins=nil) # deprecated
- if args.size > 2
- raise ArgumentError, "Unexpected parameters passed to count(options={}): #{args.inspect}"
- elsif args.size > 0
- if args[0].is_a?(Hash)
- options = args[0]
- elsif args[1].is_a?(Hash)
- column_name, options = args
- else
- # Deprecated count(conditions, joins=nil)
- ActiveSupport::Deprecation.warn(
- "You called count(#{args[0].inspect}, #{args[1].inspect}), which is a deprecated API call. " +
- "Instead you should use count(column_name, options). Passing the conditions and joins as " +
- "string parameters will be removed in Rails 2.0.", caller(2)
- )
- options.merge!(:conditions => args[0])
- options.merge!(:joins => args[1]) if args[1]
- end
- end
-
+ case args.size
+ when 1
+ args[0].is_a?(Hash) ? options = args[0] : column_name = args[0]
+ when 2
+ column_name, options = args
+ else
+ raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
+ end if args.size > 0
+
[column_name, options]
end
def construct_calculation_sql(operation, column_name, options) #:nodoc:
operation = operation.to_s.downcase
- options = options.symbolize_keys
-
+ options = options.symbolize_keys
+
scope = scope(:find)
merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
aggregate_alias = column_alias_for(operation, column_name)
- use_workaround = !Base.connection.supports_count_distinct? && options[:distinct] && operation.to_s.downcase == 'count'
- join_dependency = nil
- if merged_includes.any? && operation.to_s.downcase == 'count'
- options[:distinct] = true
- column_name = options[:select] || [table_name, primary_key] * '.'
+ if operation == 'count'
+ if merged_includes.any?
+ options[:distinct] = true
+ column_name = options[:select] || [connection.quote_table_name(table_name), primary_key] * '.'
+ end
+
+ if options[:distinct]
+ use_workaround = !connection.supports_count_distinct?
+ end
end
- sql = "SELECT #{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name}) AS #{aggregate_alias}"
+ sql = "SELECT #{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name}) AS #{aggregate_alias}"
# A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
-
+
sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
sql << " FROM (SELECT DISTINCT #{column_name}" if use_workaround
- sql << " FROM #{table_name} "
+ sql << " FROM #{connection.quote_table_name(table_name)} "
if merged_includes.any?
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, options[:joins])
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
@@ -188,17 +184,17 @@ module ActiveRecord
add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
if options[:group]
- group_key = Base.connection.adapter_name == 'FrontBase' ? :group_alias : :group_field
+ group_key = connection.adapter_name == 'FrontBase' ? :group_alias : :group_field
sql << " GROUP BY #{options[group_key]} "
end
if options[:group] && options[:having]
# FrontBase requires identifiers in the HAVING clause and chokes on function calls
- if Base.connection.adapter_name == 'FrontBase'
+ if connection.adapter_name == 'FrontBase'
options[:having].downcase!
options[:having].gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias)
end
-
+
sql << " HAVING #{options[:having]} "
end
@@ -231,7 +227,8 @@ module ActiveRecord
end
calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row|
- key = associated ? key_records[row[group_alias].to_i] : type_cast_calculated_value(row[group_alias], group_column)
+ key = type_cast_calculated_value(row[group_alias], group_column)
+ key = key_records[key] if associated
value = row[aggregate_alias]
all << [key, type_cast_calculated_value(value, column, operation)]
end
@@ -243,7 +240,7 @@ module ActiveRecord
end
# Converts a given key to the value that the database adapter returns as
- # as a usable column name.
+ # a usable column name.
# users.id #=> users_id
# sum(id) #=> sum_id
# count(distinct users.id) #=> count_distinct_users_id
@@ -261,7 +258,7 @@ module ActiveRecord
operation = operation.to_s.downcase
case operation
when 'count' then value.to_i
- when 'avg' then value.to_f
+ when 'avg' then value && value.to_f
else column ? column.type_cast(value) : value
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/callbacks.rb b/vendor/rails/activerecord/lib/active_record/callbacks.rb
index bfe96fd2..9a9bf283 100755
--- a/vendor/rails/activerecord/lib/active_record/callbacks.rb
+++ b/vendor/rails/activerecord/lib/active_record/callbacks.rb
@@ -1,25 +1,25 @@
require 'observer'
module ActiveRecord
- # Callbacks are hooks into the lifecycle of an Active Record object that allows you to trigger logic
+ # Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic
# before or after an alteration of the object state. This can be used to make sure that associated and
- # dependent objects are deleted when destroy is called (by overwriting before_destroy) or to massage attributes
- # before they're validated (by overwriting before_validation). As an example of the callbacks initiated, consider
- # the Base#save call:
+ # dependent objects are deleted when destroy is called (by overwriting +before_destroy+) or to massage attributes
+ # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
+ # the Base#save call:
#
- # * (-) save
- # * (-) valid?
- # * (1) before_validation
- # * (2) before_validation_on_create
- # * (-) validate
- # * (-) validate_on_create
- # * (3) after_validation
- # * (4) after_validation_on_create
- # * (5) before_save
- # * (6) before_create
- # * (-) create
- # * (7) after_create
- # * (8) after_save
+ # * (-) save
+ # * (-) valid
+ # * (1) before_validation
+ # * (2) before_validation_on_create
+ # * (-) validate
+ # * (-) validate_on_create
+ # * (3) after_validation
+ # * (4) after_validation_on_create
+ # * (5) before_save
+ # * (6) before_create
+ # * (-) create
+ # * (7) after_create
+ # * (8) after_save
#
# That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
# Active Record lifecycle.
@@ -62,8 +62,8 @@ module ActiveRecord
# before_destroy :destroy_readers
# end
#
- # Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is run both +destroy_author+ and
- # +destroy_readers+ is called. Contrast this to the situation where we've implemented the save behavior through overwriteable
+ # Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is run, both +destroy_author+ and
+ # +destroy_readers+ are called. Contrast this to the situation where we've implemented the save behavior through overwriteable
# methods:
#
# class Topic < ActiveRecord::Base
@@ -74,9 +74,9 @@ module ActiveRecord
# def before_destroy() destroy_readers end
# end
#
- # In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So use the callback macros when
- # you want to ensure that a certain callback is called for the entire hierarchy and the regular overwriteable methods when you
- # want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
+ # In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when
+ # you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods
+ # when you want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
#
# *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the
# associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't
@@ -143,7 +143,7 @@ module ActiveRecord
# before_destroy 'self.class.delete_all "parent_id = #{id}"'
# end
#
- # Notice that single plings (') are used so the #{id} part isn't evaluated until the callback is triggered. Also note that these
+ # Notice that single quotes (') are used so the #{id} part isn't evaluated until the callback is triggered. Also note that these
# inline callbacks can be stacked just like the regular ones:
#
# class Topic < ActiveRecord::Base
@@ -151,23 +151,23 @@ module ActiveRecord
# 'puts "Evaluated after parents are destroyed"'
# end
#
- # == The after_find and after_initialize exceptions
+ # == The +after_find+ and +after_initialize+ exceptions
#
- # Because after_find and after_initialize are called for each object found and instantiated by a finder, such as Base.find(:all), we've had
- # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, after_find and
- # after_initialize will only be run if an explicit implementation is defined (def after_find ). In that case, all of the
+ # Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder, such as Base.find(:all) , we've had
+ # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, +after_find+ and
+ # +after_initialize+ will only be run if an explicit implementation is defined (def after_find ). In that case, all of the
# callback types will be called.
#
- # == before_validation* returning statements
+ # == before_validation* returning statements
#
- # If the returning value of a before_validation callback can be evaluated to false, the process will be aborted and Base#save will return false.
- # If Base#save! is called it will raise a RecordNotSave error.
+ # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be aborted and Base#save will return +false+.
+ # If Base#save! is called it will raise a +RecordNotSaved+ exception.
# Nothing will be appended to the errors object.
#
- # == Cancelling callbacks
+ # == Canceling callbacks
#
- # If a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns
- # false, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
+ # If a before_* callback returns +false+, all the later callbacks and the associated action are cancelled. If an after_* callback returns
+ # +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
# defined as methods on the model, which are called last.
module Callbacks
CALLBACKS = %w(
@@ -177,16 +177,10 @@ module ActiveRecord
)
def self.included(base) #:nodoc:
- base.extend(ClassMethods)
- base.class_eval do
- class << self
- include Observable
- alias_method_chain :instantiate, :callbacks
- end
+ base.extend Observable
- [:initialize, :create_or_update, :valid?, :create, :update, :destroy].each do |method|
- alias_method_chain method, :callbacks
- end
+ [:create_or_update, :valid?, :create, :update, :destroy].each do |method|
+ base.send :alias_method_chain, method, :callbacks
end
CALLBACKS.each do |method|
@@ -199,39 +193,16 @@ module ActiveRecord
end
end
- module ClassMethods #:nodoc:
- def instantiate_with_callbacks(record)
- object = instantiate_without_callbacks(record)
-
- if object.respond_to_without_attributes?(:after_find)
- object.send(:callback, :after_find)
- end
-
- if object.respond_to_without_attributes?(:after_initialize)
- object.send(:callback, :after_initialize)
- end
-
- object
- end
- end
-
- # Is called when the object was instantiated by one of the finders, like Base.find.
+ # Is called when the object was instantiated by one of the finders, like Base.find .
#def after_find() end
- # Is called after the object has been instantiated by a call to Base.new.
+ # Is called after the object has been instantiated by a call to Base.new .
#def after_initialize() end
- def initialize_with_callbacks(attributes = nil) #:nodoc:
- initialize_without_callbacks(attributes)
- result = yield self if block_given?
- callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
- result
- end
-
- # Is called _before_ Base.save (regardless of whether it's a create or update save).
+ # Is called _before_ Base.save (regardless of whether it's a +create+ or +update+ save).
def before_save() end
- # Is called _after_ Base.save (regardless of whether it's a create or update save).
+ # Is called _after_ Base.save (regardless of whether it's a +create+ or +update+ save).
#
# class Contact < ActiveRecord::Base
# after_save { logger.info( 'New contact saved!' ) }
@@ -243,11 +214,12 @@ module ActiveRecord
callback(:after_save)
result
end
+ private :create_or_update_with_callbacks
- # Is called _before_ Base.save on new objects that haven't been saved yet (no record exists).
+ # Is called _before_ Base.save on new objects that haven't been saved yet (no record exists).
def before_create() end
- # Is called _after_ Base.save on new objects that haven't been saved yet (no record exists).
+ # Is called _after_ Base.save on new objects that haven't been saved yet (no record exists).
def after_create() end
def create_with_callbacks #:nodoc:
return false if callback(:before_create) == false
@@ -255,11 +227,12 @@ module ActiveRecord
callback(:after_create)
result
end
+ private :create_with_callbacks
- # Is called _before_ Base.save on existing objects that have a record.
+ # Is called _before_ Base.save on existing objects that have a record.
def before_update() end
- # Is called _after_ Base.save on existing objects that have a record.
+ # Is called _after_ Base.save on existing objects that have a record.
def after_update() end
def update_with_callbacks #:nodoc:
@@ -268,26 +241,27 @@ module ActiveRecord
callback(:after_update)
result
end
+ private :update_with_callbacks
- # Is called _before_ Validations.validate (which is part of the Base.save call).
+ # Is called _before_ Validations.validate (which is part of the Base.save call).
def before_validation() end
- # Is called _after_ Validations.validate (which is part of the Base.save call).
+ # Is called _after_ Validations.validate (which is part of the Base.save call).
def after_validation() end
- # Is called _before_ Validations.validate (which is part of the Base.save call) on new objects
+ # Is called _before_ Validations.validate (which is part of the Base.save call) on new objects
# that haven't been saved yet (no record exists).
def before_validation_on_create() end
- # Is called _after_ Validations.validate (which is part of the Base.save call) on new objects
+ # Is called _after_ Validations.validate (which is part of the Base.save call) on new objects
# that haven't been saved yet (no record exists).
def after_validation_on_create() end
- # Is called _before_ Validations.validate (which is part of the Base.save call) on
+ # Is called _before_ Validations.validate (which is part of the Base.save call) on
# existing objects that have a record.
def before_validation_on_update() end
- # Is called _after_ Validations.validate (which is part of the Base.save call) on
+ # Is called _after_ Validations.validate (which is part of the Base.save call) on
# existing objects that have a record.
def after_validation_on_update() end
@@ -304,13 +278,13 @@ module ActiveRecord
return result
end
- # Is called _before_ Base.destroy.
+ # Is called _before_ Base.destroy .
#
# Note: If you need to _destroy_ or _nullify_ associated records first,
- # use the _:dependent_ option on your associations.
+ # use the :dependent option on your associations.
def before_destroy() end
- # Is called _after_ Base.destroy (and all the attributes have been frozen).
+ # Is called _after_ Base.destroy (and all the attributes have been frozen).
#
# class Contact < ActiveRecord::Base
# after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
index 1a760508..51b90d3c 100644
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -89,10 +89,23 @@ module ActiveRecord
# Clears the cache which maps classes
def clear_reloadable_connections!
- @@active_connections.each do |name, conn|
- if conn.requires_reloading?
- conn.disconnect!
- @@active_connections.delete(name)
+ if @@allow_concurrency
+ # With concurrent connections @@active_connections is
+ # a hash keyed by thread id.
+ @@active_connections.each do |thread_id, conns|
+ conns.each do |name, conn|
+ if conn.requires_reloading?
+ conn.disconnect!
+ @@active_connections[thread_id].delete(name)
+ end
+ end
+ end
+ else
+ @@active_connections.each do |name, conn|
+ if conn.requires_reloading?
+ conn.disconnect!
+ @@active_connections.delete(name)
+ end
end
end
end
@@ -206,15 +219,31 @@ module ActiveRecord
else
spec = spec.symbolize_keys
unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
+
+ begin
+ require 'rubygems'
+ gem "activerecord-#{spec[:adapter]}-adapter"
+ require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
+ rescue LoadError
+ begin
+ require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
+ rescue LoadError
+ raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})"
+ end
+ end
+
adapter_method = "#{spec[:adapter]}_connection"
- unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
+ if !respond_to?(adapter_method)
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
+ end
+
remove_connection
establish_connection(ConnectionSpecification.new(spec, adapter_method))
end
end
# Locate the connection of the nearest super class. This can be an
- # active or defined connections: if it is the latter, it will be
+ # active or defined connection: if it is the latter, it will be
# opened and set as the active connection for the class it was defined
# for (not necessarily the current class).
def self.retrieve_connection #:nodoc:
@@ -235,15 +264,15 @@ module ActiveRecord
conn or raise ConnectionNotEstablished
end
- # Returns true if a connection that's accessible to this class have already been opened.
+ # Returns true if a connection that's accessible to this class has already been opened.
def self.connected?
active_connections[active_connection_name] ? true : false
end
# Remove the connection for this class. This will close the active
# connection and the defined connection (if they exist). The result
- # can be used as argument for establish_connection, for easy
- # re-establishing of the connection.
+ # can be used as an argument for establish_connection, for easily
+ # re-establishing the connection.
def self.remove_connection(klass=self)
spec = @@defined_connections[klass.name]
konn = active_connections[klass.name]
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 7d7c22c7..589acd39 100644
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -10,21 +10,28 @@ module ActiveRecord
# Returns a record hash with the column names as keys and column values
# as values.
def select_one(sql, name = nil)
- result = select(sql, name)
+ result = select_all(sql, name)
result.first if result
end
# Returns a single value from a record
def select_value(sql, name = nil)
- result = select_one(sql, name)
- result.nil? ? nil : result.values.first
+ if result = select_one(sql, name)
+ result.values.first
+ end
end
# Returns an array of the values of the first column in a select:
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
def select_values(sql, name = nil)
- result = select_all(sql, name)
- result.map{ |v| v.values.first }
+ result = select_rows(sql, name)
+ result.map { |v| v[0] }
+ end
+
+ # Returns an array of arrays containing the field values.
+ # Order is the same as that returned by #columns.
+ def select_rows(sql, name = nil)
+ raise NotImplementedError, "select_rows is an abstract method"
end
# Executes the SQL statement in the context of this connection.
@@ -34,17 +41,17 @@ module ActiveRecord
# Returns the last auto-generated ID from the affected table.
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- raise NotImplementedError, "insert is an abstract method"
+ insert_sql(sql, name, pk, id_value, sequence_name)
end
# Executes the update statement and returns the number of rows affected.
def update(sql, name = nil)
- execute(sql, name)
+ update_sql(sql, name)
end
# Executes the delete statement and returns the number of rows affected.
def delete(sql, name = nil)
- update(sql, name)
+ delete_sql(sql, name)
end
# Wrap a block in a transaction. Returns result of block.
@@ -53,7 +60,7 @@ module ActiveRecord
begin
if block_given?
if start_db_transaction
- begin_db_transaction
+ begin_db_transaction
transaction_open = true
end
yield
@@ -63,10 +70,17 @@ module ActiveRecord
transaction_open = false
rollback_db_transaction
end
- raise
+ raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
end
ensure
- commit_db_transaction if transaction_open
+ if transaction_open
+ begin
+ commit_db_transaction
+ rescue Exception => database_transaction_rollback
+ rollback_db_transaction
+ raise
+ end
+ end
end
# Begins the transaction (and turns off auto-committing).
@@ -84,7 +98,7 @@ module ActiveRecord
add_limit_offset!(sql, options) if options
end
- # Appends +LIMIT+ and +OFFSET+ options to a SQL statement.
+ # Appends +LIMIT+ and +OFFSET+ options to an SQL statement.
# This method *modifies* the +sql+ parameter.
# ===== Examples
# add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
@@ -99,14 +113,15 @@ module ActiveRecord
end
end
- # Appends a locking clause to a SQL statement. *Modifies the +sql+ parameter*.
+ # Appends a locking clause to an SQL statement.
+ # This method *modifies* the +sql+ parameter.
# # SELECT * FROM suppliers FOR UPDATE
# add_lock! 'SELECT * FROM suppliers', :lock => true
# add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
def add_lock!(sql, options)
case lock = options[:lock]
- when true: sql << ' FOR UPDATE'
- when String: sql << " #{lock}"
+ when true; sql << ' FOR UPDATE'
+ when String; sql << " #{lock}"
end
end
@@ -119,12 +134,38 @@ module ActiveRecord
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
end
+ # Inserts the given fixture into the table. Overridden in adapters that require
+ # something beyond a simple insert (eg. Oracle).
+ def insert_fixture(fixture, table_name)
+ execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
+ end
+
+ def empty_insert_statement(table_name)
+ "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)"
+ end
+
protected
# Returns an array of record hashes with the column names as keys and
# column values as values.
def select(sql, name = nil)
raise NotImplementedError, "select is an abstract method"
end
+
+ # Returns the last auto-generated ID from the affected table.
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
+ execute(sql, name)
+ id_value
+ end
+
+ # Executes the update statement and returns the number of rows affected.
+ def update_sql(sql, name = nil)
+ execute(sql, name)
+ end
+
+ # Executes the delete statement and returns the number of rows affected.
+ def delete_sql(sql, name = nil)
+ update_sql(sql, name)
+ end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
new file mode 100644
index 00000000..e6b8e3ae
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -0,0 +1,87 @@
+module ActiveRecord
+ module ConnectionAdapters # :nodoc:
+ module QueryCache
+ class << self
+ def included(base)
+ base.class_eval do
+ attr_accessor :query_cache_enabled
+ alias_method_chain :columns, :query_cache
+ alias_method_chain :select_all, :query_cache
+ end
+
+ dirties_query_cache base, :insert, :update, :delete
+ end
+
+ def dirties_query_cache(base, *method_names)
+ method_names.each do |method_name|
+ base.class_eval <<-end_code, __FILE__, __LINE__
+ def #{method_name}_with_query_dirty(*args)
+ clear_query_cache if @query_cache_enabled
+ #{method_name}_without_query_dirty(*args)
+ end
+
+ alias_method_chain :#{method_name}, :query_dirty
+ end_code
+ end
+ end
+ end
+
+ # Enable the query cache within the block.
+ def cache
+ old, @query_cache_enabled = @query_cache_enabled, true
+ @query_cache ||= {}
+ yield
+ ensure
+ clear_query_cache
+ @query_cache_enabled = old
+ end
+
+ # Disable the query cache within the block.
+ def uncached
+ old, @query_cache_enabled = @query_cache_enabled, false
+ yield
+ ensure
+ @query_cache_enabled = old
+ end
+
+ def clear_query_cache
+ @query_cache.clear if @query_cache
+ end
+
+ def select_all_with_query_cache(*args)
+ if @query_cache_enabled
+ cache_sql(args.first) { select_all_without_query_cache(*args) }
+ else
+ select_all_without_query_cache(*args)
+ end
+ end
+
+ def columns_with_query_cache(*args)
+ if @query_cache_enabled
+ @query_cache["SHOW FIELDS FROM #{args.first}"] ||= columns_without_query_cache(*args)
+ else
+ columns_without_query_cache(*args)
+ end
+ end
+
+ private
+ def cache_sql(sql)
+ result =
+ if @query_cache.has_key?(sql)
+ log_info(sql, "CACHE", 0.0)
+ @query_cache[sql]
+ else
+ @query_cache[sql] = yield
+ end
+
+ if Array === result
+ result.collect { |row| row.dup }
+ else
+ result.duplicable? ? result.dup : result
+ end
+ rescue TypeError
+ result
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 38ba490a..3a7bf352 100644
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -11,12 +11,12 @@ module ActiveRecord
when String, ActiveSupport::Multibyte::Chars
value = value.to_s
if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
- "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
+ "#{quoted_string_prefix}'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
elsif column && [:integer, :float].include?(column.type)
value = column.type == :integer ? value.to_i : value.to_f
value.to_s
else
- "'#{quote_string(value)}'" # ' (for ruby-mode)
+ "#{quoted_string_prefix}'#{quote_string(value)}'" # ' (for ruby-mode)
end
when NilClass then "NULL"
when TrueClass then (column && column.type == :integer ? '1' : quoted_true)
@@ -24,9 +24,12 @@ module ActiveRecord
when Float, Fixnum, Bignum then value.to_s
# BigDecimals need to be output in a non-normalized form and quoted.
when BigDecimal then value.to_s('F')
- when Date then "'#{value.to_s(:db)}'"
- when Time, DateTime then "'#{quoted_date(value)}'"
- else "'#{quote_string(value.to_yaml)}'"
+ else
+ if value.acts_like?(:date) || value.acts_like?(:time)
+ "'#{quoted_date(value)}'"
+ else
+ "#{quoted_string_prefix}'#{quote_string(value.to_yaml)}'"
+ end
end
end
@@ -36,22 +39,30 @@ module ActiveRecord
s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
end
- # Returns a quoted form of the column name. This is highly adapter
- # specific.
- def quote_column_name(name)
- name
+ # Quotes the column name. Defaults to no quoting.
+ def quote_column_name(column_name)
+ column_name
+ end
+
+ # Quotes the table name. Defaults to column name quoting.
+ def quote_table_name(table_name)
+ quote_column_name(table_name)
end
def quoted_true
"'t'"
end
-
+
def quoted_false
"'f'"
end
-
+
def quoted_date(value)
- value.strftime("%Y-%m-%d %H:%M:%S")
+ value.to_s(:db)
+ end
+
+ def quoted_string_prefix
+ ''
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index ec839e6f..2aa8a122 100644
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -6,6 +6,11 @@ module ActiveRecord
module ConnectionAdapters #:nodoc:
# An abstract definition of a column in a table.
class Column
+ module Format
+ ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
+ ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
+ end
+
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
attr_accessor :primary
@@ -19,7 +24,7 @@ module ActiveRecord
@name, @sql_type, @null = name, sql_type, null
@limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
@type = simplified_type(sql_type)
- @default = type_cast(default)
+ @default = extract_default(default)
@primary = nil
end
@@ -92,70 +97,113 @@ module ActiveRecord
Base.human_attribute_name(@name)
end
- # Used to convert from Strings to BLOBs
- def self.string_to_binary(value)
- value
+ def extract_default(default)
+ type_cast(default)
end
- # Used to convert from BLOBs to Strings
- def self.binary_to_string(value)
- value
- end
-
- def self.string_to_date(string)
- return string unless string.is_a?(String)
- date_array = ParseDate.parsedate(string)
- # treat 0000-00-00 as nil
- Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
- end
-
- def self.string_to_time(string)
- return string unless string.is_a?(String)
- time_hash = Date._parse(string)
- time_hash[:sec_fraction] = microseconds(time_hash)
- time_array = time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)
- # treat 0000-00-00 00:00:00 as nil
- Time.send(Base.default_timezone, *time_array) rescue DateTime.new(*time_array[0..5]) rescue nil
- end
-
- def self.string_to_dummy_time(string)
- return string unless string.is_a?(String)
- return nil if string.empty?
- time_hash = Date._parse(string)
- time_hash[:sec_fraction] = microseconds(time_hash)
- # pad the resulting array with dummy date information
- time_array = [2000, 1, 1]
- time_array += time_hash.values_at(:hour, :min, :sec, :sec_fraction)
- Time.send(Base.default_timezone, *time_array) rescue nil
- end
-
- # convert something to a boolean
- def self.value_to_boolean(value)
- if value == true || value == false
+ class << self
+ # Used to convert from Strings to BLOBs
+ def string_to_binary(value)
value
- else
- %w(true t 1).include?(value.to_s.downcase)
end
- end
- # convert something to a BigDecimal
- def self.value_to_decimal(value)
- if value.is_a?(BigDecimal)
+ # Used to convert from BLOBs to Strings
+ def binary_to_string(value)
value
- elsif value.respond_to?(:to_d)
- value.to_d
- else
- value.to_s.to_d
end
+
+ def string_to_date(string)
+ return string unless string.is_a?(String)
+ return nil if string.empty?
+
+ fast_string_to_date(string) || fallback_string_to_date(string)
+ end
+
+ def string_to_time(string)
+ return string unless string.is_a?(String)
+ return nil if string.empty?
+
+ fast_string_to_time(string) || fallback_string_to_time(string)
+ end
+
+ def string_to_dummy_time(string)
+ return string unless string.is_a?(String)
+ return nil if string.empty?
+
+ string_to_time "2000-01-01 #{string}"
+ end
+
+ # convert something to a boolean
+ def value_to_boolean(value)
+ if value == true || value == false
+ value
+ else
+ %w(true t 1).include?(value.to_s.downcase)
+ end
+ end
+
+ # convert something to a BigDecimal
+ def value_to_decimal(value)
+ if value.is_a?(BigDecimal)
+ value
+ elsif value.respond_to?(:to_d)
+ value.to_d
+ else
+ value.to_s.to_d
+ end
+ end
+
+ protected
+ # '0.123456' -> 123456
+ # '1.123456' -> 123456
+ def microseconds(time)
+ ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
+ end
+
+ def new_date(year, mon, mday)
+ if year && year != 0
+ Date.new(year, mon, mday) rescue nil
+ end
+ end
+
+ def new_time(year, mon, mday, hour, min, sec, microsec)
+ # Treat 0000-00-00 00:00:00 as nil.
+ return nil if year.nil? || year == 0
+
+ Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec)
+ # Over/underflow to DateTime
+ rescue ArgumentError, TypeError
+ zone_offset = Base.default_timezone == :local ? DateTime.local_offset : 0
+ DateTime.civil(year, mon, mday, hour, min, sec, zone_offset) rescue nil
+ end
+
+ def fast_string_to_date(string)
+ if string =~ Format::ISO_DATE
+ new_date $1.to_i, $2.to_i, $3.to_i
+ end
+ end
+
+ # Doesn't handle time zones.
+ def fast_string_to_time(string)
+ if string =~ Format::ISO_DATETIME
+ microsec = ($7.to_f * 1_000_000).to_i
+ new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
+ end
+ end
+
+ def fallback_string_to_date(string)
+ new_date *ParseDate.parsedate(string)[0..2]
+ end
+
+ def fallback_string_to_time(string)
+ time_hash = Date._parse(string)
+ time_hash[:sec_fraction] = microseconds(time_hash)
+
+ new_time *time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)
+ end
end
private
- # '0.123456' -> 123456
- # '1.123456' -> 123456
- def self.microseconds(time)
- ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
- end
-
def extract_limit(sql_type)
$1.to_i if sql_type =~ /\((.*)\)/
end
@@ -223,7 +271,7 @@ module ActiveRecord
end
# Represents a SQL table in an abstract way.
- # Columns are stored as ColumnDefinition in the #columns attribute.
+ # Columns are stored as a ColumnDefinition in the #columns attribute.
class TableDefinition
attr_accessor :columns
@@ -244,24 +292,29 @@ module ActiveRecord
end
# Instantiates a new column for the table.
- # The +type+ parameter must be one of the following values:
+ # The +type+ parameter is normally one of the migrations native types,
+ # which is one of the following:
# :primary_key , :string , :text ,
# :integer , :float , :decimal ,
# :datetime , :timestamp , :time ,
# :date , :binary , :boolean .
#
+ # You may use a type not in this list as long as it is supported by your
+ # database (for example, "polygon" in MySQL), but this will not be database
+ # agnostic and should usually be avoided.
+ #
# Available options are (none of these exists by default):
- # * :limit :
+ # * :limit -
# Requests a maximum column length (:string , :text ,
# :binary or :integer columns only)
- # * :default :
+ # * :default -
# The column's default value. Use nil for NULL.
- # * :null :
+ # * :null -
# Allows or disallows +NULL+ values in the column. This option could
# have been named :null_allowed .
- # * :precision :
+ # * :precision -
# Specifies the precision for a :decimal column.
- # * :scale :
+ # * :scale -
# Specifies the scale for a :decimal column.
#
# Please be aware of different RDBMS implementations behavior with
@@ -295,7 +348,7 @@ module ActiveRecord
#
# This method returns self .
#
- # ===== Examples
+ # == Examples
# # Assuming td is an instance of TableDefinition
# td.column(:granted, :boolean)
# #=> granted BOOLEAN
@@ -316,6 +369,52 @@ module ActiveRecord
# # probably wouldn't hurt to include it.
# def.column(:huge_integer, :decimal, :precision => 30)
# #=> huge_integer DECIMAL(30)
+ #
+ # == Short-hand examples
+ #
+ # Instead of calling column directly, you can also work with the short-hand definitions for the default types.
+ # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
+ # in a single statement.
+ #
+ # What can be written like this with the regular calls to column:
+ #
+ # create_table "products", :force => true do |t|
+ # t.column "shop_id", :integer
+ # t.column "creator_id", :integer
+ # t.column "name", :string, :default => "Untitled"
+ # t.column "value", :string, :default => "Untitled"
+ # t.column "created_at", :datetime
+ # t.column "updated_at", :datetime
+ # end
+ #
+ # Can also be written as follows using the short-hand:
+ #
+ # create_table :products do |t|
+ # t.integer :shop_id, :creator_id
+ # t.string :name, :value, :default => "Untitled"
+ # t.timestamps
+ # end
+ #
+ # There's a short-hand method for each of the type values declared at the top. And then there's
+ # TableDefinition#timestamps that'll add created_at and updated_at as datetimes.
+ #
+ # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
+ # column if the :polymorphic option is supplied. If :polymorphic is a hash of options, these will be
+ # used when creating the _type column. So what can be written like this:
+ #
+ # create_table :taggings do |t|
+ # t.integer :tag_id, :tagger_id, :taggable_id
+ # t.string :tagger_type
+ # t.string :taggable_type, :default => 'Photo'
+ # end
+ #
+ # Can also be written as follows using references:
+ #
+ # create_table :taggings do |t|
+ # t.references :tag
+ # t.references :tagger, :polymorphic => true
+ # t.references :taggable, :polymorphic => { :default => 'Photo' }
+ # end
def column(name, type, options = {})
column = self[name] || ColumnDefinition.new(@base, name, type)
column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym]
@@ -327,8 +426,38 @@ module ActiveRecord
self
end
+ %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
+ class_eval <<-EOV
+ def #{column_type}(*args)
+ options = args.extract_options!
+ column_names = args
+
+ column_names.each { |name| column(name, '#{column_type}', options) }
+ end
+ EOV
+ end
+
+ # Appends :datetime columns :created_at and
+ # :updated_at to the table.
+ def timestamps
+ column(:created_at, :datetime)
+ column(:updated_at, :datetime)
+ end
+
+ def references(*args)
+ options = args.extract_options!
+ polymorphic = options.delete(:polymorphic)
+ args.each do |col|
+ column("#{col}_id", :integer, options)
+ unless polymorphic.nil?
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : {})
+ end
+ end
+ end
+ alias :belongs_to :references
+
# Returns a String whose contents are the column definitions
- # concatenated together. This string can then be pre and appended to
+ # concatenated together. This string can then be prepended and appended to
# to generate the final SQL to create the table.
def to_sql
@columns * ', '
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index a98b26f5..8f981438 100644
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -54,7 +54,7 @@ module ActiveRecord
# [:temporary ]
# Make a temporary table.
# [:force ]
- # Set to true or false to drop the table before creating it.
+ # Set to true to drop the table before creating it.
# Defaults to false.
#
# ===== Examples
@@ -81,45 +81,45 @@ module ActiveRecord
# t.column :supplier_id, :integer
# end
# generates:
- # CREATE TABLE categories_suppliers_join (
+ # CREATE TABLE categories_suppliers (
# category_id int,
# supplier_id int
# )
#
# See also TableDefinition#column for details on how to create columns.
- def create_table(name, options = {})
+ def create_table(table_name, options = {})
table_definition = TableDefinition.new(self)
table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
yield table_definition
if options[:force]
- drop_table(name, options) rescue nil
+ drop_table(table_name, options) rescue nil
end
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
- create_sql << "#{name} ("
+ create_sql << "#{quote_table_name(table_name)} ("
create_sql << table_definition.to_sql
create_sql << ") #{options[:options]}"
execute create_sql
end
-
+
# Renames a table.
# ===== Example
# rename_table('octopuses', 'octopi')
- def rename_table(name, new_name)
+ def rename_table(table_name, new_name)
raise NotImplementedError, "rename_table is not implemented"
end
# Drops a table from the database.
- def drop_table(name, options = {})
- execute "DROP TABLE #{name}"
+ def drop_table(table_name, options = {})
+ execute "DROP TABLE #{quote_table_name(table_name)}"
end
# Adds a new column to the named table.
# See TableDefinition#column for details of the options you can use.
def add_column(table_name, column_name, type, options = {})
- add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+ add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
add_column_options!(add_column_sql, options)
execute(add_column_sql)
end
@@ -128,7 +128,7 @@ module ActiveRecord
# ===== Examples
# remove_column(:suppliers, :qualification)
def remove_column(table_name, column_name)
- execute "ALTER TABLE #{table_name} DROP #{quote_column_name(column_name)}"
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
end
# Changes the column's definition according to the new options.
@@ -142,7 +142,7 @@ module ActiveRecord
# Sets a new default value for a column. If you want to set the default
# value to +NULL+, you are out of luck. You need to
- # DatabaseStatements#execute the apppropriate SQL statement yourself.
+ # DatabaseStatements#execute the appropriate SQL statement yourself.
# ===== Examples
# change_column_default(:suppliers, :qualification, 'new')
# change_column_default(:accounts, :authorized, 1)
@@ -160,13 +160,13 @@ module ActiveRecord
# Adds a new index to the table. +column_name+ can be a single Symbol, or
# an Array of Symbols.
#
- # The index will be named after the table and the first column names,
+ # The index will be named after the table and the first column name,
# unless you pass +:name+ as an option.
#
# When creating an index on multiple columns, the first column is used as a name
# for the index. For example, when you specify an index on two columns
# [+:first+, +:last+], the DBMS creates an index for both columns as well as an
- # index for the first colum +:first+. Using just the first name for this index
+ # index for the first column +:first+. Using just the first name for this index
# makes sense, because you will never have to create a singular index with this
# name.
#
@@ -194,7 +194,7 @@ module ActiveRecord
index_type = options
end
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{table_name} (#{quoted_column_names})"
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
end
# Remove the given index from the table.
@@ -234,17 +234,17 @@ module ActiveRecord
# The migrations module handles this automatically.
def initialize_schema_information
begin
- execute "CREATE TABLE #{ActiveRecord::Migrator.schema_info_table_name} (version #{type_to_sql(:integer)})"
- execute "INSERT INTO #{ActiveRecord::Migrator.schema_info_table_name} (version) VALUES(0)"
+ execute "CREATE TABLE #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version #{type_to_sql(:integer)})"
+ execute "INSERT INTO #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version) VALUES(0)"
rescue ActiveRecord::StatementInvalid
- # Schema has been intialized
+ # Schema has been initialized
end
end
def dump_schema_information #:nodoc:
begin
if (current_schema = ActiveRecord::Migrator.current_version) > 0
- return "INSERT INTO #{ActiveRecord::Migrator.schema_info_table_name} (version) VALUES (#{current_schema})"
+ return "INSERT INTO #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version) VALUES (#{current_schema})"
end
rescue ActiveRecord::StatementInvalid
# No Schema Info
@@ -253,25 +253,28 @@ module ActiveRecord
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
- native = native_database_types[type]
- column_type_sql = native.is_a?(Hash) ? native[:name] : native
- if type == :decimal # ignore limit, use precison and scale
- precision ||= native[:precision]
- scale ||= native[:scale]
- if precision
- if scale
- column_type_sql << "(#{precision},#{scale})"
+ if native = native_database_types[type]
+ column_type_sql = native.is_a?(Hash) ? native[:name] : native
+ if type == :decimal # ignore limit, use precision and scale
+ precision ||= native[:precision]
+ scale ||= native[:scale]
+ if precision
+ if scale
+ column_type_sql << "(#{precision},#{scale})"
+ else
+ column_type_sql << "(#{precision})"
+ end
else
- column_type_sql << "(#{precision})"
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified" if scale
end
+ column_type_sql
else
- raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified" if scale
+ limit ||= native[:limit]
+ column_type_sql << "(#{limit})" if limit
+ column_type_sql
end
- column_type_sql
else
- limit ||= native[:limit]
- column_type_sql << "(#{limit})" if limit
- column_type_sql
+ column_type_sql = type
end
end
@@ -291,7 +294,7 @@ module ActiveRecord
# ORDER BY clause for the passed order option.
# PostgreSQL overrides this due to its stricter standards compliance.
def add_order_by_for_association_limiting!(sql, options)
- sql << "ORDER BY #{options[:order]}"
+ sql << " ORDER BY #{options[:order]}"
end
protected
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index d113897d..940707a9 100755
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -8,6 +8,7 @@ require 'active_record/connection_adapters/abstract/schema_statements'
require 'active_record/connection_adapters/abstract/database_statements'
require 'active_record/connection_adapters/abstract/quoting'
require 'active_record/connection_adapters/abstract/connection_specification'
+require 'active_record/connection_adapters/abstract/query_cache'
module ActiveRecord
module ConnectionAdapters # :nodoc:
@@ -22,8 +23,9 @@ module ActiveRecord
# SchemaStatements#remove_column are very useful.
class AbstractAdapter
include Quoting, DatabaseStatements, SchemaStatements
+ include QueryCache
@@row_even = true
-
+
def initialize(connection, logger = nil) #:nodoc:
@connection, @logger = connection, logger
@runtime = 0
@@ -41,7 +43,7 @@ module ActiveRecord
def supports_migrations?
false
end
-
+
# Does this adapter support using DISTINCT within COUNT? This is +true+
# for all adapters except sqlite.
def supports_count_distinct?
@@ -61,6 +63,19 @@ module ActiveRecord
rt
end
+ # QUOTING ==================================================
+
+ # Override to return the quoted table name if the database needs it
+ def quote_table_name(name)
+ name
+ end
+
+ # REFERENTIAL INTEGRITY ====================================
+
+ # Override to turn off referential integrity while executing +&block+
+ def disable_referential_integrity(&block)
+ yield
+ end
# CONNECTION MANAGEMENT ====================================
@@ -86,7 +101,7 @@ module ActiveRecord
end
# Lazily verify this connection, calling +active?+ only if it hasn't
- # been called for +timeout+ seconds.
+ # been called for +timeout+ seconds.
def verify!(timeout)
now = Time.now.to_i
if (now - @last_verification) > timeout
@@ -94,7 +109,7 @@ module ActiveRecord
@last_verification = now
end
end
-
+
# Provides access to the underlying database connection. Useful for
# when you need to call a proprietary method such as postgresql's lo_*
# methods
@@ -102,10 +117,17 @@ module ActiveRecord
@connection
end
+ def log_info(sql, name, runtime)
+ if @logger && @logger.debug?
+ name = "#{name.nil? ? "SQL" : name} (#{sprintf("%f", runtime)})"
+ @logger.debug format_log_entry(name, sql.squeeze(' '))
+ end
+ end
+
protected
def log(sql, name)
if block_given?
- if @logger and @logger.level <= Logger::INFO
+ if @logger and @logger.debug?
result = nil
seconds = Benchmark.realtime { result = yield }
@runtime += seconds
@@ -120,7 +142,7 @@ module ActiveRecord
end
rescue Exception => e
# Log message and raise exception.
- # Set last_verfication to 0, so that connection gets verified
+ # Set last_verification to 0, so that connection gets verified
# upon reentering the request loop
@last_verification = 0
message = "#{e.class.name}: #{e.message}: #{sql}"
@@ -128,17 +150,6 @@ module ActiveRecord
raise ActiveRecord::StatementInvalid, message
end
- def log_info(sql, name, runtime)
- return unless @logger
-
- @logger.debug(
- format_log_entry(
- "#{name.nil? ? "SQL" : name} (#{sprintf("%f", runtime)})",
- sql.gsub(/ +/, " ")
- )
- )
- end
-
def format_log_entry(message, dump = nil)
if ActiveRecord::Base.colorize_logging
if @@row_even
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/db2_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/db2_adapter.rb
deleted file mode 100644
index 7a3cd227..00000000
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/db2_adapter.rb
+++ /dev/null
@@ -1,228 +0,0 @@
-# Author/Maintainer: Maik Schmidt
-
-require 'active_record/connection_adapters/abstract_adapter'
-
-begin
- require 'db2/db2cli' unless self.class.const_defined?(:DB2CLI)
- require 'active_record/vendor/db2'
-
- module ActiveRecord
- class Base
- # Establishes a connection to the database that's used by
- # all Active Record objects
- def self.db2_connection(config) # :nodoc:
- config = config.symbolize_keys
- usr = config[:username]
- pwd = config[:password]
- schema = config[:schema]
-
- if config.has_key?(:database)
- database = config[:database]
- else
- raise ArgumentError, 'No database specified. Missing argument: database.'
- end
-
- connection = DB2::Connection.new(DB2::Environment.new)
- connection.connect(database, usr, pwd)
- ConnectionAdapters::DB2Adapter.new(connection, logger, :schema => schema)
- end
- end
-
- module ConnectionAdapters
- # The DB2 adapter works with the C-based CLI driver (http://rubyforge.org/projects/ruby-dbi/)
- #
- # Options:
- #
- # * :username -- Defaults to nothing
- # * :password -- Defaults to nothing
- # * :database -- The name of the database. No default, must be provided.
- # * :schema -- Database schema to be set initially.
- class DB2Adapter < AbstractAdapter
- def initialize(connection, logger, connection_options)
- super(connection, logger)
- @connection_options = connection_options
- if schema = @connection_options[:schema]
- with_statement do |stmt|
- stmt.exec_direct("SET SCHEMA=#{schema}")
- end
- end
- end
-
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- execute(sql, name = nil)
- id_value || last_insert_id
- end
-
- def execute(sql, name = nil)
- rows_affected = 0
- with_statement do |stmt|
- log(sql, name) do
- stmt.exec_direct(sql)
- rows_affected = stmt.row_count
- end
- end
- rows_affected
- end
-
- def begin_db_transaction
- @connection.set_auto_commit_off
- end
-
- def commit_db_transaction
- @connection.commit
- @connection.set_auto_commit_on
- end
-
- def rollback_db_transaction
- @connection.rollback
- @connection.set_auto_commit_on
- end
-
- def quote_column_name(column_name)
- column_name
- end
-
- def adapter_name()
- 'DB2'
- end
-
- def quote_string(string)
- string.gsub(/'/, "''") # ' (for ruby-mode)
- end
-
- def add_limit_offset!(sql, options)
- if limit = options[:limit]
- offset = options[:offset] || 0
- # The following trick was added by andrea+rails@webcom.it.
- sql.gsub!(/SELECT/i, 'SELECT B.* FROM (SELECT A.*, row_number() over () AS internal$rownum FROM (SELECT')
- sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{limit + offset}"
- end
- end
-
- def tables(name = nil)
- result = []
- schema = @connection_options[:schema] || '%'
- with_statement do |stmt|
- stmt.tables(schema).each { |t| result << t[2].downcase }
- end
- result
- end
-
- def indexes(table_name, name = nil)
- tmp = {}
- schema = @connection_options[:schema] || ''
- with_statement do |stmt|
- stmt.indexes(table_name, schema).each do |t|
- next unless t[5]
- next if t[4] == 'SYSIBM' # Skip system indexes.
- idx_name = t[5].downcase
- col_name = t[8].downcase
- if tmp.has_key?(idx_name)
- tmp[idx_name].columns << col_name
- else
- is_unique = t[3] == 0
- tmp[idx_name] = IndexDefinition.new(table_name, idx_name, is_unique, [col_name])
- end
- end
- end
- tmp.values
- end
-
- def columns(table_name, name = nil)
- result = []
- schema = @connection_options[:schema] || '%'
- with_statement do |stmt|
- stmt.columns(table_name, schema).each do |c|
- c_name = c[3].downcase
- c_default = c[12] == 'NULL' ? nil : c[12]
- c_default.gsub!(/^'(.*)'$/, '\1') if !c_default.nil?
- c_type = c[5].downcase
- c_type += "(#{c[6]})" if !c[6].nil? && c[6] != ''
- result << Column.new(c_name, c_default, c_type, c[17] == 'YES')
- end
- end
- result
- end
-
- def native_database_types
- {
- :primary_key => 'int generated by default as identity (start with 42) primary key',
- :string => { :name => 'varchar', :limit => 255 },
- :text => { :name => 'clob', :limit => 32768 },
- :integer => { :name => 'int' },
- :float => { :name => 'float' },
- :decimal => { :name => 'decimal' },
- :datetime => { :name => 'timestamp' },
- :timestamp => { :name => 'timestamp' },
- :time => { :name => 'time' },
- :date => { :name => 'date' },
- :binary => { :name => 'blob', :limit => 32768 },
- :boolean => { :name => 'decimal', :limit => 1 }
- }
- end
-
- def quoted_true
- '1'
- end
-
- def quoted_false
- '0'
- end
-
- def active?
- @connection.select_one 'select 1 from ibm.sysdummy1'
- true
- rescue Exception
- false
- end
-
- def reconnect!
- end
-
- def table_alias_length
- 128
- end
-
- private
-
- def with_statement
- stmt = DB2::Statement.new(@connection)
- yield stmt
- stmt.free
- end
-
- def last_insert_id
- row = select_one(<<-GETID.strip)
- with temp(id) as (values (identity_val_local())) select * from temp
- GETID
- row['id'].to_i
- end
-
- def select(sql, name = nil)
- rows = []
- with_statement do |stmt|
- log(sql, name) do
- stmt.exec_direct("#{sql.gsub(/=\s*null/i, 'IS NULL')} with ur")
- end
-
- while row = stmt.fetch_as_hash
- row.delete('internal$rownum')
- rows << row
- end
- end
- rows
- end
- end
- end
- end
-rescue LoadError
- # DB2 driver is unavailable.
- module ActiveRecord # :nodoc:
- class Base
- def self.db2_connection(config) # :nodoc:
- # Set up a reasonable error message
- raise LoadError, "DB2 Libraries could not be loaded."
- end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/firebird_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/firebird_adapter.rb
deleted file mode 100644
index 04f9f754..00000000
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/firebird_adapter.rb
+++ /dev/null
@@ -1,728 +0,0 @@
-# Author: Ken Kunz
-
-require 'active_record/connection_adapters/abstract_adapter'
-
-module FireRuby # :nodoc: all
- NON_EXISTENT_DOMAIN_ERROR = "335544569"
- class Database
- def self.db_string_for(config)
- unless config.has_key?(:database)
- raise ArgumentError, "No database specified. Missing argument: database."
- end
- host_string = config.values_at(:host, :service, :port).compact.first(2).join("/") if config[:host]
- [host_string, config[:database]].join(":")
- end
-
- def self.new_from_config(config)
- db = new db_string_for(config)
- db.character_set = config[:charset]
- return db
- end
- end
-end
-
-module ActiveRecord
- class << Base
- def firebird_connection(config) # :nodoc:
- require_library_or_gem 'fireruby'
- unless defined? FireRuby::SQLType
- raise AdapterNotFound,
- 'The Firebird adapter requires FireRuby version 0.4.0 or greater; you appear ' <<
- 'to be running an older version -- please update FireRuby (gem install fireruby).'
- end
- config.symbolize_keys!
- db = FireRuby::Database.new_from_config(config)
- connection_params = config.values_at(:username, :password)
- connection = db.connect(*connection_params)
- ConnectionAdapters::FirebirdAdapter.new(connection, logger, connection_params)
- end
- end
-
- module ConnectionAdapters
- class FirebirdColumn < Column # :nodoc:
- VARCHAR_MAX_LENGTH = 32_765
- BLOB_MAX_LENGTH = 32_767
-
- def initialize(name, domain, type, sub_type, length, precision, scale, default_source, null_flag)
- @firebird_type = FireRuby::SQLType.to_base_type(type, sub_type).to_s
-
- super(name.downcase, nil, @firebird_type, !null_flag)
-
- @default = parse_default(default_source) if default_source
- @limit = decide_limit(length)
- @domain, @sub_type, @precision, @scale = domain, sub_type, precision, scale.abs
- end
-
- def type
- if @domain =~ /BOOLEAN/
- :boolean
- elsif @type == :binary and @sub_type == 1
- :text
- else
- @type
- end
- end
-
- def default
- type_cast(decide_default) if @default
- end
-
- def self.value_to_boolean(value)
- %W(#{FirebirdAdapter.boolean_domain[:true]} true t 1).include? value.to_s.downcase
- end
-
- private
- def parse_default(default_source)
- default_source =~ /^\s*DEFAULT\s+(.*)\s*$/i
- return $1 unless $1.upcase == "NULL"
- end
-
- def decide_default
- if @default =~ /^'?(\d*\.?\d+)'?$/ or
- @default =~ /^'(.*)'$/ && [:text, :string, :binary, :boolean].include?(type)
- $1
- else
- firebird_cast_default
- end
- end
-
- # Submits a _CAST_ query to the database, casting the default value to the specified SQL type.
- # This enables Firebird to provide an actual value when context variables are used as column
- # defaults (such as CURRENT_TIMESTAMP).
- def firebird_cast_default
- sql = "SELECT CAST(#{@default} AS #{column_def}) FROM RDB$DATABASE"
- if connection = Base.active_connections.values.detect { |conn| conn && conn.adapter_name == 'Firebird' }
- connection.execute(sql).to_a.first['CAST']
- else
- raise ConnectionNotEstablished, "No Firebird connections established."
- end
- end
-
- def decide_limit(length)
- if text? or number?
- length
- elsif @firebird_type == 'BLOB'
- BLOB_MAX_LENGTH
- end
- end
-
- def column_def
- case @firebird_type
- when 'BLOB' then "VARCHAR(#{VARCHAR_MAX_LENGTH})"
- when 'CHAR', 'VARCHAR' then "#{@firebird_type}(#{@limit})"
- when 'NUMERIC', 'DECIMAL' then "#{@firebird_type}(#{@precision},#{@scale.abs})"
- when 'DOUBLE' then "DOUBLE PRECISION"
- else @firebird_type
- end
- end
-
- def simplified_type(field_type)
- if field_type == 'TIMESTAMP'
- :datetime
- else
- super
- end
- end
- end
-
- # The Firebird adapter relies on the FireRuby[http://rubyforge.org/projects/fireruby/]
- # extension, version 0.4.0 or later (available as a gem or from
- # RubyForge[http://rubyforge.org/projects/fireruby/]). FireRuby works with
- # Firebird 1.5.x on Linux, OS X and Win32 platforms.
- #
- # == Usage Notes
- #
- # === Sequence (Generator) Names
- # The Firebird adapter supports the same approach adopted for the Oracle
- # adapter. See ActiveRecord::Base#set_sequence_name for more details.
- #
- # Note that in general there is no need to create a BEFORE INSERT
- # trigger corresponding to a Firebird sequence generator when using
- # ActiveRecord. In other words, you don't have to try to make Firebird
- # simulate an AUTO_INCREMENT or +IDENTITY+ column. When saving a
- # new record, ActiveRecord pre-fetches the next sequence value for the table
- # and explicitly includes it in the +INSERT+ statement. (Pre-fetching the
- # next primary key value is the only reliable method for the Firebird
- # adapter to report back the +id+ after a successful insert.)
- #
- # === BOOLEAN Domain
- # Firebird 1.5 does not provide a native +BOOLEAN+ type. But you can easily
- # define a +BOOLEAN+ _domain_ for this purpose, e.g.:
- #
- # CREATE DOMAIN D_BOOLEAN AS SMALLINT CHECK (VALUE IN (0, 1) OR VALUE IS NULL);
- #
- # When the Firebird adapter encounters a column that is based on a domain
- # that includes "BOOLEAN" in the domain name, it will attempt to treat
- # the column as a +BOOLEAN+.
- #
- # By default, the Firebird adapter will assume that the BOOLEAN domain is
- # defined as above. This can be modified if needed. For example, if you
- # have a legacy schema with the following +BOOLEAN+ domain defined:
- #
- # CREATE DOMAIN BOOLEAN AS CHAR(1) CHECK (VALUE IN ('T', 'F'));
- #
- # ...you can add the following line to your environment.rb file:
- #
- # ActiveRecord::ConnectionAdapters::FirebirdAdapter.boolean_domain = { :true => 'T', :false => 'F' }
- #
- # === BLOB Elements
- # The Firebird adapter currently provides only limited support for +BLOB+
- # columns. You cannot currently retrieve or insert a +BLOB+ as an IO stream.
- # When selecting a +BLOB+, the entire element is converted into a String.
- # When inserting or updating a +BLOB+, the entire value is included in-line
- # in the SQL statement, limiting you to values <= 32KB in size.
- #
- # === Column Name Case Semantics
- # Firebird and ActiveRecord have somewhat conflicting case semantics for
- # column names.
- #
- # [*Firebird*]
- # The standard practice is to use unquoted column names, which can be
- # thought of as case-insensitive. (In fact, Firebird converts them to
- # uppercase.) Quoted column names (not typically used) are case-sensitive.
- # [*ActiveRecord*]
- # Attribute accessors corresponding to column names are case-sensitive.
- # The defaults for primary key and inheritance columns are lowercase, and
- # in general, people use lowercase attribute names.
- #
- # In order to map between the differing semantics in a way that conforms
- # to common usage for both Firebird and ActiveRecord, uppercase column names
- # in Firebird are converted to lowercase attribute names in ActiveRecord,
- # and vice-versa. Mixed-case column names retain their case in both
- # directions. Lowercase (quoted) Firebird column names are not supported.
- # This is similar to the solutions adopted by other adapters.
- #
- # In general, the best approach is to use unqouted (case-insensitive) column
- # names in your Firebird DDL (or if you must quote, use uppercase column
- # names). These will correspond to lowercase attributes in ActiveRecord.
- #
- # For example, a Firebird table based on the following DDL:
- #
- # CREATE TABLE products (
- # id BIGINT NOT NULL PRIMARY KEY,
- # "TYPE" VARCHAR(50),
- # name VARCHAR(255) );
- #
- # ...will correspond to an ActiveRecord model class called +Product+ with
- # the following attributes: +id+, +type+, +name+.
- #
- # ==== Quoting "TYPE" and other Firebird reserved words:
- # In ActiveRecord, the default inheritance column name is +type+. The word
- # _type_ is a Firebird reserved word, so it must be quoted in any Firebird
- # SQL statements. Because of the case mapping described above, you should
- # always reference this column using quoted-uppercase syntax
- # ("TYPE" ) within Firebird DDL or other SQL statements (as in the
- # example above). This holds true for any other Firebird reserved words used
- # as column names as well.
- #
- # === Migrations
- # The Firebird Adapter now supports Migrations.
- #
- # ==== Create/Drop Table and Sequence Generators
- # Creating or dropping a table will automatically create/drop a
- # correpsonding sequence generator, using the default naming convension.
- # You can specify a different name using the :sequence option; no
- # generator is created if :sequence is set to +false+.
- #
- # ==== Rename Table
- # The Firebird #rename_table Migration should be used with caution.
- # Firebird 1.5 lacks built-in support for this feature, so it is
- # implemented by making a copy of the original table (including column
- # definitions, indexes and data records), and then dropping the original
- # table. Constraints and Triggers are _not_ properly copied, so avoid
- # this method if your original table includes constraints (other than
- # the primary key) or triggers. (Consider manually copying your table
- # or using a view instead.)
- #
- # == Connection Options
- # The following options are supported by the Firebird adapter. None of the
- # options have default values.
- #
- # :database ::
- # Required option. Specifies one of: (i) a Firebird database alias;
- # (ii) the full path of a database file; _or_ (iii) a full Firebird
- # connection string. Do not specify :host , :service
- # or :port as separate options when using a full connection
- # string.
- # :host ::
- # Set to "remote.host.name" for remote database connections.
- # May be omitted for local connections if a full database path is
- # specified for :database . Some platforms require a value of
- # "localhost" for local connections when using a Firebird
- # database _alias_.
- # :service ::
- # Specifies a service name for the connection. Only used if :host
- # is provided. Required when connecting to a non-standard service.
- # :port ::
- # Specifies the connection port. Only used if :host is provided
- # and :service is not. Required when connecting to a non-standard
- # port and :service is not defined.
- # :username ::
- # Specifies the database user. May be omitted or set to +nil+ (together
- # with :password ) to use the underlying operating system user
- # credentials on supported platforms.
- # :password ::
- # Specifies the database password. Must be provided if :username
- # is explicitly specified; should be omitted if OS user credentials are
- # are being used.
- # :charset ::
- # Specifies the character set to be used by the connection. Refer to
- # Firebird documentation for valid options.
- class FirebirdAdapter < AbstractAdapter
- TEMP_COLUMN_NAME = 'AR$TEMP_COLUMN'
-
- @@boolean_domain = { :name => "d_boolean", :type => "smallint", :true => 1, :false => 0 }
- cattr_accessor :boolean_domain
-
- def initialize(connection, logger, connection_params = nil)
- super(connection, logger)
- @connection_params = connection_params
- end
-
- def adapter_name # :nodoc:
- 'Firebird'
- end
-
- def supports_migrations? # :nodoc:
- true
- end
-
- def native_database_types # :nodoc:
- {
- :primary_key => "BIGINT NOT NULL PRIMARY KEY",
- :string => { :name => "varchar", :limit => 255 },
- :text => { :name => "blob sub_type text" },
- :integer => { :name => "bigint" },
- :decimal => { :name => "decimal" },
- :numeric => { :name => "numeric" },
- :float => { :name => "float" },
- :datetime => { :name => "timestamp" },
- :timestamp => { :name => "timestamp" },
- :time => { :name => "time" },
- :date => { :name => "date" },
- :binary => { :name => "blob sub_type 0" },
- :boolean => boolean_domain
- }
- end
-
- # Returns true for Firebird adapter (since Firebird requires primary key
- # values to be pre-fetched before insert). See also #next_sequence_value.
- def prefetch_primary_key?(table_name = nil)
- true
- end
-
- def default_sequence_name(table_name, primary_key = nil) # :nodoc:
- "#{table_name}_seq"
- end
-
-
- # QUOTING ==================================================
-
- def quote(value, column = nil) # :nodoc:
- if [Time, DateTime].include?(value.class)
- "CAST('#{value.strftime("%Y-%m-%d %H:%M:%S")}' AS TIMESTAMP)"
- else
- super
- end
- end
-
- def quote_string(string) # :nodoc:
- string.gsub(/'/, "''")
- end
-
- def quote_column_name(column_name) # :nodoc:
- %Q("#{ar_to_fb_case(column_name.to_s)}")
- end
-
- def quoted_true # :nodoc:
- quote(boolean_domain[:true])
- end
-
- def quoted_false # :nodoc:
- quote(boolean_domain[:false])
- end
-
-
- # CONNECTION MANAGEMENT ====================================
-
- def active? # :nodoc:
- not @connection.closed?
- end
-
- def disconnect! # :nodoc:
- @connection.close rescue nil
- end
-
- def reconnect! # :nodoc:
- disconnect!
- @connection = @connection.database.connect(*@connection_params)
- end
-
-
- # DATABASE STATEMENTS ======================================
-
- def select_all(sql, name = nil) # :nodoc:
- select(sql, name)
- end
-
- def select_one(sql, name = nil) # :nodoc:
- select(sql, name).first
- end
-
- def execute(sql, name = nil, &block) # :nodoc:
- log(sql, name) do
- if @transaction
- @connection.execute(sql, @transaction, &block)
- else
- @connection.execute_immediate(sql, &block)
- end
- end
- end
-
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) # :nodoc:
- execute(sql, name)
- id_value
- end
-
- alias_method :update, :execute
- alias_method :delete, :execute
-
- def begin_db_transaction() # :nodoc:
- @transaction = @connection.start_transaction
- end
-
- def commit_db_transaction() # :nodoc:
- @transaction.commit
- ensure
- @transaction = nil
- end
-
- def rollback_db_transaction() # :nodoc:
- @transaction.rollback
- ensure
- @transaction = nil
- end
-
- def add_limit_offset!(sql, options) # :nodoc:
- if options[:limit]
- limit_string = "FIRST #{options[:limit]}"
- limit_string << " SKIP #{options[:offset]}" if options[:offset]
- sql.sub!(/\A(\s*SELECT\s)/i, '\&' + limit_string + ' ')
- end
- end
-
- # Returns the next sequence value from a sequence generator. Not generally
- # called directly; used by ActiveRecord to get the next primary key value
- # when inserting a new database record (see #prefetch_primary_key?).
- def next_sequence_value(sequence_name)
- FireRuby::Generator.new(sequence_name, @connection).next(1)
- end
-
-
- # SCHEMA STATEMENTS ========================================
-
- def current_database # :nodoc:
- file = @connection.database.file.split(':').last
- File.basename(file, '.*')
- end
-
- def recreate_database! # :nodoc:
- sql = "SELECT rdb$character_set_name FROM rdb$database"
- charset = execute(sql).to_a.first[0].rstrip
- disconnect!
- @connection.database.drop(*@connection_params)
- FireRuby::Database.create(@connection.database.file,
- @connection_params[0], @connection_params[1], 4096, charset)
- end
-
- def tables(name = nil) # :nodoc:
- sql = "SELECT rdb$relation_name FROM rdb$relations WHERE rdb$system_flag = 0"
- execute(sql, name).collect { |row| row[0].rstrip.downcase }
- end
-
- def indexes(table_name, name = nil) # :nodoc:
- index_metadata(table_name, false, name).inject([]) do |indexes, row|
- if indexes.empty? or indexes.last.name != row[0]
- indexes << IndexDefinition.new(table_name, row[0].rstrip.downcase, row[1] == 1, [])
- end
- indexes.last.columns << row[2].rstrip.downcase
- indexes
- end
- end
-
- def columns(table_name, name = nil) # :nodoc:
- sql = <<-end_sql
- SELECT r.rdb$field_name, r.rdb$field_source, f.rdb$field_type, f.rdb$field_sub_type,
- f.rdb$field_length, f.rdb$field_precision, f.rdb$field_scale,
- COALESCE(r.rdb$default_source, f.rdb$default_source) rdb$default_source,
- COALESCE(r.rdb$null_flag, f.rdb$null_flag) rdb$null_flag
- FROM rdb$relation_fields r
- JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name
- WHERE r.rdb$relation_name = '#{table_name.to_s.upcase}'
- ORDER BY r.rdb$field_position
- end_sql
- execute(sql, name).collect do |field|
- field_values = field.values.collect do |value|
- case value
- when String then value.rstrip
- when FireRuby::Blob then value.to_s
- else value
- end
- end
- FirebirdColumn.new(*field_values)
- end
- end
-
- def create_table(name, options = {}) # :nodoc:
- begin
- super
- rescue StatementInvalid
- raise unless non_existent_domain_error?
- create_boolean_domain
- super
- end
- unless options[:id] == false or options[:sequence] == false
- sequence_name = options[:sequence] || default_sequence_name(name)
- create_sequence(sequence_name)
- end
- end
-
- def drop_table(name, options = {}) # :nodoc:
- super(name)
- unless options[:sequence] == false
- sequence_name = options[:sequence] || default_sequence_name(name)
- drop_sequence(sequence_name) if sequence_exists?(sequence_name)
- end
- end
-
- def add_column(table_name, column_name, type, options = {}) # :nodoc:
- super
- rescue StatementInvalid
- raise unless non_existent_domain_error?
- create_boolean_domain
- super
- end
-
- def change_column(table_name, column_name, type, options = {}) # :nodoc:
- change_column_type(table_name, column_name, type, options)
- change_column_position(table_name, column_name, options[:position]) if options.include?(:position)
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
- end
-
- def change_column_default(table_name, column_name, default) # :nodoc:
- table_name = table_name.to_s.upcase
- sql = <<-end_sql
- UPDATE rdb$relation_fields f1
- SET f1.rdb$default_source =
- (SELECT f2.rdb$default_source FROM rdb$relation_fields f2
- WHERE f2.rdb$relation_name = '#{table_name}'
- AND f2.rdb$field_name = '#{TEMP_COLUMN_NAME}'),
- f1.rdb$default_value =
- (SELECT f2.rdb$default_value FROM rdb$relation_fields f2
- WHERE f2.rdb$relation_name = '#{table_name}'
- AND f2.rdb$field_name = '#{TEMP_COLUMN_NAME}')
- WHERE f1.rdb$relation_name = '#{table_name}'
- AND f1.rdb$field_name = '#{ar_to_fb_case(column_name.to_s)}'
- end_sql
- transaction do
- add_column(table_name, TEMP_COLUMN_NAME, :string, :default => default)
- execute sql
- remove_column(table_name, TEMP_COLUMN_NAME)
- end
- end
-
- def rename_column(table_name, column_name, new_column_name) # :nodoc:
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TO #{new_column_name}"
- end
-
- def remove_index(table_name, options) #:nodoc:
- execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
- end
-
- def rename_table(name, new_name) # :nodoc:
- if table_has_constraints_or_dependencies?(name)
- raise ActiveRecordError,
- "Table #{name} includes constraints or dependencies that are not supported by " <<
- "the Firebird rename_table migration. Try explicitly removing the constraints/" <<
- "dependencies first, or manually renaming the table."
- end
-
- transaction do
- copy_table(name, new_name)
- copy_table_indexes(name, new_name)
- end
- begin
- copy_table_data(name, new_name)
- copy_sequence_value(name, new_name)
- rescue
- drop_table(new_name)
- raise
- end
- drop_table(name)
- end
-
- def dump_schema_information # :nodoc:
- super << ";\n"
- end
-
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) # :nodoc:
- case type
- when :integer then integer_sql_type(limit)
- when :float then float_sql_type(limit)
- when :string then super(type, limit, precision, scale)
- else super(type, limit, precision, scale)
- end
- end
-
- private
- def integer_sql_type(limit)
- case limit
- when (1..2) then 'smallint'
- when (3..4) then 'integer'
- else 'bigint'
- end
- end
-
- def float_sql_type(limit)
- limit.to_i <= 4 ? 'float' : 'double precision'
- end
-
- def select(sql, name = nil)
- execute(sql, name).collect do |row|
- hashed_row = {}
- row.each do |column, value|
- value = value.to_s if FireRuby::Blob === value
- hashed_row[fb_to_ar_case(column)] = value
- end
- hashed_row
- end
- end
-
- def primary_key(table_name)
- if pk_row = index_metadata(table_name, true).to_a.first
- pk_row[2].rstrip.downcase
- end
- end
-
- def index_metadata(table_name, pk, name = nil)
- sql = <<-end_sql
- SELECT i.rdb$index_name, i.rdb$unique_flag, s.rdb$field_name
- FROM rdb$indices i
- JOIN rdb$index_segments s ON i.rdb$index_name = s.rdb$index_name
- LEFT JOIN rdb$relation_constraints c ON i.rdb$index_name = c.rdb$index_name
- WHERE i.rdb$relation_name = '#{table_name.to_s.upcase}'
- end_sql
- if pk
- sql << "AND c.rdb$constraint_type = 'PRIMARY KEY'\n"
- else
- sql << "AND (c.rdb$constraint_type IS NULL OR c.rdb$constraint_type != 'PRIMARY KEY')\n"
- end
- sql << "ORDER BY i.rdb$index_name, s.rdb$field_position\n"
- execute sql, name
- end
-
- def change_column_type(table_name, column_name, type, options = {})
- sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
- execute sql
- rescue StatementInvalid
- raise unless non_existent_domain_error?
- create_boolean_domain
- execute sql
- end
-
- def change_column_position(table_name, column_name, position)
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} POSITION #{position}"
- end
-
- def copy_table(from, to)
- table_opts = {}
- if pk = primary_key(from)
- table_opts[:primary_key] = pk
- else
- table_opts[:id] = false
- end
- create_table(to, table_opts) do |table|
- from_columns = columns(from).reject { |col| col.name == table_opts[:primary_key] }
- from_columns.each do |column|
- col_opts = [:limit, :default, :null].inject({}) { |opts, opt| opts.merge(opt => column.send(opt)) }
- table.column column.name, column.type, col_opts
- end
- end
- end
-
- def copy_table_indexes(from, to)
- indexes(from).each do |index|
- unless index.name[from.to_s]
- raise ActiveRecordError,
- "Cannot rename index #{index.name}, because the index name does not include " <<
- "the original table name (#{from}). Try explicitly removing the index on the " <<
- "original table and re-adding it on the new (renamed) table."
- end
- options = {}
- options[:name] = index.name.gsub(from.to_s, to.to_s)
- options[:unique] = index.unique
- add_index(to, index.columns, options)
- end
- end
-
- def copy_table_data(from, to)
- execute "INSERT INTO #{to} SELECT * FROM #{from}", "Copy #{from} data to #{to}"
- end
-
- def copy_sequence_value(from, to)
- sequence_value = FireRuby::Generator.new(default_sequence_name(from), @connection).last
- execute "SET GENERATOR #{default_sequence_name(to)} TO #{sequence_value}"
- end
-
- def sequence_exists?(sequence_name)
- FireRuby::Generator.exists?(sequence_name, @connection)
- end
-
- def create_sequence(sequence_name)
- FireRuby::Generator.create(sequence_name.to_s, @connection)
- end
-
- def drop_sequence(sequence_name)
- FireRuby::Generator.new(sequence_name.to_s, @connection).drop
- end
-
- def create_boolean_domain
- sql = <<-end_sql
- CREATE DOMAIN #{boolean_domain[:name]} AS #{boolean_domain[:type]}
- CHECK (VALUE IN (#{quoted_true}, #{quoted_false}) OR VALUE IS NULL)
- end_sql
- execute sql rescue nil
- end
-
- def table_has_constraints_or_dependencies?(table_name)
- table_name = table_name.to_s.upcase
- sql = <<-end_sql
- SELECT 1 FROM rdb$relation_constraints
- WHERE rdb$relation_name = '#{table_name}'
- AND rdb$constraint_type IN ('UNIQUE', 'FOREIGN KEY', 'CHECK')
- UNION
- SELECT 1 FROM rdb$dependencies
- WHERE rdb$depended_on_name = '#{table_name}'
- AND rdb$depended_on_type = 0
- end_sql
- !select(sql).empty?
- end
-
- def non_existent_domain_error?
- $!.message.include? FireRuby::NON_EXISTENT_DOMAIN_ERROR
- end
-
- # Maps uppercase Firebird column names to lowercase for ActiveRecord;
- # mixed-case columns retain their original case.
- def fb_to_ar_case(column_name)
- column_name =~ /[[:lower:]]/ ? column_name : column_name.downcase
- end
-
- # Maps lowercase ActiveRecord column names to uppercase for Fierbird;
- # mixed-case columns retain their original case.
- def ar_to_fb_case(column_name)
- column_name =~ /[[:upper:]]/ ? column_name : column_name.upcase
- end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb
deleted file mode 100644
index e6dbbd7e..00000000
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/frontbase_adapter.rb
+++ /dev/null
@@ -1,861 +0,0 @@
-# Requires FrontBase Ruby bindings (gem install ruby-frontbase)
-
-require 'active_record/connection_adapters/abstract_adapter'
-
-FB_TRACE = false
-
-module ActiveRecord
-
- class Base
- class << self
- # Establishes a connection to the database that's used by all Active Record objects.
- def frontbase_connection(config) # :nodoc:
- # FrontBase only supports one unnamed sequence per table
- define_attr_method(:set_sequence_name, :sequence_name, &Proc.new {|*args| nil})
-
- config = config.symbolize_keys
- database = config[:database]
- port = config[:port]
- host = config[:host]
- username = config[:username]
- password = config[:password]
- dbpassword = config[:dbpassword]
- session_name = config[:session_name]
-
- dbpassword = '' if dbpassword.nil?
-
- # Turn off colorization since it makes tail/less output difficult
- self.colorize_logging = false
-
- require_library_or_gem 'frontbase' unless self.class.const_defined? :FBSQL_Connect
-
- # Check bindings version
- version = "0.0.0"
- version = FBSQL_Connect::FB_BINDINGS_VERSION if defined? FBSQL_Connect::FB_BINDINGS_VERSION
-
- if ActiveRecord::ConnectionAdapters::FrontBaseAdapter.compare_versions(version,"1.0.0") == -1
- raise AdapterNotFound,
- 'The FrontBase adapter requires ruby-frontbase version 1.0.0 or greater; you appear ' <<
- "to be running an older version (#{version}) -- please update ruby-frontbase (gem install ruby-frontbase)."
- end
- connection = FBSQL_Connect.connect(host, port, database, username, password, dbpassword, session_name)
- ConnectionAdapters::FrontBaseAdapter.new(connection, logger, [host, port, database, username, password, dbpassword, session_name], config)
- end
- end
- end
-
- module ConnectionAdapters
-
- # From EOF Documentation....
- # buffer should have space for EOUniqueBinaryKeyLength (12) bytes.
- # Assigns a world-wide unique ID made up of:
- # < Sequence [2], ProcessID [2] , Time [4], IP Addr [4] >
-
- class TwelveByteKey < String #:nodoc:
- @@mutex = Mutex.new
- @@sequence_number = rand(65536)
- @@key_cached_pid_component = nil
- @@key_cached_ip_component = nil
-
- def initialize(string = nil)
- # Generate a unique key
- if string.nil?
- new_key = replace('_' * 12)
-
- new_key[0..1] = self.class.key_sequence_component
- new_key[2..3] = self.class.key_pid_component
- new_key[4..7] = self.class.key_time_component
- new_key[8..11] = self.class.key_ip_component
- new_key
- else
- if string.size == 24
- string.gsub!(/[[:xdigit:]]{2}/) { |x| x.hex.chr }
- end
- raise "string is not 12 bytes long" unless string.size == 12
- super(string)
- end
- end
-
- def inspect
- unpack("H*").first.upcase
- end
-
- alias_method :to_s, :inspect
-
- private
-
- class << self
- def key_sequence_component
- seq = nil
- @@mutex.synchronize do
- seq = @@sequence_number
- @@sequence_number = (@@sequence_number + 1) % 65536
- end
-
- sequence_component = "__"
- sequence_component[0] = seq >> 8
- sequence_component[1] = seq
- sequence_component
- end
-
- def key_pid_component
- if @@key_cached_pid_component.nil?
- @@mutex.synchronize do
- pid = $$
- pid_component = "__"
- pid_component[0] = pid >> 8
- pid_component[1] = pid
- @@key_cached_pid_component = pid_component
- end
- end
- @@key_cached_pid_component
- end
-
- def key_time_component
- time = Time.new.to_i
- time_component = "____"
- time_component[0] = (time & 0xFF000000) >> 24
- time_component[1] = (time & 0x00FF0000) >> 16
- time_component[2] = (time & 0x0000FF00) >> 8
- time_component[3] = (time & 0x000000FF)
- time_component
- end
-
- def key_ip_component
- if @@key_cached_ip_component.nil?
- @@mutex.synchronize do
- old_lookup_flag = BasicSocket.do_not_reverse_lookup
- BasicSocket.do_not_reverse_lookup = true
- udpsocket = UDPSocket.new
- udpsocket.connect("17.112.152.32",1)
- ip_string = udpsocket.addr[3]
- BasicSocket.do_not_reverse_lookup = old_lookup_flag
- packed = Socket.pack_sockaddr_in(0,ip_string)
- addr_subset = packed[4..7]
- ip = addr_subset[0] << 24 | addr_subset[1] << 16 | addr_subset[2] << 8 | addr_subset[3]
- ip_component = "____"
- ip_component[0] = (ip & 0xFF000000) >> 24
- ip_component[1] = (ip & 0x00FF0000) >> 16
- ip_component[2] = (ip & 0x0000FF00) >> 8
- ip_component[3] = (ip & 0x000000FF)
- @@key_cached_ip_component = ip_component
- end
- end
- @@key_cached_ip_component
- end
- end
- end
-
- class FrontBaseColumn < Column #:nodoc:
- attr_reader :fb_autogen
-
- def initialize(base, name, type, typename, limit, precision, scale, default, nullable)
-
- @base = base
- @name = name
- @type = simplified_type(type,typename,limit)
- @limit = limit
- @precision = precision
- @scale = scale
- @default = default
- @null = nullable == "YES"
- @text = [:string, :text].include? @type
- @number = [:float, :integer, :decimal].include? @type
- @fb_autogen = false
-
- if @default
- @default.gsub!(/^'(.*)'$/,'\1') if @text
- @fb_autogen = @default.include?("SELECT UNIQUE FROM")
- case @type
- when :boolean
- @default = @default == "TRUE"
- when :binary
- if @default != "X''"
- buffer = ""
- @default.scan(/../) { |h| buffer << h.hex.chr }
- @default = buffer
- else
- @default = ""
- end
- else
- @default = type_cast(@default)
- end
- end
- end
-
- # Casts value (which is a String) to an appropriate instance.
- def type_cast(value)
- if type == :twelvebytekey
- ActiveRecord::ConnectionAdapters::TwelveByteKey.new(value)
- else
- super(value)
- end
- end
-
- def type_cast_code(var_name)
- if type == :twelvebytekey
- "ActiveRecord::ConnectionAdapters::TwelveByteKey.new(#{var_name})"
- else
- super(var_name)
- end
- end
-
- private
- def simplified_type(field_type, type_name,limit)
- ret_type = :string
- puts "typecode: [#{field_type}] [#{type_name}]" if FB_TRACE
-
- # 12 byte primary keys are a special case that Apple's EOF
- # used heavily. Optimize for this case
- if field_type == 11 && limit == 96
- ret_type = :twelvebytekey # BIT(96)
- else
- ret_type = case field_type
- when 1 then :boolean # BOOLEAN
- when 2 then :integer # INTEGER
- when 4 then :float # FLOAT
- when 10 then :string # CHARACTER VARYING
- when 11 then :bitfield # BIT
- when 13 then :date # DATE
- when 14 then :time # TIME
- when 16 then :timestamp # TIMESTAMP
- when 20 then :text # CLOB
- when 21 then :binary # BLOB
- when 22 then :integer # TINYINT
- else
- puts "ERROR: Unknown typecode: [#{field_type}] [#{type_name}]"
- end
- end
- puts "ret_type: #{ret_type.inspect}" if FB_TRACE
- ret_type
- end
- end
-
- class FrontBaseAdapter < AbstractAdapter
-
- class << self
- def compare_versions(v1, v2)
- v1_seg = v1.split(".")
- v2_seg = v2.split(".")
- 0.upto([v1_seg.length,v2_seg.length].min) do |i|
- step = (v1_seg[i].to_i <=> v2_seg[i].to_i)
- return step unless step == 0
- end
- return v1_seg.length <=> v2_seg.length
- end
- end
-
- def initialize(connection, logger, connection_options, config)
- super(connection, logger)
- @connection_options, @config = connection_options, config
- @transaction_mode = :pessimistic
-
- # Start out in auto-commit mode
- self.rollback_db_transaction
-
- # threaded_connections_test.rb will fail unless we set the session
- # to optimistic locking mode
-# set_pessimistic_transactions
-# execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC"
- end
-
- # Returns the human-readable name of the adapter. Use mixed case - one
- # can always use downcase if needed.
- def adapter_name #:nodoc:
- 'FrontBase'
- end
-
- # Does this adapter support migrations? Backend specific, as the
- # abstract adapter always returns +false+.
- def supports_migrations? #:nodoc:
- true
- end
-
- def native_database_types #:nodoc:
- {
- :primary_key => "INTEGER DEFAULT UNIQUE PRIMARY KEY",
- :string => { :name => "VARCHAR", :limit => 255 },
- :text => { :name => "CLOB" },
- :integer => { :name => "INTEGER" },
- :float => { :name => "FLOAT" },
- :decimal => { :name => "DECIMAL" },
- :datetime => { :name => "TIMESTAMP" },
- :timestamp => { :name => "TIMESTAMP" },
- :time => { :name => "TIME" },
- :date => { :name => "DATE" },
- :binary => { :name => "BLOB" },
- :boolean => { :name => "BOOLEAN" },
- :twelvebytekey => { :name => "BYTE", :limit => 12}
- }
- end
-
-
- # QUOTING ==================================================
-
- # Quotes the column value to help prevent
- # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
- def quote(value, column = nil)
- return value.quoted_id if value.respond_to?(:quoted_id)
-
- retvalue = ""
-
- puts "quote(#{value.inspect}(#{value.class}),#{column.type.inspect})" if FB_TRACE
- # If a column was passed in, use column type information
- unless value.nil?
- if column
- retvalue = case column.type
- when :string
- if value.kind_of?(String)
- "'#{quote_string(value.to_s)}'" # ' (for ruby-mode)
- else
- "'#{quote_string(value.to_yaml)}'"
- end
- when :integer
- if value.kind_of?(TrueClass)
- '1'
- elsif value.kind_of?(FalseClass)
- '0'
- else
- value.to_i.to_s
- end
- when :float
- value.to_f.to_s
- when :decimal
- value.to_d.to_s("F")
- when :datetime, :timestamp
- "TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
- when :time
- "TIME '#{value.strftime("%H:%M:%S")}'"
- when :date
- "DATE '#{value.strftime("%Y-%m-%d")}'"
- when :twelvebytekey
- value = value.to_s.unpack("H*").first unless value.kind_of?(TwelveByteKey)
- "X'#{value.to_s}'"
- when :boolean
- value = quoted_true if value.kind_of?(TrueClass)
- value = quoted_false if value.kind_of?(FalseClass)
- value
- when :binary
- blob_handle = @connection.create_blob(value.to_s)
- puts "SQL -> Insert #{value.to_s.length} byte blob as #{retvalue}" if FB_TRACE
- blob_handle.handle
- when :text
- if value.kind_of?(String)
- clobdata = value.to_s # ' (for ruby-mode)
- else
- clobdata = value.to_yaml
- end
- clob_handle = @connection.create_clob(clobdata)
- puts "SQL -> Insert #{value.to_s.length} byte clob as #{retvalue}" if FB_TRACE
- clob_handle.handle
- else
- raise "*** UNKNOWN TYPE: #{column.type.inspect}"
- end # case
- # Since we don't have column type info, make a best guess based
- # on the Ruby class of the value
- else
- retvalue = case value
- when ActiveRecord::ConnectionAdapters::TwelveByteKey
- s = value.unpack("H*").first
- "X'#{s}'"
- when String
- if column && column.type == :binary
- s = value.unpack("H*").first
- "X'#{s}'"
- elsif column && [:integer, :float, :decimal].include?(column.type)
- value.to_s
- else
- "'#{quote_string(value)}'" # ' (for ruby-mode)
- end
- when NilClass
- "NULL"
- when TrueClass
- (column && column.type == :integer ? '1' : quoted_true)
- when FalseClass
- (column && column.type == :integer ? '0' : quoted_false)
- when Float, Fixnum, Bignum, BigDecimal
- value.to_s
- when Time, Date, DateTime
- if column
- case column.type
- when :date
- "DATE '#{value.strftime("%Y-%m-%d")}'"
- when :time
- "TIME '#{value.strftime("%H:%M:%S")}'"
- when :timestamp
- "TIMESTAMP '#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
- else
- raise NotImplementedError, "Unknown column type!"
- end # case
- else # Column wasn't passed in, so try to guess the right type
- if value.kind_of? Date
- "DATE '#{value.strftime("%Y-%m-%d")}'"
- else
- if [:hour, :min, :sec].all? {|part| value.send(:part).zero? }
- "TIME '#{value.strftime("%H:%M:%S")}'"
- else
- "TIMESTAMP '#{quoted_date(value)}'"
- end
- end
- end #if column
- else
- "'#{quote_string(value.to_yaml)}'"
- end #case
- end
- else
- retvalue = "NULL"
- end
-
- retvalue
- end # def
-
- # Quotes a string, escaping any ' (single quote) characters.
- def quote_string(s)
- s.gsub(/'/, "''") # ' (for ruby-mode)
- end
-
- def quote_column_name(name) #:nodoc:
- %( "#{name}" )
- end
-
- def quoted_true
- "true"
- end
-
- def quoted_false
- "false"
- end
-
-
- # CONNECTION MANAGEMENT ====================================
-
- def active?
- true if @connection.status == 1
- rescue => e
- false
- end
-
- def reconnect!
- @connection.close rescue nil
- @connection = FBSQL_Connect.connect(*@connection_options.first(7))
- end
-
- # Close this connection
- def disconnect!
- @connection.close rescue nil
- @active = false
- end
-
- # DATABASE STATEMENTS ======================================
-
- # Returns an array of record hashes with the column names as keys and
- # column values as values.
- def select_all(sql, name = nil) #:nodoc:
- fbsql = cleanup_fb_sql(sql)
- return_value = []
- fbresult = execute(sql, name)
- puts "select_all SQL -> #{fbsql}" if FB_TRACE
- columns = fbresult.columns
-
- fbresult.each do |row|
- puts "SQL <- #{row.inspect}" if FB_TRACE
- hashed_row = {}
- colnum = 0
- row.each do |col|
- hashed_row[columns[colnum]] = col
- if col.kind_of?(FBSQL_LOB)
- hashed_row[columns[colnum]] = col.read
- end
- colnum += 1
- end
- puts "raw row: #{hashed_row.inspect}" if FB_TRACE
- return_value << hashed_row
- end
- return_value
- end
-
- def select_one(sql, name = nil) #:nodoc:
- fbsql = cleanup_fb_sql(sql)
- return_value = []
- fbresult = execute(fbsql, name)
- puts "SQL -> #{fbsql}" if FB_TRACE
- columns = fbresult.columns
-
- fbresult.each do |row|
- puts "SQL <- #{row.inspect}" if FB_TRACE
- hashed_row = {}
- colnum = 0
- row.each do |col|
- hashed_row[columns[colnum]] = col
- if col.kind_of?(FBSQL_LOB)
- hashed_row[columns[colnum]] = col.read
- end
- colnum += 1
- end
- return_value << hashed_row
- break
- end
- fbresult.clear
- return_value.first
- end
-
- def query(sql, name = nil) #:nodoc:
- fbsql = cleanup_fb_sql(sql)
- puts "SQL(query) -> #{fbsql}" if FB_TRACE
- log(fbsql, name) { @connection.query(fbsql) }
- rescue => e
- puts "FB Exception: #{e.inspect}" if FB_TRACE
- raise e
- end
-
- def execute(sql, name = nil) #:nodoc:
- fbsql = cleanup_fb_sql(sql)
- puts "SQL(execute) -> #{fbsql}" if FB_TRACE
- log(fbsql, name) { @connection.query(fbsql) }
- rescue ActiveRecord::StatementInvalid => e
- if e.message.scan(/Table name - \w* - exists/).empty?
- puts "FB Exception: #{e.inspect}" if FB_TRACE
- raise e
- end
- end
-
- # Returns the last auto-generated ID from the affected table.
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
- puts "SQL -> #{sql.inspect}" if FB_TRACE
- execute(sql, name)
- id_value || pk
- end
-
- # Executes the update statement and returns the number of rows affected.
- def update(sql, name = nil) #:nodoc:
- puts "SQL -> #{sql.inspect}" if FB_TRACE
- execute(sql, name).num_rows
- end
-
- alias_method :delete, :update #:nodoc:
-
- def set_pessimistic_transactions
- if @transaction_mode == :optimistic
- execute "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, LOCKING PESSIMISTIC, READ WRITE"
- @transaction_mode = :pessimistic
- end
- end
-
- def set_optimistic_transactions
- if @transaction_mode == :pessimistic
- execute "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ WRITE, LOCKING OPTIMISTIC"
- @transaction_mode = :optimistic
- end
- end
-
- def begin_db_transaction #:nodoc:
- execute "SET COMMIT FALSE" rescue nil
- end
-
- def commit_db_transaction #:nodoc:
- execute "COMMIT"
- ensure
- execute "SET COMMIT TRUE"
- end
-
- def rollback_db_transaction #:nodoc:
- execute "ROLLBACK"
- ensure
- execute "SET COMMIT TRUE"
- end
-
- def add_limit_offset!(sql, options) #:nodoc:
- if limit = options[:limit]
- offset = options[:offset] || 0
-
-# Here is the full syntax FrontBase supports:
-# (from gclem@frontbase.com)
-#
-# TOP
-# TOP ( , )
-
- # "TOP 0" is not allowed, so we have
- # to use a cheap trick.
- if limit.zero?
- case sql
- when /WHERE/i
- sql.sub!(/WHERE/i, 'WHERE 0 = 1 AND ')
- when /ORDER\s+BY/i
- sql.sub!(/ORDER\s+BY/i, 'WHERE 0 = 1 ORDER BY')
- else
- sql << 'WHERE 0 = 1'
- end
- else
- if offset.zero?
- sql.replace sql.gsub("SELECT ","SELECT TOP #{limit} ")
- else
- sql.replace sql.gsub("SELECT ","SELECT TOP(#{offset},#{limit}) ")
- end
- end
- end
- end
-
- def prefetch_primary_key?(table_name = nil)
- true
- end
-
- # Returns the next sequence value from a sequence generator. Not generally
- # called directly; used by ActiveRecord to get the next primary key value
- # when inserting a new database record (see #prefetch_primary_key?).
- def next_sequence_value(sequence_name)
- unique = select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value")
- # The test cases cannot handle a zero primary key
- unique.zero? ? select_value("SELECT UNIQUE FROM #{sequence_name}","Next Sequence Value") : unique
- end
-
- def default_sequence_name(table, column)
- table
- end
-
- # Set the sequence to the max value of the table's column.
- def reset_sequence!(table, column, sequence = nil)
- klasses = classes_for_table_name(table)
- klass = klasses.nil? ? nil : klasses.first
- pk = klass.primary_key unless klass.nil?
- if pk && klass.columns_hash[pk].type == :integer
- execute("SET UNIQUE FOR #{klass.table_name}(#{pk})")
- end
- end
-
- def classes_for_table_name(table)
- ActiveRecord::Base.send(:subclasses).select {|klass| klass.table_name == table}
- end
-
- def reset_pk_sequence!(table, pk = nil, sequence = nil)
- klasses = classes_for_table_name(table)
- klass = klasses.nil? ? nil : klasses.first
- pk = klass.primary_key unless klass.nil?
- if pk && klass.columns_hash[pk].type == :integer
- mpk = select_value("SELECT MAX(#{pk}) FROM #{table}")
- execute("SET UNIQUE FOR #{klass.table_name}(#{pk})")
- end
- end
-
- # SCHEMA STATEMENTS ========================================
-
- def structure_dump #:nodoc:
- select_all("SHOW TABLES").inject('') do |structure, table|
- structure << select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] << ";\n\n"
- end
- end
-
- def recreate_database(name) #:nodoc:
- drop_database(name)
- create_database(name)
- end
-
- def create_database(name) #:nodoc:
- execute "CREATE DATABASE #{name}"
- end
-
- def drop_database(name) #:nodoc:
- execute "DROP DATABASE #{name}"
- end
-
- def current_database
- select_value('SELECT "CATALOG_NAME" FROM INFORMATION_SCHEMA.CATALOGS').downcase
- end
-
- def tables(name = nil) #:nodoc:
- select_values(<<-SQL, nil)
- SELECT "TABLE_NAME"
- FROM INFORMATION_SCHEMA.TABLES AS T0,
- INFORMATION_SCHEMA.SCHEMATA AS T1
- WHERE T0.SCHEMA_PK = T1.SCHEMA_PK
- AND "SCHEMA_NAME" = CURRENT_SCHEMA
- SQL
- end
-
- def indexes(table_name, name = nil)#:nodoc:
- indexes = []
- current_index = nil
- sql = <<-SQL
- SELECT INDEX_NAME, T2.ORDINAL_POSITION, INDEX_COLUMN_COUNT, INDEX_TYPE,
- "COLUMN_NAME", IS_NULLABLE
- FROM INFORMATION_SCHEMA.TABLES AS T0,
- INFORMATION_SCHEMA.INDEXES AS T1,
- INFORMATION_SCHEMA.INDEX_COLUMN_USAGE AS T2,
- INFORMATION_SCHEMA.COLUMNS AS T3
- WHERE T0."TABLE_NAME" = '#{table_name}'
- AND INDEX_TYPE <> 0
- AND T0.TABLE_PK = T1.TABLE_PK
- AND T0.TABLE_PK = T2.TABLE_PK
- AND T0.TABLE_PK = T3.TABLE_PK
- AND T1.INDEXES_PK = T2.INDEX_PK
- AND T2.COLUMN_PK = T3.COLUMN_PK
- ORDER BY INDEX_NAME, T2.ORDINAL_POSITION
- SQL
-
- columns = []
- query(sql).each do |row|
- index_name = row[0]
- ord_position = row[1]
- ndx_colcount = row[2]
- index_type = row[3]
- column_name = row[4]
-
- is_unique = index_type == 1
-
- columns << column_name
- if ord_position == ndx_colcount
- indexes << IndexDefinition.new(table_name, index_name, is_unique , columns)
- columns = []
- end
- end
- indexes
- end
-
- def columns(table_name, name = nil)#:nodoc:
- sql = <<-SQL
- SELECT "TABLE_NAME", "COLUMN_NAME", ORDINAL_POSITION, IS_NULLABLE, COLUMN_DEFAULT,
- DATA_TYPE, DATA_TYPE_CODE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION,
- NUMERIC_PRECISION_RADIX, NUMERIC_SCALE, DATETIME_PRECISION, DATETIME_PRECISION_LEADING
- FROM INFORMATION_SCHEMA.TABLES T0,
- INFORMATION_SCHEMA.COLUMNS T1,
- INFORMATION_SCHEMA.DATA_TYPE_DESCRIPTOR T3
- WHERE "TABLE_NAME" = '#{table_name}'
- AND T0.TABLE_PK = T1.TABLE_PK
- AND T0.TABLE_PK = T3.TABLE_OR_DOMAIN_PK
- AND T1.COLUMN_PK = T3.COLUMN_NAME_PK
- ORDER BY T1.ORDINAL_POSITION
- SQL
-
- rawresults = query(sql,name)
- columns = []
- rawresults.each do |field|
- args = [base = field[0],
- name = field[1],
- typecode = field[6],
- typestring = field[5],
- limit = field[7],
- precision = field[8],
- scale = field[9],
- default = field[4],
- nullable = field[3]]
- columns << FrontBaseColumn.new(*args)
- end
- columns
- end
-
- def create_table(name, options = {})
- table_definition = TableDefinition.new(self)
- table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
-
- yield table_definition
-
- if options[:force]
- drop_table(name) rescue nil
- end
-
- create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
- create_sql << "#{name} ("
- create_sql << table_definition.to_sql
- create_sql << ") #{options[:options]}"
- begin_db_transaction
- execute create_sql
- commit_db_transaction
- rescue ActiveRecord::StatementInvalid => e
- raise e unless e.message.match(/Table name - \w* - exists/)
- end
-
- def rename_table(name, new_name)
- columns = columns(name)
- pkcol = columns.find {|c| c.fb_autogen}
- execute "ALTER TABLE NAME #{name} TO #{new_name}"
- if pkcol
- change_column_default(new_name,pkcol.name,"UNIQUE")
- begin_db_transaction
- mpk = select_value("SELECT MAX(#{pkcol.name}) FROM #{new_name}")
- mpk = 0 if mpk.nil?
- execute "SET UNIQUE=#{mpk} FOR #{new_name}"
- commit_db_transaction
- end
- end
-
- # Drops a table from the database.
- def drop_table(name, options = {})
- execute "DROP TABLE #{name} RESTRICT"
- rescue ActiveRecord::StatementInvalid => e
- raise e unless e.message.match(/Referenced TABLE - \w* - does not exist/)
- end
-
- # Adds a new column to the named table.
- # See TableDefinition#column for details of the options you can use.
- def add_column(table_name, column_name, type, options = {})
- add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
- options[:type] = type
- add_column_options!(add_column_sql, options)
- execute(add_column_sql)
- end
-
- def add_column_options!(sql, options) #:nodoc:
- default_value = quote(options[:default], options[:column])
- if options_include_default?(options)
- if options[:type] == :boolean
- default_value = options[:default] == 0 ? quoted_false : quoted_true
- end
- sql << " DEFAULT #{default_value}"
- end
- sql << " NOT NULL" if options[:null] == false
- end
-
- # Removes the column from the table definition.
- # ===== Examples
- # remove_column(:suppliers, :qualification)
- def remove_column(table_name, column_name)
- execute "ALTER TABLE #{table_name} DROP #{column_name} RESTRICT"
- end
-
- def remove_index(table_name, options = {}) #:nodoc:
- if options[:unique]
- execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{quote_column_name(index_name(table_name, options))} RESTRICT"
- else
- execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
- end
- end
-
- def change_column_default(table_name, column_name, default) #:nodoc:
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{default}" if default != "NULL"
- end
-
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
- change_column_sql = %( ALTER COLUMN "#{table_name}"."#{column_name}" TO #{type_to_sql(type, options[:limit])} )
- execute(change_column_sql)
- change_column_sql = %( ALTER TABLE "#{table_name}" ALTER COLUMN "#{column_name}" )
-
- if options_include_default?(options)
- default_value = quote(options[:default], options[:column])
- if type == :boolean
- default_value = options[:default] == 0 ? quoted_false : quoted_true
- end
- change_column_sql << " SET DEFAULT #{default_value}"
- end
-
- execute(change_column_sql)
-
-# change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
-# add_column_options!(change_column_sql, options)
-# execute(change_column_sql)
- end
-
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
- execute %( ALTER COLUMN NAME "#{table_name}"."#{column_name}" TO "#{new_column_name}" )
- end
-
- private
-
- # Clean up sql to make it something FrontBase can digest
- def cleanup_fb_sql(sql) #:nodoc:
- # Turn non-standard != into standard <>
- cleansql = sql.gsub("!=", "<>")
- # Strip blank lines and comments
- cleansql.split("\n").reject { |line| line.match(/^(?:\s*|--.*)$/) } * "\n"
- end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 842258f1..04869bd9 100755
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -33,7 +33,8 @@ module MysqlCompat #:nodoc:
end_eval
end
- unless target.instance_methods.include?('all_hashes')
+ unless target.instance_methods.include?('all_hashes') ||
+ target.instance_methods.include?(:all_hashes)
raise "Failed to defined #{target.name}#all_hashes method. Mysql::VERSION = #{Mysql::VERSION.inspect}"
end
end
@@ -49,6 +50,11 @@ module ActiveRecord
rescue LoadError => cannot_require_mysql
# Use the bundled Ruby/MySQL driver if no driver is already in place
begin
+ ActiveRecord::Base.logger.info(
+ "WARNING: You're using the Ruby-based MySQL library that ships with Rails. This library is not suited for production. " +
+ "Please install the C-based MySQL library instead (gem install mysql)."
+ ) if ActiveRecord::Base.logger
+
require 'active_record/vendor/mysql'
rescue LoadError
raise cannot_require_mysql
@@ -85,12 +91,18 @@ module ActiveRecord
module ConnectionAdapters
class MysqlColumn < Column #:nodoc:
- TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:binary, :string, :text])
-
- def initialize(name, default, sql_type = nil, null = true)
- @original_default = default
- super
- @default = nil if missing_default_forged_as_empty_string?
+ def extract_default(default)
+ if type == :binary || type == :text
+ if default.blank?
+ default
+ else
+ raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
+ end
+ elsif missing_default_forged_as_empty_string?(default)
+ nil
+ else
+ super
+ end
end
private
@@ -102,13 +114,13 @@ module ActiveRecord
# MySQL misreports NOT NULL column default when none is given.
# We can't detect this for columns which may have a legitimate ''
- # default (string, text, binary) but we can for others (integer,
- # datetime, boolean, and the rest).
+ # default (string) but we can for others (integer, datetime, boolean,
+ # and the rest).
#
# Test whether the column has default '', is not null, and is not
# a type allowing default ''.
- def missing_default_forged_as_empty_string?
- !null && @original_default == '' && !TYPES_ALLOWING_EMPTY_STRING_DEFAULT.include?(type)
+ def missing_default_forged_as_empty_string?(default)
+ type != :string && !null && default == ''
end
end
@@ -123,6 +135,7 @@ module ActiveRecord
# * :username -- Defaults to root
# * :password -- Defaults to nothing
# * :database -- The name of the database. No default, must be provided.
+ # * :encoding -- (Optional) Sets the client encoding by executing "SET NAMES " after connection
# * :sslkey -- Necessary to use MySQL with an SSL connection
# * :sslcert -- Necessary to use MySQL with an SSL connection
# * :sslcapath -- Necessary to use MySQL with an SSL connection
@@ -195,6 +208,10 @@ module ActiveRecord
"`#{name}`"
end
+ def quote_table_name(name) #:nodoc:
+ quote_column_name(name).gsub('.', '`.`')
+ end
+
def quote_string(string) #:nodoc:
@connection.quote(string)
end
@@ -202,11 +219,23 @@ module ActiveRecord
def quoted_true
"1"
end
-
+
def quoted_false
"0"
end
+ # REFERENTIAL INTEGRITY ====================================
+
+ def disable_referential_integrity(&block) #:nodoc:
+ old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
+
+ begin
+ update("SET FOREIGN_KEY_CHECKS = 0")
+ yield
+ ensure
+ update("SET FOREIGN_KEY_CHECKS = #{old}")
+ end
+ end
# CONNECTION MANAGEMENT ====================================
@@ -231,7 +260,7 @@ module ActiveRecord
disconnect!
connect
end
-
+
def disconnect!
@connection.close rescue nil
end
@@ -239,6 +268,15 @@ module ActiveRecord
# DATABASE STATEMENTS ======================================
+ def select_rows(sql, name = nil)
+ @connection.query_with_result = true
+ result = execute(sql, name)
+ rows = []
+ result.each { |row| rows << row }
+ result.free
+ rows
+ end
+
def execute(sql, name = nil) #:nodoc:
log(sql, name) { @connection.query(sql) }
rescue ActiveRecord::StatementInvalid => exception
@@ -249,13 +287,13 @@ module ActiveRecord
end
end
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
- execute(sql, name = nil)
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+ super sql, name
id_value || @connection.insert_id
end
- def update(sql, name = nil) #:nodoc:
- execute(sql, name)
+ def update_sql(sql, name = nil) #:nodoc:
+ super
@connection.affected_rows
end
@@ -297,10 +335,10 @@ module ActiveRecord
else
sql = "SHOW TABLES"
end
-
+
select_all(sql).inject("") do |structure, table|
table.delete('Table_type')
- structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
+ structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
end
end
@@ -309,16 +347,37 @@ module ActiveRecord
create_database(name)
end
- def create_database(name) #:nodoc:
- execute "CREATE DATABASE `#{name}`"
+ # Create a new MySQL database with optional :charset and :collation.
+ # Charset defaults to utf8.
+ #
+ # Example:
+ # create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
+ # create_database 'matt_development'
+ # create_database 'matt_development', :charset => :big5
+ def create_database(name, options = {})
+ if options[:collation]
+ execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
+ else
+ execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
+ end
end
-
+
def drop_database(name) #:nodoc:
execute "DROP DATABASE IF EXISTS `#{name}`"
end
def current_database
- select_one("SELECT DATABASE() as db")["db"]
+ select_value 'SELECT DATABASE() as db'
+ end
+
+ # Returns the database character set.
+ def charset
+ show_variable 'character_set_database'
+ end
+
+ # Returns the database collation strategy.
+ def collation
+ show_variable 'collation_database'
end
def tables(name = nil) #:nodoc:
@@ -327,10 +386,14 @@ module ActiveRecord
tables
end
+ def drop_table(table_name, options = {})
+ super(table_name, options)
+ end
+
def indexes(table_name, name = nil)#:nodoc:
indexes = []
current_index = nil
- execute("SHOW KEYS FROM #{table_name}", name).each do |row|
+ execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name).each do |row|
if current_index != row[2]
next if row[2] == "PRIMARY" # skip the primary key
current_index = row[2]
@@ -343,42 +406,61 @@ module ActiveRecord
end
def columns(table_name, name = nil)#:nodoc:
- sql = "SHOW FIELDS FROM #{table_name}"
+ sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
columns = []
execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
columns
end
- def create_table(name, options = {}) #:nodoc:
- super(name, {:options => "ENGINE=InnoDB"}.merge(options))
+ def create_table(table_name, options = {}) #:nodoc:
+ super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
+ end
+
+ def rename_table(table_name, new_name)
+ execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
end
-
- def rename_table(name, new_name)
- execute "RENAME TABLE #{name} TO #{new_name}"
- end
def change_column_default(table_name, column_name, default) #:nodoc:
- current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
- execute("ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{current_type} DEFAULT #{quote(default)}")
+ execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
end
def change_column(table_name, column_name, type, options = {}) #:nodoc:
unless options_include_default?(options)
- options[:default] = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Default"]
+ if column = columns(table_name).find { |c| c.name == column_name.to_s }
+ options[:default] = column.default
+ else
+ raise "No such column: #{table_name}.#{column_name}"
+ end
end
- change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+ change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
add_column_options!(change_column_sql, options)
execute(change_column_sql)
end
def rename_column(table_name, column_name, new_column_name) #:nodoc:
- current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
- execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}"
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
+ execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
end
+ # SHOW VARIABLES LIKE 'name'
+ def show_variable(name)
+ variables = select_all("SHOW VARIABLES LIKE '#{name}'")
+ variables.first['Value'] unless variables.empty?
+ end
+
+ # Returns a table's primary key and belonging sequence.
+ def pk_and_sequence_for(table) #:nodoc:
+ keys = []
+ execute("describe #{quote_table_name(table)}").each_hash do |h|
+ keys << h["Field"]if h["Key"] == "PRI"
+ end
+ keys.length == 1 ? [keys.first, nil] : nil
+ end
+
private
def connect
encoding = @config[:encoding]
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb
deleted file mode 100644
index 71143b22..00000000
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/openbase_adapter.rb
+++ /dev/null
@@ -1,350 +0,0 @@
-require 'active_record/connection_adapters/abstract_adapter'
-
-module ActiveRecord
- class Base
- # Establishes a connection to the database that's used by all Active Record objects
- def self.openbase_connection(config) # :nodoc:
- require_library_or_gem 'openbase' unless self.class.const_defined?(:OpenBase)
-
- config = config.symbolize_keys
- host = config[:host]
- username = config[:username].to_s
- password = config[:password].to_s
-
-
- if config.has_key?(:database)
- database = config[:database]
- else
- raise ArgumentError, "No database specified. Missing argument: database."
- end
-
- oba = ConnectionAdapters::OpenBaseAdapter.new(
- OpenBase.new(database, host, username, password), logger
- )
-
- oba
- end
-
- end
-
- module ConnectionAdapters
- class OpenBaseColumn < Column #:nodoc:
- private
- def simplified_type(field_type)
- return :integer if field_type.downcase =~ /long/
- return :decimal if field_type.downcase == "money"
- return :binary if field_type.downcase == "object"
- super
- end
- end
- # The OpenBase adapter works with the Ruby/Openbase driver by Tetsuya Suzuki.
- # http://www.spice-of-life.net/ruby-openbase/ (needs version 0.7.3+)
- #
- # Options:
- #
- # * :host -- Defaults to localhost
- # * :username -- Defaults to nothing
- # * :password -- Defaults to nothing
- # * :database -- The name of the database. No default, must be provided.
- #
- # The OpenBase adapter will make use of OpenBase's ability to generate unique ids
- # for any column with an unique index applied. Thus, if the value of a primary
- # key is not specified at the time an INSERT is performed, the adapter will prefetch
- # a unique id for the primary key. This prefetching is also necessary in order
- # to return the id after an insert.
- #
- # Caveat: Operations involving LIMIT and OFFSET do not yet work!
- #
- # Maintainer: derrick.spell@gmail.com
- class OpenBaseAdapter < AbstractAdapter
- def adapter_name
- 'OpenBase'
- end
-
- def native_database_types
- {
- :primary_key => "integer UNIQUE INDEX DEFAULT _rowid",
- :string => { :name => "char", :limit => 4096 },
- :text => { :name => "text" },
- :integer => { :name => "integer" },
- :float => { :name => "float" },
- :decimal => { :name => "decimal" },
- :datetime => { :name => "datetime" },
- :timestamp => { :name => "timestamp" },
- :time => { :name => "time" },
- :date => { :name => "date" },
- :binary => { :name => "object" },
- :boolean => { :name => "boolean" }
- }
- end
-
- def supports_migrations?
- false
- end
-
- def prefetch_primary_key?(table_name = nil)
- true
- end
-
- def default_sequence_name(table_name, primary_key) # :nodoc:
- "#{table_name} #{primary_key}"
- end
-
- def next_sequence_value(sequence_name)
- ary = sequence_name.split(' ')
- if (!ary[1]) then
- ary[0] =~ /(\w+)_nonstd_seq/
- ary[0] = $1
- end
- @connection.unique_row_id(ary[0], ary[1])
- end
-
-
- # QUOTING ==================================================
-
- def quote(value, column = nil)
- if value.kind_of?(String) && column && column.type == :binary
- "'#{@connection.insert_binary(value)}'"
- else
- super
- end
- end
-
- def quoted_true
- "1"
- end
-
- def quoted_false
- "0"
- end
-
-
-
- # DATABASE STATEMENTS ======================================
-
- def add_limit_offset!(sql, options) #:nodoc:
- if limit = options[:limit]
- unless offset = options[:offset]
- sql << " RETURN RESULTS #{limit}"
- else
- limit = limit + offset
- sql << " RETURN RESULTS #{offset} TO #{limit}"
- end
- end
- end
-
- def select_all(sql, name = nil) #:nodoc:
- select(sql, name)
- end
-
- def select_one(sql, name = nil) #:nodoc:
- add_limit_offset!(sql,{:limit => 1})
- results = select(sql, name)
- results.first if results
- end
-
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
- execute(sql, name)
- update_nulls_after_insert(sql, name, pk, id_value, sequence_name)
- id_value
- end
-
- def execute(sql, name = nil) #:nodoc:
- log(sql, name) { @connection.execute(sql) }
- end
-
- def update(sql, name = nil) #:nodoc:
- execute(sql, name).rows_affected
- end
-
- alias_method :delete, :update #:nodoc:
-#=begin
- def begin_db_transaction #:nodoc:
- execute "START TRANSACTION"
- rescue Exception
- # Transactions aren't supported
- end
-
- def commit_db_transaction #:nodoc:
- execute "COMMIT"
- rescue Exception
- # Transactions aren't supported
- end
-
- def rollback_db_transaction #:nodoc:
- execute "ROLLBACK"
- rescue Exception
- # Transactions aren't supported
- end
-#=end
-
- # SCHEMA STATEMENTS ========================================
-
- # Return the list of all tables in the schema search path.
- def tables(name = nil) #:nodoc:
- tables = @connection.tables
- tables.reject! { |t| /\A_SYS_/ === t }
- tables
- end
-
- def columns(table_name, name = nil) #:nodoc:
- sql = "SELECT * FROM _sys_tables "
- sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 "
- sql << "ORDER BY columnNumber"
- columns = []
- select_all(sql, name).each do |row|
- columns << OpenBaseColumn.new(row["fieldname"],
- default_value(row["defaultvalue"]),
- sql_type_name(row["typename"],row["length"]),
- row["notnull"]
- )
- # breakpoint() if row["fieldname"] == "content"
- end
- columns
- end
-
- def indexes(table_name, name = nil)#:nodoc:
- sql = "SELECT fieldname, notnull, searchindex, uniqueindex, clusteredindex FROM _sys_tables "
- sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 "
- sql << "AND primarykey=0 "
- sql << "AND (searchindex=1 OR uniqueindex=1 OR clusteredindex=1) "
- sql << "ORDER BY columnNumber"
- indexes = []
- execute(sql, name).each do |row|
- indexes << IndexDefinition.new(table_name,index_name(row),row[3]==1,[row[0]])
- end
- indexes
- end
-
-
- private
- def select(sql, name = nil)
- sql = translate_sql(sql)
- results = execute(sql, name)
-
- date_cols = []
- col_names = []
- results.column_infos.each do |info|
- col_names << info.name
- date_cols << info.name if info.type == "date"
- end
-
- rows = []
- if ( results.rows_affected )
- results.each do |row| # loop through result rows
- hashed_row = {}
- row.each_index do |index|
- hashed_row["#{col_names[index]}"] = row[index] unless col_names[index] == "_rowid"
- end
- date_cols.each do |name|
- unless hashed_row["#{name}"].nil? or hashed_row["#{name}"].empty?
- hashed_row["#{name}"] = Date.parse(hashed_row["#{name}"],false).to_s
- end
- end
- rows << hashed_row
- end
- end
- rows
- end
-
- def default_value(value)
- # Boolean type values
- return true if value =~ /true/
- return false if value =~ /false/
-
- # Date / Time magic values
- return Time.now.to_s if value =~ /^now\(\)/i
-
- # Empty strings should be set to null
- return nil if value.empty?
-
- # Otherwise return what we got from OpenBase
- # and hope for the best...
- return value
- end
-
- def sql_type_name(type_name, length)
- return "#{type_name}(#{length})" if ( type_name =~ /char/ )
- type_name
- end
-
- def index_name(row = [])
- name = ""
- name << "UNIQUE " if row[3]
- name << "CLUSTERED " if row[4]
- name << "INDEX"
- name
- end
-
- def translate_sql(sql)
-
- # Change table.* to list of columns in table
- while (sql =~ /SELECT.*\s(\w+)\.\*/)
- table = $1
- cols = columns(table)
- if ( cols.size == 0 ) then
- # Maybe this is a table alias
- sql =~ /FROM(.+?)(?:LEFT|OUTER|JOIN|WHERE|GROUP|HAVING|ORDER|RETURN|$)/
- $1 =~ /[\s|,](\w+)\s+#{table}[\s|,]/ # get the tablename for this alias
- cols = columns($1)
- end
- select_columns = []
- cols.each do |col|
- select_columns << table + '.' + col.name
- end
- sql.gsub!(table + '.*',select_columns.join(", ")) if select_columns
- end
-
- # Change JOIN clause to table list and WHERE condition
- while (sql =~ /JOIN/)
- sql =~ /((LEFT )?(OUTER )?JOIN (\w+) ON )(.+?)(?:LEFT|OUTER|JOIN|WHERE|GROUP|HAVING|ORDER|RETURN|$)/
- join_clause = $1 + $5
- is_outer_join = $3
- join_table = $4
- join_condition = $5
- join_condition.gsub!(/=/,"*") if is_outer_join
- if (sql =~ /WHERE/)
- sql.gsub!(/WHERE/,"WHERE (#{join_condition}) AND")
- else
- sql.gsub!(join_clause,"#{join_clause} WHERE #{join_condition}")
- end
- sql =~ /(FROM .+?)(?:LEFT|OUTER|JOIN|WHERE|$)/
- from_clause = $1
- sql.gsub!(from_clause,"#{from_clause}, #{join_table} ")
- sql.gsub!(join_clause,"")
- end
-
- # ORDER BY _rowid if no explicit ORDER BY
- # This will ensure that find(:first) returns the first inserted row
- if (sql !~ /(ORDER BY)|(GROUP BY)/)
- if (sql =~ /RETURN RESULTS/)
- sql.sub!(/RETURN RESULTS/,"ORDER BY _rowid RETURN RESULTS")
- else
- sql << " ORDER BY _rowid"
- end
- end
-
- sql
- end
-
- def update_nulls_after_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- sql =~ /INSERT INTO (\w+) \((.*)\) VALUES\s*\((.*)\)/m
- table = $1
- cols = $2
- values = $3
- cols = cols.split(',')
- values.gsub!(/'[^']*'/,"''")
- values.gsub!(/"[^"]*"/,"\"\"")
- values = values.split(',')
- update_cols = []
- values.each_index { |index| update_cols << cols[index] if values[index] =~ /\s*NULL\s*/ }
- update_sql = "UPDATE #{table} SET"
- update_cols.each { |col| update_sql << " #{col}=NULL," unless col.empty? }
- update_sql.chop!()
- update_sql << " WHERE #{pk}=#{quote(id_value)}"
- execute(update_sql, name + " NULL Correction") if update_cols.size > 0
- end
-
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb
deleted file mode 100644
index c69b8447..00000000
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb
+++ /dev/null
@@ -1,690 +0,0 @@
-# oracle_adapter.rb -- ActiveRecord adapter for Oracle 8i, 9i, 10g
-#
-# Original author: Graham Jenkins
-#
-# Current maintainer: Michael Schoen
-#
-#########################################################################
-#
-# Implementation notes:
-# 1. Redefines (safely) a method in ActiveRecord to make it possible to
-# implement an autonumbering solution for Oracle.
-# 2. The OCI8 driver is patched to properly handle values for LONG and
-# TIMESTAMP columns. The driver-author has indicated that a future
-# release of the driver will obviate this patch.
-# 3. LOB support is implemented through an after_save callback.
-# 4. Oracle does not offer native LIMIT and OFFSET options; this
-# functionality is mimiced through the use of nested selects.
-# See http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
-#
-# Do what you want with this code, at your own peril, but if any
-# significant portion of my code remains then please acknowledge my
-# contribution.
-# portions Copyright 2005 Graham Jenkins
-
-require 'active_record/connection_adapters/abstract_adapter'
-require 'delegate'
-
-begin
- require_library_or_gem 'oci8' unless self.class.const_defined? :OCI8
-
- module ActiveRecord
- class Base
- def self.oracle_connection(config) #:nodoc:
- # Use OCI8AutoRecover instead of normal OCI8 driver.
- ConnectionAdapters::OracleAdapter.new OCI8AutoRecover.new(config), logger
- end
-
- # for backwards-compatibility
- def self.oci_connection(config) #:nodoc:
- config[:database] = config[:host]
- self.oracle_connection(config)
- end
-
- # After setting large objects to empty, select the OCI8::LOB
- # and write back the data.
- after_save :write_lobs
- def write_lobs() #:nodoc:
- if connection.is_a?(ConnectionAdapters::OracleAdapter)
- self.class.columns.select { |c| c.sql_type =~ /LOB$/i }.each { |c|
- value = self[c.name]
- value = value.to_yaml if unserializable_attribute?(c.name, c)
- next if value.nil? || (value == '')
- lob = connection.select_one(
- "SELECT #{c.name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = #{quote_value(id)}",
- 'Writable Large Object')[c.name]
- lob.write value
- }
- end
- end
-
- private :write_lobs
- end
-
-
- module ConnectionAdapters #:nodoc:
- class OracleColumn < Column #:nodoc:
-
- def type_cast(value)
- return guess_date_or_time(value) if type == :datetime && OracleAdapter.emulate_dates
- super
- end
-
- private
- def simplified_type(field_type)
- return :boolean if OracleAdapter.emulate_booleans && field_type == 'NUMBER(1)'
- case field_type
- when /date|time/i then :datetime
- else super
- end
- end
-
- def guess_date_or_time(value)
- (value.hour == 0 and value.min == 0 and value.sec == 0) ?
- Date.new(value.year, value.month, value.day) : value
- end
- end
-
-
- # This is an Oracle/OCI adapter for the ActiveRecord persistence
- # framework. It relies upon the OCI8 driver, which works with Oracle 8i
- # and above. Most recent development has been on Debian Linux against
- # a 10g database, ActiveRecord 1.12.1 and OCI8 0.1.13.
- # See: http://rubyforge.org/projects/ruby-oci8/
- #
- # Usage notes:
- # * Key generation assumes a "${table_name}_seq" sequence is available
- # for all tables; the sequence name can be changed using
- # ActiveRecord::Base.set_sequence_name. When using Migrations, these
- # sequences are created automatically.
- # * Oracle uses DATE or TIMESTAMP datatypes for both dates and times.
- # Consequently some hacks are employed to map data back to Date or Time
- # in Ruby. If the column_name ends in _time it's created as a Ruby Time.
- # Else if the hours/minutes/seconds are 0, I make it a Ruby Date. Else
- # it's a Ruby Time. This is a bit nasty - but if you use Duck Typing
- # you'll probably not care very much. In 9i and up it's tempting to
- # map DATE to Date and TIMESTAMP to Time, but too many databases use
- # DATE for both. Timezones and sub-second precision on timestamps are
- # not supported.
- # * Default values that are functions (such as "SYSDATE") are not
- # supported. This is a restriction of the way ActiveRecord supports
- # default values.
- # * Support for Oracle8 is limited by Rails' use of ANSI join syntax, which
- # is supported in Oracle9i and later. You will need to use #finder_sql for
- # has_and_belongs_to_many associations to run against Oracle8.
- #
- # Required parameters:
- #
- # * :username
- # * :password
- # * :database
- class OracleAdapter < AbstractAdapter
-
- @@emulate_booleans = true
- cattr_accessor :emulate_booleans
-
- @@emulate_dates = false
- cattr_accessor :emulate_dates
-
- def adapter_name #:nodoc:
- 'Oracle'
- end
-
- def supports_migrations? #:nodoc:
- true
- end
-
- def native_database_types #:nodoc:
- {
- :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
- :string => { :name => "VARCHAR2", :limit => 255 },
- :text => { :name => "CLOB" },
- :integer => { :name => "NUMBER", :limit => 38 },
- :float => { :name => "NUMBER" },
- :decimal => { :name => "DECIMAL" },
- :datetime => { :name => "DATE" },
- :timestamp => { :name => "DATE" },
- :time => { :name => "DATE" },
- :date => { :name => "DATE" },
- :binary => { :name => "BLOB" },
- :boolean => { :name => "NUMBER", :limit => 1 }
- }
- end
-
- def table_alias_length
- 30
- end
-
- # QUOTING ==================================================
- #
- # see: abstract/quoting.rb
-
- # camelCase column names need to be quoted; not that anyone using Oracle
- # would really do this, but handling this case means we pass the test...
- def quote_column_name(name) #:nodoc:
- name =~ /[A-Z]/ ? "\"#{name}\"" : name
- end
-
- def quote_string(s) #:nodoc:
- s.gsub(/'/, "''")
- end
-
- def quote(value, column = nil) #:nodoc:
- if column && [:text, :binary].include?(column.type)
- %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
- else
- super
- end
- end
-
- def quoted_true
- "1"
- end
-
- def quoted_false
- "0"
- end
-
-
- # CONNECTION MANAGEMENT ====================================
- #
-
- # Returns true if the connection is active.
- def active?
- # Pings the connection to check if it's still good. Note that an
- # #active? method is also available, but that simply returns the
- # last known state, which isn't good enough if the connection has
- # gone stale since the last use.
- @connection.ping
- rescue OCIException
- false
- end
-
- # Reconnects to the database.
- def reconnect!
- @connection.reset!
- rescue OCIException => e
- @logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}"
- end
-
- # Disconnects from the database.
- def disconnect!
- @connection.logoff rescue nil
- @connection.active = false
- end
-
-
- # DATABASE STATEMENTS ======================================
- #
- # see: abstract/database_statements.rb
-
- def execute(sql, name = nil) #:nodoc:
- log(sql, name) { @connection.exec sql }
- end
-
- # Returns the next sequence value from a sequence generator. Not generally
- # called directly; used by ActiveRecord to get the next primary key value
- # when inserting a new database record (see #prefetch_primary_key?).
- def next_sequence_value(sequence_name)
- id = 0
- @connection.exec("select #{sequence_name}.nextval id from dual") { |r| id = r[0].to_i }
- id
- end
-
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
- execute(sql, name)
- id_value
- end
-
- def begin_db_transaction #:nodoc:
- @connection.autocommit = false
- end
-
- def commit_db_transaction #:nodoc:
- @connection.commit
- ensure
- @connection.autocommit = true
- end
-
- def rollback_db_transaction #:nodoc:
- @connection.rollback
- ensure
- @connection.autocommit = true
- end
-
- def add_limit_offset!(sql, options) #:nodoc:
- offset = options[:offset] || 0
-
- if limit = options[:limit]
- sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
- elsif offset > 0
- sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
- end
- end
-
- # Returns true for Oracle adapter (since Oracle requires primary key
- # values to be pre-fetched before insert). See also #next_sequence_value.
- def prefetch_primary_key?(table_name = nil)
- true
- end
-
- def default_sequence_name(table, column) #:nodoc:
- "#{table}_seq"
- end
-
-
- # SCHEMA STATEMENTS ========================================
- #
- # see: abstract/schema_statements.rb
-
- def current_database #:nodoc:
- select_one("select sys_context('userenv','db_name') db from dual")["db"]
- end
-
- def tables(name = nil) #:nodoc:
- select_all("select lower(table_name) from user_tables").inject([]) do | tabs, t |
- tabs << t.to_a.first.last
- end
- end
-
- def indexes(table_name, name = nil) #:nodoc:
- result = select_all(<<-SQL, name)
- SELECT lower(i.index_name) as index_name, i.uniqueness, lower(c.column_name) as column_name
- FROM user_indexes i, user_ind_columns c
- WHERE i.table_name = '#{table_name.to_s.upcase}'
- AND c.index_name = i.index_name
- AND i.index_name NOT IN (SELECT uc.index_name FROM user_constraints uc WHERE uc.constraint_type = 'P')
- ORDER BY i.index_name, c.column_position
- SQL
-
- current_index = nil
- indexes = []
-
- result.each do |row|
- if current_index != row['index_name']
- indexes << IndexDefinition.new(table_name, row['index_name'], row['uniqueness'] == "UNIQUE", [])
- current_index = row['index_name']
- end
-
- indexes.last.columns << row['column_name']
- end
-
- indexes
- end
-
- def columns(table_name, name = nil) #:nodoc:
- (owner, table_name) = @connection.describe(table_name)
-
- table_cols = <<-SQL
- select column_name as name, data_type as sql_type, data_default, nullable,
- decode(data_type, 'NUMBER', data_precision,
- 'FLOAT', data_precision,
- 'VARCHAR2', data_length,
- 'CHAR', data_length,
- null) as limit,
- decode(data_type, 'NUMBER', data_scale, null) as scale
- from all_tab_columns
- where owner = '#{owner}'
- and table_name = '#{table_name}'
- order by column_id
- SQL
-
- select_all(table_cols, name).map do |row|
- limit, scale = row['limit'], row['scale']
- if limit || scale
- row['sql_type'] << "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
- end
-
- # clean up odd default spacing from Oracle
- if row['data_default']
- row['data_default'].sub!(/^(.*?)\s*$/, '\1')
- row['data_default'].sub!(/^'(.*)'$/, '\1')
- row['data_default'] = nil if row['data_default'] =~ /^null$/i
- end
-
- OracleColumn.new(oracle_downcase(row['name']),
- row['data_default'],
- row['sql_type'],
- row['nullable'] == 'Y')
- end
- end
-
- def create_table(name, options = {}) #:nodoc:
- super(name, options)
- seq_name = options[:sequence_name] || "#{name}_seq"
- execute "CREATE SEQUENCE #{seq_name} START WITH 10000" unless options[:id] == false
- end
-
- def rename_table(name, new_name) #:nodoc:
- execute "RENAME #{name} TO #{new_name}"
- execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
- end
-
- def drop_table(name, options = {}) #:nodoc:
- super(name)
- seq_name = options[:sequence_name] || "#{name}_seq"
- execute "DROP SEQUENCE #{seq_name}" rescue nil
- end
-
- def remove_index(table_name, options = {}) #:nodoc:
- execute "DROP INDEX #{index_name(table_name, options)}"
- end
-
- def change_column_default(table_name, column_name, default) #:nodoc:
- execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
- end
-
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
- change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
- add_column_options!(change_column_sql, options)
- execute(change_column_sql)
- end
-
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
- execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
- end
-
- def remove_column(table_name, column_name) #:nodoc:
- execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
- end
-
- # Find a table's primary key and sequence.
- # *Note*: Only primary key is implemented - sequence will be nil.
- def pk_and_sequence_for(table_name)
- (owner, table_name) = @connection.describe(table_name)
-
- pks = select_values(<<-SQL, 'Primary Key')
- select cc.column_name
- from all_constraints c, all_cons_columns cc
- where c.owner = '#{owner}'
- and c.table_name = '#{table_name}'
- and c.constraint_type = 'P'
- and cc.owner = c.owner
- and cc.constraint_name = c.constraint_name
- SQL
-
- # only support single column keys
- pks.size == 1 ? [oracle_downcase(pks.first), nil] : nil
- end
-
- def structure_dump #:nodoc:
- s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
- structure << "create sequence #{seq.to_a.first.last};\n\n"
- end
-
- select_all("select table_name from user_tables").inject(s) do |structure, table|
- ddl = "create table #{table.to_a.first.last} (\n "
- cols = select_all(%Q{
- select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
- from user_tab_columns
- where table_name = '#{table.to_a.first.last}'
- order by column_id
- }).map do |row|
- col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
- if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
- col << "(#{row['data_precision'].to_i}"
- col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
- col << ')'
- elsif row['data_type'].include?('CHAR')
- col << "(#{row['data_length'].to_i})"
- end
- col << " default #{row['data_default']}" if !row['data_default'].nil?
- col << ' not null' if row['nullable'] == 'N'
- col
- end
- ddl << cols.join(",\n ")
- ddl << ");\n\n"
- structure << ddl
- end
- end
-
- def structure_drop #:nodoc:
- s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
- drop << "drop sequence #{seq.to_a.first.last};\n\n"
- end
-
- select_all("select table_name from user_tables").inject(s) do |drop, table|
- drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
- end
- end
-
- # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
- #
- # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
- # queries. However, with those columns included in the SELECT DISTINCT list, you
- # won't actually get a distinct list of the column you want (presuming the column
- # has duplicates with multiple values for the ordered-by columns. So we use the
- # FIRST_VALUE function to get a single (first) value for each column, effectively
- # making every row the same.
- #
- # distinct("posts.id", "posts.created_at desc")
- def distinct(columns, order_by)
- return "DISTINCT #{columns}" if order_by.blank?
-
- # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
- # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
- order_columns = order_by.split(',').map { |s| s.strip }.reject(&:blank?)
- order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
- "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
- end
- sql = "DISTINCT #{columns}, "
- sql << order_columns * ", "
- end
-
- # ORDER BY clause for the passed order option.
- #
- # Uses column aliases as defined by #distinct.
- def add_order_by_for_association_limiting!(sql, options)
- return sql if options[:order].blank?
-
- order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
- order.map! {|s| $1 if s =~ / (.*)/}
- order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
-
- sql << "ORDER BY #{order}"
- end
-
- private
-
- def select(sql, name = nil)
- cursor = execute(sql, name)
- cols = cursor.get_col_names.map { |x| oracle_downcase(x) }
- rows = []
-
- while row = cursor.fetch
- hash = Hash.new
-
- cols.each_with_index do |col, i|
- hash[col] =
- case row[i]
- when OCI8::LOB
- name == 'Writable Large Object' ? row[i]: row[i].read
- when OraDate
- (row[i].hour == 0 and row[i].minute == 0 and row[i].second == 0) ?
- row[i].to_date : row[i].to_time
- else row[i]
- end unless col == 'raw_rnum_'
- end
-
- rows << hash
- end
-
- rows
- ensure
- cursor.close if cursor
- end
-
- # Oracle column names by default are case-insensitive, but treated as upcase;
- # for neatness, we'll downcase within Rails. EXCEPT that folks CAN quote
- # their column names when creating Oracle tables, which makes then case-sensitive.
- # I don't know anybody who does this, but we'll handle the theoretical case of a
- # camelCase column name. I imagine other dbs handle this different, since there's a
- # unit test that's currently failing test_oci.
- def oracle_downcase(column_name)
- column_name =~ /[a-z]/ ? column_name : column_name.downcase
- end
-
- end
- end
- end
-
-
- class OCI8 #:nodoc:
-
- # This OCI8 patch may not longer be required with the upcoming
- # release of version 0.2.
- class Cursor #:nodoc:
- alias :define_a_column_pre_ar :define_a_column
- def define_a_column(i)
- case do_ocicall(@ctx) { @parms[i - 1].attrGet(OCI_ATTR_DATA_TYPE) }
- when 8 : @stmt.defineByPos(i, String, 65535) # Read LONG values
- when 187 : @stmt.defineByPos(i, OraDate) # Read TIMESTAMP values
- when 108
- if @parms[i - 1].attrGet(OCI_ATTR_TYPE_NAME) == 'XMLTYPE'
- @stmt.defineByPos(i, String, 65535)
- else
- raise 'unsupported datatype'
- end
- else define_a_column_pre_ar i
- end
- end
- end
-
- # missing constant from oci8 < 0.1.14
- OCI_PTYPE_UNK = 0 unless defined?(OCI_PTYPE_UNK)
-
- # Uses the describeAny OCI call to find the target owner and table_name
- # indicated by +name+, parsing through synonynms as necessary. Returns
- # an array of [owner, table_name].
- def describe(name)
- @desc ||= @@env.alloc(OCIDescribe)
- @desc.attrSet(OCI_ATTR_DESC_PUBLIC, -1) if VERSION >= '0.1.14'
- @desc.describeAny(@svc, name.to_s, OCI_PTYPE_UNK) rescue raise %Q{"DESC #{name}" failed; does it exist?}
- info = @desc.attrGet(OCI_ATTR_PARAM)
-
- case info.attrGet(OCI_ATTR_PTYPE)
- when OCI_PTYPE_TABLE, OCI_PTYPE_VIEW
- owner = info.attrGet(OCI_ATTR_OBJ_SCHEMA)
- table_name = info.attrGet(OCI_ATTR_OBJ_NAME)
- [owner, table_name]
- when OCI_PTYPE_SYN
- schema = info.attrGet(OCI_ATTR_SCHEMA_NAME)
- name = info.attrGet(OCI_ATTR_NAME)
- describe(schema + '.' + name)
- else raise %Q{"DESC #{name}" failed; not a table or view.}
- end
- end
-
- end
-
-
- # The OracleConnectionFactory factors out the code necessary to connect and
- # configure an Oracle/OCI connection.
- class OracleConnectionFactory #:nodoc:
- def new_connection(username, password, database, async, prefetch_rows, cursor_sharing)
- conn = OCI8.new username, password, database
- conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
- conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} rescue nil
- conn.autocommit = true
- conn.non_blocking = true if async
- conn.prefetch_rows = prefetch_rows
- conn.exec "alter session set cursor_sharing = #{cursor_sharing}" rescue nil
- conn
- end
- end
-
-
- # The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
- # reset functionality. If a call to #exec fails, and autocommit is turned on
- # (ie., we're not in the middle of a longer transaction), it will
- # automatically reconnect and try again. If autocommit is turned off,
- # this would be dangerous (as the earlier part of the implied transaction
- # may have failed silently if the connection died) -- so instead the
- # connection is marked as dead, to be reconnected on it's next use.
- class OCI8AutoRecover < DelegateClass(OCI8) #:nodoc:
- attr_accessor :active
- alias :active? :active
-
- cattr_accessor :auto_retry
- class << self
- alias :auto_retry? :auto_retry
- end
- @@auto_retry = false
-
- def initialize(config, factory = OracleConnectionFactory.new)
- @active = true
- @username, @password, @database, = config[:username], config[:password], config[:database]
- @async = config[:allow_concurrency]
- @prefetch_rows = config[:prefetch_rows] || 100
- @cursor_sharing = config[:cursor_sharing] || 'similar'
- @factory = factory
- @connection = @factory.new_connection @username, @password, @database, @async, @prefetch_rows, @cursor_sharing
- super @connection
- end
-
- # Checks connection, returns true if active. Note that ping actively
- # checks the connection, while #active? simply returns the last
- # known state.
- def ping
- @connection.exec("select 1 from dual") { |r| nil }
- @active = true
- rescue
- @active = false
- raise
- end
-
- # Resets connection, by logging off and creating a new connection.
- def reset!
- logoff rescue nil
- begin
- @connection = @factory.new_connection @username, @password, @database, @async, @prefetch_rows, @cursor_sharing
- __setobj__ @connection
- @active = true
- rescue
- @active = false
- raise
- end
- end
-
- # ORA-00028: your session has been killed
- # ORA-01012: not logged on
- # ORA-03113: end-of-file on communication channel
- # ORA-03114: not connected to ORACLE
- LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
-
- # Adds auto-recovery functionality.
- #
- # See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11
- def exec(sql, *bindvars, &block)
- should_retry = self.class.auto_retry? && autocommit?
-
- begin
- @connection.exec(sql, *bindvars, &block)
- rescue OCIException => e
- raise unless LOST_CONNECTION_ERROR_CODES.include?(e.code)
- @active = false
- raise unless should_retry
- should_retry = false
- reset! rescue nil
- retry
- end
- end
-
- end
-
-rescue LoadError
- # OCI8 driver is unavailable.
- module ActiveRecord # :nodoc:
- class Base
- @@oracle_error_message = "Oracle/OCI libraries could not be loaded: #{$!.to_s}"
- def self.oracle_connection(config) # :nodoc:
- # Set up a reasonable error message
- raise LoadError, @@oracle_error_message
- end
- def self.oci_connection(config) # :nodoc:
- # Set up a reasonable error message
- raise LoadError, @@oracle_error_message
- end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index eb9d0e94..d5c6ca64 100644
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -12,29 +12,205 @@ module ActiveRecord
username = config[:username].to_s
password = config[:password].to_s
- min_messages = config[:min_messages]
-
if config.has_key?(:database)
database = config[:database]
else
raise ArgumentError, "No database specified. Missing argument: database."
end
- pga = ConnectionAdapters::PostgreSQLAdapter.new(
- PGconn.connect(host, port, "", "", database, username, password), logger, config
- )
-
- PGconn.translate_results = false if PGconn.respond_to? :translate_results=
-
- pga.schema_search_path = config[:schema_search_path] || config[:schema_order]
-
- pga
+ # The postgres drivers don't allow the creation of an unconnected PGconn object,
+ # so just pass a nil connection object for the time being.
+ ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config)
end
end
module ConnectionAdapters
- # The PostgreSQL adapter works both with the C-based (http://www.postgresql.jp/interfaces/ruby/) and the Ruby-base
- # (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1145) drivers.
+ # PostgreSQL-specific extensions to column definitions in a table.
+ class PostgreSQLColumn < Column #:nodoc:
+ # Instantiates a new PostgreSQL column definition in a table.
+ def initialize(name, default, sql_type = nil, null = true)
+ super(name, self.class.extract_value_from_default(default), sql_type, null)
+ end
+
+ private
+ # Extracts the scale from PostgreSQL-specific data types.
+ def extract_scale(sql_type)
+ # Money type has a fixed scale of 2.
+ sql_type =~ /^money/ ? 2 : super
+ end
+
+ # Extracts the precision from PostgreSQL-specific data types.
+ def extract_precision(sql_type)
+ # Actual code is defined dynamically in PostgreSQLAdapter.connect
+ # depending on the server specifics
+ super
+ end
+
+ # Escapes binary strings for bytea input to the database.
+ def self.string_to_binary(value)
+ if PGconn.respond_to?(:escape_bytea)
+ self.class.module_eval do
+ define_method(:string_to_binary) do |value|
+ PGconn.escape_bytea(value) if value
+ end
+ end
+ else
+ self.class.module_eval do
+ define_method(:string_to_binary) do |value|
+ if value
+ result = ''
+ value.each_byte { |c| result << sprintf('\\\\%03o', c) }
+ result
+ end
+ end
+ end
+ end
+ self.class.string_to_binary(value)
+ end
+
+ # Unescapes bytea output from a database to the binary string it represents.
+ def self.binary_to_string(value)
+ # In each case, check if the value actually is escaped PostgreSQL bytea output
+ # or an unescaped Active Record attribute that was just written.
+ if PGconn.respond_to?(:unescape_bytea)
+ self.class.module_eval do
+ define_method(:binary_to_string) do |value|
+ if value =~ /\\\d{3}/
+ PGconn.unescape_bytea(value)
+ else
+ value
+ end
+ end
+ end
+ else
+ self.class.module_eval do
+ define_method(:binary_to_string) do |value|
+ if value =~ /\\\d{3}/
+ result = ''
+ i, max = 0, value.size
+ while i < max
+ char = value[i]
+ if char == ?\\
+ if value[i+1] == ?\\
+ char = ?\\
+ i += 1
+ else
+ char = value[i+1..i+3].oct
+ i += 3
+ end
+ end
+ result << char
+ i += 1
+ end
+ result
+ else
+ value
+ end
+ end
+ end
+ end
+ self.class.binary_to_string(value)
+ end
+
+ # Maps PostgreSQL-specific data types to logical Rails types.
+ def simplified_type(field_type)
+ case field_type
+ # Numeric and monetary types
+ when /^(?:real|double precision)$/
+ :float
+ # Monetary types
+ when /^money$/
+ :decimal
+ # Character types
+ when /^(?:character varying|bpchar)(?:\(\d+\))?$/
+ :string
+ # Binary data types
+ when /^bytea$/
+ :binary
+ # Date/time types
+ when /^timestamp with(?:out)? time zone$/
+ :datetime
+ when /^interval$/
+ :string
+ # Geometric types
+ when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
+ :string
+ # Network address types
+ when /^(?:cidr|inet|macaddr)$/
+ :string
+ # Bit strings
+ when /^bit(?: varying)?(?:\(\d+\))?$/
+ :string
+ # XML type
+ when /^xml$/
+ :string
+ # Arrays
+ when /^\D+\[\]$/
+ :string
+ # Object identifier types
+ when /^oid$/
+ :integer
+ # Pass through all types that are not specific to PostgreSQL.
+ else
+ super
+ end
+ end
+
+ # Extracts the value from a PostgreSQL column default definition.
+ def self.extract_value_from_default(default)
+ case default
+ # Numeric types
+ when /\A-?\d+(\.\d*)?\z/
+ default
+ # Character types
+ when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
+ $1
+ # Character types (8.1 formatting)
+ when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
+ $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
+ # Binary data types
+ when /\A'(.*)'::bytea\z/m
+ $1
+ # Date/time types
+ when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
+ $1
+ when /\A'(.*)'::interval\z/
+ $1
+ # Boolean type
+ when 'true'
+ true
+ when 'false'
+ false
+ # Geometric types
+ when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
+ $1
+ # Network address types
+ when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
+ $1
+ # Bit string types
+ when /\AB'(.*)'::"?bit(?: varying)?"?\z/
+ $1
+ # XML type
+ when /\A'(.*)'::xml\z/m
+ $1
+ # Arrays
+ when /\A'(.*)'::"?\D+"?\[\]\z/
+ $1
+ # Object identifier types
+ when /\A-?\d+\z/
+ $1
+ else
+ # Anything else is blank, some user type, or some function
+ # and we can't know the value of that, so return nil.
+ nil
+ end
+ end
+ end
+ end
+
+ module ConnectionAdapters
+ # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
+ # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
#
# Options:
#
@@ -44,19 +220,21 @@ module ActiveRecord
# * :password -- Defaults to nothing
# * :database -- The name of the database. No default, must be provided.
# * :schema_search_path -- An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the :schema_order option.
- # * :encoding -- An optional client encoding that is using in a SET client_encoding TO call on connection.
- # * :min_messages -- An optional client min messages that is using in a SET client_min_messages TO call on connection.
+ # * :encoding -- An optional client encoding that is used in a SET client_encoding TO call on the connection.
+ # * :min_messages -- An optional client min messages that is used in a SET client_min_messages TO call on the connection.
# * :allow_concurrency -- If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
class PostgreSQLAdapter < AbstractAdapter
+ # Returns 'PostgreSQL' as adapter name for identification purposes.
def adapter_name
'PostgreSQL'
end
- def initialize(connection, logger, config = {})
+ # Initializes and connects a PostgreSQL adapter.
+ def initialize(connection, logger, connection_parameters, config)
super(connection, logger)
- @config = config
- @async = config[:allow_concurrency]
- configure_connection
+ @connection_parameters, @config = connection_parameters, config
+
+ connect
end
# Is this connection alive and ready for queries?
@@ -64,29 +242,32 @@ module ActiveRecord
if @connection.respond_to?(:status)
@connection.status == PGconn::CONNECTION_OK
else
+ # We're asking the driver, not ActiveRecord, so use @connection.query instead of #query
@connection.query 'SELECT 1'
true
end
- # postgres-pr raises a NoMethodError when querying if no conn is available
+ # postgres-pr raises a NoMethodError when querying if no connection is available.
rescue PGError, NoMethodError
false
end
# Close then reopen the connection.
def reconnect!
- # TODO: postgres-pr doesn't have PGconn#reset.
if @connection.respond_to?(:reset)
@connection.reset
configure_connection
+ else
+ disconnect!
+ connect
end
end
+ # Close the connection.
def disconnect!
- # Both postgres and postgres-pr respond to :close
@connection.close rescue nil
end
- def native_database_types
+ def native_database_types #:nodoc:
{
:primary_key => "serial primary key",
:string => { :name => "character varying", :limit => 255 },
@@ -103,41 +284,113 @@ module ActiveRecord
}
end
+ # Does PostgreSQL support migrations?
def supports_migrations?
true
end
+ # Does PostgreSQL support standard conforming strings?
+ def supports_standard_conforming_strings?
+ # Temporarily set the client message level above error to prevent unintentional
+ # error messages in the logs when working on a PostgreSQL database server that
+ # does not support standard conforming strings.
+ client_min_messages_old = client_min_messages
+ self.client_min_messages = 'panic'
+
+ # postgres-pr does not raise an exception when client_min_messages is set higher
+ # than error and "SHOW standard_conforming_strings" fails, but returns an empty
+ # PGresult instead.
+ has_support = execute('SHOW standard_conforming_strings')[0][0] rescue false
+ self.client_min_messages = client_min_messages_old
+ has_support
+ end
+
+ # Returns the configured supported identifier length supported by PostgreSQL,
+ # or report the default of 63 on PostgreSQL 7.x.
def table_alias_length
- 63
+ @table_alias_length ||= (postgresql_version >= 80000 ? query('SHOW max_identifier_length')[0][0].to_i : 63)
end
# QUOTING ==================================================
- def quote(value, column = nil)
+ # Quotes PostgreSQL-specific data types for SQL input.
+ def quote(value, column = nil) #:nodoc:
if value.kind_of?(String) && column && column.type == :binary
- "'#{escape_bytea(value)}'"
+ "#{quoted_string_prefix}'#{column.class.string_to_binary(value)}'"
+ elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
+ "xml '#{quote_string(value)}'"
+ elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
+ # Not truly string input, so doesn't require (or allow) escape string syntax.
+ "'#{value.to_s}'"
+ elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/
+ case value
+ when /^[01]*$/
+ "B'#{value}'" # Bit-string notation
+ when /^[0-9A-F]*$/i
+ "X'#{value}'" # Hexadecimal notation
+ end
else
super
end
end
- def quote_column_name(name)
+ # Quotes strings for use in SQL input in the postgres driver for better performance.
+ def quote_string(s) #:nodoc:
+ if PGconn.respond_to?(:escape)
+ self.class.instance_eval do
+ define_method(:quote_string) do |s|
+ PGconn.escape(s)
+ end
+ end
+ else
+ # There are some incorrectly compiled postgres drivers out there
+ # that don't define PGconn.escape.
+ self.class.instance_eval do
+ undef_method(:quote_string)
+ end
+ end
+ quote_string(s)
+ end
+
+ # Quotes column names for use in SQL queries.
+ def quote_column_name(name) #:nodoc:
%("#{name}")
end
- def quoted_date(value)
- value.strftime("%Y-%m-%d %H:%M:%S.#{sprintf("%06d", value.usec)}")
+ # Quote date/time values for use in SQL input. Includes microseconds
+ # if the value is a Time responding to usec.
+ def quoted_date(value) #:nodoc:
+ if value.acts_like?(:time) && value.respond_to?(:usec)
+ "#{super}.#{sprintf("%06d", value.usec)}"
+ else
+ super
+ end
end
+ # REFERENTIAL INTEGRITY ====================================
+
+ def disable_referential_integrity(&block) #:nodoc:
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
+ yield
+ ensure
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
+ end
# DATABASE STATEMENTS ======================================
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
- execute(sql, name)
- table = sql.split(" ", 4)[2]
- id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
+ # Executes a SELECT query and returns an array of rows. Each row is an
+ # array of field values.
+ def select_rows(sql, name = nil)
+ select_raw(sql, name).last
end
+ # Executes an INSERT query and returns the new record's ID
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
+ table = sql.split(" ", 4)[2]
+ super || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
+ end
+
+ # Queries the database and returns the results in an Array or nil otherwise.
def query(sql, name = nil) #:nodoc:
log(sql, name) do
if @async
@@ -148,7 +401,9 @@ module ActiveRecord
end
end
- def execute(sql, name = nil) #:nodoc:
+ # Executes an SQL statement, returning a PGresult object on success
+ # or raising a PGError exception otherwise.
+ def execute(sql, name = nil)
log(sql, name) do
if @async
@connection.async_exec(sql)
@@ -158,26 +413,30 @@ module ActiveRecord
end
end
- def update(sql, name = nil) #:nodoc:
- execute(sql, name).cmdtuples
+ # Executes an UPDATE query and returns the number of affected tuples.
+ def update_sql(sql, name = nil)
+ super.cmdtuples
end
- def begin_db_transaction #:nodoc:
+ # Begins a transaction.
+ def begin_db_transaction
execute "BEGIN"
end
- def commit_db_transaction #:nodoc:
+ # Commits a transaction.
+ def commit_db_transaction
execute "COMMIT"
end
- def rollback_db_transaction #:nodoc:
+ # Aborts a transaction.
+ def rollback_db_transaction
execute "ROLLBACK"
end
# SCHEMA STATEMENTS ========================================
- # Return the list of all tables in the schema search path.
- def tables(name = nil) #:nodoc:
+ # Returns the list of all tables in the schema search path or a specified schema.
+ def tables(name = nil)
schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
query(<<-SQL, name).map { |row| row[0] }
SELECT tablename
@@ -186,7 +445,8 @@ module ActiveRecord
SQL
end
- def indexes(table_name, name = nil) #:nodoc:
+ # Returns the list of all indexes for a table.
+ def indexes(table_name, name = nil)
result = query(<<-SQL, name)
SELECT i.relname, d.indisunique, a.attname
FROM pg_class t, pg_class i, pg_index d, pg_attribute a
@@ -219,34 +479,49 @@ module ActiveRecord
indexes
end
- def columns(table_name, name = nil) #:nodoc:
- column_definitions(table_name).collect do |name, type, default, notnull, typmod|
- # typmod now unused as limit, precision, scale all handled by superclass
- Column.new(name, default_value(default), translate_field_type(type), notnull == "f")
+ # Returns the list of all column definitions for a table.
+ def columns(table_name, name = nil)
+ # Limit, precision, and scale are all handled by the superclass.
+ column_definitions(table_name).collect do |name, type, default, notnull|
+ PostgreSQLColumn.new(name, default, type, notnull == 'f')
end
end
- # Set the schema search path to a string of comma-separated schema names.
- # Names beginning with $ are quoted (e.g. $user => '$user')
- # See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html
- def schema_search_path=(schema_csv) #:nodoc:
+ # Sets the schema search path to a string of comma-separated schema names.
+ # Names beginning with $ have to be quoted (e.g. $user => '$user').
+ # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
+ #
+ # This should be not be called manually but set in database.yml.
+ def schema_search_path=(schema_csv)
if schema_csv
execute "SET search_path TO #{schema_csv}"
- @schema_search_path = nil
+ @schema_search_path = schema_csv
end
end
- def schema_search_path #:nodoc:
+ # Returns the active schema search path.
+ def schema_search_path
@schema_search_path ||= query('SHOW search_path')[0][0]
end
- def default_sequence_name(table_name, pk = nil)
+ # Returns the current client message level.
+ def client_min_messages
+ query('SHOW client_min_messages')[0][0]
+ end
+
+ # Set the client message level.
+ def client_min_messages=(level)
+ execute("SET client_min_messages TO '#{level}'")
+ end
+
+ # Returns the sequence name for a table's primary key or some other specified key.
+ def default_sequence_name(table_name, pk = nil) #:nodoc:
default_pk, default_seq = pk_and_sequence_for(table_name)
default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
end
- # Resets sequence to the max value of the table's pk if present.
- def reset_pk_sequence!(table, pk = nil, sequence = nil)
+ # Resets the sequence of a table's primary key to the maximum value.
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
unless pk and sequence
default_pk, default_sequence = pk_and_sequence_for(table)
pk ||= default_pk
@@ -263,19 +538,18 @@ module ActiveRecord
end
end
- # Find a table's primary key and sequence.
- def pk_and_sequence_for(table)
+ # Returns a table's primary key and belonging sequence.
+ def pk_and_sequence_for(table) #:nodoc:
# First try looking for a sequence with a dependency on the
# given table's primary key.
result = query(<<-end_sql, 'PK and serial sequence')[0]
- SELECT attr.attname, name.nspname, seq.relname
+ SELECT attr.attname, seq.relname
FROM pg_class seq,
pg_attribute attr,
pg_depend dep,
pg_namespace name,
pg_constraint cons
WHERE seq.oid = dep.objid
- AND seq.relnamespace = name.oid
AND seq.relkind = 'S'
AND attr.attrelid = dep.refobjid
AND attr.attnum = dep.refobjsubid
@@ -289,11 +563,9 @@ module ActiveRecord
# If that fails, try parsing the primary key's default value.
# Support the 7.x and 8.0 nextval('foo'::text) as well as
# the 8.1+ nextval('foo'::regclass).
- # TODO: assumes sequence is in same schema as table.
result = query(<<-end_sql, 'PK and custom sequence')[0]
- SELECT attr.attname, name.nspname, split_part(def.adsrc, '''', 2)
+ SELECT attr.attname, split_part(def.adsrc, '''', 2)
FROM pg_class t
- JOIN pg_namespace name ON (t.relnamespace = name.oid)
JOIN pg_attribute attr ON (t.oid = attrelid)
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
@@ -302,68 +574,72 @@ module ActiveRecord
AND def.adsrc ~* 'nextval'
end_sql
end
- # check for existence of . in sequence name as in public.foo_sequence. if it does not exist, return unqualified sequence
- # We cannot qualify unqualified sequences, as rails doesn't qualify any table access, using the search path
+ # [primary_key, sequence]
[result.first, result.last]
rescue
nil
end
+ # Renames a table.
def rename_table(name, new_name)
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
end
+ # Adds a column to a table.
def add_column(table_name, column_name, type, options = {})
default = options[:default]
notnull = options[:null] == false
# Add the column.
- execute("ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type_to_sql(type, options[:limit])}")
+ execute("ALTER TABLE #{table_name} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}")
- # Set optional default. If not null, update nulls to the new default.
- if options_include_default?(options)
- change_column_default(table_name, column_name, default)
- if notnull
- execute("UPDATE #{table_name} SET #{column_name}=#{quote(default, options[:column])} WHERE #{column_name} IS NULL")
- end
- end
-
- if notnull
- execute("ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL")
- end
+ change_column_default(table_name, column_name, default) if options_include_default?(options)
+ change_column_null(table_name, column_name, false, default) if notnull
end
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
+ # Changes the column of a table.
+ def change_column(table_name, column_name, type, options = {})
begin
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
rescue ActiveRecord::StatementInvalid
- # This is PG7, so we use a more arcane way of doing it.
+ # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
begin_db_transaction
- add_column(table_name, "#{column_name}_ar_tmp", type, options)
- execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
+ tmp_column_name = "#{column_name}_ar_tmp"
+ add_column(table_name, tmp_column_name, type, options)
+ execute "UPDATE #{table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
remove_column(table_name, column_name)
- rename_column(table_name, "#{column_name}_ar_tmp", column_name)
+ rename_column(table_name, tmp_column_name, column_name)
commit_db_transaction
end
- if options_include_default?(options)
- change_column_default(table_name, column_name, options[:default])
- end
+ change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
+ change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
end
- def change_column_default(table_name, column_name, default) #:nodoc:
+ # Changes the default value of a table column.
+ def change_column_default(table_name, column_name, default)
execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
end
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
+ def change_column_null(table_name, column_name, null, default = nil)
+ unless null || default.nil?
+ execute("UPDATE #{table_name} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
+ end
+ execute("ALTER TABLE #{table_name} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
+ end
+
+ # Renames a column in a table.
+ def rename_column(table_name, column_name, new_column_name)
execute "ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
end
- def remove_index(table_name, options) #:nodoc:
+ # Drops an index from a table.
+ def remove_index(table_name, options = {})
execute "DROP INDEX #{index_name(table_name, options)}"
end
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
+ # Maps logical Rails types to PostgreSQL-specific data types.
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
return super unless type.to_s == 'integer'
if limit.nil? || limit == 4
@@ -375,32 +651,32 @@ module ActiveRecord
end
end
- # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
+ # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
#
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
# requires that the ORDER BY include the distinct column.
#
# distinct("posts.id", "posts.created_at desc")
- def distinct(columns, order_by)
+ def distinct(columns, order_by) #:nodoc:
return "DISTINCT #{columns}" if order_by.blank?
- # construct a clean list of column names from the ORDER BY clause, removing
- # any asc/desc modifiers
+ # Construct a clean list of column names from the ORDER BY clause, removing
+ # any ASC/DESC modifiers
order_columns = order_by.split(',').collect { |s| s.split.first }
order_columns.delete_if &:blank?
order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
- # return a DISTINCT ON() clause that's distinct on the columns we want but includes
- # all the required columns for the ORDER BY to work properly
+ # Return a DISTINCT ON() clause that's distinct on the columns we want but includes
+ # all the required columns for the ORDER BY to work properly.
sql = "DISTINCT ON (#{columns}) #{columns}, "
sql << order_columns * ', '
end
- # ORDER BY clause for the passed order option.
+ # Returns an ORDER BY clause for the passed order option.
#
# PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
# by wrapping the sql as a sub-select and ordering in that query.
- def add_order_by_for_association_limiting!(sql, options)
+ def add_order_by_for_association_limiting!(sql, options) #:nodoc:
return sql if options[:order].blank?
order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
@@ -410,103 +686,135 @@ module ActiveRecord
sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
end
- private
- BYTEA_COLUMN_TYPE_OID = 17
- NUMERIC_COLUMN_TYPE_OID = 1700
- TIMESTAMPOID = 1114
- TIMESTAMPTZOID = 1184
-
- def configure_connection
- if @config[:encoding]
- execute("SET client_encoding TO '#{@config[:encoding]}'")
- end
- if @config[:min_messages]
- execute("SET client_min_messages TO '#{@config[:min_messages]}'")
- end
+ protected
+ # Returns the version of the connected PostgreSQL version.
+ def postgresql_version
+ @postgresql_version ||=
+ if @connection.respond_to?(:server_version)
+ @connection.server_version
+ else
+ # Mimic PGconn.server_version behavior
+ begin
+ query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
+ ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
+ rescue
+ 0
+ end
+ end
end
- def last_insert_id(table, sequence_name)
+ private
+ # The internal PostgreSQL identifer of the money data type.
+ MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
+
+ # Connects to a PostgreSQL server and sets up the adapter depending on the
+ # connected server's characteristics.
+ def connect
+ @connection = PGconn.connect(*@connection_parameters)
+ PGconn.translate_results = false if PGconn.respond_to?(:translate_results=)
+
+ # Ignore async_exec and async_query when using postgres-pr.
+ @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec)
+
+ # Use escape string syntax if available. We cannot do this lazily when encountering
+ # the first string, because that could then break any transactions in progress.
+ # See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
+ # If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't
+ # support escape string syntax. Don't override the inherited quoted_string_prefix.
+ if supports_standard_conforming_strings?
+ self.class.instance_eval do
+ define_method(:quoted_string_prefix) { 'E' }
+ end
+ end
+
+ # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
+ # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
+ # should know about this but can't detect it there, so deal with it here.
+ money_precision = (postgresql_version >= 80300) ? 19 : 10
+ PostgreSQLColumn.module_eval(<<-end_eval)
+ def extract_precision(sql_type)
+ if sql_type =~ /^money$/
+ #{money_precision}
+ else
+ super
+ end
+ end
+ end_eval
+
+ configure_connection
+ end
+
+ # Configures the encoding, verbosity, and schema search path of the connection.
+ # This is called by #connect and should not be called manually.
+ def configure_connection
+ if @config[:encoding]
+ if @connection.respond_to?(:set_client_encoding)
+ @connection.set_client_encoding(@config[:encoding])
+ else
+ execute("SET client_encoding TO '#{@config[:encoding]}'")
+ end
+ end
+ self.client_min_messages = @config[:min_messages] if @config[:min_messages]
+ self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
+ end
+
+ # Returns the current ID of a table's sequence.
+ def last_insert_id(table, sequence_name) #:nodoc:
Integer(select_value("SELECT currval('#{sequence_name}')"))
end
+ # Executes a SELECT query and returns the results, performing any data type
+ # conversions that are required to be performed here instead of in PostgreSQLColumn.
def select(sql, name = nil)
+ fields, rows = select_raw(sql, name)
+ result = []
+ for row in rows
+ row_hash = {}
+ fields.each_with_index do |f, i|
+ row_hash[f] = row[i]
+ end
+ result << row_hash
+ end
+ result
+ end
+
+ def select_raw(sql, name = nil)
res = execute(sql, name)
results = res.result
+ fields = []
rows = []
if results.length > 0
fields = res.fields
results.each do |row|
hashed_row = {}
- row.each_index do |cel_index|
- column = row[cel_index]
-
- case res.type(cel_index)
- when BYTEA_COLUMN_TYPE_OID
- column = unescape_bytea(column)
- when TIMESTAMPTZOID, TIMESTAMPOID
- column = cast_to_time(column)
- when NUMERIC_COLUMN_TYPE_OID
- column = column.to_d if column.respond_to?(:to_d)
+ row.each_index do |cell_index|
+ # If this is a money type column and there are any currency symbols,
+ # then strip them off. Indeed it would be prettier to do this in
+ # PostgreSQLColumn.string_to_decimal but would break form input
+ # fields that call value_before_type_cast.
+ if res.type(cell_index) == MONEY_COLUMN_TYPE_OID
+ # Because money output is formatted according to the locale, there are two
+ # cases to consider (note the decimal separators):
+ # (1) $12,345,678.12
+ # (2) $12.345.678,12
+ case column = row[cell_index]
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
+ row[cell_index] = column.gsub(/[^-\d\.]/, '')
+ when /^-?\D+[\d\.]+,\d{2}$/ # (2)
+ row[cell_index] = column.gsub(/[^-\d,]/, '').sub(/,/, '.')
+ end
end
- hashed_row[fields[cel_index]] = column
+ hashed_row[fields[cell_index]] = column
end
- rows << hashed_row
+ rows << row
end
end
res.clear
- return rows
+ return fields, rows
end
- def escape_bytea(s)
- if PGconn.respond_to? :escape_bytea
- self.class.send(:define_method, :escape_bytea) do |s|
- PGconn.escape_bytea(s) if s
- end
- else
- self.class.send(:define_method, :escape_bytea) do |s|
- if s
- result = ''
- s.each_byte { |c| result << sprintf('\\\\%03o', c) }
- result
- end
- end
- end
- escape_bytea(s)
- end
-
- def unescape_bytea(s)
- if PGconn.respond_to? :unescape_bytea
- self.class.send(:define_method, :unescape_bytea) do |s|
- PGconn.unescape_bytea(s) if s
- end
- else
- self.class.send(:define_method, :unescape_bytea) do |s|
- if s
- result = ''
- i, max = 0, s.size
- while i < max
- char = s[i]
- if char == ?\\
- if s[i+1] == ?\\
- char = ?\\
- i += 1
- else
- char = s[i+1..i+3].oct
- i += 3
- end
- end
- result << char
- i += 1
- end
- result
- end
- end
- end
- unescape_bytea(s)
- end
-
- # Query a table's column names, default values, and types.
+ # Returns the list of a table's column names, data types, and default values.
#
# The underlying query is roughly:
# SELECT column.name, column.type, default.value
@@ -524,7 +832,7 @@ module ActiveRecord
# Query implementation notes:
# - format_type includes the column size constraint, e.g. varchar(50)
# - ::regclass is a function that gives the id for a table name
- def column_definitions(table_name)
+ def column_definitions(table_name) #:nodoc:
query <<-end_sql
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
FROM pg_attribute a LEFT JOIN pg_attrdef d
@@ -534,51 +842,6 @@ module ActiveRecord
ORDER BY a.attnum
end_sql
end
-
- # Translate PostgreSQL-specific types into simplified SQL types.
- # These are special cases; standard types are handled by
- # ConnectionAdapters::Column#simplified_type.
- def translate_field_type(field_type)
- # Match the beginning of field_type since it may have a size constraint on the end.
- case field_type
- # PostgreSQL array data types.
- when /\[\]$/i then 'string'
- when /^timestamp/i then 'datetime'
- when /^real|^money/i then 'float'
- when /^interval/i then 'string'
- # geometric types (the line type is currently not implemented in postgresql)
- when /^(?:point|lseg|box|"?path"?|polygon|circle)/i then 'string'
- when /^bytea/i then 'binary'
- else field_type # Pass through standard types.
- end
- end
-
- def default_value(value)
- # Boolean types
- return "t" if value =~ /true/i
- return "f" if value =~ /false/i
-
- # Char/String/Bytea type values
- return $1 if value =~ /^'(.*)'::(bpchar|text|character varying|bytea)$/
-
- # Numeric values
- return value if value =~ /^-?[0-9]+(\.[0-9]*)?/
-
- # Fixed dates / times
- return $1 if value =~ /^'(.+)'::(date|timestamp)/
-
- # Anything else is blank, some user type, or some function
- # and we can't know the value of that, so return nil.
- return nil
- end
-
- # Only needed for DateTime instances
- def cast_to_time(value)
- return value unless value.class == DateTime
- v = value
- time_array = [v.year, v.month, v.day, v.hour, v.min, v.sec, v.usec]
- Time.send(Base.default_timezone, *time_array) rescue nil
- end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
new file mode 100644
index 00000000..0827f61d
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -0,0 +1,34 @@
+require 'active_record/connection_adapters/sqlite_adapter'
+
+module ActiveRecord
+ class Base
+ # sqlite3 adapter reuses sqlite_connection.
+ def self.sqlite3_connection(config) # :nodoc:
+ parse_sqlite_config!(config)
+
+ unless self.class.const_defined?(:SQLite3)
+ require_library_or_gem(config[:adapter])
+ end
+
+ db = SQLite3::Database.new(
+ config[:database],
+ :results_as_hash => true,
+ :type_translation => false
+ )
+
+ db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
+
+ ConnectionAdapters::SQLite3Adapter.new(db, logger)
+ end
+ end
+
+ module ConnectionAdapters #:nodoc:
+ class SQLite3Adapter < SQLiteAdapter # :nodoc:
+ def table_structure(table_name)
+ returning structure = @connection.table_info(table_name) do
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 0a9c57e1..586594e0 100644
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -1,33 +1,11 @@
-# Author: Luke Holden
-# Updated for SQLite3: Jamis Buck
-
require 'active_record/connection_adapters/abstract_adapter'
module ActiveRecord
class Base
class << self
- # sqlite3 adapter reuses sqlite_connection.
- def sqlite3_connection(config) # :nodoc:
- parse_config!(config)
-
- unless self.class.const_defined?(:SQLite3)
- require_library_or_gem(config[:adapter])
- end
-
- db = SQLite3::Database.new(
- config[:database],
- :results_as_hash => true,
- :type_translation => false
- )
-
- db.busy_timeout(config[:timeout]) unless config[:timeout].nil?
-
- ConnectionAdapters::SQLite3Adapter.new(db, logger)
- end
-
# Establishes a connection to the database that's used by all Active Record objects
def sqlite_connection(config) # :nodoc:
- parse_config!(config)
+ parse_sqlite_config!(config)
unless self.class.const_defined?(:SQLite)
require_library_or_gem(config[:adapter])
@@ -47,7 +25,7 @@ module ActiveRecord
end
private
- def parse_config!(config)
+ def parse_sqlite_config!(config)
config[:database] ||= config[:dbfile]
# Require database.
unless config[:database]
@@ -56,7 +34,7 @@ module ActiveRecord
# Allow database path relative to RAILS_ROOT, but only if
# the database path is not the special path that tells
- # Sqlite build a database only in memory.
+ # Sqlite to build a database only in memory.
if Object.const_defined?(:RAILS_ROOT) && ':memory:' != config[:database]
config[:database] = File.expand_path(config[:database], RAILS_ROOT)
end
@@ -73,16 +51,16 @@ module ActiveRecord
when "\0" then "%00"
when "%" then "%25"
end
- end
+ end
end
-
+
def binary_to_string(value)
value.gsub(/%00|%25/n) do |b|
case b
when "%00" then "\0"
when "%25" then "%"
end
- end
+ end
end
end
end
@@ -105,14 +83,23 @@ module ActiveRecord
def requires_reloading?
true
end
-
+
+ def disconnect!
+ super
+ @connection.close rescue nil
+ end
+
def supports_count_distinct? #:nodoc:
- false
+ sqlite_version >= '3.2.6'
+ end
+
+ def supports_autoincrement? #:nodoc:
+ sqlite_version >= '3.1.0'
end
def native_database_types #:nodoc:
{
- :primary_key => "INTEGER PRIMARY KEY NOT NULL",
+ :primary_key => default_primary_key_type,
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "integer" },
@@ -145,44 +132,30 @@ module ActiveRecord
catch_schema_changes { log(sql, name) { @connection.execute(sql) } }
end
- def update(sql, name = nil) #:nodoc:
- execute(sql, name)
+ def update_sql(sql, name = nil) #:nodoc:
+ super
@connection.changes
end
- def delete(sql, name = nil) #:nodoc:
+ def delete_sql(sql, name = nil) #:nodoc:
sql += " WHERE 1=1" unless sql =~ /WHERE/i
- execute(sql, name)
- @connection.changes
+ super sql, name
end
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
- execute(sql, name = nil)
- id_value || @connection.last_insert_row_id
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
+ super || @connection.last_insert_row_id
end
- def select_all(sql, name = nil) #:nodoc:
+ def select_rows(sql, name = nil)
execute(sql, name).map do |row|
- record = {}
- row.each_key do |key|
- if key.is_a?(String)
- record[key.sub(/^\w+\./, '')] = row[key]
- end
- end
- record
+ (0...(row.size / 2)).map { |i| row[i] }
end
end
- def select_one(sql, name = nil) #:nodoc:
- result = select_all(sql, name)
- result.nil? ? nil : result.first
- end
-
-
def begin_db_transaction #:nodoc:
catch_schema_changes { @connection.transaction }
end
-
+
def commit_db_transaction #:nodoc:
catch_schema_changes { @connection.commit }
end
@@ -201,7 +174,13 @@ module ActiveRecord
# SCHEMA STATEMENTS ========================================
def tables(name = nil) #:nodoc:
- execute("SELECT name FROM sqlite_master WHERE type = 'table'", name).map do |row|
+ sql = <<-SQL
+ SELECT name
+ FROM sqlite_master
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
+ SQL
+
+ execute(sql, name).map do |row|
row[0]
end
end
@@ -229,7 +208,7 @@ module ActiveRecord
def remove_index(table_name, options={}) #:nodoc:
execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
end
-
+
def rename_table(name, new_name)
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
end
@@ -239,13 +218,13 @@ module ActiveRecord
# See last paragraph on http://www.sqlite.org/lang_altertable.html
execute "VACUUM"
end
-
+
def remove_column(table_name, column_name) #:nodoc:
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
end
end
-
+
def change_column_default(table_name, column_name, default) #:nodoc:
alter_table(table_name) do |definition|
definition[column_name].default = default
@@ -259,60 +238,78 @@ module ActiveRecord
self.type = type
self.limit = options[:limit] if options.include?(:limit)
self.default = options[:default] if include_default
+ self.null = options[:null] if options.include?(:null)
end
end
end
def rename_column(table_name, column_name, new_column_name) #:nodoc:
- alter_table(table_name, :rename => {column_name => new_column_name})
+ alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
+ end
+
+ def empty_insert_statement(table_name)
+ "INSERT INTO #{table_name} VALUES(NULL)"
end
-
protected
- def table_structure(table_name)
- returning structure = execute("PRAGMA table_info(#{table_name})") do
- raise ActiveRecord::StatementInvalid if structure.empty?
+ def select(sql, name = nil) #:nodoc:
+ execute(sql, name).map do |row|
+ record = {}
+ row.each_key do |key|
+ if key.is_a?(String)
+ record[key.sub(/^\w+\./, '')] = row[key]
+ end
+ end
+ record
end
end
-
+
+ def table_structure(table_name)
+ returning structure = execute("PRAGMA table_info(#{table_name})") do
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
+ end
+ end
+
def alter_table(table_name, options = {}) #:nodoc:
altered_table_name = "altered_#{table_name}"
caller = lambda {|definition| yield definition if block_given?}
transaction do
- move_table(table_name, altered_table_name,
+ move_table(table_name, altered_table_name,
options.merge(:temporary => true))
move_table(altered_table_name, table_name, &caller)
end
end
-
+
def move_table(from, to, options = {}, &block) #:nodoc:
copy_table(from, to, options, &block)
drop_table(from)
end
-
+
def copy_table(from, to, options = {}) #:nodoc:
- create_table(to, options) do |@definition|
+ options = options.merge(:id => !columns(from).detect{|c| c.name == 'id'}.nil?)
+ create_table(to, options) do |definition|
+ @definition = definition
columns(from).each do |column|
column_name = options[:rename] ?
(options[:rename][column.name] ||
options[:rename][column.name.to_sym] ||
column.name) : column.name
-
- @definition.column(column_name, column.type,
+
+ @definition.column(column_name, column.type,
:limit => column.limit, :default => column.default,
:null => column.null)
end
- @definition.primary_key(primary_key(from))
+ @definition.primary_key(primary_key(from)) if primary_key(from)
yield @definition if block_given?
end
-
+
copy_table_indexes(from, to)
- copy_table_contents(from, to,
- @definition.columns.map {|column| column.name},
+ copy_table_contents(from, to,
+ @definition.columns.map {|column| column.name},
options[:rename] || {})
end
-
+
def copy_table_indexes(from, to) #:nodoc:
indexes(from).each do |index|
name = index.name
@@ -321,27 +318,29 @@ module ActiveRecord
elsif from == "altered_#{to}"
name = name[5..-1]
end
-
+
# index name can't be the same
opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
opts[:unique] = true if index.unique
add_index(to, index.columns, opts)
end
end
-
+
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
from_columns = columns(from).collect {|col| col.name}
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ','
+
@connection.execute "SELECT * FROM #{from}" do |row|
- sql = "INSERT INTO #{to} ("+columns*','+") VALUES ("
+ sql = "INSERT INTO #{to} (#{quoted_columns}) VALUES ("
sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
sql << ')'
@connection.execute sql
end
end
-
+
def catch_schema_changes
return yield
rescue ActiveRecord::StatementInvalid => exception
@@ -352,49 +351,34 @@ module ActiveRecord
raise
end
end
- end
-
- class SQLite3Adapter < SQLiteAdapter # :nodoc:
- def table_structure(table_name)
- returning structure = @connection.table_info(table_name) do
- raise ActiveRecord::StatementInvalid if structure.empty?
+
+ def sqlite_version
+ @sqlite_version ||= select_value('select sqlite_version(*)')
+ end
+
+ def default_primary_key_type
+ if supports_autoincrement?
+ 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'.freeze
+ else
+ 'INTEGER PRIMARY KEY NOT NULL'.freeze
+ end
end
- end
end
class SQLite2Adapter < SQLiteAdapter # :nodoc:
- # SQLite 2 does not support COUNT(DISTINCT) queries:
- #
- # select COUNT(DISTINCT ArtistID) from CDs;
- #
- # In order to get the number of artists we execute the following statement
- #
- # SELECT COUNT(ArtistID) FROM (SELECT DISTINCT ArtistID FROM CDs);
- def execute(sql, name = nil) #:nodoc:
- super(rewrite_count_distinct_queries(sql), name)
+ def supports_count_distinct? #:nodoc:
+ false
end
-
- def rewrite_count_distinct_queries(sql)
- if sql =~ /count\(distinct ([^\)]+)\)( AS \w+)? (.*)/i
- distinct_column = $1
- distinct_query = $3
- column_name = distinct_column.split('.').last
- "SELECT COUNT(#{column_name}) FROM (SELECT DISTINCT #{distinct_column} #{distinct_query})"
- else
- sql
- end
- end
-
+
def rename_table(name, new_name)
move_table(name, new_name)
end
-
+
def add_column(table_name, column_name, type, options = {}) #:nodoc:
alter_table(table_name) do |definition|
definition.column(column_name, type, options)
end
end
-
end
class DeprecatedSQLiteAdapter < SQLite2Adapter # :nodoc:
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb
deleted file mode 100644
index 865cdc6b..00000000
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb
+++ /dev/null
@@ -1,591 +0,0 @@
-require 'active_record/connection_adapters/abstract_adapter'
-
-require 'bigdecimal'
-require 'bigdecimal/util'
-
-# sqlserver_adapter.rb -- ActiveRecord adapter for Microsoft SQL Server
-#
-# Author: Joey Gibson
-# Date: 10/14/2004
-#
-# Modifications: DeLynn Berry
-# Date: 3/22/2005
-#
-# Modifications (ODBC): Mark Imbriaco
-# Date: 6/26/2005
-
-# Modifications (Migrations): Tom Ward
-# Date: 27/10/2005
-#
-# Modifications (Numerous fixes as maintainer): Ryan Tomayko
-# Date: Up to July 2006
-
-# Current maintainer: Tom Ward
-
-module ActiveRecord
- class Base
- def self.sqlserver_connection(config) #:nodoc:
- require_library_or_gem 'dbi' unless self.class.const_defined?(:DBI)
-
- config = config.symbolize_keys
-
- mode = config[:mode] ? config[:mode].to_s.upcase : 'ADO'
- username = config[:username] ? config[:username].to_s : 'sa'
- password = config[:password] ? config[:password].to_s : ''
- autocommit = config.key?(:autocommit) ? config[:autocommit] : true
- if mode == "ODBC"
- raise ArgumentError, "Missing DSN. Argument ':dsn' must be set in order for this adapter to work." unless config.has_key?(:dsn)
- dsn = config[:dsn]
- driver_url = "DBI:ODBC:#{dsn}"
- else
- raise ArgumentError, "Missing Database. Argument ':database' must be set in order for this adapter to work." unless config.has_key?(:database)
- database = config[:database]
- host = config[:host] ? config[:host].to_s : 'localhost'
- driver_url = "DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};"
- end
- conn = DBI.connect(driver_url, username, password)
- conn["AutoCommit"] = autocommit
- ConnectionAdapters::SQLServerAdapter.new(conn, logger, [driver_url, username, password])
- end
- end # class Base
-
- module ConnectionAdapters
- class SQLServerColumn < Column# :nodoc:
- attr_reader :identity, :is_special
-
- def initialize(name, default, sql_type = nil, identity = false, null = true) # TODO: check ok to remove scale_value = 0
- super(name, default, sql_type, null)
- @identity = identity
- @is_special = sql_type =~ /text|ntext|image/i
- # TODO: check ok to remove @scale = scale_value
- # SQL Server only supports limits on *char and float types
- @limit = nil unless @type == :float or @type == :string
- end
-
- def simplified_type(field_type)
- case field_type
- when /money/i then :decimal
- when /image/i then :binary
- when /bit/i then :boolean
- when /uniqueidentifier/i then :string
- else super
- end
- end
-
- def type_cast(value)
- return nil if value.nil?
- case type
- when :datetime then cast_to_datetime(value)
- when :timestamp then cast_to_time(value)
- when :time then cast_to_time(value)
- when :date then cast_to_datetime(value)
- when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
- else super
- end
- end
-
- def cast_to_time(value)
- return value if value.is_a?(Time)
- time_array = ParseDate.parsedate(value)
- Time.send(Base.default_timezone, *time_array) rescue nil
- end
-
- def cast_to_datetime(value)
- return value.to_time if value.is_a?(DBI::Timestamp)
-
- if value.is_a?(Time)
- if value.year != 0 and value.month != 0 and value.day != 0
- return value
- else
- return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
- end
- end
-
- if value.is_a?(DateTime)
- return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec)
- end
-
- return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
- value
- end
-
- # TODO: Find less hack way to convert DateTime objects into Times
-
- def self.string_to_time(value)
- if value.is_a?(DateTime)
- return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec)
- else
- super
- end
- end
-
- # These methods will only allow the adapter to insert binary data with a length of 7K or less
- # because of a SQL Server statement length policy.
- def self.string_to_binary(value)
- value.gsub(/(\r|\n|\0|\x1a)/) do
- case $1
- when "\r" then "%00"
- when "\n" then "%01"
- when "\0" then "%02"
- when "\x1a" then "%03"
- end
- end
- end
-
- def self.binary_to_string(value)
- value.gsub(/(%00|%01|%02|%03)/) do
- case $1
- when "%00" then "\r"
- when "%01" then "\n"
- when "%02\0" then "\0"
- when "%03" then "\x1a"
- end
- end
- end
- end
-
- # In ADO mode, this adapter will ONLY work on Windows systems,
- # since it relies on Win32OLE, which, to my knowledge, is only
- # available on Windows.
- #
- # This mode also relies on the ADO support in the DBI module. If you are using the
- # one-click installer of Ruby, then you already have DBI installed, but
- # the ADO module is *NOT* installed. You will need to get the latest
- # source distribution of Ruby-DBI from http://ruby-dbi.sourceforge.net/
- # unzip it, and copy the file
- # src/lib/dbd_ado/ADO.rb
- # to
- # X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb
- # (you will more than likely need to create the ADO directory).
- # Once you've installed that file, you are ready to go.
- #
- # In ODBC mode, the adapter requires the ODBC support in the DBI module which requires
- # the Ruby ODBC module. Ruby ODBC 0.996 was used in development and testing,
- # and it is available at http://www.ch-werner.de/rubyodbc/
- #
- # Options:
- #
- # * :mode -- ADO or ODBC. Defaults to ADO.
- # * :username -- Defaults to sa.
- # * :password -- Defaults to empty string.
- #
- # ADO specific options:
- #
- # * :host -- Defaults to localhost.
- # * :database -- The name of the database. No default, must be provided.
- #
- # ODBC specific options:
- #
- # * :dsn -- Defaults to nothing.
- #
- # ADO code tested on Windows 2000 and higher systems,
- # running ruby 1.8.2 (2004-07-29) [i386-mswin32], and SQL Server 2000 SP3.
- #
- # ODBC code tested on a Fedora Core 4 system, running FreeTDS 0.63,
- # unixODBC 2.2.11, Ruby ODBC 0.996, Ruby DBI 0.0.23 and Ruby 1.8.2.
- # [Linux strongmad 2.6.11-1.1369_FC4 #1 Thu Jun 2 22:55:56 EDT 2005 i686 i686 i386 GNU/Linux]
- class SQLServerAdapter < AbstractAdapter
-
- def initialize(connection, logger, connection_options=nil)
- super(connection, logger)
- @connection_options = connection_options
- end
-
- def native_database_types
- {
- :primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
- :string => { :name => "varchar", :limit => 255 },
- :text => { :name => "text" },
- :integer => { :name => "int" },
- :float => { :name => "float", :limit => 8 },
- :decimal => { :name => "decimal" },
- :datetime => { :name => "datetime" },
- :timestamp => { :name => "datetime" },
- :time => { :name => "datetime" },
- :date => { :name => "datetime" },
- :binary => { :name => "image"},
- :boolean => { :name => "bit"}
- }
- end
-
- def adapter_name
- 'SQLServer'
- end
-
- def supports_migrations? #:nodoc:
- true
- end
-
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
- return super unless type.to_s == 'integer'
-
- if limit.nil? || limit == 4
- 'integer'
- elsif limit < 4
- 'smallint'
- else
- 'bigint'
- end
- end
-
- # CONNECTION MANAGEMENT ====================================#
-
- # Returns true if the connection is active.
- def active?
- @connection.execute("SELECT 1").finish
- true
- rescue DBI::DatabaseError, DBI::InterfaceError
- false
- end
-
- # Reconnects to the database, returns false if no connection could be made.
- def reconnect!
- disconnect!
- @connection = DBI.connect(*@connection_options)
- rescue DBI::DatabaseError => e
- @logger.warn "#{adapter_name} reconnection failed: #{e.message}" if @logger
- false
- end
-
- # Disconnects from the database
-
- def disconnect!
- @connection.disconnect rescue nil
- end
-
- def columns(table_name, name = nil)
- return [] if table_name.blank?
- table_name = table_name.to_s if table_name.is_a?(Symbol)
- table_name = table_name.split('.')[-1] unless table_name.nil?
- table_name = table_name.gsub(/[\[\]]/, '')
- sql = %Q{
- SELECT
- cols.COLUMN_NAME as ColName,
- cols.COLUMN_DEFAULT as DefaultValue,
- cols.NUMERIC_SCALE as numeric_scale,
- cols.NUMERIC_PRECISION as numeric_precision,
- cols.DATA_TYPE as ColType,
- cols.IS_NULLABLE As IsNullable,
- COL_LENGTH(cols.TABLE_NAME, cols.COLUMN_NAME) as Length,
- COLUMNPROPERTY(OBJECT_ID(cols.TABLE_NAME), cols.COLUMN_NAME, 'IsIdentity') as IsIdentity,
- cols.NUMERIC_SCALE as Scale
- FROM INFORMATION_SCHEMA.COLUMNS cols
- WHERE cols.TABLE_NAME = '#{table_name}'
- }
- # Comment out if you want to have the Columns select statment logged.
- # Personally, I think it adds unnecessary bloat to the log.
- # If you do comment it out, make sure to un-comment the "result" line that follows
- result = log(sql, name) { @connection.select_all(sql) }
- #result = @connection.select_all(sql)
- columns = []
- result.each do |field|
- default = field[:DefaultValue].to_s.gsub!(/[()\']/,"") =~ /null/ ? nil : field[:DefaultValue]
- if field[:ColType] =~ /numeric|decimal/i
- type = "#{field[:ColType]}(#{field[:numeric_precision]},#{field[:numeric_scale]})"
- else
- type = "#{field[:ColType]}(#{field[:Length]})"
- end
- is_identity = field[:IsIdentity] == 1
- is_nullable = field[:IsNullable] == 'YES'
- columns << SQLServerColumn.new(field[:ColName], default, type, is_identity, is_nullable)
- end
- columns
- end
-
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- execute(sql, name)
- id_value || select_one("SELECT @@IDENTITY AS Ident")["Ident"]
- end
-
- def update(sql, name = nil)
- execute(sql, name) do |handle|
- handle.rows
- end || select_one("SELECT @@ROWCOUNT AS AffectedRows")["AffectedRows"]
- end
-
- alias_method :delete, :update
-
- def execute(sql, name = nil)
- if sql =~ /^\s*INSERT/i && (table_name = query_requires_identity_insert?(sql))
- log(sql, name) do
- with_identity_insert_enabled(table_name) do
- @connection.execute(sql) do |handle|
- yield(handle) if block_given?
- end
- end
- end
- else
- log(sql, name) do
- @connection.execute(sql) do |handle|
- yield(handle) if block_given?
- end
- end
- end
- end
-
- def begin_db_transaction
- @connection["AutoCommit"] = false
- rescue Exception => e
- @connection["AutoCommit"] = true
- end
-
- def commit_db_transaction
- @connection.commit
- ensure
- @connection["AutoCommit"] = true
- end
-
- def rollback_db_transaction
- @connection.rollback
- ensure
- @connection["AutoCommit"] = true
- end
-
- def quote(value, column = nil)
- return value.quoted_id if value.respond_to?(:quoted_id)
-
- case value
- when TrueClass then '1'
- when FalseClass then '0'
- when Time, DateTime then "'#{value.strftime("%Y%m%d %H:%M:%S")}'"
- when Date then "'#{value.strftime("%Y%m%d")}'"
- else super
- end
- end
-
- def quote_string(string)
- string.gsub(/\'/, "''")
- end
-
- def quote_column_name(name)
- "[#{name}]"
- end
-
- def add_limit_offset!(sql, options)
- if options[:limit] and options[:offset]
- total_rows = @connection.select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally")[0][:TotalRows].to_i
- if (options[:limit] + options[:offset]) >= total_rows
- options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
- end
- sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT#{$1} TOP #{options[:limit] + options[:offset]} ")
- sql << ") AS tmp1"
- if options[:order]
- options[:order] = options[:order].split(',').map do |field|
- parts = field.split(" ")
- tc = parts[0]
- if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
- tc.gsub!(/\./, '\\.\\[')
- tc << '\\]'
- end
- if sql =~ /#{tc} AS (t\d_r\d\d?)/
- parts[0] = $1
- elsif parts[0] =~ /\w+\.(\w+)/
- parts[0] = $1
- end
- parts.join(' ')
- end.join(', ')
- sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
- else
- sql << " ) AS tmp2"
- end
- elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
- sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
- "SELECT#{$1} TOP #{options[:limit]}"
- end unless options[:limit].nil?
- end
- end
-
- def recreate_database(name)
- drop_database(name)
- create_database(name)
- end
-
- def drop_database(name)
- execute "DROP DATABASE #{name}"
- end
-
- def create_database(name)
- execute "CREATE DATABASE #{name}"
- end
-
- def current_database
- @connection.select_one("select DB_NAME()")[0]
- end
-
- def tables(name = nil)
- execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'", name) do |sth|
- sth.inject([]) do |tables, field|
- table_name = field[0]
- tables << table_name unless table_name == 'dtproperties'
- tables
- end
- end
- end
-
- def indexes(table_name, name = nil)
- ActiveRecord::Base.connection.instance_variable_get("@connection")["AutoCommit"] = false
- indexes = []
- execute("EXEC sp_helpindex '#{table_name}'", name) do |sth|
- sth.each do |index|
- unique = index[1] =~ /unique/
- primary = index[1] =~ /primary key/
- if !primary
- indexes << IndexDefinition.new(table_name, index[0], unique, index[2].split(", "))
- end
- end
- end
- indexes
- ensure
- ActiveRecord::Base.connection.instance_variable_get("@connection")["AutoCommit"] = true
- end
-
- def rename_table(name, new_name)
- execute "EXEC sp_rename '#{name}', '#{new_name}'"
- end
-
- # Adds a new column to the named table.
- # See TableDefinition#column for details of the options you can use.
- def add_column(table_name, column_name, type, options = {})
- add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
- add_column_options!(add_column_sql, options)
- # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
- # add_column_sql << " CONSTRAINT ck__#{table_name}__#{column_name}__date_only CHECK ( CONVERT(CHAR(12), #{quote_column_name(column_name)}, 14)='00:00:00:000' )" if type == :date
- execute(add_column_sql)
- end
-
- def rename_column(table, column, new_column_name)
- execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
- end
-
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
- sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"]
- if options_include_default?(options)
- remove_default_constraint(table_name, column_name)
- sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(options[:default], options[:column])} FOR #{column_name}"
- end
- sql_commands.each {|c|
- execute(c)
- }
- end
-
- def remove_column(table_name, column_name)
- remove_check_constraints(table_name, column_name)
- remove_default_constraint(table_name, column_name)
- execute "ALTER TABLE [#{table_name}] DROP COLUMN [#{column_name}]"
- end
-
- def remove_default_constraint(table_name, column_name)
- constraints = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
-
- constraints.each do |constraint|
- execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
- end
- end
-
- def remove_check_constraints(table_name, column_name)
- # TODO remove all constraints in single method
- constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
- constraints.each do |constraint|
- execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
- end
- end
-
- def remove_index(table_name, options = {})
- execute "DROP INDEX #{table_name}.#{quote_column_name(index_name(table_name, options))}"
- end
-
- private
- def select(sql, name = nil)
- repair_special_columns(sql)
-
- result = []
- execute(sql) do |handle|
- handle.each do |row|
- row_hash = {}
- row.each_with_index do |value, i|
- if value.is_a? DBI::Timestamp
- value = DateTime.new(value.year, value.month, value.day, value.hour, value.minute, value.sec)
- end
- row_hash[handle.column_names[i]] = value
- end
- result << row_hash
- end
- end
- result
- end
-
- # Turns IDENTITY_INSERT ON for table during execution of the block
- # N.B. This sets the state of IDENTITY_INSERT to OFF after the
- # block has been executed without regard to its previous state
-
- def with_identity_insert_enabled(table_name, &block)
- set_identity_insert(table_name, true)
- yield
- ensure
- set_identity_insert(table_name, false)
- end
-
- def set_identity_insert(table_name, enable = true)
- execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
- rescue Exception => e
- raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
- end
-
- def get_table_name(sql)
- if sql =~ /^\s*insert\s+into\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
- $1
- elsif sql =~ /from\s+([^\(\s]+)\s*/i
- $1
- else
- nil
- end
- end
-
- def identity_column(table_name)
- @table_columns = {} unless @table_columns
- @table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
- @table_columns[table_name].each do |col|
- return col.name if col.identity
- end
-
- return nil
- end
-
- def query_requires_identity_insert?(sql)
- table_name = get_table_name(sql)
- id_column = identity_column(table_name)
- sql =~ /\[#{id_column}\]/ ? table_name : nil
- end
-
- def change_order_direction(order)
- order.split(",").collect {|fragment|
- case fragment
- when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
- when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
- else String.new(fragment).split(',').join(' DESC,') + ' DESC'
- end
- }.join(",")
- end
-
- def get_special_columns(table_name)
- special = []
- @table_columns ||= {}
- @table_columns[table_name] ||= columns(table_name)
- @table_columns[table_name].each do |col|
- special << col.name if col.is_special
- end
- special
- end
-
- def repair_special_columns(sql)
- special_cols = get_special_columns(get_table_name(sql))
- for col in special_cols.to_a
- sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
- sql.gsub!(/ORDER BY #{col.to_s}/i, '')
- end
- sql
- end
-
- end #class SQLServerAdapter < AbstractAdapter
- end #module ConnectionAdapters
-end #module ActiveRecord
diff --git a/vendor/rails/activerecord/lib/active_record/connection_adapters/sybase_adapter.rb b/vendor/rails/activerecord/lib/active_record/connection_adapters/sybase_adapter.rb
deleted file mode 100644
index 708a11b2..00000000
--- a/vendor/rails/activerecord/lib/active_record/connection_adapters/sybase_adapter.rb
+++ /dev/null
@@ -1,662 +0,0 @@
-# sybase_adaptor.rb
-# Author: John R. Sheets
-#
-# 01 Mar 2006: Initial version. Based on code from Will Sobel
-# (http://dev.rubyonrails.org/ticket/2030)
-#
-# 17 Mar 2006: Added support for migrations; fixed issues with :boolean columns.
-#
-# 13 Apr 2006: Improved column type support to properly handle dates and user-defined
-# types; fixed quoting of integer columns.
-#
-# 05 Jan 2007: Updated for Rails 1.2 release:
-# restricted Fixtures#insert_fixtures monkeypatch to Sybase adapter;
-# removed SQL type precision from TEXT type to fix broken
-# ActiveRecordStore (jburks, #6878); refactored select() to use execute();
-# fixed leaked exception for no-op change_column(); removed verbose SQL dump
-# from columns(); added missing scale parameter in normalize_type().
-
-require 'active_record/connection_adapters/abstract_adapter'
-
-begin
-require 'sybsql'
-
-module ActiveRecord
- class Base
- # Establishes a connection to the database that's used by all Active Record objects
- def self.sybase_connection(config) # :nodoc:
- config = config.symbolize_keys
-
- username = config[:username] ? config[:username].to_s : 'sa'
- password = config[:password] ? config[:password].to_s : ''
-
- if config.has_key?(:host)
- host = config[:host]
- else
- raise ArgumentError, "No database server name specified. Missing argument: host."
- end
-
- if config.has_key?(:database)
- database = config[:database]
- else
- raise ArgumentError, "No database specified. Missing argument: database."
- end
-
- ConnectionAdapters::SybaseAdapter.new(
- SybSQL.new({'S' => host, 'U' => username, 'P' => password},
- ConnectionAdapters::SybaseAdapterContext), database, config, logger)
- end
- end # class Base
-
- module ConnectionAdapters
-
- # ActiveRecord connection adapter for Sybase Open Client bindings
- # (see http://raa.ruby-lang.org/project/sybase-ctlib).
- #
- # Options:
- #
- # * :host -- The name of the database server. No default, must be provided.
- # * :database -- The name of the database. No default, must be provided.
- # * :username -- Defaults to "sa".
- # * :password -- Defaults to empty string.
- #
- # Usage Notes:
- #
- # * The sybase-ctlib bindings do not support the DATE SQL column type; use DATETIME instead.
- # * Table and column names are limited to 30 chars in Sybase 12.5
- # * :binary columns not yet supported
- # * :boolean columns use the BIT SQL type, which does not allow nulls or
- # indexes. If a DEFAULT is not specified for ALTER TABLE commands, the
- # column will be declared with DEFAULT 0 (false).
- #
- # Migrations:
- #
- # The Sybase adapter supports migrations, but for ALTER TABLE commands to
- # work, the database must have the database option 'select into' set to
- # 'true' with sp_dboption (see below). The sp_helpdb command lists the current
- # options for all databases.
- #
- # 1> use mydb
- # 2> go
- # 1> master..sp_dboption mydb, "select into", true
- # 2> go
- # 1> checkpoint
- # 2> go
- class SybaseAdapter < AbstractAdapter # :nodoc:
- class ColumnWithIdentity < Column
- attr_reader :identity
-
- def initialize(name, default, sql_type = nil, nullable = nil, identity = nil, primary = nil)
- super(name, default, sql_type, nullable)
- @default, @identity, @primary = type_cast(default), identity, primary
- end
-
- def simplified_type(field_type)
- case field_type
- when /int|bigint|smallint|tinyint/i then :integer
- when /float|double|real/i then :float
- when /decimal|money|numeric|smallmoney/i then :decimal
- when /text|ntext/i then :text
- when /binary|image|varbinary/i then :binary
- when /char|nchar|nvarchar|string|varchar/i then :string
- when /bit/i then :boolean
- when /datetime|smalldatetime/i then :datetime
- else super
- end
- end
-
- def self.string_to_binary(value)
- "0x#{value.unpack("H*")[0]}"
- end
-
- def self.binary_to_string(value)
- # FIXME: sybase-ctlib uses separate sql method for binary columns.
- value
- end
- end # class ColumnWithIdentity
-
- # Sybase adapter
- def initialize(connection, database, config = {}, logger = nil)
- super(connection, logger)
- context = connection.context
- context.init(logger)
- @config = config
- @numconvert = config.has_key?(:numconvert) ? config[:numconvert] : true
- @limit = @offset = 0
- unless connection.sql_norow("USE #{database}")
- raise "Cannot USE #{database}"
- end
- end
-
- def native_database_types
- {
- :primary_key => "numeric(9,0) IDENTITY PRIMARY KEY",
- :string => { :name => "varchar", :limit => 255 },
- :text => { :name => "text" },
- :integer => { :name => "int" },
- :float => { :name => "float", :limit => 8 },
- :decimal => { :name => "decimal" },
- :datetime => { :name => "datetime" },
- :timestamp => { :name => "timestamp" },
- :time => { :name => "time" },
- :date => { :name => "datetime" },
- :binary => { :name => "image"},
- :boolean => { :name => "bit" }
- }
- end
-
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
- return super unless type.to_s == 'integer'
- if !limit.nil? && limit < 4
- 'smallint'
- else
- 'integer'
- end
- end
-
- def adapter_name
- 'Sybase'
- end
-
- def active?
- !(@connection.connection.nil? || @connection.connection_dead?)
- end
-
- def disconnect!
- @connection.close rescue nil
- end
-
- def reconnect!
- raise "Sybase Connection Adapter does not yet support reconnect!"
- # disconnect!
- # connect! # Not yet implemented
- end
-
- def table_alias_length
- 30
- end
-
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- begin
- table_name = get_table_name(sql)
- col = get_identity_column(table_name)
- ii_enabled = false
-
- if col != nil
- if query_contains_identity_column(sql, col)
- begin
- enable_identity_insert(table_name, true)
- ii_enabled = true
- rescue Exception => e
- raise ActiveRecordError, "IDENTITY_INSERT could not be turned ON"
- end
- end
- end
-
- log(sql, name) do
- execute(sql, name)
- ident = select_one("SELECT @@IDENTITY AS last_id")["last_id"]
- id_value || ident
- end
- ensure
- if ii_enabled
- begin
- enable_identity_insert(table_name, false)
- rescue Exception => e
- raise ActiveRecordError, "IDENTITY_INSERT could not be turned OFF"
- end
- end
- end
- end
-
- def execute(sql, name = nil)
- raw_execute(sql, name)
- @connection.results[0].row_count
- end
-
- def begin_db_transaction() raw_execute "BEGIN TRAN" end
- def commit_db_transaction() raw_execute "COMMIT TRAN" end
- def rollback_db_transaction() raw_execute "ROLLBACK TRAN" end
-
- def current_database
- select_one("select DB_NAME() as name")["name"]
- end
-
- def tables(name = nil)
- select("select name from sysobjects where type='U'", name).map { |row| row['name'] }
- end
-
- def indexes(table_name, name = nil)
- select("exec sp_helpindex #{table_name}", name).map do |index|
- unique = index["index_description"] =~ /unique/
- primary = index["index_description"] =~ /^clustered/
- if !primary
- cols = index["index_keys"].split(", ").each { |col| col.strip! }
- IndexDefinition.new(table_name, index["index_name"], unique, cols)
- end
- end.compact
- end
-
- def columns(table_name, name = nil)
- sql = <= 128
- primary = (sysstat2 & 8) == 8
- ColumnWithIdentity.new(name, default_value, type, nullable, identity, primary)
- end
- end
-
- def quoted_true
- "1"
- end
-
- def quoted_false
- "0"
- end
-
- def quote(value, column = nil)
- return value.quoted_id if value.respond_to?(:quoted_id)
-
- case value
- when String
- if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
- "#{quote_string(column.class.string_to_binary(value))}"
- elsif @numconvert && force_numeric?(column) && value =~ /^[+-]?[0-9]+$/o
- value
- else
- "'#{quote_string(value)}'"
- end
- when NilClass then (column && column.type == :boolean) ? '0' : "NULL"
- when TrueClass then '1'
- when FalseClass then '0'
- when Float, Fixnum, Bignum then force_numeric?(column) ? value.to_s : "'#{value.to_s}'"
- when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
- else super
- end
- end
-
- # True if column is explicitly declared non-numeric, or
- # if column is nil (not specified).
- def force_numeric?(column)
- (column.nil? || [:integer, :float, :decimal].include?(column.type))
- end
-
- def quote_string(s)
- s.gsub(/'/, "''") # ' (for ruby-mode)
- end
-
- def quote_column_name(name)
- # If column name is close to max length, skip the quotes, since they
- # seem to count as part of the length.
- ((name.to_s.length + 2) <= table_alias_length) ? "[#{name}]" : name.to_s
- end
-
- def add_limit_offset!(sql, options) # :nodoc:
- @limit = options[:limit]
- @offset = options[:offset]
- if use_temp_table?
- # Use temp table to hack offset with Sybase
- sql.sub!(/ FROM /i, ' INTO #artemp FROM ')
- elsif zero_limit?
- # "SET ROWCOUNT 0" turns off limits, so we have
- # to use a cheap trick.
- if sql =~ /WHERE/i
- sql.sub!(/WHERE/i, 'WHERE 1 = 2 AND ')
- elsif sql =~ /ORDER\s+BY/i
- sql.sub!(/ORDER\s+BY/i, 'WHERE 1 = 2 ORDER BY')
- else
- sql << 'WHERE 1 = 2'
- end
- end
- end
-
- def add_lock!(sql, options) #:nodoc:
- @logger.info "Warning: Sybase :lock option '#{options[:lock].inspect}' not supported" if @logger && options.has_key?(:lock)
- sql
- end
-
- def supports_migrations? #:nodoc:
- true
- end
-
- def rename_table(name, new_name)
- execute "EXEC sp_rename '#{name}', '#{new_name}'"
- end
-
- def rename_column(table, column, new_column_name)
- execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
- end
-
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
- begin
- execute "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
- rescue StatementInvalid => e
- # Swallow exception and reset context if no-op.
- raise e unless e.message =~ /no columns to drop, add or modify/
- @connection.context.reset
- end
-
- if options.has_key?(:default)
- remove_default_constraint(table_name, column_name)
- execute "ALTER TABLE #{table_name} REPLACE #{column_name} DEFAULT #{quote options[:default]}"
- end
- end
-
- def remove_column(table_name, column_name)
- remove_default_constraint(table_name, column_name)
- execute "ALTER TABLE #{table_name} DROP #{column_name}"
- end
-
- def remove_default_constraint(table_name, column_name)
- sql = "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
- select(sql).each do |constraint|
- execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
- end
- end
-
- def remove_index(table_name, options = {})
- execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
- end
-
- def add_column_options!(sql, options) #:nodoc:
- sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
-
- if check_null_for_column?(options[:column], sql)
- sql << (options[:null] == false ? " NOT NULL" : " NULL")
- end
- sql
- end
-
- def enable_identity_insert(table_name, enable = true)
- if has_identity_column(table_name)
- execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
- end
- end
-
- private
- def check_null_for_column?(col, sql)
- # Sybase columns are NOT NULL by default, so explicitly set NULL
- # if :null option is omitted. Disallow NULLs for boolean.
- type = col.nil? ? "" : col[:type]
-
- # Ignore :null if a primary key
- return false if type =~ /PRIMARY KEY/i
-
- # Ignore :null if a :boolean or BIT column
- if (sql =~ /\s+bit(\s+DEFAULT)?/i) || type == :boolean
- # If no default clause found on a boolean column, add one.
- sql << " DEFAULT 0" if $1.nil?
- return false
- end
- true
- end
-
- # Return the last value of the identity global value.
- def last_insert_id
- @connection.sql("SELECT @@IDENTITY")
- unless @connection.cmd_fail?
- id = @connection.top_row_result.rows.first.first
- if id
- id = id.to_i
- id = nil if id == 0
- end
- else
- id = nil
- end
- id
- end
-
- def affected_rows(name = nil)
- @connection.sql("SELECT @@ROWCOUNT")
- unless @connection.cmd_fail?
- count = @connection.top_row_result.rows.first.first
- count = count.to_i if count
- else
- 0
- end
- end
-
- # If limit is not set at all, we can ignore offset;
- # if limit *is* set but offset is zero, use normal select
- # with simple SET ROWCOUNT. Thus, only use the temp table
- # if limit is set and offset > 0.
- def use_temp_table?
- !@limit.nil? && !@offset.nil? && @offset > 0
- end
-
- def zero_limit?
- !@limit.nil? && @limit == 0
- end
-
- def raw_execute(sql, name = nil)
- log(sql, name) do
- @connection.context.reset
- @logger.debug "Setting row count to (#{@limit})" if @logger && @limit
- @connection.set_rowcount(@limit || 0)
- if sql =~ /^\s*SELECT/i
- @connection.sql(sql)
- else
- @connection.sql_norow(sql)
- end
- @limit = @offset = nil
- if @connection.cmd_fail? or @connection.context.failed?
- raise "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}"
- end
- end
- end
-
- # Select limit number of rows starting at optional offset.
- def select(sql, name = nil)
- if !use_temp_table?
- execute(sql, name)
- else
- log(sql, name) do
- # Select into a temp table and prune results
- @logger.debug "Selecting #{@limit + (@offset || 0)} or fewer rows into #artemp" if @logger
- @connection.context.reset
- @connection.set_rowcount(@limit + (@offset || 0))
- @connection.sql_norow(sql) # Select into temp table
- @logger.debug "Deleting #{@offset || 0} or fewer rows from #artemp" if @logger
- @connection.set_rowcount(@offset || 0)
- @connection.sql_norow("delete from #artemp") # Delete leading rows
- @connection.set_rowcount(0)
- @connection.sql("select * from #artemp") # Return the rest
- end
- end
-
- raise StatementInvalid, "SQL Command Failed for #{name}: #{sql}\nMessage: #{@connection.context.message}" if @connection.context.failed? or @connection.cmd_fail?
-
- rows = []
- results = @connection.top_row_result
- if results && results.rows.length > 0
- fields = results.columns.map { |column| column.sub(/_$/, '') }
- results.rows.each do |row|
- hashed_row = {}
- row.zip(fields) { |cell, column| hashed_row[column] = cell }
- rows << hashed_row
- end
- end
- @connection.sql_norow("drop table #artemp") if use_temp_table?
- @limit = @offset = nil
- rows
- end
-
- def get_table_name(sql)
- if sql =~ /^\s*insert\s+into\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
- $1
- elsif sql =~ /from\s+([^\(\s]+)\s*/i
- $1
- else
- nil
- end
- end
-
- def has_identity_column(table_name)
- !get_identity_column(table_name).nil?
- end
-
- def get_identity_column(table_name)
- @id_columns ||= {}
- if !@id_columns.has_key?(table_name)
- @logger.debug "Looking up identity column for table '#{table_name}'" if @logger
- col = columns(table_name).detect { |col| col.identity }
- @id_columns[table_name] = col.nil? ? nil : col.name
- end
- @id_columns[table_name]
- end
-
- def query_contains_identity_column(sql, col)
- sql =~ /\[#{col}\]/
- end
-
- # Resolve all user-defined types (udt) to their fundamental types.
- def resolve_type(field_type)
- (@udts ||= {})[field_type] ||= select_one("sp_help #{field_type}")["Storage_type"].strip
- end
-
- def normalize_type(field_type, prec, scale, length)
- has_scale = (!scale.nil? && scale > 0)
- type = if field_type =~ /numeric/i and !has_scale
- 'int'
- elsif field_type =~ /money/i
- 'numeric'
- else
- resolve_type(field_type.strip)
- end
-
- spec = if prec
- has_scale ? "(#{prec},#{scale})" : "(#{prec})"
- elsif length && !(type =~ /date|time|text/)
- "(#{length})"
- else
- ''
- end
- "#{type}#{spec}"
- end
- end # class SybaseAdapter
-
- class SybaseAdapterContext < SybSQLContext
- DEADLOCK = 1205
- attr_reader :message
-
- def init(logger = nil)
- @deadlocked = false
- @failed = false
- @logger = logger
- @message = nil
- end
-
- def srvmsgCB(con, msg)
- # Do not log change of context messages.
- if msg['severity'] == 10 or msg['severity'] == 0
- return true
- end
-
- if msg['msgnumber'] == DEADLOCK
- @deadlocked = true
- else
- @logger.info "SQL Command failed!" if @logger
- @failed = true
- end
-
- if @logger
- @logger.error "** SybSQLContext Server Message: **"
- @logger.error " Message number #{msg['msgnumber']} Severity #{msg['severity']} State #{msg['state']} Line #{msg['line']}"
- @logger.error " Server #{msg['srvname']}"
- @logger.error " Procedure #{msg['proc']}"
- @logger.error " Message String: #{msg['text']}"
- end
-
- @message = msg['text']
-
- true
- end
-
- def deadlocked?
- @deadlocked
- end
-
- def failed?
- @failed
- end
-
- def reset
- @deadlocked = false
- @failed = false
- @message = nil
- end
-
- def cltmsgCB(con, msg)
- return true unless ( msg.kind_of?(Hash) )
- unless ( msg[ "severity" ] ) then
- return true
- end
-
- if @logger
- @logger.error "** SybSQLContext Client-Message: **"
- @logger.error " Message number: LAYER=#{msg[ 'layer' ]} ORIGIN=#{msg[ 'origin' ]} SEVERITY=#{msg[ 'severity' ]} NUMBER=#{msg[ 'number' ]}"
- @logger.error " Message String: #{msg['msgstring']}"
- @logger.error " OS Error: #{msg['osstring']}"
-
- @message = msg['msgstring']
- end
-
- @failed = true
-
- # Not retry , CS_CV_RETRY_FAIL( probability TimeOut )
- if( msg[ 'severity' ] == "RETRY_FAIL" ) then
- @timeout_p = true
- return false
- end
-
- return true
- end
- end # class SybaseAdapterContext
-
- end # module ConnectionAdapters
-end # module ActiveRecord
-
-
-# Allow identity inserts for fixtures.
-require "active_record/fixtures"
-class Fixtures
- alias :original_insert_fixtures :insert_fixtures
-
- def insert_fixtures
- if @connection.instance_of?(ActiveRecord::ConnectionAdapters::SybaseAdapter)
- values.each do |fixture|
- @connection.enable_identity_insert(table_name, true)
- @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
- @connection.enable_identity_insert(table_name, false)
- end
- else
- original_insert_fixtures
- end
- end
-end
-
-rescue LoadError => cannot_require_sybase
- # Couldn't load sybase adapter
-end
diff --git a/vendor/rails/activerecord/lib/active_record/deprecated_associations.rb b/vendor/rails/activerecord/lib/active_record/deprecated_associations.rb
deleted file mode 100644
index 01410c4a..00000000
--- a/vendor/rails/activerecord/lib/active_record/deprecated_associations.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-module ActiveRecord
- module Associations # :nodoc:
- module ClassMethods
- def deprecated_collection_count_method(collection_name)# :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def #{collection_name}_count(force_reload = false)
- unless has_attribute?(:#{collection_name}_count)
- ActiveSupport::Deprecation.warn :#{collection_name}_count
- end
- #{collection_name}.reload if force_reload
- #{collection_name}.size
- end
- end_eval
- end
-
- def deprecated_add_association_relation(association_name)# :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def add_#{association_name}(*items)
- #{association_name}.concat(items)
- end
- deprecate :add_#{association_name} => "use #{association_name}.concat instead"
- end_eval
- end
-
- def deprecated_remove_association_relation(association_name)# :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def remove_#{association_name}(*items)
- #{association_name}.delete(items)
- end
- deprecate :remove_#{association_name} => "use #{association_name}.delete instead"
- end_eval
- end
-
- def deprecated_has_collection_method(collection_name)# :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def has_#{collection_name}?(force_reload = false)
- !#{collection_name}(force_reload).empty?
- end
- deprecate :has_#{collection_name}? => "use !#{collection_name}.empty? instead"
- end_eval
- end
-
- def deprecated_find_in_collection_method(collection_name)# :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def find_in_#{collection_name}(association_id)
- #{collection_name}.find(association_id)
- end
- deprecate :find_in_#{collection_name} => "use #{collection_name}.find instead"
- end_eval
- end
-
- def deprecated_find_all_in_collection_method(collection_name)# :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def find_all_in_#{collection_name}(runtime_conditions = nil, orderings = nil, limit = nil, joins = nil)
- ActiveSupport::Deprecation.silence do
- #{collection_name}.find_all(runtime_conditions, orderings, limit, joins)
- end
- end
- deprecate :find_all_in_#{collection_name} => "use #{collection_name}.find(:all, ...) instead"
- end_eval
- end
-
- def deprecated_collection_create_method(collection_name)# :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def create_in_#{collection_name}(attributes = {})
- #{collection_name}.create(attributes)
- end
- deprecate :create_in_#{collection_name} => "use #{collection_name}.create instead"
- end_eval
- end
-
- def deprecated_collection_build_method(collection_name)# :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def build_to_#{collection_name}(attributes = {})
- #{collection_name}.build(attributes)
- end
- deprecate :build_to_#{collection_name} => "use #{collection_name}.build instead"
- end_eval
- end
-
- def deprecated_association_comparison_method(association_name, association_class_name) # :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def #{association_name}?(comparison_object, force_reload = false)
- if comparison_object.kind_of?(#{association_class_name})
- #{association_name}(force_reload) == comparison_object
- else
- raise "Comparison object is a #{association_class_name}, should have been \#{comparison_object.class.name}"
- end
- end
- deprecate :#{association_name}? => :==
- end_eval
- end
-
- def deprecated_has_association_method(association_name) # :nodoc:
- module_eval <<-"end_eval", __FILE__, __LINE__
- def has_#{association_name}?(force_reload = false)
- !#{association_name}(force_reload).nil?
- end
- deprecate :has_#{association_name}? => "use !#{association_name} insead"
- end_eval
- end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/deprecated_finders.rb b/vendor/rails/activerecord/lib/active_record/deprecated_finders.rb
deleted file mode 100644
index d4dcaa3f..00000000
--- a/vendor/rails/activerecord/lib/active_record/deprecated_finders.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-module ActiveRecord
- class Base
- class << self
- # DEPRECATION NOTICE: This method is deprecated in favor of find with the :conditions option.
- #
- # Works like find, but the record matching +id+ must also meet the +conditions+.
- # +RecordNotFound+ is raised if no record can be found matching the +id+ or meeting the condition.
- # Example:
- # Person.find_on_conditions 5, "first_name LIKE '%dav%' AND last_name = 'heinemeier'"
- def find_on_conditions(ids, conditions) # :nodoc:
- find(ids, :conditions => conditions)
- end
- deprecate :find_on_conditions => "use find(ids, :conditions => conditions)"
-
- # DEPRECATION NOTICE: This method is deprecated in favor of find(:first, options).
- #
- # Returns the object for the first record responding to the conditions in +conditions+,
- # such as "group = 'master'". If more than one record is returned from the query, it's the first that'll
- # be used to create the object. In such cases, it might be beneficial to also specify
- # +orderings+, like "income DESC, name", to control exactly which record is to be used. Example:
- # Employee.find_first "income > 50000", "income DESC, name"
- def find_first(conditions = nil, orderings = nil, joins = nil) # :nodoc:
- find(:first, :conditions => conditions, :order => orderings, :joins => joins)
- end
- deprecate :find_first => "use find(:first, ...)"
-
- # DEPRECATION NOTICE: This method is deprecated in favor of find(:all, options).
- #
- # Returns an array of all the objects that could be instantiated from the associated
- # table in the database. The +conditions+ can be used to narrow the selection of objects (WHERE-part),
- # such as by "color = 'red'", and arrangement of the selection can be done through +orderings+ (ORDER BY-part),
- # such as by "last_name, first_name DESC". A maximum of returned objects and their offset can be specified in
- # +limit+ with either just a single integer as the limit or as an array with the first element as the limit,
- # the second as the offset. Examples:
- # Project.find_all "category = 'accounts'", "last_accessed DESC", 15
- # Project.find_all ["category = ?", category_name], "created ASC", [15, 20]
- def find_all(conditions = nil, orderings = nil, limit = nil, joins = nil) # :nodoc:
- limit, offset = limit.is_a?(Array) ? limit : [ limit, nil ]
- find(:all, :conditions => conditions, :order => orderings, :joins => joins, :limit => limit, :offset => offset)
- end
- deprecate :find_all => "use find(:all, ...)"
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/fixtures.rb b/vendor/rails/activerecord/lib/active_record/fixtures.rb
index 43d4bdae..0da8c514 100755
--- a/vendor/rails/activerecord/lib/active_record/fixtures.rb
+++ b/vendor/rails/activerecord/lib/active_record/fixtures.rb
@@ -9,10 +9,15 @@ module YAML #:nodoc:
end
end
-class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
+if defined? ActiveRecord
+ class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
+ end
+else
+ class FixtureClassNotFound < StandardError #:nodoc:
+ end
end
-# Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavours:
+# Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavors:
#
# 1. YAML fixtures
# 2. CSV fixtures
@@ -21,7 +26,7 @@ end
# = YAML fixtures
#
# This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
-# in a non-verbose, humanly-readable format. It ships with Ruby 1.8.1+.
+# in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
#
# Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed
# by Test::Unit::TestCase.fixture_path=(path) (this is automatically configured for Rails, so you can just
@@ -82,7 +87,7 @@ end
#
# = Single-file fixtures
#
-# This type of fixtures was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
+# This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
# Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
# appointed by Test::Unit::TestCase.fixture_path=(path) (this is automatically configured for Rails, so you can just
# put your files in /test/fixtures// -- like /test/fixtures/web_sites/ for the WebSite
@@ -106,7 +111,7 @@ end
# = Using Fixtures
#
# Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
-# fixtures, but first let's take a look at a sample unit test found:
+# fixtures, but first let's take a look at a sample unit test:
#
# require 'web_site'
#
@@ -124,8 +129,8 @@ end
# fixtures :web_sites # add more by separating the symbols with commas
# ...
#
-# By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here tho), we trigger
-# the testing environment to automatically load the appropriate fixtures into the database before each test.
+# By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here though), we trigger
+# the testing environment to automatically load the appropriate fixtures into the database before each test.
# To ensure consistent data, the environment deletes the fixtures before running the load.
#
# In addition to being available in the database, the fixtures are also loaded into a hash stored in an instance variable
@@ -151,7 +156,7 @@ end
# self.use_instantiated_fixtures = false
#
# - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance:
-# self.use_instantiated_fixtures = :no_instances
+# self.use_instantiated_fixtures = :no_instances
#
# Even if auto-instantiated fixtures are disabled, you can still access them
# by name via special dynamic methods. Each method has the same name as the
@@ -183,7 +188,7 @@ end
#
# = Transactional fixtures
#
-# TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
+# TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
# They can also turn off auto-instantiation of fixture data since the feature is costly and often unused.
#
# class FooTest < Test::Unit::TestCase
@@ -203,22 +208,269 @@ end
# end
# end
#
-# If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
+# If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
# then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes.
#
-# In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
+# In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
# access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
#
-# When *not* to use transactional fixtures:
-# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
-# particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
-# the results of your transaction until Active Record supports nested transactions or savepoints (in progress.)
-# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
+# When *not* to use transactional fixtures:
+# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
+# particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
+# the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
+# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
# Use InnoDB, MaxDB, or NDB instead.
+#
+# = Advanced YAML Fixtures
+#
+# YAML fixtures that don't specify an ID get some extra features:
+#
+# * Stable, autogenerated ID's
+# * Label references for associations (belongs_to, has_one, has_many)
+# * HABTM associations as inline lists
+# * Autofilled timestamp columns
+# * Fixture label interpolation
+# * Support for YAML defaults
+#
+# == Stable, autogenerated ID's
+#
+# Here, have a monkey fixture:
+#
+# george:
+# id: 1
+# name: George the Monkey
+#
+# reginald:
+# id: 2
+# name: Reginald the Pirate
+#
+# Each of these fixtures has two unique identifiers: one for the database
+# and one for the humans. Why don't we generate the primary key instead?
+# Hashing each fixture's label yields a consistent ID:
+#
+# george: # generated id: 503576764
+# name: George the Monkey
+#
+# reginald: # generated id: 324201669
+# name: Reginald the Pirate
+#
+# ActiveRecord looks at the fixture's model class, discovers the correct
+# primary key, and generates it right before inserting the fixture
+# into the database.
+#
+# The generated ID for a given label is constant, so we can discover
+# any fixture's ID without loading anything, as long as we know the label.
+#
+# == Label references for associations (belongs_to, has_one, has_many)
+#
+# Specifying foreign keys in fixtures can be very fragile, not to
+# mention difficult to read. Since ActiveRecord can figure out the ID of
+# any fixture from its label, you can specify FK's by label instead of ID.
+#
+# === belongs_to
+#
+# Let's break out some more monkeys and pirates.
+#
+# ### in pirates.yml
+#
+# reginald:
+# id: 1
+# name: Reginald the Pirate
+# monkey_id: 1
+#
+# ### in monkeys.yml
+#
+# george:
+# id: 1
+# name: George the Monkey
+# pirate_id: 1
+#
+# Add a few more monkeys and pirates and break this into multiple files,
+# and it gets pretty hard to keep track of what's going on. Let's
+# use labels instead of ID's:
+#
+# ### in pirates.yml
+#
+# reginald:
+# name: Reginald the Pirate
+# monkey: george
+#
+# ### in monkeys.yml
+#
+# george:
+# name: George the Monkey
+# pirate: reginald
+#
+# Pow! All is made clear. ActiveRecord reflects on the fixture's model class,
+# finds all the +belongs_to+ associations, and allows you to specify
+# a target *label* for the *association* (monkey: george) rather than
+# a target *id* for the *FK* (monkey_id: 1).
+#
+# ==== Polymorphic belongs_to
+#
+# Supporting polymorphic relationships is a little bit more complicated, since
+# ActiveRecord needs to know what type your association is pointing at. Something
+# like this should look familiar:
+#
+# ### in fruit.rb
+#
+# belongs_to :eater, :polymorphic => true
+#
+# ### in fruits.yml
+#
+# apple:
+# id: 1
+# name: apple
+# eater_id: 1
+# eater_type: Monkey
+#
+# Can we do better? You bet!
+#
+# apple:
+# eater: george (Monkey)
+#
+# Just provide the polymorphic target type and ActiveRecord will take care of the rest.
+#
+# === has_and_belongs_to_many
+#
+# Time to give our monkey some fruit.
+#
+# ### in monkeys.yml
+#
+# george:
+# id: 1
+# name: George the Monkey
+# pirate_id: 1
+#
+# ### in fruits.yml
+#
+# apple:
+# id: 1
+# name: apple
+#
+# orange:
+# id: 2
+# name: orange
+#
+# grape:
+# id: 3
+# name: grape
+#
+# ### in fruits_monkeys.yml
+#
+# apple_george:
+# fruit_id: 1
+# monkey_id: 1
+#
+# orange_george:
+# fruit_id: 2
+# monkey_id: 1
+#
+# grape_george:
+# fruit_id: 3
+# monkey_id: 1
+#
+# Let's make the HABTM fixture go away.
+#
+# ### in monkeys.yml
+#
+# george:
+# name: George the Monkey
+# pirate: reginald
+# fruits: apple, orange, grape
+#
+# ### in fruits.yml
+#
+# apple:
+# name: apple
+#
+# orange:
+# name: orange
+#
+# grape:
+# name: grape
+#
+# Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
+# on George's fixture, but we could've just as easily specified a list
+# of monkeys on each fruit. As with +belongs_to+, ActiveRecord reflects on
+# the fixture's model class and discovers the +has_and_belongs_to_many+
+# associations.
+#
+# == Autofilled timestamp columns
+#
+# If your table/model specifies any of ActiveRecord's
+# standard timestamp columns (created_at, created_on, updated_at, updated_on),
+# they will automatically be set to Time.now.
+#
+# If you've set specific values, they'll be left alone.
+#
+# == Fixture label interpolation
+#
+# The label of the current fixture is always available as a column value:
+#
+# geeksomnia:
+# name: Geeksomnia's Account
+# subdomain: $LABEL
+#
+# Also, sometimes (like when porting older join table fixtures) you'll need
+# to be able to get ahold of the identifier for a given label. ERB
+# to the rescue:
+#
+# george_reginald:
+# monkey_id: <%= Fixtures.identify(:reginald) %>
+# pirate_id: <%= Fixtures.identify(:george) %>
+#
+# == Support for YAML defaults
+#
+# You probably already know how to use YAML to set and reuse defaults in
+# your +database.yml+ file,. You can use the same technique in your fixtures:
+#
+# DEFAULTS: &DEFAULTS
+# created_on: <%= 3.weeks.ago.to_s(:db) %>
+#
+# first:
+# name: Smurf
+# <<: *DEFAULTS
+#
+# second:
+# name: Fraggle
+# <<: *DEFAULTS
+#
+# Any fixture labeled "DEFAULTS" is safely ignored.
+
class Fixtures < YAML::Omap
DEFAULT_FILTER_RE = /\.ya?ml$/
- def self.instantiate_fixtures(object, table_name, fixtures, load_instances=true)
+ @@all_cached_fixtures = {}
+
+ def self.reset_cache(connection = nil)
+ connection ||= ActiveRecord::Base.connection
+ @@all_cached_fixtures[connection.object_id] = {}
+ end
+
+ def self.cache_for_connection(connection)
+ @@all_cached_fixtures[connection.object_id] ||= {}
+ @@all_cached_fixtures[connection.object_id]
+ end
+
+ def self.fixture_is_cached?(connection, table_name)
+ cache_for_connection(connection)[table_name]
+ end
+
+ def self.cached_fixtures(connection, keys_to_fetch = nil)
+ if keys_to_fetch
+ fixtures = cache_for_connection(connection).values_at(*keys_to_fetch)
+ else
+ fixtures = cache_for_connection(connection).values
+ end
+ fixtures.size > 1 ? fixtures : fixtures.first
+ end
+
+ def self.cache_fixtures(connection, fixtures)
+ cache_for_connection(connection).update(fixtures.index_by(&:table_name))
+ end
+
+ def self.instantiate_fixtures(object, table_name, fixtures, load_instances = true)
object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures
if load_instances
ActiveRecord::Base.silence do
@@ -233,47 +485,63 @@ class Fixtures < YAML::Omap
end
end
- def self.instantiate_all_loaded_fixtures(object, load_instances=true)
+ def self.instantiate_all_loaded_fixtures(object, load_instances = true)
all_loaded_fixtures.each do |table_name, fixtures|
Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
end
end
-
+
cattr_accessor :all_loaded_fixtures
self.all_loaded_fixtures = {}
def self.create_fixtures(fixtures_directory, table_names, class_names = {})
table_names = [table_names].flatten.map { |n| n.to_s }
- connection = block_given? ? yield : ActiveRecord::Base.connection
- ActiveRecord::Base.silence do
- fixtures_map = {}
- fixtures = table_names.map do |table_name|
- fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s))
- end
- all_loaded_fixtures.merge! fixtures_map
+ connection = block_given? ? yield : ActiveRecord::Base.connection
- connection.transaction(Thread.current['open_transactions'] == 0) do
- fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
- fixtures.each { |fixture| fixture.insert_fixtures }
+ table_names_to_fetch = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) }
- # Cap primary key sequences to max(pk).
- if connection.respond_to?(:reset_pk_sequence!)
- table_names.each do |table_name|
- connection.reset_pk_sequence!(table_name)
+ unless table_names_to_fetch.empty?
+ ActiveRecord::Base.silence do
+ connection.disable_referential_integrity do
+ fixtures_map = {}
+
+ fixtures = table_names_to_fetch.map do |table_name|
+ fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s))
end
+
+ all_loaded_fixtures.update(fixtures_map)
+
+ connection.transaction(Thread.current['open_transactions'].to_i == 0) do
+ fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
+ fixtures.each { |fixture| fixture.insert_fixtures }
+
+ # Cap primary key sequences to max(pk).
+ if connection.respond_to?(:reset_pk_sequence!)
+ table_names.each do |table_name|
+ connection.reset_pk_sequence!(table_name)
+ end
+ end
+ end
+
+ cache_fixtures(connection, fixtures)
end
end
-
- return fixtures.size > 1 ? fixtures : fixtures.first
end
+ cached_fixtures(connection, table_names)
end
+ # Returns a consistent identifier for +label+. This will always
+ # be a positive integer, and will always be the same for a given
+ # label, assuming the same OS, platform, and version of Ruby.
+ def self.identify(label)
+ label.to_s.hash.abs
+ end
attr_reader :table_name
def initialize(connection, table_name, class_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
@connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
- @class_name = class_name ||
+ @class_name = class_name ||
(ActiveRecord::Base.pluralize_table_names ? @table_name.singularize.camelize : @table_name.camelize)
@table_name = ActiveRecord::Base.table_name_prefix + @table_name + ActiveRecord::Base.table_name_suffix
@table_name = class_name.table_name if class_name.respond_to?(:table_name)
@@ -282,63 +550,132 @@ class Fixtures < YAML::Omap
end
def delete_existing_fixtures
- @connection.delete "DELETE FROM #{@table_name}", 'Fixture Delete'
+ @connection.delete "DELETE FROM #{@connection.quote_table_name(table_name)}", 'Fixture Delete'
end
def insert_fixtures
- values.each do |fixture|
- @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
+ now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
+ now = now.to_s(:db)
+
+ # allow a standard key to be used for doing defaults in YAML
+ delete(assoc("DEFAULTS"))
+
+ # track any join tables we need to insert later
+ habtm_fixtures = Hash.new do |h, habtm|
+ h[habtm] = HabtmFixtures.new(@connection, habtm.options[:join_table], nil, nil)
+ end
+
+ each do |label, fixture|
+ row = fixture.to_hash
+
+ if model_class && model_class < ActiveRecord::Base
+ # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
+ if model_class.record_timestamps
+ timestamp_column_names.each do |name|
+ row[name] = now unless row.key?(name)
+ end
+ end
+
+ # interpolate the fixture label
+ row.each do |key, value|
+ row[key] = label if value == "$LABEL"
+ end
+
+ # generate a primary key if necessary
+ if has_primary_key_column? && !row.include?(primary_key_name)
+ row[primary_key_name] = Fixtures.identify(label)
+ end
+
+ # If STI is used, find the correct subclass for association reflection
+ reflection_class =
+ if row.include?(inheritance_column_name)
+ row[inheritance_column_name].constantize rescue model_class
+ else
+ model_class
+ end
+
+ reflection_class.reflect_on_all_associations.each do |association|
+ case association.macro
+ when :belongs_to
+ # Do not replace association name with association foreign key if they are named the same
+ fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
+
+ if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
+ if association.options[:polymorphic]
+ if value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
+ target_type = $1
+ target_type_name = (association.options[:foreign_type] || "#{association.name}_type").to_s
+
+ # support polymorphic belongs_to as "label (Type)"
+ row[target_type_name] = target_type
+ end
+ end
+
+ row[fk_name] = Fixtures.identify(value)
+ end
+ when :has_and_belongs_to_many
+ if (targets = row.delete(association.name.to_s))
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
+ join_fixtures = habtm_fixtures[association]
+
+ targets.each do |target|
+ join_fixtures["#{label}_#{target}"] = Fixture.new(
+ { association.primary_key_name => row[primary_key_name],
+ association.association_foreign_key => Fixtures.identify(target) }, nil)
+ end
+ end
+ end
+ end
+ end
+
+ @connection.insert_fixture(fixture, @table_name)
+ end
+
+ # insert any HABTM join tables we discovered
+ habtm_fixtures.values.each do |fixture|
+ fixture.delete_existing_fixtures
+ fixture.insert_fixtures
end
end
private
+ class HabtmFixtures < ::Fixtures #:nodoc:
+ def read_fixture_files; end
+ end
+
+ def model_class
+ @model_class ||= @class_name.is_a?(Class) ?
+ @class_name : @class_name.constantize rescue nil
+ end
+
+ def primary_key_name
+ @primary_key_name ||= model_class && model_class.primary_key
+ end
+
+ def has_primary_key_column?
+ @has_primary_key_column ||= model_class && primary_key_name &&
+ model_class.columns.find { |c| c.name == primary_key_name }
+ end
+
+ def timestamp_column_names
+ @timestamp_column_names ||= %w(created_at created_on updated_at updated_on).select do |name|
+ column_names.include?(name)
+ end
+ end
+
+ def inheritance_column_name
+ @inheritance_column_name ||= model_class && model_class.inheritance_column
+ end
+
+ def column_names
+ @column_names ||= @connection.columns(@table_name).collect(&:name)
+ end
def read_fixture_files
if File.file?(yaml_file_path)
- # YAML fixtures
- yaml_string = ""
- Dir["#{@fixture_path}/**/*.yml"].select {|f| test(?f,f) }.each do |subfixture_path|
- yaml_string << IO.read(subfixture_path)
- end
- yaml_string << IO.read(yaml_file_path)
-
- begin
- yaml = YAML::load(erb_render(yaml_string))
- rescue Exception=>boom
- raise Fixture::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{boom.class}: #{boom}"
- end
-
- if yaml
- # If the file is an ordered map, extract its children.
- yaml_value =
- if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
- yaml.value
- else
- [yaml]
- end
-
- yaml_value.each do |fixture|
- fixture.each do |name, data|
- unless data
- raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
- end
-
- self[name] = Fixture.new(data, @class_name)
- end
- end
- end
+ read_yaml_fixture_files
elsif File.file?(csv_file_path)
- # CSV fixtures
- reader = CSV::Reader.create(erb_render(IO.read(csv_file_path)))
- header = reader.shift
- i = 0
- reader.each do |row|
- data = {}
- row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
- self["#{Inflector::underscore(@class_name)}_#{i+=1}"]= Fixture.new(data, @class_name)
- end
- elsif File.file?(deprecated_yaml_file_path)
- raise Fixture::FormatError, ".yml extension required: rename #{deprecated_yaml_file_path} to #{yaml_file_path}"
+ read_csv_fixture_files
else
# Standard fixtures
Dir.entries(@fixture_path).each do |file|
@@ -350,12 +687,47 @@ class Fixtures < YAML::Omap
end
end
- def yaml_file_path
- "#{@fixture_path}.yml"
+ def read_yaml_fixture_files
+ yaml_string = ""
+ Dir["#{@fixture_path}/**/*.yml"].select { |f| test(?f, f) }.each do |subfixture_path|
+ yaml_string << IO.read(subfixture_path)
+ end
+ yaml_string << IO.read(yaml_file_path)
+
+ if yaml = parse_yaml_string(yaml_string)
+ # If the file is an ordered map, extract its children.
+ yaml_value =
+ if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
+ yaml.value
+ else
+ [yaml]
+ end
+
+ yaml_value.each do |fixture|
+ fixture.each do |name, data|
+ unless data
+ raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
+ end
+
+ self[name] = Fixture.new(data, @class_name)
+ end
+ end
+ end
end
- def deprecated_yaml_file_path
- "#{@fixture_path}.yaml"
+ def read_csv_fixture_files
+ reader = CSV::Reader.create(erb_render(IO.read(csv_file_path)))
+ header = reader.shift
+ i = 0
+ reader.each do |row|
+ data = {}
+ row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
+ self["#{Inflector::underscore(@class_name)}_#{i+=1}"]= Fixture.new(data, @class_name)
+ end
+ end
+
+ def yaml_file_path
+ "#{@fixture_path}.yml"
end
def csv_file_path
@@ -366,6 +738,12 @@ class Fixtures < YAML::Omap
File.basename(@fixture_path).split(".").first
end
+ def parse_yaml_string(fixture_content)
+ YAML::load(erb_render(fixture_content))
+ rescue => error
+ raise Fixture::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}"
+ end
+
def erb_render(fixture_content)
ERB.new(fixture_content).result
end
@@ -373,11 +751,15 @@ end
class Fixture #:nodoc:
include Enumerable
- class FixtureError < StandardError#:nodoc:
+
+ class FixtureError < StandardError #:nodoc:
end
- class FormatError < FixtureError#:nodoc:
+
+ class FormatError < FixtureError #:nodoc:
end
+ attr_reader :class_name
+
def initialize(fixture, class_name)
case fixture
when Hash, YAML::Omap
@@ -450,36 +832,40 @@ end
module Test #:nodoc:
module Unit #:nodoc:
class TestCase #:nodoc:
- cattr_accessor :fixture_path
- class_inheritable_accessor :fixture_table_names
- class_inheritable_accessor :fixture_class_names
- class_inheritable_accessor :use_transactional_fixtures
- class_inheritable_accessor :use_instantiated_fixtures # true, false, or :no_instances
- class_inheritable_accessor :pre_loaded_fixtures
+ superclass_delegating_accessor :fixture_path
+ superclass_delegating_accessor :fixture_table_names
+ superclass_delegating_accessor :fixture_class_names
+ superclass_delegating_accessor :use_transactional_fixtures
+ superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances
+ superclass_delegating_accessor :pre_loaded_fixtures
self.fixture_table_names = []
self.use_transactional_fixtures = false
self.use_instantiated_fixtures = true
self.pre_loaded_fixtures = false
-
- self.fixture_class_names = {}
-
+
@@already_loaded_fixtures = {}
self.fixture_class_names = {}
-
+
def self.set_fixture_class(class_names = {})
self.fixture_class_names = self.fixture_class_names.merge(class_names)
end
-
+
def self.fixtures(*table_names)
- table_names = table_names.flatten.map { |n| n.to_s }
+ if table_names.first == :all
+ table_names = Dir["#{fixture_path}/*.yml"] + Dir["#{fixture_path}/*.csv"]
+ table_names.map! { |f| File.basename(f).split('.')[0..-2].join('.') }
+ else
+ table_names = table_names.flatten.map { |n| n.to_s }
+ end
+
self.fixture_table_names |= table_names
require_fixture_classes(table_names)
setup_fixture_accessors(table_names)
end
- def self.require_fixture_classes(table_names=nil)
- (table_names || fixture_table_names).each do |table_name|
+ def self.require_fixture_classes(table_names = nil)
+ (table_names || fixture_table_names).each do |table_name|
file_name = table_name.to_s
file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
begin
@@ -490,18 +876,26 @@ module Test #:nodoc:
end
end
- def self.setup_fixture_accessors(table_names=nil)
+ def self.setup_fixture_accessors(table_names = nil)
(table_names || fixture_table_names).each do |table_name|
- table_name = table_name.to_s.tr('.','_')
- define_method(table_name) do |fixture, *optionals|
- force_reload = optionals.shift
- @fixture_cache[table_name] ||= Hash.new
- @fixture_cache[table_name][fixture] = nil if force_reload
- if @loaded_fixtures[table_name][fixture.to_s]
- @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find
- else
- raise StandardError, "No fixture with name '#{fixture}' found for table '#{table_name}'"
+ table_name = table_name.to_s.tr('.', '_')
+
+ define_method(table_name) do |*fixtures|
+ force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
+
+ @fixture_cache[table_name] ||= {}
+
+ instances = fixtures.map do |fixture|
+ @fixture_cache[table_name].delete(fixture) if force_reload
+
+ if @loaded_fixtures[table_name][fixture.to_s]
+ @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find
+ else
+ raise StandardError, "No fixture with name '#{fixture}' found for table '#{table_name}'"
+ end
end
+
+ instances.size == 1 ? instances.first : instances
end
end
end
@@ -522,13 +916,15 @@ module Test #:nodoc:
end
def setup_with_fixtures
+ return if @fixtures_setup
+ @fixtures_setup = true
return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?
if pre_loaded_fixtures && !use_transactional_fixtures
- raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
+ raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
end
- @fixture_cache = Hash.new
+ @fixture_cache = {}
# Load fixtures once and begin transaction.
if use_transactional_fixtures?
@@ -540,9 +936,9 @@ module Test #:nodoc:
end
ActiveRecord::Base.send :increment_open_transactions
ActiveRecord::Base.connection.begin_db_transaction
-
# Load fixtures for every test.
else
+ Fixtures.reset_cache
@@already_loaded_fixtures[self.class] = nil
load_fixtures
end
@@ -550,12 +946,17 @@ module Test #:nodoc:
# Instantiate fixtures for every test if requested.
instantiate_fixtures if use_instantiated_fixtures
end
-
alias_method :setup, :setup_with_fixtures
def teardown_with_fixtures
+ return if @fixtures_teardown
+ @fixtures_teardown = true
return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?
+ unless use_transactional_fixtures?
+ Fixtures.reset_cache
+ end
+
# Rollback changes if a transaction is active.
if use_transactional_fixtures? && Thread.current['open_transactions'] != 0
ActiveRecord::Base.connection.rollback_db_transaction
@@ -563,28 +964,34 @@ module Test #:nodoc:
end
ActiveRecord::Base.verify_active_connections!
end
-
alias_method :teardown, :teardown_with_fixtures
def self.method_added(method)
+ return if @__disable_method_added__
+ @__disable_method_added__ = true
+
case method.to_s
when 'setup'
unless method_defined?(:setup_without_fixtures)
alias_method :setup_without_fixtures, :setup
- define_method(:setup) do
+ define_method(:full_setup) do
setup_with_fixtures
setup_without_fixtures
end
end
+ alias_method :setup, :full_setup
when 'teardown'
unless method_defined?(:teardown_without_fixtures)
alias_method :teardown_without_fixtures, :teardown
- define_method(:teardown) do
+ define_method(:full_teardown) do
teardown_without_fixtures
teardown_with_fixtures
end
end
+ alias_method :teardown, :full_teardown
end
+
+ @__disable_method_added__ = false
end
private
@@ -623,6 +1030,5 @@ module Test #:nodoc:
use_instantiated_fixtures != :no_instances
end
end
-
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/locking.rb b/vendor/rails/activerecord/lib/active_record/locking.rb
deleted file mode 100644
index ca83a98b..00000000
--- a/vendor/rails/activerecord/lib/active_record/locking.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-module ActiveRecord
- # Active Records support optimistic locking if the field lock_version is present. Each update to the
- # record increments the lock_version column and the locking facilities ensure that records instantiated twice
- # will let the last one saved raise a StaleObjectError if the first was also updated. Example:
- #
- # p1 = Person.find(1)
- # p2 = Person.find(1)
- #
- # p1.first_name = "Michael"
- # p1.save
- #
- # p2.first_name = "should fail"
- # p2.save # Raises a ActiveRecord::StaleObjectError
- #
- # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
- # or otherwise apply the business logic needed to resolve the conflict.
- #
- # You must ensure that your database schema defaults the lock_version column to 0.
- #
- # This behavior can be turned off by setting ActiveRecord::Base.lock_optimistically = false .
- # To override the name of the lock_version column, invoke the set_locking_column method.
- # This method uses the same syntax as set_table_name
- module Locking
- def self.append_features(base) #:nodoc:
- super
- base.class_eval do
- alias_method :update_without_lock, :update
- alias_method :update, :update_with_lock
- end
- end
-
- def update_with_lock #:nodoc:
- return update_without_lock unless locking_enabled?
-
- lock_col = self.class.locking_column
- previous_value = send(lock_col)
- send(lock_col + '=', previous_value + 1)
-
- affected_rows = connection.update(<<-end_sql, "#{self.class.name} Update with optimistic locking")
- UPDATE #{self.class.table_name}
- SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))}
- WHERE #{self.class.primary_key} = #{quote(id)}
- AND #{lock_col} = #{quote(previous_value)}
- end_sql
-
- unless affected_rows == 1
- raise ActiveRecord::StaleObjectError, "Attempted to update a stale object"
- end
-
- return true
- end
- end
-
- class Base
- @@lock_optimistically = true
- cattr_accessor :lock_optimistically
-
- def locking_enabled? #:nodoc:
- lock_optimistically && respond_to?(self.class.locking_column)
- end
-
- class << self
- def set_locking_column(value = nil, &block)
- define_attr_method :locking_column, value, &block
- end
-
- def locking_column #:nodoc:
- reset_locking_column
- end
-
- def reset_locking_column #:nodoc:
- default = 'lock_version'
- set_locking_column(default)
- default
- end
- end
-
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/locking/optimistic.rb b/vendor/rails/activerecord/lib/active_record/locking/optimistic.rb
index 02cf5650..799309c1 100644
--- a/vendor/rails/activerecord/lib/active_record/locking/optimistic.rb
+++ b/vendor/rails/activerecord/lib/active_record/locking/optimistic.rb
@@ -1,15 +1,25 @@
module ActiveRecord
module Locking
+ # == What is Optimistic Locking
+ #
+ # Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
+ # conflicts with the data. It does this by checking whether another process has made changes to a record since
+ # it was opened, an ActiveRecord::StaleObjectError is thrown if that has occurred and the update is ignored.
+ #
+ # Check out ActiveRecord::Locking::Pessimistic for an alternative.
+ #
+ # == Usage
+ #
# Active Records support optimistic locking if the field lock_version is present. Each update to the
# record increments the lock_version column and the locking facilities ensure that records instantiated twice
# will let the last one saved raise a StaleObjectError if the first was also updated. Example:
#
# p1 = Person.find(1)
# p2 = Person.find(1)
- #
+ #
# p1.first_name = "Michael"
# p1.save
- #
+ #
# p2.first_name = "should fail"
# p2.save # Raises a ActiveRecord::StaleObjectError
#
@@ -23,7 +33,6 @@ module ActiveRecord
# This method uses the same syntax as set_table_name
module Optimistic
def self.included(base) #:nodoc:
- super
base.extend ClassMethods
base.cattr_accessor :lock_optimistically, :instance_writer => false
@@ -31,55 +40,77 @@ module ActiveRecord
base.alias_method_chain :update, :lock
base.alias_method_chain :attributes_from_column_definition, :lock
-
+
class << base
alias_method :locking_column=, :set_locking_column
end
end
def locking_enabled? #:nodoc:
- lock_optimistically && respond_to?(self.class.locking_column)
+ self.class.locking_enabled?
end
- def attributes_from_column_definition_with_lock
- result = attributes_from_column_definition_without_lock
-
- # If the locking column has no default value set,
- # start the lock version at zero. Note we can't use
- # locking_enabled? at this point as @attributes may
- # not have been initialized yet
-
- if lock_optimistically && result.include?(self.class.locking_column)
- result[self.class.locking_column] ||= 0
- end
-
- return result
- end
+ private
+ def attributes_from_column_definition_with_lock
+ result = attributes_from_column_definition_without_lock
- def update_with_lock #:nodoc:
- return update_without_lock unless locking_enabled?
+ # If the locking column has no default value set,
+ # start the lock version at zero. Note we can't use
+ # locking_enabled? at this point as @attributes may
+ # not have been initialized yet
- lock_col = self.class.locking_column
- previous_value = send(lock_col)
- send(lock_col + '=', previous_value + 1)
+ if lock_optimistically && result.include?(self.class.locking_column)
+ result[self.class.locking_column] ||= 0
+ end
- affected_rows = connection.update(<<-end_sql, "#{self.class.name} Update with optimistic locking")
- UPDATE #{self.class.table_name}
- SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))}
- WHERE #{self.class.primary_key} = #{quote_value(id)}
- AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}
- end_sql
-
- unless affected_rows == 1
- raise ActiveRecord::StaleObjectError, "Attempted to update a stale object"
+ return result
end
- return true
- end
+ def update_with_lock #:nodoc:
+ return update_without_lock unless locking_enabled?
+
+ lock_col = self.class.locking_column
+ previous_value = send(lock_col).to_i
+ send(lock_col + '=', previous_value + 1)
+
+ begin
+ affected_rows = connection.update(<<-end_sql, "#{self.class.name} Update with optimistic locking")
+ UPDATE #{self.class.table_name}
+ SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false, false))}
+ WHERE #{self.class.primary_key} = #{quote_value(id)}
+ AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}
+ end_sql
+
+ unless affected_rows == 1
+ raise ActiveRecord::StaleObjectError, "Attempted to update a stale object"
+ end
+
+ affected_rows
+
+ # If something went wrong, revert the version.
+ rescue Exception
+ send(lock_col + '=', previous_value)
+ raise
+ end
+ end
module ClassMethods
DEFAULT_LOCKING_COLUMN = 'lock_version'
+ def self.extended(base)
+ class < 1) if locking_enabled?
+ update_counters_without_lock(id, counters)
+ end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/migration.rb b/vendor/rails/activerecord/lib/active_record/migration.rb
index b132b3d8..cbdef1dd 100644
--- a/vendor/rails/activerecord/lib/active_record/migration.rb
+++ b/vendor/rails/activerecord/lib/active_record/migration.rb
@@ -1,13 +1,19 @@
module ActiveRecord
class IrreversibleMigration < ActiveRecordError#:nodoc:
end
-
+
class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
def initialize(version)
super("Multiple migrations have the version number #{version}")
end
end
-
+
+ class IllegalMigrationNameError < ActiveRecordError#:nodoc:
+ def initialize(name)
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
+ end
+ end
+
# Migrations can manage the evolution of a schema used by several physical databases. It's a solution
# to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to
# push that change to other developers and to the production server. With migrations, you can describe the transformations
@@ -26,9 +32,9 @@ module ActiveRecord
# end
# end
#
- # This migration will add a boolean flag to the accounts table and remove it again, if you're backing out of the migration.
+ # This migration will add a boolean flag to the accounts table and remove it if you're backing out of the migration.
# It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement
- # or remove the migration. These methods can consist of both the migration specific methods, like add_column and remove_column,
+ # or remove the migration. These methods can consist of both the migration specific methods like add_column and remove_column,
# but may also contain regular Ruby code for generating data needed for the transformations.
#
# Example of a more complex migration that also needs to initialize data:
@@ -36,11 +42,11 @@ module ActiveRecord
# class AddSystemSettings < ActiveRecord::Migration
# def self.up
# create_table :system_settings do |t|
- # t.column :name, :string
- # t.column :label, :string
- # t.column :value, :text
- # t.column :type, :string
- # t.column :position, :integer
+ # t.string :name
+ # t.string :label
+ # t.text :value
+ # t.string :type
+ # t.integer :position
# end
#
# SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
@@ -72,13 +78,14 @@ module ActiveRecord
# * change_column(table_name, column_name, type, options) : Changes the column to a different type using the same
# parameters as add_column.
# * remove_column(table_name, column_name) : Removes the column named +column_name+ from the table called +table_name+.
- # * add_index(table_name, column_names, index_type, index_name) : Add a new index with the name of the column, or +index_name+ (if specified) on the column(s). Specify an optional +index_type+ (e.g. UNIQUE).
- # * remove_index(table_name, index_name) : Remove the index specified by +index_name+.
+ # * add_index(table_name, column_names, options) : Adds a new index with the name of the column. Other options include
+ # :name and :unique (e.g. { :name => "users_name_index", :unique => true }).
+ # * remove_index(table_name, index_name) : Removes the index specified by +index_name+.
#
# == Irreversible transformations
#
# Some transformations are destructive in a manner that cannot be reversed. Migrations of that kind should raise
- # an IrreversibleMigration exception in their +down+ method.
+ # an ActiveRecord::IrreversibleMigration exception in their +down+ method.
#
# == Running migrations from within Rails
#
@@ -87,18 +94,18 @@ module ActiveRecord
# To generate a new migration, use script/generate migration MyNewMigration
# where MyNewMigration is the name of your migration. The generator will
# create a file nnn_my_new_migration.rb in the db/migrate/
- # directory, where nnn is the next largest migration number.
+ # directory where nnn is the next largest migration number.
# You may then edit the self.up and self.down methods of
- # n MyNewMigration.
+ # MyNewMigration.
#
# To run migrations against the currently configured database, use
- # rake migrate . This will update the database by running all of the
+ # rake db:migrate . This will update the database by running all of the
# pending migrations, creating the schema_info table if missing.
#
# To roll the database back to a previous migration version, use
- # rake migrate VERSION=X where X is the version to which
+ # rake db:migrate VERSION=X where X is the version to which
# you wish to downgrade. If any of the migrations throw an
- # IrreversibleMigration exception, that step will fail and you'll
+ # ActiveRecord::IrreversibleMigration exception, that step will fail and you'll
# have some manual work to do.
#
# == Database support
@@ -117,7 +124,7 @@ module ActiveRecord
#
# def self.down
# # not much we can do to restore deleted data
- # raise IrreversibleMigration
+ # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
# end
# end
#
@@ -191,11 +198,11 @@ module ActiveRecord
cattr_accessor :verbose
class << self
- def up_using_benchmarks #:nodoc:
+ def up_with_benchmarks #:nodoc:
migrate(:up)
end
- def down_using_benchmarks #:nodoc:
+ def down_with_benchmarks #:nodoc:
migrate(:down)
end
@@ -207,15 +214,15 @@ module ActiveRecord
when :up then announce "migrating"
when :down then announce "reverting"
end
-
+
result = nil
- time = Benchmark.measure { result = send("real_#{direction}") }
+ time = Benchmark.measure { result = send("#{direction}_without_benchmarks") }
case direction
when :up then announce "migrated (%.4fs)" % time.real; write
when :down then announce "reverted (%.4fs)" % time.real; write
end
-
+
result
end
@@ -224,15 +231,14 @@ module ActiveRecord
# it is safe for the call to proceed.
def singleton_method_added(sym) #:nodoc:
return if @ignore_new_methods
-
+
begin
@ignore_new_methods = true
case sym
when :up, :down
klass = (class << self; self; end)
- klass.send(:alias_method, "real_#{sym}", sym)
- klass.send(:alias_method, sym, "#{sym}_using_benchmarks")
+ klass.send(:alias_method_chain, sym, "benchmarks")
end
ensure
@ignore_new_methods = false
@@ -244,7 +250,7 @@ module ActiveRecord
end
def announce(message)
- text = "#{name}: #{message}"
+ text = "#{@version} #{name}: #{message}"
length = [0, 75 - text.length].max
write "== %s %s" % [text, "=" * length]
end
@@ -258,20 +264,24 @@ module ActiveRecord
result = nil
time = Benchmark.measure { result = yield }
say "%.4fs" % time.real, :subitem
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
result
end
def suppress_messages
- save = verbose
- self.verbose = false
+ save, self.verbose = verbose, false
yield
ensure
self.verbose = save
end
def method_missing(method, *arguments, &block)
- say_with_time "#{method}(#{arguments.map { |a| a.inspect }.join(", ")})" do
- arguments[0] = Migrator.proper_table_name(arguments.first) unless arguments.empty? || method == :execute
+ arg_list = arguments.map(&:inspect) * ', '
+
+ say_with_time "#{method}(#{arg_list})" do
+ unless arguments.empty? || method == :execute
+ arguments[0] = Migrator.proper_table_name(arguments.first)
+ end
ActiveRecord::Base.connection.send(method, *arguments, &block)
end
end
@@ -292,30 +302,29 @@ module ActiveRecord
return # You're on the right version
end
end
-
+
def up(migrations_path, target_version = nil)
self.new(:up, migrations_path, target_version).migrate
end
-
+
def down(migrations_path, target_version = nil)
self.new(:down, migrations_path, target_version).migrate
end
-
+
def schema_info_table_name
Base.table_name_prefix + "schema_info" + Base.table_name_suffix
end
def current_version
- (Base.connection.select_one("SELECT version FROM #{schema_info_table_name}") || {"version" => 0})["version"].to_i
+ Base.connection.select_value("SELECT version FROM #{schema_info_table_name}").to_i
end
def proper_table_name(name)
# Use the ActiveRecord objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
end
-
end
-
+
def initialize(direction, migrations_path, target_version = nil)
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
@direction, @migrations_path, @target_version = direction, migrations_path, target_version
@@ -327,66 +336,80 @@ module ActiveRecord
end
def migrate
- migration_classes.each do |(version, migration_class)|
- Base.logger.info("Reached target version: #{@target_version}") and break if reached_target_version?(version)
- next if irrelevant_migration?(version)
+ migration_classes.each do |migration_class|
+ if reached_target_version?(migration_class.version)
+ Base.logger.info("Reached target version: #{@target_version}")
+ break
+ end
- Base.logger.info "Migrating to #{migration_class} (#{version})"
+ next if irrelevant_migration?(migration_class.version)
+
+ Base.logger.info "Migrating to #{migration_class} (#{migration_class.version})"
migration_class.migrate(@direction)
- set_schema_version(version)
+ set_schema_version(migration_class.version)
end
end
+ def pending_migrations
+ migration_classes.select { |m| m.version > current_version }
+ end
+
private
def migration_classes
migrations = migration_files.inject([]) do |migrations, migration_file|
load(migration_file)
version, name = migration_version_and_name(migration_file)
assert_unique_migration_version(migrations, version.to_i)
- migrations << [ version.to_i, migration_class(name) ]
+ migrations << migration_class(name, version.to_i)
end
- down? ? migrations.sort.reverse : migrations.sort
+ sorted = migrations.sort_by { |m| m.version }
+ down? ? sorted.reverse : sorted
end
-
+
def assert_unique_migration_version(migrations, version)
- if !migrations.empty? && migrations.transpose.first.include?(version)
+ if !migrations.empty? && migrations.find { |m| m.version == version }
raise DuplicateMigrationVersionError.new(version)
end
end
-
+
def migration_files
files = Dir["#{@migrations_path}/[0-9]*_*.rb"].sort_by do |f|
- migration_version_and_name(f).first.to_i
+ m = migration_version_and_name(f)
+ raise IllegalMigrationNameError.new(f) unless m
+ m.first.to_i
end
down? ? files.reverse : files
end
-
- def migration_class(migration_name)
- migration_name.camelize.constantize
+
+ def migration_class(migration_name, version)
+ klass = migration_name.camelize.constantize
+ class << klass; attr_accessor :version end
+ klass.version = version
+ klass
end
-
+
def migration_version_and_name(migration_file)
return *migration_file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
end
-
+
def set_schema_version(version)
Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{down? ? version.to_i - 1 : version.to_i}")
end
-
+
def up?
@direction == :up
end
-
+
def down?
@direction == :down
end
-
+
def reached_target_version?(version)
return false if @target_version == nil
(up? && version.to_i - 1 >= @target_version) || (down? && version.to_i <= @target_version)
end
-
+
def irrelevant_migration?(version)
(up? && version.to_i <= current_version) || (down? && version.to_i > current_version)
end
diff --git a/vendor/rails/activerecord/lib/active_record/observer.rb b/vendor/rails/activerecord/lib/active_record/observer.rb
index ace52ef4..745ab96f 100644
--- a/vendor/rails/activerecord/lib/active_record/observer.rb
+++ b/vendor/rails/activerecord/lib/active_record/observer.rb
@@ -84,10 +84,11 @@ module ActiveRecord
#
# Observers will by default be mapped to the class with which they share a name. So CommentObserver will
# be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
- # differently than the class you're interested in observing, you can use the Observer.observe class method:
+ # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
+ # either the concrete class (Product) or a symbol for that class (:product):
#
# class AuditObserver < ActiveRecord::Observer
- # observe Account
+ # observe :account
#
# def after_update(account)
# AuditTrail.new(account, "UPDATED")
@@ -97,7 +98,7 @@ module ActiveRecord
# If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
#
# class AuditObserver < ActiveRecord::Observer
- # observe Account, Balance
+ # observe :account, :balance
#
# def after_update(record)
# AuditTrail.new(record, "UPDATED")
@@ -127,20 +128,22 @@ module ActiveRecord
class Observer
include Singleton
- # Observer subclasses should be reloaded by the dispatcher in Rails
- # when Dependencies.mechanism = :load.
- include Reloadable::Deprecated
-
class << self
# Attaches the observer to the supplied model classes.
def observe(*models)
+ models.flatten!
+ models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
define_method(:observed_classes) { Set.new(models) }
end
# The class observed by default is inferred from the observer's class name:
# assert_equal [Person], PersonObserver.observed_class
def observed_class
- name.scan(/(.*)Observer/)[0][0].constantize
+ if observed_class_name = name.scan(/(.*)Observer/)[0]
+ observed_class_name[0].constantize
+ else
+ nil
+ end
end
end
@@ -163,11 +166,11 @@ module ActiveRecord
protected
def observed_classes
- Set.new([self.class.observed_class].flatten)
+ Set.new([self.class.observed_class].compact.flatten)
end
def observed_subclasses
- observed_classes.sum(&:subclasses)
+ observed_classes.collect(&:subclasses).flatten
end
def add_observer!(klass)
diff --git a/vendor/rails/activerecord/lib/active_record/query_cache.rb b/vendor/rails/activerecord/lib/active_record/query_cache.rb
index e79b3e05..a8af89fc 100644
--- a/vendor/rails/activerecord/lib/active_record/query_cache.rb
+++ b/vendor/rails/activerecord/lib/active_record/query_cache.rb
@@ -1,64 +1,21 @@
module ActiveRecord
- class QueryCache #:nodoc:
- def initialize(connection)
- @connection = connection
- @query_cache = {}
- end
-
- def clear_query_cache
- @query_cache = {}
- end
-
- def select_all(sql, name = nil)
- (@query_cache[sql] ||= @connection.select_all(sql, name)).dup
- end
-
- def select_one(sql, name = nil)
- @query_cache[sql] ||= @connection.select_one(sql, name)
- end
-
- def columns(table_name, name = nil)
- @query_cache["SHOW FIELDS FROM #{table_name}"] ||= @connection.columns(table_name, name)
- end
-
- def insert(sql, name = nil, pk = nil, id_value = nil)
- clear_query_cache
- @connection.insert(sql, name, pk, id_value)
- end
-
- def update(sql, name = nil)
- clear_query_cache
- @connection.update(sql, name)
- end
-
- def delete(sql, name = nil)
- clear_query_cache
- @connection.delete(sql, name)
- end
-
- private
- def method_missing(method, *arguments, &proc)
- @connection.send(method, *arguments, &proc)
- end
- end
-
- class Base
- # Set the connection for the class with caching on
- class << self
- alias_method :connection_without_query_cache=, :connection=
-
- def connection=(spec)
- if spec.is_a?(ConnectionSpecification) and spec.config[:query_cache]
- spec = QueryCache.new(self.send(spec.adapter_method, spec.config))
- end
- self.connection_without_query_cache = spec
+ module QueryCache
+ # Enable the query cache within the block if Active Record is configured.
+ def cache(&block)
+ if ActiveRecord::Base.configurations.blank?
+ yield
+ else
+ connection.cache(&block)
end
end
- end
-
- class AbstractAdapter #:nodoc:
- # Stub method to be able to treat the connection the same whether the query cache has been turned on or not
- def clear_query_cache
+
+ # Disable the query cache within the block if Active Record is configured.
+ def uncached(&block)
+ if ActiveRecord::Base.configurations.blank?
+ yield
+ else
+ connection.uncached(&block)
+ end
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/reflection.rb b/vendor/rails/activerecord/lib/active_record/reflection.rb
index 3d8e1b7d..8a2270ac 100644
--- a/vendor/rails/activerecord/lib/active_record/reflection.rb
+++ b/vendor/rails/activerecord/lib/active_record/reflection.rb
@@ -44,7 +44,7 @@ module ActiveRecord
reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
end
- # Returns an array of AssociationReflection objects for all the aggregations in the class. If you only want to reflect on a
+ # Returns an array of AssociationReflection objects for all the associations in the class. If you only want to reflect on a
# certain association type, pass in the symbol (:has_many, :has_one, :belongs_to) for that as the first parameter.
# Example:
#
@@ -56,7 +56,7 @@ module ActiveRecord
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
end
- # Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
+ # Returns the AssociationReflection object for the named +association+ (use the symbol). Example:
#
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
@@ -71,6 +71,7 @@ module ActiveRecord
# those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
class MacroReflection
attr_reader :active_record
+
def initialize(macro, name, options, active_record)
@macro, @name, @options, @active_record = macro, name, options, active_record
end
@@ -81,7 +82,7 @@ module ActiveRecord
@name
end
- # Returns the name of the macro, so it would return :composed_of for
+ # Returns the type of the macro, so it would return :composed_of for
# "composed_of :balance, :class_name => 'Money'" or :has_many for "has_many :clients".
def macro
@macro
@@ -93,30 +94,29 @@ module ActiveRecord
@options
end
- # Returns the class for the macro, so "composed_of :balance, :class_name => 'Money'" would return the Money class and
- # "has_many :clients" would return the Client class.
- def klass() end
-
+ # Returns the class for the macro, so "composed_of :balance, :class_name => 'Money'" returns the Money class and
+ # "has_many :clients" returns the Client class.
+ def klass
+ @klass ||= class_name.constantize
+ end
+
def class_name
- @class_name ||= name_to_class_name(name.id2name)
+ @class_name ||= options[:class_name] || derive_class_name
end
def ==(other_aggregation)
name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
end
+
+ private
+ def derive_class_name
+ name.to_s.camelize
+ end
end
# Holds all the meta-data about an aggregation as it was specified in the Active Record class.
class AggregateReflection < MacroReflection #:nodoc:
- def klass
- @klass ||= Object.const_get(options[:class_name] || class_name)
- end
-
- private
- def name_to_class_name(name)
- name.capitalize.gsub(/_(.)/) { |s| $1.capitalize }
- end
end
# Holds all the meta-data about an association as it was specified in the Active Record class.
@@ -130,17 +130,9 @@ module ActiveRecord
end
def primary_key_name
- return @primary_key_name if @primary_key_name
- case
- when macro == :belongs_to
- @primary_key_name = options[:foreign_key] || class_name.foreign_key
- when options[:as]
- @primary_key_name = options[:foreign_key] || "#{options[:as]}_id"
- else
- @primary_key_name = options[:foreign_key] || active_record.name.foreign_key
- end
+ @primary_key_name ||= options[:foreign_key] || derive_primary_key_name
end
-
+
def association_foreign_key
@association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
end
@@ -202,19 +194,24 @@ module ActiveRecord
end
private
- def name_to_class_name(name)
- if name =~ /::/
- name
+ def derive_class_name
+ # get the class_name of the belongs_to association of the through reflection
+ if through_reflection
+ options[:source_type] || source_reflection.class_name
else
- if options[:class_name]
- options[:class_name]
- elsif through_reflection # get the class_name of the belongs_to association of the through reflection
- options[:source_type] || source_reflection.class_name
- else
- class_name = name.to_s.camelize
- class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
- class_name
- end
+ class_name = name.to_s.camelize
+ class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
+ class_name
+ end
+ end
+
+ def derive_primary_key_name
+ if macro == :belongs_to
+ "#{name}_id"
+ elsif options[:as]
+ "#{options[:as]}_id"
+ else
+ active_record.name.foreign_key
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/schema.rb b/vendor/rails/activerecord/lib/active_record/schema.rb
index dc854463..9d50efb7 100644
--- a/vendor/rails/activerecord/lib/active_record/schema.rb
+++ b/vendor/rails/activerecord/lib/active_record/schema.rb
@@ -8,16 +8,16 @@ module ActiveRecord
#
# ActiveRecord::Schema.define do
# create_table :authors do |t|
- # t.column :name, :string, :null => false
+ # t.string :name, :null => false
# end
#
# add_index :authors, :name, :unique
#
# create_table :posts do |t|
- # t.column :author_id, :integer, :null => false
- # t.column :subject, :string
- # t.column :body, :text
- # t.column :private, :boolean, :default => false
+ # t.integer :author_id, :null => false
+ # t.string :subject
+ # t.text :body
+ # t.boolean :private, :default => false
# end
#
# add_index :posts, :author_id
diff --git a/vendor/rails/activerecord/lib/active_record/schema_dumper.rb b/vendor/rails/activerecord/lib/active_record/schema_dumper.rb
index 98c11634..28630687 100644
--- a/vendor/rails/activerecord/lib/active_record/schema_dumper.rb
+++ b/vendor/rails/activerecord/lib/active_record/schema_dumper.rb
@@ -37,9 +37,16 @@ module ActiveRecord
define_params = @info ? ":version => #{@info['version']}" : ""
stream.puts < ")}
spec
end.compact
- keys = [:name, :type, :limit, :precision, :scale, :default, :null] & column_specs.map{ |spec| spec.keys }.inject([]){ |a,b| a | b }
+
+ # find all migration keys used in this table
+ keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map(&:keys).flatten
+
+ # figure out the lengths for each column based on above keys
lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
- format_string = lengths.map{ |len| "%-#{len}s" }.join("")
+
+ # the string we're going to sprintf our values against, with standardized column widths
+ format_string = lengths.map{ |len| "%-#{len}s" }
+
+ # find the max length for the 'type' column, which is special
+ type_length = column_specs.map{ |column| column[:type].length }.max
+
+ # add column type definition to our format string
+ format_string.unshift " t.%-#{type_length}s "
+
+ format_string *= ''
+
column_specs.each do |colspec|
values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
- tbl.print " t.column "
+ values.unshift colspec[:type]
tbl.print((format_string % values).gsub(/,\s*$/, ''))
tbl.puts
end
diff --git a/vendor/rails/activerecord/lib/active_record/serialization.rb b/vendor/rails/activerecord/lib/active_record/serialization.rb
new file mode 100644
index 00000000..2c8210a2
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/serialization.rb
@@ -0,0 +1,98 @@
+module ActiveRecord #:nodoc:
+ module Serialization
+ class Serializer #:nodoc:
+ attr_reader :options
+
+ def initialize(record, options = {})
+ @record, @options = record, options.dup
+ end
+
+ # To replicate the behavior in ActiveRecord#attributes,
+ # :except takes precedence over :only. If :only is not set
+ # for a N level model but is set for the N+1 level models,
+ # then because :except is set to a default value, the second
+ # level model can have both :except and :only set. So if
+ # :only is set, always delete :except.
+ def serializable_attribute_names
+ attribute_names = @record.attribute_names
+
+ if options[:only]
+ options.delete(:except)
+ attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
+ else
+ options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
+ attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
+ end
+
+ attribute_names
+ end
+
+ def serializable_method_names
+ Array(options[:methods]).inject([]) do |method_attributes, name|
+ method_attributes << name if @record.respond_to?(name.to_s)
+ method_attributes
+ end
+ end
+
+ def serializable_names
+ serializable_attribute_names + serializable_method_names
+ end
+
+ # Add associations specified via the :includes option.
+ # Expects a block that takes as arguments:
+ # +association+ - name of the association
+ # +records+ - the association record(s) to be serialized
+ # +opts+ - options for the association records
+ def add_includes(&block)
+ if include_associations = options.delete(:include)
+ base_only_or_except = { :except => options[:except],
+ :only => options[:only] }
+
+ include_has_options = include_associations.is_a?(Hash)
+ associations = include_has_options ? include_associations.keys : Array(include_associations)
+
+ for association in associations
+ records = case @record.class.reflect_on_association(association).macro
+ when :has_many, :has_and_belongs_to_many
+ @record.send(association).to_a
+ when :has_one, :belongs_to
+ @record.send(association)
+ end
+
+ unless records.nil?
+ association_options = include_has_options ? include_associations[association] : base_only_or_except
+ opts = options.merge(association_options)
+ yield(association, records, opts)
+ end
+ end
+
+ options[:include] = include_associations
+ end
+ end
+
+ def serializable_record
+ returning(serializable_record = {}) do
+ serializable_names.each { |name| serializable_record[name] = @record.send(name) }
+ add_includes do |association, records, opts|
+ if records.is_a?(Enumerable)
+ serializable_record[association] = records.collect { |r| self.class.new(r, opts).serializable_record }
+ else
+ serializable_record[association] = self.class.new(records, opts).serializable_record
+ end
+ end
+ end
+ end
+
+ def serialize
+ # overwrite to implement
+ end
+
+ def to_s(&block)
+ serialize(&block)
+ end
+ end
+ end
+end
+
+require 'active_record/serializers/xml_serializer'
+require 'active_record/serializers/json_serializer'
\ No newline at end of file
diff --git a/vendor/rails/activerecord/lib/active_record/serializers/json_serializer.rb b/vendor/rails/activerecord/lib/active_record/serializers/json_serializer.rb
new file mode 100644
index 00000000..cf44309d
--- /dev/null
+++ b/vendor/rails/activerecord/lib/active_record/serializers/json_serializer.rb
@@ -0,0 +1,71 @@
+module ActiveRecord #:nodoc:
+ module Serialization
+ # Returns a JSON string representing the model. Some configuration is
+ # available through +options+.
+ #
+ # Without any +options+, the returned JSON string will include all
+ # the model's attributes. For example:
+ #
+ # konata = User.find(1)
+ # konata.to_json
+ #
+ # {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true}
+ #
+ # The :only and :except options can be used to limit the attributes
+ # included, and work similar to the #attributes method. For example:
+ #
+ # konata.to_json(:only => [ :id, :name ])
+ #
+ # {"id": 1, "name": "Konata Izumi"}
+ #
+ # konata.to_json(:except => [ :id, :created_at, :age ])
+ #
+ # {"name": "Konata Izumi", "awesome": true}
+ #
+ # To include any methods on the model, use :methods.
+ #
+ # konata.to_json(:methods => :permalink)
+ #
+ # {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true,
+ # "permalink": "1-konata-izumi"}
+ #
+ # To include associations, use :include.
+ #
+ # konata.to_json(:include => :posts)
+ #
+ # {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true,
+ # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
+ # {"id": 2, author_id: 1, "title": "So I was thinking"}]}
+ #
+ # 2nd level and higher order associations work as well:
+ #
+ # konata.to_json(:include => { :posts => {
+ # :include => { :comments => {
+ # :only => :body } },
+ # :only => :title } })
+ #
+ # {"id": 1, "name": "Konata Izumi", "age": 16,
+ # "created_at": "2006/08/01", "awesome": true,
+ # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
+ # "title": "Welcome to the weblog"},
+ # {"comments": [{"body": "Don't think too hard"}],
+ # "title": "So I was thinking"}]}
+ def to_json(options = {})
+ JsonSerializer.new(self, options).to_s
+ end
+
+ def from_json(json)
+ self.attributes = ActiveSupport::JSON.decode(json)
+ self
+ end
+
+ class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
+ def serialize
+ serializable_record.to_json
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/lib/active_record/xml_serialization.rb b/vendor/rails/activerecord/lib/active_record/serializers/xml_serializer.rb
similarity index 73%
rename from vendor/rails/activerecord/lib/active_record/xml_serialization.rb
rename to vendor/rails/activerecord/lib/active_record/serializers/xml_serializer.rb
index 7a816753..84b1a534 100644
--- a/vendor/rails/activerecord/lib/active_record/xml_serialization.rb
+++ b/vendor/rails/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -1,12 +1,12 @@
module ActiveRecord #:nodoc:
- module XmlSerialization
+ module Serialization
# Builds an XML document to represent the model. Some configuration is
- # availble through +options+, however more complicated cases should use
+ # available through +options+, however more complicated cases should
# override ActiveRecord's to_xml.
#
- # By default the generated XML document will include the processing
+ # By default the generated XML document will include the processing
# instruction and all object's attributes. For example:
- #
+ #
#
#
# The First Topic
@@ -27,7 +27,7 @@ module ActiveRecord #:nodoc:
# :except options are the same as for the #attributes method.
# The default is to dasherize all column names, to disable this,
# set :dasherize to false. To not have the column type included
- # in the XML output, set :skip_types to false.
+ # in the XML output, set :skip_types to true.
#
# For instance:
#
@@ -42,7 +42,7 @@ module ActiveRecord #:nodoc:
#
# 2004-04-15
#
- #
+ #
# To include first level associations use :include
#
# firm.to_xml :include => [ :account, :clients ]
@@ -52,7 +52,7 @@ module ActiveRecord #:nodoc:
# 1
# 1
# 37signals
- #
+ #
#
# 1
# Summit
@@ -90,7 +90,24 @@ module ActiveRecord #:nodoc:
# def
#
#
- # You may override the to_xml method in your ActiveRecord::Base
+ # Alternatively, you can also just yield the builder object as part of the to_xml call:
+ #
+ # firm.to_xml do |xml|
+ # xml.creator do
+ # xml.first_name "David"
+ # xml.last_name "Heinemeier Hansson"
+ # end
+ # end
+ #
+ #
+ # # ... normal attributes as shown above ...
+ #
+ # David
+ # Heinemeier Hansson
+ #
+ #
+ #
+ # You can override the to_xml method in your ActiveRecord::Base
# subclasses if you need to. The general form of doing this is
#
# class IHaveMyOwnXML < ActiveRecord::Base
@@ -103,18 +120,18 @@ module ActiveRecord #:nodoc:
# end
# end
# end
- def to_xml(options = {})
- XmlSerializer.new(self, options).to_s
+ def to_xml(options = {}, &block)
+ serializer = XmlSerializer.new(self, options)
+ block_given? ? serializer.to_s(&block) : serializer.to_s
+ end
+
+ def from_xml(xml)
+ self.attributes = Hash.from_xml(xml).values.first
+ self
end
end
- class XmlSerializer #:nodoc:
- attr_reader :options
-
- def initialize(record, options = {})
- @record, @options = record, options.dup
- end
-
+ class XmlSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
def builder
@builder ||= begin
options[:indent] ||= 2
@@ -124,7 +141,7 @@ module ActiveRecord #:nodoc:
builder.instruct!
options[:skip_instruct] = true
end
-
+
builder
end
end
@@ -133,7 +150,7 @@ module ActiveRecord #:nodoc:
root = (options[:root] || @record.class.to_s.underscore).to_s
dasherize? ? root.dasherize : root
end
-
+
def dasherize?
!options.has_key?(:dasherize) || options[:dasherize]
end
@@ -146,64 +163,22 @@ module ActiveRecord #:nodoc:
# level model can have both :except and :only set. So if
# :only is set, always delete :except.
def serializable_attributes
- attribute_names = @record.attribute_names
-
- if options[:only]
- options.delete(:except)
- attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
- else
- options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
- attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
- end
-
- attribute_names.collect { |name| Attribute.new(name, @record) }
+ serializable_attribute_names.collect { |name| Attribute.new(name, @record) }
end
def serializable_method_attributes
- Array(options[:methods]).collect { |name| MethodAttribute.new(name.to_s, @record) }
+ Array(options[:methods]).inject([]) do |method_attributes, name|
+ method_attributes << MethodAttribute.new(name.to_s, @record) if @record.respond_to?(name.to_s)
+ method_attributes
+ end
end
-
def add_attributes
(serializable_attributes + serializable_method_attributes).each do |attribute|
add_tag(attribute)
end
end
- def add_includes
- if include_associations = options.delete(:include)
- root_only_or_except = { :except => options[:except],
- :only => options[:only] }
-
- include_has_options = include_associations.is_a?(Hash)
-
- for association in include_has_options ? include_associations.keys : Array(include_associations)
- association_options = include_has_options ? include_associations[association] : root_only_or_except
-
- opts = options.merge(association_options)
-
- case @record.class.reflect_on_association(association).macro
- when :has_many, :has_and_belongs_to_many
- records = @record.send(association).to_a
- unless records.empty?
- tag = records.first.class.to_s.underscore.pluralize
- tag = tag.dasherize if dasherize?
-
- builder.tag!(tag) do
- records.each { |r| r.to_xml(opts.merge(:root => association.to_s.singularize)) }
- end
- end
- when :has_one, :belongs_to
- if record = @record.send(association)
- record.to_xml(opts.merge(:root => association))
- end
- end
- end
-
- options[:include] = include_associations
- end
- end
-
def add_procs
if procs = options.delete(:procs)
[ *procs ].each do |proc|
@@ -212,36 +187,64 @@ module ActiveRecord #:nodoc:
end
end
-
def add_tag(attribute)
builder.tag!(
- dasherize? ? attribute.name.dasherize : attribute.name,
- attribute.value.to_s,
+ dasherize? ? attribute.name.dasherize : attribute.name,
+ attribute.value.to_s,
attribute.decorations(!options[:skip_types])
)
end
+ def add_associations(association, records, opts)
+ if records.is_a?(Enumerable)
+ tag = association.to_s
+ tag = tag.dasherize if dasherize?
+ if records.empty?
+ builder.tag!(tag, :type => :array)
+ else
+ builder.tag!(tag, :type => :array) do
+ association_name = association.to_s.singularize
+ records.each do |record|
+ record.to_xml opts.merge(
+ :root => association_name,
+ :type => (record.class.to_s.underscore == association_name ? nil : record.class.name)
+ )
+ end
+ end
+ end
+ else
+ if record = @record.send(association)
+ record.to_xml(opts.merge(:root => association))
+ end
+ end
+ end
+
def serialize
args = [root]
if options[:namespace]
args << {:xmlns=>options[:namespace]}
end
-
+
+ if options[:type]
+ args << {:type=>options[:type]}
+ end
+
builder.tag!(*args) do
add_attributes
- add_includes
+ procs = options.delete(:procs)
+ add_includes { |association, records, opts| add_associations(association, records, opts) }
+ options[:procs] = procs
add_procs
+ yield builder if block_given?
end
- end
-
- alias_method :to_s, :serialize
+ end
class Attribute #:nodoc:
attr_reader :name, :value, :type
-
+
def initialize(name, record)
@name, @record = name, record
-
+
@type = compute_type
@value = compute_value
end
@@ -258,24 +261,28 @@ module ActiveRecord #:nodoc:
def needs_encoding?
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
end
-
+
def decorations(include_types = true)
decorations = {}
if type == :binary
decorations[:encoding] = 'base64'
end
-
+
if include_types && type != :string
decorations[:type] = type
end
-
+
+ if value.nil?
+ decorations[:nil] = true
+ end
+
decorations
end
-
+
protected
def compute_type
- type = @record.class.columns_hash[name].type
+ type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @record.class.columns_hash[name].type
case type
when :text
@@ -286,10 +293,10 @@ module ActiveRecord #:nodoc:
type
end
end
-
+
def compute_value
value = @record.send(name)
-
+
if formatter = Hash::XML_FORMATTING[type.to_s]
value ? formatter.call(value) : nil
else
diff --git a/vendor/rails/activerecord/lib/active_record/timestamp.rb b/vendor/rails/activerecord/lib/active_record/timestamp.rb
index 2fff7b9d..84ae3785 100644
--- a/vendor/rails/activerecord/lib/active_record/timestamp.rb
+++ b/vendor/rails/activerecord/lib/active_record/timestamp.rb
@@ -1,6 +1,6 @@
module ActiveRecord
- # Active Record automatically timestamps create and update if the table has fields
- # created_at/created_on or updated_at/updated_on.
+ # Active Record automatically timestamps create and update operations if the table has fields
+ # named created_at/created_on or updated_at/updated_on.
#
# Timestamping can be turned off by setting
# ActiveRecord::Base.record_timestamps = false
@@ -9,34 +9,33 @@ module ActiveRecord
# ActiveRecord::Base.default_timezone = :utc
module Timestamp
def self.included(base) #:nodoc:
- super
-
base.alias_method_chain :create, :timestamps
base.alias_method_chain :update, :timestamps
- base.cattr_accessor :record_timestamps, :instance_writer => false
+ base.class_inheritable_accessor :record_timestamps, :instance_writer => false
base.record_timestamps = true
end
- def create_with_timestamps #:nodoc:
- if record_timestamps
- t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
- write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
- write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
+ private
+ def create_with_timestamps #:nodoc:
+ if record_timestamps
+ t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
+ write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
+ write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
- write_attribute('updated_at', t) if respond_to?(:updated_at)
- write_attribute('updated_on', t) if respond_to?(:updated_on)
+ write_attribute('updated_at', t) if respond_to?(:updated_at)
+ write_attribute('updated_on', t) if respond_to?(:updated_on)
+ end
+ create_without_timestamps
end
- create_without_timestamps
- end
- def update_with_timestamps #:nodoc:
- if record_timestamps
- t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
- write_attribute('updated_at', t) if respond_to?(:updated_at)
- write_attribute('updated_on', t) if respond_to?(:updated_on)
+ def update_with_timestamps #:nodoc:
+ if record_timestamps
+ t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
+ write_attribute('updated_at', t) if respond_to?(:updated_at)
+ write_attribute('updated_on', t) if respond_to?(:updated_on)
+ end
+ update_without_timestamps
end
- update_without_timestamps
- end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/transactions.rb b/vendor/rails/activerecord/lib/active_record/transactions.rb
index 6a776966..45a64184 100644
--- a/vendor/rails/activerecord/lib/active_record/transactions.rb
+++ b/vendor/rails/activerecord/lib/active_record/transactions.rb
@@ -1,5 +1,3 @@
-require 'active_record/vendor/simple.rb'
-Transaction::Simple.send(:remove_method, :transaction)
require 'thread'
module ActiveRecord
@@ -32,6 +30,19 @@ module ActiveRecord
# Exceptions will force a ROLLBACK that returns the database to the state before the transaction was begun. Be aware, though,
# that the objects by default will _not_ have their instance data returned to their pre-transactional state.
#
+ # == Different ActiveRecord classes in a single transaction
+ #
+ # Though the transaction class method is called on some ActiveRecord class,
+ # the objects within the transaction block need not all be instances of
+ # that class.
+ # In this example a Balance record is transactionally saved even
+ # though transaction is called on the Account class:
+ #
+ # Account.transaction do
+ # balance.save!
+ # account.save!
+ # end
+ #
# == Transactions are not distributed across database connections
#
# A transaction acts on a single database connection. If you have
@@ -53,52 +64,20 @@ module ActiveRecord
#
# Both Base#save and Base#destroy come wrapped in a transaction that ensures that whatever you do in validations or callbacks
# will happen under the protected cover of a transaction. So you can use validations to check for values that the transaction
- # depend on or you can raise exceptions in the callbacks to rollback.
- #
- # == Object-level transactions (deprecated)
- #
- # You can enable object-level transactions for Active Record objects, though. You do this by naming each of the Active Records
- # that you want to enable object-level transactions for, like this:
- #
- # Account.transaction(david, mary) do
- # david.withdrawal(100)
- # mary.deposit(100)
- # end
- #
- # If the transaction fails, David and Mary will be returned to their
- # pre-transactional state. No money will have changed hands in neither
- # object nor database.
- #
- # However, useful state such as validation errors are also rolled back,
- # limiting the usefulness of this feature. As such it is deprecated in
- # Rails 1.2 and will be removed in the next release. Install the
- # object_transactions plugin if you wish to continue using it.
+ # depends on or you can raise exceptions in the callbacks to rollback.
#
# == Exception handling
#
# Also have in mind that exceptions thrown within a transaction block will be propagated (after triggering the ROLLBACK), so you
- # should be ready to catch those in your application code.
- #
- # Tribute: Object-level transactions are implemented by Transaction::Simple by Austin Ziegler.
+ # should be ready to catch those in your application code. One exception is the ActiveRecord::Rollback exception, which will
+ # trigger a ROLLBACK when raised, but not be re-raised by the transaction block.
module ClassMethods
- def transaction(*objects, &block)
+ def transaction(&block)
previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" }
increment_open_transactions
begin
- unless objects.empty?
- ActiveSupport::Deprecation.warn "Object transactions are deprecated and will be removed from Rails 2.0. See http://www.rubyonrails.org/deprecation for details.", caller
- objects.each { |o| o.extend(Transaction::Simple) }
- objects.each { |o| o.start_transaction }
- end
-
- result = connection.transaction(Thread.current['start_db_transaction'], &block)
-
- objects.each { |o| o.commit_transaction }
- return result
- rescue Exception => object_transaction_rollback
- objects.each { |o| o.abort_transaction }
- raise
+ connection.transaction(Thread.current['start_db_transaction'], &block)
ensure
decrement_open_transactions
trap('TERM', previous_handler)
@@ -117,8 +96,8 @@ module ActiveRecord
end
end
- def transaction(*objects, &block)
- self.class.transaction(*objects, &block)
+ def transaction(&block)
+ self.class.transaction(&block)
end
def destroy_with_transactions #:nodoc:
@@ -126,11 +105,28 @@ module ActiveRecord
end
def save_with_transactions(perform_validation = true) #:nodoc:
- transaction { save_without_transactions(perform_validation) }
+ rollback_active_record_state! { transaction { save_without_transactions(perform_validation) } }
end
def save_with_transactions! #:nodoc:
- transaction { save_without_transactions! }
+ rollback_active_record_state! { transaction { save_without_transactions! } }
+ end
+
+ # Reset id and @new_record if the transaction rolls back.
+ def rollback_active_record_state!
+ id_present = has_attribute?(self.class.primary_key)
+ previous_id = id
+ previous_new_record = @new_record
+ yield
+ rescue Exception
+ @new_record = previous_new_record
+ if id_present
+ self.id = previous_id
+ else
+ @attributes.delete(self.class.primary_key)
+ @attributes_cache.delete(self.class.primary_key)
+ end
+ raise
end
end
end
diff --git a/vendor/rails/activerecord/lib/active_record/validations.rb b/vendor/rails/activerecord/lib/active_record/validations.rb
index 0880a7c9..f1dbe7fa 100755
--- a/vendor/rails/activerecord/lib/active_record/validations.rb
+++ b/vendor/rails/activerecord/lib/active_record/validations.rb
@@ -15,7 +15,7 @@ module ActiveRecord
end
# Active Record validation is reported to and from this object, which is used by Base#save to
- # determine whether the object in a valid state to be saved. See usage example in Validations.
+ # determine whether the object is in a valid state to be saved. See usage example in Validations.
class Errors
include Enumerable
@@ -35,10 +35,17 @@ module ActiveRecord
:too_short => "is too short (minimum is %d characters)",
:wrong_length => "is the wrong length (should be %d characters)",
:taken => "has already been taken",
- :not_a_number => "is not a number"
+ :not_a_number => "is not a number",
+ :greater_than => "must be greater than %d",
+ :greater_than_or_equal_to => "must be greater than or equal to %d",
+ :equal_to => "must be equal to %d",
+ :less_than => "must be less than %d",
+ :less_than_or_equal_to => "must be less than or equal to %d",
+ :odd => "must be odd",
+ :even => "must be even"
}
- # Holds a hash with all the default error messages, such that they can be replaced by your own copy or localizations.
+ # Holds a hash with all the default error messages that can be replaced by your own copy or localizations.
cattr_accessor :default_error_messages
@@ -76,27 +83,33 @@ module ActiveRecord
end
end
- # Will add an error message to each of the attributes in +attributes+ that has a length outside of the passed boundary +range+.
- # If the length is above the boundary, the too_long_msg message will be used. If below, the too_short_msg.
- def add_on_boundary_breaking(attributes, range, too_long_msg = @@default_error_messages[:too_long], too_short_msg = @@default_error_messages[:too_short])
- for attr in [attributes].flatten
- value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s]
- add(attr, too_short_msg % range.begin) if value && value.length < range.begin
- add(attr, too_long_msg % range.end) if value && value.length > range.end
- end
- end
-
- alias :add_on_boundry_breaking :add_on_boundary_breaking
- deprecate :add_on_boundary_breaking => :validates_length_of, :add_on_boundry_breaking => :validates_length_of
-
# Returns true if the specified +attribute+ has errors associated with it.
+ #
+ # class Company < ActiveRecord::Base
+ # validates_presence_of :name, :address, :email
+ # validates_length_of :name, :in => 5..30
+ # end
+ #
+ # company = Company.create(:address => '123 First St.')
+ # company.errors.invalid?(:name) # => true
+ # company.errors.invalid?(:address) # => false
def invalid?(attribute)
!@errors[attribute.to_s].nil?
end
- # * Returns nil, if no errors are associated with the specified +attribute+.
- # * Returns the error message, if one error is associated with the specified +attribute+.
- # * Returns an array of error messages, if more than one error is associated with the specified +attribute+.
+ # Returns nil, if no errors are associated with the specified +attribute+.
+ # Returns the error message, if one error is associated with the specified +attribute+.
+ # Returns an array of error messages, if more than one error is associated with the specified +attribute+.
+ #
+ # class Company < ActiveRecord::Base
+ # validates_presence_of :name, :address, :email
+ # validates_length_of :name, :in => 5..30
+ # end
+ #
+ # company = Company.create(:address => '123 First St.')
+ # company.errors.on(:name) # => ["is too short (minimum is 5 characters)", "can't be blank"]
+ # company.errors.on(:email) # => "can't be blank"
+ # company.errors.on(:address) # => nil
def on(attribute)
errors = @errors[attribute.to_s]
return nil if errors.nil?
@@ -105,23 +118,54 @@ module ActiveRecord
alias :[] :on
- # Returns errors assigned to base object through add_to_base according to the normal rules of on(attribute).
+ # Returns errors assigned to the base object through add_to_base according to the normal rules of on(attribute).
def on_base
on(:base)
end
# Yields each attribute and associated message per error added.
+ #
+ # class Company < ActiveRecord::Base
+ # validates_presence_of :name, :address, :email
+ # validates_length_of :name, :in => 5..30
+ # end
+ #
+ # company = Company.create(:address => '123 First St.')
+ # company.errors.each{|attr,msg| puts "#{attr} - #{msg}" } # =>
+ # name - is too short (minimum is 5 characters)
+ # name - can't be blank
+ # address - can't be blank
def each
@errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
end
# Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
# through iteration as "First name can't be empty".
+ #
+ # class Company < ActiveRecord::Base
+ # validates_presence_of :name, :address, :email
+ # validates_length_of :name, :in => 5..30
+ # end
+ #
+ # company = Company.create(:address => '123 First St.')
+ # company.errors.each_full{|msg| puts msg } # =>
+ # Name is too short (minimum is 5 characters)
+ # Name can't be blank
+ # Address can't be blank
def each_full
full_messages.each { |msg| yield msg }
end
# Returns all the full error messages in an array.
+ #
+ # class Company < ActiveRecord::Base
+ # validates_presence_of :name, :address, :email
+ # validates_length_of :name, :in => 5..30
+ # end
+ #
+ # company = Company.create(:address => '123 First St.')
+ # company.errors.full_messages # =>
+ # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
def full_messages
full_messages = []
@@ -143,22 +187,35 @@ module ActiveRecord
def empty?
@errors.empty?
end
-
- # Removes all the errors that have been added.
+
+ # Removes all errors that have been added.
def clear
@errors = {}
end
- # Returns the total number of errors added. Two errors added to the same attribute will be counted as such
- # with this as well.
+ # Returns the total number of errors added. Two errors added to the same attribute will be counted as such.
def size
@errors.values.inject(0) { |error_count, attribute| error_count + attribute.size }
end
-
+
alias_method :count, :size
alias_method :length, :size
# Return an XML representation of this error object.
+ #
+ # class Company < ActiveRecord::Base
+ # validates_presence_of :name, :address, :email
+ # validates_length_of :name, :in => 5..30
+ # end
+ #
+ # company = Company.create(:address => '123 First St.')
+ # company.errors.to_xml # =>
+ #
+ #
+ # Name is too short (minimum is 5 characters)
+ # Name can't be blank
+ # Address can't be blank
+ #
def to_xml(options={})
options[:root] ||= "errors"
options[:indent] ||= 2
@@ -231,11 +288,42 @@ module ActiveRecord
DEFAULT_VALIDATION_OPTIONS = {
:on => :save,
:allow_nil => false,
+ :allow_blank => false,
:message => nil
}.freeze
ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze
+ ALL_NUMERICALITY_CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=',
+ :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=',
+ :odd => 'odd?', :even => 'even?' }.freeze
+ # Adds a validation method or block to the class. This is useful when
+ # overriding the #validate instance method becomes too unwieldly and
+ # you're looking for more descriptive declaration of your validations.
+ #
+ # This can be done with a symbol pointing to a method:
+ #
+ # class Comment < ActiveRecord::Base
+ # validate :must_be_friends
+ #
+ # def must_be_friends
+ # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee)
+ # end
+ # end
+ #
+ # Or with a block which is passed the current record to be validated:
+ #
+ # class Comment < ActiveRecord::Base
+ # validate do |comment|
+ # comment.must_be_friends
+ # end
+ #
+ # def must_be_friends
+ # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee)
+ # end
+ # end
+ #
+ # This usage applies to #validate_on_create and #validate_on_update as well.
def validate(*methods, &block)
methods << block if block_given?
write_inheritable_set(:validate, methods)
@@ -259,8 +347,8 @@ module ActiveRecord
# whether or not to validate the record. See #validates_each.
def evaluate_condition(condition, record)
case condition
- when Symbol: record.send(condition)
- when String: eval(condition, binding)
+ when Symbol; record.send(condition)
+ when String; eval(condition, record.send(:binding))
else
if condition_block?(condition)
condition.call(record)
@@ -285,20 +373,24 @@ module ActiveRecord
# Options:
# * on - Specifies when this validation is active (default is :save, other options :create, :update)
# * allow_nil - Skip validation if attribute is nil.
+ # * allow_blank - Skip validation if attribute is blank.
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_each(*attrs)
- options = attrs.last.is_a?(Hash) ? attrs.pop.symbolize_keys : {}
+ options = attrs.extract_options!.symbolize_keys
attrs = attrs.flatten
# Declare the validation.
send(validation_method(options[:on] || :save)) do |record|
- # Don't validate when there is an :if condition and that condition is false
- unless options[:if] && !evaluate_condition(options[:if], record)
+ # Don't validate when there is an :if condition and that condition is false or there is an :unless condition and that condition is true
+ unless (options[:if] && !evaluate_condition(options[:if], record)) || (options[:unless] && evaluate_condition(options[:unless], record))
attrs.each do |attr|
value = record.send(attr)
- next if value.nil? && options[:allow_nil]
+ next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
yield record, attr, value
end
end
@@ -317,21 +409,27 @@ module ActiveRecord
# <%= password_field "person", "password" %>
# <%= password_field "person", "password_confirmation" %>
#
- # The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
- # It exists only as an in-memory variable for validating the password. This check is performed only if password_confirmation
- # is not nil and by default on save.
+ # The added +password_confirmation+ attribute is virtual; it exists only as an in-memory attribute for validating the password.
+ # To achieve this, the validation adds acccessors to the model for the confirmation attribute. NOTE: This check is performed
+ # only if +password_confirmation+ is not nil, and by default only on save. To require confirmation, make sure to add a presence
+ # check for the confirmation attribute:
+ #
+ # validates_presence_of :password_confirmation, :if => :password_changed?
#
# Configuration options:
# * message - A custom error message (default is: "doesn't match confirmation")
# * on - Specifies when this validation is active (default is :save, other options :create, :update)
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_confirmation_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration.update(attr_names.extract_options!)
- attr_accessor *(attr_names.map { |n| "#{n}_confirmation" })
+ attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" }))
validates_each(attr_names, configuration) do |record, attr_name, value|
record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
@@ -345,22 +443,33 @@ module ActiveRecord
# validates_acceptance_of :eula, :message => "must be abided"
# end
#
- # The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed only if
- # terms_of_service is not nil and by default on save.
+ # If the database column does not exist, the terms_of_service attribute is entirely virtual. This check is
+ # performed only if terms_of_service is not nil and by default on save.
#
# Configuration options:
# * message - A custom error message (default is: "must be accepted")
# * on - Specifies when this validation is active (default is :save, other options :create, :update)
+ # * allow_nil - Skip validation if attribute is nil. (default is true)
# * accept - Specifies value that is considered accepted. The default value is a string "1", which
- # makes it easy to relate to an HTML checkbox.
+ # makes it easy to relate to an HTML checkbox. This should be set to 'true' if you are validating a database
+ # column, since the attribute is typecast from "1" to true before validation.
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_acceptance_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration.update(attr_names.extract_options!)
- attr_accessor *attr_names
+ db_cols = begin
+ column_names
+ rescue ActiveRecord::StatementInvalid
+ []
+ end
+ names = attr_names.reject { |name| db_cols.include?(name.to_s) }
+ attr_accessor(*names)
validates_each(attr_names,configuration) do |record, attr_name, value|
record.errors.add(attr_name, configuration[:message]) unless value == configuration[:accept]
@@ -374,7 +483,7 @@ module ActiveRecord
# end
#
# The first_name attribute must be in the object and it cannot be blank.
- #
+ #
# If you want to validate the presence of a boolean field (where the real values are true and false),
# you will want to use validates_inclusion_of :field_name, :in => [true, false]
# This is due to the way Object#blank? handles boolean values. false.blank? # => true
@@ -383,33 +492,34 @@ module ActiveRecord
# * message - A custom error message (default is: "can't be blank")
# * on - Specifies when this validation is active (default is :save, other options :create, :update)
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
#
# === Warning
# Validate the presence of the foreign key, not the instance variable itself.
# Do this:
- # validate_presence_of :invoice_id
+ # validates_presence_of :invoice_id
#
# Not this:
- # validate_presence_of :invoice
+ # validates_presence_of :invoice
#
# If you validate the presence of the associated object, you will get
# failures on saves when both the parent object and the child object are
# new.
def validates_presence_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration.update(attr_names.extract_options!)
# can't use validates_each here, because it cannot cope with nonexistent attributes,
# while errors.add_on_empty can
- attr_names.each do |attr_name|
- send(validation_method(configuration[:on])) do |record|
- unless configuration[:if] and not evaluate_condition(configuration[:if], record)
- record.errors.add_on_blank(attr_name,configuration[:message])
- end
- end
- end
+ send(validation_method(configuration[:on])) do |record|
+ unless (configuration[:if] && !evaluate_condition(configuration[:if], record)) || (configuration[:unless] && evaluate_condition(configuration[:unless], record))
+ record.errors.add_on_blank(attr_names, configuration[:message])
+ end
+ end
end
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
@@ -418,6 +528,7 @@ module ActiveRecord
# validates_length_of :first_name, :maximum=>30
# validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind"
# validates_length_of :fax, :in => 7..32, :allow_nil => true
+ # validates_length_of :phone, :in => 7..32, :allow_blank => true
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
# validates_length_of :fav_bra_size, :minimum=>1, :too_short=>"please enter at least %d character"
# validates_length_of :smurf_leader, :is=>4, :message=>"papa is spelled with %d characters... don't play me."
@@ -430,6 +541,7 @@ module ActiveRecord
# * within - A range specifying the minimum and maximum size of the attribute
# * in - A synonym(or alias) for :within
# * allow_nil - Attribute may be nil; skip validation.
+ # * allow_blank - Attribute may be blank; skip validation.
#
# * too_long - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)")
# * too_short - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)")
@@ -437,8 +549,11 @@ module ActiveRecord
# * message - The error message to use for a :minimum, :maximum, or :is violation. An alias of the appropriate too_long/too_short/wrong_length message
# * on - Specifies when this validation is active (default is :save, other options :create, :update)
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_length_of(*attrs)
# Merge given options with defaults.
options = {
@@ -446,7 +561,7 @@ module ActiveRecord
:too_short => ActiveRecord::Errors.default_error_messages[:too_short],
:wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length]
}.merge(DEFAULT_VALIDATION_OPTIONS)
- options.update(attrs.pop.symbolize_keys) if attrs.last.is_a?(Hash)
+ options.update(attrs.extract_options!.symbolize_keys)
# Ensure that one and only one range option is specified.
range_options = ALL_RANGE_OPTIONS & options.keys
@@ -507,27 +622,34 @@ module ActiveRecord
# end
#
# It can also validate whether the value of the specified attributes are unique based on multiple scope parameters. For example,
- # making sure that a teacher can only be on the schedule once per semester for a particular class.
+ # making sure that a teacher can only be on the schedule once per semester for a particular class.
#
# class TeacherSchedule < ActiveRecord::Base
- # validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
+ # validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
# end
#
# When the record is created, a check is performed to make sure that no record exists in the database with the given value for the specified
# attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
#
+ # Because this check is performed outside the database there is still a chance that duplicate values
+ # will be inserted in two parallel transactions. To guarantee against this you should create a
+ # unique index on the field. See +create_index+ for more information.
+ #
# Configuration options:
# * message - Specifies a custom error message (default is: "has already been taken")
# * scope - One or more columns by which to limit the scope of the uniquness constraint.
# * case_sensitive - Looks for an exact match. Ignored by non-text columns (true by default).
# * allow_nil - If set to true, skips this validation if the attribute is null (default is: false)
+ # * allow_blank - If set to true, skips this validation if the attribute is blank (default is: false)
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
-
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_uniqueness_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration.update(attr_names.extract_options!)
validates_each(attr_names,configuration) do |record, attr_name, value|
if value.nil? || (configuration[:case_sensitive] || !columns_hash[attr_name.to_s].text?)
@@ -537,6 +659,7 @@ module ActiveRecord
condition_sql = "LOWER(#{record.class.table_name}.#{attr_name}) #{attribute_condition(value)}"
condition_params = [value.downcase]
end
+
if scope = configuration[:scope]
Array(scope).map do |scope_item|
scope_value = record.send(scope_item)
@@ -544,17 +667,32 @@ module ActiveRecord
condition_params << scope_value
end
end
+
unless record.new_record?
condition_sql << " AND #{record.class.table_name}.#{record.class.primary_key} <> ?"
condition_params << record.send(:id)
end
- if record.class.find(:first, :conditions => [condition_sql, *condition_params])
+
+ # The check for an existing value should be run from a class that
+ # isn't abstract. This means working down from the current class
+ # (self), to the first non-abstract class. Since classes don't know
+ # their subclasses, we have to build the hierarchy between self and
+ # the record's class.
+ class_hierarchy = [record.class]
+ while class_hierarchy.first != self
+ class_hierarchy.insert(0, class_hierarchy.first.superclass)
+ end
+
+ # Now we can work our way down the tree to the first non-abstract
+ # class (which has a database table to query from).
+ finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
+
+ if finder_class.find(:first, :conditions => [condition_sql, *condition_params])
record.errors.add(attr_name, configuration[:message])
end
end
end
-
# Validates whether the value of the specified attribute is of the correct form by matching it against the regular expression
# provided.
@@ -572,11 +710,14 @@ module ActiveRecord
# * with - The regular expression used to validate the format with (note: must be supplied!)
# * on Specifies when this validation is active (default is :save, other options :create, :update)
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_format_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration.update(attr_names.extract_options!)
raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
@@ -588,27 +729,32 @@ module ActiveRecord
# Validates whether the value of the specified attribute is available in a particular enumerable object.
#
# class Person < ActiveRecord::Base
- # validates_inclusion_of :gender, :in=>%w( m f ), :message=>"woah! what are you then!??!!"
- # validates_inclusion_of :age, :in=>0..99
+ # validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!"
+ # validates_inclusion_of :age, :in => 0..99
+ # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list"
# end
#
# Configuration options:
# * in - An enumerable object of available items
# * message - Specifies a customer error message (default is: "is not included in the list")
# * allow_nil - If set to true, skips this validation if the attribute is null (default is: false)
+ # * allow_blank - If set to true, skips this validation if the attribute is blank (default is: false)
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_inclusion_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration.update(attr_names.extract_options!)
enum = configuration[:in] || configuration[:within]
raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
validates_each(attr_names, configuration) do |record, attr_name, value|
- record.errors.add(attr_name, configuration[:message]) unless enum.include?(value)
+ record.errors.add(attr_name, configuration[:message] % value) unless enum.include?(value)
end
end
@@ -617,25 +763,30 @@ module ActiveRecord
# class Person < ActiveRecord::Base
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
+ # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed"
# end
#
# Configuration options:
# * in - An enumerable object of items that the value shouldn't be part of
# * message - Specifies a customer error message (default is: "is reserved")
# * allow_nil - If set to true, skips this validation if the attribute is null (default is: false)
+ # * allow_blank - If set to true, skips this validation if the attribute is blank (default is: false)
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_exclusion_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration.update(attr_names.extract_options!)
enum = configuration[:in] || configuration[:within]
raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?")
validates_each(attr_names, configuration) do |record, attr_name, value|
- record.errors.add(attr_name, configuration[:message]) if enum.include?(value)
+ record.errors.add(attr_name, configuration[:message] % value) if enum.include?(value)
end
end
@@ -662,17 +813,21 @@ module ActiveRecord
# is both present and guaranteed to be valid, you also need to use validates_presence_of.
#
# Configuration options:
+ # * message - A custom error message (default is: "is invalid")
# * on Specifies when this validation is active (default is :save, other options :create, :update)
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_associated(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration.update(attr_names.extract_options!)
validates_each(attr_names, configuration) do |record, attr_name, value|
record.errors.add(attr_name, configuration[:message]) unless
- (value.is_a?(Array) ? value : [value]).all? { |r| r.nil? or r.valid? }
+ (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v }
end
end
@@ -689,40 +844,67 @@ module ActiveRecord
# * on Specifies when this validation is active (default is :save, other options :create, :update)
# * only_integer Specifies whether the value has to be an integer, e.g. an integral value (default is false)
# * allow_nil Skip validation if attribute is nil (default is false). Notice that for fixnum and float columns empty strings are converted to nil
+ # * greater_than Specifies the value must be greater than the supplied value
+ # * greater_than_or_equal_to Specifies the value must be greater than or equal the supplied value
+ # * equal_to Specifies the value must be equal to the supplied value
+ # * less_than Specifies the value must be less than the supplied value
+ # * less_than_or_equal_to Specifies the value must be less than or equal the supplied value
+ # * odd Specifies the value must be an odd number
+ # * even Specifies the value must be an even number
# * if - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
- # method, proc or string should return or evaluate to a true or false value.
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * unless - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
+ # method, proc or string should return or evaluate to a true or false value.
def validates_numericality_of(*attr_names)
- configuration = { :message => ActiveRecord::Errors.default_error_messages[:not_a_number], :on => :save,
- :only_integer => false, :allow_nil => false }
- configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+ configuration = { :on => :save, :only_integer => false, :allow_nil => false }
+ configuration.update(attr_names.extract_options!)
- if configuration[:only_integer]
- validates_each(attr_names,configuration) do |record, attr_name,value|
- record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_before_type_cast").to_s =~ /\A[+-]?\d+\Z/
- end
- else
- validates_each(attr_names,configuration) do |record, attr_name,value|
- next if configuration[:allow_nil] and record.send("#{attr_name}_before_type_cast").nil?
- begin
- Kernel.Float(record.send("#{attr_name}_before_type_cast").to_s)
+
+ numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys
+
+ (numericality_options - [ :odd, :even ]).each do |option|
+ raise ArgumentError, ":#{option} must be a number" unless configuration[option].is_a?(Numeric)
+ end
+
+ validates_each(attr_names,configuration) do |record, attr_name, value|
+ raw_value = record.send("#{attr_name}_before_type_cast") || value
+
+ next if configuration[:allow_nil] and raw_value.nil?
+
+ if configuration[:only_integer]
+ unless raw_value.to_s =~ /\A[+-]?\d+\Z/
+ record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
+ next
+ end
+ raw_value = raw_value.to_i
+ else
+ begin
+ raw_value = Kernel.Float(raw_value.to_s)
rescue ArgumentError, TypeError
- record.errors.add(attr_name, configuration[:message])
+ record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number])
+ next
+ end
+ end
+
+ numericality_options.each do |option|
+ case option
+ when :odd, :even
+ record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[option]) unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
+ else
+ record.errors.add(attr_name, configuration[:message] || (ActiveRecord::Errors.default_error_messages[option] % configuration[option])) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
end
end
end
end
-
# Creates an object just like Base.create but calls save! instead of save
# so an exception is raised if the record is invalid.
def create!(attributes = nil)
if attributes.is_a?(Array)
attributes.collect { |attr| create!(attr) }
else
- attributes ||= {}
- attributes.reverse_merge!(scope(:create)) if scoped?(:create)
-
object = new(attributes)
object.save!
object
@@ -733,7 +915,7 @@ module ActiveRecord
private
def write_inheritable_set(key, methods)
existing_methods = read_inheritable_attribute(key) || []
- write_inheritable_attribute(key, methods | existing_methods)
+ write_inheritable_attribute(key, existing_methods | methods)
end
def validation_method(on)
diff --git a/vendor/rails/activerecord/lib/active_record/vendor/simple.rb b/vendor/rails/activerecord/lib/active_record/vendor/simple.rb
deleted file mode 100644
index 7ac3cd08..00000000
--- a/vendor/rails/activerecord/lib/active_record/vendor/simple.rb
+++ /dev/null
@@ -1,693 +0,0 @@
-# :title: Transaction::Simple -- Active Object Transaction Support for Ruby
-# :main: Transaction::Simple
-#
-# == Licence
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-#--
-# Transaction::Simple
-# Simple object transaction support for Ruby
-# Version 1.3.0
-#
-# Copyright (c) 2003 - 2005 Austin Ziegler
-#
-# $Id: simple.rb,v 1.5 2005/05/05 16:16:49 austin Exp $
-#++
- # The "Transaction" namespace can be used for additional transaction
- # support objects and modules.
-module Transaction
- # A standard exception for transaction errors.
- class TransactionError < StandardError; end
- # The TransactionAborted exception is used to indicate when a
- # transaction has been aborted in the block form.
- class TransactionAborted < Exception; end
- # The TransactionCommitted exception is used to indicate when a
- # transaction has been committed in the block form.
- class TransactionCommitted < Exception; end
-
- te = "Transaction Error: %s"
-
- Messages = {
- :bad_debug_object =>
- te % "the transaction debug object must respond to #<<.",
- :unique_names =>
- te % "named transactions must be unique.",
- :no_transaction_open =>
- te % "no transaction open.",
- :cannot_rewind_no_transaction =>
- te % "cannot rewind; there is no current transaction.",
- :cannot_rewind_named_transaction =>
- te % "cannot rewind to transaction %s because it does not exist.",
- :cannot_rewind_transaction_before_block =>
- te % "cannot rewind a transaction started before the execution block.",
- :cannot_abort_no_transaction =>
- te % "cannot abort; there is no current transaction.",
- :cannot_abort_transaction_before_block =>
- te % "cannot abort a transaction started before the execution block.",
- :cannot_abort_named_transaction =>
- te % "cannot abort nonexistant transaction %s.",
- :cannot_commit_no_transaction =>
- te % "cannot commit; there is no current transaction.",
- :cannot_commit_transaction_before_block =>
- te % "cannot commit a transaction started before the execution block.",
- :cannot_commit_named_transaction =>
- te % "cannot commit nonexistant transaction %s.",
- :cannot_start_empty_block_transaction =>
- te % "cannot start a block transaction with no objects.",
- :cannot_obtain_transaction_lock =>
- te % "cannot obtain transaction lock for #%s.",
- }
-
- # = Transaction::Simple for Ruby
- # Simple object transaction support for Ruby
- #
- # == Introduction
- # Transaction::Simple provides a generic way to add active transaction
- # support to objects. The transaction methods added by this module will
- # work with most objects, excluding those that cannot be
- # Marshal ed (bindings, procedure objects, IO instances, or
- # singleton objects).
- #
- # The transactions supported by Transaction::Simple are not backed
- # transactions; they are not associated with any sort of data store.
- # They are "live" transactions occurring in memory and in the object
- # itself. This is to allow "test" changes to be made to an object
- # before making the changes permanent.
- #
- # Transaction::Simple can handle an "infinite" number of transaction
- # levels (limited only by memory). If I open two transactions, commit
- # the second, but abort the first, the object will revert to the
- # original version.
- #
- # Transaction::Simple supports "named" transactions, so that multiple
- # levels of transactions can be committed, aborted, or rewound by
- # referring to the appropriate name of the transaction. Names may be any
- # object *except* +nil+. As with Hash keys, String names will be
- # duplicated and frozen before using.
- #
- # Copyright:: Copyright © 2003 - 2005 by Austin Ziegler
- # Version:: 1.3.0
- # Licence:: MIT-Style
- #
- # Thanks to David Black for help with the initial concept that led to
- # this library.
- #
- # == Usage
- # include 'transaction/simple'
- #
- # v = "Hello, you." # -> "Hello, you."
- # v.extend(Transaction::Simple) # -> "Hello, you."
- #
- # v.start_transaction # -> ... (a Marshal string)
- # v.transaction_open? # -> true
- # v.gsub!(/you/, "world") # -> "Hello, world."
- #
- # v.rewind_transaction # -> "Hello, you."
- # v.transaction_open? # -> true
- #
- # v.gsub!(/you/, "HAL") # -> "Hello, HAL."
- # v.abort_transaction # -> "Hello, you."
- # v.transaction_open? # -> false
- #
- # v.start_transaction # -> ... (a Marshal string)
- # v.start_transaction # -> ... (a Marshal string)
- #
- # v.transaction_open? # -> true
- # v.gsub!(/you/, "HAL") # -> "Hello, HAL."
- #
- # v.commit_transaction # -> "Hello, HAL."
- # v.transaction_open? # -> true
- # v.abort_transaction # -> "Hello, you."
- # v.transaction_open? # -> false
- #
- # == Named Transaction Usage
- # v = "Hello, you." # -> "Hello, you."
- # v.extend(Transaction::Simple) # -> "Hello, you."
- #
- # v.start_transaction(:first) # -> ... (a Marshal string)
- # v.transaction_open? # -> true
- # v.transaction_open?(:first) # -> true
- # v.transaction_open?(:second) # -> false
- # v.gsub!(/you/, "world") # -> "Hello, world."
- #
- # v.start_transaction(:second) # -> ... (a Marshal string)
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
- # v.rewind_transaction(:first) # -> "Hello, you."
- # v.transaction_open? # -> true
- # v.transaction_open?(:first) # -> true
- # v.transaction_open?(:second) # -> false
- #
- # v.gsub!(/you/, "world") # -> "Hello, world."
- # v.start_transaction(:second) # -> ... (a Marshal string)
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
- # v.transaction_name # -> :second
- # v.abort_transaction(:first) # -> "Hello, you."
- # v.transaction_open? # -> false
- #
- # v.start_transaction(:first) # -> ... (a Marshal string)
- # v.gsub!(/you/, "world") # -> "Hello, world."
- # v.start_transaction(:second) # -> ... (a Marshal string)
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
- #
- # v.commit_transaction(:first) # -> "Hello, HAL."
- # v.transaction_open? # -> false
- #
- # == Block Usage
- # v = "Hello, you." # -> "Hello, you."
- # Transaction::Simple.start(v) do |tv|
- # # v has been extended with Transaction::Simple and an unnamed
- # # transaction has been started.
- # tv.transaction_open? # -> true
- # tv.gsub!(/you/, "world") # -> "Hello, world."
- #
- # tv.rewind_transaction # -> "Hello, you."
- # tv.transaction_open? # -> true
- #
- # tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
- # # The following breaks out of the transaction block after
- # # aborting the transaction.
- # tv.abort_transaction # -> "Hello, you."
- # end
- # # v still has Transaction::Simple applied from here on out.
- # v.transaction_open? # -> false
- #
- # Transaction::Simple.start(v) do |tv|
- # tv.start_transaction # -> ... (a Marshal string)
- #
- # tv.transaction_open? # -> true
- # tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
- #
- # # If #commit_transaction were called without having started a
- # # second transaction, then it would break out of the transaction
- # # block after committing the transaction.
- # tv.commit_transaction # -> "Hello, HAL."
- # tv.transaction_open? # -> true
- # tv.abort_transaction # -> "Hello, you."
- # end
- # v.transaction_open? # -> false
- #
- # == Named Transaction Usage
- # v = "Hello, you." # -> "Hello, you."
- # v.extend(Transaction::Simple) # -> "Hello, you."
- #
- # v.start_transaction(:first) # -> ... (a Marshal string)
- # v.transaction_open? # -> true
- # v.transaction_open?(:first) # -> true
- # v.transaction_open?(:second) # -> false
- # v.gsub!(/you/, "world") # -> "Hello, world."
- #
- # v.start_transaction(:second) # -> ... (a Marshal string)
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
- # v.rewind_transaction(:first) # -> "Hello, you."
- # v.transaction_open? # -> true
- # v.transaction_open?(:first) # -> true
- # v.transaction_open?(:second) # -> false
- #
- # v.gsub!(/you/, "world") # -> "Hello, world."
- # v.start_transaction(:second) # -> ... (a Marshal string)
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
- # v.transaction_name # -> :second
- # v.abort_transaction(:first) # -> "Hello, you."
- # v.transaction_open? # -> false
- #
- # v.start_transaction(:first) # -> ... (a Marshal string)
- # v.gsub!(/you/, "world") # -> "Hello, world."
- # v.start_transaction(:second) # -> ... (a Marshal string)
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
- #
- # v.commit_transaction(:first) # -> "Hello, HAL."
- # v.transaction_open? # -> false
- #
- # == Thread Safety
- # Threadsafe version of Transaction::Simple and
- # Transaction::Simple::Group exist; these are loaded from
- # 'transaction/simple/threadsafe' and
- # 'transaction/simple/threadsafe/group', respectively, and are
- # represented in Ruby code as Transaction::Simple::ThreadSafe and
- # Transaction::Simple::ThreadSafe::Group, respectively.
- #
- # == Contraindications
- # While Transaction::Simple is very useful, it has some severe
- # limitations that must be understood. Transaction::Simple:
- #
- # * uses Marshal. Thus, any object which cannot be Marshal ed
- # cannot use Transaction::Simple. In my experience, this affects
- # singleton objects more often than any other object. It may be that
- # Ruby 2.0 will solve this problem.
- # * does not manage resources. Resources external to the object and its
- # instance variables are not managed at all. However, all instance
- # variables and objects "belonging" to those instance variables are
- # managed. If there are object reference counts to be handled,
- # Transaction::Simple will probably cause problems.
- # * is not inherently thread-safe. In the ACID ("atomic, consistent,
- # isolated, durable") test, Transaction::Simple provides CD, but it is
- # up to the user of Transaction::Simple to provide isolation and
- # atomicity. Transactions should be considered "critical sections" in
- # multi-threaded applications. If thread safety and atomicity is
- # absolutely required, use Transaction::Simple::ThreadSafe, which uses
- # a Mutex object to synchronize the accesses on the object during the
- # transaction operations.
- # * does not necessarily maintain Object#__id__ values on rewind or
- # abort. This may change for future versions that will be Ruby 1.8 or
- # better *only*. Certain objects that support #replace will maintain
- # Object#__id__.
- # * Can be a memory hog if you use many levels of transactions on many
- # objects.
- #
- module Simple
- TRANSACTION_SIMPLE_VERSION = '1.3.0'
-
- # Sets the Transaction::Simple debug object. It must respond to #<<.
- # Sets the transaction debug object. Debugging will be performed
- # automatically if there's a debug object. The generic transaction
- # error class.
- def self.debug_io=(io)
- if io.nil?
- @tdi = nil
- @debugging = false
- else
- unless io.respond_to?(:<<)
- raise TransactionError, Messages[:bad_debug_object]
- end
- @tdi = io
- @debugging = true
- end
- end
-
- # Returns +true+ if we are debugging.
- def self.debugging?
- @debugging
- end
-
- # Returns the Transaction::Simple debug object. It must respond to
- # #<<.
- def self.debug_io
- @tdi ||= ""
- @tdi
- end
-
- # If +name+ is +nil+ (default), then returns +true+ if there is
- # currently a transaction open.
- #
- # If +name+ is specified, then returns +true+ if there is currently a
- # transaction that responds to +name+ open.
- def transaction_open?(name = nil)
- if name.nil?
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "Transaction " <<
- "[#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n"
- end
- return (not @__transaction_checkpoint__.nil?)
- else
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "Transaction(#{name.inspect}) " <<
- "[#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n"
- end
- return ((not @__transaction_checkpoint__.nil?) and @__transaction_names__.include?(name))
- end
- end
-
- # Returns the current name of the transaction. Transactions not
- # explicitly named are named +nil+.
- def transaction_name
- if @__transaction_checkpoint__.nil?
- raise TransactionError, Messages[:no_transaction_open]
- end
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
- "Transaction Name: #{@__transaction_names__[-1].inspect}\n"
- end
- if @__transaction_names__[-1].kind_of?(String)
- @__transaction_names__[-1].dup
- else
- @__transaction_names__[-1]
- end
- end
-
- # Starts a transaction. Stores the current object state. If a
- # transaction name is specified, the transaction will be named.
- # Transaction names must be unique. Transaction names of +nil+ will be
- # treated as unnamed transactions.
- def start_transaction(name = nil)
- @__transaction_level__ ||= 0
- @__transaction_names__ ||= []
-
- if name.nil?
- @__transaction_names__ << nil
- ss = "" if Transaction::Simple.debugging?
- else
- if @__transaction_names__.include?(name)
- raise TransactionError, Messages[:unique_names]
- end
- name = name.dup.freeze if name.kind_of?(String)
- @__transaction_names__ << name
- ss = "(#{name.inspect})" if Transaction::Simple.debugging?
- end
-
- @__transaction_level__ += 1
-
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "#{'>' * @__transaction_level__} " <<
- "Start Transaction#{ss}\n"
- end
-
- @__transaction_checkpoint__ = Marshal.dump(self)
- end
-
- # Rewinds the transaction. If +name+ is specified, then the
- # intervening transactions will be aborted and the named transaction
- # will be rewound. Otherwise, only the current transaction is rewound.
- def rewind_transaction(name = nil)
- if @__transaction_checkpoint__.nil?
- raise TransactionError, Messages[:cannot_rewind_no_transaction]
- end
-
- # Check to see if we are trying to rewind a transaction that is
- # outside of the current transaction block.
- if @__transaction_block__ and name
- nix = @__transaction_names__.index(name) + 1
- if nix < @__transaction_block__
- raise TransactionError, Messages[:cannot_rewind_transaction_before_block]
- end
- end
-
- if name.nil?
- __rewind_this_transaction
- ss = "" if Transaction::Simple.debugging?
- else
- unless @__transaction_names__.include?(name)
- raise TransactionError, Messages[:cannot_rewind_named_transaction] % name.inspect
- end
- ss = "(#{name})" if Transaction::Simple.debugging?
-
- while @__transaction_names__[-1] != name
- @__transaction_checkpoint__ = __rewind_this_transaction
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
- "Rewind Transaction#{ss}\n"
- end
- @__transaction_level__ -= 1
- @__transaction_names__.pop
- end
- __rewind_this_transaction
- end
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
- "Rewind Transaction#{ss}\n"
- end
- self
- end
-
- # Aborts the transaction. Resets the object state to what it was
- # before the transaction was started and closes the transaction. If
- # +name+ is specified, then the intervening transactions and the named
- # transaction will be aborted. Otherwise, only the current transaction
- # is aborted.
- #
- # If the current or named transaction has been started by a block
- # (Transaction::Simple.start), then the execution of the block will be
- # halted with +break+ +self+.
- def abort_transaction(name = nil)
- if @__transaction_checkpoint__.nil?
- raise TransactionError, Messages[:cannot_abort_no_transaction]
- end
-
- # Check to see if we are trying to abort a transaction that is
- # outside of the current transaction block. Otherwise, raise
- # TransactionAborted if they are the same.
- if @__transaction_block__ and name
- nix = @__transaction_names__.index(name) + 1
- if nix < @__transaction_block__
- raise TransactionError, Messages[:cannot_abort_transaction_before_block]
- end
-
- raise TransactionAborted if @__transaction_block__ == nix
- end
-
- raise TransactionAborted if @__transaction_block__ == @__transaction_level__
-
- if name.nil?
- __abort_transaction(name)
- else
- unless @__transaction_names__.include?(name)
- raise TransactionError, Messages[:cannot_abort_named_transaction] % name.inspect
- end
- __abort_transaction(name) while @__transaction_names__.include?(name)
- end
- self
- end
-
- # If +name+ is +nil+ (default), the current transaction level is
- # closed out and the changes are committed.
- #
- # If +name+ is specified and +name+ is in the list of named
- # transactions, then all transactions are closed and committed until
- # the named transaction is reached.
- def commit_transaction(name = nil)
- if @__transaction_checkpoint__.nil?
- raise TransactionError, Messages[:cannot_commit_no_transaction]
- end
- @__transaction_block__ ||= nil
-
- # Check to see if we are trying to commit a transaction that is
- # outside of the current transaction block. Otherwise, raise
- # TransactionCommitted if they are the same.
- if @__transaction_block__ and name
- nix = @__transaction_names__.index(name) + 1
- if nix < @__transaction_block__
- raise TransactionError, Messages[:cannot_commit_transaction_before_block]
- end
-
- raise TransactionCommitted if @__transaction_block__ == nix
- end
-
- raise TransactionCommitted if @__transaction_block__ == @__transaction_level__
-
- if name.nil?
- ss = "" if Transaction::Simple.debugging?
- __commit_transaction
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
- "Commit Transaction#{ss}\n"
- end
- else
- unless @__transaction_names__.include?(name)
- raise TransactionError, Messages[:cannot_commit_named_transaction] % name.inspect
- end
- ss = "(#{name})" if Transaction::Simple.debugging?
-
- while @__transaction_names__[-1] != name
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
- "Commit Transaction#{ss}\n"
- end
- __commit_transaction
- end
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
- "Commit Transaction#{ss}\n"
- end
- __commit_transaction
- end
-
- self
- end
-
- # Alternative method for calling the transaction methods. An optional
- # name can be specified for named transaction support.
- #
- # #transaction(:start):: #start_transaction
- # #transaction(:rewind):: #rewind_transaction
- # #transaction(:abort):: #abort_transaction
- # #transaction(:commit):: #commit_transaction
- # #transaction(:name):: #transaction_name
- # #transaction:: #transaction_open?
- def transaction(action = nil, name = nil)
- case action
- when :start
- start_transaction(name)
- when :rewind
- rewind_transaction(name)
- when :abort
- abort_transaction(name)
- when :commit
- commit_transaction(name)
- when :name
- transaction_name
- when nil
- transaction_open?(name)
- end
- end
-
- # Allows specific variables to be excluded from transaction support.
- # Must be done after extending the object but before starting the
- # first transaction on the object.
- #
- # vv.transaction_exclusions << "@io"
- def transaction_exclusions
- @transaction_exclusions ||= []
- end
-
- class << self
- def __common_start(name, vars, &block)
- if vars.empty?
- raise TransactionError, Messages[:cannot_start_empty_block_transaction]
- end
-
- if block
- begin
- vlevel = {}
-
- vars.each do |vv|
- vv.extend(Transaction::Simple)
- vv.start_transaction(name)
- vlevel[vv.__id__] = vv.instance_variable_get(:@__transaction_level__)
- vv.instance_variable_set(:@__transaction_block__, vlevel[vv.__id__])
- end
-
- yield(*vars)
- rescue TransactionAborted
- vars.each do |vv|
- if name.nil? and vv.transaction_open?
- loop do
- tlevel = vv.instance_variable_get(:@__transaction_level__) || -1
- vv.instance_variable_set(:@__transaction_block__, -1)
- break if tlevel < vlevel[vv.__id__]
- vv.abort_transaction if vv.transaction_open?
- end
- elsif vv.transaction_open?(name)
- vv.instance_variable_set(:@__transaction_block__, -1)
- vv.abort_transaction(name)
- end
- end
- rescue TransactionCommitted
- nil
- ensure
- vars.each do |vv|
- if name.nil? and vv.transaction_open?
- loop do
- tlevel = vv.instance_variable_get(:@__transaction_level__) || -1
- break if tlevel < vlevel[vv.__id__]
- vv.instance_variable_set(:@__transaction_block__, -1)
- vv.commit_transaction if vv.transaction_open?
- end
- elsif vv.transaction_open?(name)
- vv.instance_variable_set(:@__transaction_block__, -1)
- vv.commit_transaction(name)
- end
- end
- end
- else
- vars.each do |vv|
- vv.extend(Transaction::Simple)
- vv.start_transaction(name)
- end
- end
- end
- private :__common_start
-
- def start_named(name, *vars, &block)
- __common_start(name, vars, &block)
- end
-
- def start(*vars, &block)
- __common_start(nil, vars, &block)
- end
- end
-
- def __abort_transaction(name = nil) #:nodoc:
- @__transaction_checkpoint__ = __rewind_this_transaction
-
- if name.nil?
- ss = "" if Transaction::Simple.debugging?
- else
- ss = "(#{name.inspect})" if Transaction::Simple.debugging?
- end
-
- if Transaction::Simple.debugging?
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
- "Abort Transaction#{ss}\n"
- end
- @__transaction_level__ -= 1
- @__transaction_names__.pop
- if @__transaction_level__ < 1
- @__transaction_level__ = 0
- @__transaction_names__ = []
- end
- end
-
- TRANSACTION_CHECKPOINT = "@__transaction_checkpoint__" #:nodoc:
- SKIP_TRANSACTION_VARS = [TRANSACTION_CHECKPOINT, "@__transaction_level__"] #:nodoc:
-
- def __rewind_this_transaction #:nodoc:
- rr = Marshal.restore(@__transaction_checkpoint__)
-
- begin
- self.replace(rr) if respond_to?(:replace)
- rescue
- nil
- end
-
- rr.instance_variables.each do |vv|
- next if SKIP_TRANSACTION_VARS.include?(vv)
- next if self.transaction_exclusions.include?(vv)
- if respond_to?(:instance_variable_get)
- instance_variable_set(vv, rr.instance_variable_get(vv))
- else
- instance_eval(%q|#{vv} = rr.instance_eval("#{vv}")|)
- end
- end
-
- new_ivar = instance_variables - rr.instance_variables - SKIP_TRANSACTION_VARS
- new_ivar.each do |vv|
- if respond_to?(:instance_variable_set)
- instance_variable_set(vv, nil)
- else
- instance_eval(%q|#{vv} = nil|)
- end
- end
-
- if respond_to?(:instance_variable_get)
- rr.instance_variable_get(TRANSACTION_CHECKPOINT)
- else
- rr.instance_eval(TRANSACTION_CHECKPOINT)
- end
- end
-
- def __commit_transaction #:nodoc:
- if respond_to?(:instance_variable_get)
- @__transaction_checkpoint__ = Marshal.restore(@__transaction_checkpoint__).instance_variable_get(TRANSACTION_CHECKPOINT)
- else
- @__transaction_checkpoint__ = Marshal.restore(@__transaction_checkpoint__).instance_eval(TRANSACTION_CHECKPOINT)
- end
-
- @__transaction_level__ -= 1
- @__transaction_names__.pop
-
- if @__transaction_level__ < 1
- @__transaction_level__ = 0
- @__transaction_names__ = []
- end
- end
-
- private :__abort_transaction
- private :__rewind_this_transaction
- private :__commit_transaction
- end
-end
diff --git a/vendor/rails/activerecord/lib/active_record/version.rb b/vendor/rails/activerecord/lib/active_record/version.rb
index fb1a3716..a8ee7dbe 100644
--- a/vendor/rails/activerecord/lib/active_record/version.rb
+++ b/vendor/rails/activerecord/lib/active_record/version.rb
@@ -1,8 +1,8 @@
module ActiveRecord
module VERSION #:nodoc:
- MAJOR = 1
- MINOR = 15
- TINY = 5
+ MAJOR = 2
+ MINOR = 0
+ TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end
diff --git a/vendor/rails/activerecord/lib/active_record/wrappers/yaml_wrapper.rb b/vendor/rails/activerecord/lib/active_record/wrappers/yaml_wrapper.rb
deleted file mode 100644
index 74f40a50..00000000
--- a/vendor/rails/activerecord/lib/active_record/wrappers/yaml_wrapper.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'yaml'
-
-module ActiveRecord
- module Wrappings #:nodoc:
- class YamlWrapper < AbstractWrapper #:nodoc:
- def wrap(attribute) attribute.to_yaml end
- def unwrap(attribute) YAML::load(attribute) end
- end
-
- module ClassMethods #:nodoc:
- # Wraps the attribute in Yaml encoding
- def wrap_in_yaml(*attributes) wrap_with(YamlWrapper, attributes) end
- end
- end
-end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/lib/active_record/wrappings.rb b/vendor/rails/activerecord/lib/active_record/wrappings.rb
deleted file mode 100644
index e8b60188..00000000
--- a/vendor/rails/activerecord/lib/active_record/wrappings.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-module ActiveRecord
- # A plugin framework for wrapping attribute values before they go in and unwrapping them after they go out of the database.
- # This was intended primarily for YAML wrapping of arrays and hashes, but this behavior is now native in the Base class.
- # So for now this framework is laying dormant until a need pops up.
- module Wrappings #:nodoc:
- module ClassMethods #:nodoc:
- def wrap_with(wrapper, *attributes)
- [ attributes ].flat.each { |attribute| wrapper.wrap(attribute) }
- end
- end
-
- def self.included(base)
- base.extend(ClassMethods)
- end
-
- class AbstractWrapper #:nodoc:
- def self.wrap(attribute, record_binding) #:nodoc:
- %w( before_save after_save after_initialize ).each do |callback|
- eval "#{callback} #{name}.new('#{attribute}')", record_binding
- end
- end
-
- def initialize(attribute) #:nodoc:
- @attribute = attribute
- end
-
- def save_wrapped_attribute(record) #:nodoc:
- if record.attribute_present?(@attribute)
- record.send(
- "write_attribute",
- @attribute,
- wrap(record.send("read_attribute", @attribute))
- )
- end
- end
-
- def load_wrapped_attribute(record) #:nodoc:
- if record.attribute_present?(@attribute)
- record.send(
- "write_attribute",
- @attribute,
- unwrap(record.send("read_attribute", @attribute))
- )
- end
- end
-
- alias_method :before_save, :save_wrapped_attribute #:nodoc:
- alias_method :after_save, :load_wrapped_attribute #:nodoc:
- alias_method :after_initialize, :after_save #:nodoc:
-
- # Overwrite to implement the logic that'll take the regular attribute and wrap it.
- def wrap(attribute) end
-
- # Overwrite to implement the logic that'll take the wrapped attribute and unwrap it.
- def unwrap(attribute) end
- end
- end
-end
diff --git a/vendor/rails/activerecord/lib/activerecord.rb b/vendor/rails/activerecord/lib/activerecord.rb
new file mode 100644
index 00000000..cd62b2af
--- /dev/null
+++ b/vendor/rails/activerecord/lib/activerecord.rb
@@ -0,0 +1 @@
+require 'active_record'
diff --git a/vendor/rails/activerecord/test/aaa_create_tables_test.rb b/vendor/rails/activerecord/test/aaa_create_tables_test.rb
index 2b456c1d..7703c76a 100644
--- a/vendor/rails/activerecord/test/aaa_create_tables_test.rb
+++ b/vendor/rails/activerecord/test/aaa_create_tables_test.rb
@@ -9,7 +9,7 @@ class AAACreateTablesTest < Test::Unit::TestCase
end
def test_drop_and_create_main_tables
- recreate ActiveRecord::Base
+ recreate ActiveRecord::Base unless use_migrations?
assert true
end
@@ -23,11 +23,24 @@ class AAACreateTablesTest < Test::Unit::TestCase
end
def test_drop_and_create_courses_table
- recreate Course, '2'
+ if Course.connection.supports_migrations?
+ eval(File.read("#{File.dirname(__FILE__)}/fixtures/db_definitions/schema2.rb"))
+ end
+ recreate Course, '2' unless use_migrations_for_courses?
assert true
end
private
+ def use_migrations?
+ unittest_sql_filename = ActiveRecord::Base.connection.adapter_name.downcase + ".sql"
+ not File.exist? "#{@base_path}/#{unittest_sql_filename}"
+ end
+
+ def use_migrations_for_courses?
+ unittest2_sql_filename = ActiveRecord::Base.connection.adapter_name.downcase + "2.sql"
+ not File.exist? "#{@base_path}/#{unittest2_sql_filename}"
+ end
+
def recreate(base, suffix = nil)
connection = base.connection
adapter_name = connection.adapter_name.downcase + suffix.to_s
diff --git a/vendor/rails/activerecord/test/abstract_unit.rb b/vendor/rails/activerecord/test/abstract_unit.rb
index 3fd94afc..a09c8daa 100755
--- a/vendor/rails/activerecord/test/abstract_unit.rb
+++ b/vendor/rails/activerecord/test/abstract_unit.rb
@@ -4,8 +4,7 @@ $:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
require 'test/unit'
require 'active_record'
require 'active_record/fixtures'
-require 'active_support/binding_of_caller'
-require 'active_support/breakpoint'
+require 'active_support/test_case'
require 'connection'
# Show backtraces for deprecated behavior for quicker cleanup.
@@ -36,16 +35,10 @@ class Test::Unit::TestCase #:nodoc:
end
def assert_queries(num = 1)
- ActiveRecord::Base.connection.class.class_eval do
- self.query_count = 0
- alias_method :execute, :execute_with_query_counting
- end
+ $query_count = 0
yield
ensure
- ActiveRecord::Base.connection.class.class_eval do
- alias_method :execute, :execute_without_query_counting
- end
- assert_equal num, ActiveRecord::Base.connection.query_count, "#{ActiveRecord::Base.connection.query_count} instead of #{num} queries were executed."
+ assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
end
def assert_no_queries(&block)
@@ -60,18 +53,32 @@ def current_adapter?(*types)
end
end
+def uses_mocha(test_name)
+ require 'rubygems'
+ require 'mocha'
+ yield
+rescue LoadError
+ $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again."
+end
+
ActiveRecord::Base.connection.class.class_eval do
- cattr_accessor :query_count
+ unless defined? IGNORED_SQL
+ IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/]
- # Array of regexes of queries that are not counted against query_count
- @@ignore_list = [/^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/]
+ def execute_with_counting(sql, name = nil, &block)
+ $query_count ||= 0
+ $query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
+ execute_without_counting(sql, name, &block)
+ end
- alias_method :execute_without_query_counting, :execute
- def execute_with_query_counting(sql, name = nil, &block)
- self.query_count += 1 unless @@ignore_list.any? { |r| sql =~ r }
- execute_without_query_counting(sql, name, &block)
+ alias_method_chain :execute, :counting
end
end
+# Make with_scope public for tests
+class << ActiveRecord::Base
+ public :with_scope, :with_exclusive_scope
+end
+
#ActiveRecord::Base.logger = Logger.new(STDOUT)
#ActiveRecord::Base.colorize_logging = false
diff --git a/vendor/rails/activerecord/test/active_schema_mysql.rb b/vendor/rails/activerecord/test/active_schema_mysql.rb
deleted file mode 100644
index d3ee5bfb..00000000
--- a/vendor/rails/activerecord/test/active_schema_mysql.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require 'abstract_unit'
-
-class ActiveSchemaTest < Test::Unit::TestCase
- def setup
- ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
- alias_method :real_execute, :execute
- def execute(sql, name = nil) return sql end
- end
- end
-
- def teardown
- ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:alias_method, :execute, :real_execute)
- end
-
- def test_drop_table
- assert_equal "DROP TABLE people", drop_table(:people)
- end
-
- def test_add_column
- assert_equal "ALTER TABLE people ADD last_name varchar(255)", add_column(:people, :last_name, :string)
- end
-
- def test_add_column_with_limit
- assert_equal "ALTER TABLE people ADD key varchar(32)", add_column(:people, :key, :string, :limit => 32)
- end
-
- private
- def method_missing(method_symbol, *arguments)
- ActiveRecord::Base.connection.send(method_symbol, *arguments)
- end
-end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/active_schema_test_mysql.rb b/vendor/rails/activerecord/test/active_schema_test_mysql.rb
index b7222352..673c86c7 100644
--- a/vendor/rails/activerecord/test/active_schema_test_mysql.rb
+++ b/vendor/rails/activerecord/test/active_schema_test_mysql.rb
@@ -5,27 +5,39 @@ class ActiveSchemaTest < Test::Unit::TestCase
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
alias_method :real_execute, :execute
def execute(sql, name = nil) return sql end
- end
+ end
end
-
+
def teardown
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:alias_method, :execute, :real_execute)
end
def test_drop_table
- assert_equal "DROP TABLE people", drop_table(:people)
+ assert_equal "DROP TABLE `people`", drop_table(:people)
end
-
+
+ if current_adapter?(:MysqlAdapter)
+ def test_create_mysql_database_with_encoding
+ assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
+ assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
+ assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
+ end
+ end
+
def test_add_column
- assert_equal "ALTER TABLE people ADD `last_name` varchar(255)", add_column(:people, :last_name, :string)
+ assert_equal "ALTER TABLE `people` ADD `last_name` varchar(255)", add_column(:people, :last_name, :string)
end
-
+
def test_add_column_with_limit
- assert_equal "ALTER TABLE people ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32)
+ assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32)
+ end
+
+ def test_drop_table_with_specific_database
+ assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people')
end
private
def method_missing(method_symbol, *arguments)
ActiveRecord::Base.connection.send(method_symbol, *arguments)
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activerecord/test/adapter_test.rb b/vendor/rails/activerecord/test/adapter_test.rb
index 0c12c4d3..84af72bf 100644
--- a/vendor/rails/activerecord/test/adapter_test.rb
+++ b/vendor/rails/activerecord/test/adapter_test.rb
@@ -19,7 +19,7 @@ class AdapterTest < Test::Unit::TestCase
def test_indexes
idx_name = "accounts_idx"
-
+
if @connection.respond_to?(:indexes)
indexes = @connection.indexes("accounts")
assert indexes.empty?
@@ -39,24 +39,42 @@ class AdapterTest < Test::Unit::TestCase
ensure
@connection.remove_index(:accounts, :name => idx_name) rescue nil
end
-
+
def test_current_database
if @connection.respond_to?(:current_database)
assert_equal ENV['ARUNIT_DB_NAME'] || "activerecord_unittest", @connection.current_database
end
end
+ if current_adapter?(:MysqlAdapter)
+ def test_charset
+ assert_not_nil @connection.charset
+ assert_not_equal 'character_set_database', @connection.charset
+ assert_equal @connection.show_variable('character_set_database'), @connection.charset
+ end
+
+ def test_collation
+ assert_not_nil @connection.collation
+ assert_not_equal 'collation_database', @connection.collation
+ assert_equal @connection.show_variable('collation_database'), @connection.collation
+ end
+
+ def test_show_nonexistent_variable_returns_nil
+ assert_nil @connection.show_variable('foo_bar_baz')
+ end
+ end
+
def test_table_alias
def @connection.test_table_alias_length() 10; end
class << @connection
alias_method :old_table_alias_length, :table_alias_length
alias_method :table_alias_length, :test_table_alias_length
end
-
+
assert_equal 'posts', @connection.table_alias_for('posts')
assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
assert_equal 'dbo_posts', @connection.table_alias_for('dbo.posts')
-
+
class << @connection
alias_method :table_alias_length, :old_table_alias_length
end
@@ -66,7 +84,7 @@ class AdapterTest < Test::Unit::TestCase
if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
require 'fixtures/movie'
require 'fixtures/subscriber'
-
+
def test_reset_empty_table_with_custom_pk
Movie.delete_all
Movie.connection.reset_pk_sequence! 'movies'
diff --git a/vendor/rails/activerecord/test/adapter_test_sqlserver.rb b/vendor/rails/activerecord/test/adapter_test_sqlserver.rb
index bf746712..548668d5 100644
--- a/vendor/rails/activerecord/test/adapter_test_sqlserver.rb
+++ b/vendor/rails/activerecord/test/adapter_test_sqlserver.rb
@@ -4,6 +4,8 @@ require 'fixtures/post'
require 'fixtures/task'
class SqlServerAdapterTest < Test::Unit::TestCase
+ class TableWithRealColumn < ActiveRecord::Base; end
+
fixtures :posts, :tasks
def setup
@@ -11,7 +13,11 @@ class SqlServerAdapterTest < Test::Unit::TestCase
end
def teardown
- @connection.execute("SET LANGUAGE us_english")
+ @connection.execute("SET LANGUAGE us_english") rescue nil
+ end
+
+ def test_real_column_has_float_type
+ assert_equal :float, TableWithRealColumn.columns_hash["real_number"].type
end
# SQL Server 2000 has a bug where some unambiguous date formats are not
@@ -24,6 +30,14 @@ class SqlServerAdapterTest < Test::Unit::TestCase
end
end
+ def test_indexes_with_descending_order
+ # Make sure we have an index with descending order
+ @connection.execute "CREATE INDEX idx_credit_limit ON accounts (credit_limit DESC)" rescue nil
+ assert_equal ["credit_limit"], @connection.indexes('accounts').first.columns
+ ensure
+ @connection.execute "DROP INDEX accounts.idx_credit_limit"
+ end
+
def test_execute_without_block_closes_statement
assert_all_statements_used_are_closed do
@connection.execute("SELECT 1")
diff --git a/vendor/rails/activerecord/test/aggregations_test.rb b/vendor/rails/activerecord/test/aggregations_test.rb
index 8cd4bfe4..ea0339c6 100644
--- a/vendor/rails/activerecord/test/aggregations_test.rb
+++ b/vendor/rails/activerecord/test/aggregations_test.rb
@@ -20,7 +20,7 @@ class AggregationsTest < Test::Unit::TestCase
def test_change_single_value_object
customers(:david).balance = Money.new(100)
customers(:david).save
- assert_equal 100, Customer.find(1).balance.amount
+ assert_equal 100, customers(:david).reload.balance.amount
end
def test_immutable_value_objects
@@ -92,4 +92,37 @@ class AggregationsTest < Test::Unit::TestCase
def test_nil_raises_error_when_allow_nil_is_false
assert_raises(NoMethodError) { customers(:david).balance = nil }
end
+
+ def test_allow_nil_address_loaded_when_only_some_attributes_are_nil
+ customers(:zaphod).address_street = nil
+ customers(:zaphod).save
+ customers(:zaphod).reload
+ assert_kind_of Address, customers(:zaphod).address
+ assert customers(:zaphod).address.street.nil?
+ end
+
+ def test_nil_assignment_results_in_nil
+ customers(:david).gps_location = GpsLocation.new('39x111')
+ assert_not_equal nil, customers(:david).gps_location
+ customers(:david).gps_location = nil
+ assert_equal nil, customers(:david).gps_location
+ end
+end
+
+class OverridingAggregationsTest < Test::Unit::TestCase
+ class Name; end
+ class DifferentName; end
+
+ class Person < ActiveRecord::Base
+ composed_of :composed_of, :mapping => %w(person_first_name first_name)
+ end
+
+ class DifferentPerson < Person
+ composed_of :composed_of, :class_name => 'DifferentName', :mapping => %w(different_person_first_name first_name)
+ end
+
+ def test_composed_of_aggregation_redefinition_reflections_should_differ_and_not_inherited
+ assert_not_equal Person.reflect_on_aggregation(:composed_of),
+ DifferentPerson.reflect_on_aggregation(:composed_of)
+ end
end
diff --git a/vendor/rails/activerecord/test/all.sh b/vendor/rails/activerecord/test/all.sh
index a6712cc4..8abd1562 100755
--- a/vendor/rails/activerecord/test/all.sh
+++ b/vendor/rails/activerecord/test/all.sh
@@ -1,8 +1,8 @@
#!/bin/sh
if [ -z "$1" ]; then
- echo "Usage: $0 connections/" 1>&2
+ echo "Usage: $0 " 1>&2
exit 1
fi
-ruby -I $1 -e 'Dir.foreach(".") { |file| require file if file =~ /_test.rb$/ }'
+ruby -I connections/native_$1 -e 'Dir["**/*_test.rb"].each { |path| require path }'
diff --git a/vendor/rails/activerecord/test/association_callbacks_test.rb b/vendor/rails/activerecord/test/association_callbacks_test.rb
deleted file mode 100644
index 1426fb71..00000000
--- a/vendor/rails/activerecord/test/association_callbacks_test.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-require 'abstract_unit'
-require 'fixtures/post'
-require 'fixtures/comment'
-require 'fixtures/author'
-require 'fixtures/category'
-require 'fixtures/project'
-require 'fixtures/developer'
-
-class AssociationCallbacksTest < Test::Unit::TestCase
- fixtures :posts, :authors, :projects, :developers
-
- def setup
- @david = authors(:david)
- @thinking = posts(:thinking)
- @authorless = posts(:authorless)
- assert @david.post_log.empty?
- end
-
- def test_adding_macro_callbacks
- @david.posts_with_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
- @david.posts_with_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
- "after_adding#{@thinking.id}"], @david.post_log
- end
-
- def test_adding_with_proc_callbacks
- @david.posts_with_proc_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
- @david.posts_with_proc_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
- "after_adding#{@thinking.id}"], @david.post_log
- end
-
- def test_removing_with_macro_callbacks
- first_post, second_post = @david.posts_with_callbacks[0, 2]
- @david.posts_with_callbacks.delete(first_post)
- assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
- @david.posts_with_callbacks.delete(second_post)
- assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
- "after_removing#{second_post.id}"], @david.post_log
- end
-
- def test_removing_with_proc_callbacks
- first_post, second_post = @david.posts_with_callbacks[0, 2]
- @david.posts_with_proc_callbacks.delete(first_post)
- assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
- @david.posts_with_proc_callbacks.delete(second_post)
- assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
- "after_removing#{second_post.id}"], @david.post_log
- end
-
- def test_multiple_callbacks
- @david.posts_with_multiple_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
- "after_adding_proc#{@thinking.id}"], @david.post_log
- @david.posts_with_multiple_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
- "after_adding_proc#{@thinking.id}", "before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}",
- "after_adding#{@thinking.id}", "after_adding_proc#{@thinking.id}"], @david.post_log
- end
-
- def test_has_and_belongs_to_many_add_callback
- david = developers(:david)
- ar = projects(:active_record)
- assert ar.developers_log.empty?
- ar.developers_with_callbacks << david
- assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], ar.developers_log
- ar.developers_with_callbacks << david
- assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
- "after_adding#{david.id}"], ar.developers_log
- end
-
- def test_has_and_belongs_to_many_remove_callback
- david = developers(:david)
- jamis = developers(:jamis)
- activerecord = projects(:active_record)
- assert activerecord.developers_log.empty?
- activerecord.developers_with_callbacks.delete(david)
- assert_equal ["before_removing#{david.id}", "after_removing#{david.id}"], activerecord.developers_log
-
- activerecord.developers_with_callbacks.delete(jamis)
- assert_equal ["before_removing#{david.id}", "after_removing#{david.id}", "before_removing#{jamis.id}",
- "after_removing#{jamis.id}"], activerecord.developers_log
- end
-
- def test_has_and_belongs_to_many_remove_callback_on_clear
- activerecord = projects(:active_record)
- assert activerecord.developers_log.empty?
- if activerecord.developers_with_callbacks.size == 0
- activerecord.developers << developers(:david)
- activerecord.developers << developers(:jamis)
- activerecord.reload
- assert activerecord.developers_with_callbacks.size == 2
- end
- log_array = activerecord.developers_with_callbacks.collect {|d| ["before_removing#{d.id}","after_removing#{d.id}"]}.flatten.sort
- assert activerecord.developers_with_callbacks.clear
- assert_equal log_array, activerecord.developers_log.sort
- end
-
- def test_dont_add_if_before_callback_raises_exception
- assert !@david.unchangable_posts.include?(@authorless)
- begin
- @david.unchangable_posts << @authorless
- rescue Exception => e
- end
- assert @david.post_log.empty?
- assert !@david.unchangable_posts.include?(@authorless)
- @david.reload
- assert !@david.unchangable_posts.include?(@authorless)
- end
-
- def test_push_with_attributes
- david = developers(:david)
- activerecord = projects(:active_record)
- assert activerecord.developers_log.empty?
- activerecord.developers_with_callbacks.push_with_attributes(david, {})
- assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], activerecord.developers_log
- activerecord.developers_with_callbacks.push_with_attributes(david, {})
- assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
- "after_adding#{david.id}"], activerecord.developers_log
- end
-end
-
diff --git a/vendor/rails/activerecord/test/associations/callbacks_test.rb b/vendor/rails/activerecord/test/associations/callbacks_test.rb
index d0f7fa67..4b6b7f41 100644
--- a/vendor/rails/activerecord/test/associations/callbacks_test.rb
+++ b/vendor/rails/activerecord/test/associations/callbacks_test.rb
@@ -15,29 +15,29 @@ class AssociationCallbacksTest < Test::Unit::TestCase
@authorless = posts(:authorless)
assert @david.post_log.empty?
end
-
+
def test_adding_macro_callbacks
@david.posts_with_callbacks << @thinking
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
@david.posts_with_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
+ assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
"after_adding#{@thinking.id}"], @david.post_log
end
-
+
def test_adding_with_proc_callbacks
@david.posts_with_proc_callbacks << @thinking
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
@david.posts_with_proc_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
+ assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
"after_adding#{@thinking.id}"], @david.post_log
end
-
+
def test_removing_with_macro_callbacks
first_post, second_post = @david.posts_with_callbacks[0, 2]
@david.posts_with_callbacks.delete(first_post)
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
@david.posts_with_callbacks.delete(second_post)
- assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
+ assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
"after_removing#{second_post.id}"], @david.post_log
end
@@ -46,20 +46,43 @@ class AssociationCallbacksTest < Test::Unit::TestCase
@david.posts_with_proc_callbacks.delete(first_post)
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
@david.posts_with_proc_callbacks.delete(second_post)
- assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
+ assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
"after_removing#{second_post.id}"], @david.post_log
end
-
+
def test_multiple_callbacks
@david.posts_with_multiple_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
+ assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
"after_adding_proc#{@thinking.id}"], @david.post_log
@david.posts_with_multiple_callbacks << @thinking
- assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
- "after_adding_proc#{@thinking.id}", "before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}",
+ assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
+ "after_adding_proc#{@thinking.id}", "before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}",
"after_adding#{@thinking.id}", "after_adding_proc#{@thinking.id}"], @david.post_log
end
-
+
+ def test_has_many_callbacks_with_create
+ morten = Author.create :name => "Morten"
+ post = morten.posts_with_proc_callbacks.create! :title => "Hello", :body => "How are you doing?"
+ assert_equal ["before_adding", "after_adding#{post.id}"], morten.post_log
+ end
+
+ def test_has_many_callbacks_with_create!
+ morten = Author.create! :name => "Morten"
+ post = morten.posts_with_proc_callbacks.create :title => "Hello", :body => "How are you doing?"
+ assert_equal ["before_adding", "after_adding#{post.id}"], morten.post_log
+ end
+
+ def test_has_many_callbacks_for_save_on_parent
+ jack = Author.new :name => "Jack"
+ post = jack.posts_with_callbacks.build :title => "Call me back!", :body => "Before you wake up and after you sleep"
+
+ callback_log = ["before_adding", "after_adding#{jack.posts_with_callbacks.first.id}"]
+ assert_equal callback_log, jack.post_log
+ assert jack.save
+ assert_equal 1, jack.posts_with_callbacks.count
+ assert_equal callback_log, jack.post_log
+ end
+
def test_has_and_belongs_to_many_add_callback
david = developers(:david)
ar = projects(:active_record)
@@ -67,10 +90,10 @@ class AssociationCallbacksTest < Test::Unit::TestCase
ar.developers_with_callbacks << david
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], ar.developers_log
ar.developers_with_callbacks << david
- assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
+ assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
"after_adding#{david.id}"], ar.developers_log
end
-
+
def test_has_and_belongs_to_many_remove_callback
david = developers(:david)
jamis = developers(:jamis)
@@ -78,9 +101,9 @@ class AssociationCallbacksTest < Test::Unit::TestCase
assert activerecord.developers_log.empty?
activerecord.developers_with_callbacks.delete(david)
assert_equal ["before_removing#{david.id}", "after_removing#{david.id}"], activerecord.developers_log
-
+
activerecord.developers_with_callbacks.delete(jamis)
- assert_equal ["before_removing#{david.id}", "after_removing#{david.id}", "before_removing#{jamis.id}",
+ assert_equal ["before_removing#{david.id}", "after_removing#{david.id}", "before_removing#{jamis.id}",
"after_removing#{jamis.id}"], activerecord.developers_log
end
@@ -97,7 +120,18 @@ class AssociationCallbacksTest < Test::Unit::TestCase
assert activerecord.developers_with_callbacks.clear
assert_equal log_array, activerecord.developers_log.sort
end
-
+
+ def test_has_many_and_belongs_to_many_callbacks_for_save_on_parent
+ project = Project.new :name => "Callbacks"
+ project.developers_with_callbacks.build :name => "Jack", :salary => 95000
+
+ callback_log = ["before_adding", "after_adding"]
+ assert_equal callback_log, project.developers_log
+ assert project.save
+ assert_equal 1, project.developers_with_callbacks.count
+ assert_equal callback_log, project.developers_log
+ end
+
def test_dont_add_if_before_callback_raises_exception
assert !@david.unchangable_posts.include?(@authorless)
begin
@@ -109,18 +143,5 @@ class AssociationCallbacksTest < Test::Unit::TestCase
@david.reload
assert !@david.unchangable_posts.include?(@authorless)
end
-
- def test_push_with_attributes
- assert_deprecated 'push_with_attributes' do
- david = developers(:david)
- activerecord = projects(:active_record)
- assert activerecord.developers_log.empty?
- activerecord.developers_with_callbacks.push_with_attributes(david, {})
- assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], activerecord.developers_log
- activerecord.developers_with_callbacks.push_with_attributes(david, {})
- assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
- "after_adding#{david.id}"], activerecord.developers_log
- end
- end
end
diff --git a/vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb b/vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb
index 863af199..6361d584 100644
--- a/vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb
+++ b/vendor/rails/activerecord/test/associations/cascaded_eager_loading_test.rb
@@ -1,11 +1,9 @@
require 'abstract_unit'
-require 'active_record/acts/list'
require 'fixtures/post'
require 'fixtures/comment'
require 'fixtures/author'
require 'fixtures/category'
require 'fixtures/categorization'
-require 'fixtures/mixin'
require 'fixtures/company'
require 'fixtures/topic'
require 'fixtures/reply'
@@ -53,16 +51,6 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
assert_equal 5, authors[0].posts.size
end
- def test_eager_association_loading_with_acts_as_tree
- roots = TreeMixin.find(:all, :include=>"children", :conditions=>"mixins.parent_id IS NULL", :order=>"mixins.id")
- assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], roots
- assert_no_queries do
- assert_equal 2, roots[0].children.size
- assert_equal 0, roots[1].children.size
- assert_equal 0, roots[2].children.size
- end
- end
-
def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
assert_equal 2, firms.size
@@ -73,7 +61,7 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
def test_eager_association_loading_with_has_many_sti
topics = Topic.find(:all, :include => :replies, :order => 'topics.id')
- assert_equal [topics(:first), topics(:second)], topics
+ assert_equal topics(:first, :second), topics
assert_no_queries do
assert_equal 1, topics[0].replies.size
assert_equal 0, topics[1].replies.size
@@ -103,24 +91,8 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
authors.first.posts.first.special_comments.first.post.very_special_comment
end
end
-
- def test_eager_association_loading_with_recursive_cascading_three_levels_has_many
- root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:children=>{:children=>:children}}, :order => 'mixins.id')
- assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.children.first.children.first.children.first }
- end
-
- def test_eager_association_loading_with_recursive_cascading_three_levels_has_one
- root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:first_child=>{:first_child=>:first_child}}, :order => 'mixins.id')
- assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.first_child.first_child.first_child }
- end
-
- def test_eager_association_loading_with_recursive_cascading_three_levels_belongs_to
- leaf_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:parent=>{:parent=>:parent}}, :order => 'mixins.id DESC')
- assert_equal mixins(:recursively_cascaded_tree_1), assert_no_queries { leaf_node.parent.parent.parent }
- end
end
-
require 'fixtures/vertex'
require 'fixtures/edge'
class CascadedEagerLoadingTest < Test::Unit::TestCase
diff --git a/vendor/rails/activerecord/test/associations/eager_singularization_test.rb b/vendor/rails/activerecord/test/associations/eager_singularization_test.rb
new file mode 100644
index 00000000..72164c4f
--- /dev/null
+++ b/vendor/rails/activerecord/test/associations/eager_singularization_test.rb
@@ -0,0 +1,145 @@
+require 'abstract_unit'
+
+class Virus < ActiveRecord::Base
+ belongs_to :octopus
+end
+class Octopus < ActiveRecord::Base
+ has_one :virus
+end
+class Pass < ActiveRecord::Base
+ belongs_to :bus
+end
+class Bus < ActiveRecord::Base
+ has_many :passes
+end
+class Mess < ActiveRecord::Base
+ has_and_belongs_to_many :crises
+end
+class Crisis < ActiveRecord::Base
+ has_and_belongs_to_many :messes
+ has_many :analyses, :dependent => :destroy
+ has_many :successes, :through => :analyses
+ has_many :dresses, :dependent => :destroy
+ has_many :compresses, :through => :dresses
+end
+class Analysis < ActiveRecord::Base
+ belongs_to :crisis
+ belongs_to :success
+end
+class Success < ActiveRecord::Base
+ has_many :analyses, :dependent => :destroy
+ has_many :crises, :through => :analyses
+end
+class Dress < ActiveRecord::Base
+ belongs_to :crisis
+ has_many :compresses
+end
+class Compress < ActiveRecord::Base
+ belongs_to :dress
+end
+
+
+class EagerSingularizationTest < Test::Unit::TestCase
+
+ def setup
+ if ActiveRecord::Base.connection.supports_migrations?
+ ActiveRecord::Base.connection.create_table :viri do |t|
+ t.column :octopus_id, :integer
+ t.column :species, :string
+ end
+ ActiveRecord::Base.connection.create_table :octopi do |t|
+ t.column :species, :string
+ end
+ ActiveRecord::Base.connection.create_table :passes do |t|
+ t.column :bus_id, :integer
+ t.column :rides, :integer
+ end
+ ActiveRecord::Base.connection.create_table :buses do |t|
+ t.column :name, :string
+ end
+ ActiveRecord::Base.connection.create_table :crises_messes, :id => false do |t|
+ t.column :crisis_id, :integer
+ t.column :mess_id, :integer
+ end
+ ActiveRecord::Base.connection.create_table :messes do |t|
+ t.column :name, :string
+ end
+ ActiveRecord::Base.connection.create_table :crises do |t|
+ t.column :name, :string
+ end
+ ActiveRecord::Base.connection.create_table :successes do |t|
+ t.column :name, :string
+ end
+ ActiveRecord::Base.connection.create_table :analyses do |t|
+ t.column :crisis_id, :integer
+ t.column :success_id, :integer
+ end
+ ActiveRecord::Base.connection.create_table :dresses do |t|
+ t.column :crisis_id, :integer
+ end
+ ActiveRecord::Base.connection.create_table :compresses do |t|
+ t.column :dress_id, :integer
+ end
+ @have_tables = true
+ else
+ @have_tables = false
+ end
+ end
+
+ def teardown
+ ActiveRecord::Base.connection.drop_table :viri
+ ActiveRecord::Base.connection.drop_table :octopi
+ ActiveRecord::Base.connection.drop_table :passes
+ ActiveRecord::Base.connection.drop_table :buses
+ ActiveRecord::Base.connection.drop_table :crises_messes
+ ActiveRecord::Base.connection.drop_table :messes
+ ActiveRecord::Base.connection.drop_table :crises
+ ActiveRecord::Base.connection.drop_table :successes
+ ActiveRecord::Base.connection.drop_table :analyses
+ ActiveRecord::Base.connection.drop_table :dresses
+ ActiveRecord::Base.connection.drop_table :compresses
+ end
+
+ def test_eager_no_extra_singularization_belongs_to
+ return unless @have_tables
+ assert_nothing_raised do
+ Virus.find(:all, :include => :octopus)
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_one
+ return unless @have_tables
+ assert_nothing_raised do
+ Octopus.find(:all, :include => :virus)
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_many
+ return unless @have_tables
+ assert_nothing_raised do
+ Bus.find(:all, :include => :passes)
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_and_belongs_to_many
+ return unless @have_tables
+ assert_nothing_raised do
+ Crisis.find(:all, :include => :messes)
+ Mess.find(:all, :include => :crises)
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_many_through_belongs_to
+ return unless @have_tables
+ assert_nothing_raised do
+ Crisis.find(:all, :include => :successes)
+ end
+ end
+
+ def test_eager_no_extra_singularization_has_many_through_has_many
+ return unless @have_tables
+ assert_nothing_raised do
+ Crisis.find(:all, :include => :compresses)
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/test/associations/eager_test.rb b/vendor/rails/activerecord/test/associations/eager_test.rb
index 6c054e9e..4fc2e628 100644
--- a/vendor/rails/activerecord/test/associations/eager_test.rb
+++ b/vendor/rails/activerecord/test/associations/eager_test.rb
@@ -37,6 +37,18 @@ class EagerAssociationTest < Test::Unit::TestCase
end
end
+ def test_with_two_tables_in_from_without_getting_double_quoted
+ posts = Post.find(:all,
+ :select => "posts.*",
+ :from => "authors, posts",
+ :include => :comments,
+ :conditions => "posts.author_id = authors.id",
+ :order => "posts.id"
+ )
+
+ assert_equal 2, posts.first.comments.size
+ end
+
def test_loading_with_multiple_associations
posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
assert_equal 2, posts.first.comments.size
@@ -103,6 +115,11 @@ class EagerAssociationTest < Test::Unit::TestCase
assert_equal [2], posts.collect { |p| p.id }
end
+ def test_eager_association_loading_with_explicit_join
+ posts = Post.find(:all, :include => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id')
+ assert_equal 1, posts.length
+ end
+
def test_eager_with_has_many_through
posts_with_comments = people(:michael).posts.find(:all, :include => :comments)
posts_with_author = people(:michael).posts.find(:all, :include => :author )
@@ -135,13 +152,21 @@ class EagerAssociationTest < Test::Unit::TestCase
end
def test_eager_with_has_many_and_limit_and_conditions
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
+ if current_adapter?(:OpenBaseAdapter)
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "FETCHBLOB(posts.body) = 'hello'", :order => "posts.id")
+ else
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
+ end
assert_equal 2, posts.size
assert_equal [4,5], posts.collect { |p| p.id }
end
def test_eager_with_has_many_and_limit_and_conditions_array
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
+ if current_adapter?(:OpenBaseAdapter)
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id")
+ else
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
+ end
assert_equal 2, posts.size
assert_equal [4,5], posts.collect { |p| p.id }
end
@@ -237,6 +262,15 @@ class EagerAssociationTest < Test::Unit::TestCase
assert_equal count, posts.size
end
end
+
+ def test_eager_with_scoped_order_using_association_limiting_without_explicit_scope
+ posts_with_explicit_order = Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :order => 'posts.id DESC', :limit => 2)
+ posts_with_scoped_order = Post.with_scope(:find => {:order => 'posts.id DESC'}) do
+ Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :limit => 2)
+ end
+ assert_equal posts_with_explicit_order, posts_with_scoped_order
+ end
+
def test_eager_association_loading_with_habtm
posts = Post.find(:all, :include => :categories, :order => "posts.id")
assert_equal 2, posts[0].categories.size
@@ -305,13 +339,13 @@ class EagerAssociationTest < Test::Unit::TestCase
end
def test_limited_eager_with_order
- assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
- assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
+ assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
+ assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
end
def test_limited_eager_with_multiple_order_columns
- assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
- assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
+ assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
+ assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
end
def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
@@ -399,6 +433,8 @@ class EagerAssociationTest < Test::Unit::TestCase
def test_count_with_include
if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
+ elsif current_adapter?(:OpenBaseAdapter)
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15")
else
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
end
diff --git a/vendor/rails/activerecord/test/associations/extension_test.rb b/vendor/rails/activerecord/test/associations/extension_test.rb
index e80a2b9f..67a9ee02 100644
--- a/vendor/rails/activerecord/test/associations/extension_test.rb
+++ b/vendor/rails/activerecord/test/associations/extension_test.rb
@@ -23,7 +23,12 @@ class AssociationsExtensionsTest < Test::Unit::TestCase
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_twice.find_most_recent
assert_equal projects(:active_record), developers(:david).projects_extended_by_name_twice.find_least_recent
end
-
+
+ def test_named_extension_and_block_on_habtm
+ assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_and_block.find_most_recent
+ assert_equal projects(:active_record), developers(:david).projects_extended_by_name_and_block.find_least_recent
+ end
+
def test_marshalling_extensions
david = developers(:david)
assert_equal projects(:action_controller), david.projects.find_most_recent
diff --git a/vendor/rails/activerecord/test/associations/inner_join_association_test.rb b/vendor/rails/activerecord/test/associations/inner_join_association_test.rb
new file mode 100644
index 00000000..8d583f21
--- /dev/null
+++ b/vendor/rails/activerecord/test/associations/inner_join_association_test.rb
@@ -0,0 +1,88 @@
+require 'abstract_unit'
+require 'fixtures/post'
+require 'fixtures/comment'
+require 'fixtures/author'
+require 'fixtures/category'
+require 'fixtures/categorization'
+
+class InnerJoinAssociationTest < Test::Unit::TestCase
+ fixtures :authors, :posts, :comments, :categories, :categories_posts, :categorizations
+
+ def test_construct_finder_sql_creates_inner_joins
+ sql = Author.send(:construct_finder_sql, :joins => :posts)
+ assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
+ end
+
+ def test_construct_finder_sql_cascades_inner_joins
+ sql = Author.send(:construct_finder_sql, :joins => {:posts => :comments})
+ assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
+ assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = posts.id/, sql
+ end
+
+ def test_construct_finder_sql_inner_joins_through_associations
+ sql = Author.send(:construct_finder_sql, :joins => :categorized_posts)
+ assert_match /INNER JOIN `?categorizations`?.*INNER JOIN `?posts`?/, sql
+ end
+
+ def test_construct_finder_sql_applies_association_conditions
+ sql = Author.send(:construct_finder_sql, :joins => :categories_like_general, :conditions => "TERMINATING_MARKER")
+ assert_match /INNER JOIN `?categories`? ON.*AND.*`?General`?.*TERMINATING_MARKER/, sql
+ end
+
+ def test_construct_finder_sql_unpacks_nested_joins
+ sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]})
+ assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present"
+ assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
+ assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = `?posts`?.id/, sql
+ end
+
+ def test_construct_finder_sql_ignores_empty_joins_hash
+ sql = Author.send(:construct_finder_sql, :joins => {})
+ assert_no_match /JOIN/i, sql
+ end
+
+ def test_construct_finder_sql_ignores_empty_joins_array
+ sql = Author.send(:construct_finder_sql, :joins => [])
+ assert_no_match /JOIN/i, sql
+ end
+
+ def test_find_with_implicit_inner_joins_honors_readonly_without_select
+ authors = Author.find(:all, :joins => :posts)
+ assert !authors.empty?, "expected authors to be non-empty"
+ assert authors.all? {|a| a.readonly? }, "expected all authors to be readonly"
+ end
+
+ def test_find_with_implicit_inner_joins_honors_readonly_with_select
+ authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
+ assert !authors.empty?, "expected authors to be non-empty"
+ assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+ end
+
+ def test_find_with_implicit_inner_joins_honors_readonly_false
+ authors = Author.find(:all, :joins => :posts, :readonly => false)
+ assert !authors.empty?, "expected authors to be non-empty"
+ assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
+ end
+
+ def test_find_with_implicit_inner_joins_does_not_set_associations
+ authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
+ assert !authors.empty?, "expected authors to be non-empty"
+ assert authors.all? {|a| !a.send(:instance_variables).include?("@posts")}, "expected no authors to have the @posts association loaded"
+ end
+
+ def test_count_honors_implicit_inner_joins
+ real_count = Author.find(:all).sum{|a| a.posts.count }
+ assert_equal real_count, Author.count(:joins => :posts), "plain inner join count should match the number of referenced posts records"
+ end
+
+ def test_calculate_honors_implicit_inner_joins
+ real_count = Author.find(:all).sum{|a| a.posts.count }
+ assert_equal real_count, Author.calculate(:count, 'authors.id', :joins => :posts), "plain inner join count should match the number of referenced posts records"
+ end
+
+ def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
+ real_count = Author.find(:all).select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
+ authors_with_welcoming_post_titles = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true, :conditions => "posts.title like 'Welcome%'")
+ assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
+ end
+end
diff --git a/vendor/rails/activerecord/test/associations/join_model_test.rb b/vendor/rails/activerecord/test/associations/join_model_test.rb
index 770b8038..aaef40eb 100644
--- a/vendor/rails/activerecord/test/associations/join_model_test.rb
+++ b/vendor/rails/activerecord/test/associations/join_model_test.rb
@@ -2,16 +2,19 @@ require 'abstract_unit'
require 'fixtures/tag'
require 'fixtures/tagging'
require 'fixtures/post'
+require 'fixtures/item'
require 'fixtures/comment'
require 'fixtures/author'
require 'fixtures/category'
require 'fixtures/categorization'
require 'fixtures/vertex'
require 'fixtures/edge'
+require 'fixtures/book'
+require 'fixtures/citation'
class AssociationsJoinModelTest < Test::Unit::TestCase
self.use_transactional_fixtures = false
- fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices
+ fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :items, :books
def test_has_many
assert authors(:david).categories.include?(categories(:general))
@@ -34,8 +37,8 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
author = authors(:mary)
assert !authors(:mary).unique_categorized_posts.loaded?
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
- assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title, {}) }
- assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, { :conditions => "title is NULL" }) }
+ assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title) }
+ assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, :conditions => "title is NULL") }
assert !authors(:mary).unique_categorized_posts.loaded?
end
@@ -238,7 +241,15 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
assert_equal tagging, post.tagging
end
end
-
+
+ def test_include_polymorphic_has_one_defined_in_abstract_parent
+ item = Item.find_by_id(items(:dvd).id, :include => :tagging)
+ tagging = taggings(:godfather)
+ assert_no_queries do
+ assert_equal tagging, item.tagging
+ end
+ end
+
def test_include_polymorphic_has_many_through
posts = Post.find(:all, :order => 'posts.id')
posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
@@ -273,10 +284,10 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
assert_equal nil, authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
end
-
+
def test_has_many_class_methods_called_by_method_missing
assert_equal categories(:general), authors(:david).categories.find_all_by_name('General').first
-# assert_equal nil, authors(:david).categories.find_by_name('Technology')
+ assert_equal nil, authors(:david).categories.find_by_name('Technology')
end
def test_has_many_going_through_join_model_with_custom_foreign_key
@@ -303,20 +314,20 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
def test_has_many_polymorphic
assert_raises ActiveRecord::HasManyThroughAssociationPolymorphicError do
- assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggables
+ assert_equal posts(:welcome, :thinking), tags(:general).taggables
end
assert_raises ActiveRecord::EagerLoadPolymorphicError do
- assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggings.find(:all, :include => :taggable)
+ assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable)
end
end
def test_has_many_polymorphic_with_source_type
- assert_equal [posts(:welcome), posts(:thinking)], tags(:general).tagged_posts
+ assert_equal posts(:welcome, :thinking), tags(:general).tagged_posts
end
def test_eager_has_many_polymorphic_with_source_type
tag_with_include = Tag.find(tags(:general).id, :include => :tagged_posts)
- desired = [posts(:welcome), posts(:thinking)]
+ desired = posts(:welcome, :thinking)
assert_no_queries do
assert_equal desired, tag_with_include.tagged_posts
end
@@ -348,12 +359,12 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
end
def test_has_many_through_polymorphic_has_many
- assert_equal [taggings(:welcome_general), taggings(:thinking_general)], authors(:david).taggings.uniq.sort_by { |t| t.id }
+ assert_equal taggings(:welcome_general, :thinking_general), authors(:david).taggings.uniq.sort_by { |t| t.id }
end
def test_include_has_many_through_polymorphic_has_many
author = Author.find_by_id(authors(:david).id, :include => :taggings)
- expected_taggings = [taggings(:welcome_general), taggings(:thinking_general)]
+ expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
end
@@ -399,6 +410,12 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
authors(:david).author_favorites.create :favorite_author => new_author
assert_equal new_author, authors(:david).reload.favorite_authors.first
end
+
+ def test_has_many_through_uses_conditions_specified_on_the_has_many_association
+ author = Author.find(:first)
+ assert !author.comments.blank?
+ assert author.nonexistant_comments.blank?
+ end
def test_has_many_through_uses_correct_attributes
assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
@@ -408,6 +425,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags << tags(:general).clone }
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).clone.tags << tags(:general) }
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.build }
+ assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.new }
end
def test_create_associate_when_adding_to_has_many_through
@@ -422,7 +440,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
assert_equal(count + 1, post_thinking.tags.size)
assert_equal(count + 1, post_thinking.tags(true).size)
- assert_nothing_raised { post_thinking.tags.create!(:name => 'foo') }
+ assert_kind_of Tag, post_thinking.tags.create!(:name => 'foo')
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
message = "Expected a Tag in tags collection, got #{wrong.class}.")
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
@@ -442,6 +460,21 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
assert_nothing_raised { vertices(:vertex_1).sinks << vertices(:vertex_5) }
end
+ def test_has_many_through_collection_size_doesnt_load_target_if_not_loaded
+ author = authors(:david)
+ assert_equal 9, author.comments.size
+ assert !author.comments.loaded?
+ end
+
+ uses_mocha('has_many_through_collection_size_uses_counter_cache_if_it_exists') do
+ def test_has_many_through_collection_size_uses_counter_cache_if_it_exists
+ author = authors(:david)
+ author.stubs(:read_attribute).with('comments_count').returns(100)
+ assert_equal 100, author.comments.size
+ assert !author.comments.loaded?
+ end
+ end
+
def test_adding_junk_to_has_many_through_should_raise_type_mismatch
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags << "Uhh what now?" }
end
@@ -451,6 +484,20 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
assert_equal tags, posts(:thinking).tags.push(tags(:general))
end
+ def test_delete_associate_when_deleting_from_has_many_through_with_nonstandard_id
+ count = books(:awdr).references.count
+ references_before = books(:awdr).references
+ book = Book.create!(:name => 'Getting Real')
+ book_awdr = books(:awdr)
+ book_awdr.references << book
+ assert_equal(count + 1, book_awdr.references(true).size)
+
+ assert_nothing_raised { book_awdr.references.delete(book) }
+ assert_equal(count, book_awdr.references.size)
+ assert_equal(count, book_awdr.references(true).size)
+ assert_equal(references_before.sort, book_awdr.references.sort)
+ end
+
def test_delete_associate_when_deleting_from_has_many_through
count = posts(:thinking).tags.count
tags_before = posts(:thinking).tags
@@ -492,6 +539,12 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
def test_has_many_through_has_many_with_sti
assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
end
+
+ def test_uniq_has_many_through_should_retain_order
+ comment_ids = authors(:david).comments.map(&:id)
+ assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id)
+ assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
+ end
private
# create dynamic Post models to allow different dependency options
diff --git a/vendor/rails/activerecord/test/associations_cascaded_eager_loading_test.rb b/vendor/rails/activerecord/test/associations_cascaded_eager_loading_test.rb
deleted file mode 100644
index dd12d529..00000000
--- a/vendor/rails/activerecord/test/associations_cascaded_eager_loading_test.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-require 'abstract_unit'
-require 'active_record/acts/list'
-require 'fixtures/post'
-require 'fixtures/comment'
-require 'fixtures/author'
-require 'fixtures/category'
-require 'fixtures/categorization'
-require 'fixtures/mixin'
-require 'fixtures/company'
-require 'fixtures/topic'
-require 'fixtures/reply'
-
-class CascadedEagerLoadingTest < Test::Unit::TestCase
- fixtures :authors, :mixins, :companies, :posts, :categorizations, :topics
-
- def test_eager_association_loading_with_cascaded_two_levels
- authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
- assert_equal 2, authors.size
- assert_equal 5, authors[0].posts.size
- assert_equal 1, authors[1].posts.size
- assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
- end
-
- def test_eager_association_loading_with_cascaded_two_levels_and_one_level
- authors = Author.find(:all, :include=>[{:posts=>:comments}, :categorizations], :order=>"authors.id")
- assert_equal 2, authors.size
- assert_equal 5, authors[0].posts.size
- assert_equal 1, authors[1].posts.size
- assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
- assert_equal 1, authors[0].categorizations.size
- assert_equal 1, authors[1].categorizations.size
- end
-
- def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
- authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id")
- assert_equal 2, authors.size
- assert_equal 5, authors[0].posts.size
- assert_equal 1, authors[1].posts.size
- assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
- end
-
- def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
- authors = Author.find(:all, :include=>{:posts=>[:comments, :author]}, :order=>"authors.id")
- assert_equal 2, authors.size
- assert_equal 5, authors[0].posts.size
- assert_equal authors(:david).name, authors[0].name
- assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq
- end
-
- def test_eager_association_loading_with_cascaded_two_levels_with_condition
- authors = Author.find(:all, :include=>{:posts=>:comments}, :conditions=>"authors.id=1", :order=>"authors.id")
- assert_equal 1, authors.size
- assert_equal 5, authors[0].posts.size
- end
-
- def test_eager_association_loading_with_acts_as_tree
- roots = TreeMixin.find(:all, :include=>"children", :conditions=>"mixins.parent_id IS NULL", :order=>"mixins.id")
- assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], roots
- assert_no_queries do
- assert_equal 2, roots[0].children.size
- assert_equal 0, roots[1].children.size
- assert_equal 0, roots[2].children.size
- end
- end
-
- def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
- firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
- assert_equal 2, firms.size
- assert_equal firms.first.account, firms.first.account.firm.account
- assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
- assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account }
- end
-
- def test_eager_association_loading_with_has_many_sti
- topics = Topic.find(:all, :include => :replies, :order => 'topics.id')
- assert_equal [topics(:first), topics(:second)], topics
- assert_no_queries do
- assert_equal 1, topics[0].replies.size
- assert_equal 0, topics[1].replies.size
- end
- end
-
- def test_eager_association_loading_with_belongs_to_sti
- replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
- assert_equal [topics(:second)], replies
- assert_equal topics(:first), assert_no_queries { replies.first.topic }
- end
-
- def test_eager_association_loading_with_multiple_stis_and_order
- author = Author.find(:first, :include => { :posts => [ :special_comments , :very_special_comment ] }, :order => 'authors.name, comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
- assert_equal authors(:david), author
- assert_no_queries do
- author.posts.first.special_comments
- author.posts.first.very_special_comment
- end
- end
-
- def test_eager_association_loading_of_stis_with_multiple_references
- authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
- assert_equal [authors(:david)], authors
- assert_no_queries do
- authors.first.posts.first.special_comments.first.post.special_comments
- authors.first.posts.first.special_comments.first.post.very_special_comment
- end
- end
-end
diff --git a/vendor/rails/activerecord/test/associations_extensions_test.rb b/vendor/rails/activerecord/test/associations_extensions_test.rb
deleted file mode 100644
index 915056c5..00000000
--- a/vendor/rails/activerecord/test/associations_extensions_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require 'abstract_unit'
-require 'fixtures/post'
-require 'fixtures/comment'
-require 'fixtures/project'
-require 'fixtures/developer'
-
-class AssociationsExtensionsTest < Test::Unit::TestCase
- fixtures :projects, :developers, :developers_projects, :comments, :posts
-
- def test_extension_on_has_many
- assert_equal comments(:more_greetings), posts(:welcome).comments.find_most_recent
- end
-
- def test_extension_on_habtm
- assert_equal projects(:action_controller), developers(:david).projects.find_most_recent
- end
-
- def test_named_extension_on_habtm
- assert_equal projects(:action_controller), developers(:david).projects_extended_by_name.find_most_recent
- end
-
- def test_marshalling_extensions
- david = developers(:david)
- assert_equal projects(:action_controller), david.projects.find_most_recent
-
- david = Marshal.load(Marshal.dump(david))
- assert_equal projects(:action_controller), david.projects.find_most_recent
- end
-
- def test_marshalling_named_extensions
- david = developers(:david)
- assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
-
- david = Marshal.load(Marshal.dump(david))
- assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
- end
-end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/associations_go_eager_test.rb b/vendor/rails/activerecord/test/associations_go_eager_test.rb
deleted file mode 100644
index d2ae4e0d..00000000
--- a/vendor/rails/activerecord/test/associations_go_eager_test.rb
+++ /dev/null
@@ -1,359 +0,0 @@
-require 'abstract_unit'
-require 'fixtures/post'
-require 'fixtures/comment'
-require 'fixtures/author'
-require 'fixtures/category'
-require 'fixtures/company'
-require 'fixtures/person'
-require 'fixtures/reader'
-
-class EagerAssociationTest < Test::Unit::TestCase
- fixtures :posts, :comments, :authors, :categories, :categories_posts,
- :companies, :accounts, :tags, :people, :readers
-
- def test_loading_with_one_association
- posts = Post.find(:all, :include => :comments)
- post = posts.find { |p| p.id == 1 }
- assert_equal 2, post.comments.size
- assert post.comments.include?(comments(:greetings))
-
- post = Post.find(:first, :include => :comments, :conditions => "posts.title = 'Welcome to the weblog'")
- assert_equal 2, post.comments.size
- assert post.comments.include?(comments(:greetings))
- end
-
- def test_loading_conditions_with_or
- posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
- assert_nil posts.detect { |p| p.author_id != authors(:david).id },
- "expected to find only david's posts"
- end
-
- def test_with_ordering
- list = Post.find(:all, :include => :comments, :order => "posts.id DESC")
- [:eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments,
- :authorless, :thinking, :welcome
- ].each_with_index do |post, index|
- assert_equal posts(post), list[index]
- end
- end
-
- def test_loading_with_multiple_associations
- posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
- assert_equal 2, posts.first.comments.size
- assert_equal 2, posts.first.categories.size
- assert posts.first.comments.include?(comments(:greetings))
- end
-
- def test_loading_from_an_association
- posts = authors(:david).posts.find(:all, :include => :comments, :order => "posts.id")
- assert_equal 2, posts.first.comments.size
- end
-
- def test_loading_with_no_associations
- assert_nil Post.find(posts(:authorless).id, :include => :author).author
- end
-
- def test_eager_association_loading_with_belongs_to
- comments = Comment.find(:all, :include => :post)
- assert_equal 10, comments.length
- titles = comments.map { |c| c.post.title }
- assert titles.include?(posts(:welcome).title)
- assert titles.include?(posts(:sti_post_and_comments).title)
- end
-
- def test_eager_association_loading_with_belongs_to_and_limit
- comments = Comment.find(:all, :include => :post, :limit => 5, :order => 'comments.id')
- assert_equal 5, comments.length
- assert_equal [1,2,3,5,6], comments.collect { |c| c.id }
- end
-
- def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
- comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :order => 'comments.id')
- assert_equal 3, comments.length
- assert_equal [5,6,7], comments.collect { |c| c.id }
- end
-
- def test_eager_association_loading_with_belongs_to_and_limit_and_offset
- comments = Comment.find(:all, :include => :post, :limit => 3, :offset => 2, :order => 'comments.id')
- assert_equal 3, comments.length
- assert_equal [3,5,6], comments.collect { |c| c.id }
- end
-
- def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
- comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id')
- assert_equal 3, comments.length
- assert_equal [6,7,8], comments.collect { |c| c.id }
- end
-
- def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
- comments = Comment.find(:all, :include => :post, :conditions => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id')
- assert_equal 3, comments.length
- assert_equal [6,7,8], comments.collect { |c| c.id }
- end
-
- def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
- posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :order => 'posts.id')
- assert_equal 1, posts.length
- assert_equal [1], posts.collect { |p| p.id }
- end
-
- def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
- posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id')
- assert_equal 1, posts.length
- assert_equal [2], posts.collect { |p| p.id }
- end
-
- def test_eager_with_has_many_through
- posts_with_comments = people(:michael).posts.find(:all, :include => :comments )
- posts_with_author = people(:michael).posts.find(:all, :include => :author )
- posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ])
- assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
- assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
- assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
- end
-
- def test_eager_with_has_many_and_limit
- posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
- assert_equal 2, posts.size
- assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size }
- end
-
- def test_eager_with_has_many_and_limit_and_conditions
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
- assert_equal 2, posts.size
- assert_equal [4,5], posts.collect { |p| p.id }
- end
-
- def test_eager_with_has_many_and_limit_and_conditions_array
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
- assert_equal 2, posts.size
- assert_equal [4,5], posts.collect { |p| p.id }
- end
-
- def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
- assert_equal 2, posts.size
-
- count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
- assert_equal count, posts.size
- end
-
- def test_eager_with_has_many_and_limit_ond_high_offset
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
- assert_equal 0, posts.size
- end
-
- def test_count_eager_with_has_many_and_limit_ond_high_offset
- posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
- assert_equal 0, posts
- end
-
- def test_eager_with_has_many_and_limit_with_no_results
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'")
- assert_equal 0, posts.size
- end
-
- def test_eager_with_has_and_belongs_to_many_and_limit
- posts = Post.find(:all, :include => :categories, :order => "posts.id", :limit => 3)
- assert_equal 3, posts.size
- assert_equal 2, posts[0].categories.size
- assert_equal 1, posts[1].categories.size
- assert_equal 0, posts[2].categories.size
- assert posts[0].categories.include?(categories(:technology))
- assert posts[1].categories.include?(categories(:general))
- end
-
- def test_eager_with_has_many_and_limit_and_conditions_on_the_eagers
- posts = authors(:david).posts.find(:all,
- :include => :comments,
- :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
- :limit => 2
- )
- assert_equal 2, posts.size
-
- count = Post.count(
- :include => [ :comments, :author ],
- :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
- :limit => 2
- )
- assert_equal count, posts.size
- end
-
- def test_eager_with_has_many_and_limit_and_scoped_conditions_on_the_eagers
- posts = nil
- Post.with_scope(:find => {
- :include => :comments,
- :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'"
- }) do
- posts = authors(:david).posts.find(:all, :limit => 2)
- assert_equal 2, posts.size
- end
-
- Post.with_scope(:find => {
- :include => [ :comments, :author ],
- :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')"
- }) do
- count = Post.count(:limit => 2)
- assert_equal count, posts.size
- end
- end
-
- def test_eager_with_has_many_and_limit_and_scoped_and_explicit_conditions_on_the_eagers
- Post.with_scope(:find => { :conditions => "1=1" }) do
- posts = authors(:david).posts.find(:all,
- :include => :comments,
- :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
- :limit => 2
- )
- assert_equal 2, posts.size
-
- count = Post.count(
- :include => [ :comments, :author ],
- :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
- :limit => 2
- )
- assert_equal count, posts.size
- end
- end
- def test_eager_association_loading_with_habtm
- posts = Post.find(:all, :include => :categories, :order => "posts.id")
- assert_equal 2, posts[0].categories.size
- assert_equal 1, posts[1].categories.size
- assert_equal 0, posts[2].categories.size
- assert posts[0].categories.include?(categories(:technology))
- assert posts[1].categories.include?(categories(:general))
- end
-
- def test_eager_with_inheritance
- posts = SpecialPost.find(:all, :include => [ :comments ])
- end
-
- def test_eager_has_one_with_association_inheritance
- post = Post.find(4, :include => [ :very_special_comment ])
- assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
- end
-
- def test_eager_has_many_with_association_inheritance
- post = Post.find(4, :include => [ :special_comments ])
- post.special_comments.each do |special_comment|
- assert_equal "SpecialComment", special_comment.class.to_s
- end
- end
-
- def test_eager_habtm_with_association_inheritance
- post = Post.find(6, :include => [ :special_categories ])
- assert_equal 1, post.special_categories.size
- post.special_categories.each do |special_category|
- assert_equal "SpecialCategory", special_category.class.to_s
- end
- end
-
- def test_eager_with_has_one_dependent_does_not_destroy_dependent
- assert_not_nil companies(:first_firm).account
- f = Firm.find(:first, :include => :account,
- :conditions => ["companies.name = ?", "37signals"])
- assert_not_nil f.account
- assert_equal companies(:first_firm, :reload).account, f.account
- end
-
- def test_eager_with_invalid_association_reference
- assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- post = Post.find(6, :include=> :monkeys )
- }
- assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- post = Post.find(6, :include=>[ :monkeys ])
- }
- assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
- post = Post.find(6, :include=>[ 'monkeys' ])
- }
- assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
- post = Post.find(6, :include=>[ :monkeys, :elephants ])
- }
- end
-
- def find_all_ordered(className, include=nil)
- className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
- end
-
- def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
- # Eager includes of has many and habtm associations aren't necessarily sorted in the same way
- def assert_equal_after_sort(item1, item2, item3 = nil)
- assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id})
- assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3
- end
- # Test regular association, association with conditions, association with
- # STI, and association with conditions assured not to be true
- post_types = [:posts, :hello_posts, :special_posts, :nonexistent_posts]
- # test both has_many and has_and_belongs_to_many
- [Author, Category].each do |className|
- d1 = find_all_ordered(className)
- # test including all post types at once
- d2 = find_all_ordered(className, post_types)
- d1.each_index do |i|
- assert_equal(d1[i], d2[i])
- assert_equal_after_sort(d1[i].posts, d2[i].posts)
- post_types[1..-1].each do |post_type|
- # test including post_types together
- d3 = find_all_ordered(className, [:posts, post_type])
- assert_equal(d1[i], d3[i])
- assert_equal_after_sort(d1[i].posts, d3[i].posts)
- assert_equal_after_sort(d1[i].send(post_type), d2[i].send(post_type), d3[i].send(post_type))
- end
- end
- end
- end
-
- def test_eager_with_multiple_associations_with_same_table_has_one
- d1 = find_all_ordered(Firm)
- d2 = find_all_ordered(Firm, :account)
- d1.each_index do |i|
- assert_equal(d1[i], d2[i])
- assert_equal(d1[i].account, d2[i].account)
- end
- end
-
- def test_eager_with_multiple_associations_with_same_table_belongs_to
- firm_types = [:firm, :firm_with_basic_id, :firm_with_other_name, :firm_with_condition]
- d1 = find_all_ordered(Client)
- d2 = find_all_ordered(Client, firm_types)
- d1.each_index do |i|
- assert_equal(d1[i], d2[i])
- firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) }
- end
- end
- def test_eager_with_valid_association_as_string_not_symbol
- assert_nothing_raised { Post.find(:all, :include => 'comments') }
- end
-
- def test_preconfigured_includes_with_belongs_to
- author = posts(:welcome).author_with_posts
- assert_equal 5, author.posts.size
- end
-
- def test_preconfigured_includes_with_has_one
- comment = posts(:sti_comments).very_special_comment_with_post
- assert_equal posts(:sti_comments), comment.post
- end
-
- def test_preconfigured_includes_with_has_many
- posts = authors(:david).posts_with_comments
- one = posts.detect { |p| p.id == 1 }
- assert_equal 5, posts.size
- assert_equal 2, one.comments.size
- end
-
- def test_preconfigured_includes_with_habtm
- posts = authors(:david).posts_with_categories
- one = posts.detect { |p| p.id == 1 }
- assert_equal 5, posts.size
- assert_equal 2, one.categories.size
- end
-
- def test_preconfigured_includes_with_has_many_and_habtm
- posts = authors(:david).posts_with_comments_and_categories
- one = posts.detect { |p| p.id == 1 }
- assert_equal 5, posts.size
- assert_equal 2, one.comments.size
- assert_equal 2, one.categories.size
- end
-end
diff --git a/vendor/rails/activerecord/test/associations_join_model_test.rb b/vendor/rails/activerecord/test/associations_join_model_test.rb
deleted file mode 100644
index 93cfd008..00000000
--- a/vendor/rails/activerecord/test/associations_join_model_test.rb
+++ /dev/null
@@ -1,370 +0,0 @@
-require 'abstract_unit'
-require 'fixtures/tag'
-require 'fixtures/tagging'
-require 'fixtures/post'
-require 'fixtures/comment'
-require 'fixtures/author'
-require 'fixtures/category'
-require 'fixtures/categorization'
-
-class AssociationsJoinModelTest < Test::Unit::TestCase
- self.use_transactional_fixtures = false
- fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites
-
- def test_has_many
- assert_equal categories(:general), authors(:david).categories.first
- end
-
- def test_has_many_inherited
- assert_equal categories(:sti_test), authors(:mary).categories.first
- end
-
- def test_inherited_has_many
- assert_equal authors(:mary), categories(:sti_test).authors.first
- end
-
- def test_polymorphic_has_many
- assert_equal taggings(:welcome_general), posts(:welcome).taggings.first
- end
-
- def test_polymorphic_has_one
- assert_equal taggings(:welcome_general), posts(:welcome).tagging
- end
-
- def test_polymorphic_belongs_to
- assert_equal posts(:welcome), posts(:welcome).taggings.first.taggable
- end
-
- def test_polymorphic_has_many_going_through_join_model
- assert_equal tags(:general), tag = posts(:welcome).tags.first
- assert_no_queries do
- tag.tagging
- end
- end
-
- def test_count_polymorphic_has_many
- assert_equal 1, posts(:welcome).taggings.count
- assert_equal 1, posts(:welcome).tags.count
- end
-
- def test_polymorphic_has_many_going_through_join_model_with_find
- assert_equal tags(:general), tag = posts(:welcome).tags.find(:first)
- assert_no_queries do
- tag.tagging
- end
- end
-
- def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection
- assert_equal tags(:general), tag = posts(:welcome).funky_tags.first
- assert_no_queries do
- tag.tagging
- end
- end
-
- def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection_with_find
- assert_equal tags(:general), tag = posts(:welcome).funky_tags.find(:first)
- assert_no_queries do
- tag.tagging
- end
- end
-
- def test_polymorphic_has_many_going_through_join_model_with_disabled_include
- assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
- assert_queries 1 do
- tag.tagging
- end
- end
-
- def test_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins
- assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
- tag.author_id
- end
-
- def test_polymorphic_has_many_going_through_join_model_with_custom_foreign_key
- assert_equal tags(:misc), taggings(:welcome_general).super_tag
- assert_equal tags(:misc), posts(:welcome).super_tags.first
- end
-
- def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
- post = SubStiPost.create :title => 'SubStiPost', :body => 'SubStiPost body'
- assert_instance_of SubStiPost, post
-
- tagging = tags(:misc).taggings.create(:taggable => post)
- assert_equal "SubStiPost", tagging.taggable_type
- end
-
- def test_polymorphic_has_many_going_through_join_model_with_inheritance
- assert_equal tags(:general), posts(:thinking).tags.first
- end
-
- def test_polymorphic_has_many_going_through_join_model_with_inheritance_with_custom_class_name
- assert_equal tags(:general), posts(:thinking).funky_tags.first
- end
-
- def test_polymorphic_has_many_create_model_with_inheritance
- post = posts(:thinking)
- assert_instance_of SpecialPost, post
-
- tagging = tags(:misc).taggings.create(:taggable => post)
- assert_equal "Post", tagging.taggable_type
- end
-
- def test_polymorphic_has_one_create_model_with_inheritance
- tagging = tags(:misc).create_tagging(:taggable => posts(:thinking))
- assert_equal "Post", tagging.taggable_type
- end
-
- def test_set_polymorphic_has_many
- tagging = tags(:misc).taggings.create
- posts(:thinking).taggings << tagging
- assert_equal "Post", tagging.taggable_type
- end
-
- def test_set_polymorphic_has_one
- tagging = tags(:misc).taggings.create
- posts(:thinking).tagging = tagging
- assert_equal "Post", tagging.taggable_type
- end
-
- def test_create_polymorphic_has_many_with_scope
- old_count = posts(:welcome).taggings.count
- tagging = posts(:welcome).taggings.create(:tag => tags(:misc))
- assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, posts(:welcome).taggings.count
- end
-
- def test_create_polymorphic_has_one_with_scope
- old_count = Tagging.count
- tagging = posts(:welcome).tagging.create(:tag => tags(:misc))
- assert_equal "Post", tagging.taggable_type
- assert_equal old_count+1, Tagging.count
- end
-
- def test_delete_polymorphic_has_many_with_delete_all
- assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDeleteAll'
- post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
-
- old_count = Tagging.count
- post.destroy
- assert_equal old_count-1, Tagging.count
- assert_equal 0, posts(:welcome).taggings.count
- end
-
- def test_delete_polymorphic_has_many_with_destroy
- assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDestroy'
- post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
-
- old_count = Tagging.count
- post.destroy
- assert_equal old_count-1, Tagging.count
- assert_equal 0, posts(:welcome).taggings.count
- end
-
- def test_delete_polymorphic_has_many_with_nullify
- assert_equal 1, posts(:welcome).taggings.count
- posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyNullify'
- post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
-
- old_count = Tagging.count
- post.destroy
- assert_equal old_count, Tagging.count
- assert_equal 0, posts(:welcome).taggings.count
- end
-
- def test_delete_polymorphic_has_one_with_destroy
- assert posts(:welcome).tagging
- posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneDestroy'
- post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
-
- old_count = Tagging.count
- post.destroy
- assert_equal old_count-1, Tagging.count
- assert_nil posts(:welcome).tagging(true)
- end
-
- def test_delete_polymorphic_has_one_with_nullify
- assert posts(:welcome).tagging
- posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneNullify'
- post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
-
- old_count = Tagging.count
- post.destroy
- assert_equal old_count, Tagging.count
- assert_nil posts(:welcome).tagging(true)
- end
-
- def test_has_many_with_piggyback
- assert_equal "2", categories(:sti_test).authors.first.post_id.to_s
- end
-
- def test_include_has_many_through
- posts = Post.find(:all, :order => 'posts.id')
- posts_with_authors = Post.find(:all, :include => :authors, :order => 'posts.id')
- assert_equal posts.length, posts_with_authors.length
- posts.length.times do |i|
- assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
- end
- end
-
- def test_include_polymorphic_has_one
- post = Post.find_by_id(posts(:welcome).id, :include => :tagging)
- tagging = taggings(:welcome_general)
- assert_no_queries do
- assert_equal tagging, post.tagging
- end
- end
-
- def test_include_polymorphic_has_many_through
- posts = Post.find(:all, :order => 'posts.id')
- posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
- assert_equal posts.length, posts_with_tags.length
- posts.length.times do |i|
- assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
- end
- end
-
- def test_include_polymorphic_has_many
- posts = Post.find(:all, :order => 'posts.id')
- posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
- assert_equal posts.length, posts_with_taggings.length
- posts.length.times do |i|
- assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
- end
- end
-
- def test_has_many_find_all
- assert_equal [categories(:general)], authors(:david).categories.find(:all)
- end
-
- def test_has_many_find_first
- assert_equal categories(:general), authors(:david).categories.find(:first)
- end
-
- def test_has_many_find_conditions
- assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
- assert_equal nil, authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
- end
-
- def test_has_many_class_methods_called_by_method_missing
- assert_equal categories(:general), authors(:david).categories.find_all_by_name('General').first
-# assert_equal nil, authors(:david).categories.find_by_name('Technology')
- end
-
- def test_has_many_going_through_join_model_with_custom_foreign_key
- assert_equal [], posts(:thinking).authors
- assert_equal [authors(:mary)], posts(:authorless).authors
- end
-
- def test_belongs_to_polymorphic_with_counter_cache
- assert_equal 0, posts(:welcome)[:taggings_count]
- tagging = posts(:welcome).taggings.create(:tag => tags(:general))
- assert_equal 1, posts(:welcome, :reload)[:taggings_count]
- tagging.destroy
- assert posts(:welcome, :reload)[:taggings_count].zero?
- end
-
- def test_unavailable_through_reflection
- assert_raises (ActiveRecord::HasManyThroughAssociationNotFoundError) { authors(:david).nothings }
- end
-
- def test_has_many_through_join_model_with_conditions
- assert_equal [], posts(:welcome).invalid_taggings
- assert_equal [], posts(:welcome).invalid_tags
- end
-
- def test_has_many_polymorphic
- assert_raises ActiveRecord::HasManyThroughAssociationPolymorphicError do
- assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggables
- end
- assert_raises ActiveRecord::EagerLoadPolymorphicError do
- assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggings.find(:all, :include => :taggable)
- end
- end
-
- def test_has_many_through_has_many_find_all
- assert_equal comments(:greetings), authors(:david).comments.find(:all, :order => 'comments.id').first
- end
-
- def test_has_many_through_has_many_find_all_with_custom_class
- assert_equal comments(:greetings), authors(:david).funky_comments.find(:all, :order => 'comments.id').first
- end
-
- def test_has_many_through_has_many_find_first
- assert_equal comments(:greetings), authors(:david).comments.find(:first, :order => 'comments.id')
- end
-
- def test_has_many_through_has_many_find_conditions
- assert_equal comments(:does_it_hurt), authors(:david).comments.find(:first, :conditions => "comments.type='SpecialComment'", :order => 'comments.id')
- end
-
- def test_has_many_through_has_many_find_by_id
- assert_equal comments(:more_greetings), authors(:david).comments.find(2)
- end
-
- def test_has_many_through_polymorphic_has_one
- assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tagging }
- end
-
- def test_has_many_through_polymorphic_has_many
- assert_equal [taggings(:welcome_general), taggings(:thinking_general)], authors(:david).taggings.uniq.sort_by { |t| t.id }
- end
-
- def test_include_has_many_through_polymorphic_has_many
- author = Author.find_by_id(authors(:david).id, :include => :taggings)
- expected_taggings = [taggings(:welcome_general), taggings(:thinking_general)]
- assert_no_queries do
- assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
- end
- end
-
- def test_has_many_through_has_many_through
- assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags }
- end
-
- def test_has_many_through_habtm
- assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).post_categories }
- end
-
- def test_eager_load_has_many_through_has_many
- author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id'
- SpecialComment.new; VerySpecialComment.new
- assert_no_queries do
- assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id)
- end
- end
-
- def test_eager_belongs_to_and_has_one_not_singularized
- assert_nothing_raised do
- Author.find(:first, :include => :author_address)
- AuthorAddress.find(:first, :include => :author)
- end
- end
-
- def test_self_referential_has_many_through
- assert_equal [authors(:mary)], authors(:david).favorite_authors
- assert_equal [], authors(:mary).favorite_authors
- end
-
- def test_add_to_self_referential_has_many_through
- new_author = Author.create(:name => "Bob")
- authors(:david).author_favorites.create :favorite_author => new_author
- assert_equal new_author, authors(:david).reload.favorite_authors.first
- end
-
- def test_has_many_through_uses_correct_attributes
- assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
- end
-
- private
- # create dynamic Post models to allow different dependency options
- def find_post_with_dependency(post_id, association, association_name, dependency)
- class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
- Post.find(post_id).update_attribute :type, class_name
- klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
- klass.set_table_name 'posts'
- klass.send(association, association_name, :as => :taggable, :dependent => dependency)
- klass.find(post_id)
- end
-end
diff --git a/vendor/rails/activerecord/test/associations_test.rb b/vendor/rails/activerecord/test/associations_test.rb
index 1de094fd..e35233a1 100755
--- a/vendor/rails/activerecord/test/associations_test.rb
+++ b/vendor/rails/activerecord/test/associations_test.rb
@@ -7,10 +7,15 @@ require 'fixtures/reply'
require 'fixtures/computer'
require 'fixtures/customer'
require 'fixtures/order'
+require 'fixtures/categorization'
require 'fixtures/category'
require 'fixtures/post'
require 'fixtures/author'
-
+require 'fixtures/comment'
+require 'fixtures/tag'
+require 'fixtures/tagging'
+require 'fixtures/person'
+require 'fixtures/reader'
class AssociationsTest < Test::Unit::TestCase
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
@@ -21,24 +26,26 @@ class AssociationsTest < Test::Unit::TestCase
Class.new(ActiveRecord::Base).has_many(:wheels, :name => 'wheels')
end
end
+
+ def test_should_construct_new_finder_sql_after_create
+ person = Person.new
+ assert_equal [], person.readers.find(:all)
+ person.save!
+ reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
+ assert_equal [reader], person.readers.find(:all)
+ end
def test_force_reload
firm = Firm.new("name" => "A New Firm, Inc")
firm.save
firm.clients.each {|c|} # forcing to load all clients
assert firm.clients.empty?, "New firm shouldn't have client objects"
- assert_deprecated do
- assert !firm.has_clients?, "New firm shouldn't have clients"
- end
assert_equal 0, firm.clients.size, "New firm should have 0 clients"
client = Client.new("name" => "TheClient.com", "firm_id" => firm.id)
client.save
assert firm.clients.empty?, "New firm should have cached no client objects"
- assert_deprecated do
- assert !firm.has_clients?, "New firm should have cached a no-clients response"
- end
assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"
assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
@@ -48,7 +55,7 @@ class AssociationsTest < Test::Unit::TestCase
def test_storing_in_pstore
require "tmpdir"
store_filename = File.join(Dir.tmpdir, "ar-pstore-association-test")
- File.delete(store_filename) if File.exists?(store_filename)
+ File.delete(store_filename) if File.exist?(store_filename)
require "pstore"
apple = Firm.create("name" => "Apple")
natural = Client.new("name" => "Natural Company")
@@ -67,7 +74,7 @@ class AssociationsTest < Test::Unit::TestCase
end
class AssociationProxyTest < Test::Unit::TestCase
- fixtures :authors, :posts, :developers, :projects, :developers_projects
+ fixtures :authors, :posts, :categorizations, :categories, :developers, :projects, :developers_projects
def test_proxy_accessors
welcome = posts(:welcome)
@@ -81,13 +88,28 @@ class AssociationProxyTest < Test::Unit::TestCase
assert_equal david.class.reflect_on_association(:posts), david.posts.proxy_reflection
david.posts.first # force load target
assert_equal david.posts, david.posts.proxy_target
-
+
assert_equal david, david.posts_with_extension.testing_proxy_owner
assert_equal david.class.reflect_on_association(:posts_with_extension), david.posts_with_extension.testing_proxy_reflection
david.posts_with_extension.first # force load target
assert_equal david.posts_with_extension, david.posts_with_extension.testing_proxy_target
end
+ def test_push_does_not_load_target
+ david = authors(:david)
+
+ david.categories << categories(:technology)
+ assert !david.categories.loaded?
+ assert david.categories.include?(categories(:technology))
+ end
+
+ def test_push_does_not_lose_additions_to_new_record
+ josh = Author.new(:name => "Josh")
+ josh.posts << Post.new(:title => "New on Edge", :body => "More cool stuff!")
+ assert josh.posts.loaded?
+ assert_equal 1, josh.posts.size
+ end
+
def test_save_on_parent_does_not_load_target
david = developers(:david)
@@ -100,15 +122,39 @@ class AssociationProxyTest < Test::Unit::TestCase
developer = Developer.create :name => "Bryan", :salary => 50_000
assert_equal 1, developer.reload.audit_logs.size
end
+
+ def test_failed_reload_returns_nil
+ p = setup_dangling_association
+ assert_nil p.author.reload
+ end
+
+ def test_failed_reset_returns_nil
+ p = setup_dangling_association
+ assert_nil p.author.reset
+ end
+
+ def test_reload_returns_assocition
+ david = developers(:david)
+ assert_nothing_raised do
+ assert_equal david.projects, david.projects.reload.reload
+ end
+ end
+
+ def setup_dangling_association
+ josh = Author.create(:name => "Josh")
+ p = Post.create(:title => "New on Edge", :body => "More cool stuff!", :author => josh)
+ josh.destroy
+ p
+ end
end
class HasOneAssociationsTest < Test::Unit::TestCase
fixtures :accounts, :companies, :developers, :projects, :developers_projects
-
+
def setup
Account.destroyed_account_ids.clear
end
-
+
def test_has_one
assert_equal companies(:first_firm).account, Account.find(1)
assert_equal Account.find(1).credit_limit, companies(:first_firm).account.credit_limit
@@ -156,22 +202,22 @@ class HasOneAssociationsTest < Test::Unit::TestCase
apple.account = citibank
assert_equal apple.id, citibank.firm_id
end
-
+
def test_natural_assignment_to_nil
old_account_id = companies(:first_firm).account.id
companies(:first_firm).account = nil
companies(:first_firm).save
assert_nil companies(:first_firm).account
# account is dependent, therefore is destroyed when reference to owner is lost
- assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
+ assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
end
-
+
def test_assignment_without_replacement
apple = Firm.create("name" => "Apple")
citibank = Account.create("credit_limit" => 10)
apple.account = citibank
assert_equal apple.id, citibank.firm_id
-
+
hsbc = apple.build_account({ :credit_limit => 20}, false)
assert_equal apple.id, hsbc.firm_id
hsbc.save
@@ -188,7 +234,7 @@ class HasOneAssociationsTest < Test::Unit::TestCase
citibank = Account.create("credit_limit" => 10)
apple.account = citibank
assert_equal apple.id, citibank.firm_id
-
+
hsbc = apple.create_account({:credit_limit => 10}, false)
assert_equal apple.id, hsbc.firm_id
hsbc.save
@@ -208,12 +254,6 @@ class HasOneAssociationsTest < Test::Unit::TestCase
assert_equal [account_id], Account.destroyed_account_ids[firm.id]
end
- def test_deprecated_exclusive_dependence
- assert_deprecated(/:exclusively_dependent.*:dependent => :delete_all/) do
- Firm.has_many :deprecated_exclusively_dependent_clients, :class_name => 'Client', :exclusively_dependent => true
- end
- end
-
def test_exclusive_dependence
num_accounts = Account.count
@@ -261,9 +301,9 @@ class HasOneAssociationsTest < Test::Unit::TestCase
end
def test_create_association
- firm = Firm.new("name" => "GlobalMegaCorp")
- firm.save
- assert_equal firm.create_account("credit_limit" => 1000), firm.account
+ firm = Firm.create(:name => "GlobalMegaCorp")
+ account = firm.create_account(:credit_limit => 1000)
+ assert_equal account, firm.reload.account
end
def test_build
@@ -301,7 +341,7 @@ class HasOneAssociationsTest < Test::Unit::TestCase
def test_failing_build_association
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
-
+
firm.account = account = Account.new
assert_equal account, firm.account
assert !account.save
@@ -329,6 +369,13 @@ class HasOneAssociationsTest < Test::Unit::TestCase
firm.destroy
end
+ def test_dependence_with_missing_association_and_nullify
+ Account.destroy_all
+ firm = DependentFirm.find(:first)
+ assert firm.account.nil?
+ firm.destroy
+ end
+
def test_assignment_before_parent_saved
firm = Firm.new("name" => "GlobalMegaCorp")
firm.account = a = Account.find(1)
@@ -385,21 +432,14 @@ class HasOneAssociationsTest < Test::Unit::TestCase
firm.account = Account.find(:first).clone
assert_queries(2) { firm.save! }
end
-
+
def test_save_still_works_after_accessing_nil_has_one
jp = Company.new :name => 'Jaded Pixel'
jp.dummy_account.nil?
-
+
assert_nothing_raised do
jp.save!
- end
- end
-
- def test_deprecated_inferred_foreign_key
- assert_not_deprecated { Company.belongs_to :firm }
- assert_not_deprecated { Company.belongs_to :client, :foreign_key => "firm_id" }
- assert_not_deprecated { Company.belongs_to :firm, :class_name => "Firm", :foreign_key => "client_of" }
- assert_deprecated("inferred foreign_key name") { Company.belongs_to :client, :class_name => "Firm" }
+ end
end
end
@@ -407,7 +447,7 @@ end
class HasManyAssociationsTest < Test::Unit::TestCase
fixtures :accounts, :companies, :developers, :projects,
- :developers_projects, :topics
+ :developers_projects, :topics, :authors, :comments
def setup
Client.destroyed_client_ids.clear
@@ -426,9 +466,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
end
def test_counting_with_single_conditions
- assert_deprecated 'count' do
- assert_equal 2, Firm.find(:first).plain_clients.count('1=1')
- end
+ assert_equal 2, Firm.find(:first).plain_clients.count(:conditions => '1=1')
end
def test_counting_with_single_hash
@@ -449,6 +487,36 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_equal 2, companies(:first_firm).limited_clients.find(:all, :limit => nil).size
end
+ def test_dynamic_find_should_respect_association_order
+ assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'")
+ assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client')
+ end
+
+ def test_dynamic_find_order_should_override_association_order
+ assert_equal companies(:first_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'", :order => 'id')
+ assert_equal companies(:first_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client', :order => 'id')
+ end
+
+ def test_dynamic_find_all_should_respect_association_order
+ assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'")
+ assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client')
+ end
+
+ def test_dynamic_find_all_order_should_override_association_order
+ assert_equal [companies(:first_client), companies(:second_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'", :order => 'id')
+ assert_equal [companies(:first_client), companies(:second_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client', :order => 'id')
+ end
+
+ def test_dynamic_find_all_should_respect_association_limit
+ assert_equal 1, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'").length
+ assert_equal 1, companies(:first_firm).limited_clients.find_all_by_type('Client').length
+ end
+
+ def test_dynamic_find_all_limit_should_override_association_limit
+ assert_equal 2, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'", :limit => 9_000).length
+ assert_equal 2, companies(:first_firm).limited_clients.find_all_by_type('Client', :limit => 9_000).length
+ end
+
def test_triple_equality
assert !(Array === Firm.find(:first).clients)
assert Firm.find(:first).clients === Array
@@ -485,13 +553,15 @@ class HasManyAssociationsTest < Test::Unit::TestCase
def test_counting_using_sql
assert_equal 1, Firm.find(:first).clients_using_counter_sql.size
+ assert Firm.find(:first).clients_using_counter_sql.any?
assert_equal 0, Firm.find(:first).clients_using_zero_counter_sql.size
+ assert !Firm.find(:first).clients_using_zero_counter_sql.any?
end
def test_counting_non_existant_items_using_sql
assert_equal 0, Firm.find(:first).no_clients_using_counter_sql.size
end
-
+
def test_belongs_to_sanity
c = Client.new
assert_nil c.firm
@@ -526,45 +596,47 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
end
+ def test_find_string_ids_when_using_finder_sql
+ firm = Firm.find(:first)
+
+ client = firm.clients_using_finder_sql.find("2")
+ assert_kind_of Client, client
+
+ client_ary = firm.clients_using_finder_sql.find(["2"])
+ assert_kind_of Array, client_ary
+ assert_equal client, client_ary.first
+
+ client_ary = firm.clients_using_finder_sql.find("2", "3")
+ assert_kind_of Array, client_ary
+ assert_equal 2, client_ary.size
+ assert client_ary.include?(client)
+ end
+
def test_find_all
- assert_deprecated 'find_all' do
- firm = Firm.find_first
- assert_equal firm.clients, firm.clients.find_all
- assert_equal 2, firm.clients.find(:all, :conditions => "#{QUOTED_TYPE} = 'Client'").length
- assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
- end
+ firm = Firm.find(:first)
+ assert_equal 2, firm.clients.find(:all, :conditions => "#{QUOTED_TYPE} = 'Client'").length
+ assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
end
def test_find_all_sanitized
- assert_deprecated 'find_all' do
- firm = Firm.find_first
- assert_equal firm.clients.find_all("name = 'Summit'"), firm.clients.find_all(["name = '%s'", "Summit"])
- summit = firm.clients.find(:all, :conditions => "name = 'Summit'")
- assert_equal summit, firm.clients.find(:all, :conditions => ["name = ?", "Summit"])
- assert_equal summit, firm.clients.find(:all, :conditions => ["name = :name", { :name => "Summit" }])
- end
+ firm = Firm.find(:first)
+ summit = firm.clients.find(:all, :conditions => "name = 'Summit'")
+ assert_equal summit, firm.clients.find(:all, :conditions => ["name = ?", "Summit"])
+ assert_equal summit, firm.clients.find(:all, :conditions => ["name = :name", { :name => "Summit" }])
end
def test_find_first
- assert_deprecated 'find_first' do
- firm = Firm.find_first
- client2 = Client.find(2)
- assert_equal firm.clients.first, firm.clients.find_first
- assert_equal client2, firm.clients.find_first("#{QUOTED_TYPE} = 'Client'")
- assert_equal client2, firm.clients.find(:first, :conditions => "#{QUOTED_TYPE} = 'Client'")
- end
+ firm = Firm.find(:first)
+ client2 = Client.find(2)
+ assert_equal firm.clients.first, firm.clients.find(:first)
+ assert_equal client2, firm.clients.find(:first, :conditions => "#{QUOTED_TYPE} = 'Client'")
end
def test_find_first_sanitized
- assert_deprecated 'find_first' do
- firm = Firm.find_first
- client2 = Client.find(2)
- assert_deprecated(/find_first/) do
- assert_equal client2, firm.clients.find_first(["#{QUOTED_TYPE} = ?", "Client"])
- end
- assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = ?", 'Client'])
- assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }])
- end
+ firm = Firm.find(:first)
+ client2 = Client.find(2)
+ assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = ?", 'Client'])
+ assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }])
end
def test_find_in_collection
@@ -596,19 +668,39 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_equal 3, first_firm.plain_clients.size
end
+ def test_create_with_bang_on_has_many_when_parent_is_new_raises
+ assert_raises(ActiveRecord::RecordNotSaved) do
+ firm = Firm.new
+ firm.plain_clients.create! :name=>"Whoever"
+ end
+ end
+
def test_regular_create_on_has_many_when_parent_is_new_raises
- assert_deprecated(/.build instead/) do
+ assert_raises(ActiveRecord::RecordNotSaved) do
firm = Firm.new
firm.plain_clients.create :name=>"Whoever"
end
end
+
+ def test_create_with_bang_on_has_many_raises_when_record_not_saved
+ assert_raises(ActiveRecord::RecordInvalid) do
+ firm = Firm.find(:first)
+ firm.plain_clients.create!
+ end
+ end
+
+ def test_create_with_bang_on_habtm_when_parent_is_new_raises
+ assert_raises(ActiveRecord::RecordNotSaved) do
+ Developer.new("name" => "Aredridel").projects.create!
+ end
+ end
def test_adding_a_mismatch_class
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << nil }
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << 1 }
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << Topic.find(1) }
end
-
+
def test_adding_a_collection
force_signal37_to_load_all_clients_of_firm
companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
@@ -619,12 +711,15 @@ class HasManyAssociationsTest < Test::Unit::TestCase
def test_adding_before_save
no_of_firms = Firm.count
no_of_clients = Client.count
+
new_firm = Firm.new("name" => "A New Firm, Inc")
+ c = Client.new("name" => "Apple")
+
new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
- new_firm.clients_of_firm << (c = Client.new("name" => "Apple"))
- assert new_firm.new_record?
- assert c.new_record?
+ assert_equal 1, new_firm.clients_of_firm.size
+ new_firm.clients_of_firm << c
assert_equal 2, new_firm.clients_of_firm.size
+
assert_equal no_of_firms, Firm.count # Firm was not saved to database.
assert_equal no_of_clients, Client.count # Clients were not saved to database.
assert new_firm.save
@@ -633,6 +728,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_equal new_firm, c.firm
assert_equal no_of_firms+1, Firm.count # Firm was saved to database.
assert_equal no_of_clients+2, Client.count # Clients were saved to database.
+
assert_equal 2, new_firm.clients_of_firm.size
assert_equal 2, new_firm.clients_of_firm(true).size
end
@@ -682,7 +778,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
Reply.column_names
assert_equal 1, first_topic.replies.length
-
+
assert_no_queries do
first_topic.replies.build(:title => "Not saved", :content => "Superstars")
assert_equal 2, first_topic.replies.size
@@ -698,11 +794,11 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_equal 1, first_firm.clients_of_firm.size
first_firm.clients_of_firm.reset
-
+
assert_queries(1) do
first_firm.clients_of_firm.create(:name => "Superstars")
end
-
+
assert_equal 2, first_firm.clients_of_firm.size
end
@@ -715,7 +811,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert new_client.new_record?
assert_equal 1, companies(:first_firm).clients_of_firm(true).size
end
-
+
def test_create
force_signal37_to_load_all_clients_of_firm
new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
@@ -723,18 +819,25 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_equal new_client, companies(:first_firm).clients_of_firm.last
assert_equal new_client, companies(:first_firm).clients_of_firm(true).last
end
-
+
def test_create_many
companies(:first_firm).clients_of_firm.create([{"name" => "Another Client"}, {"name" => "Another Client II"}])
assert_equal 3, companies(:first_firm).clients_of_firm(true).size
end
+ def test_find_or_initialize
+ the_client = companies(:first_firm).clients.find_or_initialize_by_name("Yet another client")
+ assert_equal companies(:first_firm).id, the_client.firm_id
+ assert_equal "Yet another client", the_client.name
+ assert the_client.new_record?
+ end
+
def test_find_or_create
number_of_clients = companies(:first_firm).clients.size
the_client = companies(:first_firm).clients.find_or_create_by_name("Yet another client")
- assert_equal number_of_clients + 1, companies(:first_firm, :refresh).clients.size
+ assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size
assert_equal the_client, companies(:first_firm).clients.find_or_create_by_name("Yet another client")
- assert_equal number_of_clients + 1, companies(:first_firm, :refresh).clients.size
+ assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size
end
def test_deleting
@@ -760,7 +863,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_equal 0, companies(:first_firm).clients_of_firm.size
assert_equal 0, companies(:first_firm).clients_of_firm(true).size
end
-
+
def test_delete_all
force_signal37_to_load_all_clients_of_firm
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
@@ -826,11 +929,36 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_equal 0, firm.exclusively_dependent_clients_of_firm.size
assert_equal 0, firm.exclusively_dependent_clients_of_firm(true).size
- assert_equal [3], Client.destroyed_client_ids[firm.id]
+ # no destroy-filters should have been called
+ assert_equal [], Client.destroyed_client_ids[firm.id]
# Should be destroyed since the association is exclusively dependent.
assert Client.find_by_id(client_id).nil?
- end
+ end
+
+ def test_dependent_association_respects_optional_conditions_on_delete
+ firm = companies(:odegy)
+ Client.create(:client_of => firm.id, :name => "BigShot Inc.")
+ Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ # only one of two clients is included in the association due to the :conditions key
+ assert_equal 2, Client.find_all_by_client_of(firm.id).size
+ assert_equal 1, firm.dependent_conditional_clients_of_firm.size
+ firm.destroy
+ # only the correctly associated client should have been deleted
+ assert_equal 1, Client.find_all_by_client_of(firm.id).size
+ end
+
+ def test_dependent_association_respects_optional_sanitized_conditions_on_delete
+ firm = companies(:odegy)
+ Client.create(:client_of => firm.id, :name => "BigShot Inc.")
+ Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ # only one of two clients is included in the association due to the :conditions key
+ assert_equal 2, Client.find_all_by_client_of(firm.id).size
+ assert_equal 1, firm.dependent_sanitized_conditional_clients_of_firm.size
+ firm.destroy
+ # only the correctly associated client should have been deleted
+ assert_equal 1, Client.find_all_by_client_of(firm.id).size
+ end
def test_clearing_without_initial_access
firm = companies(:first_firm)
@@ -893,7 +1021,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
topic = Topic.create "title" => "neat and simple"
reply = topic.replies.create "title" => "neat and simple", "content" => "still digging it"
silly_reply = reply.replies.create "title" => "neat and simple", "content" => "ain't complaining"
-
+
assert_nothing_raised { topic.destroy }
end
@@ -918,16 +1046,16 @@ class HasManyAssociationsTest < Test::Unit::TestCase
def test_depends_and_nullify
num_accounts = Account.count
num_companies = Company.count
-
+
core = companies(:rails_core)
assert_equal accounts(:rails_core_account), core.account
- assert_equal [companies(:leetsoft), companies(:jadedpixel)], core.companies
- core.destroy
+ assert_equal companies(:leetsoft, :jadedpixel), core.companies
+ core.destroy
assert_nil accounts(:rails_core_account).reload.firm_id
assert_nil companies(:leetsoft).reload.client_of
assert_nil companies(:jadedpixel).reload.client_of
-
-
+
+
assert_equal num_accounts, Account.count
end
@@ -950,19 +1078,23 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert firm.save, "Could not save firm"
firm.reload
assert_equal 1, firm.clients.length
- end
-
-
+ end
+
+ def test_replace_with_less_and_dependent_nullify
+ num_companies = Company.count
+ companies(:rails_core).companies = []
+ assert_equal num_companies, Company.count
+ end
+
def test_replace_with_new
firm = Firm.find(:first)
- new_client = Client.new("name" => "New Client")
- firm.clients = [companies(:second_client),new_client]
+ firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
firm.save
firm.reload
assert_equal 2, firm.clients.length
assert !firm.clients.include?(:first_client)
end
-
+
def test_replace_on_new_object
firm = Firm.new("name" => "New Firm")
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
@@ -971,11 +1103,11 @@ class HasManyAssociationsTest < Test::Unit::TestCase
assert_equal 2, firm.clients.length
assert firm.clients.include?(Client.find_by_name("New Client"))
end
-
+
def test_get_ids
assert_equal [companies(:first_client).id, companies(:second_client).id], companies(:first_firm).client_ids
end
-
+
def test_assign_ids
firm = Firm.new("name" => "Apple")
firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
@@ -986,24 +1118,62 @@ class HasManyAssociationsTest < Test::Unit::TestCase
end
def test_assign_ids_ignoring_blanks
- firm = Firm.new("name" => "Apple")
+ firm = Firm.create!(:name => 'Apple')
firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
- firm.save
- firm.reload
- assert_equal 2, firm.clients.length
+ firm.save!
+
+ assert_equal 2, firm.clients(true).size
assert firm.clients.include?(companies(:second_client))
end
+ def test_get_ids_for_through
+ assert_equal [comments(:eager_other_comment1).id], authors(:mary).comment_ids
+ end
+
+ def test_assign_ids_for_through
+ assert_raise(NoMethodError) { authors(:mary).comment_ids = [123] }
+ end
+
+ def test_dynamic_find_should_respect_association_order_for_through
+ assert_equal Comment.find(10), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'")
+ assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
+ end
+
+ def test_dynamic_find_order_should_override_association_order_for_through
+ assert_equal Comment.find(3), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'", :order => 'comments.id')
+ assert_equal Comment.find(3), authors(:david).comments_desc.find_by_type('SpecialComment', :order => 'comments.id')
+ end
+
+ def test_dynamic_find_all_should_respect_association_order_for_through
+ assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'")
+ assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find_all_by_type('SpecialComment')
+ end
+
+ def test_dynamic_find_all_order_should_override_association_order_for_through
+ assert_equal [Comment.find(3), Comment.find(6), Comment.find(7), Comment.find(10)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'", :order => 'comments.id')
+ assert_equal [Comment.find(3), Comment.find(6), Comment.find(7), Comment.find(10)], authors(:david).comments_desc.find_all_by_type('SpecialComment', :order => 'comments.id')
+ end
+
+ def test_dynamic_find_all_should_respect_association_limit_for_through
+ assert_equal 1, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'").length
+ assert_equal 1, authors(:david).limited_comments.find_all_by_type('SpecialComment').length
+ end
+
+ def test_dynamic_find_all_order_should_override_association_limit_for_through
+ assert_equal 4, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'", :limit => 9_000).length
+ assert_equal 4, authors(:david).limited_comments.find_all_by_type('SpecialComment', :limit => 9_000).length
+ end
+
end
class BelongsToAssociationsTest < Test::Unit::TestCase
fixtures :accounts, :companies, :developers, :projects, :topics,
- :developers_projects, :computers, :authors, :posts
-
+ :developers_projects, :computers, :authors, :posts, :tags, :taggings
+
def test_belongs_to
Client.find(3).firm.name
assert_equal companies(:first_firm).name, Client.find(3).firm.name
- assert !Client.find(3).firm.nil?, "Microsoft should have a firm"
+ assert !Client.find(3).firm.nil?, "Microsoft should have a firm"
end
def test_proxy_assignment
@@ -1056,7 +1226,7 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
citibank.save
assert_equal apple.id, citibank.firm_id
end
-
+
def test_natural_assignment_to_nil
client = Client.find(3)
client.firm = nil
@@ -1064,7 +1234,7 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
assert_nil client.firm(true)
assert_nil client.client_of
end
-
+
def test_with_different_class_name
assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name
assert_not_nil Company.find(3).firm_with_other_name, "Microsoft should have a firm"
@@ -1074,7 +1244,7 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
assert_equal Company.find(1).name, Company.find(3).firm_with_condition.name
assert_not_nil Company.find(3).firm_with_condition, "Microsoft should have a firm"
end
-
+
def test_belongs_to_counter
debate = Topic.create("title" => "debate")
assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet"
@@ -1118,6 +1288,42 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
assert_equal 0, Topic.find(t2.id).replies.size
end
+ def test_belongs_to_counter_after_save
+ topic = Topic.create!(:title => "monday night")
+ topic.replies.create!(:title => "re: monday night", :content => "football")
+ assert_equal 1, Topic.find(topic.id)[:replies_count]
+
+ topic.save!
+ assert_equal 1, Topic.find(topic.id)[:replies_count]
+ end
+
+ def test_belongs_to_counter_after_update_attributes
+ topic = Topic.create!(:title => "37s")
+ topic.replies.create!(:title => "re: 37s", :content => "rails")
+ assert_equal 1, Topic.find(topic.id)[:replies_count]
+
+ topic.update_attributes(:title => "37signals")
+ assert_equal 1, Topic.find(topic.id)[:replies_count]
+ end
+
+ def test_belongs_to_counter_after_save
+ topic = Topic.create("title" => "monday night")
+ topic.replies.create("title" => "re: monday night", "content" => "football")
+ assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
+
+ topic.save
+ assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
+ end
+
+ def test_belongs_to_counter_after_update_attributes
+ topic = Topic.create("title" => "37s")
+ topic.replies.create("title" => "re: 37s", "content" => "rails")
+ assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
+
+ topic.update_attributes("title" => "37signals")
+ assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
+ end
+
def test_assignment_before_parent_saved
client = Client.find(:first)
apple = Firm.new("name" => "Apple")
@@ -1177,7 +1383,7 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
def test_counter_cache
topic = Topic.create :title => "Zoom-zoom-zoom"
assert_equal 0, topic[:replies_count]
-
+
reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
reply.topic = topic
@@ -1205,10 +1411,10 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
def test_store_two_association_with_one_save
num_orders = Order.count
num_customers = Customer.count
- order = Order.new
+ order = Order.new
customer1 = order.billing = Customer.new
- customer2 = order.shipping = Customer.new
+ customer2 = order.shipping = Customer.new
assert order.save
assert_equal customer1, order.billing
assert_equal customer2, order.shipping
@@ -1216,28 +1422,28 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
order.reload
assert_equal customer1, order.billing
- assert_equal customer2, order.shipping
+ assert_equal customer2, order.shipping
assert_equal num_orders +1, Order.count
assert_equal num_customers +2, Customer.count
end
-
+
def test_store_association_in_two_relations_with_one_save
num_orders = Order.count
num_customers = Customer.count
- order = Order.new
-
- customer = order.billing = order.shipping = Customer.new
+ order = Order.new
+
+ customer = order.billing = order.shipping = Customer.new
assert order.save
assert_equal customer, order.billing
assert_equal customer, order.shipping
-
+
order.reload
-
+
assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
+ assert_equal customer, order.shipping
+
assert_equal num_orders +1, Order.count
assert_equal num_customers +1, Customer.count
end
@@ -1246,46 +1452,46 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
num_orders = Order.count
num_customers = Customer.count
order = Order.create
-
- customer = order.billing = order.shipping = Customer.new
+
+ customer = order.billing = order.shipping = Customer.new
assert order.save
assert_equal customer, order.billing
assert_equal customer, order.shipping
-
+
order.reload
-
+
assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
+ assert_equal customer, order.shipping
+
assert_equal num_orders +1, Order.count
assert_equal num_customers +1, Customer.count
end
-
+
def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
num_orders = Order.count
num_customers = Customer.count
order = Order.create
-
- customer = order.billing = order.shipping = Customer.new
+
+ customer = order.billing = order.shipping = Customer.new
assert order.save
assert_equal customer, order.billing
assert_equal customer, order.shipping
-
+
order.reload
-
- customer = order.billing = order.shipping = Customer.new
-
+
+ customer = order.billing = order.shipping = Customer.new
+
assert order.save
- order.reload
-
+ order.reload
+
assert_equal customer, order.billing
- assert_equal customer, order.shipping
-
+ assert_equal customer, order.shipping
+
assert_equal num_orders +1, Order.count
assert_equal num_customers +2, Customer.count
end
-
-
+
+
def test_association_assignment_sticks
post = Post.find(:first)
@@ -1306,7 +1512,7 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
# the author id of the post should be the id we set
assert_equal post.author_id, author2.id
end
-
+
end
@@ -1338,7 +1544,7 @@ end
class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects
-
+
def test_has_and_belongs_to_many
david = Developer.find(1)
@@ -1358,11 +1564,11 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
def test_adding_single
jamis = Developer.find(2)
- jamis.projects.reload # causing the collection to load
+ jamis.projects.reload # causing the collection to load
action_controller = Project.find(2)
assert_equal 1, jamis.projects.size
- assert_equal 1, action_controller.developers.size
-
+ assert_equal 1, action_controller.developers.size
+
jamis.projects << action_controller
assert_equal 2, jamis.projects.size
@@ -1383,8 +1589,8 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal 1, jamis.projects.size
assert_equal 1, action_controller.developers.size
- action_controller.developers << jamis
-
+ action_controller.developers << jamis
+
assert_equal 2, jamis.projects(true).size
assert_equal 2, action_controller.developers.size
assert_equal 2, action_controller.developers(true).size
@@ -1434,19 +1640,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal 1, project.access_level.to_i
end
- def test_adding_uses_explicit_values_on_join_table
- ac = projects(:action_controller)
- assert !developers(:jamis).projects.include?(ac)
- assert_deprecated do
- developers(:jamis).projects.push_with_attributes(ac, :access_level => 3)
- end
-
- assert developers(:jamis, :reload).projects.include?(ac)
- project = developers(:jamis).projects.detect { |p| p == ac }
- assert_equal 3, project.access_level.to_i
- end
-
- def test_hatbm_attribute_access_and_respond_to
+ def test_habtm_attribute_access_and_respond_to
project = developers(:jamis).projects[0]
assert project.has_attribute?("name")
assert project.has_attribute?("joined_on")
@@ -1455,10 +1649,12 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert project.respond_to?("name=")
assert project.respond_to?("name?")
assert project.respond_to?("joined_on")
- assert project.respond_to?("joined_on=")
+ # given that the 'join attribute' won't be persisted, I don't
+ # think we should define the mutators
+ #assert project.respond_to?("joined_on=")
assert project.respond_to?("joined_on?")
assert project.respond_to?("access_level")
- assert project.respond_to?("access_level=")
+ #assert project.respond_to?("access_level=")
assert project.respond_to?("access_level?")
end
@@ -1477,31 +1673,6 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal 2, aredridel.projects(true).size
end
- def test_habtm_adding_before_save_with_join_attributes
- no_of_devels = Developer.count
- no_of_projects = Project.count
- now = Date.today
- ken = Developer.new("name" => "Ken")
- assert_deprecated do
- ken.projects.push_with_attributes( Project.find(1), :joined_on => now )
- end
- p = Project.new("name" => "Foomatic")
- assert_deprecated do
- ken.projects.push_with_attributes( p, :joined_on => now )
- end
- assert ken.new_record?
- assert p.new_record?
- assert ken.save
- assert !ken.new_record?
- assert_equal no_of_devels+1, Developer.count
- assert_equal no_of_projects+1, Project.count
- assert_equal 2, ken.projects.size
- assert_equal 2, ken.projects(true).size
-
- kenReloaded = Developer.find_by_name 'Ken'
- kenReloaded.projects.each {|prj| assert_date_from_db(now, prj.joined_on)}
- end
-
def test_habtm_saving_multiple_relationships
new_project = Project.new("name" => "Grimetime")
amount_of_developers = 4
@@ -1517,8 +1688,8 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
end
def test_habtm_unique_order_preserved
- assert_equal [developers(:poor_jamis), developers(:jamis), developers(:david)], projects(:active_record).non_unique_developers
- assert_equal [developers(:poor_jamis), developers(:jamis), developers(:david)], projects(:active_record).developers
+ assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
+ assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
end
def test_build
@@ -1531,7 +1702,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal devel.projects.last, proj
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
-
+
def test_build_by_new_record
devel = Developer.new(:name => "Marcel", :salary => 75000)
proj1 = devel.projects.build(:name => "Make bed")
@@ -1544,7 +1715,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal devel.projects.last, proj2
assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
end
-
+
def test_create
devel = Developer.find(1)
proj = devel.projects.create("name" => "Projekt")
@@ -1552,11 +1723,11 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert !proj.new_record?
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
-
+
def test_create_by_new_record
devel = Developer.new(:name => "Marcel", :salary => 75000)
- proj1 = devel.projects.create(:name => "Make bed")
- proj2 = devel.projects.create(:name => "Lie in it")
+ proj1 = devel.projects.build(:name => "Make bed")
+ proj2 = devel.projects.build(:name => "Lie in it")
assert_equal devel.projects.last, proj2
assert proj2.new_record?
devel.save
@@ -1565,7 +1736,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal devel.projects.last, proj2
assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
end
-
+
def test_uniq_after_the_fact
developers(:jamis).projects << projects(:active_record)
developers(:jamis).projects << projects(:active_record)
@@ -1578,7 +1749,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
projects(:active_record).developers << developers(:david)
assert_equal 3, projects(:active_record, :reload).developers.size
end
-
+
def test_deleting
david = Developer.find(1)
active_record = Project.find(1)
@@ -1587,7 +1758,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal 3, active_record.developers.size
david.projects.delete(active_record)
-
+
assert_equal 1, david.projects.size
assert_equal 1, david.projects(true).size
assert_equal 2, active_record.developers(true).size
@@ -1606,7 +1777,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
active_record = Project.find(1)
active_record.developers.reload
assert_equal 3, active_record.developers_by_sql.size
-
+
active_record.developers_by_sql.delete(david)
assert_equal 2, active_record.developers_by_sql(true).size
end
@@ -1615,7 +1786,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
active_record = Project.find(1)
active_record.developers.reload
assert_equal 3, active_record.developers_by_sql.size
-
+
active_record.developers_by_sql.delete(Developer.find(:all))
assert_equal 0, active_record.developers_by_sql(true).size
end
@@ -1637,9 +1808,9 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
end
def test_additional_columns_from_join_table
- assert_date_from_db Date.new(2004, 10, 10), Developer.find(1).projects.first.joined_on
+ assert_date_from_db Date.new(2004, 10, 10), Developer.find(1).projects.first.joined_on.to_date
end
-
+
def test_destroy_all
david = Developer.find(1)
david.projects.reload
@@ -1649,14 +1820,11 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert david.projects(true).empty?
end
- def test_rich_association
+ def test_deprecated_push_with_attributes_was_removed
jamis = developers(:jamis)
- assert_deprecated 'push_with_attributes' do
+ assert_raise(NoMethodError) do
jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
end
-
- assert_date_from_db Date.today, jamis.projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on
- assert_date_from_db Date.today, developers(:jamis).projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on
end
def test_associations_with_conditions
@@ -1671,11 +1839,11 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
projects(:active_record).developers_named_david.clear
assert_equal 2, projects(:active_record, :reload).developers.size
end
-
+
def test_find_in_association
# Using sql
assert_equal developers(:david), projects(:active_record).developers.find(developers(:david).id), "SQL find"
-
+
# Using ruby
active_record = projects(:active_record)
active_record.developers.reload
@@ -1684,7 +1852,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
def test_find_in_association_with_custom_finder_sql
assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find"
-
+
active_record = projects(:active_record)
active_record.developers_with_finder_sql.reload
assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
@@ -1700,6 +1868,56 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal 3, projects(:active_record).limited_developers.find(:all, :limit => nil).size
end
+ def test_dynamic_find_should_respect_association_order
+ # Developers are ordered 'name DESC, id DESC'
+ low_id_jamis = developers(:jamis)
+ middle_id_jamis = developers(:poor_jamis)
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+
+ assert_equal high_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'")
+ assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
+ end
+
+ def test_dynamic_find_order_should_override_association_order
+ # Developers are ordered 'name DESC, id DESC'
+ low_id_jamis = developers(:jamis)
+ middle_id_jamis = developers(:poor_jamis)
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+
+ assert_equal low_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'", :order => 'id')
+ assert_equal low_id_jamis, projects(:active_record).developers.find_by_name('Jamis', :order => 'id')
+ end
+
+ def test_dynamic_find_all_should_respect_association_order
+ # Developers are ordered 'name DESC, id DESC'
+ low_id_jamis = developers(:jamis)
+ middle_id_jamis = developers(:poor_jamis)
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+
+ assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'")
+ assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis')
+ end
+
+ def test_dynamic_find_all_order_should_override_association_order
+ # Developers are ordered 'name DESC, id DESC'
+ low_id_jamis = developers(:jamis)
+ middle_id_jamis = developers(:poor_jamis)
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
+
+ assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'", :order => 'id')
+ assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis', :order => 'id')
+ end
+
+ def test_dynamic_find_all_should_respect_association_limit
+ assert_equal 1, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'").length
+ assert_equal 1, projects(:active_record).limited_developers.find_all_by_name('Jamis').length
+ end
+
+ def test_dynamic_find_all_order_should_override_association_limit
+ assert_equal 2, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'", :limit => 9_000).length
+ assert_equal 2, projects(:active_record).limited_developers.find_all_by_name('Jamis', :limit => 9_000).length
+ end
+
def test_new_with_values_in_collection
jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
@@ -1715,11 +1933,11 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
def test_find_in_association_with_options
developers = projects(:active_record).developers.find(:all)
assert_equal 3, developers.size
-
+
assert_equal developers(:poor_jamis), projects(:active_record).developers.find(:first, :conditions => "salary < 10000")
assert_equal developers(:jamis), projects(:active_record).developers.find(:first, :order => "salary DESC")
end
-
+
def test_replace_with_less
david = developers(:david)
david.projects = [projects(:action_controller)]
@@ -1734,7 +1952,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal 2, david.projects.length
assert !david.projects.include?(projects(:active_record))
end
-
+
def test_replace_on_new_object
new_developer = Developer.new("name" => "Matz")
new_developer.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
@@ -1745,16 +1963,16 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
def test_consider_type
developer = Developer.find(:first)
special_project = SpecialProject.create("name" => "Special Project")
-
+
other_project = developer.projects.first
developer.special_projects << special_project
developer.reload
-
+
assert developer.projects.include?(special_project)
assert developer.special_projects.include?(special_project)
assert !developer.special_projects.include?(other_project)
end
-
+
def test_update_attributes_after_push_without_duplicate_join_table_rows
developer = Developer.new("name" => "Kano")
project = SpecialProject.create("name" => "Special Project")
@@ -1767,20 +1985,33 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
AND developer_id = #{developer.id}
end_sql
end
-
+
def test_updating_attributes_on_non_rich_associations
welcome = categories(:technology).posts.first
welcome.title = "Something else"
assert welcome.save!
end
-
+
+ def test_habtm_respects_select
+ categories(:technology).select_testing_posts(true).each do |o|
+ assert_respond_to o, :correctness_marker
+ end
+ assert_respond_to categories(:technology).select_testing_posts.find(:first), :correctness_marker
+ end
+
def test_updating_attributes_on_rich_associations
david = projects(:action_controller).developers.first
david.name = "DHH"
assert_raises(ActiveRecord::ReadOnlyRecord) { david.save! }
end
-
+ def test_updating_attributes_on_rich_associations_with_limited_find_from_reflection
+ david = projects(:action_controller).selected_developers.first
+ david.name = "DHH"
+ assert_nothing_raised { david.save! }
+ end
+
+
def test_updating_attributes_on_rich_associations_with_limited_find
david = projects(:action_controller).developers.find(:all, :select => "developers.*").first
david.name = "DHH"
@@ -1790,7 +2021,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
def test_join_table_alias
assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
end
-
+
def test_join_with_group
group = Developer.columns.inject([]) do |g, c|
g << "developers.#{c.name}"
@@ -1802,18 +2033,18 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
end
def test_get_ids
- assert_equal [projects(:active_record).id, projects(:action_controller).id], developers(:david).project_ids
+ assert_equal projects(:active_record, :action_controller).map(&:id), developers(:david).project_ids
assert_equal [projects(:active_record).id], developers(:jamis).project_ids
end
def test_assign_ids
developer = Developer.new("name" => "Joe")
- developer.project_ids = [projects(:active_record).id, projects(:action_controller).id]
+ developer.project_ids = projects(:active_record, :action_controller).map(&:id)
developer.save
developer.reload
assert_equal 2, developer.projects.length
- assert_equal projects(:active_record), developer.projects[0]
- assert_equal projects(:action_controller), developer.projects[1]
+ assert_equal projects(:active_record), developer.projects[0]
+ assert_equal projects(:action_controller), developer.projects[1]
end
def test_assign_ids_ignoring_blanks
@@ -1822,8 +2053,8 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
developer.save
developer.reload
assert_equal 2, developer.projects.length
- assert_equal projects(:active_record), developer.projects[0]
- assert_equal projects(:action_controller), developer.projects[1]
+ assert_equal projects(:active_record), developer.projects[0]
+ assert_equal projects(:action_controller), developer.projects[1]
end
def test_select_limited_ids_list
@@ -1837,6 +2068,80 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
join_base = ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase.new(Project)
join_dep = ActiveRecord::Associations::ClassMethods::JoinDependency.new(join_base, :developers, nil)
projects = Project.send(:select_limited_ids_list, {:order => 'developers.created_at'}, join_dep)
+ assert !projects.include?("'"), projects
assert_equal %w(1 2), projects.scan(/\d/).sort
end
+
+ def test_scoped_find_on_through_association_doesnt_return_read_only_records
+ tag = Post.find(1).tags.find_by_name("General")
+
+ assert_nothing_raised do
+ tag.save!
+ end
+ end
+end
+
+
+class OverridingAssociationsTest < Test::Unit::TestCase
+ class Person < ActiveRecord::Base; end
+ class DifferentPerson < ActiveRecord::Base; end
+
+ class PeopleList < ActiveRecord::Base
+ has_and_belongs_to_many :has_and_belongs_to_many, :before_add => :enlist
+ has_many :has_many, :before_add => :enlist
+ belongs_to :belongs_to
+ has_one :has_one
+ end
+
+ class DifferentPeopleList < PeopleList
+ # Different association with the same name, callbacks should be omitted here.
+ has_and_belongs_to_many :has_and_belongs_to_many, :class_name => 'DifferentPerson'
+ has_many :has_many, :class_name => 'DifferentPerson'
+ belongs_to :belongs_to, :class_name => 'DifferentPerson'
+ has_one :has_one, :class_name => 'DifferentPerson'
+ end
+
+ def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
+ # redeclared association on AR descendant should not inherit callbacks from superclass
+ callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
+ assert_equal([:enlist], callbacks)
+ callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
+ assert_equal([], callbacks)
+ end
+
+ def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited
+ # redeclared association on AR descendant should not inherit callbacks from superclass
+ callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_many)
+ assert_equal([:enlist], callbacks)
+ callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_many)
+ assert_equal([], callbacks)
+ end
+
+ def test_habtm_association_redefinition_reflections_should_differ_and_not_inherited
+ assert_not_equal(
+ PeopleList.reflect_on_association(:has_and_belongs_to_many),
+ DifferentPeopleList.reflect_on_association(:has_and_belongs_to_many)
+ )
+ end
+
+ def test_has_many_association_redefinition_reflections_should_differ_and_not_inherited
+ assert_not_equal(
+ PeopleList.reflect_on_association(:has_many),
+ DifferentPeopleList.reflect_on_association(:has_many)
+ )
+ end
+
+ def test_belongs_to_association_redefinition_reflections_should_differ_and_not_inherited
+ assert_not_equal(
+ PeopleList.reflect_on_association(:belongs_to),
+ DifferentPeopleList.reflect_on_association(:belongs_to)
+ )
+ end
+
+ def test_has_one_association_redefinition_reflections_should_differ_and_not_inherited
+ assert_not_equal(
+ PeopleList.reflect_on_association(:has_one),
+ DifferentPeopleList.reflect_on_association(:has_one)
+ )
+ end
end
diff --git a/vendor/rails/activerecord/test/attribute_methods_test.rb b/vendor/rails/activerecord/test/attribute_methods_test.rb
index 0fa13c65..8646afdc 100755
--- a/vendor/rails/activerecord/test/attribute_methods_test.rb
+++ b/vendor/rails/activerecord/test/attribute_methods_test.rb
@@ -1,6 +1,8 @@
require 'abstract_unit'
+require 'fixtures/topic'
class AttributeMethodsTest < Test::Unit::TestCase
+ fixtures :topics
def setup
@old_suffixes = ActiveRecord::Base.send(:attribute_method_suffixes).dup
@target = Class.new(ActiveRecord::Base)
@@ -46,4 +48,99 @@ class AttributeMethodsTest < Test::Unit::TestCase
assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
end
end
+
+ def test_should_unserialize_attributes_for_frozen_records
+ myobj = {:value1 => :value2}
+ topic = Topic.create("content" => myobj)
+ topic.freeze
+ assert_equal myobj, topic.content
+ end
+
+ def test_kernel_methods_not_implemented_in_activerecord
+ %w(test name display y).each do |method|
+ assert_equal false, ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
+ end
+ end
+
+ def test_primary_key_implemented
+ assert_equal true, Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
+ end
+
+ def test_defined_kernel_methods_implemented_in_model
+ %w(test name display y).each do |method|
+ klass = Class.new ActiveRecord::Base
+ klass.class_eval "def #{method}() 'defined #{method}' end"
+ assert_equal true, klass.instance_method_already_implemented?(method), "##{method} is not defined"
+ end
+ end
+
+ def test_defined_kernel_methods_implemented_in_model_abstract_subclass
+ %w(test name display y).each do |method|
+ abstract = Class.new ActiveRecord::Base
+ abstract.class_eval "def #{method}() 'defined #{method}' end"
+ abstract.abstract_class = true
+ klass = Class.new abstract
+ assert_equal true, klass.instance_method_already_implemented?(method), "##{method} is not defined"
+ end
+ end
+
+ def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
+ %w(save create_or_update).each do |method|
+ klass = Class.new ActiveRecord::Base
+ klass.class_eval "def #{method}() 'defined #{method}' end"
+ assert_raises ActiveRecord::DangerousAttributeError do
+ klass.instance_method_already_implemented?(method)
+ end
+ end
+ end
+
+ def test_only_time_related_columns_are_meant_to_be_cached_by_default
+ expected = %w(datetime timestamp time date).sort
+ assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
+end
+
+ def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
+ default_attributes = Topic.cached_attributes
+ Topic.cache_attributes :replies_count
+ expected = default_attributes + ["replies_count"]
+ assert_equal expected.sort, Topic.cached_attributes.sort
+ Topic.instance_variable_set "@cached_attributes", nil
+ end
+
+ def test_time_related_columns_are_actually_cached
+ column_types = %w(datetime timestamp time date).map(&:to_sym)
+ column_names = Topic.columns.select{|c| column_types.include?(c.type) }.map(&:name)
+
+ assert_equal column_names.sort, Topic.cached_attributes.sort
+ assert_equal time_related_columns_on_topic.sort, Topic.cached_attributes.sort
+ end
+
+ def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
+ t = topics(:first)
+ cache = t.instance_variable_get "@attributes_cache"
+
+ assert_not_nil cache
+ assert cache.empty?
+
+ all_columns = Topic.columns.map(&:name)
+ cached_columns = time_related_columns_on_topic
+ uncached_columns = all_columns - cached_columns
+
+ all_columns.each do |attr_name|
+ attribute_gets_cached = Topic.cache_attribute?(attr_name)
+ val = t.send attr_name unless attr_name == "type"
+ if attribute_gets_cached
+ assert cached_columns.include?(attr_name)
+ assert_equal val, cache[attr_name]
+ else
+ assert uncached_columns.include?(attr_name)
+ assert !cache.include?(attr_name)
+ end
+ end
+ end
+
+ private
+ def time_related_columns_on_topic
+ Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
+ end
end
diff --git a/vendor/rails/activerecord/test/base_test.rb b/vendor/rails/activerecord/test/base_test.rb
index fd4ddc0a..3a5fdac1 100755
--- a/vendor/rails/activerecord/test/base_test.rb
+++ b/vendor/rails/activerecord/test/base_test.rb
@@ -11,6 +11,8 @@ require 'fixtures/column_name'
require 'fixtures/subscriber'
require 'fixtures/keyboard'
require 'fixtures/post'
+require 'fixtures/minimalistic'
+require 'rexml/document'
class Category < ActiveRecord::Base; end
class Smarts < ActiveRecord::Base; end
@@ -38,6 +40,11 @@ class LooseDescendant < LoosePerson
attr_protected :phone_number
end
+class LooseDescendantSecond< LoosePerson
+ attr_protected :phone_number
+ attr_protected :name
+end
+
class TightPerson < ActiveRecord::Base
self.table_name = 'people'
attr_accessible :name, :address
@@ -47,14 +54,24 @@ class TightDescendant < TightPerson
attr_accessible :phone_number
end
+class ReadonlyTitlePost < Post
+ attr_readonly :title
+end
+
class Booleantest < ActiveRecord::Base; end
class Task < ActiveRecord::Base
attr_protected :starting
end
+class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
+ self.table_name = 'topics'
+ attr_accessible :author_name
+ attr_protected :content
+end
+
class BasicsTest < Test::Unit::TestCase
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics
def test_table_exists
assert !NonExistentTable.table_exists?
@@ -174,6 +191,15 @@ class BasicsTest < Test::Unit::TestCase
assert_nil topic.title
end
+ def test_save_for_record_with_only_primary_key
+ minimalistic = Minimalistic.new
+ assert_nothing_raised { minimalistic.save }
+ end
+
+ def test_save_for_record_with_only_primary_key_that_is_provided
+ assert_nothing_raised { Minimalistic.create!(:id => 2) }
+ end
+
def test_hashes_not_mangled
new_topic = { :title => "New Topic" }
new_topic_values = { :title => "AnotherTopic" }
@@ -230,6 +256,11 @@ class BasicsTest < Test::Unit::TestCase
topicReloaded.send :write_attribute, 'does_not_exist', 'test'
assert_nothing_raised { topicReloaded.save }
end
+
+ def test_update_for_record_with_only_primary_key
+ minimalistic = minimalistics(:first)
+ assert_nothing_raised { minimalistic.save }
+ end
def test_write_attribute
topic = Topic.new
@@ -289,25 +320,59 @@ class BasicsTest < Test::Unit::TestCase
assert topic.approved?, "approved should be true"
# puts ""
end
-
- def test_reader_generation
- Topic.find(:first).title
- Firm.find(:first).name
- Client.find(:first).name
- if ActiveRecord::Base.generate_read_methods
- assert_readers(Topic, %w(type replies_count))
- assert_readers(Firm, %w(type))
- assert_readers(Client, %w(type ruby_type rating?))
- else
- [Topic, Firm, Client].each {|klass| assert_equal klass.read_methods, {}}
+
+ def test_query_attribute_string
+ [nil, "", " "].each do |value|
+ assert_equal false, Topic.new(:author_name => value).author_name?
+ end
+
+ assert_equal true, Topic.new(:author_name => "Name").author_name?
+ end
+
+ def test_query_attribute_number
+ [nil, 0, "0"].each do |value|
+ assert_equal false, Developer.new(:salary => value).salary?
+ end
+
+ assert_equal true, Developer.new(:salary => 1).salary?
+ assert_equal true, Developer.new(:salary => "1").salary?
+ end
+
+ def test_query_attribute_boolean
+ [nil, "", false, "false", "f", 0].each do |value|
+ assert_equal false, Topic.new(:approved => value).approved?
+ end
+
+ [true, "true", "1", 1].each do |value|
+ assert_equal true, Topic.new(:approved => value).approved?
end
end
+ def test_query_attribute_with_custom_fields
+ object = Company.find_by_sql(<<-SQL).first
+ SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
+ FROM companies c1, companies c2
+ WHERE c1.firm_id = c2.id
+ AND c1.id = 2
+ SQL
+
+ assert_equal "Firm", object.string_value
+ assert object.string_value?
+
+ object.string_value = " "
+ assert !object.string_value?
+
+ assert_equal 1, object.int_value.to_i
+ assert object.int_value?
+
+ object.int_value = "0"
+ assert !object.int_value?
+ end
+
+
def test_reader_for_invalid_column_names
- # column names which aren't legal ruby ids
- topic = Topic.find(:first)
- topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
- assert !Topic.read_methods.include?("mumub-jumbo")
+ Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
+ assert !Topic.generated_methods.include?("mumub-jumbo")
end
def test_non_attribute_access_and_assignment
@@ -321,8 +386,9 @@ class BasicsTest < Test::Unit::TestCase
# SQL Server doesn't have a separate column type just for dates, so all are returned as time
return true if current_adapter?(:SQLServerAdapter)
- if current_adapter?(:SybaseAdapter)
+ if current_adapter?(:SybaseAdapter, :OracleAdapter)
# Sybase ctlib does not (yet?) support the date type; use datetime instead.
+ # Oracle treats all dates/times as Time.
assert_kind_of(
Time, Topic.find(1).last_read,
"The last_read attribute should be of the Time class"
@@ -353,6 +419,13 @@ class BasicsTest < Test::Unit::TestCase
assert_equal 9900, Topic.find(2).written_on.usec
end
end
+
+ def test_custom_mutator
+ topic = Topic.find(1)
+ # This mutator is protected in the class definition
+ topic.send(:approved=, true)
+ assert topic.instance_variable_get("@custom_approved")
+ end
def test_destroy
topic = Topic.find(1)
@@ -494,17 +567,33 @@ class BasicsTest < Test::Unit::TestCase
Topic.decrement_counter("replies_count", 2)
assert_equal -2, Topic.find(2).replies_count
end
-
- def test_update_all
- # The ADO library doesn't support the number of affected rows
- return true if current_adapter?(:SQLServerAdapter)
+ def test_update_all
assert_equal 2, Topic.update_all("content = 'bulk updated!'")
assert_equal "bulk updated!", Topic.find(1).content
assert_equal "bulk updated!", Topic.find(2).content
+
assert_equal 2, Topic.update_all(['content = ?', 'bulk updated again!'])
assert_equal "bulk updated again!", Topic.find(1).content
assert_equal "bulk updated again!", Topic.find(2).content
+
+ assert_equal 2, Topic.update_all(['content = ?', nil])
+ assert_nil Topic.find(1).content
+ end
+
+ def test_update_all_with_hash
+ assert_not_nil Topic.find(1).last_read
+ assert_equal 2, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
+ assert_equal "bulk updated with hash!", Topic.find(1).content
+ assert_equal "bulk updated with hash!", Topic.find(2).content
+ assert_nil Topic.find(1).last_read
+ assert_nil Topic.find(2).last_read
+ end
+
+ if current_adapter?(:MysqlAdapter)
+ def test_update_all_with_order_and_limit
+ assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
+ end
end
def test_update_many
@@ -517,9 +606,6 @@ class BasicsTest < Test::Unit::TestCase
end
def test_delete_all
- # The ADO library doesn't support the number of affected rows
- return true if current_adapter?(:SQLServerAdapter)
-
assert_equal 2, Topic.delete_all
end
@@ -703,6 +789,12 @@ class BasicsTest < Test::Unit::TestCase
assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
end
+ def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
+ topic = TopicWithProtectedContentAndAccessibleAuthorName.new
+ assert_raises(RuntimeError) { topic.attributes = { "author_name" => "me" } }
+ assert_raises(RuntimeError) { topic.attributes = { "content" => "stuff" } }
+ end
+
def test_mass_assignment_protection
firm = Firm.new
firm.attributes = { "name" => "Next Angle", "rating" => 5 }
@@ -711,7 +803,7 @@ class BasicsTest < Test::Unit::TestCase
def test_mass_assignment_protection_against_class_attribute_writers
[:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
- :default_timezone, :allow_concurrency, :generate_read_methods, :schema_format, :verification_timeout, :lock_optimistically, :record_timestamps].each do |method|
+ :default_timezone, :allow_concurrency, :schema_format, :verification_timeout, :lock_optimistically, :record_timestamps].each do |method|
assert Task.respond_to?(method)
assert Task.respond_to?("#{method}=")
assert Task.new.respond_to?(method)
@@ -727,7 +819,7 @@ class BasicsTest < Test::Unit::TestCase
assert_nil keyboard.id
end
- def test_customized_primary_key_remains_protected_when_refered_to_as_id
+ def test_customized_primary_key_remains_protected_when_referred_to_as_id
subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
assert_nil subscriber.id
@@ -756,16 +848,32 @@ class BasicsTest < Test::Unit::TestCase
def test_mass_assignment_protection_inheritance
assert_nil LoosePerson.accessible_attributes
- assert_equal [ :credit_rating, :administrator ], LoosePerson.protected_attributes
+ assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
assert_nil LooseDescendant.accessible_attributes
- assert_equal [ :credit_rating, :administrator, :phone_number ], LooseDescendant.protected_attributes
+ assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number' ]), LooseDescendant.protected_attributes
+
+ assert_nil LooseDescendantSecond.accessible_attributes
+ assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number', 'name' ]), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections'
assert_nil TightPerson.protected_attributes
- assert_equal [ :name, :address ], TightPerson.accessible_attributes
+ assert_equal Set.new([ 'name', 'address' ]), TightPerson.accessible_attributes
assert_nil TightDescendant.protected_attributes
- assert_equal [ :name, :address, :phone_number ], TightDescendant.accessible_attributes
+ assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
+ end
+
+ def test_readonly_attributes
+ assert_equal Set.new([ 'title' ]), ReadonlyTitlePost.readonly_attributes
+
+ post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
+ post.reload
+ assert_equal "cannot change this", post.title
+
+ post.update_attributes(:title => "try to change", :body => "changed")
+ post.reload
+ assert_equal "cannot change this", post.title
+ assert_equal "changed", post.body
end
def test_multiparameter_attributes_on_date
@@ -885,6 +993,10 @@ class BasicsTest < Test::Unit::TestCase
cloned_topic.title["a"] = "c"
assert_equal "b", topic.title["a"]
+ #test if attributes set as part of after_initialize are cloned correctly
+ assert_equal topic.author_email_address, cloned_topic.author_email_address
+
+ # test if saved clone object differs from original
cloned_topic.save
assert !cloned_topic.new_record?
assert cloned_topic.id != topic.id
@@ -1149,12 +1261,12 @@ class BasicsTest < Test::Unit::TestCase
end
def test_increment_attribute
- assert_equal 1, topics(:first).replies_count
- topics(:first).increment! :replies_count
- assert_equal 2, topics(:first, :reload).replies_count
-
- topics(:first).increment(:replies_count).increment!(:replies_count)
- assert_equal 4, topics(:first, :reload).replies_count
+ assert_equal 50, accounts(:signals37).credit_limit
+ accounts(:signals37).increment! :credit_limit
+ assert_equal 51, accounts(:signals37, :reload).credit_limit
+
+ accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
+ assert_equal 53, accounts(:signals37, :reload).credit_limit
end
def test_increment_nil_attribute
@@ -1164,14 +1276,13 @@ class BasicsTest < Test::Unit::TestCase
end
def test_decrement_attribute
- topics(:first).increment(:replies_count).increment!(:replies_count)
- assert_equal 3, topics(:first).replies_count
-
- topics(:first).decrement!(:replies_count)
- assert_equal 2, topics(:first, :reload).replies_count
+ assert_equal 50, accounts(:signals37).credit_limit
- topics(:first).decrement(:replies_count).decrement!(:replies_count)
- assert_equal 0, topics(:first, :reload).replies_count
+ accounts(:signals37).decrement!(:credit_limit)
+ assert_equal 49, accounts(:signals37, :reload).credit_limit
+
+ accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
+ assert_equal 47, accounts(:signals37, :reload).credit_limit
end
def test_toggle_attribute
@@ -1250,11 +1361,8 @@ class BasicsTest < Test::Unit::TestCase
def test_count_with_join
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
- res2 = nil
- assert_deprecated 'count' do
- res2 = Post.count("posts.#{QUOTED_TYPE} = 'Post'",
- "LEFT JOIN comments ON posts.id=comments.post_id")
- end
+
+ res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
assert_equal res, res2
res3 = nil
@@ -1274,15 +1382,17 @@ class BasicsTest < Test::Unit::TestCase
assert_equal res4, res5
- res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
- res7 = nil
- assert_nothing_raised do
- res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
- :joins => "p, comments co",
- :select => "p.id",
- :distinct => true)
+ unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
+ res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
+ res7 = nil
+ assert_nothing_raised do
+ res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
+ :joins => "p, comments co",
+ :select => "p.id",
+ :distinct => true)
+ end
+ assert_equal res6, res7
end
- assert_equal res6, res7
end
def test_clear_association_cache_stored
@@ -1299,12 +1409,12 @@ class BasicsTest < Test::Unit::TestCase
client_new = Client.new
client_new.name = "The Joneses"
clients = [ client_stored, client_new ]
-
+
firm.clients << clients
+ assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
firm.clear_association_cache
-
- assert_equal firm.clients.collect{ |x| x.name }.sort, clients.collect{ |x| x.name }.sort
+ assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
end
def test_interpolate_sql
@@ -1442,28 +1552,48 @@ class BasicsTest < Test::Unit::TestCase
end
def test_to_xml
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true)
+ xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
written_on_in_current_timezone = topics(:first).written_on.xmlschema
last_read_in_current_timezone = topics(:first).last_read.xmlschema
- assert_equal "", xml.first(7)
- assert xml.include?(%(The First Topic ))
- assert xml.include?(%(David ))
- assert xml.include?(%(1 ))
- assert xml.include?(%(1 ))
- assert xml.include?(%(#{written_on_in_current_timezone} ))
- assert xml.include?(%(Have a nice day ))
- assert xml.include?(%(david@loudthinking.com ))
- assert xml.match(%( ))
+
+ assert_equal "topic", xml.root.name
+ assert_equal "The First Topic" , xml.elements["//title"].text
+ assert_equal "David" , xml.elements["//author-name"].text
+
+ assert_equal "1", xml.elements["//id"].text
+ assert_equal "integer" , xml.elements["//id"].attributes['type']
+
+ assert_equal "1", xml.elements["//replies-count"].text
+ assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
+
+ assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
+ assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
+
+ assert_equal "--- Have a nice day\n" , xml.elements["//content"].text
+ assert_equal "yaml" , xml.elements["//content"].attributes['type']
+
+ assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
+
+ assert_equal nil, xml.elements["//parent-id"].text
+ assert_equal "integer", xml.elements["//parent-id"].attributes['type']
+ assert_equal "true", xml.elements["//parent-id"].attributes['nil']
+
if current_adapter?(:SybaseAdapter, :SQLServerAdapter, :OracleAdapter)
- assert xml.include?(%(#{last_read_in_current_timezone} ))
+ assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
+ assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
else
- assert xml.include?(%(2004-04-15 ))
+ assert_equal "2004-04-15", xml.elements["//last-read"].text
+ assert_equal "date" , xml.elements["//last-read"].attributes['type']
end
+
# Oracle and DB2 don't have true boolean or time-only fields
unless current_adapter?(:OracleAdapter, :DB2Adapter)
- assert xml.include?(%(false )), "Approved should be a boolean"
- assert xml.include?(%(#{bonus_time_in_current_timezone} ))
+ assert_equal "false", xml.elements["//approved"].text
+ assert_equal "boolean" , xml.elements["//approved"].attributes['type']
+
+ assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
+ assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
end
end
@@ -1481,13 +1611,13 @@ class BasicsTest < Test::Unit::TestCase
def test_to_xml_including_has_many_association
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
assert_equal "", xml.first(7)
- assert xml.include?(%())
+ assert xml.include?(%())
assert xml.include?(%(The Second Topic's of the day ))
end
def test_array_to_xml_including_has_many_association
xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
- assert xml.include?(%())
+ assert xml.include?(%())
end
def test_array_to_xml_including_methods
@@ -1521,7 +1651,7 @@ class BasicsTest < Test::Unit::TestCase
xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
assert_equal "", xml.first(6)
assert xml.include?(%())
- assert xml.include?(%())
+ assert xml.include?(%())
end
def test_to_xml_including_multiple_associations_with_options
@@ -1532,7 +1662,7 @@ class BasicsTest < Test::Unit::TestCase
assert_equal "", xml.first(6)
assert xml.include?(%(Summit ))
- assert xml.include?(%())
+ assert xml.include?(%())
end
def test_to_xml_including_methods
@@ -1541,6 +1671,15 @@ class BasicsTest < Test::Unit::TestCase
assert xml.include?(%(I am Jack's profound disappointment ))
end
+ def test_to_xml_with_block
+ value = "Rockin' the block"
+ xml = Company.new.to_xml(:skip_instruct => true) do |xml|
+ xml.tag! "arbitrary-element", value
+ end
+ assert_equal "", xml.first(9)
+ assert xml.include?(%(#{value} ))
+ end
+
def test_except_attributes
assert_equal(
%w( author_name type id approved replies_count bonus_time written_on content author_email_address parent_id last_read),
@@ -1566,34 +1705,41 @@ class BasicsTest < Test::Unit::TestCase
def test_to_param_should_return_string
assert_kind_of String, Client.find(:first).to_param
end
-
- # FIXME: this test ought to run, but it needs to run sandboxed so that it
- # doesn't b0rk the current test environment by undefing everything.
- #
- #def test_dev_mode_memory_leak
- # counts = []
- # 2.times do
- # require_dependency 'fixtures/company'
- # Firm.find(:first)
- # Dependencies.clear
- # ActiveRecord::Base.reset_subclasses
- # Dependencies.remove_subclasses_for(ActiveRecord::Base)
- #
- # GC.start
- #
- # count = 0
- # ObjectSpace.each_object(Proc) { count += 1 }
- # counts << count
- # end
- # assert counts.last <= counts.first,
- # "expected last count (#{counts.last}) to be <= first count (#{counts.first})"
- #end
- private
- def assert_readers(model, exceptions)
- expected_readers = Set.new(model.column_names - ['id'])
- expected_readers += expected_readers.map { |col| "#{col}?" }
- expected_readers -= exceptions
- assert_equal expected_readers, model.read_methods
- end
+ def test_inspect_class
+ assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
+ assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
+ assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
+ end
+
+ def test_inspect_instance
+ topic = topics(:first)
+ assert_equal %(#), topic.inspect
+ end
+
+ def test_inspect_new_instance
+ assert_match /Topic id: nil/, Topic.new.inspect
+ end
+
+ def test_inspect_limited_select_instance
+ assert_equal %(#), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
+ assert_equal %(#), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
+ end
+
+ def test_inspect_class_without_table
+ assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
+ end
+
+ def test_attribute_for_inspect
+ t = topics(:first)
+ t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
+
+ assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
+ assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
+ end
+
+ def test_becomes
+ assert_kind_of Reply, topics(:first).becomes(Reply)
+ assert_equal "The First Topic", topics(:first).becomes(Reply).title
+ end
end
diff --git a/vendor/rails/activerecord/test/binary_test.rb b/vendor/rails/activerecord/test/binary_test.rb
index 38a5d509..6ab272fe 100644
--- a/vendor/rails/activerecord/test/binary_test.rb
+++ b/vendor/rails/activerecord/test/binary_test.rb
@@ -1,37 +1,32 @@
require 'abstract_unit'
-require 'fixtures/binary'
-class BinaryTest < Test::Unit::TestCase
- BINARY_FIXTURE_PATH = File.dirname(__FILE__) + '/fixtures/flowers.jpg'
+# Without using prepared statements, it makes no sense to test
+# BLOB data with SQL Server, because the length of a statement is
+# limited to 8KB.
+#
+# Without using prepared statements, it makes no sense to test
+# BLOB data with DB2 or Firebird, because the length of a statement
+# is limited to 32KB.
+unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
+ require 'fixtures/binary'
- def setup
- Binary.connection.execute 'DELETE FROM binaries'
- @data = File.read(BINARY_FIXTURE_PATH).freeze
- end
-
- def test_truth
- assert true
- end
+ class BinaryTest < Test::Unit::TestCase
+ FIXTURES = %w(flowers.jpg example.log)
- # Without using prepared statements, it makes no sense to test
- # BLOB data with SQL Server, because the length of a statement is
- # limited to 8KB.
- #
- # Without using prepared statements, it makes no sense to test
- # BLOB data with DB2 or Firebird, because the length of a statement
- # is limited to 32KB.
- unless %w(SQLServer Sybase DB2 Oracle Firebird).include? ActiveRecord::Base.connection.adapter_name
def test_load_save
- bin = Binary.new
- bin.data = @data
+ Binary.delete_all
- assert @data == bin.data, 'Newly assigned data differs from original'
-
- bin.save
- assert @data == bin.data, 'Data differs from original after save'
+ FIXTURES.each do |filename|
+ data = File.read("#{File.dirname(__FILE__)}/fixtures/#{filename}").freeze
- db_bin = Binary.find(bin.id)
- assert @data == db_bin.data, 'Reloaded data differs from original'
+ bin = Binary.new(:data => data)
+ assert_equal data, bin.data, 'Newly assigned data differs from original'
+
+ bin.save!
+ assert_equal data, bin.data, 'Data differs from original after save'
+
+ assert_equal data, bin.reload.data, 'Reloaded data differs from original'
+ end
end
end
end
diff --git a/vendor/rails/activerecord/test/calculations_test.rb b/vendor/rails/activerecord/test/calculations_test.rb
index e450c0a9..298a1d18 100644
--- a/vendor/rails/activerecord/test/calculations_test.rb
+++ b/vendor/rails/activerecord/test/calculations_test.rb
@@ -4,6 +4,10 @@ require 'fixtures/topic'
Company.has_many :accounts
+class NumericData < ActiveRecord::Base
+ self.table_name = 'numeric_data'
+end
+
class CalculationsTest < Test::Unit::TestCase
fixtures :companies, :accounts, :topics
@@ -17,6 +21,10 @@ class CalculationsTest < Test::Unit::TestCase
assert_in_delta 53.0, value, 0.001
end
+ def test_should_return_nil_as_average
+ assert_nil NumericData.average(:bank_balance)
+ end
+
def test_should_get_maximum_of_field
assert_equal 60, Account.maximum(:credit_limit)
end
@@ -131,6 +139,26 @@ class CalculationsTest < Test::Unit::TestCase
assert_equal 2, c[companies(:rails_core)]
assert_equal 1, c[companies(:first_client)]
end
+
+ uses_mocha 'group_by_non_numeric_foreign_key_association' do
+ def test_should_group_by_association_with_non_numeric_foreign_key
+ ActiveRecord::Base.connection.expects(:select_all).returns([{"count_all" => 1, "firm_id" => "ABC"}])
+
+ firm = mock()
+ firm.expects(:id).returns("ABC")
+ firm.expects(:class).returns(Firm)
+ Company.expects(:find).with(["ABC"]).returns([firm])
+
+ column = mock()
+ column.expects(:name).at_least_once.returns(:firm_id)
+ column.expects(:type_cast).with("ABC").returns("ABC")
+ Account.expects(:columns).at_least_once.returns([column])
+
+ c = Account.count(:all, :group => :firm)
+ assert_equal Firm, c.first.first.class
+ assert_equal 1, c.first.last
+ end
+ end
def test_should_not_modify_options_when_using_includes
options = {:conditions => 'companies.id > 1', :include => :firm}
@@ -199,16 +227,20 @@ class CalculationsTest < Test::Unit::TestCase
assert_raises(ArgumentError) { Company.send(:validate_calculation_options, :sum, :foo => :bar) }
assert_raises(ArgumentError) { Company.send(:validate_calculation_options, :count, :foo => :bar) }
end
-
+
def test_should_count_selected_field_with_include
assert_equal 6, Account.count(:distinct => true, :include => :firm)
assert_equal 4, Account.count(:distinct => true, :include => :firm, :select => :credit_limit)
end
-
- def test_deprecated_count_with_string_parameters
- assert_deprecated('count') { Account.count('credit_limit > 50') }
+
+ def test_count_with_column_parameter
+ assert_equal 5, Account.count(:firm_id)
end
-
+
+ def test_count_with_column_and_options_parameter
+ assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50")
+ end
+
def test_count_with_no_parameters_isnt_deprecated
assert_not_deprecated { Account.count }
end
diff --git a/vendor/rails/activerecord/test/callbacks_test.rb b/vendor/rails/activerecord/test/callbacks_test.rb
index fa2e3da7..6ec27e24 100644
--- a/vendor/rails/activerecord/test/callbacks_test.rb
+++ b/vendor/rails/activerecord/test/callbacks_test.rb
@@ -49,6 +49,16 @@ class CallbackDeveloper < ActiveRecord::Base
end
end
+class ParentDeveloper < ActiveRecord::Base
+ set_table_name 'developers'
+ attr_accessor :after_save_called
+ before_validation {|record| record.after_save_called = true}
+end
+
+class ChildDeveloper < ParentDeveloper
+
+end
+
class RecursiveCallbackDeveloper < ActiveRecord::Base
set_table_name 'developers'
@@ -374,4 +384,17 @@ class CallbacksTest < Test::Unit::TestCase
[ :before_validation, :returning_false ]
], david.history
end
+
+ def test_inheritence_of_callbacks
+ parent = ParentDeveloper.new
+ assert !parent.after_save_called
+ parent.save
+ assert parent.after_save_called
+
+ child = ChildDeveloper.new
+ assert !child.after_save_called
+ child.save
+ assert child.after_save_called
+ end
+
end
diff --git a/vendor/rails/activerecord/test/connection_test_firebird.rb b/vendor/rails/activerecord/test/connection_test_firebird.rb
index 4760a46d..715e6955 100644
--- a/vendor/rails/activerecord/test/connection_test_firebird.rb
+++ b/vendor/rails/activerecord/test/connection_test_firebird.rb
@@ -1,6 +1,6 @@
-require 'abstract_unit'
+require "#{File.dirname(__FILE__)}/abstract_unit"
-class ConnectionTest < Test::Unit::TestCase
+class FirebirdConnectionTest < Test::Unit::TestCase
def test_charset_properly_set
fb_conn = ActiveRecord::Base.connection.instance_variable_get(:@connection)
assert_equal 'UTF8', fb_conn.database.character_set
diff --git a/vendor/rails/activerecord/test/connection_test_mysql.rb b/vendor/rails/activerecord/test/connection_test_mysql.rb
new file mode 100644
index 00000000..e3f589c4
--- /dev/null
+++ b/vendor/rails/activerecord/test/connection_test_mysql.rb
@@ -0,0 +1,30 @@
+require "#{File.dirname(__FILE__)}/abstract_unit"
+
+class MysqlConnectionTest < Test::Unit::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_no_automatic_reconnection_after_timeout
+ assert @connection.active?
+ @connection.update('set @@wait_timeout=1')
+ sleep 2
+ assert !@connection.active?
+ end
+
+ def test_successful_reconnection_after_timeout_with_manual_reconnect
+ assert @connection.active?
+ @connection.update('set @@wait_timeout=1')
+ sleep 2
+ @connection.reconnect!
+ assert @connection.active?
+ end
+
+ def test_successful_reconnection_after_timeout_with_verify
+ assert @connection.active?
+ @connection.update('set @@wait_timeout=1')
+ sleep 2
+ @connection.verify!(0)
+ assert @connection.active?
+ end
+end
diff --git a/vendor/rails/activerecord/test/connections/native_mysql/connection.rb b/vendor/rails/activerecord/test/connections/native_mysql/connection.rb
index 8442dc20..64539248 100644
--- a/vendor/rails/activerecord/test/connections/native_mysql/connection.rb
+++ b/vendor/rails/activerecord/test/connections/native_mysql/connection.rb
@@ -6,6 +6,9 @@ RAILS_DEFAULT_LOGGER = Logger.new('debug.log')
RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
ActiveRecord::Base.logger = RAILS_DEFAULT_LOGGER
+# GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost';
+# GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost';
+
ActiveRecord::Base.configurations = {
'arunit' => {
:adapter => 'mysql',
diff --git a/vendor/rails/activerecord/test/connections/native_sqlite/connection.rb b/vendor/rails/activerecord/test/connections/native_sqlite/connection.rb
index 14ae9990..8ccce494 100644
--- a/vendor/rails/activerecord/test/connections/native_sqlite/connection.rb
+++ b/vendor/rails/activerecord/test/connections/native_sqlite/connection.rb
@@ -10,25 +10,16 @@ BASE_DIR = File.expand_path(File.dirname(__FILE__) + '/../../fixtures')
sqlite_test_db = "#{BASE_DIR}/fixture_database.sqlite"
sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite"
-def make_connection(clazz, db_file, db_definitions_file)
+def make_connection(clazz, db_file)
ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'sqlite', :database => db_file } }
unless File.exist?(db_file)
puts "SQLite database not found at #{db_file}. Rebuilding it."
- sqlite_command = %Q{sqlite #{db_file} "create table a (a integer); drop table a;"}
+ sqlite_command = %Q{sqlite "#{db_file}" "create table a (a integer); drop table a;"}
puts "Executing '#{sqlite_command}'"
raise SqliteError.new("Seems that there is no sqlite executable available") unless system(sqlite_command)
- clazz.establish_connection(clazz.name)
- script = File.read("#{BASE_DIR}/db_definitions/#{db_definitions_file}")
- # SQLite-Ruby has problems with semi-colon separated commands, so split and execute one at a time
- script.split(';').each do
- |command|
- clazz.connection.execute(command) unless command.strip.empty?
- end
- else
- clazz.establish_connection(clazz.name)
end
+ clazz.establish_connection(clazz.name)
end
-make_connection(ActiveRecord::Base, sqlite_test_db, 'sqlite.sql')
-make_connection(Course, sqlite_test_db2, 'sqlite2.sql')
-load(File.join(BASE_DIR, 'db_definitions', 'schema.rb'))
+make_connection(ActiveRecord::Base, sqlite_test_db)
+make_connection(Course, sqlite_test_db2)
diff --git a/vendor/rails/activerecord/test/connections/native_sqlite3/connection.rb b/vendor/rails/activerecord/test/connections/native_sqlite3/connection.rb
index 6dbb2b98..46ef9097 100644
--- a/vendor/rails/activerecord/test/connections/native_sqlite3/connection.rb
+++ b/vendor/rails/activerecord/test/connections/native_sqlite3/connection.rb
@@ -10,25 +10,16 @@ BASE_DIR = File.expand_path(File.dirname(__FILE__) + '/../../fixtures')
sqlite_test_db = "#{BASE_DIR}/fixture_database.sqlite3"
sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite3"
-def make_connection(clazz, db_file, db_definitions_file)
+def make_connection(clazz, db_file)
ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'sqlite3', :database => db_file, :timeout => 5000 } }
unless File.exist?(db_file)
puts "SQLite3 database not found at #{db_file}. Rebuilding it."
- sqlite_command = %Q{sqlite3 #{db_file} "create table a (a integer); drop table a;"}
+ sqlite_command = %Q{sqlite3 "#{db_file}" "create table a (a integer); drop table a;"}
puts "Executing '#{sqlite_command}'"
raise SqliteError.new("Seems that there is no sqlite3 executable available") unless system(sqlite_command)
- clazz.establish_connection(clazz.name)
- script = File.read("#{BASE_DIR}/db_definitions/#{db_definitions_file}")
- # SQLite-Ruby has problems with semi-colon separated commands, so split and execute one at a time
- script.split(';').each do
- |command|
- clazz.connection.execute(command) unless command.strip.empty?
- end
- else
- clazz.establish_connection(clazz.name)
end
+ clazz.establish_connection(clazz.name)
end
-make_connection(ActiveRecord::Base, sqlite_test_db, 'sqlite.sql')
-make_connection(Course, sqlite_test_db2, 'sqlite2.sql')
-load(File.join(BASE_DIR, 'db_definitions', 'schema.rb'))
+make_connection(ActiveRecord::Base, sqlite_test_db)
+make_connection(Course, sqlite_test_db2)
diff --git a/vendor/rails/activerecord/test/connections/native_sqlite3/in_memory_connection.rb b/vendor/rails/activerecord/test/connections/native_sqlite3/in_memory_connection.rb
index 32bf6a27..a33f44e5 100644
--- a/vendor/rails/activerecord/test/connections/native_sqlite3/in_memory_connection.rb
+++ b/vendor/rails/activerecord/test/connections/native_sqlite3/in_memory_connection.rb
@@ -15,4 +15,4 @@ end
make_connection(ActiveRecord::Base, 'sqlite.sql')
make_connection(Course, 'sqlite2.sql')
- load("#{File.dirname(__FILE__)}/../../fixtures/db_definitions/schema.rb"))
+load("#{File.dirname(__FILE__)}/../../fixtures/db_definitions/schema.rb")
diff --git a/vendor/rails/activerecord/test/connections/native_sqlserver/connection.rb b/vendor/rails/activerecord/test/connections/native_sqlserver/connection.rb
deleted file mode 100644
index 626a72b8..00000000
--- a/vendor/rails/activerecord/test/connections/native_sqlserver/connection.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-print "Using native SQLServer\n"
-require_dependency 'fixtures/course'
-require 'logger'
-
-ActiveRecord::Base.logger = Logger.new("debug.log")
-
-ActiveRecord::Base.configurations = {
- 'arunit' => {
- :adapter => 'sqlserver',
- :host => 'localhost',
- :username => 'sa',
- :database => 'activerecord_unittest'
- },
- 'arunit2' => {
- :adapter => 'sqlserver',
- :host => 'localhost',
- :username => 'sa',
- :database => 'activerecord_unittest2'
- }
-}
-
-ActiveRecord::Base.establish_connection 'arunit'
-Course.establish_connection 'arunit2'
diff --git a/vendor/rails/activerecord/test/connections/native_sqlserver_odbc/connection.rb b/vendor/rails/activerecord/test/connections/native_sqlserver_odbc/connection.rb
deleted file mode 100644
index 41fd672e..00000000
--- a/vendor/rails/activerecord/test/connections/native_sqlserver_odbc/connection.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-print "Using native SQLServer via ODBC\n"
-require_dependency 'fixtures/course'
-require 'logger'
-
-ActiveRecord::Base.logger = Logger.new("debug.log")
-
-ActiveRecord::Base.configurations = {
- 'arunit' => {
- :adapter => 'sqlserver',
- :mode => 'ODBC',
- :host => 'localhost',
- :username => 'sa',
- :dsn => 'activerecord_unittest'
- },
- 'arunit2' => {
- :adapter => 'sqlserver',
- :mode => 'ODBC',
- :host => 'localhost',
- :username => 'sa',
- :dsn => 'activerecord_unittest2'
- }
-}
-
-ActiveRecord::Base.establish_connection 'arunit'
-Course.establish_connection 'arunit2'
diff --git a/vendor/rails/activerecord/test/copy_table_sqlite.rb b/vendor/rails/activerecord/test/copy_table_test_sqlite.rb
similarity index 81%
rename from vendor/rails/activerecord/test/copy_table_sqlite.rb
rename to vendor/rails/activerecord/test/copy_table_test_sqlite.rb
index f3f5f1ce..a25780e6 100644
--- a/vendor/rails/activerecord/test/copy_table_sqlite.rb
+++ b/vendor/rails/activerecord/test/copy_table_test_sqlite.rb
@@ -26,8 +26,9 @@ class CopyTableTest < Test::Unit::TestCase
def test_copy_table_renaming_column
test_copy_table('companies', 'companies2',
:rename => {'client_of' => 'fan_of'}) do |from, to, options|
- assert_equal column_values(from, 'client_of').compact.sort,
- column_values(to, 'fan_of').compact.sort
+ expected = column_values(from, 'client_of')
+ assert expected.any?, 'only nils in resultset; real values are needed'
+ assert_equal expected, column_values(to, 'fan_of')
end
end
@@ -41,6 +42,10 @@ class CopyTableTest < Test::Unit::TestCase
end
end
+ def test_copy_table_without_primary_key
+ test_copy_table('developers_projects', 'programmers_projects')
+ end
+
protected
def copy_table(from, to, options = {})
@connection.copy_table(from, to, {:temporary => true}.merge(options))
@@ -51,7 +56,7 @@ protected
end
def column_values(table, column)
- @connection.select_all("SELECT #{column} FROM #{table}").map {|row| row[column]}
+ @connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]}
end
def table_indexes_without_name(table)
diff --git a/vendor/rails/activerecord/test/datatype_test_postgresql.rb b/vendor/rails/activerecord/test/datatype_test_postgresql.rb
index c4c3318d..53961135 100644
--- a/vendor/rails/activerecord/test/datatype_test_postgresql.rb
+++ b/vendor/rails/activerecord/test/datatype_test_postgresql.rb
@@ -1,52 +1,203 @@
require 'abstract_unit'
-class PostgresqlDatatype < ActiveRecord::Base
+class PostgresqlArray < ActiveRecord::Base
end
-class PGDataTypeTest < Test::Unit::TestCase
- self.use_transactional_fixtures = false
+class PostgresqlMoney < ActiveRecord::Base
+end
- TABLE_NAME = 'postgresql_datatypes'
- COLUMNS = [
- 'id SERIAL PRIMARY KEY',
- 'commission_by_quarter INTEGER[]',
- 'nicknames TEXT[]'
- ]
+class PostgresqlNumber < ActiveRecord::Base
+end
+
+class PostgresqlTime < ActiveRecord::Base
+end
+
+class PostgresqlNetworkAddress < ActiveRecord::Base
+end
+
+class PostgresqlBitString < ActiveRecord::Base
+end
+
+class PostgresqlOid < ActiveRecord::Base
+end
+
+class PostgresqlDataTypeTest < Test::Unit::TestCase
+ self.use_transactional_fixtures = false
def setup
@connection = ActiveRecord::Base.connection
- @connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
- @connection.execute "INSERT INTO #{TABLE_NAME} (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )"
- @first = PostgresqlDatatype.find( 1 )
- end
- def teardown
- @connection.execute "DROP TABLE #{TABLE_NAME}"
+ @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )")
+ @first_array = PostgresqlArray.find(1)
+
+ @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('$567.89')")
+ @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-$567.89')")
+ @first_money = PostgresqlMoney.find(1)
+ @second_money = PostgresqlMoney.find(2)
+
+ @connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)")
+ @first_number = PostgresqlNumber.find(1)
+
+ @connection.execute("INSERT INTO postgresql_times (time_interval) VALUES ('1 year 2 days ago')")
+ @first_time = PostgresqlTime.find(1)
+
+ @connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')")
+ @first_network_address = PostgresqlNetworkAddress.find(1)
+
+ @connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')")
+ @first_bit_string = PostgresqlBitString.find(1)
+
+ @connection.execute("INSERT INTO postgresql_oids (obj_id) VALUES (1234)")
+ @first_oid = PostgresqlOid.find(1)
end
def test_data_type_of_array_types
- assert_equal :string, @first.column_for_attribute("commission_by_quarter").type
- assert_equal :string, @first.column_for_attribute("nicknames").type
+ assert_equal :string, @first_array.column_for_attribute(:commission_by_quarter).type
+ assert_equal :string, @first_array.column_for_attribute(:nicknames).type
+ end
+
+ def test_data_type_of_money_types
+ assert_equal :decimal, @first_money.column_for_attribute(:wealth).type
+ end
+
+ def test_data_type_of_number_types
+ assert_equal :float, @first_number.column_for_attribute(:single).type
+ assert_equal :float, @first_number.column_for_attribute(:double).type
+ end
+
+ def test_data_type_of_time_types
+ assert_equal :string, @first_time.column_for_attribute(:time_interval).type
+ end
+
+ def test_data_type_of_network_address_types
+ assert_equal :string, @first_network_address.column_for_attribute(:cidr_address).type
+ assert_equal :string, @first_network_address.column_for_attribute(:inet_address).type
+ assert_equal :string, @first_network_address.column_for_attribute(:mac_address).type
+ end
+
+ def test_data_type_of_bit_string_types
+ assert_equal :string, @first_bit_string.column_for_attribute(:bit_string).type
+ assert_equal :string, @first_bit_string.column_for_attribute(:bit_string_varying).type
+ end
+
+ def test_data_type_of_oid_types
+ assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type
end
def test_array_values
- assert_equal '{35000,21000,18000,17000}', @first.commission_by_quarter
- assert_equal '{foo,bar,baz}', @first.nicknames
+ assert_equal '{35000,21000,18000,17000}', @first_array.commission_by_quarter
+ assert_equal '{foo,bar,baz}', @first_array.nicknames
+ end
+
+ def test_money_values
+ assert_equal 567.89, @first_money.wealth
+ assert_equal -567.89, @second_money.wealth
+ end
+
+ def test_number_values
+ assert_equal 123.456, @first_number.single
+ assert_equal 123456.789, @first_number.double
+ end
+
+ def test_time_values
+ assert_equal '-1 years -2 days', @first_time.time_interval
+ end
+
+ def test_network_address_values
+ assert_equal '192.168.0.0/24', @first_network_address.cidr_address
+ assert_equal '172.16.1.254', @first_network_address.inet_address
+ assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address
+ end
+
+ def test_bit_string_values
+ assert_equal '00010101', @first_bit_string.bit_string
+ assert_equal '00010101', @first_bit_string.bit_string_varying
+ end
+
+ def test_oid_values
+ assert_equal 1234, @first_oid.obj_id
end
def test_update_integer_array
new_value = '{32800,95000,29350,17000}'
- assert @first.commission_by_quarter = new_value
- assert @first.save
- assert @first.reload
- assert_equal @first.commission_by_quarter, new_value
+ assert @first_array.commission_by_quarter = new_value
+ assert @first_array.save
+ assert @first_array.reload
+ assert_equal @first_array.commission_by_quarter, new_value
+ assert @first_array.commission_by_quarter = new_value
+ assert @first_array.save
+ assert @first_array.reload
+ assert_equal @first_array.commission_by_quarter, new_value
end
def test_update_text_array
new_value = '{robby,robert,rob,robbie}'
- assert @first.nicknames = new_value
- assert @first.save
- assert @first.reload
- assert_equal @first.nicknames, new_value
+ assert @first_array.nicknames = new_value
+ assert @first_array.save
+ assert @first_array.reload
+ assert_equal @first_array.nicknames, new_value
+ assert @first_array.nicknames = new_value
+ assert @first_array.save
+ assert @first_array.reload
+ assert_equal @first_array.nicknames, new_value
+ end
+
+ def test_update_money
+ new_value = 123.45
+ assert @first_money.wealth = new_value
+ assert @first_money.save
+ assert @first_money.reload
+ assert_equal @first_money.wealth, new_value
+ end
+
+ def test_update_number
+ new_single = 789.012
+ new_double = 789012.345
+ assert @first_number.single = new_single
+ assert @first_number.double = new_double
+ assert @first_number.save
+ assert @first_number.reload
+ assert_equal @first_number.single, new_single
+ assert_equal @first_number.double, new_double
+ end
+
+ def test_update_time
+ assert @first_time.time_interval = '2 years 3 minutes'
+ assert @first_time.save
+ assert @first_time.reload
+ assert_equal @first_time.time_interval, '2 years 00:03:00'
+ end
+
+ def test_update_network_address
+ new_cidr_address = '10.1.2.3/32'
+ new_inet_address = '10.0.0.0/8'
+ new_mac_address = 'bc:de:f0:12:34:56'
+ assert @first_network_address.cidr_address = new_cidr_address
+ assert @first_network_address.inet_address = new_inet_address
+ assert @first_network_address.mac_address = new_mac_address
+ assert @first_network_address.save
+ assert @first_network_address.reload
+ assert_equal @first_network_address.cidr_address, new_cidr_address
+ assert_equal @first_network_address.inet_address, new_inet_address
+ assert_equal @first_network_address.mac_address, new_mac_address
+ end
+
+ def test_update_bit_string
+ new_bit_string = '11111111'
+ new_bit_string_varying = 'FF'
+ assert @first_bit_string.bit_string = new_bit_string
+ assert @first_bit_string.bit_string_varying = new_bit_string_varying
+ assert @first_bit_string.save
+ assert @first_bit_string.reload
+ assert_equal @first_bit_string.bit_string, new_bit_string
+ assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying
+ end
+
+ def test_update_oid
+ new_value = 567890
+ assert @first_oid.obj_id = new_value
+ assert @first_oid.save
+ assert @first_oid.reload
+ assert_equal @first_oid.obj_id, new_value
end
end
diff --git a/vendor/rails/activerecord/test/empty_date_time_test.rb b/vendor/rails/activerecord/test/date_time_test.rb
similarity index 53%
rename from vendor/rails/activerecord/test/empty_date_time_test.rb
rename to vendor/rails/activerecord/test/date_time_test.rb
index 0b50b094..3bd44fd4 100644
--- a/vendor/rails/activerecord/test/empty_date_time_test.rb
+++ b/vendor/rails/activerecord/test/date_time_test.rb
@@ -2,7 +2,19 @@ require 'abstract_unit'
require 'fixtures/topic'
require 'fixtures/task'
-class EmptyDateTimeTest < Test::Unit::TestCase
+class DateTimeTest < Test::Unit::TestCase
+ def test_saves_both_date_and_time
+ time_values = [1807, 2, 10, 15, 30, 45]
+ now = DateTime.civil(*time_values)
+
+ task = Task.new
+ task.starting = now
+ task.save!
+
+ # check against Time.local_time, since some platforms will return a Time instead of a DateTime
+ assert_equal Time.local_time(*time_values), Task.find(task.id).starting
+ end
+
def test_assign_empty_date_time
task = Task.new
task.starting = ''
diff --git a/vendor/rails/activerecord/test/defaults_test.rb b/vendor/rails/activerecord/test/defaults_test.rb
index 39dcc82b..9701d1a8 100644
--- a/vendor/rails/activerecord/test/defaults_test.rb
+++ b/vendor/rails/activerecord/test/defaults_test.rb
@@ -31,7 +31,8 @@ class DefaultTest < Test::Unit::TestCase
assert_equal 0, klass.columns_hash['zero'].default
assert !klass.columns_hash['zero'].null
- assert_equal nil, klass.columns_hash['omit'].default
+ # 0 in MySQL 4, nil in 5.
+ assert [0, nil].include?(klass.columns_hash['omit'].default)
assert !klass.columns_hash['omit'].null
assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
@@ -57,4 +58,10 @@ class DefaultTest < Test::Unit::TestCase
assert_equal BigDecimal.new("2.78"), default.decimal_number
end
end
+
+ if current_adapter?(:PostgreSQLAdapter)
+ def test_multiline_default_text
+ assert_equal "--- []\n\n", Default.columns_hash['multiline_default'].default
+ end
+ end
end
diff --git a/vendor/rails/activerecord/test/deprecated_associations_test.rb b/vendor/rails/activerecord/test/deprecated_associations_test.rb
deleted file mode 100755
index d9b1cc94..00000000
--- a/vendor/rails/activerecord/test/deprecated_associations_test.rb
+++ /dev/null
@@ -1,396 +0,0 @@
-require 'abstract_unit'
-require 'fixtures/developer'
-require 'fixtures/project'
-require 'fixtures/company'
-require 'fixtures/topic'
-require 'fixtures/reply'
-
-# Can't declare new classes in test case methods, so tests before that
-bad_collection_keys = false
-begin
- class Car < ActiveRecord::Base; has_many :wheels, :name => "wheels"; end
-rescue ArgumentError
- bad_collection_keys = true
-end
-raise "ActiveRecord should have barked on bad collection keys" unless bad_collection_keys
-
-
-class DeprecatedAssociationWarningsTest < Test::Unit::TestCase
- def test_deprecation_warnings
- assert_deprecated('find_first') { Firm.find_first }
- assert_deprecated('find_all') { Firm.find_all }
- assert_deprecated('has_account?') { Firm.find(:first).has_account? }
- assert_deprecated('has_clients?') { Firm.find(:first).has_clients? }
- end
-end
-
-class DeprecatedAssociationsTest < Test::Unit::TestCase
- fixtures :accounts, :companies, :developers, :projects, :topics,
- :developers_projects
-
- def setup
- @firm = companies(:first_firm)
- end
-
- def test_has_many_find
- assert_equal 2, @firm.clients.length
- end
-
- def test_has_many_orders
- assert_equal "Summit", @firm.clients.first.name
- end
-
- def test_has_many_class_name
- assert_equal "Microsoft", @firm.clients_sorted_desc.first.name
- end
-
- def test_has_many_foreign_key
- assert_equal "Microsoft", @firm.clients_of_firm.first.name
- end
-
- def test_has_many_conditions
- assert_equal "Microsoft", @firm.clients_like_ms.first.name
- end
-
- def test_has_many_sql
- assert_equal "Microsoft", @firm.clients_using_sql.first.name
- assert_equal 1, @firm.clients_using_sql.count
- assert_equal 1, @firm.clients_using_sql.count
- end
-
- def test_has_many_counter_sql
- assert_equal 1, @firm.clients_using_counter_sql.count
- end
-
- def test_has_many_queries
- assert !@firm.clients.loaded?
- assert_deprecated 'has_clients?' do
- assert_queries(1) { assert @firm.has_clients? }
- end
- assert !@firm.clients.loaded?
- assert_deprecated 'clients_count' do
- assert_queries(1) { assert_equal 2, @firm.clients_count }
- end
- assert !@firm.clients.loaded?
- assert_queries(1) { @firm.clients.size }
- assert !@firm.clients.loaded?
- assert_queries(0) { @firm.clients }
- assert !@firm.clients.loaded?
- assert_queries(1) { @firm.clients.reload }
- assert @firm.clients.loaded?
- assert_queries(0) { @firm.clients.size }
- assert_queries(1) { @firm.clients.count }
- end
-
- def test_has_many_dependence
- count = Client.count
- Firm.find(:first).destroy
- assert_equal count - 2, Client.count
- end
-
- uses_transaction :test_has_many_dependence_with_transaction_support_on_failure
- def test_has_many_dependence_with_transaction_support_on_failure
- count = Client.count
-
- clients = @firm.clients
- clients.last.instance_eval { def before_destroy() raise "Trigger rollback" end }
-
- @firm.destroy rescue "do nothing"
-
- assert_equal count, Client.count
- end
-
- def test_has_one_dependence
- num_accounts = Account.count
- assert_not_nil @firm.account
- @firm.destroy
- assert_equal num_accounts - 1, Account.count
- end
-
- def test_has_one_dependence_with_missing_association
- Account.destroy_all
- assert_nil @firm.account
- @firm.destroy
- end
-
- def test_belongs_to
- client = companies(:second_client)
- assert_deprecated('has_firm?') do
- assert companies(:second_client).has_firm?, "Microsoft should have a firm"
- end
- assert_equal companies(:first_firm), client.firm, "Microsoft should have a firm"
- end
-
- def test_belongs_to_with_different_class_name
- assert_equal @firm, companies(:second_client).firm_with_other_name
- end
-
- def test_belongs_to_with_condition
- assert_equal @firm, companies(:second_client).firm_with_condition
- end
-
- def test_belongs_to_equality
- assert_equal @firm, companies(:second_client).firm, 'Microsoft should have 37signals as firm'
- end
-
- def test_has_one
- assert_equal accounts(:signals37), @firm.account
- assert_deprecated 'has_account?' do
- assert @firm.has_account?, "37signals should have an account"
- end
- assert_deprecated 'firm?' do
- assert accounts(:signals37).firm?(@firm), "37signals account should be able to backtrack"
- end
- assert_deprecated 'has_firm?' do
- assert accounts(:signals37).has_firm?, "37signals account should be able to backtrack"
- end
-
- assert_nil accounts(:unknown).firm, "Unknown isn't linked"
- end
-
- def test_has_many_dependence_on_account
- num_accounts = Account.count
- @firm.destroy
- assert_equal num_accounts - 1, Account.count
- end
-
- def test_find_in
- assert_deprecated 'find_in_clients' do
- assert_equal companies(:first_client), @firm.find_in_clients(2)
- assert_raises(ActiveRecord::RecordNotFound) { @firm.find_in_clients(6) }
- end
- end
-
- def test_force_reload
- ActiveSupport::Deprecation.silence do
- firm = Firm.new("name" => "A New Firm, Inc")
- firm.save
- firm.clients.each {|c|} # forcing to load all clients
- assert firm.clients.empty?, "New firm shouldn't have client objects"
- assert !firm.has_clients?, "New firm shouldn't have clients"
- assert_equal 0, firm.clients_count, "New firm should have 0 clients"
-
- client = Client.new("name" => "TheClient.com", "firm_id" => firm.id)
- client.save
-
- assert firm.clients.empty?, "New firm should have cached no client objects"
- assert !firm.has_clients?, "New firm should have cached a no-clients response"
- assert_equal 0, firm.clients_count, "New firm should have cached 0 clients count"
-
- assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
- assert firm.has_clients?(true), "New firm should have reloaded with a have-clients response"
- assert_equal 1, firm.clients_count(true), "New firm should have reloaded clients count"
- end
- end
-
- def test_included_in_collection
- assert @firm.clients.include?(Client.find(2))
- end
-
- def test_build_to_collection
- count = @firm.clients_of_firm.count
- new_client = nil
- assert_deprecated 'build_to_clients_of_firm' do
- new_client = @firm.build_to_clients_of_firm("name" => "Another Client")
- end
- assert_equal "Another Client", new_client.name
- assert new_client.save
-
- assert_equal @firm, new_client.firm
- assert_equal count + 1, @firm.clients_of_firm.count
- end
-
- def test_create_in_collection
- assert_deprecated 'create_in_clients_of_firm' do
- assert_equal @firm.create_in_clients_of_firm("name" => "Another Client"), @firm.clients_of_firm(true).last
- end
- end
-
- def test_has_and_belongs_to_many
- david = Developer.find(1)
- assert_deprecated 'has_projects?' do
- assert david.has_projects?
- end
- assert_deprecated 'projects_count' do
- assert_equal 2, david.projects_count
- end
-
- active_record = Project.find(1)
- assert_deprecated 'has_developers?' do
- assert active_record.has_developers?
- end
- assert_deprecated 'developers_count' do
- assert_equal 3, active_record.developers_count
- end
- assert active_record.developers.include?(david)
- end
-
- def test_has_and_belongs_to_many_removing
- david = Developer.find(1)
- active_record = Project.find(1)
-
- assert_deprecated do
- david.remove_projects(active_record)
- assert_equal 1, david.projects_count
- assert_equal 2, active_record.developers_count
- end
- end
-
- def test_has_and_belongs_to_many_zero
- david = Developer.find(1)
- assert_deprecated do
- david.remove_projects Project.find_all
- assert_equal 0, david.projects_count
- assert !david.has_projects?
- end
- end
-
- def test_has_and_belongs_to_many_adding
- jamis = Developer.find(2)
- action_controller = Project.find(2)
-
- assert_deprecated do
- jamis.add_projects(action_controller)
- assert_equal 2, jamis.projects_count
- assert_equal 2, action_controller.developers_count
- end
- end
-
- def test_has_and_belongs_to_many_adding_from_the_project
- jamis = Developer.find(2)
- action_controller = Project.find(2)
-
- assert_deprecated do
- action_controller.add_developers(jamis)
- assert_equal 2, jamis.projects_count
- assert_equal 2, action_controller.developers_count
- end
- end
-
- def test_has_and_belongs_to_many_adding_a_collection
- aredridel = Developer.new("name" => "Aredridel")
- aredridel.save
-
- assert_deprecated do
- aredridel.add_projects([ Project.find(1), Project.find(2) ])
- assert_equal 2, aredridel.projects_count
- end
- end
-
- def test_belongs_to_counter
- topic = Topic.create("title" => "Apple", "content" => "hello world")
- assert_equal 0, topic.send(:read_attribute, "replies_count"), "No replies yet"
-
- reply = assert_deprecated { topic.create_in_replies("title" => "I'm saying no!", "content" => "over here") }
- assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count"), "First reply created"
-
- reply.destroy
- assert_equal 0, Topic.find(topic.id).send(:read_attribute, "replies_count"), "First reply deleted"
- end
-
- def test_natural_assignment_of_has_one
- apple = Firm.create("name" => "Apple")
- citibank = Account.create("credit_limit" => 10)
- apple.account = citibank
- assert_equal apple.id, citibank.firm_id
- end
-
- def test_natural_assignment_of_belongs_to
- apple = Firm.create("name" => "Apple")
- citibank = Account.create("credit_limit" => 10)
- citibank.firm = apple
- assert_equal apple.id, citibank.firm_id
- end
-
- def test_natural_assignment_of_has_many
- apple = Firm.create("name" => "Apple")
- natural = Client.create("name" => "Natural Company")
- apple.clients << natural
- assert_equal apple.id, natural.firm_id
- assert_equal Client.find(natural.id), Firm.find(apple.id).clients.find(natural.id)
- apple.clients.delete natural
- assert_raises(ActiveRecord::RecordNotFound) {
- Firm.find(apple.id).clients.find(natural.id)
- }
- end
-
- def test_natural_adding_of_has_and_belongs_to_many
- rails = Project.create("name" => "Rails")
- ap = Project.create("name" => "Action Pack")
- john = Developer.create("name" => "John")
- mike = Developer.create("name" => "Mike")
- rails.developers << john
- rails.developers << mike
-
- assert_equal Developer.find(john.id), Project.find(rails.id).developers.find(john.id)
- assert_equal Developer.find(mike.id), Project.find(rails.id).developers.find(mike.id)
- assert_equal Project.find(rails.id), Developer.find(mike.id).projects.find(rails.id)
- assert_equal Project.find(rails.id), Developer.find(john.id).projects.find(rails.id)
- ap.developers << john
- assert_equal Developer.find(john.id), Project.find(ap.id).developers.find(john.id)
- assert_equal Project.find(ap.id), Developer.find(john.id).projects.find(ap.id)
-
- ap.developers.delete john
- assert_raises(ActiveRecord::RecordNotFound) {
- Project.find(ap.id).developers.find(john.id)
- }
- assert_raises(ActiveRecord::RecordNotFound) {
- Developer.find(john.id).projects.find(ap.id)
- }
- end
-
- def test_storing_in_pstore
- require "pstore"
- require "tmpdir"
- apple = Firm.create("name" => "Apple")
- natural = Client.new("name" => "Natural Company")
- apple.clients << natural
-
- db = PStore.new(File.join(Dir.tmpdir, "ar-pstore-association-test"))
- db.transaction do
- db["apple"] = apple
- end
-
- db = PStore.new(File.join(Dir.tmpdir, "ar-pstore-association-test"))
- db.transaction do
- assert_equal "Natural Company", db["apple"].clients.first.name
- end
- end
-
- def test_has_many_find_all
- assert_deprecated 'find_all_in_clients' do
- assert_equal 2, @firm.find_all_in_clients("#{QUOTED_TYPE} = 'Client'").length
- assert_equal 1, @firm.find_all_in_clients("name = 'Summit'").length
- end
- end
-
- def test_has_one
- assert_equal Account.find(1), @firm.account, "37signals should have an account"
- assert_equal @firm, Account.find(1).firm, "37signals account should be able to backtrack"
- assert_nil Account.find(2).firm, "Unknown isn't linked"
- end
-
- def test_has_one_build
- firm = Firm.new("name" => "GlobalMegaCorp")
- assert firm.save
-
- account = firm.build_account(:credit_limit => 1000)
- assert account.save
- assert_equal account, firm.account
- end
-
- def test_has_one_failing_build_association
- firm = Firm.new("name" => "GlobalMegaCorp")
- firm.save
-
- account = firm.build_account
- assert !account.save
- assert_equal "can't be empty", account.errors.on("credit_limit")
- end
-
- def test_has_one_create
- firm = Firm.new("name" => "GlobalMegaCorp")
- firm.save
- assert_equal firm.create_account("credit_limit" => 1000), firm.account
- end
-end
diff --git a/vendor/rails/activerecord/test/deprecated_finder_test.rb b/vendor/rails/activerecord/test/deprecated_finder_test.rb
index 796e46f8..097c9131 100755
--- a/vendor/rails/activerecord/test/deprecated_finder_test.rb
+++ b/vendor/rails/activerecord/test/deprecated_finder_test.rb
@@ -1,90 +1,19 @@
require 'abstract_unit'
-require 'fixtures/company'
-require 'fixtures/topic'
-require 'fixtures/reply'
require 'fixtures/entrant'
-require 'fixtures/developer'
class DeprecatedFinderTest < Test::Unit::TestCase
- fixtures :companies, :topics, :entrants, :developers
+ fixtures :entrants
- def test_find_all_with_limit
- entrants = assert_deprecated { Entrant.find_all nil, "id ASC", 2 }
- assert_equal 2, entrants.size
- assert_equal entrants(:first), entrants.first
+ def test_deprecated_find_all_was_removed
+ assert_raise(NoMethodError) { Entrant.find_all }
end
- def test_find_all_with_prepared_limit_and_offset
- entrants = assert_deprecated { Entrant.find_all nil, "id ASC", [2, 1] }
- assert_equal 2, entrants.size
- assert_equal entrants(:second), entrants.first
+ def test_deprecated_find_first_was_removed
+ assert_raise(NoMethodError) { Entrant.find_first }
end
- def test_find_first
- first = assert_deprecated { Topic.find_first "title = 'The First Topic'" }
- assert_equal topics(:first), first
- end
-
- def test_find_first_failing
- first = assert_deprecated { Topic.find_first "title = 'The First Topic!'" }
- assert_nil first
- end
-
- def test_deprecated_find_on_conditions
- assert_deprecated 'find_on_conditions' do
- assert Topic.find_on_conditions(1, ["approved = ?", false])
- assert_raises(ActiveRecord::RecordNotFound) { Topic.find_on_conditions(1, ["approved = ?", true]) }
- end
- end
-
- def test_condition_interpolation
- assert_deprecated do
- assert_kind_of Firm, Company.find_first(["name = '%s'", "37signals"])
- assert_nil Company.find_first(["name = '%s'", "37signals!"])
- assert_nil Company.find_first(["name = '%s'", "37signals!' OR 1=1"])
- assert_kind_of Time, Topic.find_first(["id = %d", 1]).written_on
- end
- end
-
- def test_bind_variables
- assert_deprecated do
- assert_kind_of Firm, Company.find_first(["name = ?", "37signals"])
- assert_nil Company.find_first(["name = ?", "37signals!"])
- assert_nil Company.find_first(["name = ?", "37signals!' OR 1=1"])
- assert_kind_of Time, Topic.find_first(["id = ?", 1]).written_on
- assert_raises(ActiveRecord::PreparedStatementInvalid) {
- Company.find_first(["id=? AND name = ?", 2])
- }
- assert_raises(ActiveRecord::PreparedStatementInvalid) {
- Company.find_first(["id=?", 2, 3, 4])
- }
- end
- end
-
- def test_bind_variables_with_quotes
- Company.create("name" => "37signals' go'es agains")
- assert_deprecated do
- assert_not_nil Company.find_first(["name = ?", "37signals' go'es agains"])
- end
- end
-
- def test_named_bind_variables_with_quotes
- Company.create("name" => "37signals' go'es agains")
- assert_deprecated do
- assert_not_nil Company.find_first(["name = :name", {:name => "37signals' go'es agains"}])
- end
- end
-
- def test_named_bind_variables
- assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
- assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
-
- assert_deprecated do
- assert_kind_of Firm, Company.find_first(["name = :name", { :name => "37signals" }])
- assert_nil Company.find_first(["name = :name", { :name => "37signals!" }])
- assert_nil Company.find_first(["name = :name", { :name => "37signals!' OR 1=1" }])
- assert_kind_of Time, Topic.find_first(["id = :id", { :id => 1 }]).written_on
- end
+ def test_deprecated_find_on_conditions_was_removed
+ assert_raise(NoMethodError) { Entrant.find_on_conditions }
end
def test_count
@@ -98,54 +27,4 @@ class DeprecatedFinderTest < Test::Unit::TestCase
assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
end
-
- def test_find_all_with_limit
- assert_deprecated do
- first_five_developers = Developer.find_all nil, 'id ASC', 5
- assert_equal 5, first_five_developers.length
- assert_equal 'David', first_five_developers.first.name
- assert_equal 'fixture_5', first_five_developers.last.name
-
- no_developers = Developer.find_all nil, 'id ASC', 0
- assert_equal 0, no_developers.length
-
- assert_equal first_five_developers, Developer.find_all(nil, 'id ASC', [5])
- assert_equal no_developers, Developer.find_all(nil, 'id ASC', [0])
- end
- end
-
- def test_find_all_with_limit_and_offset
- assert_deprecated do
- first_three_developers = Developer.find_all nil, 'id ASC', [3, 0]
- second_three_developers = Developer.find_all nil, 'id ASC', [3, 3]
- last_two_developers = Developer.find_all nil, 'id ASC', [2, 8]
-
- assert_equal 3, first_three_developers.length
- assert_equal 3, second_three_developers.length
- assert_equal 2, last_two_developers.length
-
- assert_equal 'David', first_three_developers.first.name
- assert_equal 'fixture_4', second_three_developers.first.name
- assert_equal 'fixture_9', last_two_developers.first.name
- end
- end
-
- def test_find_all_by_one_attribute_with_options
- assert_not_deprecated do
- topics = Topic.find_all_by_content("Have a nice day", "id DESC")
- assert topics(:first), topics.last
-
- topics = Topic.find_all_by_content("Have a nice day", "id DESC")
- assert topics(:first), topics.first
- end
- end
-
- protected
- def bind(statement, *vars)
- if vars.first.is_a?(Hash)
- ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
- else
- ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
- end
- end
end
diff --git a/vendor/rails/activerecord/test/finder_test.rb b/vendor/rails/activerecord/test/finder_test.rb
index abf7f657..13cac2ac 100644
--- a/vendor/rails/activerecord/test/finder_test.rb
+++ b/vendor/rails/activerecord/test/finder_test.rb
@@ -1,4 +1,6 @@
require 'abstract_unit'
+require 'fixtures/author'
+require 'fixtures/comment'
require 'fixtures/company'
require 'fixtures/topic'
require 'fixtures/reply'
@@ -7,18 +9,18 @@ require 'fixtures/developer'
require 'fixtures/post'
class FinderTest < Test::Unit::TestCase
- fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :accounts
+ fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors
def test_find
assert_equal(topics(:first).title, Topic.find(1).title)
end
-
+
# find should handle strings that come from URLs
# (example: Category.find(params[:id]))
def test_find_with_string
assert_equal(Topic.find(1).title,Topic.find("1").title)
end
-
+
def test_exists
assert Topic.exists?(1)
assert Topic.exists?("1")
@@ -26,18 +28,38 @@ class FinderTest < Test::Unit::TestCase
assert Topic.exists?(:author_name => "Mary", :approved => true)
assert Topic.exists?(["parent_id = ?", 1])
assert !Topic.exists?(45)
- assert !Topic.exists?("foo")
+
+ begin
+ assert !Topic.exists?("foo")
+ rescue ActiveRecord::StatementInvalid
+ # PostgreSQL complains about string comparison with integer field
+ rescue Exception
+ flunk
+ end
+
assert_raise(NoMethodError) { Topic.exists?([1,2]) }
end
-
+
def test_find_by_array_of_one_id
assert_kind_of(Array, Topic.find([ 1 ]))
assert_equal(1, Topic.find([ 1 ]).length)
end
-
+
def test_find_by_ids
- assert_equal(2, Topic.find(1, 2).length)
- assert_equal(topics(:second).title, Topic.find([ 2 ]).first.title)
+ assert_equal 2, Topic.find(1, 2).size
+ assert_equal topics(:second).title, Topic.find([2]).first.title
+ end
+
+ def test_find_by_ids_with_limit_and_offset
+ assert_equal 2, Entrant.find([1,3,2], :limit => 2).size
+ assert_equal 1, Entrant.find([1,3,2], :limit => 3, :offset => 2).size
+
+ # Also test an edge case: If you have 11 results, and you set a
+ # limit of 3 and offset of 9, then you should find that there
+ # will be only 2 results, regardless of the limit.
+ devs = Developer.find :all
+ last_devs = Developer.find devs.map(&:id), :limit => 3, :offset => 9
+ assert_equal 2, last_devs.size
end
def test_find_an_empty_array
@@ -45,14 +67,12 @@ class FinderTest < Test::Unit::TestCase
end
def test_find_by_ids_missing_one
- assert_raises(ActiveRecord::RecordNotFound) {
- Topic.find(1, 2, 45)
- }
+ assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
end
-
+
def test_find_all_with_limit
entrants = Entrant.find(:all, :order => "id ASC", :limit => 2)
-
+
assert_equal(2, entrants.size)
assert_equal(entrants(:first).name, entrants.first.name)
end
@@ -67,12 +87,12 @@ class FinderTest < Test::Unit::TestCase
assert_equal(1, entrants.size)
assert_equal(entrants(:third).name, entrants.first.name)
end
-
+
def test_find_all_with_limit_and_offset_and_multiple_orderings
developers = Developer.find(:all, :order => "salary ASC, id DESC", :limit => 3, :offset => 1)
assert_equal ["David", "fixture_10", "fixture_9"], developers.collect {|d| d.name}
end
-
+
def test_find_with_limit_and_condition
developers = Developer.find(:all, :order => "id DESC", :conditions => "salary = 100000", :limit => 3, :offset =>7)
assert_equal(1, developers.size)
@@ -81,28 +101,28 @@ class FinderTest < Test::Unit::TestCase
def test_find_with_entire_select_statement
topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
-
+
assert_equal(1, topics.size)
assert_equal(topics(:second).title, topics.first.title)
end
-
+
def test_find_with_prepared_select_statement
topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
-
+
assert_equal(1, topics.size)
assert_equal(topics(:second).title, topics.first.title)
end
-
+
def test_find_by_sql_with_sti_on_joined_table
accounts = Account.find_by_sql("SELECT * FROM accounts INNER JOIN companies ON companies.id = accounts.firm_id")
assert_equal [Account], accounts.collect(&:class).uniq
end
-
+
def test_find_first
first = Topic.find(:first, :conditions => "title = 'The First Topic'")
assert_equal(topics(:first).title, first.title)
end
-
+
def test_find_first_failing
first = Topic.find(:first, :conditions => "title = 'The First Topic!'")
assert_nil(first)
@@ -112,60 +132,89 @@ class FinderTest < Test::Unit::TestCase
assert_raises(ActiveRecord::RecordNotFound) {
Topic.find(1).parent
}
-
+
Topic.find(2).topic
end
-
+
def test_find_only_some_columns
topic = Topic.find(1, :select => "author_name")
- assert_raises(NoMethodError) { topic.title }
+ assert_raises(ActiveRecord::MissingAttributeError) {topic.title}
assert_equal "David", topic.author_name
assert !topic.attribute_present?("title")
- assert !topic.respond_to?("title")
+ #assert !topic.respond_to?("title")
assert topic.attribute_present?("author_name")
assert topic.respond_to?("author_name")
end
+
+ def test_find_on_blank_conditions
+ [nil, " ", [], {}].each do |blank|
+ assert_nothing_raised { Topic.find(:first, :conditions => blank) }
+ end
+ end
+
+ def test_find_on_blank_bind_conditions
+ [ [""], ["",{}] ].each do |blank|
+ assert_nothing_raised { Topic.find(:first, :conditions => blank) }
+ end
+ end
def test_find_on_array_conditions
assert Topic.find(1, :conditions => ["approved = ?", false])
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => ["approved = ?", true]) }
end
-
+
def test_find_on_hash_conditions
assert Topic.find(1, :conditions => { :approved => false })
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :approved => true }) }
end
-
+
+ def test_find_on_hash_conditions_with_explicit_table_name
+ assert Topic.find(1, :conditions => { 'topics.approved' => false })
+ assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) }
+ end
+
+ def test_find_on_association_proxy_conditions
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10], Comment.find_all_by_post_id(authors(:david).posts).map(&:id).sort
+ end
+
def test_find_on_hash_conditions_with_range
assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1..2 }).map(&:id).sort
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
end
-
+
def test_find_on_hash_conditions_with_multiple_ranges
assert_equal [1,2,3], Comment.find(:all, :conditions => { :id => 1..3, :post_id => 1..2 }).map(&:id).sort
assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort
end
-
+
def test_find_on_multiple_hash_conditions
assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false })
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }) }
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
end
-
+
+
+ def test_condition_interpolation
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
+ end
+
def test_condition_array_interpolation
assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
end
-
+
def test_condition_hash_interpolation
assert_kind_of Firm, Company.find(:first, :conditions => { :name => "37signals"})
assert_nil Company.find(:first, :conditions => { :name => "37signals!"})
assert_kind_of Time, Topic.find(:first, :conditions => {:id => 1}).written_on
end
-
+
def test_hash_condition_find_malformed
assert_raises(ActiveRecord::StatementInvalid) {
Company.find(:first, :conditions => { :id => 2, :dhh => true })
@@ -201,7 +250,7 @@ class FinderTest < Test::Unit::TestCase
Company.find(:first, :conditions => ["id=?", 2, 3, 4])
}
end
-
+
def test_bind_variables_with_quotes
Company.create("name" => "37signals' go'es agains")
assert Company.find(:first, :conditions => ["name = ?", "37signals' go'es agains"])
@@ -215,16 +264,16 @@ class FinderTest < Test::Unit::TestCase
def test_bind_arity
assert_nothing_raised { bind '' }
assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
-
+
assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '?' }
assert_nothing_raised { bind '?', 1 }
assert_raises(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
end
-
+
def test_named_bind_variables
assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
-
+
assert_kind_of Firm, Company.find(:first, :conditions => ["name = :name", { :name => "37signals" }])
assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!" }])
assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!' OR 1=1" }])
@@ -232,18 +281,20 @@ class FinderTest < Test::Unit::TestCase
end
def test_bind_enumerable
+ quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
+
assert_equal '1,2,3', bind('?', [1, 2, 3])
- assert_equal %('a','b','c'), bind('?', %w(a b c))
+ assert_equal quoted_abc, bind('?', %w(a b c))
assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
- assert_equal %('a','b','c'), bind(':a', :a => %w(a b c)) # '
+ assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
require 'set'
assert_equal '1,2,3', bind('?', Set.new([1, 2, 3]))
- assert_equal %('a','b','c'), bind('?', Set.new(%w(a b c)))
+ assert_equal quoted_abc, bind('?', Set.new(%w(a b c)))
assert_equal '1,2,3', bind(':a', :a => Set.new([1, 2, 3]))
- assert_equal %('a','b','c'), bind(':a', :a => Set.new(%w(a b c))) # '
+ assert_equal quoted_abc, bind(':a', :a => Set.new(%w(a b c))) # '
end
def test_bind_empty_enumerable
@@ -254,7 +305,7 @@ class FinderTest < Test::Unit::TestCase
end
def test_bind_string
- assert_equal "''", bind('?', '')
+ assert_equal ActiveRecord::Base.connection.quote(''), bind('?', '')
end
def test_bind_record
@@ -266,8 +317,8 @@ class FinderTest < Test::Unit::TestCase
end
def test_string_sanitation
- assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
- assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
+ assert_not_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
+ assert_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something; select table'", ActiveRecord::Base.sanitize("something; select table")
end
def test_count
@@ -286,6 +337,21 @@ class FinderTest < Test::Unit::TestCase
assert_equal topics(:first), Topic.find_by_title("The First Topic")
assert_nil Topic.find_by_title("The First Topic!")
end
+
+ def test_find_by_one_attribute_caches_dynamic_finder
+ # ensure this test can run independently of order
+ class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.respond_to?(:find_by_title)
+ assert !Topic.respond_to?(:find_by_title)
+ t = Topic.find_by_title("The First Topic")
+ assert Topic.respond_to?(:find_by_title)
+ end
+
+ def test_dynamic_finder_returns_same_results_after_caching
+ # ensure this test can run independently of order
+ class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.respond_to?(:find_by_title)
+ t = Topic.find_by_title("The First Topic")
+ assert_equal t, Topic.find_by_title("The First Topic") # find_by_title has been cached
+ end
def test_find_by_one_attribute_with_order_option
assert_equal accounts(:signals37), Account.find_by_credit_limit(50, :order => 'id')
@@ -296,14 +362,29 @@ class FinderTest < Test::Unit::TestCase
assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
end
+ def test_dynamic_finder_on_one_attribute_with_conditions_caches_method
+ # ensure this test can run independently of order
+ class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.respond_to?(:find_by_credit_limit)
+ assert !Account.respond_to?(:find_by_credit_limit)
+ a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
+ assert Account.respond_to?(:find_by_credit_limit)
+ end
+
+ def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
+ # ensure this test can run independently of order
+ class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.respond_to?(:find_by_credit_limit)
+ a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
+ assert_equal a, Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6]) # find_by_credit_limit has been cached
+ end
+
def test_find_by_one_attribute_with_several_options
assert_equal accounts(:unknown), Account.find_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
end
-
+
def test_find_by_one_missing_attribute
assert_raises(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
end
-
+
def test_find_by_invalid_method_syntax
assert_raises(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
assert_raises(NoMethodError) { Topic.find_by_title?("The First Topic") }
@@ -323,7 +404,7 @@ class FinderTest < Test::Unit::TestCase
assert_equal [], Topic.find_all_by_title("The First Topic!!")
end
-
+
def test_find_all_by_one_attribute_with_options
topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC")
assert topics(:first), topics.last
@@ -345,19 +426,19 @@ class FinderTest < Test::Unit::TestCase
assert_equal 1, topics.size
assert topics.include?(topics(:second))
end
-
+
def test_find_by_nil_attribute
topic = Topic.find_by_last_read nil
assert_not_nil topic
assert_nil topic.last_read
end
-
+
def test_find_all_by_nil_attribute
topics = Topic.find_all_by_last_read nil
assert_equal 1, topics.size
assert_nil topics[0].last_read
end
-
+
def test_find_by_nil_and_not_nil_attributes
topic = Topic.find_by_last_read_and_author_name nil, "Mary"
assert_equal "Mary", topic.author_name
@@ -384,13 +465,47 @@ class FinderTest < Test::Unit::TestCase
assert_equal another, Topic.find_or_create_by_title_and_author_name("Another topic", "John")
assert !another.new_record?
end
-
+
+ def test_find_or_create_from_one_attribute_and_hash
+ number_of_companies = Company.count
+ sig38 = Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
+ assert_equal number_of_companies + 1, Company.count
+ assert_equal sig38, Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
+ assert !sig38.new_record?
+ assert_equal "38signals", sig38.name
+ assert_equal 17, sig38.firm_id
+ assert_equal 23, sig38.client_of
+ end
+
def test_find_or_initialize_from_one_attribute
sig38 = Company.find_or_initialize_by_name("38signals")
assert_equal "38signals", sig38.name
assert sig38.new_record?
end
+ def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected
+ c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000)
+ assert_equal "Fortune 1000", c.name
+ assert_equal 1000, c.rating
+ assert c.valid?
+ assert c.new_record?
+ end
+
+ def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected
+ c = Company.find_or_create_by_name_and_rating("Fortune 1000", 1000)
+ assert_equal "Fortune 1000", c.name
+ assert_equal 1000, c.rating
+ assert c.valid?
+ assert !c.new_record?
+ end
+
+ def test_dynamic_find_or_initialize_from_one_attribute_caches_method
+ class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.respond_to?(:find_or_initialize_by_name)
+ assert !Company.respond_to?(:find_or_initialize_by_name)
+ sig38 = Company.find_or_initialize_by_name("38signals")
+ assert Company.respond_to?(:find_or_initialize_by_name)
+ end
+
def test_find_or_initialize_from_two_attributes
another = Topic.find_or_initialize_by_title_and_author_name("Another topic","John")
assert_equal "Another topic", another.title
@@ -398,6 +513,14 @@ class FinderTest < Test::Unit::TestCase
assert another.new_record?
end
+ def test_find_or_initialize_from_one_attribute_and_hash
+ sig38 = Company.find_or_initialize_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
+ assert_equal "38signals", sig38.name
+ assert_equal 17, sig38.firm_id
+ assert_equal 23, sig38.client_of
+ assert sig38.new_record?
+ end
+
def test_find_with_bad_sql
assert_raises(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
end
@@ -407,12 +530,16 @@ class FinderTest < Test::Unit::TestCase
assert_raises(ArgumentError) { Topic.find :first, :conditions => '1 = 1', :join => "It should be `joins'" }
end
+ def test_dynamic_finder_with_invalid_params
+ assert_raises(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" }
+ end
+
def test_find_all_with_limit
first_five_developers = Developer.find :all, :order => 'id ASC', :limit => 5
assert_equal 5, first_five_developers.length
assert_equal 'David', first_five_developers.first.name
assert_equal 'fixture_5', first_five_developers.last.name
-
+
no_developers = Developer.find :all, :order => 'id ASC', :limit => 0
assert_equal 0, no_developers.length
end
@@ -421,11 +548,11 @@ class FinderTest < Test::Unit::TestCase
first_three_developers = Developer.find :all, :order => 'id ASC', :limit => 3, :offset => 0
second_three_developers = Developer.find :all, :order => 'id ASC', :limit => 3, :offset => 3
last_two_developers = Developer.find :all, :order => 'id ASC', :limit => 2, :offset => 8
-
+
assert_equal 3, first_three_developers.length
assert_equal 3, second_three_developers.length
assert_equal 2, last_two_developers.length
-
+
assert_equal 'David', first_three_developers.first.name
assert_equal 'fixture_4', second_three_developers.first.name
assert_equal 'fixture_9', last_two_developers.first.name
@@ -443,8 +570,8 @@ class FinderTest < Test::Unit::TestCase
def test_find_all_with_join
developers_on_project_one = Developer.find(
- :all,
- :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
+ :all,
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
:conditions => 'project_id=1'
)
assert_equal 3, developers_on_project_one.length
@@ -453,6 +580,15 @@ class FinderTest < Test::Unit::TestCase
assert developer_names.include?('Jamis')
end
+ def test_joins_dont_clobber_id
+ first = Firm.find(
+ :first,
+ :joins => 'INNER JOIN companies AS clients ON clients.firm_id = companies.id',
+ :conditions => 'companies.id = 1'
+ )
+ assert_equal 1, first.id
+ end
+
def test_find_by_id_with_conditions_with_or
assert_nothing_raised do
Post.find([1,2,3],
@@ -493,6 +629,16 @@ class FinderTest < Test::Unit::TestCase
assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
end
+ def test_select_rows
+ assert_equal(
+ [["1", nil, nil, "37signals"],
+ ["2", "1", "2", "Summit"],
+ ["3", "1", "1", "Microsoft"]],
+ Company.connection.select_rows("SELECT id, firm_id, client_of, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}})
+ assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
+ Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}}
+ end
+
protected
def bind(statement, *vars)
if vars.first.is_a?(Hash)
diff --git a/vendor/rails/activerecord/test/fixtures/all/developers.yml b/vendor/rails/activerecord/test/fixtures/all/developers.yml
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/rails/activerecord/test/fixtures/all/people.csv b/vendor/rails/activerecord/test/fixtures/all/people.csv
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/rails/activerecord/test/fixtures/all/tasks.yml b/vendor/rails/activerecord/test/fixtures/all/tasks.yml
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/rails/activerecord/test/fixtures/author.rb b/vendor/rails/activerecord/test/fixtures/author.rb
index 5571f2cf..1503e17f 100644
--- a/vendor/rails/activerecord/test/fixtures/author.rb
+++ b/vendor/rails/activerecord/test/fixtures/author.rb
@@ -15,13 +15,18 @@ class Author < ActiveRecord::Base
end
end
has_many :comments, :through => :posts
+ has_many :comments_desc, :through => :posts, :source => :comments, :order => 'comments.id DESC'
+ has_many :limited_comments, :through => :posts, :source => :comments, :limit => 1
has_many :funky_comments, :through => :posts, :source => :comments
+ has_many :ordered_uniq_comments, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id'
+ has_many :ordered_uniq_comments_desc, :through => :posts, :source => :comments, :uniq => true, :order => 'comments.id DESC'
has_many :special_posts
has_many :special_post_comments, :through => :special_posts, :source => :comments
has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'"
has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => "comments.post_id = 0"
+ has_many :nonexistant_comments, :through => :posts
has_many :hello_posts, :class_name => "Post", :conditions => "posts.body = 'hello'"
has_many :hello_post_comments, :through => :hello_posts, :source => :comments
@@ -33,13 +38,13 @@ class Author < ActiveRecord::Base
:before_remove => :log_before_removing,
:after_remove => :log_after_removing
has_many :posts_with_proc_callbacks, :class_name => "Post",
- :before_add => Proc.new {|o, r| o.post_log << "before_adding#{r.id}"},
- :after_add => Proc.new {|o, r| o.post_log << "after_adding#{r.id}"},
+ :before_add => Proc.new {|o, r| o.post_log << "before_adding#{r.id || ''}"},
+ :after_add => Proc.new {|o, r| o.post_log << "after_adding#{r.id || ''}"},
:before_remove => Proc.new {|o, r| o.post_log << "before_removing#{r.id}"},
:after_remove => Proc.new {|o, r| o.post_log << "after_removing#{r.id}"}
has_many :posts_with_multiple_callbacks, :class_name => "Post",
- :before_add => [:log_before_adding, Proc.new {|o, r| o.post_log << "before_adding_proc#{r.id}"}],
- :after_add => [:log_after_adding, Proc.new {|o, r| o.post_log << "after_adding_proc#{r.id}"}]
+ :before_add => [:log_before_adding, Proc.new {|o, r| o.post_log << "before_adding_proc#{r.id || ''}"}],
+ :after_add => [:log_after_adding, Proc.new {|o, r| o.post_log << "after_adding_proc#{r.id || ''}"}]
has_many :unchangable_posts, :class_name => "Post", :before_add => :raise_exception, :after_add => :log_after_adding
has_many :categorizations
@@ -68,9 +73,13 @@ class Author < ActiveRecord::Base
@post_log = []
end
+ def label
+ "#{id}-#{name}"
+ end
+
private
def log_before_adding(object)
- @post_log << "before_adding#{object.id}"
+ @post_log << "before_adding#{object.id || ''}"
end
def log_after_adding(object)
diff --git a/vendor/rails/activerecord/test/fixtures/binaries.yml b/vendor/rails/activerecord/test/fixtures/binaries.yml
index b01e6902..d150c570 100644
--- a/vendor/rails/activerecord/test/fixtures/binaries.yml
+++ b/vendor/rails/activerecord/test/fixtures/binaries.yml
@@ -1,437 +1,132 @@
flowers:
id: 1
- data: !binary | /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsL
- DBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/
- 2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy
- MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAFeAQcDASIAAhEBAxEB/8QA
- HAAAAgMBAQEBAAAAAAAAAAAAAAUDBAYCBwEI/8QARRAAAgEDAwIEAwYEBAQD
- CAMAAQIDAAQRBRIhMUEGE1FhInGBFDKRobHBFSNC0SRSYuEzcvDxBxZDJTRT
- Y3OCkqJEstL/xAAaAQADAQEBAQAAAAAAAAAAAAAAAgMBBAUG/8QAKxEAAgIC
- AgICAgIBBAMAAAAAAAECEQMhEjEEQRNRIjIFYaEUI0KBcZGx/9oADAMBAAIR
- AxEAPwD3+iiigAooooAKKKKACiiigAooqrf6haaZaPdXtxHBCvV3OPoPU+1A
- Fqo5p4reF5ZnVI0BZmY4AApBp/iy01qK5fT9wWCXy3MgwegIOPQ57+lSGXz9
- xL+Z2POaFsOhbe/+JGjW0hSBZ7kD/wBREwn4nk/QVHD4zfUELWzRqMf0jOPn
- np+Ffbzw9otxG7T2MKZyWkT4D88ilGn6XZ6e0v2SNgjH/iSEFmHYewqU7Xsd
- UxjNqd9Nz9plAPUD/aq+ZmU+bI7c9yTx+NSrt5AxnviuRgMQSfnUmxkivMko
- t5PIYGbafL3DI3Y4zUGnXBtbVbZrlzcRAmXbkc5ycc+9XWkQA5PGPqKzng+x
- OqmZpmfDO3mMDghc8YPrWxV6Rr6NLZ6/dOFaG9kZTnaM7s44PWjUfHl5ozQm
- 4s1uInJBYZQ59KzFpaz6Fqs2kytlQ++3du+en4jj51Y8QvDcaUF3Av56ADHI
- OcHP40OUogops9GtPEllcBRIWgdh0ccfiKbI6yKGRgynoQcg151tA4AzirFt
- c3Fo5e2mdD3CnIPzFOsn2K4fRv6Kztl4ojICX6GJu8iqdv8AtT+KWOaNZInV
- 0YZVlOQaqmn0I1R3RRRWmBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAU
- UUi1TWGBaC1J9GlH6D+9Y3QJFrUdYisgUQCSb/LnhfnWYeBfEE8v8Sj86Jei
- MeAe2PTv0qIqWVsTMnvj+9TQyvBbCKCRNx5aRxyT8qTnuxqMdqWnyeCtTeS0
- eRtM1H4MMcmNwcgZ79Tg+nyrR+FtU+1zy27cMi5bJ4znioNc0yTWNPmgciR2
- X4GJ+6w6Y9P96W+DUVvD6nawnSZ1kcH4gwPGT8jSXux2rRo9WklbVltpR/h5
- YMR55BIOScfhR5SEbhkgjpnNRX0811bwBgBcRSh0ZiACOhB7ZIohiRQYymNp
- wTtFLJ7BdEhQ46DjoOlLdUj1CSPFrK0L9Q+0FfkfY+vamRBAciQr74Jrpjv2
- MsilCMjjOfrWaNsWeH7HUrI3V9q7KqrATCcq3xd/u+1MvBaQjw/HJEMu7MJP
- mGP96W6nqZ0u1/lyfAzZwrdKe+HpI5dEhljQIrs7kdsljmqQq9Cyeip4p0aT
- VLeKe1QNdwdADguvpn58/jWI1CDW4PLF7Yy7XkGP5YG9+wyOpzXqu7r/AN6y
- Hi2/JvtLsYQMi6V3bBOCBkDHyOa3LFVbNxSfIpaWdTUN/ESEkP3YdmCo9Wx0
- z2FNt27+lh/y1BHE6AkXBLMcneepqdGcjDKp9xUn/RpIFBGSWJ/1VNZXFzpU
- 7T2h8yJzmS3HRvceh/WuN4AyQT7CoZ5YUTzHLY6AhelCdGNG407U7bU7cTW7
- gjuO4PpVystpyC2tIlUkFRnPQ5praa5aTXYs2mX7R6DofbPr7V0p/ZIaUUUV
- oBRRRQAUUUUAFFFFABRRRQAUUUUAFFFI9b1IBHtYnKnGJHXkj2FY3QIj1bVt
- 7m2t3wv9bjv7fKkgCqBt49s4rvChVAIwR3FUri+t4AwBDt/lU5IqDdlEi0wb
- qGP4igDoeM+uKV/xiIuCLZsgfeBFTJq0Dbd4kjJ7EVlm0XfLywOyPPqc1m7K
- Qaf4tvbBXDxXq/aYmRsDcPvD5itCk0cikqSV9uaw3ieC60nUbXWbVH2QShg/
- YZ7Ee/SjsaPdG6+PaeXHGPi+Kvq4XOSSf+XioYb6G5tVnWaMb0V1G7Gc88Zq
- YljkDI49jWCg5B3Kccjngj86TPdXGkTJ5+JrVzxL/f0b3/q+dOdzc9Bx3FVp
- XcBo7iHfGwwXjG4YPqvX9ankjf8ATArx2tvq9wpuIle2lJhPzIyrexzVrwoy
- 2PhlIp3OYJ5YyzHJOHNZ+DUP4XDJaKA/xCe3kU8DBG5T+Rx2qTSn82/1JpN2
- EuCVbkD4iScDt2rcWZPXsKtM1UWvW0k5jZZI1z8LshAP/X0pTqNlbfx+G5uJ
- WFrnz12ty79MZqxEMHImb/8AL+9KNatXu7zSLYOcS3JQ4OMjGccfKquV9mRV
- PQ7VgzHZh0z8J9R2r4ygjJgOR6GvgiMfwFcY4x6e1SKcZwG49KUY+bR03MPY
- io5UeRo18xVj3DcGA6Z55qYnjJPHuKjCuZnYrwcbfzzWICxe3c038u2bZH3f
- puHtzxUEcSx4KiFccg4qfZnBKr060eWrf+mh96ZuxaNDpGqmcCC4dfN/oYH7
- 3+9OKw2NoBUKMc5BrS6TqX2uLypionUev3h61WE70xWvY0oooqgoUUUUAFFF
- FABRRRQAUUVDd3Mdnay3Epwkaljjv7UALNe12DR4UViTNLwoXBKjuxrINrtp
- 8Z/mk5yOBzz86w2v22q61rN5qV1ckFz8EYHCL/So9gPxOaZeEfCsGsWly1/N
- KskUgVfLAAwR3yDUv3einFJWxzda1PczR2lhbu80owoIGc/sPepLfwO8+ZdU
- 1CRnbny4Oi/U9fwp7o3h+y0NZfs293kxueTG7HoMdqamnUUhXL6MqfAmnf03
- V2Pqp/aqdz4OvrYM9hqmVAJ2Tqf2yK2hrlxlGHqD+lDimHJnnUv22ythcDEx
- ABIjBRjn0x1qP+Pw3VpNZXnmbJVKOlwCSAfzpyozaRkf6TWquLa3ucieCKYf
- /MQN+tShHkikpJHleo3yfaLNIifLREjXYODgY4rZ2+pQGBRL8L4GcqcE/Okf
- iHw7Y2/i/RpREYbG9cwSrExUB8cEemcj8Kin8PxRW0vkyTRshZQwkORzjPvW
- SVM1U1Zp1vrZwwEij/7hXYuoC6YmTnpzSlvBN1jCa25/5oz/AHqH/wAl6n0/
- jEeP+RqPjkZcfs41y0S9VpIDCJoviLAYPPTJ7qfun0OD2r54Xt2tNJkkmcMJ
- ZN2ScEEDBBqm2lXlhLceZfebLGTFjACyBh9315qHSI7vUGuNLtZ1Ty084tJ0
- zkcHg+p/Co8ayX7D0bBJ4eAJFz6Ag0n1m8W3v9Ikt23SpdhsenAGPzquPC+u
- MuUvrNvQ5P8A/mld5puoQ6nBZ3MqNPuDI0S5A4zjGOapJNKxoU2bjz4ppjIH
- XLEnG6uvNjDEGVAfQsKyMPh/X5YopTJahZCQm4bTnnqMcdDVoeG/EQXn7ER6
- 7v8Aam4SYujSNcQooZnUAjgk1Vk1e1jbClpOOqAnFIBoV7BfCK8u1Zdm7bb/
- AAjr3OM9qt2nhG2u1aee9vNpYgRq/Ax7nJpVFt0DaSsmk19UwRGqjHR2AxVV
- vEvnSeTCUaTH3IlLn9KbweEtEhOfsXmn1lkZ/wB64nsLS21BBbW0UIEYyI0A
- zyf7Uzx0rFUk2Imv9SuHKW+nXcjA87VAH1xU0C+J4LlLmDTJEkRtykuOD+Na
- TRRiCZvWT9hTPNNCCasxyp0PtJvpNQ06KeaBrecjEsLHOxu/09KvVnLK7NrO
- CfuNw4/etECCAQcg1YQ+0UUUAFFFFABRRRQAVjfFl7Jc3CWMLqIozmTJ6t2H
- 0rU31wLWymm3BSqEgnpntXmbxRFi93eFpGOWII6nr61LLKlQ8Fuylc282Sm9
- GLZxtPFPfBltJBZ3byDDPMBjOeg6/nSaaGzyT9rcducf25p14Tt1gF5i5EpY
- r8I4wOeaTF2PPo0lfK+18roInyuTzx619Nc5yR86AMmn/uij0X+9arOe9Zcg
- Lan/AJW/U1pVbKA+oH6VHF7KZPRl/wDxAguZPDyXFvn/AAs6zPhckAdG9sHr
- 7Gu9NUa1pSz+YiCdd+SCeT1H41opESWN45FDRuCrKehB4IrzPStU/wDL+o3O
- jzo0qwTlYOvIzwMDrng0ZNbGhuNL0enhgVBB4r4XGcZ57CobdmNuhZNjEZ25
- 6Z5xSme41FNajxbM9sgyxDBQFIIyBnLHPb2qquhIxuxdrM9iLq4tp7nE87/B
- Eg+LPGPl659qVWd0ItanAPlSXVtASPUgDP49aZ3sS6hqmNnCTGSKXaTzxkYH
- Oec4r5B/Cv4zJY+WJSYUy8icRhST97qM59ulQS5y0WStaNFo6sYQsz4y/Xpx
- Vu50/SrbVTfyI5uUUlGZshBjGR/ekg1CQ38sUdxCI4uGVyCu3b14yevH071F
- q2tm40O5ayQz7I2SRZOGMeSu/wBcZHz71alFD4sDlNL7HR1iC7ZYbWZJZFYl
- gxycd+frXUM4eC423SlUDFnEmCp78Y4rxWzvrmyuT5UjJvDAc9Djg/iBWi0G
- z1OWC7nvUxb3iZCyyhDLk5OBnv61kZNns5vAWJfsb5pUcIkzpdIcslwnBx6Z
- qzDbm1i8vOVDEg56gms/a3t19maNIII41ciBUbKP/pO3gZ9+9ffD+uS3IuRf
- xSQyrhREImyOee1brs4s/hucXKPo0OaUXuTqDsD9xRn8P96ak4AIIKsMg+tK
- ZmBnum684/IClydHkxTTplvSRtsz7uf2phmqGncWae5J/OreaaHSFl2SZp1p
- F1vjNu5+JOV9xSLNSQzNbzJKucqc/OmMNbRXEUiyxLIhyrDIrug0KKKKACii
- uJHEcbOegGaAFOtOssE0LKXRY23KOp4rz4G2KqYrXzSB1MhOfpW5nlKpJKSM
- gFufWkDFY4wcqD7cCo5R4GflePlm01Rjvg048JvbNNe+RC6nCbmL7gOvH71z
- NMh/rQY5zmrOgXkbXU0COjBl3jaO4/2NLjVMeTtGgr4aK+E10ET4a5/qHzoJ
- rnuOO9BhmSMwMPdx+Zp9A262iPqin8qRngEf63H503tDmygP+gfpUcXsrk6R
- P8ZcgIdgXcX7DnpXnOvW/keMLO8x/LndkJxxuGQP1FbXVrxba2IbcQ3BCnBG
- eAfx4+tYi91G+sFa4liaWzyFwy8MT0x6H3olttfQ2FPtG5nvrO3s42uyjKdg
- 2k45OPz6UtvJ7OzX+L3N/cSxN9yPHwEYxjH9Rz3+VUNW1C1bVbDTZraV5cBg
- XJ2o3RenJ619n1C+kv7iyfS47q2ihDLC2SgbdgLkDt6VXotGHFHwv5MkGr6f
- bsIGhMkqI5Ujg4O2ki+JGu5YtS+yC2aCRlLA7lZyh27vUdevv0qbSLkskpuo
- pPMM3mJAchdmcFc91yzD5AVNJFDZ6vYXbqYrFpzIYHA2IdgIJ9Tlu/SpKVp+
- jE1xaS2z7JPeaQl5qi6Oi6nJI3ltC2+IjIySOpx+fFfbLULmQLe3Vu9tLOrB
- 9q/CA3XI/PGO9T3Op2sV5HZlJWi6ZRMoueSQ3TH9qQ313cNrU9n9saa3SMPF
- txg7jjHAHvTN0jVKUUnI+ar4euINIjlijE0GTK95GRgbuAo74+feov8AzXdJ
- bSpcxx+RFCqCIjKS9sN36VY0zV5U8QyjyjeWUluYZLYOQriNfh/Aj86+eIbO
- C70e3uY9GuLK6mw00IRjEqrkb1PoSRxSxaltHteL5X+phU4/lfY78MapBa6A
- 2pzLsWMsiW0RZsDspJJyOc57Yqg7R6nplrqtoLiR7TepG7bvVWyOexwevtXS
- aWb3w3Y2lpfPGtunnO0cfwM56Bj1yMdKjtLN9QsorC31FLXVbdyksUHwM68H
- cezH3HrzTd6FjlhHI3e7/wAG40mXULvR47i+hjiZmyio3RT0HrVCUh4p27lj
- +pq1ptpLp1p5U15c3Ug+887Zx7AdBVILnT2bONxFLk6SPFzOMsspR6GlkNtn
- EP8ATVjNQwDEEY9FH6VLVV0crOs0ZrnNFaYONC1KN55dPLfzI1EijPY9f2/G
- n1eeQXMdv4hjvAnxxfy2YHqvf9a9CUhgCDkHoaVOxmqPtFFFMYFUdRkxGsY6
- scn5VepPev5l03ovAoAWaqC2lXIBwdmc/UVmryATxqzyMSnI9K10iCWJ42+6
- 6lT9ayYsL74lLxoBkZLdcGo5NbHhspSbUhZAMFl6188NqV8RR7XLKUfPHtU0
- +n3TDHn2pOO7Hp+FWPDGnyxarPcTPHlEwqo2c56n6Y/Olg7Y8lSZq818Nfa5
- NXInJNcE81xdTpbW0kz/AHUGfn7VW068+22KTH72SrfMH/tWclfEwVOOZP8A
- 6rfrTKzcCwh3EDjH5ml03Dze0zfvVq2KHTB5jBVUsMk9OTSYV+VM6eKlSYv8
- Szo1lJFjEoUlM9++PkQOD6rWSl1GPVNIudPQZUWrPvP+ZQCAPw/On+txfb9M
- YRuXVVOwjnHr/wBfKs5a+H2to5J5b3EiwvlFwEAIPWun4JKTr2j0MfjPEnH7
- Rb0O/TVbaF5IVDxIsQCgu74/PmtNNbyrbrcOVgeNcrGAM8c7iQOG9ecdqwXh
- C5u43kitI1aTf1ILdR6d69Dgg1JmC3dygk67Qq/sKEvwSbJZE1BJtJCm0vG1
- HR/Pmgt/tQm3w7i0bZHTnHIx17VT8TX88Wgwy3VpayyzTlFkWTcsLDuBjvjk
- GpdUtG1LTHhglkV5JiiKATg55OR0yfeszqpKrp2nXVyzRjYJVPCnLEFh+PWo
- T2miM0q19lvw/BqF/EkvkwSQxRsyksUDM2AARyDyDx061SuLhY9RR12hnQxy
- NjlcZA/DJrVXFvarexWAypnjEVwqNtVWjH3/AJlcge/NYeVHinEbv8LMcM/U
- Zri8mMqTT6ORt9n2y1aXQbthp7lrlsoFMQfqfetT4Wn1t9bmk1VtRvLdQd4+
- 0ZjVj2IzgnHbt6VVh0qS1msYI7VZprtVlcn4OMnI389AM1usGCwYFI02ofhj
- GFHHaqeMr2bGTXQuvb43asQqRxIpWNEHAH96R3+iTW+u21/aIWNwoJKttZWC
- jODVzdi3b3WtKijy48j7oBHscVbHtuy6yvHKyNmYaczu4Z/LOT747+9U8YsU
- X3/TirWoEixkx7fqKqghhAB3GcfM1mTtEou7Y2XhQPQV1XzNFWJH2vvp865/
- OvvagDPJMftcwbqHbr869C8O3n2zSY8nLxfy2+nT8sV57e2E/wDF5DAUMb4Y
- 5bGCeorUeEVubW6mhm27JUBGGz8Q/wBjUouplGribCiiirEzmRtkbN6AmkJJ
- JJPU02v322rD/MQKUUAHz6VjdunjcXu5nBJwPQdhWz71kbkP9olC6Ygk3nJ2
- gjOe1SylMfZQlGkYJM0wHrkCrPhj7AmtSfZ5pWeSIhEbtg5JriUTkfFpkBI9
- QKseHZJP4vIg06OBfLPmSgY+QH1qWP8AYpL9TVk1H5ikZBB+X71WvZpIwCF/
- l7SxORx8x1rLa54peztU8pfizxnvjviunrs6PH8KWT/sZ6/M8toIwAESTdNh
- uQuPhOO/Jr54YdZLC5aMlohOVVuxwADj615/b69qeoaq08cbvb7fLuFUZBQk
- 9flk16fZoLfSrWK3gwFjVUQHAUY6n9frUlivLzsn5XirDNJPsXTn+bdA/wDx
- jj86U6jeJb2RkuHxbxSt1PUk54Hc1b1HU4rS5ePyWnk35l8vlV/ufYfM1gvE
- Mlzf6jHCC6yP8axFs+Wp7kdASPyrqxYuDcrs7/GwcGpSHV7eX1xobTWsQt0L
- qI92S7ZOOnQUvk8OXklh5z3Msmw75IT0cdz8/nUHiDxMp0dLC1zHeJMjMwxh
- dvPB789qjt/H9yLUR3dmjybcebE2AfmP7VWWXE7Q2by8TvGXtIvZdJ1PUHjb
- Ys6xMpx3yeR+dX9S8V3ul30BCRSrMDnepLAdBgjpnmvPodUuWnlkZgzMMYbk
- Ae1fBqtw95FPI+4xgKN3IA+tcDuzyJytJ/Z6SPFlvNA0UsJhZhjGf0980jPk
- ag80TyZuG5LNzypzgew9PnVFmaRhNLAPL3KOExgk1Sllc3SNajE6uWPP3vlW
- OaUqZabg40ns1uma1Dp97MsyBlZCvxLkx89P+WkV8Gv7u6nRT5QYFQzYIU8D
- A79K6Zxd3Pm4I2oRIcfqKl0x7h7hzHCsqKdrsFB2Z7j9allleokZf7m6IPJ1
- C5svtZaRre3HlKwbO056e3BNbDTNf+1aJJDcuBOIgiHu/b9vzpVc3FzHYtM8
- 3mxlzDcqMfyjxgkf5T2P71Rj8tdRRQ/lqM4A6ZPr7VBuWOaUfZFKmaHP8nHr
- WpR1bhTyvBBHIrPTLZWUP8y5EwxztRjj8BUN3fXlpGl2pE0BAwzPyAfQ16sM
- HFNyZ6P+kc1bdD7UHxbquOWccH6moxGRcQsP+GQAPxrODxDNdvkBYbdOMdSx
- P5AVqbDElnFksSGzyfQ0QxxyxckZHxv9pyLwr7XxSDnBBIri4nW2haR8kDgK
- OSx7Ae5pG67PPao73DdtyN2M49q6HSq9rC6K0kxBnkIL46D0Uew/uameWOLb
- vdV3HCgnk/Kl5atgI7pYm1OZjfSRfFgoCODjmmmmOltdwT/xF22MMqSMEd6V
- 3Em3U5vMsDKSxG4KDkDpV5WhdCG01hn/AOVmot/kXS0ejCiqumzi5063mAI3
- RjgjBorpIEGpt/w0+ZpfVvUGzdY/yqBVSgD4x2qT6DNI5ZY+GduW5zinbruR
- l9QRWNuYjIqB5HAUYwvFSyDwL8k0KoWDA49aqWuoKuoRCMln5Coq5ySMc+1U
- nVUTqfTJr5oK2y+IIQ80kbPlEZT1J7H2NThTkWj3shv9Qvopjc3AYRnekqbc
- lSCRkEHHpS/WbqC/8LQ3ChfMViOgznJHz9K2uuae08EhtZo4lwfMSRcqw+Ve
- fT2V5cyNZwNb+TGcyeQmFz8zkn3roPfw5ccoprTRBpvlWNgFnIMcoO/y/vxt
- 1Vx649PpW3i1K8ufDdobBV81l2NJnhSOoX3wM5PCjk+lefajpuo6YVuWhdrK
- U8SgcBuhU+nPrTDTNVl03T7hHybeYEBCeG6ZGe3zrljOUMr5KzxJRyZPIbSv
- ZFc6mJppXVi6hyikfdwP8vtyeTyaQajqN1Cs1nGw8mSTzS45cnbjBbrjHGKc
- XiFUEtr/AC4CcALyDnBz6+tZ67tZBG8oRtkbBHJ7Ek4/HBp8kZqbk3pjediy
- 4Zu32KpG7CmOiaeZdQtZLy3ZrJ5Bvy20MM4HPpnFUZAf8hP0q3c6o8oYxIY5
- GTBYHABxzgDpWxqjghXs0WsWFjNExgtYbW4iB4hXaGXuCPUdc1jAp3EGnEuv
- S3LDKhJiMNj7p46/OoJNJuYLCLUiUa3kYrlWyyn3H0plrseauCaLFhcXETrF
- 5itHwWVxkEjp9acwaXHdSiZLYOqMCQJMLk9Mhsj86zkEzSXGegGWxn0FNLa+
- lis5JLa8mt7gDbIqNgSxnr0689q55wuVoxPpmgtLOWS2lZmJUEMzyOAAfcjq
- Dg8DPNPrS6g1K6ktbOwhgKBVy6/CMcsB3yKx2l6sttcLIYhcKse0RkZIOM7g
- PbmnOlX0mpaVObdtupQnzBI7qo29CqjOT79uRW4fxSXstFpvfbDU7Bk1nzLj
- NnEQBNKULRHPQE9s4pVLmO7lERQ/GdpDZBAx0PpTfxDLq1n4Wij1LUIj5s2y
- SEgGRl6g5HYd/pzWbtUjhs2mWXzGX4uOMe36UZqg7JypScWO4/EFrCBHcpcM
- /XK4wD8s1WkvjqtwsNu7NGWwqHjBPcis5d3G+Tdg5bOCO9QwTSQyrKjlHU5B
- HY1s5znHi3oZ+TOS4y6PQL/TvIgh8pcPCNpH+cHrWgiuxaWSiVxGoxuJPTPQ
- f7VlbDXHlRUuI2AIBV+uTjn61dtJI2kE8rEuGzDGxGB/qYk4B9M125fKxwin
- jPanmgsaUTV298DdLAu2OJI2ll3n4lXgLn0J5OOwFVrzVIoWFzIQ0i/+72+e
- Vz/6jDsT2z0HuazyWr3F35twpVmJkLQANxn1z0HSrwg0tzsjuZkctktLHnHu
- Tj8q4ZvLNNw/yeRkwy3OySzvr26BiSRzvbLMik4HpTi3tPs264MZaVQT5kzc
- jjsBk/iahtg8cax2WpxzKD8SqieZj2z1q9LaSNbyhru4clGwMqAePQCp4MDW
- 5ttnOL4r6OM7pGxnk59aaRX8Lx5WQHA7VmIPKePDBSetMkWBcbBtx3U4q10W
- aN34dvEutPbY27y3K/jz+9FJ/Bjqs17EHLbgr8/Uf2oroi7RF9jS7O67lPvi
- oKkmO6Zz6sajpjArJ63E9pNNKCrQFwPvEMpP7Z/WtZWT8QixN1Mtx5vxgBlX
- AB461HOriNG/QnR0nbaZynrlckUxmW10uGK2S5UTzpuErdz25/asyA9sWcOZ
- IlOPNI4+voahlvbe9E0VwFmi3KqSrz5QPX8+a4vFzT+Rwmv/AAdfhtc6yly3
- 1mSa9NnegPLzHuJ3itfpVlGI1iOFbb8QRR+HXivOLCwuZ4pbvzBw+wzSOFAx
- x1NbnwvrOnTnyBeIZohsBYbBJ7jPWvTtJbPR/kY44QTg9/Q61W5stN0qQXWU
- gcGPCruJLA/9+a810/TJ7u5FtJbNLCzbd/Kqvo3rWm8S6xDdynT51P2cuCjI
- fvY45/Glya6ukXUXm7JIYv5bNHyQh7ZHccHFGOcHLg+w/jcsccJQ/wCb+y7N
- 4SjFmYY7whs8jy+ODxjng0raOSTxHb6HJCV0+VQJfg+KQKM8t6ZxWwinWR1Y
- SLJDMA0Uq9Dn+9c3Vosw3D4ZB0YdR8q7XhjI6MrnmhwkzPa34DsrmNZNOb7J
- Mp+IElkZflng1kn8F3q69Bp5eNlmUv8AaFBwFHXI9eRx7ivV45fMiDNjdjDY
- 9ahRR5obA3KCAfrTPxsct0csvCxSd1TPOr//AMOHtru1axne5RpVWVHUAqp6
- tn0FSXuiXekeDdRhvvLbADxmMkgHcPb1r0VcctVW5tY9RzbTxLLbhgZVYZDE
- chf3NLPxYPoWfhQpqOjyLw54fu9YMs6FYrVT5Tzv0BPp64/emH/k7UyjSwiO
- RMOVBcKxAOBkH1616usVtDD5YijWIcbFQBfkAKzWr6DA8UrWsW2/nYgNH1VD
- wR1wBiufJ4/FWck/D4pGQ0DQbq+t7u4M32aOJ1VhKjckc9uncH507tJtL1Gz
- k0/StPgNw33zIQoYY5YZ5YZ47VHptjq9np0kc6faIzKqCGSUnacjH0B5OK7+
- x2+l2989rO0GrRjMfl8qWIyqjdkHIzkVDhSJ8IxTTFZjkksp49eWZZc7LWQq
- SVYHO044wfeoZY/8C8QABxj2HvVOfX9Rv5IINTuA3kSH4WjCEN0ycdT1poLK
- W7RzDtYIuWBYDI9Oa4M6lLIlFEG+b0hJHp8Mlr5ousXhkCRwbOCvTdu/Ouls
- EgeQ3GSyHaiQ/HkDuSOPrVm6eOEMsIQzbQpdzxj29KoB7sfF52VUfHsHTnHp
- z1FHJzivQtK6SNJpltNe6WTGhWDc2AHwSe5JPNfNMuIrTW5LSWEXEQj2eQRu
- ZD169D/Y01gvo7fSf8LA+ETc0sq4APqf8x/Kl2i6bJaXrXbEtdRL57Bj94lv
- iH54r1oeMoxS/wDZ7ePx6jFP/s0M1vp8b201vAYJHI3KmUGD24PyqVB5dwGS
- 3ildeMSSsJPmCTj6Zqtq+qRXEEb2YZVE2xm+6UbHwg/PI/EU20a+t9V05HdQ
- JSSkiMOdynnj1quLx4Y5Pj0XhhjjVpdjK3vFKIs4eN2+5v8A0+dXtjLglSM9
- KR3TXlmN0UMc0HRkY9R9ehqxZ6/Z3DxwfzUJGPiXOwj17ikzY1F2ed5Xipfl
- ETw21lNdyst15aFziPI+HnpTIadalBi7P5VSt2mLuX06OTLHLKQQ3PUVfWS2
- uIZolskeWNSZoQBvUZ7Dv9K8/i2zl3Q68I20dteT4lErsMFvQdQKKPDO2OfK
- 25gUscKRgniirQ6OeXZfY5Y/OvlFFOYFZ3XJJRclBZCX4QVfGePw9c1oaQ38
- 4E8jM5ADkDHtU8nQ8OxG0c15ayJJFLZiNS4KfCWPT0rz4SzWNzLMsJkhclTG
- 54f5Y+Wc9q9RNxEwPTB7N3rH+ItklykpaNM4SOFEGyNAeeffvSRpqmel4+aH
- B48iszyWX8Zg321rIhJ3CN2JU+6kcZ+dSWIl01wJ4JYecfGpxWkit7OwuWe3
- EcaPGGPlggnPOMDgY9R7VPNNBLZFLpRIrLzuIIXHqaHgU4W3Q0fBWXH8rkIr
- 7WbaVlgcFGkGWfoAexHofen2h+KVgSHTLi0jkjxhZIlAIGOpHQ/Okdrpel3u
- oRJJazENlg0LFgQOwxk/Stfaz+GrOHybeWGNV+EgRsDn0ORmujwY8VSkHh0k
- 4t3/APS9b3On3kPl20kYHP8AL27CD/y/2r79peE+XOfZZOx9j6Glk0/h6brd
- pn1wRj8q5+0wbSkGqQzof/TmYE/j1/WvTX9nqQj9jTzSHYoDgj4h6GoGvwry
- BZI92DtwwPOeaWzTs04toSwVVBOeck9vcelT/wAEkWM/cywzszz09a83P/IR
- xyonPJCPboam4OyOMMFduM+nrXaTwrGUjcEIcHB5zWeSU2s+04CNxzxhugx7
- 0fxBYoGkCkDvI/wIPqeTXZgzLLHkVjBSVjvzTLKoJC88e3+9LdUuJZbqTTLY
- yJKIw5McgEijPLcdPQDvmsze+Ir1966apAHLTOMbvYDsKg0K1uX1BtTErWk2
- QkqoMK3f8/SsyTt8UcPkSd8Yj2/khtLa+uo7gGFFVpo1fDM2eDuHIPbpz9Kj
- s9Ys9S0+ENa/yVnCMHCMGYj75475xx3qD7THd29y1hCIY9388MBho8Egc5wC
- fl3qC5N3baR/Fba2jt7tCN5GNu1uMbenoa5XfLRxzjxf9IkfQtOluriW4khu
- C80eWQZ8o4OUIHQEAcmoNXsV05/M2NFA5xGCckH5ZqOw1jUNSuDeTyx4hiDT
- KiYDx89scnI70/tJv41bwM9vI0qS7UTG4lseo46H8qnPHHLFrr6JcOUbRmvs
- 9rdQgeaWuiSSuPhI9u9aTRvCSvYIL7EkQZZFRGwp6Ee/51WTSbY6jOl7cIkz
- uTEYsoEI4K7T3Bx165rcaPYNDZEMWYM3KsOMEDkenTp71GOBpq6pCwxq7ZVf
- SNNuykBjJTPmqquQFIPGfXB9azmq2Y0S8S71EtFayboxKhyFOMgMR0zTOXUY
- rPxRDaqx8iRWJPv8/T+9P5ZrKaCSzmCPDICrxkZDAjpXVHI4nouOTErj7R5h
- pmoWuo2et2sKs4MgkQkHlSMZ/Fac6ZdmzVXkt2aCZ1e6mXBETAbSxHXnCn8a
- W654ck8E63He6erHRbzZDNvbcYyTkg98cZB+dSyXcLM9gtz5YEgNwQMgxA5I
- PzHHtVvkuDd7Mh5LlifLtGutNSs763M0F1G8YIBDccdzSO4YWV7IcKDICY3x
- nIxmqwmstP0Wa8sPLexlYRSM7fzFycFlI4OODgio/EGo2F/YwG3uFMwZVjkZ
- Qu5SSBjvj1zUPmluxMPlOE3e0RWuuTfaAsdwTCx2BMY6HPA9Ke+H5LdtVkKt
- M87p5kkshxkNztGPxwemazMfhO9JucXUEi2qhm2MecckD5Yp5b6ncTWifZYW
- iD4/xEhALpjAxx1965vy/aTObK9cr7NppD7Lp1afe4Ysf9KkcUUj0ENcyPu8
- ryVAOWViCTnvkZNFPC6OJ9msor6ww7D3NfKoKfKy+qRhbyeMu6hn3cN2PNai
- kfiC3gkaFpHaJyCNyjqPSpZP1saPZm3gRTtLNtz+NKNT+zhHEagt1AJ4Bp29
- rbIMteSbSP8AL+vtWf1bTrG7YCC7mMgIAyBtOTXK5WikutDe18J77RLx9ctE
- hIBZ44idnHK53Y/EVl7klnIDFkyQMjGQfarUVjJaPJG1wZmzt3jeMjpjDAGu
- Whw3I69M1yZszrhHSQnzT48L0T6Glgl0v2/4baJSVXnlu3TmtaL3R7+4Cx3U
- csznhdrbj+VYpUyvPyrQeGdUtrJJ4ZjFD/X5zDBI9Ca7P43yWprE6p+/Z1eF
- m4y4j86ZB/8ACX8Kim061Cf8CNvmuaXSeNNOuLxbPTvOvZ2PAhTC+5LN2+QN
- NVSRbcz3m3d/8JCSMnoMnkmvo4uMuj2seXl7ErD7PeMyglWwwKD7p+XcVfbX
- Lc8vARKqkBicVNHASgY43Hk46Z9qq3VtGGVizJ8eMqccEVw5v4vHkfIaeCGR
- 7KDHzrhZnDRpw4c+5ruPQbeZc3QLy9dxYkfgelNIbRYyVOSWHU9/+hivisLJ
- xHMcQMcI5/o9j7fpXTh8eGGNDpJKkLZtDhZdpQEdvSvl/Dqa39stnDGsG0Ft
- oB6HnOecAHjHc1oJItsZbggDPPcVmvE2rWltpN7BJPLBM0RRQpKuSemCOtZl
- ikuzjzvXL6F1zd6dqdtb2SedCty25zCmzeM42tjjkisNeWF1ZXTwXMbxpGu4
- 7j94E/COOP7U10zxci28EF3p0UjwQCGGeI7XU9j/ANquWkd9rl3KSZL0MrJ2
- Ynb2we3b61xS2eXJxyK/Yu0S9gg1K2u2EkkrPh0U4Bzxj3xkGvRZrhw4isol
- RUViV+4S2OAPr1rKWfhjVYkW71S2gtbZCBGmFMmM9OOF496cSwK2pG8tGkll
- QbVRvurkcknPI7/nWQk9pofBFvTHelNcasjT6nZ75xKCMgjJAGCAeeP2rSW9
- 3kLGCWVmOcsOD9KxtjPfy6usFxB/h2QjevxBT3wR19K01tGE+KMxuCOo7H9q
- HIuoxlZD4ut9It9GaY25jvIQWimiTGG75Pcf96znhy+tJHE096jPECArPl3k
- PVj2x2Fa271BCn2aZPMd0J8vGeO5Py9axmo6Zd6Zdj+HwWwhcFyjjJiUYyc5
- 5HIP1pX3ZfDm44niff2Pdfubq58N6hZ2MMN0WhZZftDbQARyU9T6ZIry/RU+
- xyNFcy7ILjCXGF3sF6g/Q+lPr7UJpoRE8n8sc46An1P/AFxWduJAjkKOh7VK
- c9ni5c1zuJtYxpdlpckVs0NyhlCCRnyGQ9SynnuR88Gq95avbW0EH2KFIEcS
- KJXzvVQW2ljjsMZ+VItKKXWrRRSKfjYFeT1HPIHXjNPfE2k+XH9uiZkh24ET
- FiS2eDzkAVnJyi2VUnKHM50VtJEM2pXEm6N3/m2BDblyeDnoRyK0rs0OmNK8
- PkxDLRxhgw2fP2GKxOkXyW4dbuG2ubfkTEkjjGfvYOOR0rXrdreG2eO03BYm
- MiEkIR6gt09xRCblF32M5OcbZSt7jXJjGLKznjglbzIm2gq+AQf2opxo2nTy
- apY5nmZUD7vMfI5U4HHGBg4NFPBVFCckjZzjbcSD0Y1HVi+XbeSe5B/Kk+oN
- qgAGnpbMD1MjYI/amk+KsgW7i4itYTLM4SMHBY9KyesXmlXV35hvp5WYcKG2
- qnyBAqLVby6imjg1F3W7kQ+VFbv94c/E2BgAevWsyZo7dwTbeZIGwPh5yOvD
- DP5V5+fO5fjX+BW6Lv8Ag4P/AOHPdXIOd9xJvT6BeB9RVKK4SDUPMLRxSK25
- I3/obORg8kUuf+K6nM7RRzLGGztAxj8gM1Fd2ps1BmiKSP0AYZ+ZxU4xlW9G
- t+x/qWoi+1CW4ZURpME7O5wATVYMvRxketJ4maFkiZWTcgcBs5Oee/0NX4p9
- vDDI/OubIpKT5GE8yhcnI55AHpS2/j8yzuEyclCePxppIA6JJkEc5PTpzSm5
- mKwSMRyVYn8KzE6kmNHRpP8Aw50ZLfRjqkijzrpiEJ7Ip4/ME/QVqx/ipVcZ
- 8lD/AC/9R/zf2rzDQ/Gz28Flpd4iR6fDH5ZZASWPYt7Z5IFaS68aRXut2mla
- PJlN2+W4xw20Z2r7ccn8K+sx5YqKSPX8fPjUEkzYRIoUpxlWxil0irdTSbcG
- NSVz2J7/AKVAlzdandzwfy4UADA7dzf6iD27VcFxp9tvthcRIbdAXUtyM859
- 6sp32dsclO2KPtt3BNHA20xxyKFdvvZx0PqOafiKG/tWUjKOCpB6j2NZjXQZ
- fDGqXQyjlDKnquCCv14FLvD/AIut9UtPKnna21MLtYIcCf3Hv6j8KV5adCzz
- xUuPTHEKXkNvsimd4cf8Mnr8s0g8S3lhq+mSbwEMcZKyOMMrDoPx/Wm99r9p
- pMDFpY3SNF4RgWz6Y715hqepnUr0O6hELFgqDpk9KhkypKjl8ryIJNfZb0XS
- o7lXmllMSEbYnxwZew+Q616X4A0iHSb3ULYXMUsjxpIGVeUHIweccnPvxXn+
- jatbQbbXUcmyTmNkHxRv746g+lXrHx21p4ltb57RY4owYp0tzjzVPUnjtwcf
- nXKmqPOUoKKrs9B8QWn2hGS4mPl8H4BkAjnOO9In0drnymtLuSOK2X4lXIbj
- H4nAx6c0z1TxNpmswRrpdyk7SDdz8JTp94HoeelVobOSKIhJm3nqO1a5L6O2
- E01Y2F1Iu23t449pIJcFgfoKeWaeYqBYgCi7RkEDnpyKx9uih4ZbiAOpGGlm
- cBU7kgVfh1qxk2KLqVE+7lAwwo6ZBpW77LNJJJFzxRYQzaa7mSOHUIEMkZBy
- SAPiVSOcEV5zHqt4qSJ9pdklTYdxz8Ptnp0rd3N3ZQ2dw6yLLvU7mPxALjkn
- 6dq81Z1DkoCqE5UHqB2FcXlNppxPO8l/lpn28md9kYJCjBZ2P5VWjlRW+CMu
- /LNI4yM+w7VDfTfzVDZwV6+lcxLtt1IKCSQ9Ce1avzinI4utIu2ZYX0CxAea
- ZAqjoQSa9SZP4jpzw6osRQw7D8OAvoTg49+PSvLLRfK1C2aaXYFcEyEZAAre
- XmorLpJheZIw/KoVwXwQeT+lWx8Urs6vHVY22ZnRbi1gvjayws8ckiodv9RB
- 4AHTnA61tbVlEDLLM8QJAOzGY8f0n3rMEWPlbnmYuBhQoywHcbvT589auR+J
- ZoH+C3iZMYCycn8RU/lhFU2ZySR6b4ehiWWAxxhR5ec7cE8dTRS3wHq95q1z
- MZrNY4YouJVJwWyOOfaiumElKNokaTU1xcq3qtLp5lgiaRlZsdFQZZj6AdzT
- fVU/lxv6Ej8aV/rTgYLWfFk8N5NHa2X2GdgFeaVf5xHb5fnWaeZ7mVppZWkl
- c5Z2OST6k16drOg2etIn2jekicLImM49DnqKx2q+EodLKSLJcTo3BwhXn5jN
- QlyW2TlCTYgGCcMK4kiik+GRQc9Dmp3l0/LIJmjK9RJ1B+dV5giopc4RzhW7
- GpPIn6M+KS6LOoyxXmlWdusIS5tmYCVcAuCeAfcVTt2kI2yxsko+8jKR9R7V
- 3ItxaTiOYMkg5VvXH70xlvpddvIIfMggdRmVSNqv1w2T3z8gM0mTGssf7Gjb
- dPsjiXzIJUHOVzjvkc0p1CGWS3nWFGdhG2FUZPA5P608+y3Gn3ixzwtHIpDA
- P0I9fl8qz3i0tY7beNyrM+8Mp529QfzFcOPG/lUWM9GSYEHkEGrOn3UljfQ3
- UYzJC+4D/MO4/DNTR3ySaa1nLYQyShi63C5EvuDzgj6VTRgHG1ufQj9q9i2u
- jE2naNVfeNJP5bWCsshJ3mQdB0wMGs8uqXg16PUHDSzq4YoQeR3GKt21gsjx
- O24RSZxsOCvqcn0OKtzlkyFMk0h5duWZj6k0S8p+tsu82Se2zQa/4jWfRDbW
- 0ZMU6qJDIuNqnnGPXjmsLNdBASu1c+gxXUt15oIJxjtnrVM4d8vwvqaxyeSX
- KYmXLLJK2dwqsxLyMcc8dM4GaIo2YghGIGM4XNfVUShmVSzL/SP6RWg0PTY5
- La5lmaOQFGR4GGHHGcofX1p+L7CMHJoTJaSEEojMBg4wc13c20cKKDIpkC42
- 5HzOT7+lPJ9IhtVVorRvLkRv6ySg28/maWali1dIk+48YYOx+IH9/rUZRmp9
- mTg49kvhTXLXR9azqEX8mRfL3Af8I5+9jvWz1rxPoh0txZXn2m6mUpFHHlSr
- EEBjnGOa81ad3XLS5Xp90cflUBXy3DByCCCOelWTQ0MriqR+idA0vzNIgjuc
- O6LhmbnJAxzWT1i4svC2qzWkkZuE2hoxCQxyc5Df5ewrJwf+JWu2caJGLcuM
- 5eRC27I9Cce9K9W1ufXr37bcxQR3J4drdNm/0JHqOmayT0Wn5DfRb1LWZ9Su
- cBTBDnCxKevPf1qJpMnPA9qjtbq53f8AEZtgOcnt9amS4cNuZInB9Y1/tXBk
- dvZzSdu2U7mHzimWHBy3yr6paSVG2MEUgBiKYRSrvBNuhOc8ZH6HpUheO5lU
- CBUKjBVDu5z+9Cm+LVCcf7OYm+NQmGYcg9hVtBlgDyxHQVcttAu5pCkNtPnY
- HKlArY+pxjPpUt3ptzpsSG5heCN225KcE/PnNS+KTX9Gq+ikV29evtTC102W
- SMSMqqp7McE+9W7HS4ZSJFmBPbctPYtMKqC90uM8gDqKtjw07ZVR9s2Xge2a
- 30QbsZZiRgdBnNFONHjEemx7RgNyB7dqK9FKkITXyeZaSDuBuH0pHWkYBlIP
- QjFZyRDHIyHqpxWgc0p120FxaF5mkeBCC0KMQCPU464ptVLVA38PkKEBgQQD
- 356Uk1aZq7MVJbaTGCsUTxn1Tg0j8RWsUenwvG855yBI5OAfyxWpmkuTnFvG
- aT6uWn06aGWFvNbaEbdkDnpXLqqKyX4sztrq0m4/aIlnyuwlvTtx/bFXLI77
- pFtQYy+Fd5E3BR3wBz74qWw0FJI0d51jjZiqsQTlh1Geg7U30WO4sbK5luJo
- IrHfguluzPC6kYYvx36YJ9xzQoS7ILk9sd2um2UNr5K3GrXoJ3Yki3KD6qGX
- C/Q1mPEOivqUjxvZzbkXEWFBcAeuCcda1djJq+rWD3lzOEgdSIIIcRmQZxvZ
- zkqD1wO1KryCTSLcS6fd3InlI3ToqLEcclVQj7o5PAxz1yapONrkx1vR5Fd2
- 0lrKySqykNgZBGaplS5H3j645r0u8kbUTLPcQLKWx5hePCk89MDH71kboTJk
- RxwwEEg+Uo6jtu/7UmPLOtxM+OiOyaaR7XTLeV1MjYLOvKg8nH0Ga9WsPD2n
- jSFto0Mibif5oG5s9z715La6deOgvreQRNGSBLJKEywHIUnqcHpWrtf/ABBZ
- LTZeWOZcYDRtgH35HFU4rsrCUV2UPFPhqLTbhpLZtqsf+GecfKs0UMgCiEyA
- cEpzg1e1TW7rVrvMkoDHgAdAPmaBdJBbCK3iDt/UQOT+1TdxZzzavQt2NGG8
- oshb7y+tW7HVbywE32Jgkk8ex3I5HuD2NQu87sS0RI9MV3FDuIyg3enpT/LJ
- IFJokttV1SCe5k8wO9wmyR5RuJFUirvITO3J+EZ6+1MWijVFVwIyRlTnr71S
- Y+VKdwUopwxLZ59K3nKTprYXJ6ZL9jJtuFUYbOQRkVE9ihKtv5P3gMcVZiuV
- OCxCqOuB39atwWcN9cpbwyiOaQ4QN0Y/tUuUovYJehRJEpGZHGQMA19iQwuB
- klWHBIxVuLS5biZ4kJd0PxqFO5ee4q7qmiSWE8fkXMdxuQscIfhA755AFXSc
- o6GUW1YW0R+yvJ/mYLn2HP7ipAoHU5qe2sriSyijgMUkmPM27wMbu3z4/Olk
- iak888DwyxNAMyjbtCD1J/6zXBFc5NJmPQ0tbdrqfyY3jRwuTvfbxWg0yyj0
- +VjqFsz2xPxyQurEe5AO4Dvkc1nvDniGPRwyGKaQMfvxygMPoykGvR7fV9Mu
- o4kkuYbuWRQyoyIWAI6HsK6ceKC7ZiJVmcrCftCSnP8AhL3Pwvn/ANOTHQnp
- 79eCMUt8V3C3dnYlEJfe++BhypGAQfcHirOtaMEsJbrSlEE6ruaOPhJlHUFe
- hOOlJtSMmoeHdPu7hFS9uJCQ6ZXKKMA49SMfgKq206Y8eyWyVNoZrKQcZyoN
- NoPKlmRI7SfccBQzNtz+lKNPj1FI1VLkEYx8XP7VpvDtvqNzrVtFcXAMSNvf
- GPiA59PlSJbKt6PSII/KgSP/ACqBRUlFdREKS6nHsut4HDjP1p1VLUovMtSw
- HKHP070AJagvI0ltJVkzt2k8HBGOQanqC8OLKc/6DWPoEY6W2umORcvgjnk/
- 3pPqNtJIPjlJx0Nad24xik2pA4OK42dA20+GG48F20iARvAjDK8fFnBPuT1r
- Py2DapqZU+YLcSrvt8MnxYzgA9SR6+oxWk8FSb9InibB8q5bGfcA06isIIZT
- KFLSFiwZuSMiuqrRNSq0SRRRwwRxRLtjRQqr6ADAFU9S02C/Xe4bzkX+W4OS
- PoeD9avmuTW0Iecz2V7GjPPJNELeR4hHuOwttyp29OCfr9aQXunxPbESK0kp
- X4pAQNxz1P0zXqmq2yyaVeoigNIhc47sBnP5V5VqNxJFC7/dBGNyjJB+VRya
- ZRO0xa99bwaPLYl4VkEoKqsALP65fsB6VTltEuC7wzyGJRks8O0j54J/KtN4
- O0KTV5biSad1tlwsyj70meg9unWtBr+l2/nfZrUR27BEKAfCOmOv0rYxbViQ
- jzls8oOJTiJmKgYyRivqvPGgUYCjuBmrV6GN5KY8CINgADGT3P618CfAA64b
- 1xxj1odPQlJumVQJ7hgkcjEk8AGmKWcsFzCxL7wuVx146n86saaFs70z7Qy7
- CoOO56Grdje+bqrSsOCoCA9sHn8f2pJqT/X0JNNPRNHFeyLtiVmyON3ApJq2
- nTrqUEGNzygAdBl+/wAu1OF1O6ikliWSTy1dvhViMAnoDU7xLeW6tFIEETCQ
- OPvg+5PSkx1CW0EWk9kQ8OQyWUYjaS3vGUb1m5B7cYyOvSqFtpUkG6WSWPfa
- zmJowcknI/DrWq0+/i+zQJMRLcBiXaXOW+R+ePwqjDfQ3Ud0r2JgltFJ2E5J
- AJz27YrpmouLqmdcowq0c2mpONZiVZfKebb9p3oPTlgT6/kc+tT67qf2Tw+s
- ljcQMsrPHJHIgJyeMrg+n45zSDxDbix1dTF5p3wrIPNbcTn27ZHaq6RRypse
- MhW5AxnY3sfT1FJz+HXdE/lcbiUdNa+ieSSxLho0LvsGfhHUkelWbvWL7Uni
- e8kEoT4em3cOuDjr7Vs/CFnpltMLvdKt6FaFrcfEJM+gxnt64rTWvhrS0eaZ
- tOhVrgfzYc7kHORgdj7ilUFN8l2R2eX280EsCJcRrHGhKnyYVBJ9SSe/5ela
- 7w5bWWoI+mrDHBGfjVlkLS7h3zjBHtWquPD+lXSyh7KIGV1kZkG0lh0PH/Rp
- VN4OEF0LnS7kxENuEch4XnPBH70Sxv3sKaHGlaG2mkhdRuJIjn+UwAXJGKVe
- JAkV7ZWsahY4YPhUdhnH7VpbI3RgUXiRiUcExtkN7+1ZXxG+7xKI/SBP1NO0
- ow0isOyxZECNWFbjwdbFnubxgccRrn8T+1YC2mCRkBc4FeuaLZmx0m3hYYfb
- uf8A5jyaXEt2PN6L9FFFdBIK+MAykEZBGDX2igDNzxmCZ4z/AEn8qp33Nhcf
- /TP6U81aDhZ1HT4W/aktyN1rMPVGH5Vj6Ax7TygkEjr1xSzUpJJUIDbcHt3p
- kxR03A9BxSm7c5I61xnQx74EY/ZNQUnkTqf/ANa1ZrI+BnDDU1A6SRn8jWtN
- dUf1RCXYGuDXRrk0xhHJgqQ3Q8dOua8mubIzXJEn/CjJAT1Pqa9aJrz3VYhD
- qdynTErEfr+9RzLSHgr0X/BCbLfUOn/FQf8A6muPFyhbu1kbgFCMnjv/AL11
- 4NkENvfq27HmqchScfD3xUHieBJWgkja3lJY52oc9uvJrJSqBj1Iwd9Gv24k
- bWQnjHAA9a+yRqMrERtQKPqRz8qaXVi0w4RVjzy+No/Oq2VnllRcKjEbWXpn
- j8uK5YZFOdIndMoqkqqCDjAyRVmNwx6A/Lgip/IchgchxwwHao57bylVsHqB
- x2J9K9SMVVlLPvwZLEda5MgR8pwehI7j3qRI5jGCpWRD0PFVZWnhPNpI/uoP
- 9qGk9NGNJ9l62kaKRpVwdq55HpXemRx3PiCO9kjLN5obykBOSOfrXOjxXL+b
- PPFsjYbNjDtTvQbNbfWU2/dwzKT8ulTnF1S6N1xoy8ltNqL3WqsjlGmw0jnA
- 3HsPkKkt9qPsUbhwSScAD962PiuNItEit4UVFMmFVRgdDWRtF8xkgJxskG7v
- wO+K4c8HH2Rao3Vnpa21rBcQxpmWNRcI/Ru4Jx0x69vxp1aTI6eXlxIo5SRs
- sB657j3r7bjEEY+EfCPu5x07Z5r79liLq23G05AHGD6j0+nWuuEOKVDk9drX
- ArtaqaSrWI8QZ/8ANTEDIEcYP4VtxWN1kbvEtwfRE/8A61PJ+o0Oxt4UsF1T
- W0UofKgIkfI9Og+pxXq1Zzwdpn2HSBcOuJrrEhz1C/0j9/rWjrcapBJ2wooo
- pxQooooA4ljWWNkYfCwwazU0TRStE/UHHzrUUs1a0DoLlF+NBhvdaAPMkj2I
- 2TjBwaW3K/zCexFMr1GjvblMkKsjcfWl1yTkY6dK4/Z0ehr4IAEmqY7vH+jV
- rc1kvBP3tTPrJH+hrWGumH6ojLsDXJozXw+1MKcmsR4hj/8AbMuOM4P5CtnL
- NHCpaR1RR3Y1jtcniub7zYW3LgDOMc496ll6Hh2TeD+P4ivfzEP5GrPiZBJb
- 2xc/CshyT6YrO6bqF3ZXNzHbbcyhSSVz0z/epLyW+uXU3bkpnAyRx+FJOV4+
- K9mtO7KVw/nbnGREhAz7ZqiFjVxJGMZkIX6k/wB6cTxobcxp93HHvVL7IGsn
- QfNT6HAIquPx4xSSF4ld7ZmIKSOrx8gp3HpVuwmaWUDU7aGSBPvKhKmQY4wR
- kDnFcRRGWMkDD4wyg8g1KLeYH7owe9dFejHFNFtxZ4IgjCkgt5fHxHvg1Xt7
- mKSQyWwBUfCyg4ZPYr1FfXsrl1GAQwIZSO3/AF+9cX2kK0ivIpSXaNskbbWx
- 7GnTYUi87JcR5CEZ79aqvP8AZCnxgMrAg+lS2kL20BEshuD2LyFSPnjrVd18
- ssNwCuMFQvbOetLk5PSRkaRa8S3Qns7Bh/UWY/MACqtpo6ahcIgkaEkDc6AZ
- OOe9VL2VpZLRCxKjP0JNOtPnEEwZeOvNcWVJzplONxo1MKOiAPJvIGM7QKmF
- J11GTswPzArsalIDg7f/AMarzQnFjeu1NKRqUn+RSK7XVcY3Rcexo5oOLG4p
- HaaS+seO5ISD5CKkkx/0hRx9elXY9Vt2OG3p8xkflW10XSo9PSWcr/iLkq0p
- PbAwF+n6k1rqQLQ0UBQAAAB0Ar7RRTGBRRRQAUUUUAFfCARgjIr7RQB5p4r0
- 99O1SZyv+GuRujbHQ4AIrMzcgH2r2PVtMh1bT5LSbgMMqw6o3YivINRsrjTr
- p7W5XbJGcH0I7EexrmyRp2Wi7VFrwvf2thHfmdyGaRCFAyTwaZv4k3HENtj3
- kb9hWLimEc0o75BxTKGQMoOcZplNqJjjbHUmrXcuP5wQZ5CDFQSXUkn35HY+
- 7GqYJ4ye3UUF8Zx1+dJybNpEhlxnIAqhcu7PuOCAQanL5PIqCXLE8dqxmooJ
- GVujPvxxjaOlSSuzjkmu3iODgmoirUICNbgvI4Q5SNeT23H/AG/WrkW14Qw6
- kheD3B4/Hp+FVlwgZSNoAOe1RW8/lYEn3JEByfcf9f8AQrsjL2Y0N7eCMsrA
- hXJ4Yn73saZyzWtvbq8pQL/qGAf/ALvu/nWdkla3ySWKc4frj/m/v+lfftpw
- WSTCN0KnIJ9Kqpk3D6HDaraENsKY9mzVAanBPExVQxQ9TyM+g+g/OqkrB9hk
- hhfAwWZRx+XNQyS5JAAXHAG3H4Ctc7Rih9nFzqSxqvmBhG3SVQduPfuKrpeW
- 78/akYexz+lRyyPb3LKOVYB2UDpk4yPwqzA1rnzEeEe4wCP3pHNspxomh+PB
- KYU/d3dcVfjGzjH4VHEhY5weatxpx93pXFJ2zTuN8EZNWVbPaoo0JHYVIqsM
- c/P2rAOwd3ORQQf+1GzByG4NMtI0d9VvRbqWCdZHH9I/v6VvZljLwhopvLv7
- fcR/yIW/lg/1P/YfrXoFRW1vFaW8cEKhY412qKlroiqRNuwooorTAooooAKK
- KKACiiigApB4o8OprlkDGQl5ECYn9f8ASfY/lT+isaTVM1Oj89pbvFq09vcx
- NHLGdrIw5BpvHHkAFRx3r03xF4ai1YC5hCpeIuMnpIPQ/wB68+lt3gkaKWMp
- Ipwyngg1zSg4lVKyq+UOAnOK+/F0IqRsgnAJ6d6+kYyTgcVhpEyAd6iYZxgG
- rBOcgkdM1Eyg457+ta0ZZV+IgcYqEbiMZHftVpo88deTUDIoY5Hf0oApXMKu
- w3sxXoQGxmorpgPK2/f5wPYDNXbiMGM5Ax3pf/h7aQlsq/TJyTj2qsHao1Ek
- Vw3l/wApQwGMoDyvyPce3/aomigmcmN/Kl7hCUf6r3/CqeWQr95d2SvZhz7+
- vHBqwsomVvNjWdV4yByvzB5H0qqYNFgQXATH2vI7b4lbH4YqHyrzJBuoV7F/
- KJP5muQLUHAknjB5xuf9DXErwnrfSnv9/wD2reQUBgSJHDO0k0uF3OeW+Q9B
- 1qzZBpAvwIzDjeQM1VhCbt0cbAkYMr5yfx5NN9NtDs3kYycj2FTm/YN6LkEJ
- xzVoIg5JFfUh24461OIhkY+VRFIxhQQqliOldKJSc7V2nsetShQVyM8VZsLW
- TULpba3G+RvwUep9BRQH2ysp7+dLeCIF29+AO5PtXo+l6ZDpdmsEXLdXcjlz
- 61Ho+kQ6TbbE+KV+ZJMdT/amVWjGibdhRRRTmBRRRQAUUUUAFFFFABRRRQAU
- UUUAFKNa0GDVo92fLuVGFkA/I+opvRWNWB5Rf6dcWFw0NzGVcDIOeGHqD3qq
- 0Y5Bz3r1e+sLbUbdoLmMOp6HoVPqD2NYfVvCl1YbpbcNcQDuBl1HuO/zFSlC
- iikZsRKuOV6cn1oKAfeOK+tBKAojVhj/AEgYrowylQDG3TrnFTNISqkt944P
- p61C4QykAMSwHQVbMErMT5Z577sV8ktZDgso445NAFWSJSjfB9Mil1zC4IKA
- AkA53dac+VtPKp/+WKPswZQPg49/WmX9AZOZWlxHLsUBgTzyapF9kjRsQxHC
- knBx7N+xrXXOkw3OQ+3PsKqDw5GxGLiUgdB5ef2qin9jKSESzunWadAOgZM/
- n/vUivJKuRcMSfSP9zT1fDmDnzZQPZMfvViLQ7dCGkSZ/TeRj8M0PIFoU2Vi
- 85yd2OzNyTT6G3ZQMZwOlTKscIGyIEccbufwoW5VwDGkfX+pjU3K+xT6IOCu
- CRnIyehqRVOOenuelEbyO4VUjUngYUtn6VqdI8K3M5WbUSIo+vlKuHb5nt+v
- yrUr6MboUaXpFzqk+2IYQffkP3V/39q3ul6TbaVb+VbryfvuerH/AK7Vbhgj
- t4liiRUReiqOKkqsYpCN2FFFFMYFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAF
- FFFABRRRQAp1Lw/Z6iC2DDMf/UjwD9R0NZPUvDWp23Mam4jH9UR+L6j+2a9C
- opXFM1Ojx+SFhIQ24FeCDkH8K62oDx9a9UutPs70YubaOX3ZeR9etJLnwZp0
- uTBJNAfQHcPz5/Ok+NjcjC5RR8KDHyroSqCMKBk+laSfwPcAHybyJh/rUr+m
- aUv4fuLJSGaEhfiOGY9fmKWmjbKfmlRXJuGBOQMdjVl7KVkwDGPnmrUHh27u
- Nu1rfJxnLsBz9KygFTyP97IJx24qNVDbXb4Succ/jWvh8DyEAzXiL67EJ/U0
- ztfB2lwAeYJZ/Z2wPwFbwkw5IwCxvcSiKFHeXH3EXLVoNN8HXtx8d0RbITkg
- 8v8Ah2+tbm3tLe0TZbwRxL6IoFT06x/YrkLtO0Wx0wAwRDzMYMjcsfr2+lMa
- KKpVChRRRQAUUUUAFFFFABRRRQAUUUUAf//Z
-
+ data: !binary | /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAA0JCQoKCg4LCw4UDQsNFBcRDg4R
+ FxsVFRUVFRsbFRcXFxcVGxoeICEgHhonJyoqJyc1NTU1NTY2NjY2NjY2Njb/
+ 2wBDAQ4NDRERERcRERcXExQTFx0ZGhoZHSYdHR4dHSYsJCAgICAkLCgrJiYm
+ KygvLywsLy82NjY2NjY2NjY2NjY2Njb/wAARCACvAIMDAREAAhEBAxEB/8QA
+ GwAAAgMBAQEAAAAAAAAAAAAABAUAAwYCAQf/xABAEAACAQMCBAMFBwMBBQkA
+ AAABAgMABBESIQUTMUEiUWEUMnGBkQYjQlKxwdEzYqGSFSRD4fAlNUVyc4Ki
+ svH/xAAZAQADAQEBAAAAAAAAAAAAAAAAAQIDBAX/xAAqEQACAgEEAQQCAgID
+ AAAAAAAAAQIRAxIhMUFRBBMiYTJxgaFCUpGxwf/aAAwDAQACEQMRAD8A+nUA
+ SgCUASgDxmCjJ2A6k0ADe3QumqFhID7rA+E/OgBNLecbWcCVoRA2f6Wcj/Vv
+ USvyNHDtI5y7s3zqCgWR2S7gjiy0koZQM4Axg5+NC+hkHFr8WcjpIYriL3kf
+ xAMp8S+KnbQqHEHGXG06Z/uT+KpT8icRnBcwzjMTBvMdx8RVkltAEoAlAEoA
+ lAEoAlAEoArmmSBNbnb9aAEtxdG7bTKP927x9Q3ofOp1DoTwCSy4okMAK2TB
+ pY4z2/Og+ZyKm9yqD2bVO594NupO2M/h+VJgjmWPmIVyyZ7r1pDJw9Iku44V
+ Yu8Ebl+Zu+XK75/xVRJZXxzh8WmS7ErRM2kMgAIdiQo+dOaHB7nMCFECMzNj
+ 8T7k/SoGEBuURIjaXXcMD+tCYmh7bXetE52EkbHwJ8hWpAVTAlAEoAlAEoAl
+ AFc0ywxl26D/ADQAjurlpW5khwOw8hWTZSQMLiAn3x/18aQwTi03s8UV0oyI
+ ZAzEdkPhfb4GgaDgV2ZTqB3B86BFVwCQGV9Dr7rfh+DVEl/DAGt7qJLiKULm
+ 5cOknpnBwfhp2pwyL+Q8l97O72+ZUEpR1ZFG2fEBg7+tW3YkW/HY0iiMB086
+ SAsYM7623Pb0+FVZI2sL3mfdSf1B0P5v+dXGVioOqhEoAlAEoA8JxuelAGNu
+ ftDeX1/7Na2x5eWEJfbVp6t8+1RzwVQXDwtrkcziO7fhgU4VfiV61SQr8Hb8
+ E4eR4UMZ7FHYH9TRSCxfDYXEuoQzAJpAZJBqDasg7j+KiKstsEtzfWlxdZjV
+ wjBCAdtlDArnsQaTQfYf7RxEdbGT5Ff5o0sNvIvEgF8sxhdZnDKsW27dBsPS
+ s6qV9gXXd5ccnBtpIiWXS5G2QwP12q2OIQvEZ239jm33zpPf5U6Yie2Xsp0w
+ 2uG6EOQCMeeaVAR4+KgBpGjjUnBAOo/oP1p6aJs7g4bNIqzG8kVveXQo28uu
+ acY9hfRqbSbmxDJy42f4+fzrQkvoAlAEoAV8cuWjhECdZfeOcYUdd/Wom9hx
+ EFhHjiMTYO2r8QONjUwLlwP61Mzk0AAWGxYeg/eoh2VLoC4iUteIrPM3+7Xa
+ cps7BZI/d+qk0pFR4/QzkuFiVGOcMQNgT1rTolLkSSpJJc61KIBLpjYn3ve2
+ +O/asq1PYsae1WsdseYA7QeNde+SncD0rSqKhjbkl5Flp9qPapFXkMZG1agp
+ 8K4G2TjvSTO+fpNHYfDe25lRWOJZAdJII6dck4p/Zz5fTtptdFl2fCo9c/QU
+ pnEi2DaJB6CnHoT5CbaflSBvwnZvhTAcUASgCUAZ/i6e0SK222eozttUTKiL
+ 7WEJexMWX3vygHofKpiUx3Wpmck0AL7Q4kb/AMv71EOypcIB+0EkLRpE5wUc
+ SasZC4OdwO2KT8eCsf0WT36sXXK+AqkZ8R+9xq+80dh5VZajQseaWVI5IHCR
+ MwZkIHhckAOPyrkdKlPnoNqdLdkml9mI0za0VtAjCr/VOWLMRjrTGpNU2BSH
+ lxy3HD5dAClZYG6nR4QQcYI3+NJb7o9DDnWaPyj8r5DC9w0cVsYyyrCGUrjU
+ 8vhJ0b48PlT+hKUVJ773/Q4kLmGMPq5nLOvVjVnGN9O1KfR52SnOTXAWmyge
+ lX0YnuaAGfCrkzwMGxqiYrt+X8JpAHUwKrltMZ8ztQAg4mhJXwlgwK4H1qJF
+ RFqQyNd240MMON8Y6bmpiyn2aA1qZlRkUSCPPjIyB6ClfQAMBxK3wP61OPk1
+ q6Qn4pJcyXeI49UUiaZCPy1r7bt/cTphh0WvKPeCS+02oZtTSkYYquBkbAlt
+ qK+KtkyT0pbJFV/dSxW8kZBLNEq83w6HcnG4x1rOXZnJbbeQO9RrSKKMnW6t
+ 45Nj4hjYbZ6GufMpbU+DBthXBrG61ZeZo4bZtXs4B0bjOGzse1XhHGTXAebZ
+ b/dzh4yrow/Cd+nyq49minpYXMfHj+0D6miXKIXYXWhBKAOOBTSxXZidTpbK
+ E42yNxWa5KfBpa0JBLxt1X50AKOKe5Gdup66vL+2omVHkVxY9rg2QeMdpM1E
+ eS3wNrq/htQzSnSFGf8A8rY0xencv5FnD76PiN61xETohBU5HXsNPp5+tZ6L
+ nqsnNg9trfkF4rfrbRvp8cm66B6nbP8AFbQhTbuzpw4qpsVT3XD3n5d1cttE
+ MFchAxzkeHoR5GtHOG5eTPDeIJFx8w2/Kt5Gj8eU22GWydq5jgk+PsvueJST
+ BeeBIi7YAwSD3x50tW+5pKq2e5JZ2uFS3c/dBmKsBuc+dZzfSMn8ugi3llsp
+ fBI0nNULIr+bdCPlWduMkl2QuRvDOLTVzPTVjfSPXFdscdJts6vYcld0WwTx
+ XcjGM5Axg/ChQU02gWH4Nh5IAyeg6mkcp6hJAJGn41NgBWsgSUuCMq+fecdD
+ UdmlbGuVtQBHQ7itTMAuDmVvTagBfxCUxquBnrUTKiK2upDLEFAXUwGc+tRH
+ ktAvFkubZpI5NUyTKyjIz4z00/Stj1MUoNKtmgHhd+bSRrd9UK8soW/EvcN8
+ DWCk1N2rPOnqlle1/IW8QnnwfEdW5yPI9cfKqlq1N3syvUxyY5PfkVQ8oSa5
+ TshB0fm9KaOWP2X35iuZOZbJ4QMyaBgAeZA6U0VL8VRdaSxkYmy6Lkjpnby1
+ VlKO4vDHFhcw+EIcsQS6YBchd8IfM5p49q8mi/tlF8US45aKwyOcS/hZQ2+N
+ O9LJ8XZD5aBH4s7HlP8A0s+IrsxX1qpSlJVexXvSez4HlrcQq5e3O0gAx0C4
+ 863nmhFLQehKcdCSDBfXEsZCqAc+HUwHw8Jx0/WuaTnJPT/ZwSxveQRBE4TU
+ +qSRRnfGB323OanHjfMm2zI8trqXHbT8d60NDT8Pn5tnE7bMRv8ALatUzMGk
+ OXY+ppgA8UH+7Z6EHYk4/WoycDRnVvUjViyDndE1HYefn9a5sOR63GS/Rv6f
+ 8qmWcFuGmnHtPVieSzncjyz3O1dn7Ov1cYxinF7+DvjEltcXSK7qlvb+BmA8
+ z4lb08qIuLel8h6OaipLnI/J3NwuEqDbeDBXWPe1Rjqu/wCYV0PGmazcskdL
+ Kr37OcOuHWSNeTkqSE93HcaexIp+xBmD9NB/TOG+zdnCJmtQUeVWRIycp4hp
+ 3zk+tKWBdCl6ZU62A2+y0VtbLqLTyKCZCh07/wBoINYyxUYS9PVFdnAIIo8R
+ bFG9paUeMdT91q2Bx9ajSTpSsFDR3Gow6pvDp5jDEjd/EMnp0rly25KkZP5P
+ gEdIYyBpyw643GfM57071JdCrpIbcrRJaCI632MrdUC+WO9dkcKSS/5PRji2
+ j/Y2iWGS4eGNkDLh1wPCenu5zWkMSi3XBosaiuORlDcxgmJ2CuOgI05/mpyQ
+ o5M2Ct0B2yQ6D4Vzuerj9q5aMR9w9glpGoG2P39atGbPasQNf7wYzjJ8gf1q
+ ZcDjyZjjNr9ykyyeOLvjr8umP1qFR2YpxpxmrAU4YdpGkC3Kf6D5fCn7eqPN
+ DXptcdeoJ4ct887TcyCNR4csQdQ7rpP61p6aNdhg7Td/9jb2hh+TI2+7YEf6
+ etdp2RRwbiViREpbT72O1cmT1Si6FKUV3R6t+Op2PTftW+PIpqy1G9wKbjCS
+ utvCGdnb7x12wnfSehNKcujmyvpFL3DBy64a3CFnGC+wbJKE/pWL5OeSr9Ir
+ jis7hG9mHLlc6o2IwXGdye/eolBTT68EabVoL4TwlJpi08XQEddjj8461msT
+ tXVIUYb2MWtYLxHMOiO4hfSM+4WXorY3x8K3U6Ov5w47QktmntzpuAIry2zE
+ yY2zksuCeqsp6+Vaavjd7hHNcN+UH3V3rg1toFzEA2gHOdW3gz19Ky9x72Rj
+ z03e6KeHz3CSK12GUxnVGrZJPVTo8t6y35ZjPz5NDbXDNCpVCR500c4bViBe
+ IIWtyR1Xfy9KiXA0Zu+R1hbEYJ8iwz8t6xst/QFPy2iRYoWhf/ian15P6CsM
+ mR1pWyRPuyrTewTwuZVK2zJHpc+++c+gFb+jzfJQdb/5dm3pslOhoPYJWZYQ
+ khT3yuCF+JG1esqZ6MZ32VgTQKTHjSxzgiuXJ6GMnY5YoyOBaId5xqDdc9M+
+ tb48KxqiuFR5d20FtDI+eTgasjAJ076ct50pr7OfL58Gdu5IeIrG0UmZzsFi
+ BAC4911Pr5Vzs43Ut+w7hHtNvbkOhTUwKnGl8HqwDb9qUXzsPEjQcLmymVyx
+ zplkI0sxHmKLNdKdlHE/ub1buFFMGwkRAch+xYLQaY8lQcH+XkRcavFuLyK6
+ 3PKTl4PQjuNt+hxuaiUjz55PlsW8znWjzxwgLFiMBcHAG++odKV2my7uOoL4
+ bMjW2F0q+nGnfVqPUnPb0HrQpXF+Rt6kWiK7xtJPj00gfLBqorYnY1FwwiLk
+ gkKeijJ+gpvbczEfFOLAjkhBo/FzMgj4qP3rly5b2/8ABGflu87xx/dxnOvG
+ AfXVuTUJP9DPfazO5d/eY5b4msZ3bsRxc40nPQg/pRDlMa2GHCOI8Lg4ZDFH
+ IMJpWRejyTP1AXvv3r24TVI78WSOlbjXUs4wv9IEgsdskdhmtdR0KQDHfxQX
+ Pss7aucmuINtqUErpGe9S59ClkV1wxZxy/itrf2csJTINLQtvjyOe1ZzmjHP
+ ljTXkF4HPwzh8trcysA4fSTk5GrwuSOmBnY+VYnJcUl5Hl6sE105X75cAmWN
+ vmACDRaOiLDLS7UHMhA7KuQNvTOKRbXAp+0ExS5SSJuXzFOtVbr2ye24rnzN
+ pqjlzPfkzzylpWxvj/raj8kmznHfApFS1uhJup/4R6Nt3+taRquTbF+Dst4d
+ JHaJonZR+UqPwnz071OuK7FexsrBoZ7WOWLdG6eEjvitVJEF12CJWx33FUBk
+ +MWXFOYzuRNbA6gVwgGe5Xz9azd8ktMUuPDg4YfHIqHJeBaGTaVyxOmYBUEW
+ n38bD5j/ADUThrX2NffJzejl2zPICOX4WHffp1rmjF60ijOgCNo3R8nZsjqp
+ 8jnuPSvQuhJ1wHzz390BJLNoVGDKo93I74pvP/LNfdm+yrifEpL2USEjIAVf
+ T4UtTk7kRPI5MHt7d7iQIvXclv3p0wSbouktOSjcwb7fLPSs2pahSjRbacbn
+ srRrRUR0bOJCMMurr0OD6VoVGdD3iH2i4W9nCLLLXsWATIh93GD6Ghmksol5
+ rSku8hLMe46fSuWTMXueezg+IOMkjbfejVs1RNfYdHbyhdTKQvn28utRpl/A
+ 1fAwtOGyP4mU47Yx9Sa0hj8l15PodlFybWGP8qAV1USU3y7q3ypgAXKh4XU9
+ MGpl2Bl723iMMoCqpKnGFK+tYmj4YDaW91I0fO2B3EmMkY33P7mhJmSvkb3t
+ vBPaj7tvZiMs2Nyo6Y1uSd+m9VJdlGOveGCE5WTVEc6fCc/x/mlHI/8AUWkY
+ 8AurKAv7eNOpcQM4OnQOoHXeqpcmkWhdeexy3MrW33dv6/sKndGUudgeJnjc
+ GFipHQjbHwqtbBNnsvN0qskjOoJxk567nNGpvrcLb5OhbpJgINR2AGd/lUW1
+ yBwLaXSZhGdKEAt8a05Q6DH+5VNSNkjbbqeprmS1N7gN+C21tcoJllTnr1jf
+ dRnoD339K2hjXkSGl/NKIbi2dTq0rJ5hl1AZDdznber+ike8OTmvFGFbxsB7
+ n75qVyU+Dd4rYgquk1xHzG4+VACxsaTnpijoDN3kC8s+JjnqMmudmoQvD/b7
+ O1YYVdAWXG2dOV7Vt0QnyNXijePlsoKYxj4dKdEmV43ZJaYDkurKTk7+n6Vn
+ LYtdiyLhVxeWYuWAECjRGPxNjPTemkSlqYm0EOQDgjbI6fKhiregm3tlOkyS
+ feO+APLHeplfXRMgmaCK5i0qzFwckbduuKmGz3Bc7hElhZ5ieNgjkZAyR7oD
+ Z7jI771tJKnwzZxj0VtfzItxa4xLlgrZ93OOnbfvU6tG3NE66tFllwDiE9rG
+ I7kLG+S8erKgfhbbz8utSop7rkzD7fgt3Zyq8kSXMfuycv39PoD+1Dj53Aa8
+ RSKGxRYwQGdQNWScbtjxb9qp0o8FxGH2bi5s+v8ADCM/+47ClBFSNRWpBKAF
+ NzHy2dO2+PhR0Blblm5ZBbY1zmo24If+zYvTUP8A5GtlwjNhpqhCT7RQiSOI
+ noGIP6/tWeToqIN/4CiqMkb48vGaTfxFwzOPb4dv7jt/ArGMrlSJ7PBGyb9h
+ 512JFHrtoGfd9cUUn0Kgq0iadrf8hfUMjv2NTJdLgfRxdWfst1ytWuVgGd/N
+ m3OK5skWuyDVwW/IxygNJ6r0+JFbxjVFBQqwAuNDMEX/AKoP0BqZ8DjyaPgN
+ mbWxXUPvZfG/z6D6URWwMZVQiUADXsOtNQ95f070AYi5wMr+Un9aw7NehpwT
+ /u+P4v8A/Y1rHhGbDqoQs41g22M76gcd6znwVHkWx3kX+zfZ9LM+46bDxZ61
+ Mn8K8g+RfPExboCcZz6+Qq4YUqFR5GpVgwzpO5U7q3xBrUVFrmX3kiAC9gMf
+ SqCjt2bSHxpc/wCKUr4oEcjl3d4zS+5lQfkB0rnmvluVWw9W5hHc1epE0WLc
+ w/mp6kFBENkvEpoRkGGF+ZL8gcL8zRyBpaYEoAlAEoAx/wBobBrWcyqMwTEk
+ H8rdSv8AFZTXZafQutOJSxwLBHhQhbfGTuSe9NS2Bota6lb3nJ+dTYUgaZiw
+ 60mMF3UEb77mhAeI+kjX3z8q3TAJiuo4gRpGr1rTUQ4lct5IeunHlijULSBc
+ ySRyMjbqDsR/NS5F0Fwx/XzrnfIwxM0AXRxSSuI0XLscAetArNhw2xWxtxEN
+ 3O8jebVqiAumBKAJQBKAKbm2iuoWhmGqNxgikBirngsvDZWV/HGxJjkx7w8j
+ 61k40aJlWnekByy0wB3U0ADTLgnoNXVjVxYFPNDjDdvP9iN6sD1iMfi2/vH7
+ 0WBLePXINI2+v+TUyYDaOLA6VmIvSMswUdT7oHegDUcJ4WLRebLvcMP9I8hW
+ iRDGdUBKAJQBKAJQBKAK54IriMxyrqQ9qQGZ4jwaW1JeMGWDz7r8R+9Q4lJi
+ rH9tSM5ZCR0/xQBXLbcwYxin+gF83D7nV4Vz/dmrUh2epw+7OxGB6n/nRqDY
+ YW9qkA3bxd6ixB9rayXL6IFLHv5D4mnQjS8O4TFZ+NvHP+fy9Fq0ibGFMCUA
+ SgCUASgCUASgCUASgAC74PbXOTvE5/En7jpSoLE8/wBn7uPJjKyj46T9D/NT
+ pKsXyQzxNpdNP0/akB5484WkMKh4RfXG4UafNmH7b0UxWMrb7Nxg6rl9Z/Im
+ w+vWq0iscQwxQroiUIvkKsRZQBKAJQBKAJQB/9k=
diff --git a/vendor/rails/activerecord/test/fixtures/book.rb b/vendor/rails/activerecord/test/fixtures/book.rb
new file mode 100644
index 00000000..cfd07abd
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/book.rb
@@ -0,0 +1,4 @@
+class Book < ActiveRecord::Base
+ has_many :citations, :foreign_key => 'book1_id'
+ has_many :references, :through => :citations, :source => :reference_of, :uniq => true
+end
diff --git a/vendor/rails/activerecord/test/fixtures/books.yml b/vendor/rails/activerecord/test/fixtures/books.yml
new file mode 100644
index 00000000..473663ff
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/books.yml
@@ -0,0 +1,7 @@
+awdr:
+ id: 1
+ name: "Agile Web Development with Rails"
+
+rfr:
+ id: 2
+ name: "Ruby for Rails"
diff --git a/vendor/rails/activerecord/test/fixtures/category.rb b/vendor/rails/activerecord/test/fixtures/category.rb
index 295bd827..71685234 100644
--- a/vendor/rails/activerecord/test/fixtures/category.rb
+++ b/vendor/rails/activerecord/test/fixtures/category.rb
@@ -3,6 +3,12 @@ class Category < ActiveRecord::Base
has_and_belongs_to_many :special_posts, :class_name => "Post"
has_and_belongs_to_many :other_posts, :class_name => "Post"
+ has_and_belongs_to_many(:select_testing_posts,
+ :class_name => 'Post',
+ :foreign_key => 'category_id',
+ :association_foreign_key => 'post_id',
+ :select => 'posts.*, 1 as correctness_marker')
+
def self.what_are_you
'a category...'
end
diff --git a/vendor/rails/activerecord/test/fixtures/citation.rb b/vendor/rails/activerecord/test/fixtures/citation.rb
new file mode 100644
index 00000000..545aa811
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/citation.rb
@@ -0,0 +1,6 @@
+class Citation < ActiveRecord::Base
+ belongs_to :reference_of, :class_name => "Book", :foreign_key => :book2_id
+
+ belongs_to :book1, :class_name => "Book", :foreign_key => :book1_id
+ belongs_to :book2, :class_name => "Book", :foreign_key => :book2_id
+end
diff --git a/vendor/rails/activerecord/test/fixtures/company.rb b/vendor/rails/activerecord/test/fixtures/company.rb
index c012a978..1048d5a6 100755
--- a/vendor/rails/activerecord/test/fixtures/company.rb
+++ b/vendor/rails/activerecord/test/fixtures/company.rb
@@ -1,4 +1,8 @@
-class Company < ActiveRecord::Base
+class AbstractCompany < ActiveRecord::Base
+ self.abstract_class = true
+end
+
+class Company < AbstractCompany
attr_protected :rating
set_sequence_name :companies_nonstd_seq
@@ -34,6 +38,7 @@ class Firm < Company
has_many :no_clients_using_counter_sql, :class_name => "Client",
:finder_sql => 'SELECT * FROM companies WHERE client_of = 1000',
:counter_sql => 'SELECT COUNT(*) FROM companies WHERE client_of = 1000'
+ has_many :clients_using_finder_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE 1=1'
has_many :plain_clients, :class_name => 'Client'
has_one :account, :foreign_key => "firm_id", :dependent => :destroy
@@ -46,6 +51,8 @@ end
class ExclusivelyDependentFirm < Company
has_one :account, :foreign_key => "firm_id", :dependent => :delete
+ has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'"
+ has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.']
end
class Client < Company
diff --git a/vendor/rails/activerecord/test/fixtures/computer.rb b/vendor/rails/activerecord/test/fixtures/computer.rb
index cc8deb1b..f26312f9 100644
--- a/vendor/rails/activerecord/test/fixtures/computer.rb
+++ b/vendor/rails/activerecord/test/fixtures/computer.rb
@@ -1,3 +1,4 @@
class Computer < ActiveRecord::Base
belongs_to :developer, :foreign_key=>'developer'
end
+
diff --git a/vendor/rails/activerecord/test/fixtures/contact.rb b/vendor/rails/activerecord/test/fixtures/contact.rb
new file mode 100644
index 00000000..c574196d
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/contact.rb
@@ -0,0 +1,16 @@
+class Contact < ActiveRecord::Base
+ # mock out self.columns so no pesky db is needed for these tests
+ def self.column(name, sql_type = nil, options = {})
+ @columns ||= []
+ @columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, options[:default], sql_type.to_s, options[:null])
+ end
+
+ column :name, :string
+ column :age, :integer
+ column :avatar, :binary
+ column :created_at, :datetime
+ column :awesome, :boolean
+ column :preferences, :string
+
+ serialize :preferences
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/customer.rb b/vendor/rails/activerecord/test/fixtures/customer.rb
index ccbe0359..3d8d644f 100644
--- a/vendor/rails/activerecord/test/fixtures/customer.rb
+++ b/vendor/rails/activerecord/test/fixtures/customer.rb
@@ -1,6 +1,6 @@
class Customer < ActiveRecord::Base
composed_of :address, :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ], :allow_nil => true
- composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
+ composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
composed_of :gps_location, :allow_nil => true
end
@@ -52,4 +52,4 @@ class GpsLocation
def ==(other)
self.latitude == other.latitude && self.longitude == other.longitude
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/db2.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/db2.drop.sql
index 9837def3..286066ea 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/db2.drop.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/db2.drop.sql
@@ -30,3 +30,4 @@ DROP TABLE keyboards;
DROP TABLE legacy_things;
DROP TABLE numeric_data;
DROP TABLE mixed_case_monkeys;
+DROP TABLE minimalistics;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/db2.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/db2.sql
index 750e74a0..6e088bd8 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/db2.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/db2.sql
@@ -229,3 +229,7 @@ CREATE TABLE mixed_case_monkeys (
monkeyID INT NOT NULL PRIMARY KEY,
fleaCount INT
);
+
+CREATE TABLE minimalistics (
+ id INT NOT NULL PRIMARY KEY
+);
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/firebird.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/firebird.drop.sql
index 69aa0931..378843bf 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/firebird.drop.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/firebird.drop.sql
@@ -31,6 +31,7 @@ DROP TABLE defaults;
DROP TABLE legacy_things;
DROP TABLE numeric_data;
DROP TABLE mixed_case_monkeys;
+DROP TABLE minimalistics;
DROP DOMAIN D_BOOLEAN;
@@ -60,4 +61,5 @@ DROP GENERATOR keyboards_seq;
DROP GENERATOR defaults_seq;
DROP GENERATOR legacy_things_seq;
DROP GENERATOR numeric_data_seq;
-DROP GENERATOR mixed_case_monkeys_seq;
\ No newline at end of file
+DROP GENERATOR mixed_case_monkeys_seq;
+DROP GENERATOR minimalistics_seq;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/firebird.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/firebird.sql
index 779701c3..9ed6453f 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/firebird.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/firebird.sql
@@ -302,3 +302,9 @@ CREATE TABLE mixed_case_monkeys (
);
CREATE GENERATOR mixed_case_monkeys_seq;
SET GENERATOR mixed_case_monkeys_seq TO 10000;
+
+CREATE TABLE minimalistics (
+ id BIGINT NOT NULL
+);
+CREATE GENERATOR minimalistics_seq;
+SET GENERATOR minimalistics_seq TO 10000;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql
index 1c87fdf7..58ec56bd 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.drop.sql
@@ -30,3 +30,4 @@ DROP TABLE keyboards CASCADE;
DROP TABLE legacy_things CASCADE;
DROP TABLE numeric_data CASCADE;
DROP TABLE mixed_case_monkeys CASCADE;
+DROP TABLE minimalistics CASCADE;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql
index 798b0204..83441798 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/frontbase.sql
@@ -266,3 +266,8 @@ CREATE TABLE mixed_case_monkeys (
"fleaCount" integer
);
SET UNIQUE FOR mixed_case_monkeys("monkeyID");
+
+CREATE TABLE minimalistics (
+ "id" integer NOT NULL
+);
+SET UNIQUE FOR minimalistics("id");
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/mysql.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/mysql.drop.sql
deleted file mode 100644
index f269b3a3..00000000
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/mysql.drop.sql
+++ /dev/null
@@ -1,32 +0,0 @@
-DROP TABLE accounts;
-DROP TABLE funny_jokes;
-DROP TABLE companies;
-DROP TABLE topics;
-DROP TABLE developers;
-DROP TABLE projects;
-DROP TABLE developers_projects;
-DROP TABLE customers;
-DROP TABLE orders;
-DROP TABLE movies;
-DROP TABLE subscribers;
-DROP TABLE booleantests;
-DROP TABLE auto_id_tests;
-DROP TABLE entrants;
-DROP TABLE colnametests;
-DROP TABLE mixins;
-DROP TABLE people;
-DROP TABLE readers;
-DROP TABLE binaries;
-DROP TABLE computers;
-DROP TABLE tasks;
-DROP TABLE posts;
-DROP TABLE comments;
-DROP TABLE authors;
-DROP TABLE categories;
-DROP TABLE categories_posts;
-DROP TABLE fk_test_has_fk;
-DROP TABLE fk_test_has_pk;
-DROP TABLE keyboards;
-DROP TABLE legacy_things;
-DROP TABLE numeric_data;
-DROP TABLE mixed_case_monkeys;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/mysql.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/mysql.sql
deleted file mode 100755
index 1781ee00..00000000
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/mysql.sql
+++ /dev/null
@@ -1,234 +0,0 @@
-CREATE TABLE `accounts` (
- `id` int(11) NOT NULL auto_increment,
- `firm_id` int(11) default NULL,
- `credit_limit` int(5) default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `funny_jokes` (
- `id` int(11) NOT NULL auto_increment,
- `name` varchar(50) default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `companies` (
- `id` int(11) NOT NULL auto_increment,
- `type` varchar(50) default NULL,
- `ruby_type` varchar(50) default NULL,
- `firm_id` int(11) default NULL,
- `name` varchar(50) default NULL,
- `client_of` int(11) default NULL,
- `rating` int(11) default NULL default 1,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-
-CREATE TABLE `topics` (
- `id` int(11) NOT NULL auto_increment,
- `title` varchar(255) default NULL,
- `author_name` varchar(255) default NULL,
- `author_email_address` varchar(255) default NULL,
- `written_on` datetime default NULL,
- `bonus_time` time default NULL,
- `last_read` date default NULL,
- `content` text,
- `approved` tinyint(1) default 1,
- `replies_count` int(11) default 0,
- `parent_id` int(11) default NULL,
- `type` varchar(50) default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `developers` (
- `id` int(11) NOT NULL auto_increment,
- `name` varchar(100) default NULL,
- `salary` int(11) default 70000,
- `created_at` datetime default NULL,
- `updated_at` datetime default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `projects` (
- `id` int(11) NOT NULL auto_increment,
- `name` varchar(100) default NULL,
- `type` VARCHAR(255) default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `developers_projects` (
- `developer_id` int(11) NOT NULL,
- `project_id` int(11) NOT NULL,
- `joined_on` date default NULL,
- `access_level` smallint default 1
-) TYPE=InnoDB;
-
-CREATE TABLE `orders` (
- `id` int(11) NOT NULL auto_increment,
- `name` varchar(100) default NULL,
- `billing_customer_id` int(11) default NULL,
- `shipping_customer_id` int(11) default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `customers` (
- `id` int(11) NOT NULL auto_increment,
- `name` varchar(100) default NULL,
- `balance` int(6) default 0,
- `address_street` varchar(100) default NULL,
- `address_city` varchar(100) default NULL,
- `address_country` varchar(100) default NULL,
- `gps_location` varchar(100) default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `movies` (
- `movieid` int(11) NOT NULL auto_increment,
- `name` varchar(100) default NULL,
- PRIMARY KEY (`movieid`)
-) TYPE=InnoDB;
-
-CREATE TABLE `subscribers` (
- `nick` varchar(100) NOT NULL,
- `name` varchar(100) default NULL,
- PRIMARY KEY (`nick`)
-) TYPE=InnoDB;
-
-CREATE TABLE `booleantests` (
- `id` int(11) NOT NULL auto_increment,
- `value` integer default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `auto_id_tests` (
- `auto_id` int(11) NOT NULL auto_increment,
- `value` integer default NULL,
- PRIMARY KEY (`auto_id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `entrants` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY,
- `name` VARCHAR(255) NOT NULL,
- `course_id` INTEGER NOT NULL
-);
-
-CREATE TABLE `colnametests` (
- `id` int(11) NOT NULL auto_increment,
- `references` int(11) NOT NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `mixins` (
- `id` int(11) NOT NULL auto_increment,
- `parent_id` int(11) default NULL,
- `pos` int(11) default NULL,
- `created_at` datetime default NULL,
- `updated_at` datetime default NULL,
- `lft` int(11) default NULL,
- `rgt` int(11) default NULL,
- `root_id` int(11) default NULL,
- `type` varchar(40) default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `people` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY,
- `first_name` VARCHAR(40) NOT NULL,
- `lock_version` INTEGER NOT NULL DEFAULT 0
-) TYPE=InnoDB;
-
-CREATE TABLE `readers` (
- `id` int(11) NOT NULL auto_increment PRIMARY KEY,
- `post_id` INTEGER NOT NULL,
- `person_id` INTEGER NOT NULL
-) TYPE=InnoDB;
-
-CREATE TABLE `binaries` (
- `id` int(11) NOT NULL auto_increment,
- `data` mediumblob,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `computers` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY,
- `developer` INTEGER NOT NULL,
- `extendedWarranty` INTEGER NOT NULL
-) TYPE=InnoDB;
-
-CREATE TABLE `posts` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY,
- `author_id` INTEGER,
- `title` VARCHAR(255) NOT NULL,
- `body` TEXT NOT NULL,
- `type` VARCHAR(255) default NULL
-) TYPE=InnoDB;
-
-CREATE TABLE `comments` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY,
- `post_id` INTEGER NOT NULL,
- `body` TEXT NOT NULL,
- `type` VARCHAR(255) default NULL
-) TYPE=InnoDB;
-
-CREATE TABLE `authors` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY,
- `name` VARCHAR(255) NOT NULL
-) TYPE=InnoDB;
-
-CREATE TABLE `tasks` (
- `id` int(11) NOT NULL auto_increment,
- `starting` datetime NOT NULL default '0000-00-00 00:00:00',
- `ending` datetime NOT NULL default '0000-00-00 00:00:00',
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `categories` (
- `id` int(11) NOT NULL auto_increment,
- `name` VARCHAR(255) NOT NULL,
- `type` VARCHAR(255) default NULL,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `categories_posts` (
- `category_id` int(11) NOT NULL,
- `post_id` int(11) NOT NULL
-) TYPE=InnoDB;
-
-CREATE TABLE `fk_test_has_pk` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY
-) TYPE=InnoDB;
-
-CREATE TABLE `fk_test_has_fk` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY,
- `fk_id` INTEGER NOT NULL,
-
- FOREIGN KEY (`fk_id`) REFERENCES `fk_test_has_pk`(`id`)
-) TYPE=InnoDB;
-
-
-CREATE TABLE `keyboards` (
- `key_number` int(11) NOT NULL auto_increment primary key,
- `name` varchar(50) default NULL
-);
-
--- Altered lock_version column name.
-CREATE TABLE `legacy_things` (
- `id` int(11) NOT NULL auto_increment,
- `tps_report_number` int(11) default NULL,
- `version` int(11) NOT NULL default 0,
- PRIMARY KEY (`id`)
-) TYPE=InnoDB;
-
-CREATE TABLE `numeric_data` (
- `id` INTEGER NOT NULL auto_increment PRIMARY KEY,
- `bank_balance` decimal(10,2),
- `big_bank_balance` decimal(15,2),
- `world_population` decimal(10),
- `my_house_population` decimal(2),
- `decimal_number_with_default` decimal(3,2) DEFAULT 2.78
-) TYPE=InnoDB;
-
-CREATE TABLE mixed_case_monkeys (
- `monkeyID` int(11) NOT NULL auto_increment,
- `fleaCount` int(11),
- PRIMARY KEY (`monkeyID`)
-) TYPE=InnoDB;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/mysql2.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/mysql2.drop.sql
deleted file mode 100644
index df00ffd7..00000000
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/mysql2.drop.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-DROP TABLE courses;
-
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/mysql2.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/mysql2.sql
deleted file mode 100644
index 0bfd2e6f..00000000
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/mysql2.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-CREATE TABLE `courses` (
- `id` INTEGER NOT NULL PRIMARY KEY,
- `name` VARCHAR(255) NOT NULL
-) TYPE=InnoDB;
-
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/openbase.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/openbase.sql
index 4f5d27e6..ec0029b0 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/openbase.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/openbase.sql
@@ -1,5 +1,5 @@
CREATE TABLE accounts (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
firm_id integer,
credit_limit integer
)
@@ -8,7 +8,7 @@ CREATE PRIMARY KEY accounts (id)
go
CREATE TABLE funny_jokes (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
name char(50) DEFAULT NULL
)
go
@@ -16,7 +16,7 @@ CREATE PRIMARY KEY funny_jokes (id)
go
CREATE TABLE companies (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
type char(50),
ruby_type char(50),
firm_id integer,
@@ -37,7 +37,7 @@ CREATE TABLE developers_projects (
go
CREATE TABLE developers (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
name char(100),
salary integer DEFAULT 70000,
created_at datetime,
@@ -48,7 +48,7 @@ CREATE PRIMARY KEY developers (id)
go
CREATE TABLE projects (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
name char(100),
type char(255)
)
@@ -57,7 +57,7 @@ CREATE PRIMARY KEY projects (id)
go
CREATE TABLE topics (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
title char(255),
author_name char(255),
author_email_address char(255),
@@ -75,7 +75,7 @@ CREATE PRIMARY KEY topics (id)
go
CREATE TABLE customers (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
name char,
balance integer default 0,
address_street char,
@@ -88,7 +88,7 @@ CREATE PRIMARY KEY customers (id)
go
CREATE TABLE orders (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
name char,
billing_customer_id integer,
shipping_customer_id integer
@@ -98,7 +98,7 @@ CREATE PRIMARY KEY orders (id)
go
CREATE TABLE movies (
- movieid integer UNIQUE INDEX DEFAULT _rowid,
+ movieid integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
name text
)
go
@@ -114,7 +114,7 @@ CREATE PRIMARY KEY subscribers (nick)
go
CREATE TABLE booleantests (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
value boolean
)
go
@@ -131,12 +131,17 @@ CREATE TABLE defaults (
fixed_time timestamp default '2004-01-01 00:00:00.000000-00',
char1 char(1) default 'Y',
char2 char(50) default 'a char field',
- char3 text default 'a text field'
+ char3 text default 'a text field',
+ positive_integer integer default 1,
+ negative_integer integer default -1,
+ decimal_number money default 2.78
)
go
+CREATE PRIMARY KEY defaults (id)
+go
CREATE TABLE auto_id_tests (
- auto_id integer UNIQUE INDEX DEFAULT _rowid,
+ auto_id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
value integer
)
go
@@ -144,20 +149,24 @@ CREATE PRIMARY KEY auto_id_tests (auto_id)
go
CREATE TABLE entrants (
- id integer UNIQUE INDEX ,
- name text,
- course_id integer
+ id integer NOT NULL UNIQUE INDEX,
+ name text NOT NULL,
+ course_id integer NOT NULL
)
go
+CREATE PRIMARY KEY entrants (id)
+go
CREATE TABLE colnametests (
id integer UNIQUE INDEX ,
references integer NOT NULL
)
go
+CREATE PRIMARY KEY colnametests (id)
+go
CREATE TABLE mixins (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
parent_id integer,
type char,
pos integer,
@@ -172,7 +181,7 @@ CREATE PRIMARY KEY mixins (id)
go
CREATE TABLE people (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
first_name text,
lock_version integer default 0
)
@@ -181,7 +190,7 @@ CREATE PRIMARY KEY people (id)
go
CREATE TABLE readers (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
post_id integer NOT NULL,
person_id integer NOT NULL
)
@@ -190,7 +199,7 @@ CREATE PRIMARY KEY readers (id)
go
CREATE TABLE binaries (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
data object
)
go
@@ -228,7 +237,7 @@ CREATE TABLE authors (
go
CREATE TABLE tasks (
- id integer UNIQUE INDEX DEFAULT _rowid,
+ id integer NOT NULL UNIQUE INDEX DEFAULT _rowid,
starting datetime,
ending datetime
)
@@ -283,11 +292,11 @@ go
CREATE TABLE numeric_data (
id INTEGER NOT NULL DEFAULT _rowid,
- bank_balance DECIMAL(10,2),
- big_bank_balance DECIMAL(15,2),
- world_population DECIMAL(10),
- my_house_population DECIMAL(2),
- decimal_number_with_default DECIMAL(3,2) DEFAULT 2.78
+ bank_balance MONEY,
+ big_bank_balance MONEY,
+ world_population longlong,
+ my_house_population longlong,
+ decimal_number_with_default MONEY DEFAULT 2.78
);
go
CREATE PRIMARY KEY numeric_data (id)
@@ -300,3 +309,10 @@ CREATE TABLE mixed_case_monkeys (
go
CREATE PRIMARY KEY mixed_case_monkeys (monkeyID)
go
+
+CREATE TABLE minimalistics (
+ id INTEGER NOT NULL DEFAULT _rowid
+);
+go
+CREATE PRIMARY KEY minimalistics (id)
+go
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/oracle.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/oracle.drop.sql
index 910b118e..21f40ed8 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/oracle.drop.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/oracle.drop.sql
@@ -31,6 +31,7 @@ drop table keyboards;
drop table legacy_things;
drop table numeric_data;
drop table mixed_case_monkeys;
+drop table minimalistics;
drop sequence accounts_seq;
drop sequence funny_jokes_seq;
@@ -63,3 +64,4 @@ drop sequence keyboards_seq;
drop sequence legacy_things_seq;
drop sequence numeric_data_seq;
drop sequence mixed_case_monkeys_seq;
+drop sequence minimalistics_seq;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/oracle.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/oracle.sql
index f84a86a6..22ca0baa 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/oracle.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/oracle.sql
@@ -323,3 +323,8 @@ CREATE TABLE mixed_case_monkeys (
"fleaCount" INTEGER
);
create sequence mixed_case_monkeys_seq minvalue 10000;
+
+CREATE TABLE minimalistics (
+ id INTEGER NOT NULL PRIMARY KEY
+);
+create sequence minimalistics_seq minvalue 10000;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql.drop.sql
index 3afe6d39..31e2d387 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql.drop.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql.drop.sql
@@ -35,3 +35,10 @@ DROP TABLE legacy_things;
DROP TABLE numeric_data;
DROP TABLE column_data;
DROP TABLE mixed_case_monkeys;
+DROP TABLE postgresql_arrays;
+DROP TABLE postgresql_moneys;
+DROP TABLE postgresql_numbers;
+DROP TABLE postgresql_times;
+DROP TABLE postgresql_network_addresses;
+DROP TABLE postgresql_bit_strings;
+DROP TABLE postgresql_oids;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql.sql
index 71c35fbd..5e38978e 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql.sql
@@ -1,10 +1,9 @@
CREATE SEQUENCE public.accounts_id_seq START 100;
CREATE TABLE accounts (
- id integer DEFAULT nextval('public.accounts_id_seq'),
+ id integer primary key DEFAULT nextval('public.accounts_id_seq'),
firm_id integer,
- credit_limit integer,
- PRIMARY KEY (id)
+ credit_limit integer
);
CREATE TABLE funny_jokes (
@@ -15,14 +14,13 @@ CREATE TABLE funny_jokes (
CREATE SEQUENCE companies_nonstd_seq START 101;
CREATE TABLE companies (
- id integer DEFAULT nextval('companies_nonstd_seq'),
+ id integer primary key DEFAULT nextval('companies_nonstd_seq'),
"type" character varying(50),
"ruby_type" character varying(50),
firm_id integer,
name character varying(50),
client_of integer,
- rating integer default 1,
- PRIMARY KEY (id)
+ rating integer default 1
);
CREATE TABLE developers_projects (
@@ -33,25 +31,23 @@ CREATE TABLE developers_projects (
);
CREATE TABLE developers (
- id serial,
+ id serial primary key,
name character varying(100),
salary integer DEFAULT 70000,
created_at timestamp,
- updated_at timestamp,
- PRIMARY KEY (id)
+ updated_at timestamp
);
SELECT setval('developers_id_seq', 100);
CREATE TABLE projects (
- id serial,
+ id serial primary key,
name character varying(100),
- type varchar(255),
- PRIMARY KEY (id)
+ type varchar(255)
);
SELECT setval('projects_id_seq', 100);
CREATE TABLE topics (
- id serial,
+ id serial primary key,
title character varying(255),
author_name character varying(255),
author_email_address character varying(255),
@@ -62,52 +58,46 @@ CREATE TABLE topics (
approved boolean default true,
replies_count integer default 0,
parent_id integer,
- "type" character varying(50),
- PRIMARY KEY (id)
+ "type" character varying(50)
);
SELECT setval('topics_id_seq', 100);
CREATE TABLE customers (
- id serial,
+ id serial primary key,
name character varying,
balance integer default 0,
address_street character varying,
address_city character varying,
address_country character varying,
- gps_location character varying,
- PRIMARY KEY (id)
+ gps_location character varying
);
SELECT setval('customers_id_seq', 100);
CREATE TABLE orders (
- id serial,
+ id serial primary key,
name character varying,
billing_customer_id integer,
- shipping_customer_id integer,
- PRIMARY KEY (id)
+ shipping_customer_id integer
);
SELECT setval('orders_id_seq', 100);
CREATE TABLE movies (
- movieid serial,
- name text,
- PRIMARY KEY (movieid)
+ movieid serial primary key,
+ name text
);
CREATE TABLE subscribers (
- nick text NOT NULL,
- name text,
- PRIMARY KEY (nick)
+ nick text primary key NOT NULL,
+ name text
);
CREATE TABLE booleantests (
- id serial,
- value boolean,
- PRIMARY KEY (id)
+ id serial primary key,
+ value boolean
);
CREATE TABLE defaults (
- id serial,
+ id serial primary key,
modified_date date default CURRENT_DATE,
modified_date_function date default now(),
fixed_date date default '2004-01-01',
@@ -119,28 +109,28 @@ CREATE TABLE defaults (
char3 text default 'a text field',
positive_integer integer default 1,
negative_integer integer default -1,
- decimal_number decimal(3,2) default 2.78
+ decimal_number decimal(3,2) default 2.78,
+ multiline_default text DEFAULT E'--- []\n\n'::text
);
CREATE TABLE auto_id_tests (
- auto_id serial,
- value integer,
- PRIMARY KEY (auto_id)
+ auto_id serial primary key,
+ value integer
);
CREATE TABLE entrants (
- id serial,
+ id serial primary key,
name text not null,
course_id integer not null
);
CREATE TABLE colnametests (
- id serial,
+ id serial primary key,
"references" integer NOT NULL
);
CREATE TABLE mixins (
- id serial,
+ id serial primary key,
parent_id integer,
type character varying,
pos integer,
@@ -148,38 +138,34 @@ CREATE TABLE mixins (
rgt integer,
root_id integer,
created_at timestamp,
- updated_at timestamp,
- PRIMARY KEY (id)
+ updated_at timestamp
);
CREATE TABLE people (
- id serial,
+ id serial primary key,
first_name text,
- lock_version integer default 0,
- PRIMARY KEY (id)
+ lock_version integer default 0
);
CREATE TABLE readers (
- id serial,
+ id serial primary key,
post_id integer NOT NULL,
- person_id integer NOT NULL,
- primary key (id)
+ person_id integer NOT NULL
);
-CREATE TABLE binaries (
- id serial ,
- data bytea,
- PRIMARY KEY (id)
+CREATE TABLE binaries (
+ id serial primary key,
+ data bytea
);
CREATE TABLE computers (
- id serial,
+ id serial primary key,
developer integer NOT NULL,
"extendedWarranty" integer NOT NULL
);
CREATE TABLE posts (
- id serial,
+ id serial primary key,
author_id integer,
title varchar(255),
type varchar(255),
@@ -187,26 +173,25 @@ CREATE TABLE posts (
);
CREATE TABLE comments (
- id serial,
+ id serial primary key,
post_id integer,
type varchar(255),
body text
);
CREATE TABLE authors (
- id serial,
+ id serial primary key,
name varchar(255) default NULL
);
CREATE TABLE tasks (
- id serial,
+ id serial primary key,
starting timestamp,
- ending timestamp,
- PRIMARY KEY (id)
+ ending timestamp
);
CREATE TABLE categories (
- id serial,
+ id serial primary key,
name varchar(255),
type varchar(255)
);
@@ -261,3 +246,47 @@ CREATE TABLE mixed_case_monkeys (
"monkeyID" INTEGER PRIMARY KEY,
"fleaCount" INTEGER
);
+
+CREATE TABLE postgresql_arrays (
+ id SERIAL PRIMARY KEY,
+ commission_by_quarter INTEGER[],
+ nicknames TEXT[]
+);
+
+CREATE TABLE postgresql_moneys (
+ id SERIAL PRIMARY KEY,
+ wealth MONEY
+);
+
+CREATE TABLE postgresql_numbers (
+ id SERIAL PRIMARY KEY,
+ single REAL,
+ double DOUBLE PRECISION
+);
+
+CREATE TABLE postgresql_times (
+ id SERIAL PRIMARY KEY,
+ time_interval INTERVAL
+);
+
+CREATE TABLE postgresql_network_addresses (
+ id SERIAL PRIMARY KEY,
+ cidr_address CIDR,
+ inet_address INET,
+ mac_address MACADDR
+);
+
+CREATE TABLE postgresql_bit_strings (
+ id SERIAL PRIMARY KEY,
+ bit_string BIT(8),
+ bit_string_varying BIT VARYING(8)
+);
+
+CREATE TABLE postgresql_oids (
+ id SERIAL PRIMARY KEY,
+ obj_id OID
+);
+
+CREATE TABLE minimalistics (
+ id serial primary key
+);
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql2.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql2.sql
index c0d7f79b..4605b938 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql2.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/postgresql2.sql
@@ -1,5 +1,4 @@
CREATE TABLE courses (
- id serial,
+ id serial primary key,
name text
);
-
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/schema.rb b/vendor/rails/activerecord/test/fixtures/db_definitions/schema.rb
index ca1cac51..a943b84f 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/schema.rb
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/schema.rb
@@ -1,5 +1,214 @@
ActiveRecord::Schema.define do
+ # adapter name is checked because we are under a transition of
+ # moving the sql files under activerecord/test/fixtures/db_definitions
+ # to this file, schema.rb.
+ if adapter_name == "MySQL"
+
+ # Please keep these create table statements in alphabetical order
+ # unless the ordering matters. In which case, define them below
+ create_table :accounts, :force => true do |t|
+ t.integer :firm_id
+ t.integer :credit_limit
+ end
+
+ create_table :authors, :force => true do |t|
+ t.string :name, :null => false
+ end
+
+ create_table :auto_id_tests, :force => true, :id => false do |t|
+ t.primary_key :auto_id
+ t.integer :value
+ end
+
+ create_table :binaries, :force => true do |t|
+ t.binary :data
+ end
+
+ create_table :booleantests, :force => true do |t|
+ t.integer :value
+ end
+
+ create_table :categories, :force => true do |t|
+ t.string :name, :null => false
+ t.string :type
+ end
+
+ create_table :categories_posts, :force => true, :id => false do |t|
+ t.integer :category_id, :null => false
+ t.integer :post_id, :null => false
+ end
+
+ create_table :colnametests, :force => true do |t|
+ t.integer :references, :null => false
+ end
+
+ create_table :comments, :force => true do |t|
+ t.integer :post_id, :null => false
+ t.text :body, :null => false
+ t.string :type
+ end
+
+ create_table :companies, :force => true do |t|
+ t.string :type
+ t.string :ruby_type
+ t.integer :firm_id
+ t.string :name
+ t.integer :client_of
+ t.integer :rating, :default => 1
+ end
+
+ create_table :computers, :force => true do |t|
+ t.integer :developer, :null => false
+ t.integer :extendedWarranty, :null => false
+ end
+
+
+ create_table :customers, :force => true do |t|
+ t.string :name
+ t.integer :balance, :default => 0
+ t.string :address_street
+ t.string :address_city
+ t.string :address_country
+ t.string :gps_location
+ end
+
+ create_table :developers, :force => true do |t|
+ t.string :name
+ t.integer :salary, :default => 70000
+ t.datetime :created_at
+ t.datetime :updated_at
+ end
+
+ create_table :developers_projects, :force => true, :id => false do |t|
+ t.integer :developer_id, :null => false
+ t.integer :project_id, :null => false
+ t.date :joined_on
+ t.integer :access_level, :default => 1
+ end
+
+ create_table :entrants, :force => true do |t|
+ t.string :name, :null => false
+ t.integer :course_id, :null => false
+ end
+
+ create_table :funny_jokes, :force => true do |t|
+ t.string :name
+ end
+
+ create_table :keyboards, :force => true, :id => false do |t|
+ t.primary_key :key_number
+ t.string :name
+ end
+
+ create_table :legacy_things, :force => true do |t|
+ t.integer :tps_report_number
+ t.integer :version, :null => false, :default => 0
+ end
+
+ create_table :minimalistics, :force => true do |t|
+ end
+
+ create_table :mixed_case_monkeys, :force => true, :id => false do |t|
+ t.primary_key :monkeyID
+ t.integer :fleaCount
+ end
+
+ create_table :mixins, :force => true do |t|
+ t.integer :parent_id
+ t.integer :pos
+ t.datetime :created_at
+ t.datetime :updated_at
+ t.integer :lft
+ t.integer :rgt
+ t.integer :root_id
+ t.string :type
+ end
+
+ create_table :movies, :force => true, :id => false do |t|
+ t.primary_key :movieid
+ t.string :name
+ end
+
+ create_table :numeric_data, :force => true do |t|
+ t.decimal :bank_balance, :precision => 10, :scale => 2
+ t.decimal :big_bank_balance, :precision => 15, :scale => 2
+ t.decimal :world_population, :precision => 10, :scale => 0
+ t.decimal :my_house_population, :precision => 2, :scale => 0
+ t.decimal :decimal_number_with_default, :precision => 3, :scale => 2, :default => 2.78
+ end
+
+ create_table :orders, :force => true do |t|
+ t.string :name
+ t.integer :billing_customer_id
+ t.integer :shipping_customer_id
+ end
+
+ create_table :people, :force => true do |t|
+ t.string :first_name, :null => false
+ t.integer :lock_version, :null => false, :default => 0
+ end
+
+ create_table :posts, :force => true do |t|
+ t.integer :author_id
+ t.string :title, :null => false
+ t.text :body, :null => false
+ t.string :type
+ end
+
+ create_table :projects, :force => true do |t|
+ t.string :name
+ t.string :type
+ end
+
+ create_table :readers, :force => true do |t|
+ t.integer :post_id, :null => false
+ t.integer :person_id, :null => false
+ end
+
+ create_table :subscribers, :force => true, :id => false do |t|
+ t.string :nick, :null => false
+ t.string :name
+ end
+ add_index :subscribers, :nick, :unique => true
+
+ create_table :tasks, :force => true do |t|
+ t.datetime :starting
+ t.datetime :ending
+ end
+
+ create_table :topics, :force => true do |t|
+ t.string :title
+ t.string :author_name
+ t.string :author_email_address
+ t.datetime :written_on
+ t.time :bonus_time
+ t.date :last_read
+ t.text :content
+ t.boolean :approved, :default => true
+ t.integer :replies_count, :default => 0
+ t.integer :parent_id
+ t.string :type
+ end
+
+
+
+ ### These tables are created last as the order is significant
+
+ # fk_test_has_fk should be before fk_test_has_pk
+ create_table :fk_test_has_fk, :force => true do |t|
+ t.integer :fk_id, :null => false
+ end
+
+ create_table :fk_test_has_pk, :force => true do |t|
+ end
+
+ execute 'alter table fk_test_has_fk
+ add FOREIGN KEY (`fk_id`) REFERENCES `fk_test_has_pk`(`id`)'
+
+
+ end
+
# For Firebird, set the sequence values 10000 when create_table is called;
# this prevents primary key collisions between "normally" created records
# and fixture-based (YAML) records.
@@ -58,8 +267,88 @@ ActiveRecord::Schema.define do
t.column :custom_lock_version, :integer
end
+ create_table :items, :force => true do |t|
+ t.column :name, :integer
+ end
+
+ # For sqlite 3.1.0+, make a table with a autoincrement column
+ if adapter_name == 'SQLite' and supports_autoincrement?
+ create_table :table_with_autoincrement, :force => true do |t|
+ t.column :name, :string
+ end
+ end
+
+ # For sqlserver 2000+, ensure real columns can be used
+ if adapter_name.starts_with?("SQLServer")
+ create_table :table_with_real_columns, :force => true do |t|
+ t.column :real_number, :real
+ end
+ end
+
create_table :audit_logs, :force => true do |t|
t.column :message, :string, :null=>false
t.column :developer_id, :integer, :null=>false
end
+
+ create_table :books, :force => true do |t|
+ t.column :name, :string
+ end
+
+ create_table :citations, :force => true do |t|
+ t.column :book1_id, :integer
+ t.column :book2_id, :integer
+ end
+
+ create_table :inept_wizards, :force => true do |t|
+ t.column :name, :string, :null => false
+ t.column :city, :string, :null => false
+ t.column :type, :string
+ end
+
+ create_table :parrots, :force => true do |t|
+ t.column :name, :string
+ t.column :parrot_sti_class, :string
+ t.column :killer_id, :integer
+ t.column :created_at, :datetime
+ t.column :created_on, :datetime
+ t.column :updated_at, :datetime
+ t.column :updated_on, :datetime
+ end
+
+ create_table :pirates, :force => true do |t|
+ t.column :catchphrase, :string
+ t.column :parrot_id, :integer
+ t.column :created_on, :datetime
+ t.column :updated_on, :datetime
+ end
+
+ create_table :parrots_pirates, :id => false, :force => true do |t|
+ t.column :parrot_id, :integer
+ t.column :pirate_id, :integer
+ end
+
+ create_table :treasures, :force => true do |t|
+ t.column :name, :string
+ t.column :looter_id, :integer
+ t.column :looter_type, :string
+ end
+
+ create_table :parrots_treasures, :id => false, :force => true do |t|
+ t.column :parrot_id, :integer
+ t.column :treasure_id, :integer
+ end
+
+ create_table :mateys, :id => false, :force => true do |t|
+ t.column :pirate_id, :integer
+ t.column :target_id, :integer
+ t.column :weight, :integer
+ end
+
+ create_table :ships, :force => true do |t|
+ t.string :name
+ t.datetime :created_at
+ t.datetime :created_on
+ t.datetime :updated_at
+ t.datetime :updated_on
+ end
end
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/schema2.rb b/vendor/rails/activerecord/test/fixtures/db_definitions/schema2.rb
new file mode 100644
index 00000000..863237d2
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/schema2.rb
@@ -0,0 +1,11 @@
+ActiveRecord::Schema.define do
+
+ # adapter name is checked because we are under a transition of
+ # moving the sql files under activerecord/test/fixtures/db_definitions
+ # to this file, schema.rb.
+ if adapter_name == "MySQL"
+ Course.connection.create_table :courses, :force => true do |t|
+ t.column :name, :string, :null => false
+ end
+ end
+end
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlite.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/sqlite.drop.sql
index f269b3a3..419cdaa5 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlite.drop.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/sqlite.drop.sql
@@ -30,3 +30,4 @@ DROP TABLE keyboards;
DROP TABLE legacy_things;
DROP TABLE numeric_data;
DROP TABLE mixed_case_monkeys;
+DROP TABLE minimalistics;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlite.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/sqlite.sql
index 973639f1..2e19dd25 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlite.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/sqlite.sql
@@ -213,3 +213,7 @@ CREATE TABLE mixed_case_monkeys (
'monkeyID' INTEGER NOT NULL PRIMARY KEY,
'fleaCount' INTEGER
);
+
+CREATE TABLE minimalistics (
+ 'id' INTEGER NOT NULL PRIMARY KEY
+);
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver.drop.sql
deleted file mode 100644
index c415d223..00000000
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver.drop.sql
+++ /dev/null
@@ -1,34 +0,0 @@
-DROP TABLE accounts;
-DROP TABLE funny_jokes;
-DROP TABLE companies;
-DROP TABLE topics;
-DROP TABLE developers;
-DROP TABLE projects;
-DROP TABLE developers_projects;
-DROP TABLE customers;
-DROP TABLE orders;
-DROP TABLE movies;
-DROP TABLE subscribers;
-DROP TABLE booleantests;
-DROP TABLE defaults;
-DROP TABLE auto_id_tests;
-DROP TABLE entrants;
-DROP TABLE colnametests;
-DROP TABLE mixins;
-DROP TABLE people;
-DROP TABLE readers;
-DROP TABLE binaries;
-DROP TABLE computers;
-DROP TABLE posts;
-DROP TABLE comments;
-DROP TABLE authors;
-DROP TABLE tasks;
-DROP TABLE categories;
-DROP TABLE categories_posts;
-DROP TABLE fk_test_has_fk;
-DROP TABLE fk_test_has_pk;
-DROP TABLE keyboards;
-DROP TABLE legacy_things;
-DROP TABLE numeric_data;
-DROP TABLE [order];
-DROP TABLE mixed_case_monkeys;
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver.sql
deleted file mode 100644
index 3cec4e4a..00000000
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver.sql
+++ /dev/null
@@ -1,243 +0,0 @@
-CREATE TABLE accounts (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- firm_id int default NULL,
- credit_limit int default NULL
-);
-
-CREATE TABLE funny_jokes (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(50) default NULL
-);
-
-CREATE TABLE companies (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- type varchar(50) default NULL,
- ruby_type varchar(50) default NULL,
- firm_id int default NULL,
- name varchar(50) default NULL,
- client_of int default NULL,
- rating int default 1
-);
-
-CREATE TABLE topics (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- title varchar(255) default NULL,
- author_name varchar(255) default NULL,
- author_email_address varchar(255) default NULL,
- written_on datetime default NULL,
- bonus_time datetime default NULL,
- last_read datetime default NULL,
- content varchar(255) default NULL,
- approved bit default 1,
- replies_count int default 0,
- parent_id int default NULL,
- type varchar(50) default NULL
-);
-
-CREATE TABLE developers (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(100) default NULL,
- salary int default 70000,
- created_at datetime default NULL,
- updated_at datetime default NULL
-);
-
-CREATE TABLE projects (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(100) default NULL,
- type varchar(255) default NULL
-);
-
-CREATE TABLE developers_projects (
- developer_id int NOT NULL,
- project_id int NOT NULL,
- joined_on datetime default NULL,
- access_level int default 1
-);
-
-CREATE TABLE orders (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(100) default NULL,
- billing_customer_id int default NULL,
- shipping_customer_id int default NULL
-);
-
-
-CREATE TABLE customers (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(100) default NULL,
- balance int default 0,
- address_street varchar(100) default NULL,
- address_city varchar(100) default NULL,
- address_country varchar(100) default NULL,
- gps_location varchar(100) default NULL
-);
-
-CREATE TABLE movies (
- movieid int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(100) default NULL
-);
-
-CREATE TABLE subscribers (
- nick varchar(100) NOT NULL PRIMARY KEY,
- name varchar(100) default NULL
-);
-
-CREATE TABLE booleantests (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- value bit default NULL
-);
-
-CREATE TABLE defaults (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
--- these brought from the PostgreSQL defaults_test.rb but
--- tests only exist for integers and decimals, currently
--- modified_date date default CURRENT_DATE,
--- modified_date_function date default now(),
--- fixed_date date default '2004-01-01',
--- modified_time timestamp default CURRENT_TIMESTAMP,
--- modified_time_function timestamp default now(),
--- fixed_time timestamp default '2004-01-01 00:00:00.000000-00',
--- char1 char(1) default 'Y',
--- char2 character varying(50) default 'a varchar field',
--- char3 text default 'a text field',
- positive_integer integer default 1,
- negative_integer integer default -1,
- decimal_number decimal(3,2) default 2.78
-);
-
-CREATE TABLE auto_id_tests (
- auto_id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- value int default NULL
-);
-
-CREATE TABLE entrants (
- id int NOT NULL PRIMARY KEY,
- name varchar(255) NOT NULL,
- course_id int NOT NULL
-);
-
-CREATE TABLE colnametests (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- [references] int NOT NULL
-);
-
-CREATE TABLE mixins (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- parent_id int default NULL,
- pos int default NULL,
- created_at datetime default NULL,
- updated_at datetime default NULL,
- lft int default NULL,
- rgt int default NULL,
- root_id int default NULL,
- type varchar(40) default NULL
-);
-
-CREATE TABLE people (
- id int NOT NULL IDENTITY(1, 1),
- first_name varchar(40) NULL,
- lock_version int default 0,
- PRIMARY KEY (id)
-);
-
-CREATE TABLE readers (
- id int NOT NULL IDENTITY(1, 1),
- post_id int NOT NULL,
- person_id int NOT NULL,
- primary key (id)
-);
-
-CREATE TABLE binaries (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- data image NULL
-);
-
-CREATE TABLE computers (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- developer int NOT NULL,
- extendedWarranty int NOT NULL
-);
-
-CREATE TABLE posts (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- author_id int default NULL,
- title varchar(255) default NULL,
- type varchar(255) default NULL,
- body varchar(4096) default NULL
-);
-
-CREATE TABLE comments (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- post_id int default NULL,
- type varchar(255) default NULL,
- body varchar(4096) default NULL
-);
-
-CREATE TABLE authors (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(255) default NULL
-);
-
-CREATE TABLE tasks (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- starting datetime default NULL,
- ending datetime default NULL
-);
-
-CREATE TABLE categories (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(255),
- type varchar(255) default NULL
-);
-
-CREATE TABLE categories_posts (
- category_id int NOT NULL,
- post_id int NOT NULL
-);
-
-CREATE TABLE fk_test_has_pk (
- id INTEGER NOT NULL PRIMARY KEY
-);
-
-CREATE TABLE fk_test_has_fk (
- id INTEGER NOT NULL PRIMARY KEY,
- fk_id INTEGER NOT NULL,
-
- FOREIGN KEY (fk_id) REFERENCES fk_test_has_pk(id)
-);
-
-CREATE TABLE keyboards (
- key_number int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- name varchar(50) default NULL
-);
-
---This table has an altered lock_version column name.
-CREATE TABLE legacy_things (
- id int NOT NULL IDENTITY(1, 1),
- tps_report_number int default NULL,
- version int default 0,
- PRIMARY KEY (id)
-);
-
-CREATE TABLE numeric_data (
- id int NOT NULL IDENTITY(1, 1),
- bank_balance decimal(10,2),
- big_bank_balance decimal(15,2),
- world_population decimal(10),
- my_house_population decimal(2),
- decimal_number_with_default decimal(3,2) DEFAULT 2.78
-);
-
-CREATE TABLE [order] (
- id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
- color varchar(255),
- fruit_size varchar(255),
- texture varchar(255),
- flavor varchar(255)
-);
-
-CREATE TABLE mixed_case_monkeys (
- [monkeyID] int NOT NULL IDENTITY(1, 1),
- [fleaCount] int default NULL
-);
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver2.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver2.drop.sql
deleted file mode 100644
index df00ffd7..00000000
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver2.drop.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-DROP TABLE courses;
-
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver2.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver2.sql
deleted file mode 100644
index 9198cf5f..00000000
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/sqlserver2.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-CREATE TABLE courses (
- id int NOT NULL PRIMARY KEY,
- name varchar(255) NOT NULL
-);
-
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/sybase.drop.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/sybase.drop.sql
index 9d852216..ebb91931 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/sybase.drop.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/sybase.drop.sql
@@ -30,5 +30,6 @@ DROP TABLE keyboards
DROP TABLE legacy_things
DROP TABLE numeric_data
DROP TABLE mixed_case_monkeys
+DROP TABLE minimalistics
DROP TABLE schema_info
go
diff --git a/vendor/rails/activerecord/test/fixtures/db_definitions/sybase.sql b/vendor/rails/activerecord/test/fixtures/db_definitions/sybase.sql
index 6c42c660..9f0cb3eb 100644
--- a/vendor/rails/activerecord/test/fixtures/db_definitions/sybase.sql
+++ b/vendor/rails/activerecord/test/fixtures/db_definitions/sybase.sql
@@ -215,4 +215,8 @@ CREATE TABLE mixed_case_monkeys (
[fleaCount] numeric(9,0)
);
+CREATE TABLE minimalistics (
+ id numeric(9,0) IDENTITY PRIMARY KEY
+);
+
go
diff --git a/vendor/rails/activerecord/test/fixtures/developer.rb b/vendor/rails/activerecord/test/fixtures/developer.rb
index ebe91a48..20005ed0 100644
--- a/vendor/rails/activerecord/test/fixtures/developer.rb
+++ b/vendor/rails/activerecord/test/fixtures/developer.rb
@@ -29,6 +29,16 @@ class Developer < ActiveRecord::Base
:association_foreign_key => "project_id",
:extend => [DeveloperProjectsAssociationExtension, DeveloperProjectsAssociationExtension2]
+ has_and_belongs_to_many :projects_extended_by_name_and_block,
+ :class_name => "Project",
+ :join_table => "developers_projects",
+ :association_foreign_key => "project_id",
+ :extend => DeveloperProjectsAssociationExtension do
+ def find_least_recent
+ find(:first, :order => "id ASC")
+ end
+ end
+
has_and_belongs_to_many :special_projects, :join_table => 'developers_projects', :association_foreign_key => 'project_id'
has_many :audit_logs
diff --git a/vendor/rails/activerecord/test/fixtures/example.log b/vendor/rails/activerecord/test/fixtures/example.log
new file mode 100644
index 00000000..f084369d
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/example.log
@@ -0,0 +1 @@
+# Logfile created on Wed Oct 31 16:05:13 +0000 2007 by logger.rb/1.5.2.9
diff --git a/vendor/rails/activerecord/test/fixtures/flowers.jpg b/vendor/rails/activerecord/test/fixtures/flowers.jpg
index 3687b097..fe9df546 100644
Binary files a/vendor/rails/activerecord/test/fixtures/flowers.jpg and b/vendor/rails/activerecord/test/fixtures/flowers.jpg differ
diff --git a/vendor/rails/activerecord/test/fixtures/item.rb b/vendor/rails/activerecord/test/fixtures/item.rb
new file mode 100644
index 00000000..c2571dd7
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/item.rb
@@ -0,0 +1,7 @@
+class AbstractItem < ActiveRecord::Base
+ self.abstract_class = true
+ has_one :tagging, :as => :taggable
+end
+
+class Item < AbstractItem
+end
diff --git a/vendor/rails/activerecord/test/fixtures/items.yml b/vendor/rails/activerecord/test/fixtures/items.yml
new file mode 100644
index 00000000..31fd657d
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/items.yml
@@ -0,0 +1,4 @@
+dvd:
+ id: 1
+ name: Godfather
+
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/joke.rb b/vendor/rails/activerecord/test/fixtures/joke.rb
index 8006a43b..3978abc2 100644
--- a/vendor/rails/activerecord/test/fixtures/joke.rb
+++ b/vendor/rails/activerecord/test/fixtures/joke.rb
@@ -1,6 +1,3 @@
class Joke < ActiveRecord::Base
set_table_name 'funny_jokes'
end
-class Joke < ActiveRecord::Base
- set_table_name 'funny_jokes'
-end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/matey.rb b/vendor/rails/activerecord/test/fixtures/matey.rb
new file mode 100644
index 00000000..47b0baa9
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/matey.rb
@@ -0,0 +1,4 @@
+class Matey < ActiveRecord::Base
+ belongs_to :pirate
+ belongs_to :target, :class_name => 'Pirate'
+end
diff --git a/vendor/rails/activerecord/test/fixtures/mateys.yml b/vendor/rails/activerecord/test/fixtures/mateys.yml
new file mode 100644
index 00000000..9ecdd4ec
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/mateys.yml
@@ -0,0 +1,4 @@
+blackbeard_to_redbeard:
+ pirate_id: <%= Fixtures.identify(:blackbeard) %>
+ target_id: <%= Fixtures.identify(:redbeard) %>
+ weight: 10
diff --git a/vendor/rails/activerecord/test/fixtures/minimalistic.rb b/vendor/rails/activerecord/test/fixtures/minimalistic.rb
new file mode 100644
index 00000000..2e3f8e08
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/minimalistic.rb
@@ -0,0 +1,2 @@
+class Minimalistic < ActiveRecord::Base
+end
diff --git a/vendor/rails/activerecord/test/fixtures/minimalistics.yml b/vendor/rails/activerecord/test/fixtures/minimalistics.yml
new file mode 100644
index 00000000..c3ec5462
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/minimalistics.yml
@@ -0,0 +1,2 @@
+first:
+ id: 1
diff --git a/vendor/rails/activerecord/test/fixtures/mixin.rb b/vendor/rails/activerecord/test/fixtures/mixin.rb
deleted file mode 100644
index 7f877a8c..00000000
--- a/vendor/rails/activerecord/test/fixtures/mixin.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-class Mixin < ActiveRecord::Base
-
-end
-
-class TreeMixin < Mixin
- acts_as_tree :foreign_key => "parent_id", :order => "id"
-end
-
-class TreeMixinWithoutOrder < Mixin
- acts_as_tree :foreign_key => "parent_id"
-end
-
-class RecursivelyCascadedTreeMixin < Mixin
- acts_as_tree :foreign_key => "parent_id"
- has_one :first_child, :class_name => 'RecursivelyCascadedTreeMixin', :foreign_key => :parent_id
-end
-
-class ListMixin < Mixin
- acts_as_list :column => "pos", :scope => :parent
-
- def self.table_name() "mixins" end
-end
-
-class ListMixinSub1 < ListMixin
-end
-
-class ListMixinSub2 < ListMixin
-end
-
-
-class ListWithStringScopeMixin < ActiveRecord::Base
- acts_as_list :column => "pos", :scope => 'parent_id = #{parent_id}'
-
- def self.table_name() "mixins" end
-end
-
-class NestedSet < Mixin
- acts_as_nested_set :scope => "root_id IS NULL"
-
- def self.table_name() "mixins" end
-end
-
-class NestedSetWithStringScope < Mixin
- acts_as_nested_set :scope => 'root_id = #{root_id}'
-
- def self.table_name() "mixins" end
-end
-
-class NestedSetWithSymbolScope < Mixin
- acts_as_nested_set :scope => :root
-
- def self.table_name() "mixins" end
-end
-
-class NestedSetSuperclass < Mixin
- acts_as_nested_set :scope => :root
-
- def self.table_name() "mixins" end
-end
-
-class NestedSetSubclass < NestedSetSuperclass
-
-end
diff --git a/vendor/rails/activerecord/test/fixtures/mixins.yml b/vendor/rails/activerecord/test/fixtures/mixins.yml
index 881b97c1..0f60e92c 100644
--- a/vendor/rails/activerecord/test/fixtures/mixins.yml
+++ b/vendor/rails/activerecord/test/fixtures/mixins.yml
@@ -1,98 +1,8 @@
-# tree mixins
-tree_1:
- id: 1001
- type: TreeMixin
- parent_id:
-
-tree_2:
- id: 1002
- type: TreeMixin
- parent_id: 1001
-
-tree_3:
- id: 1003
- type: TreeMixin
- parent_id: 1002
-
-tree_4:
- id: 1004
- type: TreeMixin
- parent_id: 1001
-
-tree2_1:
- id: 1005
- type: TreeMixin
- parent_id:
-
-tree3_1:
- id: 1006
- type: TreeMixin
- parent_id:
-
-tree_without_order_1:
- id: 1101
- type: TreeMixinWithoutOrder
- parent_id:
-
-tree_without_order_2:
- id: 1100
- type: TreeMixinWithoutOrder
- parent_id:
-
-recursively_cascaded_tree_1:
- id: 5005
- type: RecursivelyCascadedTreeMixin
- parent_id:
-
-recursively_cascaded_tree_2:
- id: 5006
- type: RecursivelyCascadedTreeMixin
- parent_id: 5005
-
-recursively_cascaded_tree_3:
- id: 5007
- type: RecursivelyCascadedTreeMixin
- parent_id: 5006
-
-recursively_cascaded_tree_4:
- id: 5008
- type: RecursivelyCascadedTreeMixin
- parent_id: 5007
-
-# List mixins
-
-<% (1..4).each do |counter| %>
-list_<%= counter %>:
- id: <%= counter+1006 %>
- pos: <%= counter %>
- type: ListMixin
- parent_id: 5
-<% end %>
-
# Nested set mixins
<% (1..10).each do |counter| %>
set_<%= counter %>:
id: <%= counter+3000 %>
- type: NestedSet
-<% end %>
-
-# Nested set with STI
-<%
-[ [3100, 0, 1, 10, "NestedSetSuperclass"],
- [3101, 3100, 2, 5, "NestedSetSubclass"],
- [3102, 3101, 3, 4, "NestedSetSuperclass"],
- [3103, 3100, 6, 9, "NestedSetSuperclass"],
- [3104, 3103, 7, 8, "NestedSetSubclass"]
-].each do |sti| %>
-sti_set_<%= sti[0] %>:
- id: <%= sti[0] %>
- parent_id: <%= sti[1] %>
- lft: <%= sti[2] %>
- rgt: <%= sti[3] %>
- type: <%= sti[4] %>
- root_id: 3100
-
<% end %>
# Big old set
@@ -101,10 +11,10 @@ sti_set_<%= sti[0] %>:
[4002, 4001, 2, 7],
[4003, 4002, 3, 4],
[4004, 4002, 5, 6],
- [4005, 4001, 8, 13],
+ [4005, 4001, 14, 13],
[4006, 4005, 9, 10],
[4007, 4005, 11, 12],
- [4008, 4001, 14, 19],
+ [4008, 4001, 8, 19],
[4009, 4008, 15, 16],
[4010, 4008, 17, 18]].each do |set| %>
tree_<%= set[0] %>:
@@ -117,11 +27,3 @@ tree_<%= set[0] %>:
<% end %>
-# subclasses of list items
-<% (1..4).each do |i| %>
-list_sub_<%= i %>:
- id: <%= i + 5000 %>
- pos: <%= i %>
- parent_id: 5000
- type: <%= (i % 2 == 1) ? ListMixinSub1 : ListMixinSub2 %>
-<% end %>
diff --git a/vendor/rails/activerecord/test/fixtures/parrot.rb b/vendor/rails/activerecord/test/fixtures/parrot.rb
new file mode 100644
index 00000000..65191c1a
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/parrot.rb
@@ -0,0 +1,13 @@
+class Parrot < ActiveRecord::Base
+ set_inheritance_column :parrot_sti_class
+ has_and_belongs_to_many :pirates
+ has_and_belongs_to_many :treasures
+ has_many :loots, :as => :looter
+end
+
+class LiveParrot < Parrot
+end
+
+class DeadParrot < Parrot
+ belongs_to :killer, :class_name => 'Pirate'
+end
diff --git a/vendor/rails/activerecord/test/fixtures/parrots.yml b/vendor/rails/activerecord/test/fixtures/parrots.yml
new file mode 100644
index 00000000..8b73b8cd
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/parrots.yml
@@ -0,0 +1,27 @@
+george:
+ name: "Curious George"
+ treasures: diamond, sapphire
+ parrot_sti_class: LiveParrot
+
+louis:
+ name: "King Louis"
+ treasures: [diamond, sapphire]
+ parrot_sti_class: LiveParrot
+
+frederick:
+ name: $LABEL
+ parrot_sti_class: LiveParrot
+
+polly:
+ id: 4
+ name: $LABEL
+ killer: blackbeard
+ treasures: sapphire, ruby
+ parrot_sti_class: DeadParrot
+
+DEFAULTS: &DEFAULTS
+ treasures: sapphire, ruby
+ parrot_sti_class: LiveParrot
+
+davey:
+ <<: *DEFAULTS
diff --git a/vendor/rails/activerecord/test/fixtures/parrots_pirates.yml b/vendor/rails/activerecord/test/fixtures/parrots_pirates.yml
new file mode 100644
index 00000000..6b17a37d
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/parrots_pirates.yml
@@ -0,0 +1,7 @@
+george_blackbeard:
+ parrot_id: <%= Fixtures.identify(:george) %>
+ pirate_id: <%= Fixtures.identify(:blackbeard) %>
+
+louis_blackbeard:
+ parrot_id: <%= Fixtures.identify(:louis) %>
+ pirate_id: <%= Fixtures.identify(:blackbeard) %>
diff --git a/vendor/rails/activerecord/test/fixtures/pirate.rb b/vendor/rails/activerecord/test/fixtures/pirate.rb
new file mode 100644
index 00000000..f7193dfb
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/pirate.rb
@@ -0,0 +1,5 @@
+class Pirate < ActiveRecord::Base
+ belongs_to :parrot
+ has_and_belongs_to_many :parrots
+ has_many :loots, :as => :looter
+end
diff --git a/vendor/rails/activerecord/test/fixtures/pirates.yml b/vendor/rails/activerecord/test/fixtures/pirates.yml
new file mode 100644
index 00000000..abb91101
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/pirates.yml
@@ -0,0 +1,9 @@
+blackbeard:
+ catchphrase: "Yar."
+ parrot: george
+
+redbeard:
+ catchphrase: "Avast!"
+ parrot: louis
+ created_on: <%= 2.weeks.ago.to_s(:db) %>
+ updated_on: <%= 2.weeks.ago.to_s(:db) %>
diff --git a/vendor/rails/activerecord/test/fixtures/post.rb b/vendor/rails/activerecord/test/fixtures/post.rb
index 44ac3c10..4627da52 100644
--- a/vendor/rails/activerecord/test/fixtures/post.rb
+++ b/vendor/rails/activerecord/test/fixtures/post.rb
@@ -16,6 +16,7 @@ class Post < ActiveRecord::Base
has_one :very_special_comment
has_one :very_special_comment_with_post, :class_name => "VerySpecialComment", :include => :post
has_many :special_comments
+ has_many :nonexistant_comments, :class_name => 'Comment', :conditions => 'comments.id < 0'
has_and_belongs_to_many :categories
has_and_belongs_to_many :special_categories, :join_table => "categories_posts", :association_foreign_key => 'category_id'
diff --git a/vendor/rails/activerecord/test/fixtures/project.rb b/vendor/rails/activerecord/test/fixtures/project.rb
index 2079dc9b..b90d2c6f 100644
--- a/vendor/rails/activerecord/test/fixtures/project.rb
+++ b/vendor/rails/activerecord/test/fixtures/project.rb
@@ -1,5 +1,6 @@
class Project < ActiveRecord::Base
has_and_belongs_to_many :developers, :uniq => true, :order => 'developers.name desc, developers.id desc'
+ has_and_belongs_to_many :selected_developers, :class_name => "Developer", :select => "developers.*", :uniq => true
has_and_belongs_to_many :non_unique_developers, :order => 'developers.name desc, developers.id desc', :class_name => 'Developer'
has_and_belongs_to_many :limited_developers, :class_name => "Developer", :limit => 1
has_and_belongs_to_many :developers_named_david, :class_name => "Developer", :conditions => "name = 'David'", :uniq => true
@@ -7,8 +8,8 @@ class Project < ActiveRecord::Base
has_and_belongs_to_many :salaried_developers, :class_name => "Developer", :conditions => "salary > 0"
has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id}'
has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => "DELETE FROM developers_projects WHERE project_id = \#{id} AND developer_id = \#{record.id}"
- has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id}"},
- :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id}"},
+ has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || ''}"},
+ :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || ''}"},
:before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"},
:after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}
diff --git a/vendor/rails/activerecord/test/fixtures/reserved_words/distinct.yml b/vendor/rails/activerecord/test/fixtures/reserved_words/distinct.yml
new file mode 100644
index 00000000..0988f89c
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/reserved_words/distinct.yml
@@ -0,0 +1,5 @@
+distinct1:
+ id: 1
+
+distinct2:
+ id: 2
diff --git a/vendor/rails/activerecord/test/fixtures/reserved_words/distincts_selects.yml b/vendor/rails/activerecord/test/fixtures/reserved_words/distincts_selects.yml
new file mode 100644
index 00000000..90e8c95f
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/reserved_words/distincts_selects.yml
@@ -0,0 +1,11 @@
+distincts_selects1:
+ distinct_id: 1
+ select_id: 1
+
+distincts_selects2:
+ distinct_id: 1
+ select_id: 2
+
+distincts_selects3:
+ distinct_id: 2
+ select_id: 3
diff --git a/vendor/rails/activerecord/test/fixtures/reserved_words/group.yml b/vendor/rails/activerecord/test/fixtures/reserved_words/group.yml
new file mode 100644
index 00000000..39abea7a
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/reserved_words/group.yml
@@ -0,0 +1,14 @@
+group1:
+ id: 1
+ select_id: 1
+ order: x
+
+group2:
+ id: 2
+ select_id: 2
+ order: y
+
+group3:
+ id: 3
+ select_id: 2
+ order: z
diff --git a/vendor/rails/activerecord/test/fixtures/reserved_words/select.yml b/vendor/rails/activerecord/test/fixtures/reserved_words/select.yml
new file mode 100644
index 00000000..a4c35a2b
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/reserved_words/select.yml
@@ -0,0 +1,8 @@
+select1:
+ id: 1
+
+select2:
+ id: 2
+
+select3:
+ id: 3
diff --git a/vendor/rails/activerecord/test/fixtures/reserved_words/values.yml b/vendor/rails/activerecord/test/fixtures/reserved_words/values.yml
new file mode 100644
index 00000000..7d109609
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/reserved_words/values.yml
@@ -0,0 +1,7 @@
+values1:
+ id: 1
+ group_id: 2
+
+values2:
+ id: 2
+ group_id: 1
diff --git a/vendor/rails/activerecord/test/fixtures/ship.rb b/vendor/rails/activerecord/test/fixtures/ship.rb
new file mode 100644
index 00000000..05b09fc1
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/ship.rb
@@ -0,0 +1,3 @@
+class Ship < ActiveRecord::Base
+ self.record_timestamps = false
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/ships.yml b/vendor/rails/activerecord/test/fixtures/ships.yml
new file mode 100644
index 00000000..137055aa
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/ships.yml
@@ -0,0 +1,5 @@
+black_pearl:
+ name: "Black Pearl"
+interceptor:
+ id: 2
+ name: "Interceptor"
diff --git a/vendor/rails/activerecord/test/fixtures/tagging.rb b/vendor/rails/activerecord/test/fixtures/tagging.rb
index 4695f075..a1fa1a97 100644
--- a/vendor/rails/activerecord/test/fixtures/tagging.rb
+++ b/vendor/rails/activerecord/test/fixtures/tagging.rb
@@ -1,3 +1,7 @@
+# test that attr_readonly isn't called on the :taggable polymorphic association
+module Taggable
+end
+
class Tagging < ActiveRecord::Base
belongs_to :tag, :include => :tagging
belongs_to :super_tag, :class_name => 'Tag', :foreign_key => 'super_tag_id'
diff --git a/vendor/rails/activerecord/test/fixtures/taggings.yml b/vendor/rails/activerecord/test/fixtures/taggings.yml
index 617210d6..2213b154 100644
--- a/vendor/rails/activerecord/test/fixtures/taggings.yml
+++ b/vendor/rails/activerecord/test/fixtures/taggings.yml
@@ -15,4 +15,11 @@ fake:
id: 3
tag_id: 1
taggable_id: 1
- taggable_type: FakeModel
\ No newline at end of file
+ taggable_type: FakeModel
+
+godfather:
+ id: 4
+ tag_id: 1
+ taggable_id: 1
+ taggable_type: Item
+
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/topic.rb b/vendor/rails/activerecord/test/fixtures/topic.rb
index d7cd52e3..b2541c86 100755
--- a/vendor/rails/activerecord/test/fixtures/topic.rb
+++ b/vendor/rails/activerecord/test/fixtures/topic.rb
@@ -13,8 +13,14 @@ class Topic < ActiveRecord::Base
def topic_id
id
end
+
protected
+ def approved=(val)
+ @custom_approved = val
+ write_attribute(:approved, val)
+ end
+
def default_written_on
self.written_on = Time.now unless attribute_present?("written_on")
end
@@ -22,4 +28,10 @@ class Topic < ActiveRecord::Base
def destroy_children
self.class.delete_all "parent_id = #{id}"
end
-end
+
+ def after_initialize
+ if self.new_record?
+ self.author_email_address = 'test@test.com'
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/fixtures/treasure.rb b/vendor/rails/activerecord/test/fixtures/treasure.rb
new file mode 100644
index 00000000..7a429e2a
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/treasure.rb
@@ -0,0 +1,4 @@
+class Treasure < ActiveRecord::Base
+ has_and_belongs_to_many :parrots
+ belongs_to :looter, :polymorphic => true
+end
diff --git a/vendor/rails/activerecord/test/fixtures/treasures.yml b/vendor/rails/activerecord/test/fixtures/treasures.yml
new file mode 100644
index 00000000..9db15798
--- /dev/null
+++ b/vendor/rails/activerecord/test/fixtures/treasures.yml
@@ -0,0 +1,10 @@
+diamond:
+ name: $LABEL
+
+sapphire:
+ name: $LABEL
+ looter: redbeard (Pirate)
+
+ruby:
+ name: $LABEL
+ looter: louis (Parrot)
diff --git a/vendor/rails/activerecord/test/fixtures_test.rb b/vendor/rails/activerecord/test/fixtures_test.rb
index 2a9c78e4..deeb391e 100755
--- a/vendor/rails/activerecord/test/fixtures_test.rb
+++ b/vendor/rails/activerecord/test/fixtures_test.rb
@@ -1,5 +1,8 @@
require 'abstract_unit'
+require 'fixtures/post'
+require 'fixtures/binary'
require 'fixtures/topic'
+require 'fixtures/computer'
require 'fixtures/developer'
require 'fixtures/company'
require 'fixtures/task'
@@ -7,6 +10,11 @@ require 'fixtures/reply'
require 'fixtures/joke'
require 'fixtures/course'
require 'fixtures/category'
+require 'fixtures/parrot'
+require 'fixtures/pirate'
+require 'fixtures/treasure'
+require 'fixtures/matey'
+require 'fixtures/ship'
class FixturesTest < Test::Unit::TestCase
self.use_instantiated_fixtures = true
@@ -49,15 +57,18 @@ class FixturesTest < Test::Unit::TestCase
def test_inserts
topics = create_fixtures("topics")
- firstRow = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'David'")
- assert_equal("The First Topic", firstRow["title"])
+ first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'David'")
+ assert_equal("The First Topic", first_row["title"])
- secondRow = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'Mary'")
- assert_nil(secondRow["author_email_address"])
+ second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'Mary'")
+ assert_nil(second_row["author_email_address"])
end
if ActiveRecord::Base.connection.supports_migrations?
def test_inserts_with_pre_and_suffix
+ # Reset cache to make finds on the new table work
+ Fixtures.reset_cache
+
ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
t.column :title, :string
t.column :author_name, :string
@@ -82,15 +93,15 @@ class FixturesTest < Test::Unit::TestCase
topics = create_fixtures("topics")
- firstRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
- assert_equal("The First Topic", firstRow["title"])
+ first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
+ assert_equal("The First Topic", first_row["title"])
- secondRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
- assert_nil(secondRow["author_email_address"])
+ second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
+ assert_nil(second_row["author_email_address"])
ensure
# Restore prefix/suffix to its previous values
- ActiveRecord::Base.table_name_prefix = old_prefix
- ActiveRecord::Base.table_name_suffix = old_suffix
+ ActiveRecord::Base.table_name_prefix = old_prefix
+ ActiveRecord::Base.table_name_suffix = old_suffix
ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
end
@@ -187,7 +198,7 @@ class FixturesTest < Test::Unit::TestCase
def test_binary_in_fixtures
assert_equal 1, @binaries.size
- data = File.read(BINARY_FIXTURE_PATH).freeze
+ data = File.open(BINARY_FIXTURE_PATH, "rb").read.freeze
assert_equal data, @flowers.data
end
end
@@ -199,6 +210,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
def setup
@instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')]
+ Fixtures.reset_cache # make sure tables get reinitialized
end
def test_resets_to_min_pk_with_specified_pk_and_sequence
@@ -223,7 +235,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
end
end
- def test_create_fixtures_resets_sequences
+ def test_create_fixtures_resets_sequences_when_not_cached
@instances.each do |instance|
max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)|
fixture_id = fixture['id'].to_i
@@ -238,7 +250,6 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
end
end
-
class FixturesWithoutInstantiationTest < Test::Unit::TestCase
self.use_instantiated_fixtures = false
fixtures :topics, :developers, :accounts
@@ -259,8 +270,20 @@ class FixturesWithoutInstantiationTest < Test::Unit::TestCase
assert_equal "Jamis", developers(:jamis).name
assert_equal 50, accounts(:signals37).credit_limit
end
-end
+ def test_accessor_methods_with_multiple_args
+ assert_equal 2, topics(:first, :second).size
+ assert_raise(StandardError) { topics([:first, :second]) }
+ end
+
+ uses_mocha 'reloading_fixtures_through_accessor_methods' do
+ def test_reloading_fixtures_through_accessor_methods
+ assert_equal "The First Topic", topics(:first).title
+ @loaded_fixtures['topics']['first'].expects(:find).returns(stub(:title => "Fresh Topic!"))
+ assert_equal "Fresh Topic!", topics(:first, true).title
+ end
+ end
+end
class FixturesWithoutInstanceInstantiationTest < Test::Unit::TestCase
self.use_instantiated_fixtures = true
@@ -276,7 +299,6 @@ class FixturesWithoutInstanceInstantiationTest < Test::Unit::TestCase
end
end
-
class TransactionalFixturesTest < Test::Unit::TestCase
self.use_instantiated_fixtures = true
self.use_transactional_fixtures = true
@@ -293,7 +315,6 @@ class TransactionalFixturesTest < Test::Unit::TestCase
end
end
-
class MultipleFixturesTest < Test::Unit::TestCase
fixtures :topics
fixtures :developers, :accounts
@@ -303,6 +324,21 @@ class MultipleFixturesTest < Test::Unit::TestCase
end
end
+# This is to reproduce a bug where if a TestCase is loaded
+# twice by Ruby, it loses its fixture setup hook.
+class_def = <<-CODE
+ class DoubleLoadedTestCase < Test::Unit::TestCase
+ fixtures :topics
+
+ def setup
+ end
+
+ def test_should_properly_setup_fixtures
+ assert_nothing_raised { topics(:first) }
+ end
+ end
+CODE
+2.times { eval(class_def) }
class OverlappingFixturesTest < Test::Unit::TestCase
fixtures :topics, :developers
@@ -313,7 +349,6 @@ class OverlappingFixturesTest < Test::Unit::TestCase
end
end
-
class ForeignKeyFixturesTest < Test::Unit::TestCase
fixtures :fk_test_has_pk, :fk_test_has_fk
@@ -333,7 +368,7 @@ end
class SetTableNameFixturesTest < Test::Unit::TestCase
set_fixture_class :funny_jokes => 'Joke'
fixtures :funny_jokes
-
+
def test_table_method
assert_kind_of Joke, funny_jokes(:a_joke)
end
@@ -342,7 +377,7 @@ end
class CustomConnectionFixturesTest < Test::Unit::TestCase
set_fixture_class :courses => Course
fixtures :courses
-
+
def test_connection
assert_kind_of Course, courses(:ruby)
assert_equal Course.connection, courses(:ruby).connection
@@ -368,17 +403,15 @@ class CheckEscapedYamlFixturesTest < Test::Unit::TestCase
end
end
-class DevelopersProject; end;
-
+class DevelopersProject; end
class ManyToManyFixturesWithClassDefined < Test::Unit::TestCase
fixtures :developers_projects
-
+
def test_this_should_run_cleanly
assert true
end
end
-
class FixturesBrokenRollbackTest < Test::Unit::TestCase
def blank_setup; end
alias_method :ar_setup_with_fixtures, :setup_with_fixtures
@@ -403,3 +436,167 @@ class FixturesBrokenRollbackTest < Test::Unit::TestCase
raise 'argh'
end
end
+
+class LoadAllFixturesTest < Test::Unit::TestCase
+ self.fixture_path= File.join(File.dirname(__FILE__), '/fixtures/all')
+ fixtures :all
+
+ def test_all_there
+ assert_equal %w(developers people tasks), fixture_table_names.sort
+ end
+end
+
+class FasterFixturesTest < Test::Unit::TestCase
+ fixtures :categories, :authors
+
+ def load_extra_fixture(name)
+ fixture = create_fixtures(name)
+ assert fixture.is_a?(Fixtures)
+ @loaded_fixtures[fixture.table_name] = fixture
+ end
+
+ def test_cache
+ assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'categories')
+ assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'authors')
+
+ assert_no_queries do
+ create_fixtures('categories')
+ create_fixtures('authors')
+ end
+
+ load_extra_fixture('posts')
+ assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
+ self.class.setup_fixture_accessors('posts')
+ assert_equal 'Welcome to the weblog', posts(:welcome).title
+ end
+end
+
+class FoxyFixturesTest < Test::Unit::TestCase
+ fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, :developers
+
+ def test_identifies_strings
+ assert_equal(Fixtures.identify("foo"), Fixtures.identify("foo"))
+ assert_not_equal(Fixtures.identify("foo"), Fixtures.identify("FOO"))
+ end
+
+ def test_identifies_symbols
+ assert_equal(Fixtures.identify(:foo), Fixtures.identify(:foo))
+ end
+
+ TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
+
+ def test_populates_timestamp_columns
+ TIMESTAMP_COLUMNS.each do |property|
+ assert_not_nil(parrots(:george).send(property), "should set #{property}")
+ end
+ end
+
+ def test_does_not_populate_timestamp_columns_if_model_has_set_record_timestamps_to_false
+ TIMESTAMP_COLUMNS.each do |property|
+ assert_nil(ships(:black_pearl).send(property), "should not set #{property}")
+ end
+ end
+
+ def test_populates_all_columns_with_the_same_time
+ last = nil
+
+ TIMESTAMP_COLUMNS.each do |property|
+ current = parrots(:george).send(property)
+ last ||= current
+
+ assert_equal(last, current)
+ last = current
+ end
+ end
+
+ def test_only_populates_columns_that_exist
+ assert_not_nil(pirates(:blackbeard).created_on)
+ assert_not_nil(pirates(:blackbeard).updated_on)
+ end
+
+ def test_preserves_existing_fixture_data
+ assert_equal(2.weeks.ago.to_date, pirates(:redbeard).created_on.to_date)
+ assert_equal(2.weeks.ago.to_date, pirates(:redbeard).updated_on.to_date)
+ end
+
+ def test_generates_unique_ids
+ assert_not_nil(parrots(:george).id)
+ assert_not_equal(parrots(:george).id, parrots(:louis).id)
+ end
+
+ def test_automatically_sets_primary_key
+ assert_not_nil(ships(:black_pearl))
+ end
+
+ def test_preserves_existing_primary_key
+ assert_equal(2, ships(:interceptor).id)
+ end
+
+ def test_resolves_belongs_to_symbols
+ assert_equal(parrots(:george), pirates(:blackbeard).parrot)
+ end
+
+ def test_ignores_belongs_to_symbols_if_association_and_foreign_key_are_named_the_same
+ assert_equal(developers(:david), computers(:workstation).developer)
+ end
+
+ def test_supports_join_tables
+ assert(pirates(:blackbeard).parrots.include?(parrots(:george)))
+ assert(pirates(:blackbeard).parrots.include?(parrots(:louis)))
+ assert(parrots(:george).pirates.include?(pirates(:blackbeard)))
+ end
+
+ def test_supports_inline_habtm
+ assert(parrots(:george).treasures.include?(treasures(:diamond)))
+ assert(parrots(:george).treasures.include?(treasures(:sapphire)))
+ assert(!parrots(:george).treasures.include?(treasures(:ruby)))
+ end
+
+ def test_supports_inline_habtm_with_specified_id
+ assert(parrots(:polly).treasures.include?(treasures(:ruby)))
+ assert(parrots(:polly).treasures.include?(treasures(:sapphire)))
+ assert(!parrots(:polly).treasures.include?(treasures(:diamond)))
+ end
+
+ def test_supports_yaml_arrays
+ assert(parrots(:louis).treasures.include?(treasures(:diamond)))
+ assert(parrots(:louis).treasures.include?(treasures(:sapphire)))
+ end
+
+ def test_strips_DEFAULTS_key
+ assert_raise(StandardError) { parrots(:DEFAULTS) }
+
+ # this lets us do YAML defaults and not have an extra fixture entry
+ %w(sapphire ruby).each { |t| assert(parrots(:davey).treasures.include?(treasures(t))) }
+ end
+
+ def test_supports_label_interpolation
+ assert_equal("frederick", parrots(:frederick).name)
+ end
+
+ def test_supports_polymorphic_belongs_to
+ assert_equal(pirates(:redbeard), treasures(:sapphire).looter)
+ assert_equal(parrots(:louis), treasures(:ruby).looter)
+ end
+
+ def test_only_generates_a_pk_if_necessary
+ m = Matey.find(:first)
+ m.pirate = pirates(:blackbeard)
+ m.target = pirates(:redbeard)
+ end
+
+ def test_supports_sti
+ assert_kind_of DeadParrot, parrots(:polly)
+ assert_equal pirates(:blackbeard), parrots(:polly).killer
+ end
+end
+
+class ActiveSupportSubclassWithFixturesTest < ActiveSupport::TestCase
+ fixtures :parrots
+
+ # This seemingly useless assertion catches a bug that caused the fixtures
+ # setup code call nil[]
+ def test_foo
+ assert_equal parrots(:louis), Parrot.find_by_name("King Louis")
+ end
+end
diff --git a/vendor/rails/activerecord/test/inheritance_test.rb b/vendor/rails/activerecord/test/inheritance_test.rb
index 8b0d70cf..2d0a834b 100755
--- a/vendor/rails/activerecord/test/inheritance_test.rb
+++ b/vendor/rails/activerecord/test/inheritance_test.rb
@@ -6,6 +6,13 @@ require 'fixtures/subscriber'
class InheritanceTest < Test::Unit::TestCase
fixtures :companies, :projects, :subscribers, :accounts
+ def test_company_descends_from_active_record
+ assert_raise(NoMethodError) { ActiveRecord::Base.descends_from_active_record? }
+ assert AbstractCompany.descends_from_active_record?, 'AbstractCompany should descend from ActiveRecord::Base'
+ assert Company.descends_from_active_record?, 'Company should descend from ActiveRecord::Base'
+ assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base'
+ end
+
def test_a_bad_type_column
#SQLServer need to turn Identity Insert On before manually inserting into the Identity column
if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
@@ -144,7 +151,6 @@ class InheritanceTest < Test::Unit::TestCase
switch_to_alt_inheritance_column
test_eager_load_belongs_to_something_inherited
switch_to_default_inheritance_column
- ActiveRecord::Base.logger.debug "cocksucker"
end
def test_inheritance_without_mapping
diff --git a/vendor/rails/activerecord/test/json_serialization_test.rb b/vendor/rails/activerecord/test/json_serialization_test.rb
new file mode 100644
index 00000000..5aeb24b2
--- /dev/null
+++ b/vendor/rails/activerecord/test/json_serialization_test.rb
@@ -0,0 +1,180 @@
+require 'abstract_unit'
+require 'fixtures/contact'
+require 'fixtures/post'
+require 'fixtures/author'
+require 'fixtures/tagging'
+require 'fixtures/tag'
+require 'fixtures/comment'
+
+class JsonSerializationTest < Test::Unit::TestCase
+ def setup
+ @contact = Contact.new(
+ :name => 'Konata Izumi',
+ :age => 16,
+ :avatar => 'binarydata',
+ :created_at => Time.utc(2006, 8, 1),
+ :awesome => true,
+ :preferences => { :shows => 'anime' }
+ )
+ end
+
+ def test_should_encode_all_encodable_attributes
+ json = @contact.to_json
+
+ assert_match %r{"name": "Konata Izumi"}, json
+ assert_match %r{"age": 16}, json
+ assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_match %r{"awesome": true}, json
+ assert_match %r{"preferences": \{"shows": "anime"\}}, json
+ end
+
+ def test_should_allow_attribute_filtering_with_only
+ json = @contact.to_json(:only => [:name, :age])
+
+ assert_match %r{"name": "Konata Izumi"}, json
+ assert_match %r{"age": 16}, json
+ assert_no_match %r{"awesome": true}, json
+ assert !json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_no_match %r{"preferences": \{"shows": "anime"\}}, json
+ end
+
+ def test_should_allow_attribute_filtering_with_except
+ json = @contact.to_json(:except => [:name, :age])
+
+ assert_no_match %r{"name": "Konata Izumi"}, json
+ assert_no_match %r{"age": 16}, json
+ assert_match %r{"awesome": true}, json
+ assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_match %r{"preferences": \{"shows": "anime"\}}, json
+ end
+
+ def test_methods_are_called_on_object
+ # Define methods on fixture.
+ def @contact.label; "Has cheezburger"; end
+ def @contact.favorite_quote; "Constraints are liberating"; end
+
+ # Single method.
+ assert_match %r{"label": "Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
+
+ # Both methods.
+ methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
+ assert_match %r{"label": "Has cheezburger"}, methods_json
+ assert_match %r{"favorite_quote": "Constraints are liberating"}, methods_json
+ end
+end
+
+class DatabaseConnectedJsonEncodingTest < Test::Unit::TestCase
+ fixtures :authors, :posts, :comments, :tags, :taggings
+
+ def setup
+ @david = authors(:david)
+ @mary = authors(:mary)
+ end
+
+ def test_includes_uses_association_name
+ json = @david.to_json(:include => :posts)
+
+ assert_match %r{"posts": \[}, json
+
+ assert_match %r{"id": 1}, json
+ assert_match %r{"name": "David"}, json
+
+ assert_match %r{"author_id": 1}, json
+ assert_match %r{"title": "Welcome to the weblog"}, json
+ assert_match %r{"body": "Such a lovely day"}, json
+
+ assert_match %r{"title": "So I was thinking"}, json
+ assert_match %r{"body": "Like I hopefully always am"}, json
+ end
+
+ def test_includes_uses_association_name_and_applies_attribute_filters
+ json = @david.to_json(:include => { :posts => { :only => :title } })
+
+ assert_match %r{"name": "David"}, json
+ assert_match %r{"posts": \[}, json
+
+ assert_match %r{"title": "Welcome to the weblog"}, json
+ assert_no_match %r{"body": "Such a lovely day"}, json
+
+ assert_match %r{"title": "So I was thinking"}, json
+ assert_no_match %r{"body": "Like I hopefully always am"}, json
+ end
+
+ def test_includes_fetches_second_level_associations
+ json = @david.to_json(:include => { :posts => { :include => { :comments => { :only => :body } } } })
+
+ assert_match %r{"name": "David"}, json
+ assert_match %r{"posts": \[}, json
+
+ assert_match %r{"comments": \[}, json
+ assert_match %r{\{"body": "Thank you again for the welcome"\}}, json
+ assert_match %r{\{"body": "Don't think too hard"\}}, json
+ assert_no_match %r{"post_id": }, json
+ end
+
+ def test_includes_fetches_nth_level_associations
+ json = @david.to_json(
+ :include => {
+ :posts => {
+ :include => {
+ :taggings => {
+ :include => {
+ :tag => { :only => :name }
+ }
+ }
+ }
+ }
+ })
+
+ assert_match %r{"name": "David"}, json
+ assert_match %r{"posts": \[}, json
+
+ assert_match %r{"taggings": \[}, json
+ assert_match %r{"tag": \{"name": "General"\}}, json
+ end
+
+ def test_should_not_call_methods_on_associations_that_dont_respond
+ def @david.favorite_quote; "Constraints are liberating"; end
+ json = @david.to_json(:include => :posts, :methods => :favorite_quote)
+
+ assert !@david.posts.first.respond_to?(:favorite_quote)
+ assert_match %r{"favorite_quote": "Constraints are liberating"}, json
+ assert_equal %r{"favorite_quote": }.match(json).size, 1
+ end
+
+ def test_should_allow_only_option_for_list_of_authors
+ authors = [@david, @mary]
+
+ assert_equal %([{"name": "David"}, {"name": "Mary"}]), authors.to_json(:only => :name)
+ end
+
+ def test_should_allow_except_option_for_list_of_authors
+ authors = [@david, @mary]
+
+ assert_equal %([{"id": 1}, {"id": 2}]), authors.to_json(:except => [:name, :author_address_id])
+ end
+
+ def test_should_allow_includes_for_list_of_authors
+ authors = [@david, @mary]
+ json = authors.to_json(
+ :only => :name,
+ :include => {
+ :posts => { :only => :id }
+ }
+ )
+
+ ['"name": "David"', '"posts": [', '{"id": 1}', '{"id": 2}', '{"id": 4}',
+ '{"id": 5}', '{"id": 6}', '"name": "Mary"', '"posts": [{"id": 7}]'].each do |fragment|
+ assert json.include?(fragment), json
+ end
+ end
+
+ def test_should_allow_options_for_hash_of_authors
+ authors_hash = {
+ 1 => @david,
+ 2 => @mary
+ }
+
+ assert_equal %({1: {"name": "David"}}), authors_hash.to_json(:only => [1, :name])
+ end
+end
diff --git a/vendor/rails/activerecord/test/lifecycle_test.rb b/vendor/rails/activerecord/test/lifecycle_test.rb
index 2fdeb418..5f9e12a7 100755
--- a/vendor/rails/activerecord/test/lifecycle_test.rb
+++ b/vendor/rails/activerecord/test/lifecycle_test.rb
@@ -27,7 +27,7 @@ class TopicManualObserver
end
class TopicaObserver < ActiveRecord::Observer
- def self.observed_class() Topic end
+ observe :topic
attr_reader :topic
diff --git a/vendor/rails/activerecord/test/locking_test.rb b/vendor/rails/activerecord/test/locking_test.rb
index 471771bf..9a3b76e8 100644
--- a/vendor/rails/activerecord/test/locking_test.rb
+++ b/vendor/rails/activerecord/test/locking_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'fixtures/person'
+require 'fixtures/reader'
require 'fixtures/legacy_thing'
class LockWithoutDefault < ActiveRecord::Base; end
@@ -9,9 +10,18 @@ class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
set_locking_column :custom_lock_version
end
+class ReadonlyFirstNamePerson < Person
+ attr_readonly :first_name
+end
+
class OptimisticLockingTest < Test::Unit::TestCase
fixtures :people, :legacy_things
+ # need to disable transactional fixtures, because otherwise the sqlite3
+ # adapter (at least) chokes when we try and change the schema in the middle
+ # of a test (see test_increment_counter_*).
+ self.use_transactional_fixtures = false
+
def test_lock_existing
p1 = Person.find(1)
p2 = Person.find(1)
@@ -24,6 +34,20 @@ class OptimisticLockingTest < Test::Unit::TestCase
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
end
+
+ def test_lock_repeating
+ p1 = Person.find(1)
+ p2 = Person.find(1)
+ assert_equal 0, p1.lock_version
+ assert_equal 0, p2.lock_version
+
+ p1.save!
+ assert_equal 1, p1.lock_version
+ assert_equal 0, p2.lock_version
+
+ assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
+ assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
+ end
def test_lock_new
p1 = Person.new(:first_name => 'anika')
@@ -40,6 +64,15 @@ class OptimisticLockingTest < Test::Unit::TestCase
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
end
+
+ def test_lock_new_with_nil
+ p1 = Person.new(:first_name => 'anika')
+ p1.save!
+ p1.lock_version = nil # simulate bad fixture or column with no default
+ p1.save!
+ assert_equal 1, p1.lock_version
+ end
+
def test_lock_column_name_existing
t1 = LegacyThing.find(1)
@@ -73,6 +106,65 @@ class OptimisticLockingTest < Test::Unit::TestCase
t1 = LockWithCustomColumnWithoutDefault.new
assert_equal 0, t1.custom_lock_version
end
+
+ def test_readonly_attributes
+ assert_equal Set.new([ 'first_name' ]), ReadonlyFirstNamePerson.readonly_attributes
+
+ p = ReadonlyFirstNamePerson.create(:first_name => "unchangeable name")
+ p.reload
+ assert_equal "unchangeable name", p.first_name
+
+ p.update_attributes(:first_name => "changed name")
+ p.reload
+ assert_equal "unchangeable name", p.first_name
+ end
+
+ { :lock_version => Person, :custom_lock_version => LegacyThing }.each do |name, model|
+ define_method("test_increment_counter_updates_#{name}") do
+ counter_test model, 1 do |id|
+ model.increment_counter :test_count, id
+ end
+ end
+
+ define_method("test_decrement_counter_updates_#{name}") do
+ counter_test model, -1 do |id|
+ model.decrement_counter :test_count, id
+ end
+ end
+
+ define_method("test_update_counters_updates_#{name}") do
+ counter_test model, 1 do |id|
+ model.update_counters id, :test_count => 1
+ end
+ end
+ end
+
+ private
+
+ def add_counter_column_to(model)
+ model.connection.add_column model.table_name, :test_count, :integer, :null => false, :default => 0
+ model.reset_column_information
+ # OpenBase does not set a value to existing rows when adding a not null default column
+ model.update_all(:test_count => 0) if current_adapter?(:OpenBaseAdapter)
+ end
+
+ def remove_counter_column_from(model)
+ model.connection.remove_column model.table_name, :test_count
+ model.reset_column_information
+ end
+
+ def counter_test(model, expected_count)
+ add_counter_column_to(model)
+ object = model.find(:first)
+ assert_equal 0, object.test_count
+ assert_equal 0, object.send(model.locking_column)
+ yield object.id
+ object.reload
+ assert_equal expected_count, object.test_count
+ assert_equal 1, object.send(model.locking_column)
+ ensure
+ remove_counter_column_from(model)
+ end
end
@@ -81,9 +173,9 @@ end
# blocks, so separate script called by Kernel#system is needed.
# (See exec vs. async_exec in the PostgreSQL adapter.)
-# TODO: The SQL Server and Sybase adapters currently have no support for pessimistic locking
+# TODO: The SQL Server, Sybase, and OpenBase adapters currently have no support for pessimistic locking
-unless current_adapter?(:SQLServerAdapter, :SybaseAdapter)
+unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :OpenBaseAdapter)
class PessimisticLockingTest < Test::Unit::TestCase
self.use_transactional_fixtures = false
fixtures :people, :readers
diff --git a/vendor/rails/activerecord/test/migration_test.rb b/vendor/rails/activerecord/test/migration_test.rb
index 8ae97c71..64b8d51f 100644
--- a/vendor/rails/activerecord/test/migration_test.rb
+++ b/vendor/rails/activerecord/test/migration_test.rb
@@ -24,6 +24,8 @@ if ActiveRecord::Base.connection.supports_migrations?
class MigrationTest < Test::Unit::TestCase
self.use_transactional_fixtures = false
+
+ fixtures :people
def setup
ActiveRecord::Migration.verbose = true
@@ -40,7 +42,7 @@ if ActiveRecord::Base.connection.supports_migrations?
Reminder.reset_column_information
%w(last_name key bio age height wealth birthday favorite_day
- male administrator).each do |column|
+ moment_of_truth male administrator funny).each do |column|
Person.connection.remove_column('people', column) rescue nil
end
Person.connection.remove_column("people", "first_name") rescue nil
@@ -59,7 +61,8 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
# Orcl nds shrt indx nms. Sybs 2.
- unless current_adapter?(:OracleAdapter, :SybaseAdapter)
+ # OpenBase does not have named indexes. You must specify a single column name
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter)
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
@@ -72,11 +75,15 @@ if ActiveRecord::Base.connection.supports_migrations?
# quoting
# Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
- assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
- assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
-
+ # OpenBase does not have named indexes. You must specify a single column name
+ unless current_adapter?(:OpenBaseAdapter)
+ assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
+ end
+
# Sybase adapter does not support indexes on :boolean columns
- unless current_adapter?(:SybaseAdapter)
+ # OpenBase does not have named indexes. You must specify a single column
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
end
@@ -108,11 +115,15 @@ if ActiveRecord::Base.connection.supports_migrations?
end
def test_create_table_with_defaults
+ # MySQL doesn't allow defaults on TEXT or BLOB columns.
+ mysql = current_adapter?(:MysqlAdapter)
+
Person.connection.create_table :testings do |t|
t.column :one, :string, :default => "hello"
t.column :two, :boolean, :default => true
t.column :three, :boolean, :default => false
t.column :four, :integer, :default => 1
+ t.column :five, :text, :default => "hello" unless mysql
end
columns = Person.connection.columns(:testings)
@@ -120,11 +131,13 @@ if ActiveRecord::Base.connection.supports_migrations?
two = columns.detect { |c| c.name == "two" }
three = columns.detect { |c| c.name == "three" }
four = columns.detect { |c| c.name == "four" }
+ five = columns.detect { |c| c.name == "five" } unless mysql
assert_equal "hello", one.default
assert_equal true, two.default
assert_equal false, three.default
assert_equal 1, four.default
+ assert_equal "hello", five.default unless mysql
ensure
Person.connection.drop_table :testings rescue nil
@@ -167,9 +180,8 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.connection.drop_table :testings rescue nil
end
- # SQL Server and Sybase will not allow you to add a NOT NULL column
- # to a table without specifying a default value, so the
- # following test must be skipped
+ # SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
+ # column to a table without a default value.
unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :SQLiteAdapter)
def test_add_column_not_null_without_default
Person.connection.create_table :testings do |t|
@@ -197,7 +209,12 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
assert_raises(ActiveRecord::StatementInvalid) do
- Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
+ unless current_adapter?(:OpenBaseAdapter)
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
+ else
+ Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
+ "Testing Insert","id",2)
+ end
end
ensure
Person.connection.drop_table :testings rescue nil
@@ -207,8 +224,6 @@ if ActiveRecord::Base.connection.supports_migrations?
# functionality. This allows us to more easily catch INSERT being broken,
# but SELECT actually working fine.
def test_native_decimal_insert_manual_vs_automatic
- # SQLite3 always uses float in violation of SQL
- # 16 decimal places
correct_value = '0012345678901234567890.0123456789'.to_d
Person.delete_all
@@ -218,6 +233,8 @@ if ActiveRecord::Base.connection.supports_migrations?
# Do a manual insertion
if current_adapter?(:OracleAdapter)
Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
+ elsif current_adapter?(:OpenBaseAdapter)
+ Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
else
Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
end
@@ -230,6 +247,7 @@ if ActiveRecord::Base.connection.supports_migrations?
unless current_adapter?(:SQLite3Adapter)
assert_equal correct_value, row.wealth
end
+
# Reset to old state
Person.delete_all
@@ -244,6 +262,7 @@ if ActiveRecord::Base.connection.supports_migrations?
unless current_adapter?(:SQLite3Adapter)
assert_equal correct_value, row.wealth
end
+
# Reset to old state
Person.connection.del_column "people", "wealth" rescue nil
Person.reset_column_information
@@ -258,10 +277,19 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
Person.connection.add_column "people", "birthday", :datetime
Person.connection.add_column "people", "favorite_day", :date
+ Person.connection.add_column "people", "moment_of_truth", :datetime
Person.connection.add_column "people", "male", :boolean
- assert_nothing_raised { Person.create :first_name => 'bob', :last_name => 'bobsen', :bio => "I was born ....", :age => 18, :height => 1.78, :wealth => BigDecimal.new("12345678901234567890.0123456789"), :birthday => 18.years.ago, :favorite_day => 10.days.ago, :male => true }
- bob = Person.find(:first)
+ Person.reset_column_information
+ assert_nothing_raised do
+ Person.create :first_name => 'bob', :last_name => 'bobsen',
+ :bio => "I was born ....", :age => 18, :height => 1.78,
+ :wealth => BigDecimal.new("12345678901234567890.0123456789"),
+ :birthday => 18.years.ago, :favorite_day => 10.days.ago,
+ :moment_of_truth => "1782-10-10 21:40:18", :male => true
+ end
+
+ bob = Person.find(:first)
assert_equal 'bob', bob.first_name
assert_equal 'bobsen', bob.last_name
assert_equal "I was born ....", bob.bio
@@ -269,10 +297,11 @@ if ActiveRecord::Base.connection.supports_migrations?
# Test for 30 significent digits (beyond the 16 of float), 10 of them
# after the decimal place.
+
unless current_adapter?(:SQLite3Adapter)
assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
end
-
+
assert_equal true, bob.male?
assert_equal String, bob.first_name.class
@@ -288,10 +317,34 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_equal Date, bob.favorite_day.class
end
+ # Test DateTime column and defaults, including timezone.
+ # FIXME: moment of truth may be Time on 64-bit platforms.
+ if bob.moment_of_truth.is_a?(DateTime)
+ assert_equal DateTime.now.offset, bob.moment_of_truth.offset
+ assert_not_equal 0, bob.moment_of_truth.offset
+ assert_not_equal "Z", bob.moment_of_truth.zone
+ assert_equal DateTime::ITALY, bob.moment_of_truth.start
+ end
+
assert_equal TrueClass, bob.male?.class
assert_kind_of BigDecimal, bob.wealth
end
+ if current_adapter?(:MysqlAdapter)
+ def test_unabstracted_database_dependent_types
+ Person.delete_all
+
+ ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
+ Person.reset_column_information
+ Person.create :intelligence_quotient => 300
+ jonnyg = Person.find(:first)
+ assert_equal 127, jonnyg.intelligence_quotient
+ jonnyg.destroy
+ ensure
+ ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
+ end
+ end
+
def test_add_remove_single_field_using_string_arguments
assert !Person.column_methods_hash.include?(:last_name)
@@ -325,6 +378,7 @@ if ActiveRecord::Base.connection.supports_migrations?
begin
Person.connection.add_column "people", "girlfriend", :string
+ Person.reset_column_information
Person.create :girlfriend => 'bobette'
Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
@@ -342,9 +396,11 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_rename_column_using_symbol_arguments
begin
+ names_before = Person.find(:all).map(&:first_name)
Person.connection.rename_column :people, :first_name, :nick_name
Person.reset_column_information
assert Person.column_names.include?("nick_name")
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
ensure
Person.connection.remove_column("people","nick_name")
Person.connection.add_column("people","first_name", :string)
@@ -353,15 +409,38 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_rename_column
begin
+ names_before = Person.find(:all).map(&:first_name)
Person.connection.rename_column "people", "first_name", "nick_name"
Person.reset_column_information
assert Person.column_names.include?("nick_name")
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
ensure
Person.connection.remove_column("people","nick_name")
Person.connection.add_column("people","first_name", :string)
end
end
+ def test_rename_column_with_sql_reserved_word
+ begin
+ assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
+ Person.reset_column_information
+ assert Person.column_names.include?("group")
+ ensure
+ Person.connection.remove_column("people", "group") rescue nil
+ Person.connection.add_column("people", "first_name", :string) rescue nil
+ end
+ end
+
+ def test_change_type_of_not_null_column
+ assert_nothing_raised do
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
+ Topic.reset_column_information
+
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
+ Topic.reset_column_information
+ end
+ end
+
def test_rename_table
begin
ActiveRecord::Base.connection.create_table :octopuses do |t|
@@ -382,6 +461,19 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Base.connection.drop_table :octopi rescue nil
end
end
+
+ def test_change_column_nullability
+ Person.delete_all
+ Person.connection.add_column "people", "funny", :boolean
+ Person.reset_column_information
+ assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
+ Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
+ Person.reset_column_information
+ assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
+ Person.connection.change_column "people", "funny", :boolean, :null => true
+ Person.reset_column_information
+ assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
+ end
def test_rename_table_with_an_index
begin
@@ -420,7 +512,7 @@ if ActiveRecord::Base.connection.supports_migrations?
old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
- new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
+ new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
@@ -435,6 +527,8 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.reset_column_information
assert !Person.new.contributor?
assert_nil Person.new.contributor
+ ensure
+ Person.connection.remove_column("people", "contributor") rescue nil
end
def test_change_column_with_new_default
@@ -445,6 +539,8 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
Person.reset_column_information
assert !Person.new.administrator?
+ ensure
+ Person.connection.remove_column("people", "administrator") rescue nil
end
def test_change_column_default
@@ -452,7 +548,19 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.reset_column_information
assert_equal "Tester", Person.new.first_name
end
-
+
+ def test_change_column_quotes_column_names
+ Person.connection.create_table :testings do |t|
+ t.column :select, :string
+ end
+
+ assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
+
+ assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
+ ensure
+ Person.connection.drop_table :testings rescue nil
+ end
+
def test_change_column_default_to_null
Person.connection.change_column_default "people", "first_name", nil
Person.reset_column_information
@@ -463,8 +571,8 @@ if ActiveRecord::Base.connection.supports_migrations?
assert !Reminder.table_exists?
WeNeedReminders.up
-
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
+
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
assert_equal "hello world", Reminder.find(:first).content
WeNeedReminders.down
@@ -506,7 +614,7 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
# This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
- # precision/scale explictly left out. By the SQL standard, numbers
+ # precision/scale explicitly left out. By the SQL standard, numbers
# assigned to this field should be truncated but that's seldom respected.
if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
# - PostgreSQL changes the SQL spec on columns declared simply as
@@ -685,29 +793,27 @@ if ActiveRecord::Base.connection.supports_migrations?
Reminder.reset_sequence_name
end
-# FrontBase does not support default values on BLOB/CLOB columns
- unless current_adapter?(:FrontBaseAdapter)
- def test_create_table_with_binary_column
- Person.connection.drop_table :binary_testings rescue nil
+ def test_create_table_with_binary_column
+ Person.connection.drop_table :binary_testings rescue nil
- assert_nothing_raised {
- Person.connection.create_table :binary_testings do |t|
- t.column "data", :binary, :default => "", :null => false
- end
- }
-
- columns = Person.connection.columns(:binary_testings)
- data_column = columns.detect { |c| c.name == "data" }
-
- if current_adapter?(:OracleAdapter)
- assert_equal "empty_blob()", data_column.default
- else
- assert_equal "", data_column.default
+ assert_nothing_raised {
+ Person.connection.create_table :binary_testings do |t|
+ t.column "data", :binary, :null => false
end
+ }
- Person.connection.drop_table :binary_testings rescue nil
+ columns = Person.connection.columns(:binary_testings)
+ data_column = columns.detect { |c| c.name == "data" }
+
+ if current_adapter?(:MysqlAdapter)
+ assert_equal '', data_column.default
+ else
+ assert_nil data_column.default
end
+
+ Person.connection.drop_table :binary_testings rescue nil
end
+
def test_migrator_with_duplicates
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
@@ -720,11 +826,12 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_equal 4, ActiveRecord::Migrator.current_version
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 2)
+ Person.reset_column_information
assert !Reminder.table_exists?
assert Person.column_methods_hash.include?(:last_name)
assert_equal 2, ActiveRecord::Migrator.current_version
end
-
+
def test_create_table_with_custom_sequence_name
return unless current_adapter? :OracleAdapter
@@ -761,7 +868,66 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.connection.execute("select suitably_short_seq.nextval from dual")
end
end
-
end
+
+ uses_mocha 'Sexy migration tests' do
+ class SexyMigrationsTest < Test::Unit::TestCase
+ def test_references_column_type_adds_id
+ with_new_table do |t|
+ t.expects(:column).with('customer_id', :integer, {})
+ t.references :customer
+ end
+ end
+
+ def test_references_column_type_with_polymorphic_adds_type
+ with_new_table do |t|
+ t.expects(:column).with('taggable_type', :string, {})
+ t.expects(:column).with('taggable_id', :integer, {})
+ t.references :taggable, :polymorphic => true
+ end
+ end
+
+ def test_belongs_to_works_like_references
+ with_new_table do |t|
+ t.expects(:column).with('customer_id', :integer, {})
+ t.belongs_to :customer
+ end
+ end
+
+ def test_timestamps_creates_updated_at_and_created_at
+ with_new_table do |t|
+ t.expects(:column).with(:created_at, :datetime)
+ t.expects(:column).with(:updated_at, :datetime)
+ t.timestamps
+ end
+ end
+
+ def test_integer_creates_integer_column
+ with_new_table do |t|
+ t.expects(:column).with(:foo, 'integer', {})
+ t.expects(:column).with(:bar, 'integer', {})
+ t.integer :foo, :bar
+ end
+ end
+
+ def test_string_creates_string_column
+ with_new_table do |t|
+ t.expects(:column).with(:foo, 'string', {})
+ t.expects(:column).with(:bar, 'string', {})
+ t.string :foo, :bar
+ end
+ end
+
+ protected
+ def with_new_table
+ Person.connection.create_table :delete_me do |t|
+ yield t
+ end
+ ensure
+ Person.connection.drop_table :delete_me rescue nil
+ end
+
+ end # SexyMigrationsTest
+ end # uses_mocha
end
diff --git a/vendor/rails/activerecord/test/mixin_nested_set_test.rb b/vendor/rails/activerecord/test/mixin_nested_set_test.rb
deleted file mode 100644
index 7ed9075b..00000000
--- a/vendor/rails/activerecord/test/mixin_nested_set_test.rb
+++ /dev/null
@@ -1,196 +0,0 @@
-require 'abstract_unit'
-require 'active_record/acts/nested_set'
-require 'fixtures/mixin'
-require 'pp'
-
-class MixinNestedSetTest < Test::Unit::TestCase
- fixtures :mixins
-
- def test_mixing_in_methods
- ns = NestedSet.new
- assert( ns.respond_to?( :all_children ) )
- assert_equal( ns.scope_condition, "root_id IS NULL" )
-
- check_method_mixins ns
- end
-
- def test_string_scope
- ns = NestedSetWithStringScope.new
-
- ns.root_id = 1
- assert_equal( ns.scope_condition, "root_id = 1" )
- ns.root_id = 42
- assert_equal( ns.scope_condition, "root_id = 42" )
- check_method_mixins ns
- end
-
- def test_symbol_scope
- ns = NestedSetWithSymbolScope.new
- ns.root_id = 1
- assert_equal( ns.scope_condition, "root_id = 1" )
- ns.root_id = 42
- assert_equal( ns.scope_condition, "root_id = 42" )
- check_method_mixins ns
- end
-
- def check_method_mixins( obj )
- [:scope_condition, :left_col_name, :right_col_name, :parent_column, :root?, :add_child,
- :children_count, :full_set, :all_children, :direct_children].each { |symbol| assert( obj.respond_to?(symbol)) }
- end
-
- def set( id )
- NestedSet.find( 3000 + id )
- end
-
- def test_adding_children
- assert( set(1).unknown? )
- assert( set(2).unknown? )
- set(1).add_child set(2)
-
- # Did we maintain adding the parent_ids?
- assert( set(1).root? )
- assert( set(2).child? )
- assert( set(2).parent_id == set(1).id )
-
- # Check boundies
- assert_equal( set(1).lft, 1 )
- assert_equal( set(2).lft, 2 )
- assert_equal( set(2).rgt, 3 )
- assert_equal( set(1).rgt, 4 )
-
- # Check children cound
- assert_equal( set(1).children_count, 1 )
-
- set(1).add_child set(3)
-
- #check boundries
- assert_equal( set(1).lft, 1 )
- assert_equal( set(2).lft, 2 )
- assert_equal( set(2).rgt, 3 )
- assert_equal( set(3).lft, 4 )
- assert_equal( set(3).rgt, 5 )
- assert_equal( set(1).rgt, 6 )
-
- # How is the count looking?
- assert_equal( set(1).children_count, 2 )
-
- set(2).add_child set(4)
-
- # boundries
- assert_equal( set(1).lft, 1 )
- assert_equal( set(2).lft, 2 )
- assert_equal( set(4).lft, 3 )
- assert_equal( set(4).rgt, 4 )
- assert_equal( set(2).rgt, 5 )
- assert_equal( set(3).lft, 6 )
- assert_equal( set(3).rgt, 7 )
- assert_equal( set(1).rgt, 8 )
-
- # Children count
- assert_equal( set(1).children_count, 3 )
- assert_equal( set(2).children_count, 1 )
- assert_equal( set(3).children_count, 0 )
- assert_equal( set(4).children_count, 0 )
-
- set(2).add_child set(5)
- set(4).add_child set(6)
-
- assert_equal( set(2).children_count, 3 )
-
-
- # Children accessors
- assert_equal( set(1).full_set.length, 6 )
- assert_equal( set(2).full_set.length, 4 )
- assert_equal( set(4).full_set.length, 2 )
-
- assert_equal( set(1).all_children.length, 5 )
- assert_equal( set(6).all_children.length, 0 )
-
- assert_equal( set(1).direct_children.length, 2 )
-
- end
-
- def test_snipping_tree
- big_tree = NestedSetWithStringScope.find( 4001 )
-
- # Make sure we have the right one
- assert_equal( 3, big_tree.direct_children.length )
- assert_equal( 10, big_tree.full_set.length )
-
- NestedSetWithStringScope.find( 4005 ).destroy
-
- big_tree = NestedSetWithStringScope.find( 4001 )
-
- assert_equal( 7, big_tree.full_set.length )
- assert_equal( 2, big_tree.direct_children.length )
-
- assert_equal( 1, NestedSetWithStringScope.find(4001).lft )
- assert_equal( 2, NestedSetWithStringScope.find(4002).lft )
- assert_equal( 3, NestedSetWithStringScope.find(4003).lft )
- assert_equal( 4, NestedSetWithStringScope.find(4003).rgt )
- assert_equal( 5, NestedSetWithStringScope.find(4004).lft )
- assert_equal( 6, NestedSetWithStringScope.find(4004).rgt )
- assert_equal( 7, NestedSetWithStringScope.find(4002).rgt )
- assert_equal( 8, NestedSetWithStringScope.find(4008).lft )
- assert_equal( 9, NestedSetWithStringScope.find(4009).lft )
- assert_equal(10, NestedSetWithStringScope.find(4009).rgt )
- assert_equal(11, NestedSetWithStringScope.find(4010).lft )
- assert_equal(12, NestedSetWithStringScope.find(4010).rgt )
- assert_equal(13, NestedSetWithStringScope.find(4008).rgt )
- assert_equal(14, NestedSetWithStringScope.find(4001).rgt )
- end
-
- def test_deleting_root
- NestedSetWithStringScope.find(4001).destroy
-
- assert( NestedSetWithStringScope.count == 0 )
- end
-
- def test_common_usage
- mixins(:set_1).add_child( mixins(:set_2) )
- assert_equal( 1, mixins(:set_1).direct_children.length )
-
- mixins(:set_2).add_child( mixins(:set_3) )
- assert_equal( 1, mixins(:set_1).direct_children.length )
-
- # Local cache is now out of date!
- # Problem: the update_alls update all objects up the tree
- mixins(:set_1).reload
- assert_equal( 2, mixins(:set_1).all_children.length )
-
- assert_equal( 1, mixins(:set_1).lft )
- assert_equal( 2, mixins(:set_2).lft )
- assert_equal( 3, mixins(:set_3).lft )
- assert_equal( 4, mixins(:set_3).rgt )
- assert_equal( 5, mixins(:set_2).rgt )
- assert_equal( 6, mixins(:set_1).rgt )
-
- assert( mixins(:set_1).root? )
-
- begin
- mixins(:set_4).add_child( mixins(:set_1) )
- fail
- rescue
- end
-
- assert_equal( 2, mixins(:set_1).all_children.length )
-
- mixins(:set_1).add_child mixins(:set_4)
-
- assert_equal( 3, mixins(:set_1).all_children.length )
- end
-
- def test_inheritance
- parent = mixins(:sti_set_3100)
- child = mixins(:sti_set_3101)
- grandchild = mixins(:sti_set_3102)
- assert_equal 5, parent.full_set.size
- assert_equal 2, child.full_set.size
- assert_equal 4, parent.all_children.size
- assert_equal 1, child.all_children.size
- assert_equal 2, parent.direct_children.size
- assert_equal 1, child.direct_children.size
- child.destroy
- assert_equal 3, parent.full_set.size
- end
-end
diff --git a/vendor/rails/activerecord/test/mixin_test.rb b/vendor/rails/activerecord/test/mixin_test.rb
index e10650e7..d2118e67 100644
--- a/vendor/rails/activerecord/test/mixin_test.rb
+++ b/vendor/rails/activerecord/test/mixin_test.rb
@@ -1,8 +1,7 @@
require 'abstract_unit'
-require 'active_record/acts/tree'
-require 'active_record/acts/list'
-require 'active_record/acts/nested_set'
-require 'fixtures/mixin'
+
+class Mixin < ActiveRecord::Base
+end
# Let us control what Time.now returns for the TouchTest suite
class Time
@@ -21,354 +20,6 @@ class Time
end
end
-class ListTest < Test::Unit::TestCase
- fixtures :mixins
-
- def test_reordering
- assert_equal [mixins(:list_1),
- mixins(:list_2),
- mixins(:list_3),
- mixins(:list_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_2).move_lower
-
- assert_equal [mixins(:list_1),
- mixins(:list_3),
- mixins(:list_2),
- mixins(:list_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_2).move_higher
-
- assert_equal [mixins(:list_1),
- mixins(:list_2),
- mixins(:list_3),
- mixins(:list_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_1).move_to_bottom
-
- assert_equal [mixins(:list_2),
- mixins(:list_3),
- mixins(:list_4),
- mixins(:list_1)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_1).move_to_top
-
- assert_equal [mixins(:list_1),
- mixins(:list_2),
- mixins(:list_3),
- mixins(:list_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
-
- mixins(:list_2).move_to_bottom
-
- assert_equal [mixins(:list_1),
- mixins(:list_3),
- mixins(:list_4),
- mixins(:list_2)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_4).move_to_top
-
- assert_equal [mixins(:list_4),
- mixins(:list_1),
- mixins(:list_3),
- mixins(:list_2)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- end
-
- def test_move_to_bottom_with_next_to_last_item
- assert_equal [mixins(:list_1),
- mixins(:list_2),
- mixins(:list_3),
- mixins(:list_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_3).move_to_bottom
-
- assert_equal [mixins(:list_1),
- mixins(:list_2),
- mixins(:list_4),
- mixins(:list_3)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
- end
-
- def test_next_prev
- assert_equal mixins(:list_2), mixins(:list_1).lower_item
- assert_nil mixins(:list_1).higher_item
- assert_equal mixins(:list_3), mixins(:list_4).higher_item
- assert_nil mixins(:list_4).lower_item
- end
-
-
- def test_injection
- item = ListMixin.new("parent_id"=>1)
- assert_equal "parent_id = 1", item.scope_condition
- assert_equal "pos", item.position_column
- end
-
- def test_insert
- new = ListMixin.create("parent_id"=>20)
- assert_equal 1, new.pos
- assert new.first?
- assert new.last?
-
- new = ListMixin.create("parent_id"=>20)
- assert_equal 2, new.pos
- assert !new.first?
- assert new.last?
-
- new = ListMixin.create("parent_id"=>20)
- assert_equal 3, new.pos
- assert !new.first?
- assert new.last?
-
- new = ListMixin.create("parent_id"=>0)
- assert_equal 1, new.pos
- assert new.first?
- assert new.last?
- end
-
- def test_insert_at
- new = ListMixin.create("parent_id" => 20)
- assert_equal 1, new.pos
-
- new = ListMixin.create("parent_id" => 20)
- assert_equal 2, new.pos
-
- new = ListMixin.create("parent_id" => 20)
- assert_equal 3, new.pos
-
- new4 = ListMixin.create("parent_id" => 20)
- assert_equal 4, new4.pos
-
- new4.insert_at(3)
- assert_equal 3, new4.pos
-
- new.reload
- assert_equal 4, new.pos
-
- new.insert_at(2)
- assert_equal 2, new.pos
-
- new4.reload
- assert_equal 4, new4.pos
-
- new5 = ListMixin.create("parent_id" => 20)
- assert_equal 5, new5.pos
-
- new5.insert_at(1)
- assert_equal 1, new5.pos
-
- new4.reload
- assert_equal 5, new4.pos
- end
-
- def test_delete_middle
- assert_equal [mixins(:list_1),
- mixins(:list_2),
- mixins(:list_3),
- mixins(:list_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_2).destroy
-
- assert_equal [mixins(:list_1, :reload),
- mixins(:list_3, :reload),
- mixins(:list_4, :reload)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- assert_equal 1, mixins(:list_1).pos
- assert_equal 2, mixins(:list_3).pos
- assert_equal 3, mixins(:list_4).pos
-
- mixins(:list_1).destroy
-
- assert_equal [mixins(:list_3, :reload),
- mixins(:list_4, :reload)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- assert_equal 1, mixins(:list_3).pos
- assert_equal 2, mixins(:list_4).pos
-
- end
-
- def test_with_string_based_scope
- new = ListWithStringScopeMixin.create("parent_id"=>500)
- assert_equal 1, new.pos
- assert new.first?
- assert new.last?
- end
-
- def test_nil_scope
- new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create
- new2.move_higher
- assert_equal [new2, new1, new3], ListMixin.find(:all, :conditions => 'parent_id IS NULL', :order => 'pos')
- end
-
- def test_remove_from_list_should_then_fail_in_list?
- assert_equal true, mixins(:list_1).in_list?
- mixins(:list_1).remove_from_list
- assert_equal false, mixins(:list_1).in_list?
- end
-
- def test_remove_from_list_should_set_position_to_nil
- assert_equal [mixins(:list_1),
- mixins(:list_2),
- mixins(:list_3),
- mixins(:list_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_2).remove_from_list
-
- assert_equal [mixins(:list_2, :reload),
- mixins(:list_1, :reload),
- mixins(:list_3, :reload),
- mixins(:list_4, :reload)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- assert_equal 1, mixins(:list_1).pos
- assert_equal nil, mixins(:list_2).pos
- assert_equal 2, mixins(:list_3).pos
- assert_equal 3, mixins(:list_4).pos
- end
-
- def test_remove_before_destroy_does_not_shift_lower_items_twice
- assert_equal [mixins(:list_1),
- mixins(:list_2),
- mixins(:list_3),
- mixins(:list_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- mixins(:list_2).remove_from_list
- mixins(:list_2).destroy
-
- assert_equal [mixins(:list_1, :reload),
- mixins(:list_3, :reload),
- mixins(:list_4, :reload)],
- ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos')
-
- assert_equal 1, mixins(:list_1).pos
- assert_equal 2, mixins(:list_3).pos
- assert_equal 3, mixins(:list_4).pos
- end
-
-end
-
-class TreeTest < Test::Unit::TestCase
- fixtures :mixins
-
- def test_has_child
- assert_deprecated 'has_children?' do
- assert_equal true, mixins(:tree_1).has_children?
- assert_equal true, mixins(:tree_2).has_children?
- assert_equal false, mixins(:tree_3).has_children?
- assert_equal false, mixins(:tree_4).has_children?
- end
- end
-
- def test_children
- assert_equal mixins(:tree_1).children, [mixins(:tree_2), mixins(:tree_4)]
- assert_equal mixins(:tree_2).children, [mixins(:tree_3)]
- assert_equal mixins(:tree_3).children, []
- assert_equal mixins(:tree_4).children, []
- end
-
- def test_has_parent
- assert_deprecated 'has_parent?' do
- assert_equal false, mixins(:tree_1).has_parent?
- assert_equal true, mixins(:tree_2).has_parent?
- assert_equal true, mixins(:tree_3).has_parent?
- assert_equal true, mixins(:tree_4).has_parent?
- end
- end
-
- def test_parent
- assert_equal mixins(:tree_2).parent, mixins(:tree_1)
- assert_equal mixins(:tree_2).parent, mixins(:tree_4).parent
- assert_nil mixins(:tree_1).parent
- end
-
- def test_delete
- assert_equal 6, TreeMixin.count
- mixins(:tree_1).destroy
- assert_equal 2, TreeMixin.count
- mixins(:tree2_1).destroy
- mixins(:tree3_1).destroy
- assert_equal 0, TreeMixin.count
- end
-
- def test_insert
- @extra = mixins(:tree_1).children.create
-
- assert @extra
-
- assert_equal @extra.parent, mixins(:tree_1)
-
- assert_equal 3, mixins(:tree_1).children.size
- assert mixins(:tree_1).children.include?(@extra)
- assert mixins(:tree_1).children.include?(mixins(:tree_2))
- assert mixins(:tree_1).children.include?(mixins(:tree_4))
- end
-
- def test_ancestors
- assert_equal [], mixins(:tree_1).ancestors
- assert_equal [mixins(:tree_1)], mixins(:tree_2).ancestors
- assert_equal [mixins(:tree_2), mixins(:tree_1)], mixins(:tree_3).ancestors
- assert_equal [mixins(:tree_1)], mixins(:tree_4).ancestors
- assert_equal [], mixins(:tree2_1).ancestors
- assert_equal [], mixins(:tree3_1).ancestors
- end
-
- def test_root
- assert_equal mixins(:tree_1), TreeMixin.root
- assert_equal mixins(:tree_1), mixins(:tree_1).root
- assert_equal mixins(:tree_1), mixins(:tree_2).root
- assert_equal mixins(:tree_1), mixins(:tree_3).root
- assert_equal mixins(:tree_1), mixins(:tree_4).root
- assert_equal mixins(:tree2_1), mixins(:tree2_1).root
- assert_equal mixins(:tree3_1), mixins(:tree3_1).root
- end
-
- def test_roots
- assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], TreeMixin.roots
- end
-
- def test_siblings
- assert_equal [mixins(:tree2_1), mixins(:tree3_1)], mixins(:tree_1).siblings
- assert_equal [mixins(:tree_4)], mixins(:tree_2).siblings
- assert_equal [], mixins(:tree_3).siblings
- assert_equal [mixins(:tree_2)], mixins(:tree_4).siblings
- assert_equal [mixins(:tree_1), mixins(:tree3_1)], mixins(:tree2_1).siblings
- assert_equal [mixins(:tree_1), mixins(:tree2_1)], mixins(:tree3_1).siblings
- end
-
- def test_self_and_siblings
- assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], mixins(:tree_1).self_and_siblings
- assert_equal [mixins(:tree_2), mixins(:tree_4)], mixins(:tree_2).self_and_siblings
- assert_equal [mixins(:tree_3)], mixins(:tree_3).self_and_siblings
- assert_equal [mixins(:tree_2), mixins(:tree_4)], mixins(:tree_4).self_and_siblings
- assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], mixins(:tree2_1).self_and_siblings
- assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], mixins(:tree3_1).self_and_siblings
- end
-end
-
-class TreeTestWithoutOrder < Test::Unit::TestCase
- fixtures :mixins
-
- def test_root
- assert [mixins(:tree_without_order_1), mixins(:tree_without_order_2)].include?(TreeMixinWithoutOrder.root)
- end
-
- def test_roots
- assert_equal [], [mixins(:tree_without_order_1), mixins(:tree_without_order_2)] - TreeMixinWithoutOrder.roots
- end
-end
class TouchTest < Test::Unit::TestCase
fixtures :mixins
@@ -427,171 +78,18 @@ class TouchTest < Test::Unit::TestCase
def test_create_turned_off
Mixin.record_timestamps = false
- assert_nil mixins(:tree_1).updated_at
- mixins(:tree_1).save
- assert_nil mixins(:tree_1).updated_at
+ mixin = Mixin.new
+ assert_nil mixin.updated_at
+ mixin.save
+ assert_nil mixin.updated_at
+
+ # Make sure Mixin.record_timestamps gets reset, even if this test fails,
+ # so that other tests do not fail because Mixin.record_timestamps == false
+ rescue Exception => e
+ raise e
+ ensure
Mixin.record_timestamps = true
end
end
-
-
-class ListSubTest < Test::Unit::TestCase
- fixtures :mixins
-
- def test_reordering
- assert_equal [mixins(:list_sub_1),
- mixins(:list_sub_2),
- mixins(:list_sub_3),
- mixins(:list_sub_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- mixins(:list_sub_2).move_lower
-
- assert_equal [mixins(:list_sub_1),
- mixins(:list_sub_3),
- mixins(:list_sub_2),
- mixins(:list_sub_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- mixins(:list_sub_2).move_higher
-
- assert_equal [mixins(:list_sub_1),
- mixins(:list_sub_2),
- mixins(:list_sub_3),
- mixins(:list_sub_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- mixins(:list_sub_1).move_to_bottom
-
- assert_equal [mixins(:list_sub_2),
- mixins(:list_sub_3),
- mixins(:list_sub_4),
- mixins(:list_sub_1)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- mixins(:list_sub_1).move_to_top
-
- assert_equal [mixins(:list_sub_1),
- mixins(:list_sub_2),
- mixins(:list_sub_3),
- mixins(:list_sub_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
-
- mixins(:list_sub_2).move_to_bottom
-
- assert_equal [mixins(:list_sub_1),
- mixins(:list_sub_3),
- mixins(:list_sub_4),
- mixins(:list_sub_2)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- mixins(:list_sub_4).move_to_top
-
- assert_equal [mixins(:list_sub_4),
- mixins(:list_sub_1),
- mixins(:list_sub_3),
- mixins(:list_sub_2)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- end
-
- def test_move_to_bottom_with_next_to_last_item
- assert_equal [mixins(:list_sub_1),
- mixins(:list_sub_2),
- mixins(:list_sub_3),
- mixins(:list_sub_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- mixins(:list_sub_3).move_to_bottom
-
- assert_equal [mixins(:list_sub_1),
- mixins(:list_sub_2),
- mixins(:list_sub_4),
- mixins(:list_sub_3)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
- end
-
- def test_next_prev
- assert_equal mixins(:list_sub_2), mixins(:list_sub_1).lower_item
- assert_nil mixins(:list_sub_1).higher_item
- assert_equal mixins(:list_sub_3), mixins(:list_sub_4).higher_item
- assert_nil mixins(:list_sub_4).lower_item
- end
-
-
- def test_injection
- item = ListMixin.new("parent_id"=>1)
- assert_equal "parent_id = 1", item.scope_condition
- assert_equal "pos", item.position_column
- end
-
-
- def test_insert_at
- new = ListMixin.create("parent_id" => 20)
- assert_equal 1, new.pos
-
- new = ListMixinSub1.create("parent_id" => 20)
- assert_equal 2, new.pos
-
- new = ListMixinSub2.create("parent_id" => 20)
- assert_equal 3, new.pos
-
- new4 = ListMixin.create("parent_id" => 20)
- assert_equal 4, new4.pos
-
- new4.insert_at(3)
- assert_equal 3, new4.pos
-
- new.reload
- assert_equal 4, new.pos
-
- new.insert_at(2)
- assert_equal 2, new.pos
-
- new4.reload
- assert_equal 4, new4.pos
-
- new5 = ListMixinSub1.create("parent_id" => 20)
- assert_equal 5, new5.pos
-
- new5.insert_at(1)
- assert_equal 1, new5.pos
-
- new4.reload
- assert_equal 5, new4.pos
- end
-
- def test_delete_middle
- assert_equal [mixins(:list_sub_1),
- mixins(:list_sub_2),
- mixins(:list_sub_3),
- mixins(:list_sub_4)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- mixins(:list_sub_2).destroy
-
- assert_equal [mixins(:list_sub_1, :reload),
- mixins(:list_sub_3, :reload),
- mixins(:list_sub_4, :reload)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- assert_equal 1, mixins(:list_sub_1).pos
- assert_equal 2, mixins(:list_sub_3).pos
- assert_equal 3, mixins(:list_sub_4).pos
-
- mixins(:list_sub_1).destroy
-
- assert_equal [mixins(:list_sub_3, :reload),
- mixins(:list_sub_4, :reload)],
- ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos')
-
- assert_equal 1, mixins(:list_sub_3).pos
- assert_equal 2, mixins(:list_sub_4).pos
-
- end
-
-end
-
diff --git a/vendor/rails/activerecord/test/pk_test.rb b/vendor/rails/activerecord/test/pk_test.rb
index 5eaa6c47..431ff9af 100644
--- a/vendor/rails/activerecord/test/pk_test.rb
+++ b/vendor/rails/activerecord/test/pk_test.rb
@@ -83,11 +83,8 @@ class PrimaryKeysTest < Test::Unit::TestCase
def test_delete_should_quote_pkey
assert_nothing_raised { MixedCaseMonkey.delete(1) }
end
- def test_increment_counter_should_quote_pkey_and_quote_counter_columns
- assert_nothing_raised { MixedCaseMonkey.increment_counter(:fleaCount, 1) }
- end
- def test_decrement_counter_should_quote_pkey_and_quote_counter_columns
- assert_nothing_raised { MixedCaseMonkey.decrement_counter(:fleaCount, 1) }
+ def test_update_counters_should_quote_pkey_and_quote_counter_columns
+ assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) }
end
def test_find_with_one_id_should_quote_pkey
assert_nothing_raised { MixedCaseMonkey.find(1) }
@@ -98,7 +95,7 @@ class PrimaryKeysTest < Test::Unit::TestCase
def test_instance_update_should_quote_pkey
assert_nothing_raised { MixedCaseMonkey.find(1).save }
end
- def test_instance_destry_should_quote_pkey
+ def test_instance_destroy_should_quote_pkey
assert_nothing_raised { MixedCaseMonkey.find(1).destroy }
end
end
diff --git a/vendor/rails/activerecord/test/query_cache_test.rb b/vendor/rails/activerecord/test/query_cache_test.rb
new file mode 100644
index 00000000..08a49215
--- /dev/null
+++ b/vendor/rails/activerecord/test/query_cache_test.rb
@@ -0,0 +1,104 @@
+require 'abstract_unit'
+require 'fixtures/topic'
+require 'fixtures/reply'
+require 'fixtures/task'
+require 'fixtures/course'
+
+
+class QueryCacheTest < Test::Unit::TestCase
+ fixtures :tasks, :topics
+
+ def test_find_queries
+ assert_queries(2) { Task.find(1); Task.find(1) }
+ end
+
+ def test_find_queries_with_cache
+ Task.cache do
+ assert_queries(1) { Task.find(1); Task.find(1) }
+ end
+ end
+
+ def test_count_queries_with_cache
+ Task.cache do
+ assert_queries(1) { Task.count; Task.count }
+ end
+ end
+
+ def test_query_cache_dups_results_correctly
+ Task.cache do
+ now = Time.now.utc
+ task = Task.find 1
+ assert_not_equal now, task.starting
+ task.starting = now
+ task.reload
+ assert_not_equal now, task.starting
+ end
+ end
+
+ def test_cache_is_flat
+ Task.cache do
+ Topic.columns # don't count this query
+ assert_queries(1) { Topic.find(1); Topic.find(1); }
+ end
+
+ ActiveRecord::Base.cache do
+ assert_queries(1) { Task.find(1); Task.find(1) }
+ end
+ end
+
+ def test_cache_does_not_wrap_string_results_in_arrays
+ Task.cache do
+ assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
+ end
+ end
+end
+
+uses_mocha 'QueryCacheExpiryTest' do
+
+class QueryCacheExpiryTest < Test::Unit::TestCase
+ fixtures :tasks
+
+ def test_find
+ Task.connection.expects(:clear_query_cache).times(1)
+
+ assert !Task.connection.query_cache_enabled
+ Task.cache do
+ assert Task.connection.query_cache_enabled
+ Task.find(1)
+
+ Task.uncached do
+ assert !Task.connection.query_cache_enabled
+ Task.find(1)
+ end
+
+ assert Task.connection.query_cache_enabled
+ end
+ assert !Task.connection.query_cache_enabled
+ end
+
+ def test_update
+ Task.connection.expects(:clear_query_cache).times(2)
+
+ Task.cache do
+ Task.find(1).save!
+ end
+ end
+
+ def test_destroy
+ Task.connection.expects(:clear_query_cache).times(2)
+
+ Task.cache do
+ Task.find(1).destroy
+ end
+ end
+
+ def test_insert
+ ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
+
+ Task.cache do
+ Task.create!
+ end
+ end
+end
+
+end
diff --git a/vendor/rails/activerecord/test/reflection_test.rb b/vendor/rails/activerecord/test/reflection_test.rb
index 796076bc..1ed5c261 100644
--- a/vendor/rails/activerecord/test/reflection_test.rb
+++ b/vendor/rails/activerecord/test/reflection_test.rb
@@ -60,6 +60,13 @@ class ReflectionTest < Test::Unit::TestCase
assert_equal :integer, @first.column_for_attribute("id").type
end
+ def test_reflection_klass_for_nested_class_name
+ reflection = ActiveRecord::Reflection::MacroReflection.new(nil, nil, { :class_name => 'MyApplication::Business::Company' }, nil)
+ assert_nothing_raised do
+ assert_equal MyApplication::Business::Company, reflection.klass
+ end
+ end
+
def test_aggregation_reflection
reflection_for_address = ActiveRecord::Reflection::AggregateReflection.new(
:composed_of, :address, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer
@@ -104,6 +111,15 @@ class ReflectionTest < Test::Unit::TestCase
assert_equal 'accounts', Firm.reflect_on_association(:account).table_name
end
+ def test_belongs_to_inferred_foreign_key_from_assoc_name
+ Company.belongs_to :foo
+ assert_equal "foo_id", Company.reflect_on_association(:foo).primary_key_name
+ Company.belongs_to :bar, :class_name => "Xyzzy"
+ assert_equal "bar_id", Company.reflect_on_association(:bar).primary_key_name
+ Company.belongs_to :baz, :class_name => "Xyzzy", :foreign_key => "xyzzy_id"
+ assert_equal "xyzzy_id", Company.reflect_on_association(:baz).primary_key_name
+ end
+
def test_association_reflection_in_modules
assert_reflection MyApplication::Business::Firm,
:clients_of_firm,
diff --git a/vendor/rails/activerecord/test/reserved_word_test_mysql.rb b/vendor/rails/activerecord/test/reserved_word_test_mysql.rb
new file mode 100644
index 00000000..10a9fca1
--- /dev/null
+++ b/vendor/rails/activerecord/test/reserved_word_test_mysql.rb
@@ -0,0 +1,177 @@
+require "#{File.dirname(__FILE__)}/abstract_unit"
+
+class Group < ActiveRecord::Base
+ Group.table_name = 'group'
+ belongs_to :select, :class_name => 'Select'
+ has_one :values
+end
+
+class Select < ActiveRecord::Base
+ Select.table_name = 'select'
+ has_many :groups
+end
+
+class Values < ActiveRecord::Base
+ Values.table_name = 'values'
+end
+
+class Distinct < ActiveRecord::Base
+ Distinct.table_name = 'distinct'
+ has_and_belongs_to_many :selects
+ has_many :values, :through => :groups
+end
+
+# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
+# reserved word names (ie: group, order, values, etc...)
+class MysqlReservedWordTest < Test::Unit::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+
+ # we call execute directly here (and do similar below) because ActiveRecord::Base#create_table()
+ # will fail with these table names if these test cases fail
+
+ create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int',
+ 'select'=>'id int auto_increment primary key',
+ 'values'=>'id int auto_increment primary key, group_id int',
+ 'distinct'=>'id int auto_increment primary key',
+ 'distincts_selects'=>'distinct_id int, select_id int'
+ end
+
+ def teardown
+ drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order']
+ end
+
+ # create tables with reserved-word names and columns
+ def test_create_tables
+ assert_nothing_raised {
+ @connection.create_table :order do |t|
+ t.column :group, :string
+ end
+ }
+ end
+
+ # rename tables with reserved-word names
+ def test_rename_tables
+ assert_nothing_raised { @connection.rename_table(:group, :order) }
+ end
+
+ # alter column with a reserved-word name in a table with a reserved-word name
+ def test_change_columns
+ assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') }
+ #the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter
+ assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) }
+ assert_nothing_raised { @connection.rename_column(:group, :order, :values) }
+ end
+
+ # dump structure of table with reserved word name
+ def test_structure_dump
+ assert_nothing_raised { @connection.structure_dump }
+ end
+
+ # introspect table with reserved word name
+ def test_introspect
+ assert_nothing_raised { @connection.columns(:group) }
+ assert_nothing_raised { @connection.indexes(:group) }
+ end
+
+ #fixtures
+ self.use_instantiated_fixtures = true
+ self.use_transactional_fixtures = false
+
+ #fixtures :group
+
+ def test_fixtures
+ f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+
+ assert_nothing_raised {
+ f.each do |x|
+ x.delete_existing_fixtures
+ end
+ }
+
+ assert_nothing_raised {
+ f.each do |x|
+ x.insert_fixtures
+ end
+ }
+ end
+
+ #activerecord model class with reserved-word table name
+ def test_activerecord_model
+ create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+ x = nil
+ assert_nothing_raised { x = Group.new }
+ x.order = 'x'
+ assert_nothing_raised { x.save }
+ x.order = 'y'
+ assert_nothing_raised { x.save }
+ assert_nothing_raised { y = Group.find_by_order('y') }
+ assert_nothing_raised { y = Group.find(1) }
+ x = Group.find(1)
+ end
+
+ # has_one association with reserved-word table name
+ def test_has_one_associations
+ create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+ v = nil
+ assert_nothing_raised { v = Group.find(1).values }
+ assert_equal v.id, 2
+ end
+
+ # belongs_to association with reserved-word table name
+ def test_belongs_to_associations
+ create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+ gs = nil
+ assert_nothing_raised { gs = Select.find(2).groups }
+ assert_equal gs.length, 2
+ assert(gs.collect{|x| x.id}.sort == [2, 3])
+ end
+
+ # has_and_belongs_to_many with reserved-word table name
+ def test_has_and_belongs_to_many
+ create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
+ s = nil
+ assert_nothing_raised { s = Distinct.find(1).selects }
+ assert_equal s.length, 2
+ assert(s.collect{|x|x.id}.sort == [1, 2])
+ end
+
+ # activerecord model introspection with reserved-word table and column names
+ def test_activerecord_introspection
+ assert_nothing_raised { Group.table_exists? }
+ assert_nothing_raised { Group.columns }
+ end
+
+ # Calculations
+ def test_calculations_work_with_reserved_words
+ assert_nothing_raised { Group.count }
+ end
+
+ def test_associations_work_with_reserved_words
+ assert_nothing_raised { Select.find(:all, :include => [:groups]) }
+ end
+
+ #the following functions were added to DRY test cases
+
+ private
+ # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path
+ def create_test_fixtures(*fixture_names)
+ fixture_path = "./test/fixtures/reserved_words"
+ Fixtures.create_fixtures(fixture_path, fixture_names)
+ end
+
+ # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
+ def drop_tables_directly(table_names, connection = @connection)
+ table_names.each do |name|
+ connection.execute("DROP TABLE IF EXISTS `#{name}`")
+ end
+ end
+
+ # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
+ def create_tables_directly (tables, connection = @connection)
+ tables.each do |table_name, column_properties|
+ connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
+ end
+ end
+
+end
diff --git a/vendor/rails/activerecord/test/schema_dumper_test.rb b/vendor/rails/activerecord/test/schema_dumper_test.rb
index 5cae8da7..c6041e4f 100644
--- a/vendor/rails/activerecord/test/schema_dumper_test.rb
+++ b/vendor/rails/activerecord/test/schema_dumper_test.rb
@@ -19,6 +19,11 @@ if ActiveRecord::Base.connection.respond_to?(:tables)
assert_no_match %r{create_table "schema_info"}, output
end
+ def test_schema_dump_excludes_sqlite_sequence
+ output = standard_dump
+ assert_no_match %r{create_table "sqlite_sequence"}, output
+ end
+
def assert_line_up(lines, pattern, required = false)
return assert(true) if lines.empty?
matches = lines.map { |line| line.match(pattern) }
@@ -27,11 +32,27 @@ if ActiveRecord::Base.connection.respond_to?(:tables)
return assert(true) if matches.empty?
assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
end
+
+ def column_definition_lines(output = standard_dump)
+ output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
+ end
+
+ def test_types_line_up
+ column_definition_lines.each do |column_set|
+ next if column_set.empty?
+
+ lengths = column_set.map do |column|
+ if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/)
+ match[0].length
+ end
+ end
+
+ assert_equal 1, lengths.uniq.length
+ end
+ end
def test_arguments_line_up
- output = standard_dump
- output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }.each do |column_set|
- assert_line_up(column_set, /:(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)/, true)
+ column_definition_lines.each do |column_set|
assert_line_up(column_set, /:default => /)
assert_line_up(column_set, /:limit => /)
assert_line_up(column_set, /:null => /)
@@ -84,6 +105,20 @@ if ActiveRecord::Base.connection.respond_to?(:tables)
end
end
+ if current_adapter?(:MysqlAdapter)
+ def test_schema_dump_should_not_add_default_value_for_mysql_text_field
+ output = standard_dump
+ assert_match %r{t.text\s+"body",\s+:default => "",\s+:null => false$}, output
+ end
+
+ def test_mysql_schema_dump_should_honor_nonstandard_primary_keys
+ output = standard_dump
+ match = output.match(%r{create_table "movies"(.*)do})
+ assert_not_nil(match, "nonstandardpk table not found")
+ assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
+ end
+ end
+
def test_schema_dump_includes_decimal_options
stream = StringIO.new
ActiveRecord::SchemaDumper.ignore_tables = [/^[^n]/]
diff --git a/vendor/rails/activerecord/test/serialization_test.rb b/vendor/rails/activerecord/test/serialization_test.rb
new file mode 100644
index 00000000..a8295da2
--- /dev/null
+++ b/vendor/rails/activerecord/test/serialization_test.rb
@@ -0,0 +1,47 @@
+require 'abstract_unit'
+require 'fixtures/contact'
+
+class SerializationTest < Test::Unit::TestCase
+ FORMATS = [ :xml, :json ]
+
+ def setup
+ @contact_attributes = {
+ :name => 'aaron stack',
+ :age => 25,
+ :avatar => 'binarydata',
+ :created_at => Time.utc(2006, 8, 1),
+ :awesome => false,
+ :preferences => { :gem => 'ruby ' }
+ }
+
+ @contact = Contact.new(@contact_attributes)
+ end
+
+ def test_serialize_should_be_reversible
+ for format in FORMATS
+ @serialized = Contact.new.send("to_#{format}")
+ contact = Contact.new.send("from_#{format}", @serialized)
+
+ assert_equal @contact_attributes.keys.collect(&:to_s).sort, contact.attributes.keys.collect(&:to_s).sort, "For #{format}"
+ end
+ end
+
+ def test_serialize_should_allow_attribute_only_filtering
+ for format in FORMATS
+ @serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
+ contact = Contact.new.send("from_#{format}", @serialized)
+ assert_equal @contact_attributes[:name], contact.name, "For #{format}"
+ assert_nil contact.avatar, "For #{format}"
+ end
+ end
+
+ def test_serialize_should_allow_attribute_except_filtering
+ for format in FORMATS
+ @serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
+ contact = Contact.new.send("from_#{format}", @serialized)
+ assert_nil contact.name, "For #{format}"
+ assert_nil contact.age, "For #{format}"
+ assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activerecord/test/transactions_test.rb b/vendor/rails/activerecord/test/transactions_test.rb
index b0d1eb4e..a31d993d 100644
--- a/vendor/rails/activerecord/test/transactions_test.rb
+++ b/vendor/rails/activerecord/test/transactions_test.rb
@@ -81,32 +81,12 @@ class TransactionTest < Test::Unit::TestCase
assert @first.approved?, "First should still be changed in the objects"
assert !@second.approved?, "Second should still be changed in the objects"
-
+
assert !Topic.find(1).approved?, "First shouldn't have been approved"
assert Topic.find(2).approved?, "Second should still be approved"
end
-
- def test_failing_with_object_rollback
- assert !@first.approved?, "First should be unapproved initially"
- begin
- assert_deprecated /Object transactions/ do
- Topic.transaction(@first, @second) do
- @first.approved = true
- @second.approved = false
- @first.save
- @second.save
- raise "Bad things!"
- end
- end
- rescue
- # caught it
- end
-
- assert !@first.approved?, "First shouldn't have been approved"
- assert @second.approved?, "Second should still be approved"
- end
-
+
def test_callback_rollback_in_save
add_exception_raising_after_save_callback_to_topic
@@ -122,6 +102,38 @@ class TransactionTest < Test::Unit::TestCase
end
end
+ def test_callback_rollback_in_create
+ new_topic = Topic.new(
+ :title => "A new topic",
+ :author_name => "Ben",
+ :author_email_address => "ben@example.com",
+ :written_on => "2003-07-16t15:28:11.2233+01:00",
+ :last_read => "2004-04-15",
+ :bonus_time => "2005-01-30t15:28:00.00+01:00",
+ :content => "Have a nice day",
+ :approved => false)
+ new_record_snapshot = new_topic.new_record?
+ id_present = new_topic.has_attribute?(Topic.primary_key)
+ id_snapshot = new_topic.id
+
+ # Make sure the second save gets the after_create callback called.
+ 2.times do
+ begin
+ add_exception_raising_after_create_callback_to_topic
+ new_topic.approved = true
+ new_topic.save
+ flunk
+ rescue => e
+ assert_equal "Make the transaction rollback", e.message
+ assert_equal new_record_snapshot, new_topic.new_record?, "The topic should have its old new_record value"
+ assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
+ assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
+ ensure
+ remove_exception_raising_after_create_callback_to_topic
+ end
+ end
+ end
+
def test_nested_explicit_transactions
Topic.transaction do
Topic.transaction do
@@ -136,14 +148,53 @@ class TransactionTest < Test::Unit::TestCase
assert !Topic.find(2).approved?, "Second should have been unapproved"
end
+ def test_manually_rolling_back_a_transaction
+ Topic.transaction do
+ @first.approved = true
+ @second.approved = false
+ @first.save
+ @second.save
+
+ raise ActiveRecord::Rollback
+ end
+
+ assert @first.approved?, "First should still be changed in the objects"
+ assert !@second.approved?, "Second should still be changed in the objects"
+
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
+ assert Topic.find(2).approved?, "Second should still be approved"
+ end
+
+ uses_mocha 'mocking connection.commit_db_transaction' do
+ def test_rollback_when_commit_raises
+ Topic.connection.expects(:begin_db_transaction)
+ Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
+ Topic.connection.expects(:rollback_db_transaction)
+
+ assert_raise RuntimeError do
+ Topic.transaction do
+ # do nothing
+ end
+ end
+ end
+ end
+
private
def add_exception_raising_after_save_callback_to_topic
Topic.class_eval { def after_save() raise "Make the transaction rollback" end }
end
-
+
def remove_exception_raising_after_save_callback_to_topic
Topic.class_eval { remove_method :after_save }
end
+
+ def add_exception_raising_after_create_callback_to_topic
+ Topic.class_eval { def after_create() raise "Make the transaction rollback" end }
+ end
+
+ def remove_exception_raising_after_create_callback_to_topic
+ Topic.class_eval { remove_method :after_create }
+ end
end
if current_adapter?(:PostgreSQLAdapter)
diff --git a/vendor/rails/activerecord/test/unconnected_test.rb b/vendor/rails/activerecord/test/unconnected_test.rb
index 2d06410d..8079a051 100755
--- a/vendor/rails/activerecord/test/unconnected_test.rb
+++ b/vendor/rails/activerecord/test/unconnected_test.rb
@@ -3,7 +3,7 @@ require 'abstract_unit'
class TestRecord < ActiveRecord::Base
end
-class TestUnconnectedAdaptor < Test::Unit::TestCase
+class TestUnconnectedAdapter < Test::Unit::TestCase
self.use_transactional_fixtures = false
def setup
diff --git a/vendor/rails/activerecord/test/validations_test.rb b/vendor/rails/activerecord/test/validations_test.rb
index 93c9ad48..82b321a8 100755
--- a/vendor/rails/activerecord/test/validations_test.rb
+++ b/vendor/rails/activerecord/test/validations_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'fixtures/topic'
require 'fixtures/reply'
+require 'fixtures/person'
require 'fixtures/developer'
# The following methods in Topic are used in test_conditional_validation_*
@@ -14,6 +15,44 @@ class Topic
end
end
+class ProtectedPerson < ActiveRecord::Base
+ set_table_name 'people'
+ attr_accessor :addon
+ attr_protected :first_name
+end
+
+class UniqueReply < Reply
+ validates_uniqueness_of :content, :scope => 'parent_id'
+end
+
+class PlagiarizedReply < Reply
+ validates_acceptance_of :author_name
+end
+
+class SillyUniqueReply < UniqueReply
+end
+
+class Topic < ActiveRecord::Base
+ has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
+ has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
+end
+
+class Wizard < ActiveRecord::Base
+ self.abstract_class = true
+
+ validates_uniqueness_of :name
+end
+
+class IneptWizard < Wizard
+ validates_uniqueness_of :city
+end
+
+class Conjurer < IneptWizard
+end
+
+class Thaumaturgist < IneptWizard
+end
+
class ValidationsTest < Test::Unit::TestCase
fixtures :topics, :developers
@@ -88,12 +127,36 @@ class ValidationsTest < Test::Unit::TestCase
end
end
+ def test_exception_on_create_bang_many
+ assert_raises(ActiveRecord::RecordInvalid) do
+ Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
+ end
+ end
+
def test_scoped_create_without_attributes
Reply.with_scope(:create => {}) do
assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
end
end
+ def test_create_with_exceptions_using_scope_for_protected_attributes
+ assert_nothing_raised do
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
+ person = ProtectedPerson.create! :addon => "Addon"
+ assert_equal person.first_name, "Mary", "scope should ignore attr_protected"
+ end
+ end
+ end
+
+ def test_create_with_exceptions_using_scope_and_empty_attributes
+ assert_nothing_raised do
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
+ person = ProtectedPerson.create!
+ assert_equal person.first_name, "Mary", "should be ok when no attributes are passed to create!"
+ end
+ end
+ end
+
def test_single_error_per_attr_iteration
r = Reply.new
r.save
@@ -233,6 +296,19 @@ class ValidationsTest < Test::Unit::TestCase
assert t.save
end
+ def test_validates_acceptance_of_as_database_column
+ reply = PlagiarizedReply.create("author_name" => "Dan Brown")
+ assert_equal "Dan Brown", reply["author_name"]
+ end
+
+ def test_validates_acceptance_of_with_non_existant_table
+ Object.const_set :IncorporealModel, Class.new(ActiveRecord::Base)
+
+ assert_nothing_raised ActiveRecord::StatementInvalid do
+ IncorporealModel.validates_acceptance_of(:incorporeal_column)
+ end
+ end
+
def test_validate_presences
Topic.validates_presence_of(:title, :content)
@@ -289,6 +365,21 @@ class ValidationsTest < Test::Unit::TestCase
assert r3.valid?, "Saving r3"
end
+ def test_validate_uniqueness_scoped_to_defining_class
+ t = Topic.create("title" => "What, me worry?")
+
+ r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun"
+ assert r1.valid?, "Saving r1"
+
+ r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun"
+ assert !r2.valid?, "Saving r2"
+
+ # Should succeed as validates_uniqueness_of only applies to
+ # UniqueReply and its subclasses
+ r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun"
+ assert r3.valid?, "Saving r3"
+ end
+
def test_validate_uniqueness_with_scope_array
Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
@@ -344,6 +435,35 @@ class ValidationsTest < Test::Unit::TestCase
assert t2.save, "should save with nil"
end
+ def test_validate_straight_inheritance_uniqueness
+ w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
+ assert w1.valid?, "Saving w1"
+
+ # Should use validation from base class (which is abstract)
+ w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
+ assert !w2.valid?, "w2 shouldn't be valid"
+ assert w2.errors.on(:name), "Should have errors for name"
+ assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
+
+ w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
+ assert !w3.valid?, "w3 shouldn't be valid"
+ assert w3.errors.on(:name), "Should have errors for name"
+ assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
+
+ w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
+ assert w4.valid?, "Saving w4"
+
+ w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
+ assert !w5.valid?, "w5 shouldn't be valid"
+ assert w5.errors.on(:name), "Should have errors for name"
+ assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
+
+ w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
+ assert !w6.valid?, "w6 shouldn't be valid"
+ assert w6.errors.on(:city), "Should have errors for city"
+ assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
+ end
+
def test_validate_format
Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
@@ -419,6 +539,42 @@ class ValidationsTest < Test::Unit::TestCase
assert Topic.create("title" => nil, "content" => "abc").valid?
end
+ def test_numericality_with_getter_method
+ Developer.validates_numericality_of( :salary )
+ developer = Developer.new("name" => "michael", "salary" => nil)
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
+ assert developer.valid?
+ end
+
+ def test_validates_length_of_with_allow_nil
+ Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
+
+ assert !Topic.create("title" => "ab").valid?
+ assert !Topic.create("title" => "").valid?
+ assert Topic.create("title" => nil).valid?
+ assert Topic.create("title" => "abcde").valid?
+ end
+
+ def test_validates_length_of_with_allow_blank
+ Topic.validates_length_of( :title, :is => 5, :allow_blank=>true )
+
+ assert !Topic.create("title" => "ab").valid?
+ assert Topic.create("title" => "").valid?
+ assert Topic.create("title" => nil).valid?
+ assert Topic.create("title" => "abcde").valid?
+ end
+
+ def test_validates_inclusion_of_with_formatted_message
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %s is not in the list" )
+
+ assert Topic.create("title" => "a", "content" => "abc").valid?
+
+ t = Topic.create("title" => "uhoh", "content" => "abc")
+ assert !t.valid?
+ assert t.errors.on(:title)
+ assert_equal "option uhoh is not in the list", t.errors["title"]
+ end
+
def test_numericality_with_allow_nil_and_getter_method
Developer.validates_numericality_of( :salary, :allow_nil => true)
developer = Developer.new("name" => "michael", "salary" => nil)
@@ -433,6 +589,17 @@ class ValidationsTest < Test::Unit::TestCase
assert !Topic.create("title" => "monkey", "content" => "abc").valid?
end
+ def test_validates_exclusion_of_with_formatted_message
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option %s is restricted" )
+
+ assert Topic.create("title" => "something", "content" => "abc")
+
+ t = Topic.create("title" => "monkey")
+ assert !t.valid?
+ assert t.errors.on(:title)
+ assert_equal "option monkey is restricted", t.errors["title"]
+ end
+
def test_validates_length_of_using_minimum
Topic.validates_length_of :title, :minimum => 5
@@ -605,7 +772,7 @@ class ValidationsTest < Test::Unit::TestCase
end
end
- def test_validates_length_with_globaly_modified_error_message
+ def test_validates_length_with_globally_modified_error_message
ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre %d'
Topic.validates_length_of :title, :minimum => 10
t = Topic.create(:title => 'too short')
@@ -613,25 +780,13 @@ class ValidationsTest < Test::Unit::TestCase
assert_equal 'tu est trops petit hombre 10', t.errors['title']
end
-
- def test_add_on_boundary_breaking_is_deprecated
- t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
- class << t
- def validate
- errors.add_on_boundary_breaking('title', 1..6)
- end
- end
- assert_deprecated 'add_on_boundary_breaking' do
- assert !t.valid?
- end
- end
def test_validates_size_of_association
assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
assert !t.save
assert t.errors.on(:replies)
- t.replies.build('title' => 'areply', 'content' => 'whateveragain')
+ reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
assert t.valid?
end
@@ -832,19 +987,21 @@ class ValidationsTest < Test::Unit::TestCase
def test_validates_associated_many
Topic.validates_associated( :replies )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
- t.replies << [r = Reply.create("title" => "A reply"), r2 = Reply.create("title" => "Another reply")]
+ t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
assert !t.valid?
assert t.errors.on(:replies)
assert_equal 1, r.errors.count # make sure all associated objects have been validated
- assert_equal 1, r2.errors.count
- r.content = r2.content = "non-empty"
+ assert_equal 0, r2.errors.count
+ assert_equal 1, r3.errors.count
+ assert_equal 0, r4.errors.count
+ r.content = r3.content = "non-empty"
assert t.valid?
end
def test_validates_associated_one
Reply.validates_associated( :topic )
Topic.validates_presence_of( :content )
- r = Reply.create("title" => "A reply", "content" => "with content!")
+ r = Reply.new("title" => "A reply", "content" => "with content!")
r.topic = Topic.create("title" => "uhohuhoh")
assert !r.valid?
assert r.errors.on(:topic)
@@ -877,7 +1034,7 @@ class ValidationsTest < Test::Unit::TestCase
d = Developer.new
d.salary = "0"
assert !d.valid?
- assert_equal d.errors.on(:salary).first, "This string contains 'single' and \"double\" quotes"
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
end
def test_validates_confirmation_of_with_custom_error_using_quotes
@@ -902,7 +1059,7 @@ class ValidationsTest < Test::Unit::TestCase
d = Developer.new
d.salary = "90,000"
assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).first
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
end
def test_validates_length_of_with_custom_too_long_using_quotes
@@ -910,7 +1067,7 @@ class ValidationsTest < Test::Unit::TestCase
d = Developer.new
d.name = "Jeffrey"
assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
end
def test_validates_length_of_with_custom_too_short_using_quotes
@@ -918,7 +1075,7 @@ class ValidationsTest < Test::Unit::TestCase
d = Developer.new
d.name = "Joe"
assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
end
def test_validates_length_of_with_custom_message_using_quotes
@@ -926,7 +1083,7 @@ class ValidationsTest < Test::Unit::TestCase
d = Developer.new
d.name = "Joe"
assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
end
def test_validates_presence_of_with_custom_message_using_quotes
@@ -942,7 +1099,7 @@ class ValidationsTest < Test::Unit::TestCase
d = Developer.new
d.name = "David"
assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
end
def test_validates_associated_with_custom_message_using_quotes
@@ -951,10 +1108,10 @@ class ValidationsTest < Test::Unit::TestCase
r = Reply.create("title" => "A reply", "content" => "with content!")
r.topic = Topic.create("title" => "uhohuhoh")
assert !r.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).first
+ assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
end
- def test_conditional_validation_using_method_true
+ def test_if_validation_using_method_true
# When the method returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -963,7 +1120,15 @@ class ValidationsTest < Test::Unit::TestCase
assert_equal "hoo 5", t.errors["title"]
end
- def test_conditional_validation_using_method_false
+ def test_unless_validation_using_method_true
+ # When the method returns true
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true )
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+ assert t.valid?
+ assert !t.errors.on(:title)
+ end
+
+ def test_if_validation_using_method_false
# When the method returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true_but_its_not )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -971,7 +1136,16 @@ class ValidationsTest < Test::Unit::TestCase
assert !t.errors.on(:title)
end
- def test_conditional_validation_using_string_true
+ def test_unless_validation_using_method_false
+ # When the method returns false
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true_but_its_not )
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+ assert !t.valid?
+ assert t.errors.on(:title)
+ assert_equal "hoo 5", t.errors["title"]
+ end
+
+ def test_if_validation_using_string_true
# When the evaluated string returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "a = 1; a == 1" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -980,7 +1154,15 @@ class ValidationsTest < Test::Unit::TestCase
assert_equal "hoo 5", t.errors["title"]
end
- def test_conditional_validation_using_string_false
+ def test_unless_validation_using_string_true
+ # When the evaluated string returns true
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "a = 1; a == 1" )
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+ assert t.valid?
+ assert !t.errors.on(:title)
+ end
+
+ def test_if_validation_using_string_false
# When the evaluated string returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "false")
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -988,7 +1170,16 @@ class ValidationsTest < Test::Unit::TestCase
assert !t.errors.on(:title)
end
- def test_conditional_validation_using_block_true
+ def test_unless_validation_using_string_false
+ # When the evaluated string returns false
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "false")
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+ assert !t.valid?
+ assert t.errors.on(:title)
+ assert_equal "hoo 5", t.errors["title"]
+ end
+
+ def test_if_validation_using_block_true
# When the block returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
:if => Proc.new { |r| r.content.size > 4 } )
@@ -998,7 +1189,16 @@ class ValidationsTest < Test::Unit::TestCase
assert_equal "hoo 5", t.errors["title"]
end
- def test_conditional_validation_using_block_false
+ def test_unless_validation_using_block_true
+ # When the block returns true
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
+ :unless => Proc.new { |r| r.content.size > 4 } )
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+ assert t.valid?
+ assert !t.errors.on(:title)
+ end
+
+ def test_if_validation_using_block_false
# When the block returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
:if => Proc.new { |r| r.title != "uhohuhoh"} )
@@ -1007,6 +1207,16 @@ class ValidationsTest < Test::Unit::TestCase
assert !t.errors.on(:title)
end
+ def test_unless_validation_using_block_false
+ # When the block returns false
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
+ :unless => Proc.new { |r| r.title != "uhohuhoh"} )
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+ assert !t.valid?
+ assert t.errors.on(:title)
+ assert_equal "hoo 5", t.errors["title"]
+ end
+
def test_validates_associated_missing
Reply.validates_presence_of(:topic)
r = Reply.create("title" => "A reply", "content" => "with content!")
@@ -1025,6 +1235,37 @@ class ValidationsTest < Test::Unit::TestCase
assert xml.include?("Title is Wrong Create ")
assert xml.include?("Content Empty ")
end
+
+ def test_validation_order
+ Topic.validates_presence_of :title
+ Topic.validates_length_of :title, :minimum => 2
+
+ t = Topic.new("title" => "")
+ assert !t.valid?
+ assert_equal "can't be blank", t.errors.on("title").first
+ end
+
+ # previous implementation of validates_presence_of eval'd the
+ # string with the wrong binding, this regression test is to
+ # ensure that it works correctly
+ def test_validation_with_if_as_string
+ Topic.validates_presence_of(:title)
+ Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
+
+ t = Topic.new
+ assert !t.valid?, "A topic without a title should not be valid"
+ assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author"
+
+ t.title = "Just a title"
+ assert t.valid?, "A topic with a basic title should be valid"
+
+ t.title = "A very important title"
+ assert !t.valid?, "A topic with an important title, but without an author, should not be valid"
+ assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author"
+
+ t.author_name = "Hubert J. Farnsworth"
+ assert t.valid?, "A topic with an important title and author should be valid"
+ end
end
@@ -1073,11 +1314,68 @@ class ValidatesNumericalityTest < Test::Unit::TestCase
valid!(NIL + INTEGERS)
end
+ def test_validates_numericality_with_greater_than
+ Topic.validates_numericality_of :approved, :greater_than => 10
+
+ invalid!([-10, 10], 'must be greater than 10')
+ valid!([11])
+ end
+
+ def test_validates_numericality_with_greater_than_or_equal
+ Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
+
+ invalid!([-9, 9], 'must be greater than or equal to 10')
+ valid!([10])
+ end
+
+ def test_validates_numericality_with_equal_to
+ Topic.validates_numericality_of :approved, :equal_to => 10
+
+ invalid!([-10, 11], 'must be equal to 10')
+ valid!([10])
+ end
+
+ def test_validates_numericality_with_less_than
+ Topic.validates_numericality_of :approved, :less_than => 10
+
+ invalid!([10], 'must be less than 10')
+ valid!([-9, 9])
+ end
+
+ def test_validates_numericality_with_less_than_or_equal_to
+ Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
+
+ invalid!([11], 'must be less than or equal to 10')
+ valid!([-10, 10])
+ end
+
+ def test_validates_numericality_with_odd
+ Topic.validates_numericality_of :approved, :odd => true
+
+ invalid!([-2, 2], 'must be odd')
+ valid!([-1, 1])
+ end
+
+ def test_validates_numericality_with_even
+ Topic.validates_numericality_of :approved, :even => true
+
+ invalid!([-1, 1], 'must be even')
+ valid!([-2, 2])
+ end
+
+ def test_validates_numericality_with_greater_than_less_than_and_even
+ Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
+
+ invalid!([1, 3, 4])
+ valid!([2])
+ end
+
private
- def invalid!(values)
+ def invalid!(values, error=nil)
with_each_topic_approved_value(values) do |topic, value|
assert !topic.valid?, "#{value.inspect} not rejected as a number"
assert topic.errors.on(:approved)
+ assert_equal error, topic.errors.on(:approved) if error
end
end
diff --git a/vendor/rails/activerecord/test/xml_serialization_test.rb b/vendor/rails/activerecord/test/xml_serialization_test.rb
index 73ff354c..9b74ddd4 100644
--- a/vendor/rails/activerecord/test/xml_serialization_test.rb
+++ b/vendor/rails/activerecord/test/xml_serialization_test.rb
@@ -1,20 +1,9 @@
require 'abstract_unit'
+require 'fixtures/contact'
require 'fixtures/post'
require 'fixtures/author'
-
-class Contact < ActiveRecord::Base
- # mock out self.columns so no pesky db is needed for these tests
- def self.columns() @columns ||= []; end
- def self.column(name, sql_type = nil, default = nil, null = true)
- columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
- end
-
- column :name, :string
- column :age, :integer
- column :avatar, :binary
- column :created_at, :datetime
- column :awesome, :boolean
-end
+require 'fixtures/tagging'
+require 'fixtures/comment'
class XmlSerializationTest < Test::Unit::TestCase
def test_should_serialize_default_root
@@ -22,19 +11,19 @@ class XmlSerializationTest < Test::Unit::TestCase
assert_match %r{^}, @xml
assert_match %r{ $}, @xml
end
-
+
def test_should_serialize_default_root_with_namespace
@xml = Contact.new.to_xml :namespace=>"http://xml.rubyonrails.org/contact"
assert_match %r{^}, @xml
assert_match %r{ $}, @xml
end
-
+
def test_should_serialize_custom_root
@xml = Contact.new.to_xml :root => 'xml_contact'
assert_match %r{^}, @xml
assert_match %r{ $}, @xml
end
-
+
def test_should_allow_undasherized_tags
@xml = Contact.new.to_xml :root => 'xml_contact', :dasherize => false
assert_match %r{^}, @xml
@@ -42,45 +31,45 @@ class XmlSerializationTest < Test::Unit::TestCase
assert_match %r{ [:age, :name]
- assert_match %r{ [:age, :name]
- assert_no_match %r{David}, @xml
end
end
class DefaultXmlSerializationTest < Test::Unit::TestCase
def setup
- @xml = Contact.new(:name => 'aaron stack', :age => 25, :avatar => 'binarydata', :created_at => Time.utc(2006, 8, 1), :awesome => false).to_xml
+ @xml = Contact.new(:name => 'aaron stack', :age => 25, :avatar => 'binarydata', :created_at => Time.utc(2006, 8, 1), :awesome => false, :preferences => { :gem => 'ruby' }).to_xml
end
def test_should_serialize_string
assert_match %r{aaron stack }, @xml
end
-
+
def test_should_serialize_integer
assert_match %r{25 }, @xml
end
-
+
def test_should_serialize_binary
assert_match %r{YmluYXJ5ZGF0YQ==\n}, @xml
assert_match %r{2006-08-01T00:00:00Z}, @xml
end
-
+
def test_should_serialize_boolean
assert_match %r{false }, @xml
end
+
+ def test_should_serialize_yaml
+ assert_match %r{--- \n:gem: ruby\n }, @xml
+ end
end
class NilXmlSerializationTest < Test::Unit::TestCase
@@ -89,32 +78,51 @@ class NilXmlSerializationTest < Test::Unit::TestCase
end
def test_should_serialize_string
- assert_match %r{ }, @xml
+ assert_match %r{ }, @xml
end
-
+
def test_should_serialize_integer
- assert_match %r{ }, @xml
+ assert %r{ }.match(@xml)
+ attributes = $1
+ assert_match %r{nil="true"}, attributes
+ assert_match %r{type="integer"}, attributes
end
-
+
def test_should_serialize_binary
- assert_match %r{> }, @xml
- assert_match %r{ }.match(@xml)
+ attributes = $1
+ assert_match %r{type="binary"}, attributes
+ assert_match %r{encoding="base64"}, attributes
+ assert_match %r{nil="true"}, attributes
end
-
+
def test_should_serialize_datetime
- assert_match %r{ }, @xml
+ assert %r{ }.match(@xml)
+ attributes = $1
+ assert_match %r{nil="true"}, attributes
+ assert_match %r{type="datetime"}, attributes
end
-
+
def test_should_serialize_boolean
- assert_match %r{ }, @xml
+ assert %r{ }.match(@xml)
+ attributes = $1
+ assert_match %r{type="boolean"}, attributes
+ assert_match %r{nil="true"}, attributes
+ end
+
+ def test_should_serialize_yaml
+ assert %r{ }.match(@xml)
+ attributes = $1
+ assert_match %r{type="yaml"}, attributes
+ assert_match %r{nil="true"}, attributes
end
end
class DatabaseConnectedXmlSerializationTest < Test::Unit::TestCase
fixtures :authors, :posts
# to_xml used to mess with the hash the user provided which
- # caused the builder to be reused
+ # caused the builder to be reused. This meant the document kept
+ # getting appended to.
def test_passing_hash_shouldnt_reuse_builder
options = {:include=>:posts}
david = authors(:david)
@@ -122,4 +130,73 @@ class DatabaseConnectedXmlSerializationTest < Test::Unit::TestCase
second_xml_size = david.to_xml(options).size
assert_equal first_xml_size, second_xml_size
end
+
+ def test_include_uses_association_name
+ xml = authors(:david).to_xml :include=>:hello_posts, :indent => 0
+ assert_match %r{}, xml
+ assert_match %r{}, xml
+ assert_match %r{}, xml
+ end
+
+ def test_methods_are_called_on_object
+ xml = authors(:david).to_xml :methods => :label, :indent => 0
+ assert_match %r{.* }, xml
+ end
+
+ def test_should_not_call_methods_on_associations_that_dont_respond
+ xml = authors(:david).to_xml :include=>:hello_posts, :methods => :label, :indent => 2
+ assert !authors(:david).hello_posts.first.respond_to?(:label)
+ assert_match %r{^ .* }, xml
+ assert_no_match %r{^ }, xml
+ end
+
+ def test_procs_are_called_on_object
+ proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
+ xml = authors(:david).to_xml(:procs => [ proc ])
+ assert_match %r{Danish }, xml
+ end
+
+ def test_top_level_procs_arent_applied_to_associations
+ author_proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
+ xml = authors(:david).to_xml(:procs => [ author_proc ], :include => :posts, :indent => 2)
+
+ assert_match %r{^ Danish }, xml
+ assert_no_match %r{^ {6}Danish }, xml
+ end
+
+ def test_procs_on_included_associations_are_called
+ posts_proc = Proc.new { |options| options[:builder].tag!('copyright', 'DHH') }
+ xml = authors(:david).to_xml(
+ :indent => 2,
+ :include => {
+ :posts => { :procs => [ posts_proc ] }
+ }
+ )
+
+ assert_no_match %r{^ DHH }, xml
+ assert_match %r{^ {6}DHH }, xml
+ end
+
+ def test_should_include_empty_has_many_as_empty_array
+ authors(:david).posts.delete_all
+ xml = authors(:david).to_xml :include=>:posts, :indent => 2
+
+ assert_equal [], Hash.from_xml(xml)['author']['posts']
+ assert_match %r{^ }, xml
+ end
+
+ def test_should_has_many_array_elements_should_include_type_when_different_from_guessed_value
+ xml = authors(:david).to_xml :include=>:posts_with_comments, :indent => 2
+
+ assert Hash.from_xml(xml)
+ assert_match %r{^ }, xml
+ assert_match %r{^ }, xml
+ assert_match %r{^ }, xml
+
+ types = Hash.from_xml(xml)['author']['posts_with_comments'].collect {|t| t['type'] }
+ assert types.include?('SpecialPost')
+ assert types.include?('Post')
+ assert types.include?('StiPost')
+ end
+
end
\ No newline at end of file
diff --git a/vendor/rails/activeresource/CHANGELOG b/vendor/rails/activeresource/CHANGELOG
new file mode 100644
index 00000000..243511ca
--- /dev/null
+++ b/vendor/rails/activeresource/CHANGELOG
@@ -0,0 +1,223 @@
+*2.0.2* (December 16th, 2007)
+
+* Added more specific exceptions for 400, 401, and 403 (all descending from ClientError so existing rescues will work) #10326 [trek]
+
+* Correct empty response handling. #10445 [seangeo]
+
+
+*2.0.1* (December 7th, 2007)
+
+* Don't cache net/http object so that ActiveResource is more thread-safe. Closes #10142 [kou]
+
+* Update XML documentation examples to include explicit type attributes. Closes #9754 [hasmanyjosh]
+
+* Added one-off declarations of mock behavior [DHH]. Example:
+
+ Before:
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1.xml", {}, "David "
+ end
+
+ Now:
+ ActiveResource::HttpMock.respond_to.get "/people/1.xml", {}, "David "
+
+* Added ActiveResource.format= which defaults to :xml but can also be set to :json [DHH]. Example:
+
+ class Person < ActiveResource::Base
+ self.site = "http://app/"
+ self.format = :json
+ end
+
+ person = Person.find(1) # => GET http://app/people/1.json
+ person.name = "David"
+ person.save # => PUT http://app/people/1.json {name: "David"}
+
+ Person.format = :xml
+ person.name = "Mary"
+ person.save # => PUT http://app/people/1.json Mary
+
+* Fix reload error when path prefix is used. #8727 [Ian Warshak]
+
+* Remove ActiveResource::Struct because it hasn't proven very useful. Creating a new ActiveResource::Base subclass is often less code and always clearer. #8612 [Josh Peek]
+
+* Fix query methods on resources. [Cody Fauser]
+
+* pass the prefix_options to the instantiated record when using find without a specific id. Closes #8544 [alloy]
+
+* Recognize and raise an exception on 405 Method Not Allowed responses. #7692 [Josh Peek]
+
+* Handle string and symbol param keys when splitting params into prefix params and query params.
+
+ Comment.find(:all, :params => { :article_id => 5, :page => 2 }) or Comment.find(:all, :params => { 'article_id' => 5, :page => 2 })
+
+* Added find-one with symbol [DHH]. Example: Person.find(:one, :from => :leader) # => GET /people/leader.xml
+
+* BACKWARDS INCOMPATIBLE: Changed the finder API to be more extensible with :params and more strict usage of scopes [DHH]. Changes:
+
+ Person.find(:all, :title => "CEO") ...becomes: Person.find(:all, :params => { :title => "CEO" })
+ Person.find(:managers) ...becomes: Person.find(:all, :from => :managers)
+ Person.find("/companies/1/manager.xml") ...becomes: Person.find(:one, :from => "/companies/1/manager.xml")
+
+* Add support for setting custom headers per Active Resource model [Rick]
+
+ class Project
+ headers['X-Token'] = 'foo'
+ end
+
+ # makes the GET request with the custom X-Token header
+ Project.find(:all)
+
+* Added find-by-path options to ActiveResource::Base.find [DHH]. Examples:
+
+ employees = Person.find(:all, :from => "/companies/1/people.xml") # => GET /companies/1/people.xml
+ manager = Person.find("/companies/1/manager.xml") # => GET /companies/1/manager.xml
+
+
+* Added support for using classes from within a single nested module [DHH]. Example:
+
+ module Highrise
+ class Note < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+
+ class Comment < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+ end
+
+ assert_kind_of Highrise::Comment, Note.find(1).comments.first
+
+
+* Added load_attributes_from_response as a way of loading attributes from other responses than just create [DHH]
+
+ class Highrise::Task < ActiveResource::Base
+ def complete
+ load_attributes_from_response(post(:complete))
+ end
+ end
+
+ ...will set "done_at" when complete is called.
+
+
+* Added support for calling custom methods #6979 [rwdaigle]
+
+ Person.find(:managers) # => GET /people/managers.xml
+ Kase.find(1).post(:close) # => POST /kases/1/close.xml
+
+* Remove explicit prefix_options parameter for ActiveResource::Base#initialize. [Rick]
+ ActiveResource splits the prefix_options from it automatically.
+
+* Allow ActiveResource::Base.delete with custom prefix. [Rick]
+
+* Add ActiveResource::Base#dup [Rick]
+
+* Fixed constant warning when fetching the same object multiple times [DHH]
+
+* Added that saves which get a body response (and not just a 201) will use that response to update themselves [DHH]
+
+* Disregard namespaces from the default element name, so Highrise::Person will just try to fetch from "/people", not "/highrise/people" [DHH]
+
+* Allow array and hash query parameters. #7756 [Greg Spurrier]
+
+* Loading a resource preserves its prefix_options. #7353 [Ryan Daigle]
+
+* Carry over the convenience of #create from ActiveRecord. Closes #7340. [Ryan Daigle]
+
+* Increase ActiveResource::Base test coverage. Closes #7173, #7174 [Rich Collins]
+
+* Interpret 422 Unprocessable Entity as ResourceInvalid. #7097 [dkubb]
+
+* Mega documentation patches. #7025, #7069 [rwdaigle]
+
+* Base.exists?(id, options) and Base#exists? check whether the resource is found. #6970 [rwdaigle]
+
+* Query string support. [untext, Jeremy Kemper]
+ # GET /forums/1/topics.xml?sort=created_at
+ Topic.find(:all, :forum_id => 1, :sort => 'created_at')
+
+* Base#==, eql?, and hash methods. == returns true if its argument is identical to self or if it's an instance of the same class, is not new?, and has the same id. eql? is an alias for ==. hash delegates to id. [Jeremy Kemper]
+
+* Allow subclassed resources to share the site info [Rick, Jeremy Kemper]
+d
+ class BeastResource < ActiveResource::Base
+ self.site = 'http://beast.caboo.se'
+ end
+
+ class Forum < BeastResource
+ # taken from BeastResource
+ # self.site = 'http://beast.caboo.se'
+ end
+
+ class Topic < BeastResource
+ self.site += '/forums/:forum_id'
+ end
+
+* Fix issues with ActiveResource collection handling. Closes #6291. [bmilekic]
+
+* Use attr_accessor_with_default to dry up attribute initialization. References #6538. [Stuart Halloway]
+
+* Add basic logging support for logging outgoing requests. [Jamis Buck]
+
+* Add Base.delete for deleting resources without having to instantiate them first. [Jamis Buck]
+
+* Make #save behavior mimic AR::Base#save (true on success, false on failure). [Jamis Buck]
+
+* Add Basic HTTP Authentication to ActiveResource (closes #6305). [jonathan]
+
+* Extracted #id_from_response as an entry point for customizing how a created resource gets its own ID.
+ By default, it extracts from the Location response header.
+
+* Optimistic locking: raise ActiveResource::ResourceConflict on 409 Conflict response. [Jeremy Kemper]
+
+ # Example controller action
+ def update
+ @person.save!
+ rescue ActiveRecord::StaleObjectError
+ render :xml => @person.reload.to_xml, :status => '409 Conflict'
+ end
+
+* Basic validation support [Rick Olson]
+
+ Parses the xml response of ActiveRecord::Errors#to_xml with a similar interface to ActiveRecord::Errors.
+
+ render :xml => @person.errors.to_xml, :status => '400 Validation Error'
+
+* Deep hashes are converted into collections of resources. [Jeremy Kemper]
+ Person.new :name => 'Bob',
+ :address => { :id => 1, :city => 'Portland' },
+ :contacts => [{ :id => 1 }, { :id => 2 }]
+ Looks for Address and Contact resources and creates them if unavailable.
+ So clients can fetch a complex resource in a single request if you e.g.
+ render :xml => @person.to_xml(:include => [:address, :contacts])
+ in your controller action.
+
+* Major updates [Rick Olson]
+
+ * Add full support for find/create/update/destroy
+ * Add support for specifying prefixes.
+ * Allow overriding of element_name, collection_name, and primary key
+ * Provide simpler HTTP mock interface for testing
+
+ # rails routing code
+ map.resources :posts do |post|
+ post.resources :comments
+ end
+
+ # ActiveResources
+ class Post < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000/"
+ end
+
+ class Comment < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000/posts/:post_id/"
+ end
+
+ @post = Post.find 5
+ @comments = Comment.find :all, :post_id => @post.id
+
+ @comment = Comment.new({:body => 'hello world'}, {:post_id => @post.id})
+ @comment.save
+
+* Base.site= accepts URIs. 200...400 are valid response codes. PUT and POST request bodies default to ''. [Jeremy Kemper]
+
+* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. [DHH]
diff --git a/vendor/rails/activeresource/MIT-LICENSE b/vendor/rails/activeresource/MIT-LICENSE
new file mode 100644
index 00000000..7f65dd19
--- /dev/null
+++ b/vendor/rails/activeresource/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2006 David Heinemeier Hansson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/rails/activeresource/README b/vendor/rails/activeresource/README
new file mode 100644
index 00000000..bcb7b3cb
--- /dev/null
+++ b/vendor/rails/activeresource/README
@@ -0,0 +1,165 @@
+= Active Resource
+
+Active Resource (ARes) connects business objects and Representational State Transfer (REST)
+web services. It implements object-relational mapping for REST webservices to provide transparent
+proxying capabilities between a client (ActiveResource) and a RESTful service (which is provided by Simply RESTful routing
+in ActionController::Resources).
+
+== Philosophy
+
+Active Resource attempts to provide a coherent wrapper object-relational mapping for REST
+web services. It follows the same philosophy as Active Record, in that one of its prime aims
+is to reduce the amount of code needed to map to these resources. This is made possible
+by relying on a number of code- and protocol-based conventions that make it easy for Active Resource
+to infer complex relations and structures. These conventions are outlined in detail in the documentation
+for ActiveResource::Base.
+
+== Overview
+
+Model classes are mapped to remote REST resources by Active Resource much the same way Active Record maps model classes to database
+tables. When a request is made to a remote resource, a REST XML request is generated, transmitted, and the result
+received and serialized into a usable Ruby object.
+
+=== Configuration and Usage
+
+Putting ActiveResource to use is very similar to ActiveRecord. It's as simple as creating a model class
+that inherits from ActiveResource::Base and providing a site class variable to it:
+
+ class Person < ActiveResource::Base
+ self.site = "http://api.people.com:3000/"
+ end
+
+Now the Person class is REST enabled and can invoke REST services very similarly to how ActiveRecord invokes
+lifecycle methods that operate against a persistent store.
+
+ # Find a person with id = 1
+ ryan = Person.find(1)
+ Person.exists?(1) #=> true
+
+As you can see, the methods are quite similar to Active Record's methods for dealing with database
+records. But rather than dealing with
+
+==== Protocol
+
+Active Resource is built on a standard XML format for requesting and submitting resources over HTTP. It mirrors the RESTful routing
+built into ActionController but will also work with any other REST service that properly implements the protocol.
+REST uses HTTP, but unlike "typical" web applications, it makes use of all the verbs available in the HTTP specification:
+
+* GET requests are used for finding and retrieving resources.
+* POST requests are used to create new resources.
+* PUT requests are used to update existing resources.
+* DELETE requests are used to delete resources.
+
+For more information on how this protocol works with Active Resource, see the ActiveResource::Base documentation;
+for more general information on REST web services, see the article here[http://en.wikipedia.org/wiki/Representational_State_Transfer].
+
+==== Find
+
+GET Http requests expect the XML form of whatever resource/resources is/are being requested. So,
+for a request for a single element - the XML of that item is expected in response:
+
+ # Expects a response of
+ #
+ # 1 value1 ..
+ #
+ # for GET http://api.people.com:3000/people/1.xml
+ #
+ ryan = Person.find(1)
+
+The XML document that is received is used to build a new object of type Person, with each
+XML element becoming an attribute on the object.
+
+ ryan.is_a? Person #=> true
+ ryan.attribute1 #=> 'value1'
+
+Any complex element (one that contains other elements) becomes its own object:
+
+ # With this response:
+ #
+ # 1 value1 value2
+ #
+ # for GET http://api.people.com:3000/people/1.xml
+ #
+ ryan = Person.find(1)
+ ryan.complex #=>
+ ryan.complex.attribute2 #=> 'value2'
+
+Collections can also be requested in a similar fashion
+
+ # Expects a response of
+ #
+ #
+ # 1 Ryan
+ # 2 Jim
+ #
+ #
+ # for GET http://api.people.com:3000/people.xml
+ #
+ people = Person.find(:all)
+ people.first #=> 'Ryan' ...>
+ people.last #=> 'Jim' ...>
+
+==== Create
+
+Creating a new resource submits the xml form of the resource as the body of the request and expects
+a 'Location' header in the response with the RESTful URL location of the newly created resource. The
+id of the newly created resource is parsed out of the Location response header and automatically set
+as the id of the ARes object.
+
+ # Ryan
+ #
+ # is submitted as the body on
+ #
+ # POST http://api.people.com:3000/people.xml
+ #
+ # when save is called on a new Person object. An empty response is
+ # is expected with a 'Location' header value:
+ #
+ # Response (201): Location: http://api.people.com:3000/people/2
+ #
+ ryan = Person.new(:first => 'Ryan')
+ ryan.new? #=> true
+ ryan.save #=> true
+ ryan.new? #=> false
+ ryan.id #=> 2
+
+==== Update
+
+'save' is also used to update an existing resource - and follows the same protocol as creating a resource
+with the exception that no response headers are needed - just an empty response when the update on the
+server side was successful.
+
+ # Ryan
+ #
+ # is submitted as the body on
+ #
+ # PUT http://api.people.com:3000/people/1.xml
+ #
+ # when save is called on an existing Person object. An empty response is
+ # is expected with code (204)
+ #
+ ryan = Person.find(1)
+ ryan.first #=> 'Ryan'
+ ryan.first = 'Rizzle'
+ ryan.save #=> true
+
+==== Delete
+
+Destruction of a resource can be invoked as a class and instance method of the resource.
+
+ # A request is made to
+ #
+ # DELETE http://api.people.com:3000/people/1.xml
+ #
+ # for both of these forms. An empty response with
+ # is expected with response code (200)
+ #
+ ryan = Person.find(1)
+ ryan.destroy #=> true
+ ryan.exists? #=> false
+ Person.delete(2) #=> true
+ Person.exists?(2) #=> false
+
+
+You can find more usage information in the ActiveResource::Base documentation.
+
diff --git a/vendor/rails/activeresource/Rakefile b/vendor/rails/activeresource/Rakefile
new file mode 100644
index 00000000..3434864b
--- /dev/null
+++ b/vendor/rails/activeresource/Rakefile
@@ -0,0 +1,133 @@
+require 'rubygems'
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+require 'rake/packagetask'
+require 'rake/gempackagetask'
+require 'rake/contrib/rubyforgepublisher'
+require File.join(File.dirname(__FILE__), 'lib', 'active_resource', 'version')
+
+PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
+PKG_NAME = 'activeresource'
+PKG_VERSION = ActiveResource::VERSION::STRING + PKG_BUILD
+PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
+
+RELEASE_NAME = "REL #{PKG_VERSION}"
+
+RUBY_FORGE_PROJECT = "activerecord"
+RUBY_FORGE_USER = "webster132"
+
+PKG_FILES = FileList[
+ "lib/**/*", "test/**/*", "[A-Z]*", "Rakefile"
+].exclude(/\bCVS\b|~$/)
+
+desc "Default Task"
+task :default => [ :test ]
+
+# Run the unit tests
+
+Rake::TestTask.new { |t|
+ t.libs << "test"
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+ t.warning = true
+}
+
+
+# Generate the RDoc documentation
+
+Rake::RDocTask.new { |rdoc|
+ rdoc.rdoc_dir = 'doc'
+ rdoc.title = "Active Resource -- Object-oriented REST services"
+ rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
+ rdoc.options << '--charset' << 'utf-8'
+ rdoc.template = "#{ENV['template']}.rb" if ENV['template']
+ rdoc.rdoc_files.include('README', 'CHANGELOG')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+}
+
+
+# Create compressed packages
+
+dist_dirs = [ "lib", "test", "examples", "dev-utils" ]
+
+spec = Gem::Specification.new do |s|
+ s.name = PKG_NAME
+ s.version = PKG_VERSION
+ s.summary = "Think Active Record for web resources."
+ s.description = %q{Wraps web resources in model classes that can be manipulated through XML over REST.}
+
+ s.files = [ "Rakefile", "README", "CHANGELOG" ]
+ dist_dirs.each do |dir|
+ s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
+ end
+
+ s.add_dependency('activesupport', '= 2.0.2' + PKG_BUILD)
+
+ s.require_path = 'lib'
+ s.autorequire = 'active_resource'
+
+ s.has_rdoc = true
+ s.extra_rdoc_files = %w( README )
+ s.rdoc_options.concat ['--main', 'README']
+
+ s.author = "David Heinemeier Hansson"
+ s.email = "david@loudthinking.com"
+ s.homepage = "http://www.rubyonrails.org"
+ s.rubyforge_project = "activeresource"
+end
+
+Rake::GemPackageTask.new(spec) do |p|
+ p.gem_spec = spec
+ p.need_tar = true
+ p.need_zip = true
+end
+
+task :lines do
+ lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
+
+ for file_name in FileList["lib/active_resource/**/*.rb"]
+ next if file_name =~ /vendor/
+ f = File.open(file_name)
+
+ while line = f.gets
+ lines += 1
+ next if line =~ /^\s*$/
+ next if line =~ /^\s*#/
+ codelines += 1
+ end
+ puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
+
+ total_lines += lines
+ total_codelines += codelines
+
+ lines, codelines = 0, 0
+ end
+
+ puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
+end
+
+
+# Publishing ------------------------------------------------------
+
+desc "Publish the beta gem"
+task :pgem => [:package] do
+ Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+ `ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
+end
+
+desc "Publish the API documentation"
+task :pdoc => [:rdoc] do
+ Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/ar", "doc").upload
+end
+
+desc "Publish the release files to RubyForge."
+task :release => [ :package ] do
+ `rubyforge login`
+
+ for ext in %w( gem tgz zip )
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
+ puts release_command
+ system(release_command)
+ end
+end
diff --git a/vendor/rails/activeresource/lib/active_resource.rb b/vendor/rails/activeresource/lib/active_resource.rb
new file mode 100644
index 00000000..96f2257a
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource.rb
@@ -0,0 +1,47 @@
+#--
+# Copyright (c) 2006 David Heinemeier Hansson
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#++
+
+$:.unshift(File.dirname(__FILE__)) unless
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
+
+unless defined?(ActiveSupport)
+ begin
+ $:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib")
+ require 'active_support'
+ rescue LoadError
+ require 'rubygems'
+ gem 'activesupport'
+ end
+end
+
+require 'active_resource/formats'
+require 'active_resource/base'
+require 'active_resource/validations'
+require 'active_resource/custom_methods'
+
+module ActiveResource
+ Base.class_eval do
+ include Validations
+ include CustomMethods
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activeresource/lib/active_resource/base.rb b/vendor/rails/activeresource/lib/active_resource/base.rb
new file mode 100644
index 00000000..47c4d0c8
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource/base.rb
@@ -0,0 +1,872 @@
+require 'active_resource/connection'
+require 'cgi'
+require 'set'
+
+module ActiveResource
+ # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application.
+ #
+ # For an outline of what Active Resource is capable of, see link:files/README.html.
+ #
+ # == Automated mapping
+ #
+ # Active Resource objects represent your RESTful resources as manipulatable Ruby objects. To map resources
+ # to Ruby objects, Active Resource only needs a class name that corresponds to the resource name (e.g., the class
+ # Person maps to the resources people, very similarly to Active Record) and a +site+ value, which holds the
+ # URI of the resources.
+ #
+ # class Person < ActiveResource::Base
+ # self.site = "http://api.people.com:3000/"
+ # end
+ #
+ # Now the Person class is mapped to RESTful resources located at http://api.people.com:3000/people/ , and
+ # you can now use Active Resource's lifecycles methods to manipulate resources.
+ #
+ # == Lifecycle methods
+ #
+ # Active Resource exposes methods for creating, finding, updating, and deleting resources
+ # from REST web services.
+ #
+ # ryan = Person.new(:first => 'Ryan', :last => 'Daigle')
+ # ryan.save #=> true
+ # ryan.id #=> 2
+ # Person.exists?(ryan.id) #=> true
+ # ryan.exists? #=> true
+ #
+ # ryan = Person.find(1)
+ # # => Resource holding our newly create Person object
+ #
+ # ryan.first = 'Rizzle'
+ # ryan.save #=> true
+ #
+ # ryan.destroy #=> true
+ #
+ # As you can see, these are very similar to Active Record's lifecycle methods for database records.
+ # You can read more about each of these methods in their respective documentation.
+ #
+ # === Custom REST methods
+ #
+ # Since simple CRUD/lifecycle methods can't accomplish every task, Active Resource also supports
+ # defining your own custom REST methods.
+ #
+ # Person.new(:name => 'Ryan).post(:register)
+ # # => { :id => 1, :name => 'Ryan', :position => 'Clerk' }
+ #
+ # Person.find(1).put(:promote, :position => 'Manager')
+ # # => { :id => 1, :name => 'Ryan', :position => 'Manager' }
+ #
+ # For more information on creating and using custom REST methods, see the
+ # ActiveResource::CustomMethods documentation.
+ #
+ # == Validations
+ #
+ # You can validate resources client side by overriding validation methods in the base class.
+ #
+ # class Person < ActiveResource::Base
+ # self.site = "http://api.people.com:3000/"
+ # protected
+ # def validate
+ # errors.add("last", "has invalid characters") unless last =~ /[a-zA-Z]*/
+ # end
+ # end
+ #
+ # See the ActiveResource::Validations documentation for more information.
+ #
+ # == Authentication
+ #
+ # Many REST APIs will require authentication, usually in the form of basic
+ # HTTP authentication. Authentication can be specified by putting the credentials
+ # in the +site+ variable of the Active Resource class you need to authenticate.
+ #
+ # class Person < ActiveResource::Base
+ # self.site = "http://ryan:password@api.people.com:3000/"
+ # end
+ #
+ # For obvious security reasons, it is probably best if such services are available
+ # over HTTPS.
+ #
+ # == Errors & Validation
+ #
+ # Error handling and validation is handled in much the same manner as you're used to seeing in
+ # Active Record. Both the response code in the Http response and the body of the response are used to
+ # indicate that an error occurred.
+ #
+ # === Resource errors
+ #
+ # When a get is requested for a resource that does not exist, the HTTP +404+ (Resource Not Found)
+ # response code will be returned from the server which will raise an ActiveResource::ResourceNotFound
+ # exception.
+ #
+ # # GET http://api.people.com:3000/people/999.xml
+ # ryan = Person.find(999) # => Raises ActiveResource::ResourceNotFound
+ # # => Response = 404
+ #
+ # +404+ is just one of the HTTP error response codes that ActiveResource will handle with its own exception. The
+ # following HTTP response codes will also result in these exceptions:
+ #
+ # 200 - 399:: Valid response, no exception
+ # 404:: ActiveResource::ResourceNotFound
+ # 409:: ActiveResource::ResourceConflict
+ # 422:: ActiveResource::ResourceInvalid (rescued by save as validation errors)
+ # 401 - 499:: ActiveResource::ClientError
+ # 500 - 599:: ActiveResource::ServerError
+ #
+ # These custom exceptions allow you to deal with resource errors more naturally and with more precision
+ # rather than returning a general HTTP error. For example:
+ #
+ # begin
+ # ryan = Person.find(my_id)
+ # rescue ActiveResource::ResourceNotFound
+ # redirect_to :action => 'not_found'
+ # rescue ActiveResource::ResourceConflict, ActiveResource::ResourceInvalid
+ # redirect_to :action => 'new'
+ # end
+ #
+ # === Validation errors
+ #
+ # Active Resource supports validations on resources and will return errors if any these validations fail
+ # (e.g., "First name can not be blank" and so on). These types of errors are denoted in the response by
+ # a response code of +422+ and an XML representation of the validation errors. The save operation will
+ # then fail (with a +false+ return value) and the validation errors can be accessed on the resource in question.
+ #
+ # ryan = Person.find(1)
+ # ryan.first #=> ''
+ # ryan.save #=> false
+ #
+ # # When
+ # # PUT http://api.people.com:3000/people/1.xml
+ # # is requested with invalid values, the response is:
+ # #
+ # # Response (422):
+ # # First cannot be empty
+ # #
+ #
+ # ryan.errors.invalid?(:first) #=> true
+ # ryan.errors.full_messages #=> ['First cannot be empty']
+ #
+ # Learn more about Active Resource's validation features in the ActiveResource::Validations documentation.
+ #
+ class Base
+ # The logger for diagnosing and tracing Active Resource calls.
+ cattr_accessor :logger
+
+ class << self
+ # Gets the URI of the REST resources to map for this class. The site variable is required
+ # ActiveResource's mapping to work.
+ def site
+ if defined?(@site)
+ @site
+ elsif superclass != Object && superclass.site
+ superclass.site.dup.freeze
+ end
+ end
+
+ # Sets the URI of the REST resources to map for this class to the value in the +site+ argument.
+ # The site variable is required ActiveResource's mapping to work.
+ def site=(site)
+ @connection = nil
+ @site = site.nil? ? nil : create_site_uri_from(site)
+ end
+
+ # Sets the format that attributes are sent and received in from a mime type reference. Example:
+ #
+ # Person.format = :json
+ # Person.find(1) # => GET /people/1.json
+ #
+ # Person.format = ActiveResource::Formats::XmlFormat
+ # Person.find(1) # => GET /people/1.xml
+ #
+ # Default format is :xml.
+ def format=(mime_type_reference_or_format)
+ format = mime_type_reference_or_format.is_a?(Symbol) ?
+ ActiveResource::Formats[mime_type_reference_or_format] : mime_type_reference_or_format
+
+ write_inheritable_attribute("format", format)
+ connection.format = format
+ end
+
+ # Returns the current format, default is ActiveResource::Formats::XmlFormat
+ def format # :nodoc:
+ read_inheritable_attribute("format") || ActiveResource::Formats[:xml]
+ end
+
+ # An instance of ActiveResource::Connection that is the base connection to the remote service.
+ # The +refresh+ parameter toggles whether or not the connection is refreshed at every request
+ # or not (defaults to +false+).
+ def connection(refresh = false)
+ if defined?(@connection) || superclass == Object
+ @connection = Connection.new(site, format) if refresh || @connection.nil?
+ @connection
+ else
+ superclass.connection
+ end
+ end
+
+ def headers
+ @headers ||= {}
+ end
+
+ # Do not include any modules in the default element name. This makes it easier to seclude ARes objects
+ # in a separate namespace without having to set element_name repeatedly.
+ attr_accessor_with_default(:element_name) { to_s.split("::").last.underscore } #:nodoc:
+
+ attr_accessor_with_default(:collection_name) { element_name.pluralize } #:nodoc:
+ attr_accessor_with_default(:primary_key, 'id') #:nodoc:
+
+ # Gets the prefix for a resource's nested URL (e.g., prefix/collectionname/1.xml )
+ # This method is regenerated at runtime based on what the prefix is set to.
+ def prefix(options={})
+ default = site.path
+ default << '/' unless default[-1..-1] == '/'
+ # generate the actual method based on the current site path
+ self.prefix = default
+ prefix(options)
+ end
+
+ # An attribute reader for the source string for the resource path prefix. This
+ # method is regenerated at runtime based on what the prefix is set to.
+ def prefix_source
+ prefix # generate #prefix and #prefix_source methods first
+ prefix_source
+ end
+
+ # Sets the prefix for a resource's nested URL (e.g., prefix/collectionname/1.xml ).
+ # Default value is site.path .
+ def prefix=(value = '/')
+ # Replace :placeholders with '#{embedded options[:lookups]}'
+ prefix_call = value.gsub(/:\w+/) { |key| "\#{options[#{key}]}" }
+
+ # Redefine the new methods.
+ code = <<-end_code
+ def prefix_source() "#{value}" end
+ def prefix(options={}) "#{prefix_call}" end
+ end_code
+ silence_warnings { instance_eval code, __FILE__, __LINE__ }
+ rescue
+ logger.error "Couldn't set prefix: #{$!}\n #{code}"
+ raise
+ end
+
+ alias_method :set_prefix, :prefix= #:nodoc:
+
+ alias_method :set_element_name, :element_name= #:nodoc:
+ alias_method :set_collection_name, :collection_name= #:nodoc:
+
+ # Gets the element path for the given ID in +id+. If the +query_options+ parameter is omitted, Rails
+ # will split from the prefix options.
+ #
+ # ==== Options
+ # +prefix_options+:: A hash to add a prefix to the request for nested URL's (e.g., :account_id => 19
+ # would yield a URL like /accounts/19/purchases.xml ).
+ # +query_options+:: A hash to add items to the query string for the request.
+ #
+ # ==== Examples
+ # Post.element_path(1)
+ # # => /posts/1.xml
+ #
+ # Comment.element_path(1, :post_id => 5)
+ # # => /posts/5/comments/1.xml
+ #
+ # Comment.element_path(1, :post_id => 5, :active => 1)
+ # # => /posts/5/comments/1.xml?active=1
+ #
+ # Comment.element_path(1, {:post_id => 5}, {:active => 1})
+ # # => /posts/5/comments/1.xml?active=1
+ #
+ def element_path(id, prefix_options = {}, query_options = nil)
+ prefix_options, query_options = split_options(prefix_options) if query_options.nil?
+ "#{prefix(prefix_options)}#{collection_name}/#{id}.#{format.extension}#{query_string(query_options)}"
+ end
+
+ # Gets the collection path for the REST resources. If the +query_options+ parameter is omitted, Rails
+ # will split from the +prefix_options+.
+ #
+ # ==== Options
+ # +prefix_options+:: A hash to add a prefix to the request for nested URL's (e.g., :account_id => 19
+ # would yield a URL like /accounts/19/purchases.xml ).
+ # +query_options+:: A hash to add items to the query string for the request.
+ #
+ # ==== Examples
+ # Post.collection_path
+ # # => /posts.xml
+ #
+ # Comment.collection_path(:post_id => 5)
+ # # => /posts/5/comments.xml
+ #
+ # Comment.collection_path(:post_id => 5, :active => 1)
+ # # => /posts/5/comments.xml?active=1
+ #
+ # Comment.collection_path({:post_id => 5}, {:active => 1})
+ # # => /posts/5/comments.xml?active=1
+ #
+ def collection_path(prefix_options = {}, query_options = nil)
+ prefix_options, query_options = split_options(prefix_options) if query_options.nil?
+ "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
+ end
+
+ alias_method :set_primary_key, :primary_key= #:nodoc:
+
+ # Create a new resource instance and request to the remote service
+ # that it be saved, making it equivalent to the following simultaneous calls:
+ #
+ # ryan = Person.new(:first => 'ryan')
+ # ryan.save
+ #
+ # The newly created resource is returned. If a failure has occurred an
+ # exception will be raised (see save). If the resource is invalid and
+ # has not been saved then valid? will return false ,
+ # while new? will still return true .
+ #
+ # ==== Examples
+ # Person.create(:name => 'Jeremy', :email => 'myname@nospam.com', :enabled => true)
+ # my_person = Person.find(:first)
+ # my_person.email
+ # # => myname@nospam.com
+ #
+ # dhh = Person.create(:name => 'David', :email => 'dhh@nospam.com', :enabled => true)
+ # dhh.valid?
+ # # => true
+ # dhh.new?
+ # # => false
+ #
+ # # We'll assume that there's a validation that requires the name attribute
+ # that_guy = Person.create(:name => '', :email => 'thatguy@nospam.com', :enabled => true)
+ # that_guy.valid?
+ # # => false
+ # that_guy.new?
+ # # => true
+ #
+ def create(attributes = {})
+ returning(self.new(attributes)) { |res| res.save }
+ end
+
+ # Core method for finding resources. Used similarly to Active Record's find method.
+ #
+ # ==== Arguments
+ # The first argument is considered to be the scope of the query. That is, how many
+ # resources are returned from the request. It can be one of the following.
+ #
+ # +:one+:: Returns a single resource.
+ # +:first+:: Returns the first resource found.
+ # +:all+:: Returns every resource that matches the request.
+ #
+ # ==== Options
+ # +from+:: Sets the path or custom method that resources will be fetched from.
+ # +params+:: Sets query and prefix (nested URL) parameters.
+ #
+ # ==== Examples
+ # Person.find(1)
+ # # => GET /people/1.xml
+ #
+ # Person.find(:all)
+ # # => GET /people.xml
+ #
+ # Person.find(:all, :params => { :title => "CEO" })
+ # # => GET /people.xml?title=CEO
+ #
+ # Person.find(:first, :from => :managers)
+ # # => GET /people/managers.xml
+ #
+ # Person.find(:all, :from => "/companies/1/people.xml")
+ # # => GET /companies/1/people.xml
+ #
+ # Person.find(:one, :from => :leader)
+ # # => GET /people/leader.xml
+ #
+ # Person.find(:one, :from => "/companies/1/manager.xml")
+ # # => GET /companies/1/manager.xml
+ #
+ # StreetAddress.find(1, :params => { :person_id => 1 })
+ # # => GET /people/1/street_addresses/1.xml
+ def find(*arguments)
+ scope = arguments.slice!(0)
+ options = arguments.slice!(0) || {}
+
+ case scope
+ when :all then find_every(options)
+ when :first then find_every(options).first
+ when :one then find_one(options)
+ else find_single(scope, options)
+ end
+ end
+
+ # Deletes the resources with the ID in the +id+ parameter.
+ #
+ # ==== Options
+ # All options specify prefix and query parameters.
+ #
+ # ==== Examples
+ # Event.delete(2)
+ # # => DELETE /events/2
+ #
+ # Event.create(:name => 'Free Concert', :location => 'Community Center')
+ # my_event = Event.find(:first)
+ # # => Events (id: 7)
+ # Event.delete(my_event.id)
+ # # => DELETE /events/7
+ #
+ # # Let's assume a request to events/5/cancel.xml
+ # Event.delete(params[:id])
+ # # => DELETE /events/5
+ #
+ def delete(id, options = {})
+ connection.delete(element_path(id, options))
+ end
+
+ # Asserts the existence of a resource, returning true if the resource is found.
+ #
+ # ==== Examples
+ # Note.create(:title => 'Hello, world.', :body => 'Nothing more for now...')
+ # Note.exists?(1)
+ # # => true
+ #
+ # Note.exists(1349)
+ # # => false
+ def exists?(id, options = {})
+ id && !find_single(id, options).nil?
+ rescue ActiveResource::ResourceNotFound
+ false
+ end
+
+ private
+ # Find every resource
+ def find_every(options)
+ case from = options[:from]
+ when Symbol
+ instantiate_collection(get(from, options[:params]))
+ when String
+ path = "#{from}#{query_string(options[:params])}"
+ instantiate_collection(connection.get(path, headers) || [])
+ else
+ prefix_options, query_options = split_options(options[:params])
+ path = collection_path(prefix_options, query_options)
+ instantiate_collection( (connection.get(path, headers) || []), prefix_options )
+ end
+ end
+
+ # Find a single resource from a one-off URL
+ def find_one(options)
+ case from = options[:from]
+ when Symbol
+ instantiate_record(get(from, options[:params]))
+ when String
+ path = "#{from}#{query_string(options[:params])}"
+ instantiate_record(connection.get(path, headers))
+ end
+ end
+
+ # Find a single resource from the default URL
+ def find_single(scope, options)
+ prefix_options, query_options = split_options(options[:params])
+ path = element_path(scope, prefix_options, query_options)
+ instantiate_record(connection.get(path, headers), prefix_options)
+ end
+
+ def instantiate_collection(collection, prefix_options = {})
+ collection.collect! { |record| instantiate_record(record, prefix_options) }
+ end
+
+ def instantiate_record(record, prefix_options = {})
+ returning new(record) do |resource|
+ resource.prefix_options = prefix_options
+ end
+ end
+
+
+ # Accepts a URI and creates the site URI from that.
+ def create_site_uri_from(site)
+ site.is_a?(URI) ? site.dup : URI.parse(site)
+ end
+
+ # contains a set of the current prefix parameters.
+ def prefix_parameters
+ @prefix_parameters ||= prefix_source.scan(/:\w+/).map { |key| key[1..-1].to_sym }.to_set
+ end
+
+ # Builds the query string for the request.
+ def query_string(options)
+ "?#{options.to_query}" unless options.nil? || options.empty?
+ end
+
+ # split an option hash into two hashes, one containing the prefix options,
+ # and the other containing the leftovers.
+ def split_options(options = {})
+ prefix_options, query_options = {}, {}
+
+ (options || {}).each do |key, value|
+ next if key.blank?
+ (prefix_parameters.include?(key.to_sym) ? prefix_options : query_options)[key.to_sym] = value
+ end
+
+ [ prefix_options, query_options ]
+ end
+ end
+
+ attr_accessor :attributes #:nodoc:
+ attr_accessor :prefix_options #:nodoc:
+
+ # Constructor method for new resources; the optional +attributes+ parameter takes a +Hash+
+ # of attributes for the new resource.
+ #
+ # ==== Examples
+ # my_course = Course.new
+ # my_course.name = "Western Civilization"
+ # my_course.lecturer = "Don Trotter"
+ # my_course.save
+ #
+ # my_other_course = Course.new(:name => "Philosophy: Reason and Being", :lecturer => "Ralph Cling")
+ # my_other_course.save
+ def initialize(attributes = {})
+ @attributes = {}
+ @prefix_options = {}
+ load(attributes)
+ end
+
+ # A method to determine if the resource a new object (i.e., it has not been POSTed to the remote service yet).
+ #
+ # ==== Examples
+ # not_new = Computer.create(:brand => 'Apple', :make => 'MacBook', :vendor => 'MacMall')
+ # not_new.new?
+ # # => false
+ #
+ # is_new = Computer.new(:brand => 'IBM', :make => 'Thinkpad', :vendor => 'IBM')
+ # is_new.new?
+ # # => true
+ #
+ # is_new.save
+ # is_new.new?
+ # # => false
+ #
+ def new?
+ id.nil?
+ end
+
+ # Get the +id+ attribute of the resource.
+ def id
+ attributes[self.class.primary_key]
+ end
+
+ # Set the +id+ attribute of the resource.
+ def id=(id)
+ attributes[self.class.primary_key] = id
+ end
+
+ # Allows ActiveResource objects to be used as parameters in ActionPack URL generation.
+ def to_param
+ id && id.to_s
+ end
+
+ # Test for equality. Resource are equal if and only if +other+ is the same object or
+ # is an instance of the same class, is not +new?+, and has the same +id+.
+ #
+ # ==== Examples
+ # ryan = Person.create(:name => 'Ryan')
+ # jamie = Person.create(:name => 'Jamie')
+ #
+ # ryan == jamie
+ # # => false (Different name attribute and id)
+ #
+ # ryan_again = Person.new(:name => 'Ryan')
+ # ryan == ryan_again
+ # # => false (ryan_again is new?)
+ #
+ # ryans_clone = Person.create(:name => 'Ryan')
+ # ryan == ryans_clone
+ # # => false (Different id attributes)
+ #
+ # ryans_twin = Person.find(ryan.id)
+ # ryan == ryans_twin
+ # # => true
+ #
+ def ==(other)
+ other.equal?(self) || (other.instance_of?(self.class) && !other.new? && other.id == id)
+ end
+
+ # Tests for equality (delegates to ==).
+ def eql?(other)
+ self == other
+ end
+
+ # Delegates to id in order to allow two resources of the same type and id to work with something like:
+ # [Person.find(1), Person.find(2)] & [Person.find(1), Person.find(4)] # => [Person.find(1)]
+ def hash
+ id.hash
+ end
+
+ # Duplicate the current resource without saving it.
+ #
+ # ==== Examples
+ # my_invoice = Invoice.create(:customer => 'That Company')
+ # next_invoice = my_invoice.dup
+ # next_invoice.new?
+ # # => true
+ #
+ # next_invoice.save
+ # next_invoice == my_invoice
+ # # => false (different id attributes)
+ #
+ # my_invoice.customer
+ # # => That Company
+ # next_invoice.customer
+ # # => That Company
+ def dup
+ returning self.class.new do |resource|
+ resource.attributes = @attributes
+ resource.prefix_options = @prefix_options
+ end
+ end
+
+ # A method to save (+POST+) or update (+PUT+) a resource. It delegates to +create+ if a new object,
+ # +update+ if it is existing. If the response to the save includes a body, it will be assumed that this body
+ # is XML for the final object as it looked after the save (which would include attributes like +created_at+
+ # that weren't part of the original submit).
+ #
+ # ==== Examples
+ # my_company = Company.new(:name => 'RoleModel Software', :owner => 'Ken Auer', :size => 2)
+ # my_company.new?
+ # # => true
+ # my_company.save
+ # # => POST /companies/ (create)
+ #
+ # my_company.new?
+ # # => false
+ # my_company.size = 10
+ # my_company.save
+ # # => PUT /companies/1 (update)
+ def save
+ new? ? create : update
+ end
+
+ # Deletes the resource from the remote service.
+ #
+ # ==== Examples
+ # my_id = 3
+ # my_person = Person.find(my_id)
+ # my_person.destroy
+ # Person.find(my_id)
+ # # => 404 (Resource Not Found)
+ #
+ # new_person = Person.create(:name => 'James')
+ # new_id = new_person.id
+ # # => 7
+ # new_person.destroy
+ # Person.find(new_id)
+ # # => 404 (Resource Not Found)
+ def destroy
+ connection.delete(element_path, self.class.headers)
+ end
+
+ # Evaluates to true if this resource is not +new?+ and is
+ # found on the remote service. Using this method, you can check for
+ # resources that may have been deleted between the object's instantiation
+ # and actions on it.
+ #
+ # ==== Examples
+ # Person.create(:name => 'Theodore Roosevelt')
+ # that_guy = Person.find(:first)
+ # that_guy.exists?
+ # # => true
+ #
+ # that_lady = Person.new(:name => 'Paul Bean')
+ # that_lady.exists?
+ # # => false
+ #
+ # guys_id = that_guy.id
+ # Person.delete(guys_id)
+ # that_guy.exists?
+ # # => false
+ def exists?
+ !new? && self.class.exists?(id, :params => prefix_options)
+ end
+
+ # A method to convert the the resource to an XML string.
+ #
+ # ==== Options
+ # The +options+ parameter is handed off to the +to_xml+ method on each
+ # attribute, so it has the same options as the +to_xml+ methods in
+ # ActiveSupport.
+ #
+ # indent:: Set the indent level for the XML output (default is +2+).
+ # dasherize:: Boolean option to determine whether or not element names should
+ # replace underscores with dashes (default is +false+).
+ # skip_instruct:: Toggle skipping the +instruct!+ call on the XML builder
+ # that generates the XML declaration (default is +false+).
+ #
+ # ==== Examples
+ # my_group = SubsidiaryGroup.find(:first)
+ # my_group.to_xml
+ # # =>
+ # # [...]
+ #
+ # my_group.to_xml(:dasherize => true)
+ # # =>
+ # # [...]
+ #
+ # my_group.to_xml(:skip_instruct => true)
+ # # => [...]
+ def to_xml(options={})
+ attributes.to_xml({:root => self.class.element_name}.merge(options))
+ end
+
+ # A method to reload the attributes of this object from the remote web service.
+ #
+ # ==== Examples
+ # my_branch = Branch.find(:first)
+ # my_branch.name
+ # # => Wislon Raod
+ #
+ # # Another client fixes the typo...
+ #
+ # my_branch.name
+ # # => Wislon Raod
+ # my_branch.reload
+ # my_branch.name
+ # # => Wilson Road
+ def reload
+ self.load(self.class.find(id, :params => @prefix_options).attributes)
+ end
+
+ # A method to manually load attributes from a hash. Recursively loads collections of
+ # resources. This method is called in initialize and create when a +Hash+ of attributes
+ # is provided.
+ #
+ # ==== Examples
+ # my_attrs = {:name => 'J&J Textiles', :industry => 'Cloth and textiles'}
+ #
+ # the_supplier = Supplier.find(:first)
+ # the_supplier.name
+ # # => 'J&M Textiles'
+ # the_supplier.load(my_attrs)
+ # the_supplier.name('J&J Textiles')
+ #
+ # # These two calls are the same as Supplier.new(my_attrs)
+ # my_supplier = Supplier.new
+ # my_supplier.load(my_attrs)
+ #
+ # # These three calls are the same as Supplier.create(my_attrs)
+ # your_supplier = Supplier.new
+ # your_supplier.load(my_attrs)
+ # your_supplier.save
+ def load(attributes)
+ raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
+ @prefix_options, attributes = split_options(attributes)
+ attributes.each do |key, value|
+ @attributes[key.to_s] =
+ case value
+ when Array
+ resource = find_or_create_resource_for_collection(key)
+ value.map { |attrs| resource.new(attrs) }
+ when Hash
+ resource = find_or_create_resource_for(key)
+ resource.new(value)
+ else
+ value.dup rescue value
+ end
+ end
+ self
+ end
+
+ # For checking respond_to? without searching the attributes (which is faster).
+ alias_method :respond_to_without_attributes?, :respond_to?
+
+ # A method to determine if an object responds to a message (e.g., a method call). In Active Resource, a +Person+ object with a
+ # +name+ attribute can answer +true+ to +my_person.respond_to?("name")+, +my_person.respond_to?("name=")+, and
+ # +my_person.respond_to?("name?")+.
+ def respond_to?(method, include_priv = false)
+ method_name = method.to_s
+ if attributes.nil?
+ return super
+ elsif attributes.has_key?(method_name)
+ return true
+ elsif ['?','='].include?(method_name.last) && attributes.has_key?(method_name.first(-1))
+ return true
+ end
+ # super must be called at the end of the method, because the inherited respond_to?
+ # would return true for generated readers, even if the attribute wasn't present
+ super
+ end
+
+
+ protected
+ def connection(refresh = false)
+ self.class.connection(refresh)
+ end
+
+ # Update the resource on the remote service.
+ def update
+ returning connection.put(element_path(prefix_options), to_xml, self.class.headers) do |response|
+ load_attributes_from_response(response)
+ end
+ end
+
+ # Create (i.e., save to the remote service) the new resource.
+ def create
+ returning connection.post(collection_path, to_xml, self.class.headers) do |response|
+ self.id = id_from_response(response)
+ load_attributes_from_response(response)
+ end
+ end
+
+ def load_attributes_from_response(response)
+ if response['Content-Length'] != "0" && response.body.strip.size > 0
+ load(self.class.format.decode(response.body))
+ end
+ end
+
+ # Takes a response from a typical create post and pulls the ID out
+ def id_from_response(response)
+ response['Location'][/\/([^\/]*?)(\.\w+)?$/, 1]
+ end
+
+ def element_path(options = nil)
+ self.class.element_path(id, options || prefix_options)
+ end
+
+ def collection_path(options = nil)
+ self.class.collection_path(options || prefix_options)
+ end
+
+ private
+ # Tries to find a resource for a given collection name; if it fails, then the resource is created
+ def find_or_create_resource_for_collection(name)
+ find_or_create_resource_for(name.to_s.singularize)
+ end
+
+ # Tries to find a resource for a given name; if it fails, then the resource is created
+ def find_or_create_resource_for(name)
+ resource_name = name.to_s.camelize
+
+ # FIXME: Make it generic enough to support any depth of module nesting
+ if (ancestors = self.class.name.split("::")).size > 1
+ begin
+ ancestors.first.constantize.const_get(resource_name)
+ rescue NameError
+ self.class.const_get(resource_name)
+ end
+ else
+ self.class.const_get(resource_name)
+ end
+ rescue NameError
+ resource = self.class.const_set(resource_name, Class.new(ActiveResource::Base))
+ resource.prefix = self.class.prefix
+ resource.site = self.class.site
+ resource
+ end
+
+ def split_options(options = {})
+ self.class.send!(:split_options, options)
+ end
+
+ def method_missing(method_symbol, *arguments) #:nodoc:
+ method_name = method_symbol.to_s
+
+ case method_name.last
+ when "="
+ attributes[method_name.first(-1)] = arguments.first
+ when "?"
+ attributes[method_name.first(-1)]
+ else
+ attributes.has_key?(method_name) ? attributes[method_name] : super
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activeresource/lib/active_resource/connection.rb b/vendor/rails/activeresource/lib/active_resource/connection.rb
new file mode 100644
index 00000000..2bf83b16
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource/connection.rb
@@ -0,0 +1,172 @@
+require 'net/https'
+require 'date'
+require 'time'
+require 'uri'
+require 'benchmark'
+
+module ActiveResource
+ class ConnectionError < StandardError # :nodoc:
+ attr_reader :response
+
+ def initialize(response, message = nil)
+ @response = response
+ @message = message
+ end
+
+ def to_s
+ "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
+ end
+ end
+
+ # 3xx Redirection
+ class Redirection < ConnectionError # :nodoc:
+ def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
+ end
+
+ # 4xx Client Error
+ class ClientError < ConnectionError; end # :nodoc:
+
+ # 400 Bad Request
+ class BadRequest < ClientError; end # :nodoc
+
+ # 401 Unauthorized
+ class UnauthorizedAccess < ClientError; end # :nodoc
+
+ # 403 Forbidden
+ class ForbiddenAccess < ClientError; end # :nodoc
+
+ # 404 Not Found
+ class ResourceNotFound < ClientError; end # :nodoc:
+
+ # 409 Conflict
+ class ResourceConflict < ClientError; end # :nodoc:
+
+ # 5xx Server Error
+ class ServerError < ConnectionError; end # :nodoc:
+
+ # 405 Method Not Allowed
+ class MethodNotAllowed < ClientError # :nodoc:
+ def allowed_methods
+ @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
+ end
+ end
+
+ # Class to handle connections to remote web services.
+ # This class is used by ActiveResource::Base to interface with REST
+ # services.
+ class Connection
+ attr_reader :site
+ attr_accessor :format
+
+ class << self
+ def requests
+ @@requests ||= []
+ end
+ end
+
+ # The +site+ parameter is required and will set the +site+
+ # attribute to the URI for the remote resource service.
+ def initialize(site, format = ActiveResource::Formats[:xml])
+ raise ArgumentError, 'Missing site URI' unless site
+ self.site = site
+ self.format = format
+ end
+
+ # Set URI for remote service.
+ def site=(site)
+ @site = site.is_a?(URI) ? site : URI.parse(site)
+ end
+
+ # Execute a GET request.
+ # Used to get (find) resources.
+ def get(path, headers = {})
+ format.decode(request(:get, path, build_request_headers(headers)).body)
+ end
+
+ # Execute a DELETE request (see HTTP protocol documentation if unfamiliar).
+ # Used to delete resources.
+ def delete(path, headers = {})
+ request(:delete, path, build_request_headers(headers))
+ end
+
+ # Execute a PUT request (see HTTP protocol documentation if unfamiliar).
+ # Used to update resources.
+ def put(path, body = '', headers = {})
+ request(:put, path, body.to_s, build_request_headers(headers))
+ end
+
+ # Execute a POST request.
+ # Used to create new resources.
+ def post(path, body = '', headers = {})
+ request(:post, path, body.to_s, build_request_headers(headers))
+ end
+
+
+ private
+ # Makes request to remote service.
+ def request(method, path, *arguments)
+ logger.info "#{method.to_s.upcase} #{site.scheme}://#{site.host}:#{site.port}#{path}" if logger
+ result = nil
+ time = Benchmark.realtime { result = http.send(method, path, *arguments) }
+ logger.info "--> #{result.code} #{result.message} (#{result.body ? result.body : 0}b %.2fs)" % time if logger
+ handle_response(result)
+ end
+
+ # Handles response and error codes from remote service.
+ def handle_response(response)
+ case response.code.to_i
+ when 301,302
+ raise(Redirection.new(response))
+ when 200...400
+ response
+ when 400
+ raise(BadRequest.new(response))
+ when 401
+ raise(UnauthorizedAccess.new(response))
+ when 403
+ raise(ForbiddenAccess.new(response))
+ when 404
+ raise(ResourceNotFound.new(response))
+ when 405
+ raise(MethodNotAllowed.new(response))
+ when 409
+ raise(ResourceConflict.new(response))
+ when 422
+ raise(ResourceInvalid.new(response))
+ when 401...500
+ raise(ClientError.new(response))
+ when 500...600
+ raise(ServerError.new(response))
+ else
+ raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
+ end
+ end
+
+ # Creates new Net::HTTP instance for communication with
+ # remote service and resources.
+ def http
+ http = Net::HTTP.new(@site.host, @site.port)
+ http.use_ssl = @site.is_a?(URI::HTTPS)
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
+ http
+ end
+
+ def default_header
+ @default_header ||= { 'Content-Type' => format.mime_type }
+ end
+
+ # Builds headers for request to remote service.
+ def build_request_headers(headers)
+ authorization_header.update(default_header).update(headers)
+ end
+
+ # Sets authorization header; authentication information is pulled from credentials provided with site URI.
+ def authorization_header
+ (@site.user || @site.password ? { 'Authorization' => 'Basic ' + ["#{@site.user}:#{ @site.password}"].pack('m').delete("\r\n") } : {})
+ end
+
+ def logger #:nodoc:
+ ActiveResource::Base.logger
+ end
+ end
+end
diff --git a/vendor/rails/activeresource/lib/active_resource/custom_methods.rb b/vendor/rails/activeresource/lib/active_resource/custom_methods.rb
new file mode 100644
index 00000000..e08c6643
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource/custom_methods.rb
@@ -0,0 +1,105 @@
+# A module to support custom REST methods and sub-resources, allowing you to break out
+# of the "default" REST methods with your own custom resource requests. For example,
+# say you use Rails to expose a REST service and configure your routes with:
+#
+# map.resources :people, :new => { :register => :post },
+# :element => { :promote => :put, :deactivate => :delete }
+# :collection => { :active => :get }
+#
+# This route set creates routes for the following http requests:
+#
+# POST /people/new/register.xml #=> PeopleController.register
+# PUT /people/1/promote.xml #=> PeopleController.promote with :id => 1
+# DELETE /people/1/deactivate.xml #=> PeopleController.deactivate with :id => 1
+# GET /people/active.xml #=> PeopleController.active
+#
+# Using this module, Active Resource can use these custom REST methods just like the
+# standard methods.
+#
+# class Person < ActiveResource::Base
+# self.site = "http://37s.sunrise.i:3000"
+# end
+#
+# Person.new(:name => 'Ryan).post(:register) # POST /people/new/register.xml
+# # => { :id => 1, :name => 'Ryan' }
+#
+# Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.xml
+# Person.find(1).delete(:deactivate) # DELETE /people/1/deactivate.xml
+#
+# Person.get(:active) # GET /people/active.xml
+# # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
+#
+module ActiveResource
+ module CustomMethods
+ def self.included(within)
+ within.class_eval do
+ extend ActiveResource::CustomMethods::ClassMethods
+ include ActiveResource::CustomMethods::InstanceMethods
+
+ class << self
+ alias :orig_delete :delete
+
+ def get(method_name, options = {})
+ connection.get(custom_method_collection_url(method_name, options), headers)
+ end
+
+ def post(method_name, options = {}, body = '')
+ connection.post(custom_method_collection_url(method_name, options), body, headers)
+ end
+
+ def put(method_name, options = {}, body = '')
+ connection.put(custom_method_collection_url(method_name, options), body, headers)
+ end
+
+ # Need to jump through some hoops to retain the original class 'delete' method
+ def delete(custom_method_name, options = {})
+ if (custom_method_name.is_a?(Symbol))
+ connection.delete(custom_method_collection_url(custom_method_name, options), headers)
+ else
+ orig_delete(custom_method_name, options)
+ end
+ end
+ end
+ end
+ end
+
+ module ClassMethods
+ def custom_method_collection_url(method_name, options = {})
+ prefix_options, query_options = split_options(options)
+ "#{prefix(prefix_options)}#{collection_name}/#{method_name}.xml#{query_string(query_options)}"
+ end
+ end
+
+ module InstanceMethods
+ def get(method_name, options = {})
+ connection.get(custom_method_element_url(method_name, options), self.class.headers)
+ end
+
+ def post(method_name, options = {}, body = '')
+ if new?
+ connection.post(custom_method_new_element_url(method_name, options), (body.nil? ? to_xml : body), self.class.headers)
+ else
+ connection.post(custom_method_element_url(method_name, options), body, self.class.headers)
+ end
+ end
+
+ def put(method_name, options = {}, body = '')
+ connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
+ end
+
+ def delete(method_name, options = {})
+ connection.delete(custom_method_element_url(method_name, options), self.class.headers)
+ end
+
+
+ private
+ def custom_method_element_url(method_name, options = {})
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.xml#{self.class.send!(:query_string, options)}"
+ end
+
+ def custom_method_new_element_url(method_name, options = {})
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.xml#{self.class.send!(:query_string, options)}"
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activeresource/lib/active_resource/formats.rb b/vendor/rails/activeresource/lib/active_resource/formats.rb
new file mode 100644
index 00000000..28864cf5
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource/formats.rb
@@ -0,0 +1,14 @@
+module ActiveResource
+ module Formats
+ # Lookup the format class from a mime type reference symbol. Example:
+ #
+ # ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat
+ # ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat
+ def self.[](mime_type_reference)
+ ActiveResource::Formats.const_get(mime_type_reference.to_s.camelize + "Format")
+ end
+ end
+end
+
+require 'active_resource/formats/xml_format'
+require 'active_resource/formats/json_format'
\ No newline at end of file
diff --git a/vendor/rails/activeresource/lib/active_resource/formats/json_format.rb b/vendor/rails/activeresource/lib/active_resource/formats/json_format.rb
new file mode 100644
index 00000000..df0d6ca3
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource/formats/json_format.rb
@@ -0,0 +1,23 @@
+module ActiveResource
+ module Formats
+ module JsonFormat
+ extend self
+
+ def extension
+ "json"
+ end
+
+ def mime_type
+ "application/json"
+ end
+
+ def encode(hash)
+ hash.to_json
+ end
+
+ def decode(json)
+ ActiveSupport::JSON.decode(json)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activeresource/lib/active_resource/formats/xml_format.rb b/vendor/rails/activeresource/lib/active_resource/formats/xml_format.rb
new file mode 100644
index 00000000..01c28dce
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource/formats/xml_format.rb
@@ -0,0 +1,34 @@
+module ActiveResource
+ module Formats
+ module XmlFormat
+ extend self
+
+ def extension
+ "xml"
+ end
+
+ def mime_type
+ "application/xml"
+ end
+
+ def encode(hash)
+ hash.to_xml
+ end
+
+ def decode(xml)
+ from_xml_data(Hash.from_xml(xml))
+ end
+
+ private
+ # Manipulate from_xml Hash, because xml_simple is not exactly what we
+ # want for ActiveResource.
+ def from_xml_data(data)
+ if data.is_a?(Hash) && data.keys.size == 1
+ data.values.first
+ else
+ data
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activeresource/lib/active_resource/http_mock.rb b/vendor/rails/activeresource/lib/active_resource/http_mock.rb
new file mode 100644
index 00000000..b54bf09c
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource/http_mock.rb
@@ -0,0 +1,147 @@
+require 'active_resource/connection'
+
+module ActiveResource
+ class InvalidRequestError < StandardError; end #:nodoc:
+
+ class HttpMock
+ class Responder
+ def initialize(responses)
+ @responses = responses
+ end
+
+ for method in [ :post, :put, :get, :delete ]
+ module_eval <<-EOE
+ def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {})
+ @responses[Request.new(:#{method}, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
+ end
+ EOE
+ end
+ end
+
+ class << self
+ def requests
+ @@requests ||= []
+ end
+
+ def responses
+ @@responses ||= {}
+ end
+
+ def respond_to(pairs = {})
+ reset!
+ pairs.each do |(path, response)|
+ responses[path] = response
+ end
+
+ if block_given?
+ yield Responder.new(responses)
+ else
+ Responder.new(responses)
+ end
+ end
+
+ def reset!
+ requests.clear
+ responses.clear
+ end
+ end
+
+ for method in [ :post, :put ]
+ module_eval <<-EOE
+ def #{method}(path, body, headers)
+ request = ActiveResource::Request.new(:#{method}, path, body, headers)
+ self.class.requests << request
+ self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for: \#{request.inspect}"))
+ end
+ EOE
+ end
+
+ for method in [ :get, :delete ]
+ module_eval <<-EOE
+ def #{method}(path, headers)
+ request = ActiveResource::Request.new(:#{method}, path, nil, headers)
+ self.class.requests << request
+ self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for: \#{request.inspect}"))
+ end
+ EOE
+ end
+
+ def initialize(site)
+ @site = site
+ end
+ end
+
+ class Request
+ attr_accessor :path, :method, :body, :headers
+
+ def initialize(method, path, body = nil, headers = {})
+ @method, @path, @body, @headers = method, path, body, headers.dup
+ @headers.update('Content-Type' => 'application/xml')
+ end
+
+ def ==(other_request)
+ other_request.hash == hash
+ end
+
+ def eql?(other_request)
+ self == other_request
+ end
+
+ def to_s
+ "<#{method.to_s.upcase}: #{path} [#{headers}] (#{body})>"
+ end
+
+ def hash
+ "#{path}#{method}#{headers}".hash
+ end
+ end
+
+ class Response
+ attr_accessor :body, :message, :code, :headers
+
+ def initialize(body, message = 200, headers = {})
+ @body, @message, @headers = body, message.to_s, headers
+ @code = @message[0,3].to_i
+
+ resp_cls = Net::HTTPResponse::CODE_TO_OBJ[@code.to_s]
+ if resp_cls && !resp_cls.body_permitted?
+ @body = nil
+ end
+
+ if @body.nil?
+ self['Content-Length'] = "0"
+ else
+ self['Content-Length'] = body.size.to_s
+ end
+ end
+
+ def success?
+ (200..299).include?(code)
+ end
+
+ def [](key)
+ headers[key]
+ end
+
+ def []=(key, value)
+ headers[key] = value
+ end
+
+ def ==(other)
+ if (other.is_a?(Response))
+ other.body == body && other.message == message && other.headers == headers
+ else
+ false
+ end
+ end
+ end
+
+ class Connection
+ private
+ silence_warnings do
+ def http
+ @http ||= HttpMock.new(@site)
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activeresource/lib/active_resource/validations.rb b/vendor/rails/activeresource/lib/active_resource/validations.rb
new file mode 100644
index 00000000..57d2ae55
--- /dev/null
+++ b/vendor/rails/activeresource/lib/active_resource/validations.rb
@@ -0,0 +1,288 @@
+module ActiveResource
+ class ResourceInvalid < ClientError #:nodoc:
+ end
+
+ # Active Resource validation is reported to and from this object, which is used by Base#save
+ # to determine whether the object in a valid state to be saved. See usage example in Validations.
+ class Errors
+ include Enumerable
+ attr_reader :errors
+
+ delegate :empty?, :to => :errors
+
+ def initialize(base) # :nodoc:
+ @base, @errors = base, {}
+ end
+
+ # Add an error to the base Active Resource object rather than an attribute.
+ #
+ # ==== Examples
+ # my_folder = Folder.find(1)
+ # my_folder.errors.add_to_base("You can't edit an existing folder")
+ # my_folder.errors.on_base
+ # # => "You can't edit an existing folder"
+ #
+ # my_folder.errors.add_to_base("This folder has been tagged as frozen")
+ # my_folder.valid?
+ # # => false
+ # my_folder.errors.on_base
+ # # => ["You can't edit an existing folder", "This folder has been tagged as frozen"]
+ #
+ def add_to_base(msg)
+ add(:base, msg)
+ end
+
+ # Adds an error to an Active Resource object's attribute (named for the +attribute+ parameter)
+ # with the error message in +msg+.
+ #
+ # ==== Examples
+ # my_resource = Node.find(1)
+ # my_resource.errors.add('name', 'can not be "base"') if my_resource.name == 'base'
+ # my_resource.errors.on('name')
+ # # => 'can not be "base"!'
+ #
+ # my_resource.errors.add('desc', 'can not be blank') if my_resource.desc == ''
+ # my_resource.valid?
+ # # => false
+ # my_resource.errors.on('desc')
+ # # => 'can not be blank!'
+ #
+ def add(attribute, msg)
+ @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil?
+ @errors[attribute.to_s] << msg
+ end
+
+ # Returns true if the specified +attribute+ has errors associated with it.
+ #
+ # ==== Examples
+ # my_resource = Disk.find(1)
+ # my_resource.errors.add('location', 'must be Main') unless my_resource.location == 'Main'
+ # my_resource.errors.on('location')
+ # # => 'must be Main!'
+ #
+ # my_resource.errors.invalid?('location')
+ # # => true
+ # my_resource.errors.invalid?('name')
+ # # => false
+ def invalid?(attribute)
+ !@errors[attribute.to_s].nil?
+ end
+
+ # A method to return the errors associated with +attribute+, which returns nil, if no errors are
+ # associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+,
+ # or an array of error messages if more than one error is associated with the specified +attribute+.
+ #
+ # ==== Examples
+ # my_person = Person.new(params[:person])
+ # my_person.errors.on('login')
+ # # => nil
+ #
+ # my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+ # my_person.errors.on('login')
+ # # => 'can not be empty'
+ #
+ # my_person.errors.add('login', 'can not be longer than 10 characters') if my_person.login.length > 10
+ # my_person.errors.on('login')
+ # # => ['can not be empty', 'can not be longer than 10 characters']
+ def on(attribute)
+ errors = @errors[attribute.to_s]
+ return nil if errors.nil?
+ errors.size == 1 ? errors.first : errors
+ end
+
+ alias :[] :on
+
+ # A method to return errors assigned to +base+ object through add_to_base, which returns nil, if no errors are
+ # associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+,
+ # or an array of error messages if more than one error is associated with the specified +attribute+.
+ #
+ # ==== Examples
+ # my_account = Account.find(1)
+ # my_account.errors.on_base
+ # # => nil
+ #
+ # my_account.errors.add_to_base("This account is frozen")
+ # my_account.errors.on_base
+ # # => "This account is frozen"
+ #
+ # my_account.errors.add_to_base("This account has been closed")
+ # my_account.errors.on_base
+ # # => ["This account is frozen", "This account has been closed"]
+ #
+ def on_base
+ on(:base)
+ end
+
+ # Yields each attribute and associated message per error added.
+ #
+ # ==== Examples
+ # my_person = Person.new(params[:person])
+ #
+ # my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+ # my_person.errors.add('password', 'can not be empty') if my_person.password == ''
+ # messages = ''
+ # my_person.errors.each {|attr, msg| messages += attr.humanize + " " + msg + " "}
+ # messages
+ # # => "Login can not be empty Password can not be empty "
+ #
+ def each
+ @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
+ end
+
+ # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
+ # through iteration as "First name can't be empty".
+ #
+ # ==== Examples
+ # my_person = Person.new(params[:person])
+ #
+ # my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+ # my_person.errors.add('password', 'can not be empty') if my_person.password == ''
+ # messages = ''
+ # my_person.errors.each_full {|msg| messages += msg + " "}
+ # messages
+ # # => "Login can not be empty Password can not be empty "
+ #
+ def each_full
+ full_messages.each { |msg| yield msg }
+ end
+
+ # Returns all the full error messages in an array.
+ #
+ # ==== Examples
+ # my_person = Person.new(params[:person])
+ #
+ # my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+ # my_person.errors.add('password', 'can not be empty') if my_person.password == ''
+ # messages = ''
+ # my_person.errors.full_messages.each {|msg| messages += msg + " "}
+ # messages
+ # # => "Login can not be empty Password can not be empty "
+ #
+ def full_messages
+ full_messages = []
+
+ @errors.each_key do |attr|
+ @errors[attr].each do |msg|
+ next if msg.nil?
+
+ if attr == "base"
+ full_messages << msg
+ else
+ full_messages << [attr.humanize, msg].join(' ')
+ end
+ end
+ end
+ full_messages
+ end
+
+ def clear
+ @errors = {}
+ end
+
+ # Returns the total number of errors added. Two errors added to the same attribute will be counted as such
+ # with this as well.
+ #
+ # ==== Examples
+ # my_person = Person.new(params[:person])
+ # my_person.errors.size
+ # # => 0
+ #
+ # my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+ # my_person.errors.add('password', 'can not be empty') if my_person.password == ''
+ # my_person.error.size
+ # # => 2
+ #
+ def size
+ @errors.values.inject(0) { |error_count, attribute| error_count + attribute.size }
+ end
+
+ alias_method :count, :size
+ alias_method :length, :size
+
+ # Grabs errors from the XML response.
+ def from_xml(xml)
+ clear
+ humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) }
+ messages = Hash.from_xml(xml)['errors']['error'] rescue []
+ messages.each do |message|
+ attr_message = humanized_attributes.keys.detect do |attr_name|
+ if message[0, attr_name.size + 1] == "#{attr_name} "
+ add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1]
+ end
+ end
+
+ add_to_base message if attr_message.nil?
+ end
+ end
+ end
+
+ # Module to allow validation of ActiveResource objects, which creates an Errors instance for every resource.
+ # Methods are implemented by overriding +Base#validate+ or its variants Each of these methods can inspect
+ # the state of the object, which usually means ensuring that a number of attributes have a certain value
+ # (such as not empty, within a given range, matching a certain regular expression and so on).
+ #
+ # ==== Example
+ #
+ # class Person < ActiveResource::Base
+ # self.site = "http://www.localhost.com:3000/"
+ # protected
+ # def validate
+ # errors.add_on_empty %w( first_name last_name )
+ # errors.add("phone_number", "has invalid format") unless phone_number =~ /[0-9]*/
+ # end
+ #
+ # def validate_on_create # is only run the first time a new object is saved
+ # unless valid_member?(self)
+ # errors.add("membership_discount", "has expired")
+ # end
+ # end
+ #
+ # def validate_on_update
+ # errors.add_to_base("No changes have occurred") if unchanged_attributes?
+ # end
+ # end
+ #
+ # person = Person.new("first_name" => "Jim", "phone_number" => "I will not tell you.")
+ # person.save # => false (and doesn't do the save)
+ # person.errors.empty? # => false
+ # person.errors.count # => 2
+ # person.errors.on "last_name" # => "can't be empty"
+ # person.attributes = { "last_name" => "Halpert", "phone_number" => "555-5555" }
+ # person.save # => true (and person is now saved to the remote service)
+ #
+ module Validations
+ def self.included(base) # :nodoc:
+ base.class_eval do
+ alias_method_chain :save, :validation
+ end
+ end
+
+ # Validate a resource and save (POST) it to the remote web service.
+ def save_with_validation
+ save_without_validation
+ true
+ rescue ResourceInvalid => error
+ errors.from_xml(error.response.body)
+ false
+ end
+
+ # Checks for errors on an object (i.e., is resource.errors empty?).
+ #
+ # ==== Examples
+ # my_person = Person.create(params[:person])
+ # my_person.valid?
+ # # => true
+ #
+ # my_person.errors.add('login', 'can not be empty') if my_person.login == ''
+ # my_person.valid?
+ # # => false
+ def valid?
+ errors.empty?
+ end
+
+ # Returns the Errors object that holds all information about attribute error messages.
+ def errors
+ @errors ||= Errors.new(self)
+ end
+ end
+end
diff --git a/vendor/rails/actionwebservice/lib/action_web_service/version.rb b/vendor/rails/activeresource/lib/active_resource/version.rb
similarity index 55%
rename from vendor/rails/actionwebservice/lib/action_web_service/version.rb
rename to vendor/rails/activeresource/lib/active_resource/version.rb
index a1b3d592..4f7de5e1 100644
--- a/vendor/rails/actionwebservice/lib/action_web_service/version.rb
+++ b/vendor/rails/activeresource/lib/active_resource/version.rb
@@ -1,8 +1,8 @@
-module ActionWebService
+module ActiveResource
module VERSION #:nodoc:
- MAJOR = 1
- MINOR = 2
- TINY = 5
+ MAJOR = 2
+ MINOR = 0
+ TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end
diff --git a/vendor/rails/activeresource/lib/activeresource.rb b/vendor/rails/activeresource/lib/activeresource.rb
new file mode 100644
index 00000000..e076455b
--- /dev/null
+++ b/vendor/rails/activeresource/lib/activeresource.rb
@@ -0,0 +1 @@
+require 'active_resource'
diff --git a/vendor/rails/activeresource/test/abstract_unit.rb b/vendor/rails/activeresource/test/abstract_unit.rb
new file mode 100644
index 00000000..db1e0b95
--- /dev/null
+++ b/vendor/rails/activeresource/test/abstract_unit.rb
@@ -0,0 +1,10 @@
+require 'test/unit'
+
+$:.unshift "#{File.dirname(__FILE__)}/../lib"
+require 'active_resource'
+require 'active_resource/http_mock'
+
+$:.unshift "#{File.dirname(__FILE__)}/../test"
+require 'setter_trap'
+
+ActiveResource::Base.logger = Logger.new("#{File.dirname(__FILE__)}/debug.log")
\ No newline at end of file
diff --git a/vendor/rails/activeresource/test/authorization_test.rb b/vendor/rails/activeresource/test/authorization_test.rb
new file mode 100644
index 00000000..58bd36cb
--- /dev/null
+++ b/vendor/rails/activeresource/test/authorization_test.rb
@@ -0,0 +1,82 @@
+require "#{File.dirname(__FILE__)}/abstract_unit"
+require 'base64'
+
+class AuthorizationTest < Test::Unit::TestCase
+ Response = Struct.new(:code)
+
+ def setup
+ @conn = ActiveResource::Connection.new('http://localhost')
+ @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
+ @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person')
+ @authenticated_conn = ActiveResource::Connection.new("http://david:test123@localhost")
+ @authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' }
+
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/2.xml", @authorization_request_header, @david
+ mock.put "/people/2.xml", @authorization_request_header, nil, 204
+ mock.delete "/people/2.xml", @authorization_request_header, nil, 200
+ mock.post "/people/2/addresses.xml", @authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5'
+ end
+ end
+
+ def test_authorization_header
+ authorization_header = @authenticated_conn.send!(:authorization_header)
+ assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization']
+ authorization = authorization_header["Authorization"].to_s.split
+
+ assert_equal "Basic", authorization[0]
+ assert_equal ["david", "test123"], Base64.decode64(authorization[1]).split(":")[0..1]
+ end
+
+ def test_authorization_header_with_username_but_no_password
+ @conn = ActiveResource::Connection.new("http://david:@localhost")
+ authorization_header = @conn.send!(:authorization_header)
+ authorization = authorization_header["Authorization"].to_s.split
+
+ assert_equal "Basic", authorization[0]
+ assert_equal ["david"], Base64.decode64(authorization[1]).split(":")[0..1]
+ end
+
+ def test_authorization_header_with_password_but_no_username
+ @conn = ActiveResource::Connection.new("http://:test123@localhost")
+ authorization_header = @conn.send!(:authorization_header)
+ authorization = authorization_header["Authorization"].to_s.split
+
+ assert_equal "Basic", authorization[0]
+ assert_equal ["", "test123"], Base64.decode64(authorization[1]).split(":")[0..1]
+ end
+
+ def test_get
+ david = @authenticated_conn.get("/people/2.xml")
+ assert_equal "David", david["name"]
+ end
+
+ def test_post
+ response = @authenticated_conn.post("/people/2/addresses.xml")
+ assert_equal "/people/1/addresses/5", response["Location"]
+ end
+
+ def test_put
+ response = @authenticated_conn.put("/people/2.xml")
+ assert_equal 204, response.code
+ end
+
+ def test_delete
+ response = @authenticated_conn.delete("/people/2.xml")
+ assert_equal 200, response.code
+ end
+
+ def test_raises_invalid_request_on_unauthorized_requests
+ assert_raises(ActiveResource::InvalidRequestError) { @conn.post("/people/2.xml") }
+ assert_raises(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") }
+ assert_raises(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") }
+ assert_raises(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") }
+ end
+
+ protected
+ def assert_response_raises(klass, code)
+ assert_raise(klass, "Expected response code #{code} to raise #{klass}") do
+ @conn.send!(:handle_response, Response.new(code))
+ end
+ end
+end
diff --git a/vendor/rails/activeresource/test/base/custom_methods_test.rb b/vendor/rails/activeresource/test/base/custom_methods_test.rb
new file mode 100644
index 00000000..9864e987
--- /dev/null
+++ b/vendor/rails/activeresource/test/base/custom_methods_test.rb
@@ -0,0 +1,96 @@
+require "#{File.dirname(__FILE__)}/../abstract_unit"
+require "#{File.dirname(__FILE__)}/../fixtures/person"
+require "#{File.dirname(__FILE__)}/../fixtures/street_address"
+
+class CustomMethodsTest < Test::Unit::TestCase
+ def setup
+ @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
+ @matz_deep = { :id => 1, :name => 'Matz', :other => 'other' }.to_xml(:root => 'person')
+ @matz_array = [{ :id => 1, :name => 'Matz' }].to_xml(:root => 'people')
+ @ryan = { :name => 'Ryan' }.to_xml(:root => 'person')
+ @addy = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address')
+ @addy_deep = { :id => 1, :street => '12345 Street', :zip => "27519" }.to_xml(:root => 'address')
+ @default_request_headers = { 'Content-Type' => 'application/xml' }
+
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1.xml", {}, @matz
+ mock.get "/people/1/shallow.xml", {}, @matz
+ mock.get "/people/1/deep.xml", {}, @matz_deep
+ mock.get "/people/retrieve.xml?name=Matz", {}, @matz_array
+ mock.get "/people/managers.xml", {}, @matz_array
+ mock.post "/people/hire.xml?name=Matz", {}, nil, 201
+ mock.put "/people/1/promote.xml?position=Manager", {}, nil, 204
+ mock.put "/people/promote.xml?name=Matz", {}, nil, 204, {}
+ mock.put "/people/sort.xml?by=name", {}, nil, 204
+ mock.delete "/people/deactivate.xml?name=Matz", {}, nil, 200
+ mock.delete "/people/1/deactivate.xml", {}, nil, 200
+ mock.post "/people/new/register.xml", {}, @ryan, 201, 'Location' => '/people/5.xml'
+ mock.post "/people/1/register.xml", {}, @matz, 201
+ mock.get "/people/1/addresses/1.xml", {}, @addy
+ mock.get "/people/1/addresses/1/deep.xml", {}, @addy_deep
+ mock.put "/people/1/addresses/1/normalize_phone.xml?locale=US", {}, nil, 204
+ mock.put "/people/1/addresses/sort.xml?by=name", {}, nil, 204
+ mock.post "/people/1/addresses/new/link.xml", {}, { :street => '12345 Street' }.to_xml(:root => 'address'), 201, 'Location' => '/people/1/addresses/2.xml'
+ end
+ end
+
+ def teardown
+ ActiveResource::HttpMock.reset!
+ end
+
+ def test_custom_collection_method
+ # GET
+ assert_equal([{ "id" => 1, "name" => 'Matz' }], Person.get(:retrieve, :name => 'Matz'))
+
+ # POST
+ assert_equal(ActiveResource::Response.new("", 201, {}), Person.post(:hire, :name => 'Matz'))
+
+ # PUT
+ assert_equal ActiveResource::Response.new("", 204, {}),
+ Person.put(:promote, {:name => 'Matz'}, 'atestbody')
+ assert_equal ActiveResource::Response.new("", 204, {}), Person.put(:sort, :by => 'name')
+
+ # DELETE
+ Person.delete :deactivate, :name => 'Matz'
+
+ # Nested resource
+ assert_equal ActiveResource::Response.new("", 204, {}), StreetAddress.put(:sort, :person_id => 1, :by => 'name')
+ end
+
+ def test_custom_element_method
+ # Test GET against an element URL
+ assert_equal Person.find(1).get(:shallow), {"id" => 1, "name" => 'Matz'}
+ assert_equal Person.find(1).get(:deep), {"id" => 1, "name" => 'Matz', "other" => 'other'}
+
+ # Test PUT against an element URL
+ assert_equal ActiveResource::Response.new("", 204, {}), Person.find(1).put(:promote, {:position => 'Manager'}, 'body')
+
+ # Test DELETE against an element URL
+ assert_equal ActiveResource::Response.new("", 200, {}), Person.find(1).delete(:deactivate)
+
+ # With nested resources
+ assert_equal StreetAddress.find(1, :params => { :person_id => 1 }).get(:deep),
+ { "id" => 1, "street" => '12345 Street', "zip" => "27519" }
+ assert_equal ActiveResource::Response.new("", 204, {}),
+ StreetAddress.find(1, :params => { :person_id => 1 }).put(:normalize_phone, :locale => 'US')
+ end
+
+ def test_custom_new_element_method
+ # Test POST against a new element URL
+ ryan = Person.new(:name => 'Ryan')
+ assert_equal ActiveResource::Response.new(@ryan, 201, {'Location' => '/people/5.xml'}), ryan.post(:register)
+
+ # Test POST against a nested collection URL
+ addy = StreetAddress.new(:street => '123 Test Dr.', :person_id => 1)
+ assert_equal ActiveResource::Response.new({ :street => '12345 Street' }.to_xml(:root => 'address'),
+ 201, {'Location' => '/people/1/addresses/2.xml'}),
+ addy.post(:link)
+
+ matz = Person.new(:id => 1, :name => 'Matz')
+ assert_equal ActiveResource::Response.new(@matz, 201), matz.post(:register)
+ end
+
+ def test_find_custom_resources
+ assert_equal 'Matz', Person.find(:all, :from => :managers).first.name
+ end
+end
diff --git a/vendor/rails/activeresource/test/base/equality_test.rb b/vendor/rails/activeresource/test/base/equality_test.rb
new file mode 100644
index 00000000..4a3f75f9
--- /dev/null
+++ b/vendor/rails/activeresource/test/base/equality_test.rb
@@ -0,0 +1,43 @@
+require "#{File.dirname(__FILE__)}/../abstract_unit"
+require "fixtures/person"
+require "fixtures/street_address"
+
+class BaseEqualityTest < Test::Unit::TestCase
+ def setup
+ @new = Person.new
+ @one = Person.new(:id => 1)
+ @two = Person.new(:id => 2)
+ @street = StreetAddress.new(:id => 2)
+ end
+
+ def test_should_equal_self
+ assert @new == @new, '@new == @new'
+ assert @one == @one, '@one == @one'
+ end
+
+ def test_shouldnt_equal_new_resource
+ assert @new != @one, '@new != @one'
+ assert @one != @new, '@one != @new'
+ end
+
+ def test_shouldnt_equal_different_class
+ assert @two != @street, 'person != street_address with same id'
+ assert @street != @two, 'street_address != person with same id'
+ end
+
+ def test_eql_should_alias_equals_operator
+ assert_equal @new == @new, @new.eql?(@new)
+ assert_equal @new == @one, @new.eql?(@one)
+
+ assert_equal @one == @one, @one.eql?(@one)
+ assert_equal @one == @new, @one.eql?(@new)
+
+ assert_equal @one == @street, @one.eql?(@street)
+ end
+
+ def test_hash_should_be_id_hash
+ [@new, @one, @two, @street].each do |resource|
+ assert_equal resource.id.hash, resource.hash
+ end
+ end
+end
diff --git a/vendor/rails/activeresource/test/base/load_test.rb b/vendor/rails/activeresource/test/base/load_test.rb
new file mode 100644
index 00000000..622afb2f
--- /dev/null
+++ b/vendor/rails/activeresource/test/base/load_test.rb
@@ -0,0 +1,111 @@
+require "#{File.dirname(__FILE__)}/../abstract_unit"
+require "fixtures/person"
+require "fixtures/street_address"
+
+module Highrise
+ class Note < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+
+ class Comment < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+end
+
+
+class BaseLoadTest < Test::Unit::TestCase
+ def setup
+ @matz = { :id => 1, :name => 'Matz' }
+
+ @first_address = { :id => 1, :street => '12345 Street' }
+ @addresses = [@first_address, { :id => 2, :street => '67890 Street' }]
+ @addresses_from_xml = { :street_addresses => @addresses }
+ @addresses_from_xml_single = { :street_addresses => [ @first_address ] }
+
+ @deep = { :id => 1, :street => {
+ :id => 1, :state => { :id => 1, :name => 'Oregon',
+ :notable_rivers => [
+ { :id => 1, :name => 'Willamette' },
+ { :id => 2, :name => 'Columbia', :rafted_by => @matz }] }}}
+
+ @person = Person.new
+ end
+
+ def test_load_expects_hash
+ assert_raise(ArgumentError) { @person.load nil }
+ assert_raise(ArgumentError) { @person.load ' ' }
+ end
+
+ def test_load_simple_hash
+ assert_equal Hash.new, @person.attributes
+ assert_equal @matz.stringify_keys, @person.load(@matz).attributes
+ end
+
+ def test_load_one_with_existing_resource
+ address = @person.load(:street_address => @first_address).street_address
+ assert_kind_of StreetAddress, address
+ assert_equal @first_address.stringify_keys, address.attributes
+ end
+
+ def test_load_one_with_unknown_resource
+ address = silence_warnings { @person.load(:address => @first_address).address }
+ assert_kind_of Person::Address, address
+ assert_equal @first_address.stringify_keys, address.attributes
+ end
+
+ def test_load_collection_with_existing_resource
+ addresses = @person.load(@addresses_from_xml).street_addresses
+ assert_kind_of Array, addresses
+ addresses.each { |address| assert_kind_of StreetAddress, address }
+ assert_equal @addresses.map(&:stringify_keys), addresses.map(&:attributes)
+ end
+
+ def test_load_collection_with_unknown_resource
+ Person.send!(:remove_const, :Address) if Person.const_defined?(:Address)
+ assert !Person.const_defined?(:Address), "Address shouldn't exist until autocreated"
+ addresses = silence_warnings { @person.load(:addresses => @addresses).addresses }
+ assert Person.const_defined?(:Address), "Address should have been autocreated"
+ addresses.each { |address| assert_kind_of Person::Address, address }
+ assert_equal @addresses.map(&:stringify_keys), addresses.map(&:attributes)
+ end
+
+ def test_load_collection_with_single_existing_resource
+ addresses = @person.load(@addresses_from_xml_single).street_addresses
+ assert_kind_of Array, addresses
+ addresses.each { |address| assert_kind_of StreetAddress, address }
+ assert_equal [ @first_address ].map(&:stringify_keys), addresses.map(&:attributes)
+ end
+
+ def test_load_collection_with_single_unknown_resource
+ Person.send!(:remove_const, :Address) if Person.const_defined?(:Address)
+ assert !Person.const_defined?(:Address), "Address shouldn't exist until autocreated"
+ addresses = silence_warnings { @person.load(:addresses => [ @first_address ]).addresses }
+ assert Person.const_defined?(:Address), "Address should have been autocreated"
+ addresses.each { |address| assert_kind_of Person::Address, address }
+ assert_equal [ @first_address ].map(&:stringify_keys), addresses.map(&:attributes)
+ end
+
+ def test_recursively_loaded_collections
+ person = @person.load(@deep)
+ assert_equal @deep[:id], person.id
+
+ street = person.street
+ assert_kind_of Person::Street, street
+ assert_equal @deep[:street][:id], street.id
+
+ state = street.state
+ assert_kind_of Person::Street::State, state
+ assert_equal @deep[:street][:state][:id], state.id
+
+ rivers = state.notable_rivers
+ assert_kind_of Array, rivers
+ assert_kind_of Person::Street::State::NotableRiver, rivers.first
+ assert_equal @deep[:street][:state][:notable_rivers].first[:id], rivers.first.id
+ assert_equal @matz[:id], rivers.last.rafted_by.id
+ end
+
+ def test_nested_collections_within_the_same_namespace
+ n = Highrise::Note.new(:comments => [{ :name => "1" }])
+ assert_kind_of Highrise::Comment, n.comments.first
+ end
+end
diff --git a/vendor/rails/activeresource/test/base_errors_test.rb b/vendor/rails/activeresource/test/base_errors_test.rb
new file mode 100644
index 00000000..8706326b
--- /dev/null
+++ b/vendor/rails/activeresource/test/base_errors_test.rb
@@ -0,0 +1,48 @@
+require "#{File.dirname(__FILE__)}/abstract_unit"
+require "fixtures/person"
+
+class BaseErrorsTest < Test::Unit::TestCase
+ def setup
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/people.xml", {}, "Age can't be blank Name can't be blank Name must start with a letter Person quota full for today. ", 422
+ end
+ @person = Person.new(:name => '', :age => '')
+ assert_equal @person.save, false
+ end
+
+ def test_should_mark_as_invalid
+ assert !@person.valid?
+ end
+
+ def test_should_parse_xml_errors
+ assert_kind_of ActiveResource::Errors, @person.errors
+ assert_equal 4, @person.errors.size
+ end
+
+ def test_should_parse_errors_to_individual_attributes
+ assert @person.errors.invalid?(:name)
+ assert_equal "can't be blank", @person.errors.on(:age)
+ assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name]
+ assert_equal "Person quota full for today.", @person.errors.on_base
+ end
+
+ def test_should_iterate_over_errors
+ errors = []
+ @person.errors.each { |attribute, message| errors << [attribute, message] }
+ assert errors.include?(["name", "can't be blank"])
+ end
+
+ def test_should_iterate_over_full_errors
+ errors = []
+ @person.errors.each_full { |message| errors << message }
+ assert errors.include?("Name can't be blank")
+ end
+
+ def test_should_format_full_errors
+ full = @person.errors.full_messages
+ assert full.include?("Age can't be blank")
+ assert full.include?("Name can't be blank")
+ assert full.include?("Name must start with a letter")
+ assert full.include?("Person quota full for today.")
+ end
+end
diff --git a/vendor/rails/activeresource/test/base_test.rb b/vendor/rails/activeresource/test/base_test.rb
new file mode 100644
index 00000000..89bc28de
--- /dev/null
+++ b/vendor/rails/activeresource/test/base_test.rb
@@ -0,0 +1,454 @@
+require "#{File.dirname(__FILE__)}/abstract_unit"
+require "fixtures/person"
+require "fixtures/street_address"
+require "fixtures/beast"
+
+class BaseTest < Test::Unit::TestCase
+ def setup
+ @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
+ @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person')
+ @addy = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address')
+ @default_request_headers = { 'Content-Type' => 'application/xml' }
+ @rick = { :name => "Rick", :age => 25 }.to_xml(:root => "person")
+ @people = [{ :id => 1, :name => 'Matz' }, { :id => 2, :name => 'David' }].to_xml(:root => 'people')
+ @people_david = [{ :id => 2, :name => 'David' }].to_xml(:root => 'people')
+ @addresses = [{ :id => 1, :street => '12345 Street' }].to_xml(:root => 'addresses')
+
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1.xml", {}, @matz
+ mock.get "/people/2.xml", {}, @david
+ mock.get "/people/3.xml", {'key' => 'value'}, nil, 404
+ mock.put "/people/1.xml", {}, nil, 204
+ mock.delete "/people/1.xml", {}, nil, 200
+ mock.delete "/people/2.xml", {}, nil, 400
+ mock.get "/people/99.xml", {}, nil, 404
+ mock.post "/people.xml", {}, @rick, 201, 'Location' => '/people/5.xml'
+ mock.get "/people.xml", {}, @people
+ mock.get "/people/1/addresses.xml", {}, @addresses
+ mock.get "/people/1/addresses/1.xml", {}, @addy
+ mock.get "/people/1/addresses/2.xml", {}, nil, 404
+ mock.get "/people/2/addresses/1.xml", {}, nil, 404
+ mock.put "/people/1/addresses/1.xml", {}, nil, 204
+ mock.delete "/people/1/addresses/1.xml", {}, nil, 200
+ mock.post "/people/1/addresses.xml", {}, nil, 201, 'Location' => '/people/1/addresses/5'
+ mock.get "/people//addresses.xml", {}, nil, 404
+ mock.get "/people//addresses/1.xml", {}, nil, 404
+ mock.put "/people//addresses/1.xml", {}, nil, 404
+ mock.delete "/people//addresses/1.xml", {}, nil, 404
+ mock.post "/people//addresses.xml", {}, nil, 404
+ end
+ end
+
+
+ def test_site_accessor_accepts_uri_or_string_argument
+ site = URI.parse('http://localhost')
+
+ assert_nothing_raised { Person.site = 'http://localhost' }
+ assert_equal site, Person.site
+
+ assert_nothing_raised { Person.site = site }
+ assert_equal site, Person.site
+ end
+
+ def test_should_use_site_prefix_and_credentials
+ assert_equal 'http://foo:bar@beast.caboo.se', Forum.site.to_s
+ assert_equal 'http://foo:bar@beast.caboo.se/forums/:forum_id', Topic.site.to_s
+ end
+
+ def test_site_variable_can_be_reset
+ actor = Class.new(ActiveResource::Base)
+ assert_nil actor.site
+ actor.site = 'http://localhost:31337'
+ actor.site = nil
+ assert_nil actor.site
+ end
+
+ def test_site_reader_uses_superclass_site_until_written
+ # Superclass is Object so returns nil.
+ assert_nil ActiveResource::Base.site
+ assert_nil Class.new(ActiveResource::Base).site
+
+ # Subclass uses superclass site.
+ actor = Class.new(Person)
+ assert_equal Person.site, actor.site
+
+ # Subclass returns frozen superclass copy.
+ assert !Person.site.frozen?
+ assert actor.site.frozen?
+
+ # Changing subclass site doesn't change superclass site.
+ actor.site = 'http://localhost:31337'
+ assert_not_equal Person.site, actor.site
+
+ # Changed subclass site is not frozen.
+ assert !actor.site.frozen?
+
+ # Changing superclass site doesn't overwrite subclass site.
+ Person.site = 'http://somewhere.else'
+ assert_not_equal Person.site, actor.site
+
+ # Changing superclass site after subclassing changes subclass site.
+ jester = Class.new(actor)
+ actor.site = 'http://nomad'
+ assert_equal actor.site, jester.site
+ assert jester.site.frozen?
+
+ # Subclasses are always equal to superclass site when not overridden
+ fruit = Class.new(ActiveResource::Base)
+ apple = Class.new(fruit)
+
+ fruit.site = 'http://market'
+ assert_equal fruit.site, apple.site, 'subclass did not adopt changes to parent class'
+
+ fruit.site = 'http://supermarket'
+ assert_equal fruit.site, apple.site, 'subclass did not adopt changes to parent class'
+ end
+
+ def test_updating_baseclass_site_object_wipes_descendent_cached_connection_objects
+ # Subclasses are always equal to superclass site when not overridden
+ fruit = Class.new(ActiveResource::Base)
+ apple = Class.new(fruit)
+
+ fruit.site = 'http://market'
+ assert_equal fruit.connection.site, apple.connection.site
+
+ fruit.site = 'http://supermarket'
+ assert_equal fruit.connection.site, apple.connection.site
+ end
+
+ def test_collection_name
+ assert_equal "people", Person.collection_name
+ end
+
+ def test_collection_path
+ assert_equal '/people.xml', Person.collection_path
+ end
+
+ def test_collection_path_with_parameters
+ assert_equal '/people.xml?gender=male', Person.collection_path(:gender => 'male')
+ assert_equal '/people.xml?gender=false', Person.collection_path(:gender => false)
+ assert_equal '/people.xml?gender=', Person.collection_path(:gender => nil)
+
+ assert_equal '/people.xml?gender=male', Person.collection_path('gender' => 'male')
+
+ # Use includes? because ordering of param hash is not guaranteed
+ assert Person.collection_path(:gender => 'male', :student => true).include?('/people.xml?')
+ assert Person.collection_path(:gender => 'male', :student => true).include?('gender=male')
+ assert Person.collection_path(:gender => 'male', :student => true).include?('student=true')
+
+ assert_equal '/people.xml?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false])
+
+ assert_equal '/people.xml?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'})
+ end
+
+ def test_custom_element_path
+ assert_equal '/people/1/addresses/1.xml', StreetAddress.element_path(1, :person_id => 1)
+ assert_equal '/people/1/addresses/1.xml', StreetAddress.element_path(1, 'person_id' => 1)
+ end
+
+ def test_custom_element_path_with_parameters
+ assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :person_id => 1, :type => 'work')
+ assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, 'person_id' => 1, :type => 'work')
+ assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :type => 'work', :person_id => 1)
+ assert_equal '/people/1/addresses/1.xml?type%5B%5D=work&type%5B%5D=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time'])
+ end
+
+ def test_custom_element_path_with_prefix_and_parameters
+ assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, {:person_id => 1}, {:type => 'work'})
+ end
+
+ def test_custom_collection_path
+ assert_equal '/people/1/addresses.xml', StreetAddress.collection_path(:person_id => 1)
+ assert_equal '/people/1/addresses.xml', StreetAddress.collection_path('person_id' => 1)
+ end
+
+ def test_custom_collection_path_with_parameters
+ assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path(:person_id => 1, :type => 'work')
+ assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path('person_id' => 1, :type => 'work')
+ end
+
+ def test_custom_collection_path_with_prefix_and_parameters
+ assert_equal '/people/1/addresses.xml?type=work', StreetAddress.collection_path({:person_id => 1}, {:type => 'work'})
+ end
+
+ def test_custom_element_name
+ assert_equal 'address', StreetAddress.element_name
+ end
+
+ def test_custom_collection_name
+ assert_equal 'addresses', StreetAddress.collection_name
+ end
+
+ def test_prefix
+ assert_equal "/", Person.prefix
+ assert_equal Set.new, Person.send!(:prefix_parameters)
+ end
+
+ def test_set_prefix
+ SetterTrap.rollback_sets(Person) do |person_class|
+ person_class.prefix = "the_prefix"
+ assert_equal "the_prefix", person_class.prefix
+ end
+ end
+
+ def test_set_prefix_with_inline_keys
+ SetterTrap.rollback_sets(Person) do |person_class|
+ person_class.prefix = "the_prefix:the_param"
+ assert_equal "the_prefixthe_param_value", person_class.prefix(:the_param => "the_param_value")
+ end
+ end
+
+ def test_set_prefix_with_default_value
+ SetterTrap.rollback_sets(Person) do |person_class|
+ person_class.set_prefix
+ assert_equal "/", person_class.prefix
+ end
+ end
+
+ def test_custom_prefix
+ assert_equal '/people//', StreetAddress.prefix
+ assert_equal '/people/1/', StreetAddress.prefix(:person_id => 1)
+ assert_equal [:person_id].to_set, StreetAddress.send!(:prefix_parameters)
+ end
+
+ def test_find_by_id
+ matz = Person.find(1)
+ assert_kind_of Person, matz
+ assert_equal "Matz", matz.name
+ assert matz.name?
+ end
+
+ def test_respond_to
+ matz = Person.find(1)
+ assert matz.respond_to?(:name)
+ assert matz.respond_to?(:name=)
+ assert matz.respond_to?(:name?)
+ assert !matz.respond_to?(:super_scalable_stuff)
+ end
+
+ def test_find_by_id_with_custom_prefix
+ addy = StreetAddress.find(1, :params => { :person_id => 1 })
+ assert_kind_of StreetAddress, addy
+ assert_equal '12345 Street', addy.street
+ end
+
+ def test_find_all
+ all = Person.find(:all)
+ assert_equal 2, all.size
+ assert_kind_of Person, all.first
+ assert_equal "Matz", all.first.name
+ assert_equal "David", all.last.name
+ end
+
+ def test_find_first
+ matz = Person.find(:first)
+ assert_kind_of Person, matz
+ assert_equal "Matz", matz.name
+ end
+
+ def test_custom_header
+ Person.headers['key'] = 'value'
+ assert_raises(ActiveResource::ResourceNotFound) { Person.find(3) }
+ ensure
+ Person.headers.delete('key')
+ end
+
+ def test_find_by_id_not_found
+ assert_raises(ActiveResource::ResourceNotFound) { Person.find(99) }
+ assert_raises(ActiveResource::ResourceNotFound) { StreetAddress.find(1) }
+ end
+
+ def test_find_all_by_from
+ ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/people.xml", {}, @people_david }
+
+ people = Person.find(:all, :from => "/companies/1/people.xml")
+ assert_equal 1, people.size
+ assert_equal "David", people.first.name
+ end
+
+ def test_find_all_by_from_with_options
+ ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/people.xml", {}, @people_david }
+
+ people = Person.find(:all, :from => "/companies/1/people.xml")
+ assert_equal 1, people.size
+ assert_equal "David", people.first.name
+ end
+
+ def test_find_all_by_symbol_from
+ ActiveResource::HttpMock.respond_to { |m| m.get "/people/managers.xml", {}, @people_david }
+
+ people = Person.find(:all, :from => :managers)
+ assert_equal 1, people.size
+ assert_equal "David", people.first.name
+ end
+
+ def test_find_single_by_from
+ ActiveResource::HttpMock.respond_to { |m| m.get "/companies/1/manager.xml", {}, @david }
+
+ david = Person.find(:one, :from => "/companies/1/manager.xml")
+ assert_equal "David", david.name
+ end
+
+ def test_find_single_by_symbol_from
+ ActiveResource::HttpMock.respond_to { |m| m.get "/people/leader.xml", {}, @david }
+
+ david = Person.find(:one, :from => :leader)
+ assert_equal "David", david.name
+ end
+
+ def test_save
+ rick = Person.new
+ assert_equal true, rick.save
+ assert_equal '5', rick.id
+ end
+
+ def test_id_from_response
+ p = Person.new
+ resp = {'Location' => '/foo/bar/1'}
+ assert_equal '1', p.send!(:id_from_response, resp)
+
+ resp['Location'] << '.xml'
+ assert_equal '1', p.send!(:id_from_response, resp)
+ end
+
+ def test_create_with_custom_prefix
+ matzs_house = StreetAddress.new(:person_id => 1)
+ matzs_house.save
+ assert_equal '5', matzs_house.id
+ end
+
+ # Test that loading a resource preserves its prefix_options.
+ def test_load_preserves_prefix_options
+ address = StreetAddress.find(1, :params => { :person_id => 1 })
+ ryan = Person.new(:id => 1, :name => 'Ryan', :address => address)
+ assert_equal address.prefix_options, ryan.address.prefix_options
+ end
+
+ def test_reload_works_with_prefix_options
+ address = StreetAddress.find(1, :params => { :person_id => 1 })
+ assert_equal address, address.reload
+ end
+
+ def test_reload_works_without_prefix_options
+ person = Person.find(:first)
+ assert_equal person, person.reload
+ end
+
+
+ def test_create
+ rick = Person.create(:name => 'Rick')
+ assert rick.valid?
+ assert !rick.new?
+ assert_equal '5', rick.id
+
+ # test additional attribute returned on create
+ assert_equal 25, rick.age
+
+ # Test that save exceptions get bubbled up too
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.post "/people.xml", {}, nil, 409
+ end
+ assert_raises(ActiveResource::ResourceConflict) { Person.create(:name => 'Rick') }
+ end
+
+ def test_update
+ matz = Person.find(:first)
+ matz.name = "David"
+ assert_kind_of Person, matz
+ assert_equal "David", matz.name
+ assert_equal true, matz.save
+ end
+
+ def test_update_with_custom_prefix_with_specific_id
+ addy = StreetAddress.find(1, :params => { :person_id => 1 })
+ addy.street = "54321 Street"
+ assert_kind_of StreetAddress, addy
+ assert_equal "54321 Street", addy.street
+ addy.save
+ end
+
+ def test_update_with_custom_prefix_without_specific_id
+ addy = StreetAddress.find(:first, :params => { :person_id => 1 })
+ addy.street = "54321 Lane"
+ assert_kind_of StreetAddress, addy
+ assert_equal "54321 Lane", addy.street
+ addy.save
+ end
+
+ def test_update_conflict
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/2.xml", {}, @david
+ mock.put "/people/2.xml", @default_request_headers, nil, 409
+ end
+ assert_raises(ActiveResource::ResourceConflict) { Person.find(2).save }
+ end
+
+ def test_destroy
+ assert Person.find(1).destroy
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1.xml", {}, nil, 404
+ end
+ assert_raises(ActiveResource::ResourceNotFound) { Person.find(1).destroy }
+ end
+
+ def test_destroy_with_custom_prefix
+ assert StreetAddress.find(1, :params => { :person_id => 1 }).destroy
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1/addresses/1.xml", {}, nil, 404
+ end
+ assert_raises(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) }
+ end
+
+ def test_delete
+ assert Person.delete(1)
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1.xml", {}, nil, 404
+ end
+ assert_raises(ActiveResource::ResourceNotFound) { Person.find(1) }
+ end
+
+ def test_delete_with_custom_prefix
+ assert StreetAddress.delete(1, :person_id => 1)
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/1/addresses/1.xml", {}, nil, 404
+ end
+ assert_raises(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) }
+ end
+
+ def test_exists
+ # Class method.
+ assert !Person.exists?(nil)
+ assert Person.exists?(1)
+ assert !Person.exists?(99)
+
+ # Instance method.
+ assert !Person.new.exists?
+ assert Person.find(1).exists?
+ assert !Person.new(:id => 99).exists?
+
+ # Nested class method.
+ assert StreetAddress.exists?(1, :params => { :person_id => 1 })
+ assert !StreetAddress.exists?(1, :params => { :person_id => 2 })
+ assert !StreetAddress.exists?(2, :params => { :person_id => 1 })
+
+ # Nested instance method.
+ assert StreetAddress.find(1, :params => { :person_id => 1 }).exists?
+ assert !StreetAddress.new({:id => 1, :person_id => 2}).exists?
+ assert !StreetAddress.new({:id => 2, :person_id => 1}).exists?
+ end
+
+ def test_to_xml
+ matz = Person.find(1)
+ xml = matz.to_xml
+ assert xml.starts_with?('')
+ assert xml.include?('Matz ')
+ assert xml.include?('1 ')
+ end
+
+ def test_to_param_quacks_like_active_record
+ new_person = Person.new
+ assert_nil new_person.to_param
+ matz = Person.find(1)
+ assert_equal '1', matz.to_param
+ end
+end
diff --git a/vendor/rails/activeresource/test/connection_test.rb b/vendor/rails/activeresource/test/connection_test.rb
new file mode 100644
index 00000000..ffad9744
--- /dev/null
+++ b/vendor/rails/activeresource/test/connection_test.rb
@@ -0,0 +1,170 @@
+require "#{File.dirname(__FILE__)}/abstract_unit"
+require 'base64'
+
+class ConnectionTest < Test::Unit::TestCase
+ ResponseCodeStub = Struct.new(:code)
+
+ def setup
+ @conn = ActiveResource::Connection.new('http://localhost')
+ @matz = { :id => 1, :name => 'Matz' }
+ @david = { :id => 2, :name => 'David' }
+ @people = [ @matz, @david ].to_xml(:root => 'people')
+ @people_single = [ @matz ].to_xml(:root => 'people-single-elements')
+ @people_empty = [ ].to_xml(:root => 'people-empty-elements')
+ @matz = @matz.to_xml(:root => 'person')
+ @david = @david.to_xml(:root => 'person')
+ @header = {'key' => 'value'}.freeze
+
+ @default_request_headers = { 'Content-Type' => 'application/xml' }
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.get "/people/2.xml", @header, @david
+ mock.get "/people.xml", {}, @people
+ mock.get "/people_single_elements.xml", {}, @people_single
+ mock.get "/people_empty_elements.xml", {}, @people_empty
+ mock.get "/people/1.xml", {}, @matz
+ mock.put "/people/1.xml", {}, nil, 204
+ mock.put "/people/2.xml", {}, @header, 204
+ mock.delete "/people/1.xml", {}, nil, 200
+ mock.delete "/people/2.xml", @header, nil, 200
+ mock.post "/people.xml", {}, nil, 201, 'Location' => '/people/5.xml'
+ mock.post "/members.xml", {}, @header, 201, 'Location' => '/people/6.xml'
+ end
+ end
+
+ def test_handle_response
+ # 2xx and 3xx are valid responses.
+ [200, 299, 300, 399].each do |code|
+ expected = ResponseCodeStub.new(code)
+ assert_equal expected, handle_response(expected)
+ end
+
+ # 400 is a bad request (e.g. malformed URI or missing request parameter)
+ assert_response_raises ActiveResource::BadRequest, 400
+
+ # 401 is an unauthorized request
+ assert_response_raises ActiveResource::UnauthorizedAccess, 401
+
+ # 403 is a forbidden requst (and authorizing will not help)
+ assert_response_raises ActiveResource::ForbiddenAccess, 403
+
+ # 404 is a missing resource.
+ assert_response_raises ActiveResource::ResourceNotFound, 404
+
+ # 405 is a missing not allowed error
+ assert_response_raises ActiveResource::MethodNotAllowed, 405
+
+ # 409 is an optimistic locking error
+ assert_response_raises ActiveResource::ResourceConflict, 409
+
+ # 422 is a validation error
+ assert_response_raises ActiveResource::ResourceInvalid, 422
+
+ # 4xx are client errors.
+ [402, 499].each do |code|
+ assert_response_raises ActiveResource::ClientError, code
+ end
+
+ # 5xx are server errors.
+ [500, 599].each do |code|
+ assert_response_raises ActiveResource::ServerError, code
+ end
+
+ # Others are unknown.
+ [199, 600].each do |code|
+ assert_response_raises ActiveResource::ConnectionError, code
+ end
+ end
+
+ ResponseHeaderStub = Struct.new(:code, :message, 'Allow')
+ def test_should_return_allowed_methods_for_method_no_allowed_exception
+ begin
+ handle_response ResponseHeaderStub.new(405, "HTTP Failed...", "GET, POST")
+ rescue ActiveResource::MethodNotAllowed => e
+ assert_equal "Failed with 405 HTTP Failed...", e.message
+ assert_equal [:get, :post], e.allowed_methods
+ end
+ end
+
+ def test_initialize_raises_argument_error_on_missing_site
+ assert_raise(ArgumentError) { ActiveResource::Connection.new(nil) }
+ end
+
+ def test_site_accessor_accepts_uri_or_string_argument
+ site = URI.parse("http://localhost")
+
+ assert_raise(URI::InvalidURIError) { @conn.site = nil }
+
+ assert_nothing_raised { @conn.site = "http://localhost" }
+ assert_equal site, @conn.site
+
+ assert_nothing_raised { @conn.site = site }
+ assert_equal site, @conn.site
+ end
+
+ def test_get
+ matz = @conn.get("/people/1.xml")
+ assert_equal "Matz", matz["name"]
+ end
+
+ def test_get_with_header
+ david = @conn.get("/people/2.xml", @header)
+ assert_equal "David", david["name"]
+ end
+
+ def test_get_collection
+ people = @conn.get("/people.xml")
+ assert_equal "Matz", people[0]["name"]
+ assert_equal "David", people[1]["name"]
+ end
+
+ def test_get_collection_single
+ people = @conn.get("/people_single_elements.xml")
+ assert_equal "Matz", people[0]["name"]
+ end
+
+ def test_get_collection_empty
+ people = @conn.get("/people_empty_elements.xml")
+ assert_equal [], people
+ end
+
+ def test_post
+ response = @conn.post("/people.xml")
+ assert_equal "/people/5.xml", response["Location"]
+ end
+
+ def test_post_with_header
+ response = @conn.post("/members.xml", @header)
+ assert_equal "/people/6.xml", response["Location"]
+ end
+
+ def test_put
+ response = @conn.put("/people/1.xml")
+ assert_equal 204, response.code
+ end
+
+ def test_put_with_header
+ response = @conn.put("/people/2.xml", @header)
+ assert_equal 204, response.code
+ end
+
+ def test_delete
+ response = @conn.delete("/people/1.xml")
+ assert_equal 200, response.code
+ end
+
+ def test_delete_with_header
+ response = @conn.delete("/people/2.xml", @header)
+ assert_equal 200, response.code
+ end
+
+ protected
+ def assert_response_raises(klass, code)
+ assert_raise(klass, "Expected response code #{code} to raise #{klass}") do
+ handle_response ResponseCodeStub.new(code)
+ end
+ end
+
+ def handle_response(response)
+ @conn.send!(:handle_response, response)
+ end
+end
diff --git a/vendor/rails/activeresource/test/fixtures/beast.rb b/vendor/rails/activeresource/test/fixtures/beast.rb
new file mode 100644
index 00000000..e31ec583
--- /dev/null
+++ b/vendor/rails/activeresource/test/fixtures/beast.rb
@@ -0,0 +1,14 @@
+class BeastResource < ActiveResource::Base
+ self.site = 'http://beast.caboo.se'
+ site.user = 'foo'
+ site.password = 'bar'
+end
+
+class Forum < BeastResource
+ # taken from BeastResource
+ # self.site = 'http://beast.caboo.se'
+end
+
+class Topic < BeastResource
+ self.site += '/forums/:forum_id'
+end
diff --git a/vendor/rails/activeresource/test/fixtures/person.rb b/vendor/rails/activeresource/test/fixtures/person.rb
new file mode 100644
index 00000000..e88bb693
--- /dev/null
+++ b/vendor/rails/activeresource/test/fixtures/person.rb
@@ -0,0 +1,3 @@
+class Person < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+end
diff --git a/vendor/rails/activeresource/test/fixtures/street_address.rb b/vendor/rails/activeresource/test/fixtures/street_address.rb
new file mode 100644
index 00000000..94a86702
--- /dev/null
+++ b/vendor/rails/activeresource/test/fixtures/street_address.rb
@@ -0,0 +1,4 @@
+class StreetAddress < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000/people/:person_id/"
+ self.element_name = 'address'
+end
diff --git a/vendor/rails/activeresource/test/format_test.rb b/vendor/rails/activeresource/test/format_test.rb
new file mode 100644
index 00000000..60903025
--- /dev/null
+++ b/vendor/rails/activeresource/test/format_test.rb
@@ -0,0 +1,42 @@
+require "#{File.dirname(__FILE__)}/abstract_unit"
+require "fixtures/person"
+
+class FormatTest < Test::Unit::TestCase
+ def setup
+ @matz = { :id => 1, :name => 'Matz' }
+ @david = { :id => 2, :name => 'David' }
+
+ @programmers = [ @matz, @david ]
+ end
+
+ def test_formats_on_single_element
+ for format in [ :json, :xml ]
+ using_format(Person, format) do
+ ActiveResource::HttpMock.respond_to.get "/people/1.#{format}", {}, ActiveResource::Formats[format].encode(@david)
+ assert_equal @david[:name], Person.find(1).name
+ end
+ end
+ end
+
+ def test_formats_on_collection
+ for format in [ :json, :xml ]
+ using_format(Person, format) do
+ ActiveResource::HttpMock.respond_to.get "/people.#{format}", {}, ActiveResource::Formats[format].encode(@programmers)
+ remote_programmers = Person.find(:all)
+ assert_equal 2, remote_programmers.size
+ assert remote_programmers.select { |p| p.name == 'David' }
+ end
+ end
+ end
+
+
+ private
+ def using_format(klass, mime_type_reference)
+ previous_format = klass.format
+ klass.format = mime_type_reference
+
+ yield
+ ensure
+ klass.format = previous_format
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activeresource/test/setter_trap.rb b/vendor/rails/activeresource/test/setter_trap.rb
new file mode 100644
index 00000000..c5cb4f0e
--- /dev/null
+++ b/vendor/rails/activeresource/test/setter_trap.rb
@@ -0,0 +1,27 @@
+class SetterTrap < BasicObject
+ class << self
+ def rollback_sets(obj)
+ returning yield(setter_trap = new(obj)) do
+ setter_trap.rollback_sets
+ end
+ end
+ end
+
+ def initialize(obj)
+ @cache = {}
+ @obj = obj
+ end
+
+ def respond_to?(method)
+ @obj.respond_to?(method)
+ end
+
+ def method_missing(method, *args, &proc)
+ @cache[method] ||= @obj.send($`) if method.to_s =~ /=$/
+ @obj.send method, *args, &proc
+ end
+
+ def rollback_sets
+ @cache.each { |k, v| @obj.send k, v }
+ end
+end
diff --git a/vendor/rails/activesupport/CHANGELOG b/vendor/rails/activesupport/CHANGELOG
index f6a025f4..79869c6f 100644
--- a/vendor/rails/activesupport/CHANGELOG
+++ b/vendor/rails/activesupport/CHANGELOG
@@ -1,17 +1,271 @@
-*1.4.4* (October 12th, 2007)
+*SVN*
-* Backport: allow array and hash query parameters. Array route parameters are converted/to/a/path as before. #6765, #7047, #7462 [bgipsy, Jeremy McAnally, Dan Kubb, brendan, Diego Algorta Casamayou]
+* Ruby 1.9 compatibility. #1689, #10466, #10468 [Cheah Chu Yeow, Pratik Naik, Jeremy Kemper]
+
+* TimeZone#to_s uses UTC rather than GMT. #1689 [Cheah Chu Yeow]
+
+* Refactor of Hash#symbolize_keys! to use Hash#replace. Closes #10420 [ReinH]
+
+* Fix HashWithIndifferentAccess#to_options! so it doesn't clear the options hash. Closes #10419 [ReinH]
-*1.4.3* (October 4th, 2007)
+*2.0.1* (December 7th, 2007)
+
+* Added Array#from and Array#to that behaves just from String#from and String#to [DHH]
+
+* Fix that empty collections should be treated as empty arrays regardless of whitespace for Hash#from_xml #10255 [adamj]
+
+* Time#time_with_datetime_fallback, Time#to_datetime, Date#to_datetime and String#to_datetime honor Ruby's default calendar reform setting. #10201 [Geoff Buesing]
+
+* Change Time and DateTime #end_of_month to return last second of month instead of beginning of last day of month. Closes #10200 [Geoff Buesing]
+
+* Speedup String#blank? [Jeremy Kemper, Koz]
+
+* Add documentation for Hash#diff. Closes #9306 [Tarmo Tänav]
+
+* Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. [Koz, Tarmo Tänav]
+
+* Change JSON to encode %w(< > &) as 4 digit hex codes to be in compliance with the JSON spec. Closes #9975 [Josh Peek, Cheah Chu Yeow, Tim Pope]
+
+* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 [Rick, theamazingrando]
+
+* Introduce a base class for all test cases used by rails applications. ActiveSupport::TestCase [Koz]
+
+ The intention is to use this to reduce the amount of monkeypatching / overriding that
+ is done to test/unit's classes.
+
+* Document Enumerable and Hash #to_json. #9970 [Cheah Chu Yeow]
+
+* Hash#to_xml handles symbol values. #9954 [Assaf]
+
+* Hash#symbolize_keys behaves well with integer keys. #9890 [PotatoSalad]
+
+* Multibyte: String#slice supports regexp argument. #9646 [yob]
+
+* object.duplicable? returns true if object.dup is safe. False for nil, true, false, symbols, and numbers; true otherwise. #9333 [sur]
+
+* Time, Date and DateTime #advance accept :weeks option. #9866 [Geoff Buesing]
+
+* Fix Time#years_ago and #years_since from leap days. #9865 [Geoff Buesing]
+
+* Time and DateTime#advance accept :hours, :minutes, and :seconds options. #9825 [Geoff Buesing]
+
+* Fix Date#years_ago and #years_since from leap days. #9864 [Geoff Buesing]
+
+* Refactor Time and Date#months_since and #months_ago to use #advance. #9863 [Geoff Buesing]
+
+* Rebundle Builder 2.1.2 but prefer a newer RubyGem if available. [Jeremy Kemper]
+
+* Add Range#overlaps?(range), Range#include?(range), and Range#step without a block. [brandon]
+
+* Correct BufferedLogger#level? checks. #9806 [wildchild, Johan Sorensen]
+
+* String#to_xs uses Eric Wong's fast_xs extension, if available, for Builder speedup. http://bogomips.org/fast_xs/ [Jeremy Kemper]
+
+* Introduce BasicObject as Builder::BlankSlate for Ruby 1.9 forward compatibility. [Jeremy Kemper]
+
+* Unbundle Builder in favor of a gem dependency. [Jeremy Kemper]
+
+* Disambiguate Time, Date, and DateTime#to_json formatting. #9750 [Geoff Buesing, Cheah Chu Yeow]
+
+* Hash#to_json takes :only or :except options to specific or omit certain hash keys. Enumerable#to_json passes through its options to each element. #9751 [Cheah Chu Yeow]
+
+* BufferedLogger#auto_flushing = N flushes the log every N messages. Buffers with an array instead of string. Disabling auto_flushing still flushes when the buffer hits a maximum size, as a failsafe against memory-gobbling. [Jeremy Kemper]
+
+* Fixed Date#xmlschema for dates outside the range of what can be created with Time #9744 [Geoff Buesing]
+
+* Fixed that La Paz was included in -25200 and -14400 offsets when it should only be in -14400 #9735 [bermi]
+
+* Fixed JSON encoding to use quoted keys according to the JSON standard. #8762 [choonkat, Cheah Chu Yeow]
+
+* Alias Object#send to send! for Ruby 1.9 forward compatibility. [Jeremy Kemper]
+
+* Backport Object#instance_variable_defined? for Ruby < 1.8.6. [Jeremy Kemper]
+
+* BufferedLogger#add converts the message to a string. #9702, #9724 [eigentone, DrMark, tomafro]
+
+* Added ActiveSupport::BufferedLogger as a duck-typing alternative (albeit with no formatter) to the Ruby Logger, which provides a very nice speed bump (inspired by Ezra's buffered logger) [DHH]
+
+* Object#instance_exec produces fewer garbage methods. [Mauricio Fernandez]
+
+* Decode json strings as Dates/Times if they're using a YAML-compatible format. Closes #9614 [Rick]
+
+* Fixed cache_page to use the request url instead of the routing options when picking a save path. #8614 [Josh Peek]
+
+* Object.subclasses_of includes anonymous subclasses. [Jeremy Kemper]
+
+* Fixed that pluralizing an empty string should return the same empty string, not "s". #7720 [Josh Peek]
+
+* Added call to inspect on non-string classes for the logger #8533 [codahale]
+
+* Deprecation: remove deprecated :mday option from Time, Date, and DateTime#change. [Jeremy Kemper]
+
+* Fix JSON decoder with nested quotes and commas. #9579 [zdennis]
+
+* Hash#to_xml doesn't double-unescape. #8806 [Ezran]
+
+* Added Array#rand #9170 [Norbert Crombach]. Examples:
+
+ [].rand # => nil
+ ['a'].rand # => 'a'
+ [1,2,3].rand # => 1 or 2 or 3
+
+* Deprecation: removed Reloadable. [Jeremy Kemper]
+
+* Make the utf-handler return the correct value for non-matching regular expressions. Closes #9049 [manfred]
+
+* Add ljust, rjust and center to utf8-handler. Closes #9165 [manfred]
+
+* Fix Time#advance bug when trying to advance a year from leap day. Closes #8655 [gbuesing]
+
+* Add support for []= on ActiveSupport::Multibyte::Chars. Closes #9142. [ewan, manfred]
+
+* Added Array#extract_options! to encapsulate the pattern of getting an options hash out of a variable number of parameters. #8759 [Norbert Crombach]
+
+* Let alias_attribute work with attributes with initial capital letters (legacy columns etc). Closes #8596 [mpalmer]
+
+* Added Hash#except which is the inverse of Hash#slice -- return the hash except the keys that are specified [DHH]
+
+* Added support for pluralization with a different starting letter than the singular version (cow/kine) #4929 [norri_b/hasmanyjosh]
* Demote Hash#to_xml to use XmlSimple#xml_in_string so it can't read files or stdin. #8453 [candlerb, Jeremy Kemper]
+* Backport clean_logger changes to support ruby 1.8.2 [mislav]
+
+* Added proper handling of arrays #8537 [hasmanyjosh]
+
+ Before:
+ Hash.from_xml ' '
+ # => {:images => nil}
+
+ Hash.from_xml 'foo.jpg '
+ # => {:images => {:image => "foo.jpg"}}
+
+ Hash.from_xml 'foo.jpg bar.jpg '
+ # => {:images => {:image => ["foo.jpg", "bar.jpg"]}}
+
+ After:
+ Hash.from_xml ' '
+ # => {:images => []}
+
+ Hash.from_xml 'foo.jpg '
+ # => {:images => ["foo.jpg"]}
+
+ Hash.from_xml 'foo.jpg bar.jpg '
+ # => {:images => ["foo.jpg", "bar.jpg"]}
+
+* Improve Time and Date test coverage. #8646 [Josh Peek]
+
+* Add Date#since, ago, beginning_of_day, and end_of_day. Date + seconds works now. #8575 [Geoff Buesing]
+
+* String#to_time overflows to DateTime. Add String#to_datetime. #8572 [Geoff Buesing]
+
+* Date.yesterday and .tomorrow. #8571 [Geoff Buesing]
+
+* Readable Date and DateTime#inspect. #8570 [Geoff Buesing]
+
+* Move common DateTime calculations to Date. #8536 [Geoff Buesing]
+
+* Added Date#change (like Time#change) [DHH]
+
+* DateTime#to_time converts to Time unless out of range. #8512 [Geoff Buesing]
+
+* Date#to_datetime, #to_s(:rfc822). #8512 [Geoff Buesing]
+
+* Time durations use since instead of + for accuracy. #8513 [Geoff Buesing]
+
+* escape <'s and >'s in JSON strings. #8371 [Rick]
+
+* Inflections: MatrixTest -> MatrixTests instead of MatricesTest. #8496 [jbwiv]
+
+* Multibyte strings respond_to the String methods they proxy so they can be duck-typed. #6549 [Tuxie]
+
+* Array#to_xml yields the builder just like Hash and ActiveRecord::Base. #8472 [seth]
+
+* Date, Time, and DateTime support formatting blocks in addition to strftime strings. Introduce :long_ordinal format, e.g. "February 21st, 2005". #8191 [Coda Hale]
+
* Document Object#blank?. #6491 [Chris Mear]
+* Date, Time, and DateTime#to_json. #8399 [wycats]
+
+* Simplify API of assert_difference by passing in an expression that is evaluated before and after the passed in block. See documenation for examples of new API. [Marcel Molina Jr.]
+
+* Added assert_difference and assert_no_difference to test/unit assertions [Tobias Luetke]
+
+* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [DHH]
+
+* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [DHH]
+
+
+ David
+ R0lGODlhkACZAPUAAM5lcfjrtMQCG=\n
+
+
+ ...becomes:
+
+ attributes = { :person => { :name => "David", :avatar => # } }
+ attributes[:person][:avatar].content_type # => "image/jpg"
+ attributes[:person][:avatar].original_filename # => "me.jpg"
+ attributes[:person][:avatar].read # => binary data of the file
+
+ Which is duck-type compatible with the files that you get when doing multipart uploads through HTML.
+
+* Improved multibyte performance by relying less on exception raising #8159 [Blaine]
+
+* Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 [Tim Pope]
+
+* Added yielding of builder in Hash#to_xml [DHH]
+
+* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) [DHH]
+
+* Hash#to_xml supports YAML attributes. #7502 [jonathan]
+
+* Refactor ActiveSupport::JSON to be less obtuse. Add support for JSON decoding by way of Syck with ActiveSupport::JSON.decode(json_string). Prevent hash keys that are JavaScript reserved words from being unquoted during encoding. [Sam Stephenson]
+
+* alias_method_chain preserves the original method's visibility. #7854 [Jonathan Viney]
+
* Update Dependencies to ignore constants inherited from ancestors. Closes #6951. [Nicholas Seckar]
-* Improved multibyte performance by relying less on exception raising #8159 [Blaine]
+* Array#to_query preserves its ordering. #7756 [Greg Spurrier]
+
+* Out-of-range Time calculations transparently overflow to DateTime. Introduce Time#to_datetime. #7706, #7715 [Geoff Buesing]
+
+* DateTime calculations analogous to the Date and Time extensions. #7693 [Geoff Buesing]
+
+* Give DateTime correct .to_s implementations, lets it play nice with ActiveRecord quoting. #7649 [Geoff Buesing]
+
+* Add File.atomic_write, allows you to write large files in an atomic manner, preventing users from seeing half written files. [Koz]
+
+* Allow users to provide custom formatters to Logger. [aeden]
+
+* Hash#to_query CGI-escapes its keys. [Jeremy Kemper]
+
+* Optimize Class Inheritable Attributes so that unnecessary hashes are not created. Closes #7472 [Bruce Perens]
+
+* :db format for Date#to_s [Jeremy Kemper]
+ Date.new(2007, 1, 27).to_s(:db) # => '2007-01-27'
+
+* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick]
+
+* Added Hash#to_query to turn a hash of values into a form-encoded query string [Nicholas Seckar]
+
+* Increase test coverage for subclasses_of. Closes #7335. [Roman2K, Nicholas Seckar]
+
+* Remove unused code from Duration#inspect. Closes #7180. [Rich Collins]
+
+* Added test coverage for Inflector.inflections.clear. Closes #7179. [Rich Collins]
+
+* ActiveSupport::Multibyte::Handlers::UTF8Handler should raise when a range and an integer are passed in (just like the native implementation). Closes #7176 [Rich Collins]
+
+* A couple extra tests for #classify. Closes #7273. [Josh Susser]
+
+* Better docs for Object extensions [zackchandler, Jamis Buck]
+
+* Fix that Dates couldn't be subtracted from Dates after [5940]. [Sam Stephenson]
+
+* Add Object#acts_like? and Time#acts_like_time? and Date#acts_like_date? to facilitate duck-typing. [Jamis Buck]
+
+* Make 1.months and friends accurate by introducing a Duration class. #6835 [eventualbuddha]
*1.4.2* (March 12th, 2007)
@@ -43,12 +297,22 @@ public for compatibility. [Jeremy Kemper]
* Deprecation: silence warnings when reporting test errors. [Jeremy Kemper]
+* Hash#slice(*keys) returns a new hash with only the given keys. #slice! replaces the hash with only the given keys. Works with HashWithIndifferentAccess also. [Jeremy Kemper]
+
+* HashWithIndifferentAccess#to_hash converts to a Hash with String keys and the same default value. [Jeremy Kemper]
+
* Fix remove_constant to correctly handle constant names of the form "::A::...". References #6720. [Nicholas Seckar]
* Fixed Array#to_xml when it contains a series of hashes (each piece would get its own XML declaration) #6610 [thkarcher/cyu]
+* Added Time#to_s(:time) which will just return H:M, like 17:44 [DHH]
+
+* Add Module#attr_accessor_with_default to initialize value of attribute before setting it. Closes #6538. [Stuart Halloway, Marcel Molina Jr.]
+
* Hash#to_xml handles keys with the same name as Kernel methods. #6613 [Catfish]
+* Added Time#end_of_day to get 23:59:59 of that day [DHH]
+
* Don't quote hash keys in Hash#to_json if they're valid JavaScript identifiers. Disable this with ActiveSupport::JSON.unquote_hash_key_identifiers = false if you need strict JSON compliance. [Sam Stephenson]
* Lazily load the Unicode Database in the UTF-8 Handler [Rick Olson]
@@ -57,7 +321,7 @@ public for compatibility. [Jeremy Kemper]
* Fix unicode JSON regexp for Onigurama compatibility. #6494 [whitley]
-* Update XmlSimple to 1.0.10. Closes #6532. [nicksieger]
+* update XmlSimple to 1.0.10. Closes #6532. [nicksieger]
* Update dependencies to allow constants to be defined alongside their siblings. A common case for this is AR model classes with STI; user.rb might define User, Administrator and Guest for example. [Nicholas Seckar]
@@ -73,7 +337,7 @@ public for compatibility. [Jeremy Kemper]
* Make String#chars slicing behaviour consistent with String. Closes #6387 [Manfred Stienstra]
-* Pull in latest multibye patch. Closes #6346 [Manfred Stienstra]
+* Pull in latest multibyte patch. Closes #6346 [Manfred Stienstra]
* Add ActiveSupport::Multibyte. Provides String#chars which lets you deal with strings as a sequence of chars, not of bytes. Closes #6242 [Julian Tarkhanov, Manfred Stienstra, Thijs van der Vossen & Jan Behrens]
@@ -93,6 +357,8 @@ public for compatibility. [Jeremy Kemper]
* Equate Kernel.const_missing with Object.const_missing. Fixes #5988. [Nicholas Seckar]
+* Add ApplicationController special case to Dependencies. [Nicholas Seckar]
+
* Don't pad remaining places with in_groups_of if specified padding value is false. [Marcel Molina Jr.]
* Fix cases where empty xml nodes weren't being translated to nil in Hash.create_from_xml [Rick Olso n]
@@ -137,6 +403,8 @@ public for compatibility. [Jeremy Kemper]
* Add extention to obtain the missing constant from NameError instances. [Nicholas Seckar]
+* Thoroughly document inflections. #5700 [petermichaux@gmail.com]
+
* Added Module#alias_attribute [Jamis/DHH]. Example:
class Content < ActiveRecord::Base
@@ -260,7 +528,6 @@ public for compatibility. [Jeremy Kemper]
* Add Array#split for dividing arrays into one or more subarrays by value or block. [Sam Stephenson]
-
*1.3.1* (April 6th, 2006)
* Clean paths inside of exception messages and traces. [Nicholas Seckar]
diff --git a/vendor/rails/activesupport/MIT-LICENSE b/vendor/rails/activesupport/MIT-LICENSE
index c1caf0bf..dbe78035 100644
--- a/vendor/rails/activesupport/MIT-LICENSE
+++ b/vendor/rails/activesupport/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2005-2006 David Heinemeier Hansson
+Copyright (c) 2005-2007 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/vendor/rails/activesupport/Rakefile b/vendor/rails/activesupport/Rakefile
index 03997a35..f8322c4c 100644
--- a/vendor/rails/activesupport/Rakefile
+++ b/vendor/rails/activesupport/Rakefile
@@ -30,6 +30,7 @@ Rake::RDocTask.new { |rdoc|
rdoc.rdoc_dir = 'doc'
rdoc.title = "Active Support -- Utility classes and standard library extensions from Rails"
rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.options << '--charset' << 'utf-8'
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.rdoc_files.include('README', 'CHANGELOG')
rdoc.rdoc_files.include('lib/active_support.rb')
@@ -79,4 +80,4 @@ task :release => [ :package ] do
rubyforge = RubyForge.new
rubyforge.login
rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/install.rb b/vendor/rails/activesupport/install.rb
index f84f06bd..9c54d8c1 100644
--- a/vendor/rails/activesupport/install.rb
+++ b/vendor/rails/activesupport/install.rb
@@ -18,7 +18,7 @@ unless $sitedir
end
end
-# the acual gruntwork
+# the actual gruntwork
Dir.chdir("lib")
Find.find("active_support", "active_support.rb") { |f|
diff --git a/vendor/rails/activesupport/lib/active_support.rb b/vendor/rails/activesupport/lib/active_support.rb
index 5504fa8f..0b3816ec 100644
--- a/vendor/rails/activesupport/lib/active_support.rb
+++ b/vendor/rails/activesupport/lib/active_support.rb
@@ -22,23 +22,28 @@
#++
$:.unshift(File.dirname(__FILE__))
-$:.unshift(File.dirname(__FILE__) + "/active_support/vendor")
-
-require 'builder'
+require 'active_support/vendor'
+require 'active_support/basic_object'
require 'active_support/inflector'
require 'active_support/core_ext'
+
require 'active_support/clean_logger'
+require 'active_support/buffered_logger'
+
require 'active_support/dependencies'
-require 'active_support/reloadable'
require 'active_support/deprecation'
require 'active_support/ordered_options'
require 'active_support/option_merger'
require 'active_support/values/time_zone'
+require 'active_support/duration'
require 'active_support/json'
require 'active_support/multibyte'
+
+require 'active_support/testing'
+
diff --git a/vendor/rails/activesupport/lib/active_support/basic_object.rb b/vendor/rails/activesupport/lib/active_support/basic_object.rb
new file mode 100644
index 00000000..5e79de99
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/basic_object.rb
@@ -0,0 +1,5 @@
+# Ruby 1.9 introduces BasicObject. Use Builder's BlankSlate until then.
+unless defined? BasicObject
+ require 'blankslate'
+ BasicObject = BlankSlate
+end
diff --git a/vendor/rails/activesupport/lib/active_support/binding_of_caller.rb b/vendor/rails/activesupport/lib/active_support/binding_of_caller.rb
deleted file mode 100644
index e224c996..00000000
--- a/vendor/rails/activesupport/lib/active_support/binding_of_caller.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-begin
- require 'simplecc'
-rescue LoadError
- class Continuation # :nodoc: # for RDoc
- end
- def Continuation.create(*args, &block) # :nodoc:
- cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
- result ||= args
- return *[cc, *result]
- end
-end
-
-class Binding; end # for RDoc
-# This method returns the binding of the method that called your
-# method. It will raise an Exception when you're not inside a method.
-#
-# It's used like this:
-# def inc_counter(amount = 1)
-# Binding.of_caller do |binding|
-# # Create a lambda that will increase the variable 'counter'
-# # in the caller of this method when called.
-# inc = eval("lambda { |arg| counter += arg }", binding)
-# # We can refer to amount from inside this block safely.
-# inc.call(amount)
-# end
-# # No other statements can go here. Put them inside the block.
-# end
-# counter = 0
-# 2.times { inc_counter }
-# counter # => 2
-#
-# Binding.of_caller must be the last statement in the method.
-# This means that you will have to put everything you want to
-# do after the call to Binding.of_caller into the block of it.
-# This should be no problem however, because Ruby has closures.
-# If you don't do this an Exception will be raised. Because of
-# the way that Binding.of_caller is implemented it has to be
-# done this way.
-def Binding.of_caller(&block)
- old_critical = Thread.critical
- Thread.critical = true
- count = 0
- cc, result, error, extra_data = Continuation.create(nil, nil)
- error.call if error
-
- tracer = lambda do |*args|
- type, context, extra_data = args[0], args[4], args
- if type == "return"
- count += 1
- # First this method and then calling one will return --
- # the trace event of the second event gets the context
- # of the method which called the method that called this
- # method.
- if count == 2
- # It would be nice if we could restore the trace_func
- # that was set before we swapped in our own one, but
- # this is impossible without overloading set_trace_func
- # in current Ruby.
- set_trace_func(nil)
- cc.call(eval("binding", context), nil, extra_data)
- end
- elsif type == "line" then
- nil
- elsif type == "c-return" and extra_data[3] == :set_trace_func then
- nil
- else
- set_trace_func(nil)
- error_msg = "Binding.of_caller used in non-method context or " +
- "trailing statements of method using it aren't in the block."
- cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
- end
- end
-
- unless result
- set_trace_func(tracer)
- return nil
- else
- Thread.critical = old_critical
- case block.arity
- when 1 then yield(result)
- else yield(result, extra_data)
- end
- end
-end
diff --git a/vendor/rails/activesupport/lib/active_support/breakpoint.rb b/vendor/rails/activesupport/lib/active_support/breakpoint.rb
deleted file mode 100755
index 8207117c..00000000
--- a/vendor/rails/activesupport/lib/active_support/breakpoint.rb
+++ /dev/null
@@ -1,528 +0,0 @@
-# The Breakpoint library provides the convenience of
-# being able to inspect and modify state, diagnose
-# bugs all via IRB by simply setting breakpoints in
-# your applications by the call of a method.
-#
-# This library was written and is supported by me,
-# Florian Gross. I can be reached at flgr@ccan.de
-# and enjoy getting feedback about my libraries.
-#
-# The whole library (including breakpoint_client.rb
-# and binding_of_caller.rb) is licensed under the
-# same license that Ruby uses. (Which is currently
-# either the GNU General Public License or a custom
-# one that allows for commercial usage.) If you for
-# some good reason need to use this under another
-# license please contact me.
-
-require 'irb'
-require File.dirname(__FILE__) + '/binding_of_caller' unless defined? Binding.of_caller
-require 'drb'
-require 'drb/acl'
-
-module Breakpoint
- id = %q$Id: breakpoint.rb 92 2005-02-04 22:35:53Z flgr $
- Version = id.split(" ")[2].to_i
-
- extend self
-
- # This will pop up an interactive ruby session at a
- # pre-defined break point in a Ruby application. In
- # this session you can examine the environment of
- # the break point.
- #
- # You can get a list of variables in the context using
- # local_variables via +local_variables+. You can then
- # examine their values by typing their names.
- #
- # You can have a look at the call stack via +caller+.
- #
- # The source code around the location where the breakpoint
- # was executed can be examined via +source_lines+. Its
- # argument specifies how much lines of context to display.
- # The default amount of context is 5 lines. Note that
- # the call to +source_lines+ can raise an exception when
- # it isn't able to read in the source code.
- #
- # breakpoints can also return a value. They will execute
- # a supplied block for getting a default return value.
- # A custom value can be returned from the session by doing
- # +throw(:debug_return, value)+.
- #
- # You can also give names to break points which will be
- # used in the message that is displayed upon execution
- # of them.
- #
- # Here's a sample of how breakpoints should be placed:
- #
- # class Person
- # def initialize(name, age)
- # @name, @age = name, age
- # breakpoint("Person#initialize")
- # end
- #
- # attr_reader :age
- # def name
- # breakpoint("Person#name") { @name }
- # end
- # end
- #
- # person = Person.new("Random Person", 23)
- # puts "Name: #{person.name}"
- #
- # And here is a sample debug session:
- #
- # Executing break point "Person#initialize" at file.rb:4 in `initialize'
- # irb(#):001:0> local_variables
- # => ["name", "age", "_", "__"]
- # irb(#):002:0> [name, age]
- # => ["Random Person", 23]
- # irb(#):003:0> [@name, @age]
- # => ["Random Person", 23]
- # irb(#):004:0> self
- # => #
- # irb(#):005:0> @age += 1; self
- # => #
- # irb(#):006:0> exit
- # Executing break point "Person#name" at file.rb:9 in `name'
- # irb(#):001:0> throw(:debug_return, "Overriden name")
- # Name: Overriden name
- #
- # Breakpoint sessions will automatically have a few
- # convenience methods available. See Breakpoint::CommandBundle
- # for a list of them.
- #
- # Breakpoints can also be used remotely over sockets.
- # This is implemented by running part of the IRB session
- # in the application and part of it in a special client.
- # You have to call Breakpoint.activate_drb to enable
- # support for remote breakpoints and then run
- # breakpoint_client.rb which is distributed with this
- # library. See the documentation of Breakpoint.activate_drb
- # for details.
- def breakpoint(id = nil, context = nil, &block)
- callstack = caller
- callstack.slice!(0, 3) if callstack.first["breakpoint"]
- file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
-
- message = "Executing break point " + (id ? "#{id.inspect} " : "") +
- "at #{file}:#{line}" + (method ? " in `#{method}'" : "")
-
- if context then
- return handle_breakpoint(context, message, file, line, &block)
- end
-
- Binding.of_caller do |binding_context|
- handle_breakpoint(binding_context, message, file, line, &block)
- end
- end
-
- module CommandBundle #:nodoc:
- # Proxy to a Breakpoint client. Lets you directly execute code
- # in the context of the client.
- class Client #:nodoc:
- def initialize(eval_handler) # :nodoc:
- eval_handler.untaint
- @eval_handler = eval_handler
- end
-
- instance_methods.each do |method|
- next if method[/^__.+__$/]
- undef_method method
- end
-
- # Executes the specified code at the client.
- def eval(code)
- @eval_handler.call(code)
- end
-
- # Will execute the specified statement at the client.
- def method_missing(method, *args, &block)
- if args.empty? and not block
- result = eval "#{method}"
- else
- # This is a bit ugly. The alternative would be using an
- # eval context instead of an eval handler for executing
- # the code at the client. The problem with that approach
- # is that we would have to handle special expressions
- # like "self", "nil" or constants ourself which is hard.
- remote = eval %{
- result = lambda { |block, *args| #{method}(*args, &block) }
- def result.call_with_block(*args, &block)
- call(block, *args)
- end
- result
- }
- remote.call_with_block(*args, &block)
- end
-
- return result
- end
- end
-
- # Returns the source code surrounding the location where the
- # breakpoint was issued.
- def source_lines(context = 5, return_line_numbers = false)
- lines = File.readlines(@__bp_file).map { |line| line.chomp }
-
- break_line = @__bp_line
- start_line = [break_line - context, 1].max
- end_line = break_line + context
-
- result = lines[(start_line - 1) .. (end_line - 1)]
-
- if return_line_numbers then
- return [start_line, break_line, result]
- else
- return result
- end
- end
-
- # Lets an object that will forward method calls to the breakpoint
- # client. This is useful for outputting longer things at the client
- # and so on. You can for example do these things:
- #
- # client.puts "Hello" # outputs "Hello" at client console
- # # outputs "Hello" into the file temp.txt at the client
- # client.File.open("temp.txt", "w") { |f| f.puts "Hello" }
- def client()
- if Breakpoint.use_drb? then
- sleep(0.5) until Breakpoint.drb_service.eval_handler
- Client.new(Breakpoint.drb_service.eval_handler)
- else
- Client.new(lambda { |code| eval(code, TOPLEVEL_BINDING) })
- end
- end
- end
-
- def handle_breakpoint(context, message, file = "", line = "", &block) # :nodoc:
- catch(:debug_return) do |value|
- eval(%{
- @__bp_file = #{file.inspect}
- @__bp_line = #{line}
- extend Breakpoint::CommandBundle
- extend DRbUndumped if self
- }, context) rescue nil
-
- if not use_drb? then
- puts message
- IRB.start(nil, IRB::WorkSpace.new(context))
- else
- @drb_service.add_breakpoint(context, message)
- end
-
- block.call if block
- end
- end
-
- # These exceptions will be raised on failed asserts
- # if Breakpoint.asserts_cause_exceptions is set to
- # true.
- class FailedAssertError < RuntimeError #:nodoc:
- end
-
- # This asserts that the block evaluates to true.
- # If it doesn't evaluate to true a breakpoint will
- # automatically be created at that execution point.
- #
- # You can disable assert checking in production
- # code by setting Breakpoint.optimize_asserts to
- # true. (It will still be enabled when Ruby is run
- # via the -d argument.)
- #
- # Example:
- # person_name = "Foobar"
- # assert { not person_name.nil? }
- #
- # Note: If you want to use this method from an
- # unit test, you will have to call it by its full
- # name, Breakpoint.assert.
- def assert(context = nil, &condition)
- return if Breakpoint.optimize_asserts and not $DEBUG
- return if yield
-
- callstack = caller
- callstack.slice!(0, 3) if callstack.first["assert"]
- file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
-
- message = "Assert failed at #{file}:#{line}#{" in `#{method}'" if method}."
-
- if Breakpoint.asserts_cause_exceptions and not $DEBUG then
- raise(Breakpoint::FailedAssertError, message)
- end
-
- message += " Executing implicit breakpoint."
-
- if context then
- return handle_breakpoint(context, message, file, line)
- end
-
- Binding.of_caller do |context|
- handle_breakpoint(context, message, file, line)
- end
- end
-
- # Whether asserts should be ignored if not in debug mode.
- # Debug mode can be enabled by running ruby with the -d
- # switch or by setting $DEBUG to true.
- attr_accessor :optimize_asserts
- self.optimize_asserts = false
-
- # Whether an Exception should be raised on failed asserts
- # in non-$DEBUG code or not. By default this is disabled.
- attr_accessor :asserts_cause_exceptions
- self.asserts_cause_exceptions = false
- @use_drb = false
-
- attr_reader :drb_service # :nodoc:
-
- class DRbService # :nodoc:
- include DRbUndumped
-
- def initialize
- @handler = @eval_handler = @collision_handler = nil
-
- IRB.instance_eval { @CONF[:RC] = true }
- IRB.run_config
- end
-
- def collision
- sleep(0.5) until @collision_handler
-
- @collision_handler.untaint
-
- @collision_handler.call
- end
-
- def ping() end
-
- def add_breakpoint(context, message)
- workspace = IRB::WorkSpace.new(context)
- workspace.extend(DRbUndumped)
-
- sleep(0.5) until @handler
-
- @handler.untaint
- @handler.call(workspace, message)
- end
-
- attr_accessor :handler, :eval_handler, :collision_handler
- end
-
- # Will run Breakpoint in DRb mode. This will spawn a server
- # that can be attached to via the breakpoint-client command
- # whenever a breakpoint is executed. This is useful when you
- # are debugging CGI applications or other applications where
- # you can't access debug sessions via the standard input and
- # output of your application.
- #
- # You can specify an URI where the DRb server will run at.
- # This way you can specify the port the server runs on. The
- # default URI is druby://localhost:42531.
- #
- # Please note that breakpoints will be skipped silently in
- # case the DRb server can not spawned. (This can happen if
- # the port is already used by another instance of your
- # application on CGI or another application.)
- #
- # Also note that by default this will only allow access
- # from localhost. You can however specify a list of
- # allowed hosts or nil (to allow access from everywhere).
- # But that will still not protect you from somebody
- # reading the data as it goes through the net.
- #
- # A good approach for getting security and remote access
- # is setting up an SSH tunnel between the DRb service
- # and the client. This is usually done like this:
- #
- # $ ssh -L20000:127.0.0.1:20000 -R10000:127.0.0.1:10000 example.com
- # (This will connect port 20000 at the client side to port
- # 20000 at the server side, and port 10000 at the server
- # side to port 10000 at the client side.)
- #
- # After that do this on the server side: (the code being debugged)
- # Breakpoint.activate_drb("druby://127.0.0.1:20000", "localhost")
- #
- # And at the client side:
- # ruby breakpoint_client.rb -c druby://127.0.0.1:10000 -s druby://127.0.0.1:20000
- #
- # Running through such a SSH proxy will also let you use
- # breakpoint.rb in case you are behind a firewall.
- #
- # Detailed information about running DRb through firewalls is
- # available at http://www.rubygarden.org/ruby?DrbTutorial
- def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
- ignore_collisions = false)
-
- return false if @use_drb
-
- uri ||= 'druby://localhost:42531'
-
- if allowed_hosts then
- acl = ["deny", "all"]
-
- Array(allowed_hosts).each do |host|
- acl += ["allow", host]
- end
-
- DRb.install_acl(ACL.new(acl))
- end
-
- @use_drb = true
- @drb_service = DRbService.new
- did_collision = false
- begin
- @service = DRb.start_service(uri, @drb_service)
- rescue Errno::EADDRINUSE
- if ignore_collisions then
- nil
- else
- # The port is already occupied by another
- # Breakpoint service. We will try to tell
- # the old service that we want its port.
- # It will then forward that request to the
- # user and retry.
- unless did_collision then
- DRbObject.new(nil, uri).collision
- did_collision = true
- end
- sleep(10)
- retry
- end
- end
-
- return true
- end
-
- # Deactivates a running Breakpoint service.
- def deactivate_drb
- @service.stop_service unless @service.nil?
- @service = nil
- @use_drb = false
- @drb_service = nil
- end
-
- # Returns true when Breakpoints are used over DRb.
- # Breakpoint.activate_drb causes this to be true.
- def use_drb?
- @use_drb == true
- end
-end
-
-module IRB #:nodoc:
- class << self; remove_method :start; end
- def self.start(ap_path = nil, main_context = nil, workspace = nil)
- $0 = File::basename(ap_path, ".rb") if ap_path
-
- # suppress some warnings about redefined constants
- old_verbose, $VERBOSE = $VERBOSE, nil
- IRB.setup(ap_path)
- $VERBOSE = old_verbose
-
- if @CONF[:SCRIPT] then
- irb = Irb.new(main_context, @CONF[:SCRIPT])
- else
- irb = Irb.new(main_context)
- end
-
- if workspace then
- irb.context.workspace = workspace
- end
-
- @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
- @CONF[:MAIN_CONTEXT] = irb.context
-
- old_sigint = trap("SIGINT") do
- begin
- irb.signal_handle
- rescue RubyLex::TerminateLineInput
- # ignored
- end
- end
-
- catch(:IRB_EXIT) do
- irb.eval_input
- end
- ensure
- trap("SIGINT", old_sigint)
- end
-
- class << self
- alias :old_CurrentContext :CurrentContext
- remove_method :CurrentContext
- end
- def IRB.CurrentContext
- if old_CurrentContext.nil? and Breakpoint.use_drb? then
- result = Object.new
- def result.last_value; end
- return result
- else
- old_CurrentContext
- end
- end
-
- class << self
- alias :old_parse_opts :parse_opts
- remove_method :parse_opts
- end
- def IRB.parse_opts() end
-
- class Context #:nodoc:
- alias :old_evaluate :evaluate
- def evaluate(line, line_no)
- if line.chomp == "exit" then
- exit
- else
- old_evaluate(line, line_no)
- end
- end
- end
-
- class WorkSpace #:nodoc:
- alias :old_evaluate :evaluate
-
- def evaluate(*args)
- if Breakpoint.use_drb? then
- result = old_evaluate(*args)
- if args[0] != :no_proxy and
- not [true, false, nil].include?(result)
- then
- result.extend(DRbUndumped) rescue nil
- end
- return result
- else
- old_evaluate(*args)
- end
- end
- end
-
- module InputCompletor #:nodoc:
- def self.eval(code, context, *more)
- # Big hack, this assumes that InputCompletor
- # will only call eval() when it wants code
- # to be executed in the IRB context.
- IRB.conf[:MAIN_CONTEXT].workspace.evaluate(:no_proxy, code, *more)
- end
- end
-end
-
-module DRb # :nodoc:
- class DRbObject #:nodoc:
- undef :inspect if method_defined?(:inspect)
- undef :clone if method_defined?(:clone)
- end
-end
-
-# See Breakpoint.breakpoint
-def breakpoint(id = nil, &block)
- Binding.of_caller do |context|
- Breakpoint.breakpoint(id, context, &block)
- end
-end
-
-# See Breakpoint.assert
-def assert(&block)
- Binding.of_caller do |context|
- Breakpoint.assert(context, &block)
- end
-end
diff --git a/vendor/rails/activesupport/lib/active_support/buffered_logger.rb b/vendor/rails/activesupport/lib/active_support/buffered_logger.rb
new file mode 100644
index 00000000..c854599e
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/buffered_logger.rb
@@ -0,0 +1,107 @@
+module ActiveSupport
+ # Inspired by the buffered logger idea by Ezra
+ class BufferedLogger
+ module Severity
+ DEBUG = 0
+ INFO = 1
+ WARN = 2
+ ERROR = 3
+ FATAL = 4
+ UNKNOWN = 5
+ end
+ include Severity
+
+ MAX_BUFFER_SIZE = 1000
+
+ # Set to false to disable the silencer
+ cattr_accessor :silencer
+ self.silencer = true
+
+ # Silences the logger for the duration of the block.
+ def silence(temporary_level = ERROR)
+ if silencer
+ begin
+ old_logger_level, self.level = level, temporary_level
+ yield self
+ ensure
+ self.level = old_logger_level
+ end
+ else
+ yield self
+ end
+ end
+
+ attr_accessor :level
+ attr_reader :auto_flushing
+ attr_reader :buffer
+
+ def initialize(log, level = DEBUG)
+ @level = level
+ @buffer = []
+ @auto_flushing = 1
+
+ if log.respond_to?(:write)
+ @log = log
+ elsif File.exist?(log)
+ @log = open(log, (File::WRONLY | File::APPEND))
+ @log.sync = true
+ else
+ @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
+ @log.sync = true
+ @log.write("# Logfile created on %s" % [Time.now.to_s])
+ end
+ end
+
+ def add(severity, message = nil, progname = nil, &block)
+ return if @level > severity
+ message = (message || (block && block.call) || progname).to_s
+ # If a newline is necessary then create a new message ending with a newline.
+ # Ensures that the original message is not mutated.
+ message = "#{message}\n" unless message[-1] == ?\n
+ @buffer << message
+ auto_flush
+ message
+ end
+
+ for severity in Severity.constants
+ class_eval <<-EOT, __FILE__, __LINE__
+ def #{severity.downcase}(message = nil, progname = nil, &block)
+ add(#{severity}, message, progname, &block)
+ end
+
+ def #{severity.downcase}?
+ #{severity} >= @level
+ end
+ EOT
+ end
+
+ # Set the auto-flush period. Set to true to flush after every log message,
+ # to an integer to flush every N messages, or to false, nil, or zero to
+ # never auto-flush. If you turn auto-flushing off, be sure to regularly
+ # flush the log yourself -- it will eat up memory until you do.
+ def auto_flushing=(period)
+ @auto_flushing =
+ case period
+ when true; 1
+ when false, nil, 0; MAX_BUFFER_SIZE
+ when Integer; period
+ else raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
+ end
+ end
+
+ def flush
+ @log.write(@buffer.slice!(0..-1).to_s) unless @buffer.empty?
+ end
+
+ def close
+ flush
+ @log.close if @log.respond_to?(:close)
+ @log = nil
+ end
+
+ protected
+ def auto_flush
+ flush if @buffer.size >= @auto_flushing
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/caching_tools.rb b/vendor/rails/activesupport/lib/active_support/caching_tools.rb
deleted file mode 100644
index c889c148..00000000
--- a/vendor/rails/activesupport/lib/active_support/caching_tools.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-module ActiveSupport
- module CachingTools #:nodoc:
-
- # Provide shortcuts to simply the creation of nested default hashes. This
- # pattern is useful, common practice, and unsightly when done manually.
- module HashCaching
- # Dynamically create a nested hash structure used to cache calls to +method_name+
- # The cache method is named +#{method_name}_cache+ unless :as => :alternate_name
- # is given.
- #
- # The hash structure is created using nested Hash.new. For example:
- #
- # def slow_method(a, b) a ** b end
- #
- # can be cached using hash_cache :slow_method, which will define the method
- # slow_method_cache. We can then find the result of a ** b using:
- #
- # slow_method_cache[a][b]
- #
- # The hash structure returned by slow_method_cache would look like this:
- #
- # Hash.new do |as, a|
- # as[a] = Hash.new do |bs, b|
- # bs[b] = slow_method(a, b)
- # end
- # end
- #
- # The generated code is actually compressed onto a single line to maintain
- # sensible backtrace signatures.
- #
- def hash_cache(method_name, options = {})
- selector = options[:as] || "#{method_name}_cache"
- method = self.instance_method(method_name)
-
- args = []
- code = "def #{selector}(); @#{selector} ||= "
-
- (1..method.arity).each do |n|
- args << "v#{n}"
- code << "Hash.new {|h#{n}, v#{n}| h#{n}[v#{n}] = "
- end
-
- # Add the method call with arguments, followed by closing braces and end.
- code << "#{method_name}(#{args * ', '}) #{'}' * method.arity} end"
-
- # Extract the line number information from the caller. Exceptions arising
- # in the generated code should point to the +hash_cache :...+ line.
- if caller[0] && /^(.*):(\d+)$/ =~ caller[0]
- file, line_number = $1, $2.to_i
- else # We can't give good trackback info; fallback to this line:
- file, line_number = __FILE__, __LINE__
- end
-
- # We use eval rather than building proc's because it allows us to avoid
- # linking the Hash's to this method's binding. Experience has shown that
- # doing so can cause obtuse memory leaks.
- class_eval code, file, line_number
- end
- end
-
- end
-end
diff --git a/vendor/rails/activesupport/lib/active_support/clean_logger.rb b/vendor/rails/activesupport/lib/active_support/clean_logger.rb
index 376896cb..b4c27ebc 100644
--- a/vendor/rails/activesupport/lib/active_support/clean_logger.rb
+++ b/vendor/rails/activesupport/lib/active_support/clean_logger.rb
@@ -1,10 +1,23 @@
require 'logger'
-require File.dirname(__FILE__) + '/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/class/attribute_accessors'
-class Logger #:nodoc:
+# Extensions to the built in Ruby logger.
+#
+# If you want to use the default log formatter as defined in the Ruby core, then you
+# will need to set the formatter for the logger as in:
+#
+# logger.formatter = Formatter.new
+#
+# You can then specify the datetime format, for example:
+#
+# logger.datetime_format = "%Y-%m-%d"
+#
+# Note: This logger is deprecated in favor of ActiveSupport::BufferedLogger
+class Logger
+ # Set to false to disable the silencer
cattr_accessor :silencer
self.silencer = true
-
+
# Silences the logger for the duration of the block.
def silence(temporary_level = Logger::ERROR)
if silencer
@@ -18,6 +31,73 @@ class Logger #:nodoc:
yield self
end
end
+
+ alias :old_datetime_format= :datetime_format=
+ # Logging date-time format (string passed to +strftime+). Ignored if the formatter
+ # does not respond to datetime_format=.
+ def datetime_format=(datetime_format)
+ formatter.datetime_format = datetime_format if formatter.respond_to?(:datetime_format=)
+ end
+
+ alias :old_datetime_format :datetime_format
+ # Get the logging datetime format. Returns nil if the formatter does not support
+ # datetime formatting.
+ def datetime_format
+ formatter.datetime_format if formatter.respond_to?(:datetime_format)
+ end
+
+ alias :old_formatter :formatter if method_defined?(:formatter)
+ # Get the current formatter. The default formatter is a SimpleFormatter which only
+ # displays the log message
+ def formatter
+ @formatter ||= SimpleFormatter.new
+ end
+
+ unless const_defined? :Formatter
+ class Formatter
+ Format = "%s, [%s#%d] %5s -- %s: %s\n"
+
+ attr_accessor :datetime_format
+
+ def initialize
+ @datetime_format = nil
+ end
+
+ def call(severity, time, progname, msg)
+ Format % [severity[0..0], format_datetime(time), $$, severity, progname,
+ msg2str(msg)]
+ end
+
+ private
+ def format_datetime(time)
+ if @datetime_format.nil?
+ time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
+ else
+ time.strftime(@datetime_format)
+ end
+ end
+
+ def msg2str(msg)
+ case msg
+ when ::String
+ msg
+ when ::Exception
+ "#{ msg.message } (#{ msg.class })\n" <<
+ (msg.backtrace || []).join("\n")
+ else
+ msg.inspect
+ end
+ end
+ end
+ end
+
+ # Simple formatter which only displays the message.
+ class SimpleFormatter < Logger::Formatter
+ # This method is invoked when a log event occurs
+ def call(severity, timestamp, progname, msg)
+ "#{String === msg ? msg : msg.inspect}\n"
+ end
+ end
private
alias old_format_message format_message
@@ -28,11 +108,20 @@ class Logger #:nodoc:
# with Logger from 1.8.3 and vice versa.
if method_defined?(:formatter=)
def format_message(severity, timestamp, progname, msg)
- "#{msg}\n"
+ formatter.call(severity, timestamp, progname, msg)
end
else
def format_message(severity, timestamp, msg, progname)
- "#{msg}\n"
+ formatter.call(severity, timestamp, progname, msg)
end
+
+ attr_writer :formatter
+ public :formatter=
+
+ alias old_format_datetime format_datetime
+ def format_datetime(datetime) datetime end
+
+ alias old_msg2str msg2str
+ def msg2str(msg) msg end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext.rb b/vendor/rails/activesupport/lib/active_support/core_ext.rb
index 573313e7..4deef8c7 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext.rb
@@ -1 +1,4 @@
-Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].each { |file| require(file) }
+Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].sort.each do |path|
+ filename = File.basename(path)
+ require "active_support/core_ext/#{filename}"
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/array.rb b/vendor/rails/activesupport/lib/active_support/core_ext/array.rb
index d47b9882..cc0a1ebc 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/array.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/array.rb
@@ -1,7 +1,13 @@
-require File.dirname(__FILE__) + '/array/conversions'
-require File.dirname(__FILE__) + '/array/grouping'
+require 'active_support/core_ext/array/access'
+require 'active_support/core_ext/array/conversions'
+require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/array/grouping'
+require 'active_support/core_ext/array/random_access'
class Array #:nodoc:
+ include ActiveSupport::CoreExtensions::Array::Access
include ActiveSupport::CoreExtensions::Array::Conversions
+ include ActiveSupport::CoreExtensions::Array::ExtractOptions
include ActiveSupport::CoreExtensions::Array::Grouping
+ include ActiveSupport::CoreExtensions::Array::RandomAccess
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/array/access.rb b/vendor/rails/activesupport/lib/active_support/core_ext/array/access.rb
new file mode 100644
index 00000000..fce319d3
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/array/access.rb
@@ -0,0 +1,28 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Array #:nodoc:
+ # Makes it easier to access parts of an array.
+ module Access
+ # Returns the remaining of the array from the +position+.
+ #
+ # Examples:
+ # %w( a b c d ).from(0) # => %w( a b c d )
+ # %w( a b c d ).from(2) # => %w( c d )
+ # %w( a b c d ).from(10) # => nil
+ def from(position)
+ self[position..-1]
+ end
+
+ # Returns the beginning of the array up to the +position+.
+ #
+ # Examples:
+ # %w( a b c d ).to(0) # => %w( a )
+ # %w( a b c d ).to(2) # => %w( a b c )
+ # %w( a b c d ).to(10) # => %w( a b c d )
+ def to(position)
+ self[0..position]
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/array/conversions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/array/conversions.rb
index 51ac8329..7574dc13 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -1,36 +1,50 @@
+require 'builder'
+
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Array #:nodoc:
module Conversions
- # Converts the array to comma-seperated sentence where the last element is joined by the connector word. Options:
- # * :connector : The word used to join the last element in arrays with two or more elements (default: "and")
- # * :skip_last_comma : Set to true to return "a, b and c" instead of "a, b, and c".
+ # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:
+ # * :connector - The word used to join the last element in arrays with two or more elements (default: "and")
+ # * :skip_last_comma - Set to true to return "a, b and c" instead of "a, b, and c".
def to_sentence(options = {})
options.assert_valid_keys(:connector, :skip_last_comma)
options.reverse_merge! :connector => 'and', :skip_last_comma => false
-
+ options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == ''
+
case length
- when 0
- ""
+ when 0
+ ""
when 1
- self[0]
+ self[0].to_s
when 2
- "#{self[0]} #{options[:connector]} #{self[1]}"
+ "#{self[0]} #{options[:connector]}#{self[1]}"
else
- "#{self[0...-1].join(', ')}#{options[:skip_last_comma] ? '' : ','} #{options[:connector]} #{self[-1]}"
+ "#{self[0...-1].join(', ')}#{options[:skip_last_comma] ? '' : ','} #{options[:connector]}#{self[-1]}"
end
end
- # When an array is given to url_for, it is converted to a slash separated string.
+ # Calls to_param on all its elements and joins the result with slashes. This is used by url_for in Action Pack.
def to_param
- join '/'
+ map(&:to_param).join '/'
end
-
- def self.included(klass) #:nodoc:
- klass.send(:alias_method, :to_default_s, :to_s)
- klass.send(:alias_method, :to_s, :to_formatted_s)
+
+ # Converts an array into a string suitable for use as a URL query string, using the given key as the
+ # param name.
+ #
+ # ==== Example:
+ # ['Rails', 'coding'].to_query('hobbies') => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
+ def to_query(key)
+ collect { |value| value.to_query("#{key}[]") } * '&'
end
-
+
+ def self.included(base) #:nodoc:
+ base.class_eval do
+ alias_method :to_default_s, :to_s
+ alias_method :to_s, :to_formatted_s
+ end
+ end
+
def to_formatted_s(format = :default)
case format
when :db
@@ -43,7 +57,7 @@ module ActiveSupport #:nodoc:
to_default_s
end
end
-
+
def to_xml(options = {})
raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml }
@@ -63,7 +77,15 @@ module ActiveSupport #:nodoc:
opts = options.merge({ :root => children })
- options[:builder].tag!(root) { each { |e| e.to_xml(opts.merge!({ :skip_instruct => true })) } }
+ xml = options[:builder]
+ if empty?
+ xml.tag!(root, options[:skip_types] ? {} : {:type => "array"})
+ else
+ xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) {
+ yield xml if block_given?
+ each { |e| e.to_xml(opts.merge!({ :skip_instruct => true })) }
+ }
+ end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/array/extract_options.rb b/vendor/rails/activesupport/lib/active_support/core_ext/array/extract_options.rb
new file mode 100644
index 00000000..980d3640
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/array/extract_options.rb
@@ -0,0 +1,19 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Array #:nodoc:
+ module ExtractOptions
+ # Extract options from a set of arguments. Removes and returns the last element in the array if it's a hash, otherwise returns a blank hash.
+ #
+ # def options(*args)
+ # args.extract_options!
+ # end
+ #
+ # options(1, 2) # => {}
+ # options(1, 2, :a => :b) # => {:a=>:b}
+ def extract_options!
+ last.is_a?(::Hash) ? pop : {}
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb b/vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb
index fae23d4a..52ed61d3 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/array/grouping.rb
@@ -1,3 +1,5 @@
+require 'enumerator'
+
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Array #:nodoc:
@@ -21,14 +23,23 @@ module ActiveSupport #:nodoc:
# ["1", "2"]
# ["3"]
def in_groups_of(number, fill_with = nil, &block)
- require 'enumerator'
- collection = dup
- collection << fill_with until collection.size.modulo(number).zero? unless fill_with == false
- grouped_collection = [] unless block_given?
- collection.each_slice(number) do |group|
- block_given? ? yield(group) : grouped_collection << group
+ if fill_with == false
+ collection = self
+ else
+ # size % number gives how many extra we have;
+ # subtracting from number gives how many to add;
+ # modulo number ensures we don't add group of just fill.
+ padding = (number - size % number) % number
+ collection = dup.concat([fill_with] * padding)
+ end
+
+ if block_given?
+ collection.each_slice(number, &block)
+ else
+ returning [] do |groups|
+ collection.each_slice(number) { |group| groups << group }
+ end
end
- grouped_collection unless block_given?
end
# Divide the array into one or more subarrays based on a delimiting +value+
@@ -40,12 +51,14 @@ module ActiveSupport #:nodoc:
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
def split(value = nil, &block)
block ||= Proc.new { |e| e == value }
+
inject([[]]) do |results, element|
if block.call(element)
results << []
else
results.last << element
end
+
results
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/array/random_access.rb b/vendor/rails/activesupport/lib/active_support/core_ext/array/random_access.rb
new file mode 100644
index 00000000..b7ee0074
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/array/random_access.rb
@@ -0,0 +1,12 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Array #:nodoc:
+ module RandomAccess
+ # Return a random element from the array.
+ def rand
+ self[Kernel.rand(length)]
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb b/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb
index 436b3e00..b442ae96 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal.rb
@@ -1,3 +1,2 @@
require 'bigdecimal'
-
-require File.dirname(__FILE__) + '/bigdecimal/formatting.rb'
+require 'active_support/core_ext/bigdecimal/conversions'
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb b/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb
similarity index 94%
rename from vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb
rename to vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb
index d86adbea..dcdb08ae 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/formatting.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb
@@ -1,7 +1,6 @@
class BigDecimal #:nodoc:
-
alias :_original_to_s :to_s
def to_s(format="F")
_original_to_s(format)
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/blank.rb b/vendor/rails/activesupport/lib/active_support/core_ext/blank.rb
index d2a940f5..c4695816 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/blank.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/blank.rb
@@ -7,13 +7,7 @@ class Object
# to
# if !address.blank?
def blank?
- if respond_to?(:empty?) && respond_to?(:strip)
- empty? or strip.empty?
- elsif respond_to?(:empty?)
- empty?
- else
- !self
- end
+ respond_to?(:empty?) ? empty? : !self
end
end
@@ -45,7 +39,7 @@ end
class String #:nodoc:
def blank?
- empty? || strip.empty?
+ self !~ /\S/
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/cgi.rb b/vendor/rails/activesupport/lib/active_support/core_ext/cgi.rb
index 072a7c99..db90e5c7 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/cgi.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/cgi.rb
@@ -1,5 +1,5 @@
-require File.dirname(__FILE__) + '/cgi/escape_skipping_slashes'
+require 'active_support/core_ext/cgi/escape_skipping_slashes'
class CGI #:nodoc:
- extend(ActiveSupport::CoreExtensions::CGI::EscapeSkippingSlashes)
+ extend ActiveSupport::CoreExtensions::CGI::EscapeSkippingSlashes
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/class.rb b/vendor/rails/activesupport/lib/active_support/core_ext/class.rb
index 7bacdb39..44ad6c8c 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/class.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/class.rb
@@ -1,3 +1,4 @@
-require File.dirname(__FILE__) + '/class/attribute_accessors'
-require File.dirname(__FILE__) + '/class/inheritable_attributes'
-require File.dirname(__FILE__) + '/class/removal'
\ No newline at end of file
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/removal'
+require 'active_support/core_ext/class/delegating_attributes'
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
index 79247e00..eee61d48 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -21,7 +21,7 @@ class Class # :nodoc:
end
def cattr_writer(*syms)
- options = syms.last.is_a?(Hash) ? syms.pop : {}
+ options = syms.extract_options!
syms.flatten.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym}
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/vendor/rails/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
new file mode 100644
index 00000000..f5f0ef87
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
@@ -0,0 +1,40 @@
+# These class attributes behave something like the class
+# inheritable accessors. But instead of copying the hash over at
+# the time the subclass is first defined, the accessors simply
+# delegate to their superclass unless they have been given a
+# specific value. This stops the strange situation where values
+# set after class definition don't get applied to subclasses.
+class Class
+ def superclass_delegating_reader(*names)
+ class_name_to_stop_searching_on = self.superclass.name.blank? ? "Object" : self.superclass.name
+ names.each do |name|
+ class_eval <<-EOS
+ def self.#{name}
+ if defined?(@#{name})
+ @#{name}
+ elsif superclass < #{class_name_to_stop_searching_on} && superclass.respond_to?(:#{name})
+ superclass.#{name}
+ end
+ end
+ def #{name}
+ self.class.#{name}
+ end
+ EOS
+ end
+ end
+
+ def superclass_delegating_writer(*names)
+ names.each do |name|
+ class_eval <<-EOS
+ def self.#{name}=(value)
+ @#{name} = value
+ end
+ EOS
+ end
+ end
+
+ def superclass_delegating_accessor(*names)
+ superclass_delegating_reader(*names)
+ superclass_delegating_writer(*names)
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/vendor/rails/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
index 2dd0c577..371d074d 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
@@ -23,7 +23,7 @@ class Class # :nodoc:
end
def class_inheritable_writer(*syms)
- options = syms.last.is_a?(Hash) ? syms.pop : {}
+ options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
def self.#{sym}=(obj)
@@ -40,7 +40,7 @@ class Class # :nodoc:
end
def class_inheritable_array_writer(*syms)
- options = syms.last.is_a?(Hash) ? syms.pop : {}
+ options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
def self.#{sym}=(obj)
@@ -57,7 +57,7 @@ class Class # :nodoc:
end
def class_inheritable_hash_writer(*syms)
- options = syms.last.is_a?(Hash) ? syms.pop : {}
+ options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
def self.#{sym}=(obj)
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/class/removal.rb b/vendor/rails/activesupport/lib/active_support/core_ext/class/removal.rb
index b217c195..0c70e718 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/class/removal.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/class/removal.rb
@@ -17,8 +17,8 @@ class Class #:nodoc:
# Skip this class if it does not match the current one bound to this name
next unless parent.const_defined?(basename) && klass = parent.const_get(basename)
-
- parent.send :remove_const, basename unless parent == klass
+
+ parent.instance_eval { remove_const basename } unless parent == klass
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/date.rb b/vendor/rails/activesupport/lib/active_support/core_ext/date.rb
index 239b8c14..3f56c560 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/date.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/date.rb
@@ -1,6 +1,10 @@
require 'date'
-require File.dirname(__FILE__) + '/date/conversions'
+require 'active_support/core_ext/date/behavior'
+require 'active_support/core_ext/date/calculations'
+require 'active_support/core_ext/date/conversions'
class Date#:nodoc:
+ include ActiveSupport::CoreExtensions::Date::Behavior
+ include ActiveSupport::CoreExtensions::Date::Calculations
include ActiveSupport::CoreExtensions::Date::Conversions
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/date/behavior.rb b/vendor/rails/activesupport/lib/active_support/core_ext/date/behavior.rb
new file mode 100644
index 00000000..011cc17c
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/date/behavior.rb
@@ -0,0 +1,13 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Date #:nodoc:
+ module Behavior
+ # Enable more predictable duck-typing on Date-like classes. See
+ # Object#acts_like?.
+ def acts_like_date?
+ true
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/date/calculations.rb b/vendor/rails/activesupport/lib/active_support/core_ext/date/calculations.rb
new file mode 100644
index 00000000..b82f7be4
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -0,0 +1,188 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Date #:nodoc:
+ # Enables the use of time calculations within Time itself
+ module Calculations
+ def self.included(base) #:nodoc:
+ base.extend ClassMethods
+
+ base.instance_eval do
+ alias_method :plus_without_duration, :+
+ alias_method :+, :plus_with_duration
+
+ alias_method :minus_without_duration, :-
+ alias_method :-, :minus_with_duration
+ end
+ end
+
+ module ClassMethods
+ def yesterday
+ ::Date.today.yesterday
+ end
+
+ def tomorrow
+ ::Date.today.tomorrow
+ end
+ end
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+ # and then subtracts the specified number of seconds
+ def ago(seconds)
+ to_time.since(-seconds)
+ end
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+ # and then adds the specified number of seconds
+ def since(seconds)
+ to_time.since(seconds)
+ end
+ alias :in :since
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
+ def beginning_of_day
+ to_time
+ end
+ alias :midnight :beginning_of_day
+ alias :at_midnight :beginning_of_day
+ alias :at_beginning_of_day :beginning_of_day
+
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
+ def end_of_day
+ to_time.end_of_day
+ end
+
+ def plus_with_duration(other) #:nodoc:
+ if ActiveSupport::Duration === other
+ other.since(self)
+ else
+ plus_without_duration(other)
+ end
+ end
+
+ def minus_with_duration(other) #:nodoc:
+ if ActiveSupport::Duration === other
+ plus_with_duration(-other)
+ else
+ minus_without_duration(other)
+ end
+ end
+
+ # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
+ # any of these keys: :years, :months, :weeks, :days.
+ def advance(options)
+ d = self
+ d = d >> options.delete(:years) * 12 if options[:years]
+ d = d >> options.delete(:months) if options[:months]
+ d = d + options.delete(:weeks) * 7 if options[:weeks]
+ d = d + options.delete(:days) if options[:days]
+ d
+ end
+
+ # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
+ #
+ # Examples:
+ #
+ # Date.new(2007, 5, 12).change(:day => 1) # => Date.new(2007, 5, 1)
+ # Date.new(2007, 5, 12).change(:year => 2005, :month => 1) # => Date.new(2005, 1, 12)
+ def change(options)
+ ::Date.new(
+ options[:year] || self.year,
+ options[:month] || self.month,
+ options[:day] || self.day
+ )
+ end
+
+ # Returns a new Date/DateTime representing the time a number of specified months ago
+ def months_ago(months)
+ advance(:months => -months)
+ end
+
+ # Returns a new Date/DateTime representing the time a number of specified months in the future
+ def months_since(months)
+ advance(:months => months)
+ end
+
+ # Returns a new Date/DateTime representing the time a number of specified years ago
+ def years_ago(years)
+ advance(:years => -years)
+ end
+
+ # Returns a new Date/DateTime representing the time a number of specified years in the future
+ def years_since(years)
+ advance(:years => years)
+ end
+
+ # Short-hand for years_ago(1)
+ def last_year
+ years_ago(1)
+ end
+
+ # Short-hand for years_since(1)
+ def next_year
+ years_since(1)
+ end
+
+ # Short-hand for months_ago(1)
+ def last_month
+ months_ago(1)
+ end
+
+ # Short-hand for months_since(1)
+ def next_month
+ months_since(1)
+ end
+
+ # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00)
+ def beginning_of_week
+ days_to_monday = self.wday!=0 ? self.wday-1 : 6
+ result = self - days_to_monday
+ self.acts_like?(:time) ? result.midnight : result
+ end
+ alias :monday :beginning_of_week
+ alias :at_beginning_of_week :beginning_of_week
+
+ # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday).
+ def next_week(day = :monday)
+ days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
+ result = (self + 7).beginning_of_week + days_into_week[day]
+ self.acts_like?(:time) ? result.change(:hour => 0) : result
+ end
+
+ # Returns a new ; DateTime objects will have time set to 0:00DateTime representing the start of the month (1st of the month; DateTime objects will have time set to 0:00)
+ def beginning_of_month
+ self.acts_like?(:time) ? change(:day => 1,:hour => 0, :min => 0, :sec => 0) : change(:day => 1)
+ end
+ alias :at_beginning_of_month :beginning_of_month
+
+ # Returns a new Date/DateTime representing the end of the month (last day of the month; DateTime objects will have time set to 0:00)
+ def end_of_month
+ last_day = ::Time.days_in_month( self.month, self.year )
+ self.acts_like?(:time) ? change(:day => last_day, :hour => 23, :min => 59, :sec => 59) : change(:day => last_day)
+ end
+ alias :at_end_of_month :end_of_month
+
+ # Returns a new Date/DateTime representing the start of the quarter (1st of january, april, july, october; DateTime objects will have time set to 0:00)
+ def beginning_of_quarter
+ beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month })
+ end
+ alias :at_beginning_of_quarter :beginning_of_quarter
+
+ # Returns a new Date/DateTime representing the start of the year (1st of january; DateTime objects will have time set to 0:00)
+ def beginning_of_year
+ self.acts_like?(:time) ? change(:month => 1, :day => 1, :hour => 0, :min => 0, :sec => 0) : change(:month => 1, :day => 1)
+ end
+ alias :at_beginning_of_year :beginning_of_year
+
+ # Convenience method which returns a new Date/DateTime representing the time 1 day ago
+ def yesterday
+ self - 1
+ end
+
+ # Convenience method which returns a new Date/DateTime representing the time 1 day since the instance time
+ def tomorrow
+ self + 1
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/date/conversions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/date/conversions.rb
index 4b9388da..f34d8601 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -4,32 +4,91 @@ module ActiveSupport #:nodoc:
# Getting dates in different convenient string representations and other objects
module Conversions
DATE_FORMATS = {
- :short => "%e %b",
- :long => "%B %e, %Y"
+ :short => "%e %b",
+ :long => "%B %e, %Y",
+ :db => "%Y-%m-%d",
+ :long_ordinal => lambda { |date| date.strftime("%B #{date.day.ordinalize}, %Y") }, # => "April 25th, 2007"
+ :rfc822 => "%e %b %Y"
}
- def self.included(klass) #:nodoc:
- klass.send(:alias_method, :to_default_s, :to_s)
- klass.send(:alias_method, :to_s, :to_formatted_s)
- end
+ def self.included(base) #:nodoc:
+ base.instance_eval do
+ alias_method :to_default_s, :to_s
+ alias_method :to_s, :to_formatted_s
+ alias_method :default_inspect, :inspect
+ alias_method :inspect, :readable_inspect
- def to_formatted_s(format = :default)
- DATE_FORMATS[format] ? strftime(DATE_FORMATS[format]).strip : to_default_s
- end
+ # Ruby 1.9 has Date#to_time which converts to localtime only.
+ remove_method :to_time if base.instance_methods.include?(:to_time)
- # To be able to keep Dates and Times interchangeable on conversions
- def to_date
- self
- end
-
- def to_time(form = :local)
- if respond_to?(:hour)
- ::Time.send(form, year, month, day, hour, min, sec)
- else
- ::Time.send(form, year, month, day)
+ # Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
+ remove_method :xmlschema if base.instance_methods.include?(:xmlschema)
end
end
+ # Convert to a formatted string - see DATE_FORMATS for predefined formats.
+ # You can also add your own formats to the DATE_FORMATS constant and use them with this method.
+ #
+ # This method is also aliased as to_s .
+ #
+ # ==== Examples:
+ # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
+ #
+ # date.to_formatted_s(:db) # => "2007-11-10"
+ # date.to_s(:db) # => "2007-11-10"
+ #
+ # date.to_formatted_s(:short) # => "10 Nov"
+ # date.to_formatted_s(:long) # => "November 10, 2007"
+ # date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
+ # date.to_formatted_s(:rfc822) # => "10 Nov 2007"
+ def to_formatted_s(format = :default)
+ if formatter = DATE_FORMATS[format]
+ if formatter.respond_to?(:call)
+ formatter.call(self).to_s
+ else
+ strftime(formatter)
+ end
+ else
+ to_default_s
+ end
+ end
+
+ # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
+ def readable_inspect
+ strftime("%a, %d %b %Y")
+ end
+
+ # A method to keep Time, Date and DateTime instances interchangeable on conversions.
+ # In this case, it simply returns +self+.
+ def to_date
+ self
+ end if RUBY_VERSION < '1.9'
+
+ # Converts a Date instance to a Time, where the time is set to the beginning of the day.
+ # The timezone can be either :local or :utc (default :local).
+ #
+ # ==== Examples:
+ # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
+ #
+ # date.to_time # => Sat Nov 10 00:00:00 0800 2007
+ # date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007
+ #
+ # date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007
+ def to_time(form = :local)
+ ::Time.send("#{form}_time", year, month, day)
+ end
+
+ # Converts a Date instance to a DateTime, where the time is set to the beginning of the day
+ # and UTC offset is set to 0.
+ #
+ # ==== Example:
+ # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
+ #
+ # date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000
+ def to_datetime
+ ::DateTime.civil(year, month, day, 0, 0, 0, 0)
+ end if RUBY_VERSION < '1.9'
+
def xmlschema
to_time.xmlschema
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/date_time.rb b/vendor/rails/activesupport/lib/active_support/core_ext/date_time.rb
new file mode 100644
index 00000000..1d711de7
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/date_time.rb
@@ -0,0 +1,10 @@
+require 'date'
+require 'active_support/core_ext/time/behavior'
+require 'active_support/core_ext/date_time/calculations'
+require 'active_support/core_ext/date_time/conversions'
+
+class DateTime
+ include ActiveSupport::CoreExtensions::Time::Behavior
+ include ActiveSupport::CoreExtensions::DateTime::Calculations
+ include ActiveSupport::CoreExtensions::DateTime::Conversions
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/vendor/rails/activesupport/lib/active_support/core_ext/date_time/calculations.rb
new file mode 100644
index 00000000..2e85d145
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -0,0 +1,77 @@
+require 'rational'
+
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module DateTime #:nodoc:
+ # Enables the use of time calculations within DateTime itself
+ module Calculations
+ def self.included(base) #:nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone
+ def local_offset
+ ::Time.local(2007).utc_offset.to_r / 86400
+ end
+ end
+
+ # Seconds since midnight: DateTime.now.seconds_since_midnight
+ def seconds_since_midnight
+ self.sec + (self.min * 60) + (self.hour * 3600)
+ end
+
+ # Returns a new DateTime where one or more of the elements have been changed according to the +options+ parameter. The time options
+ # (hour, minute, sec) reset cascadingly, so if only the hour is passed, then minute and sec is set to 0. If the hour and
+ # minute is passed, then sec is set to 0.
+ def change(options)
+ ::DateTime.civil(
+ options[:year] || self.year,
+ options[:month] || self.month,
+ options[:day] || self.day,
+ options[:hour] || self.hour,
+ options[:min] || (options[:hour] ? 0 : self.min),
+ options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec),
+ options[:offset] || self.offset,
+ options[:start] || self.start
+ )
+ end
+
+ # Uses Date to provide precise Time calculations for years, months, and days. The +options+ parameter takes a hash with
+ # any of these keys: :years, :months, :weeks, :days, :hours, :minutes, :seconds.
+ def advance(options)
+ d = to_date.advance(options)
+ datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
+ seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
+ seconds_to_advance == 0 ? datetime_advanced_by_date : datetime_advanced_by_date.since(seconds_to_advance)
+ end
+
+ # Returns a new DateTime representing the time a number of seconds ago
+ # Do not use this method in combination with x.months, use months_ago instead!
+ def ago(seconds)
+ self.since(-seconds)
+ end
+
+ # Returns a new DateTime representing the time a number of seconds since the instance time
+ # Do not use this method in combination with x.months, use months_since instead!
+ def since(seconds)
+ self + Rational(seconds.round, 86400)
+ end
+ alias :in :since
+
+ # Returns a new DateTime representing the start of the day (0:00)
+ def beginning_of_day
+ change(:hour => 0)
+ end
+ alias :midnight :beginning_of_day
+ alias :at_midnight :beginning_of_day
+ alias :at_beginning_of_day :beginning_of_day
+
+ # Returns a new DateTime representing the end of the day (23:59:59)
+ def end_of_day
+ change(:hour => 23, :min => 59, :sec => 59)
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/date_time/conversions.rb
new file mode 100644
index 00000000..8ea8679b
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/date_time/conversions.rb
@@ -0,0 +1,74 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module DateTime #:nodoc:
+ # Getting datetimes in different convenient string representations and other objects
+ module Conversions
+ def self.included(base)
+ base.class_eval do
+ alias_method :to_datetime_default_s, :to_s
+ alias_method :to_s, :to_formatted_s
+ alias_method :default_inspect, :inspect
+ alias_method :inspect, :readable_inspect
+
+ # Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows
+ # DateTimes outside the range of what can be created with Time.
+ remove_method :to_time if base.instance_methods.include?(:to_time)
+ end
+ end
+
+ # Convert to a formatted string - see DATE_FORMATS for predefined formats.
+ # You can also add your own formats to the DATE_FORMATS constant and use them with this method.
+ #
+ # This method is also aliased as to_s .
+ #
+ # === Examples:
+ # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
+ #
+ # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
+ # datetime.to_s(:db) # => "2007-12-04 00:00:00"
+ # datetime.to_s(:number) # => "20071204000000"
+ # datetime.to_formatted_s(:short) # => "04 Dec 00:00"
+ # datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
+ # datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
+ # datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
+ def to_formatted_s(format = :default)
+ if formatter = ::Time::DATE_FORMATS[format]
+ if formatter.respond_to?(:call)
+ formatter.call(self).to_s
+ else
+ strftime(formatter)
+ end
+ else
+ to_datetime_default_s
+ end
+ end
+
+ # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000"
+ def readable_inspect
+ to_s(:rfc822)
+ end
+
+ # Converts self to a Ruby Date object; time portion is discarded
+ def to_date
+ ::Date.new(year, month, day)
+ end
+
+ # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class
+ # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time
+ def to_time
+ self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec) : self
+ end
+
+ # To be able to keep Times, Dates and DateTimes interchangeable on conversions
+ def to_datetime
+ self
+ end
+
+ # Converts datetime to an appropriate format for use in XML
+ def xmlschema
+ strftime("%Y-%m-%dT%H:%M:%S%Z")
+ end if RUBY_VERSION < '1.9'
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/duplicable.rb b/vendor/rails/activesupport/lib/active_support/core_ext/duplicable.rb
new file mode 100644
index 00000000..adbbfd8c
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/duplicable.rb
@@ -0,0 +1,37 @@
+class Object
+ # Can you safely .dup this object?
+ # False for nil, false, true, symbols, and numbers; true otherwise.
+ def duplicable?
+ true
+ end
+end
+
+class NilClass #:nodoc:
+ def duplicable?
+ false
+ end
+end
+
+class FalseClass #:nodoc:
+ def duplicable?
+ false
+ end
+end
+
+class TrueClass #:nodoc:
+ def duplicable?
+ false
+ end
+end
+
+class Symbol #:nodoc:
+ def duplicable?
+ false
+ end
+end
+
+class Numeric #:nodoc:
+ def duplicable?
+ false
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/enumerable.rb b/vendor/rails/activesupport/lib/active_support/core_ext/enumerable.rb
index 93372bb4..f35c8f86 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -38,6 +38,7 @@ module Enumerable
#
def sum(identity = 0, &block)
return identity unless size > 0
+
if block_given?
map(&block).sum
else
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/exception.rb b/vendor/rails/activesupport/lib/active_support/core_ext/exception.rb
index ec15a915..14cd5770 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/exception.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/exception.rb
@@ -8,8 +8,8 @@ class Exception # :nodoc:
def clean_backtrace
backtrace.collect do |line|
- Pathname.clean_within(TraceSubstitutions.inject(line) do |line, (regexp, sub)|
- line.gsub regexp, sub
+ Pathname.clean_within(TraceSubstitutions.inject(line) do |result, (regexp, sub)|
+ result.gsub regexp, sub
end)
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/file.rb b/vendor/rails/activesupport/lib/active_support/core_ext/file.rb
new file mode 100644
index 00000000..cd43be37
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/file.rb
@@ -0,0 +1,21 @@
+require 'tempfile'
+
+# Write to a file atomically. Useful for situations where you don't
+# want other processes or threads to see half-written files.
+#
+# File.atomic_write("important.file") do |file|
+# file.write("hello")
+# end
+#
+# If your temp directory is not on the same filesystem as the file you're
+# trying to write, you can provide a different temporary directory.
+#
+# File.atomic_write("/data/something.imporant", "/data/tmp") do |f|
+# file.write("hello")
+# end
+def File.atomic_write(file_name, temp_dir = Dir.tmpdir)
+ temp_file = Tempfile.new(File.basename(file_name), temp_dir)
+ yield temp_file
+ temp_file.close
+ File.rename(temp_file.path, file_name)
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/float.rb b/vendor/rails/activesupport/lib/active_support/core_ext/float.rb
new file mode 100644
index 00000000..86862b71
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/float.rb
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/float/rounding'
+
+class Float #:nodoc:
+ include ActiveSupport::CoreExtensions::Float::Rounding
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/float/rounding.rb b/vendor/rails/activesupport/lib/active_support/core_ext/float/rounding.rb
new file mode 100644
index 00000000..062d4668
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/float/rounding.rb
@@ -0,0 +1,24 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Float #:nodoc:
+ module Rounding
+ def self.included(base) #:nodoc:
+ base.class_eval do
+ alias_method :round_without_precision, :round
+ alias_method :round, :round_with_precision
+ end
+ end
+
+ # Rounds the float with the specified precision.
+ #
+ # x = 1.337
+ # x.round # => 1
+ # x.round(1) # => 1.3
+ # x.round(2) # => 1.34
+ def round_with_precision(precision = nil)
+ precision.nil? ? round_without_precision : (self * (10 ** precision)).round / (10 ** precision).to_f
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/hash.rb b/vendor/rails/activesupport/lib/active_support/core_ext/hash.rb
index 7d94d709..6cbd9dd3 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/hash.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/hash.rb
@@ -1,8 +1,6 @@
-require File.dirname(__FILE__) + '/hash/keys'
-require File.dirname(__FILE__) + '/hash/indifferent_access'
-require File.dirname(__FILE__) + '/hash/reverse_merge'
-require File.dirname(__FILE__) + '/hash/conversions'
-require File.dirname(__FILE__) + '/hash/diff'
+%w(keys indifferent_access reverse_merge conversions diff slice except).each do |ext|
+ require "active_support/core_ext/hash/#{ext}"
+end
class Hash #:nodoc:
include ActiveSupport::CoreExtensions::Hash::Keys
@@ -10,4 +8,6 @@ class Hash #:nodoc:
include ActiveSupport::CoreExtensions::Hash::ReverseMerge
include ActiveSupport::CoreExtensions::Hash::Conversions
include ActiveSupport::CoreExtensions::Hash::Diff
+ include ActiveSupport::CoreExtensions::Hash::Slice
+ include ActiveSupport::CoreExtensions::Hash::Except
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/hash/conversions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 93311404..8021dfa8 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -1,23 +1,8 @@
require 'date'
-require 'xml_simple'
require 'cgi'
-
-# Extensions needed for Hash#to_query
-class Object
- def to_param #:nodoc:
- to_s
- end
-
- def to_query(key) #:nodoc:
- "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
- end
-end
-
-class Array
- def to_query(key) #:nodoc:
- collect { |value| value.to_query("#{key}[]") }.sort * '&'
- end
-end
+require 'base64'
+require 'builder'
+require 'xmlsimple'
# Locked down XmlSimple#xml_in_string
class XmlSimple
@@ -45,27 +30,66 @@ module ActiveSupport #:nodoc:
module Hash #:nodoc:
module Conversions
XML_TYPE_NAMES = {
+ "Symbol" => "symbol",
"Fixnum" => "integer",
"Bignum" => "integer",
- "BigDecimal" => "numeric",
+ "BigDecimal" => "decimal",
"Float" => "float",
"Date" => "date",
"DateTime" => "datetime",
"Time" => "datetime",
"TrueClass" => "boolean",
"FalseClass" => "boolean"
- } unless defined? XML_TYPE_NAMES
+ } unless defined?(XML_TYPE_NAMES)
XML_FORMATTING = {
+ "symbol" => Proc.new { |symbol| symbol.to_s },
"date" => Proc.new { |date| date.to_s(:db) },
"datetime" => Proc.new { |time| time.xmlschema },
- "binary" => Proc.new { |binary| Base64.encode64(binary) }
- } unless defined? XML_FORMATTING
+ "binary" => Proc.new { |binary| Base64.encode64(binary) },
+ "yaml" => Proc.new { |yaml| yaml.to_yaml }
+ } unless defined?(XML_FORMATTING)
+
+ # TODO: use Time.xmlschema instead of Time.parse;
+ # use regexp instead of Date.parse
+ unless defined?(XML_PARSING)
+ XML_PARSING = {
+ "symbol" => Proc.new { |symbol| symbol.to_sym },
+ "date" => Proc.new { |date| ::Date.parse(date) },
+ "datetime" => Proc.new { |time| ::Time.parse(time).utc },
+ "integer" => Proc.new { |integer| integer.to_i },
+ "float" => Proc.new { |float| float.to_f },
+ "decimal" => Proc.new { |number| BigDecimal(number) },
+ "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
+ "string" => Proc.new { |string| string.to_s },
+ "yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
+ "base64Binary" => Proc.new { |bin| Base64.decode64(bin) },
+ # FIXME: Get rid of eval and institute a proper decorator here
+ "file" => Proc.new do |file, entity|
+ f = StringIO.new(Base64.decode64(file))
+ eval "def f.original_filename() '#{entity["name"]}' || 'untitled' end"
+ eval "def f.content_type() '#{entity["content_type"]}' || 'application/octet-stream' end"
+ f
+ end
+ }
+
+ XML_PARSING.update(
+ "double" => XML_PARSING["float"],
+ "dateTime" => XML_PARSING["datetime"]
+ )
+ end
def self.included(klass)
klass.extend(ClassMethods)
end
+ # Converts a hash into a string suitable for use as a URL query string. An optional namespace can be
+ # passed to enclose the param names (see example below).
+ #
+ # ==== Example:
+ # { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish"
+ #
+ # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
def to_query(namespace = nil)
collect do |key, value|
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
@@ -119,6 +143,8 @@ module ActiveSupport #:nodoc:
end
end
end
+
+ yield options[:builder] if block_given?
end
end
@@ -126,7 +152,7 @@ module ActiveSupport #:nodoc:
module ClassMethods
def from_xml(xml)
# TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
- undasherize_keys(typecast_xml_value(XmlSimple.xml_in_string(xml,
+ typecast_xml_value(undasherize_keys(XmlSimple.xml_in_string(xml,
'forcearray' => false,
'forcecontent' => true,
'keeproot' => true,
@@ -134,52 +160,68 @@ module ActiveSupport #:nodoc:
))
end
- def create_from_xml(xml)
- ActiveSupport::Deprecation.warn("Hash.create_from_xml has been renamed to Hash.from_xml", caller)
- from_xml(xml)
- end
-
private
def typecast_xml_value(value)
case value.class.to_s
- when "Hash"
- if value.has_key?("__content__")
- content = translate_xml_entities(value["__content__"])
- case value["type"]
- when "integer" then content.to_i
- when "boolean" then content.strip == "true"
- when "datetime" then ::Time.parse(content).utc
- when "date" then ::Date.parse(content)
- else content
+ when 'Hash'
+ if value['type'] == 'array'
+ child_key, entries = value.detect { |k,v| k != 'type' } # child_key is throwaway
+ if entries.nil? || (c = value['__content__'] && c.blank?)
+ []
+ else
+ case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a?
+ when "Array"
+ entries.collect { |v| typecast_xml_value(v) }
+ when "Hash"
+ [typecast_xml_value(entries)]
+ else
+ raise "can't typecast #{entries.inspect}"
+ end
end
+ elsif value.has_key?("__content__")
+ content = value["__content__"]
+ if parser = XML_PARSING[value["type"]]
+ if parser.arity == 2
+ XML_PARSING[value["type"]].call(content, value)
+ else
+ XML_PARSING[value["type"]].call(content)
+ end
+ else
+ content
+ end
+ elsif value['type'] == 'string' && value['nil'] != 'true'
+ ""
+ # blank or nil parsed values are represented by nil
+ elsif value.blank? || value['nil'] == 'true'
+ nil
+ # If the type is the only element which makes it then
+ # this still makes the value nil
+ elsif value['type'] && value.size == 1
+ nil
else
- (value.blank? || value['type'] || value['nil'] == 'true') ? nil : value.inject({}) do |h,(k,v)|
+ xml_value = value.inject({}) do |h,(k,v)|
h[k] = typecast_xml_value(v)
h
end
+
+ # Turn { :files => { :file => # } into { :files => # } so it is compatible with
+ # how multipart uploaded files from HTML appear
+ xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
end
- when "Array"
+ when 'Array'
value.map! { |i| typecast_xml_value(i) }
case value.length
when 0 then nil
when 1 then value.first
else value
end
- when "String"
+ when 'String'
value
else
- raise "can't typecast #{value.inspect}"
+ raise "can't typecast #{value.class.name} - #{value.inspect}"
end
end
- def translate_xml_entities(value)
- value.gsub(/</, "<").
- gsub(/>/, ">").
- gsub(/"/, '"').
- gsub(/'/, "'").
- gsub(/&/, "&")
- end
-
def undasherize_keys(params)
case params.class.to_s
when "Hash"
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/hash/diff.rb b/vendor/rails/activesupport/lib/active_support/core_ext/hash/diff.rb
index deace40a..6abd6788 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/hash/diff.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/hash/diff.rb
@@ -2,6 +2,14 @@ module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Hash #:nodoc:
module Diff
+ # Returns a hash that represents the difference between two hashes.
+ #
+ # Examples:
+ #
+ # {1 => 2}.diff(1 => 2) # => {}
+ # {1 => 2}.diff(1 => 3) # => {1 => 2}
+ # {}.diff(1 => 2) # => {1 => 2}
+ # {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
def diff(h2)
self.dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| self.has_key?(k) })
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/hash/except.rb b/vendor/rails/activesupport/lib/active_support/core_ext/hash/except.rb
new file mode 100644
index 00000000..8362cd88
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/hash/except.rb
@@ -0,0 +1,24 @@
+require 'set'
+
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Hash #:nodoc:
+ # Return a hash that includes everything but the given keys. This is useful for
+ # limiting a set of parameters to everything but a few known toggles:
+ #
+ # @person.update_attributes(params[:person].except(:admin))
+ module Except
+ # Returns a new hash without the given keys.
+ def except(*keys)
+ rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
+ reject { |key,| rejected.include?(key) }
+ end
+
+ # Replaces the hash without only the given keys.
+ def except!(*keys)
+ replace(except(*keys))
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/vendor/rails/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index 9a3b02e5..fda0489e 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -1,4 +1,4 @@
-# this class has dubious semantics and we only have it so that
+# This class has dubious semantics and we only have it so that
# people can write params[:key] instead of params['key']
class HashWithIndifferentAccess < Hash
@@ -63,13 +63,27 @@ class HashWithIndifferentAccess < Hash
def stringify_keys!; self end
def symbolize_keys!; self end
+ def to_options!; self end
+
+ # Convert to a Hash with String keys.
+ def to_hash
+ Hash.new(default).merge(self)
+ end
protected
def convert_key(key)
key.kind_of?(Symbol) ? key.to_s : key
end
+
def convert_value(value)
- value.is_a?(Hash) ? value.with_indifferent_access : value
+ case value
+ when Hash
+ value.with_indifferent_access
+ when Array
+ value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
+ else
+ value
+ end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/hash/keys.rb b/vendor/rails/activesupport/lib/active_support/core_ext/hash/keys.rb
index 3c8a59f0..2bd4138b 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -24,25 +24,26 @@ module ActiveSupport #:nodoc:
# Return a new hash with all keys converted to symbols.
def symbolize_keys
inject({}) do |options, (key, value)|
- options[key.to_sym] = value
+ options[key.to_sym || key] = value
options
end
end
# Destructively convert all keys to symbols.
def symbolize_keys!
- keys.each do |key|
- unless key.is_a?(Symbol)
- self[key.to_sym] = self[key]
- delete(key)
- end
- end
- self
+ self.replace(self.symbolize_keys)
end
alias_method :to_options, :symbolize_keys
alias_method :to_options!, :symbolize_keys!
+ # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
+ # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbol
+ # as keys, this will fail.
+ # examples:
+ # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): years, name"
+ # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
def assert_valid_keys(*valid_keys)
unknown_keys = keys - [valid_keys].flatten
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb b/vendor/rails/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
index 46c53871..3c4908ac 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
@@ -18,8 +18,8 @@ module ActiveSupport #:nodoc:
replace(reverse_merge(other_hash))
end
- alias_method :reverse_update, :reverse_merge
+ alias_method :reverse_update, :reverse_merge!
end
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/hash/slice.rb b/vendor/rails/activesupport/lib/active_support/core_ext/hash/slice.rb
new file mode 100644
index 00000000..6fe5e053
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -0,0 +1,28 @@
+require 'set'
+
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Hash #:nodoc:
+ # Slice a hash to include only the given keys. This is useful for
+ # limiting an options hash to valid keys before passing to a method:
+ #
+ # def search(criteria = {})
+ # assert_valid_keys(:mass, :velocity, :time)
+ # end
+ #
+ # search(options.slice(:mass, :velocity, :time))
+ module Slice
+ # Returns a new hash with only the given keys.
+ def slice(*keys)
+ allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
+ reject { |key,| !allowed.include?(key) }
+ end
+
+ # Replaces the hash with only the given keys.
+ def slice!(*keys)
+ replace(slice(*keys))
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/integer.rb b/vendor/rails/activesupport/lib/active_support/core_ext/integer.rb
index 9346b88f..d1e6d76a 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/integer.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/integer.rb
@@ -1,5 +1,5 @@
-require File.dirname(__FILE__) + '/integer/even_odd'
-require File.dirname(__FILE__) + '/integer/inflections'
+require 'active_support/core_ext/integer/even_odd'
+require 'active_support/core_ext/integer/inflections'
class Integer #:nodoc:
include ActiveSupport::CoreExtensions::Integer::EvenOdd
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/integer/even_odd.rb b/vendor/rails/activesupport/lib/active_support/core_ext/integer/even_odd.rb
index 3762308c..7ea4cbf2 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/integer/even_odd.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/integer/even_odd.rb
@@ -10,14 +10,14 @@ module ActiveSupport #:nodoc:
def multiple_of?(number)
self % number == 0
end
-
+
def even?
multiple_of? 2
- end
-
+ end if RUBY_VERSION < '1.9'
+
def odd?
!even?
- end
+ end if RUBY_VERSION < '1.9'
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/kernel.rb b/vendor/rails/activesupport/lib/active_support/core_ext/kernel.rb
index 1aa4f72b..1922d804 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/kernel.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/kernel.rb
@@ -1,4 +1,5 @@
-require File.dirname(__FILE__) + '/kernel/daemonizing'
-require File.dirname(__FILE__) + '/kernel/reporting'
-require File.dirname(__FILE__) + '/kernel/agnostics'
-require File.dirname(__FILE__) + '/kernel/requires'
+require 'active_support/core_ext/kernel/daemonizing'
+require 'active_support/core_ext/kernel/reporting'
+require 'active_support/core_ext/kernel/agnostics'
+require 'active_support/core_ext/kernel/requires'
+require 'active_support/core_ext/kernel/debugger'
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/kernel/debugger.rb b/vendor/rails/activesupport/lib/active_support/core_ext/kernel/debugger.rb
new file mode 100644
index 00000000..c74d6cf8
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/kernel/debugger.rb
@@ -0,0 +1,13 @@
+module Kernel
+ unless respond_to?(:debugger)
+ # Starts a debugging session if ruby-debug has been loaded (call script/server --debugger to do load it).
+ def debugger
+ RAILS_DEFAULT_LOGGER.info "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
+ end
+ end
+
+ def breakpoint
+ RAILS_DEFAULT_LOGGER.info "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n"
+ debugger
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/module.rb b/vendor/rails/activesupport/lib/active_support/core_ext/module.rb
index 8d48de53..da8d5b37 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/module.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/module.rb
@@ -1,7 +1,8 @@
-require File.dirname(__FILE__) + '/module/inclusion'
-require File.dirname(__FILE__) + '/module/attribute_accessors'
-require File.dirname(__FILE__) + '/module/attr_internal'
-require File.dirname(__FILE__) + '/module/delegation'
-require File.dirname(__FILE__) + '/module/introspection'
-require File.dirname(__FILE__) + '/module/loading'
-require File.dirname(__FILE__) + '/module/aliasing'
+require 'active_support/core_ext/module/inclusion'
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/core_ext/module/attr_internal'
+require 'active_support/core_ext/module/attr_accessor_with_default'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/module/introspection'
+require 'active_support/core_ext/module/loading'
+require 'active_support/core_ext/module/aliasing'
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb b/vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb
index 81e81696..1894e3b0 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb
@@ -25,8 +25,20 @@ class Module
# e.g. target?_without_feature is not a valid method name.
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
yield(aliased_target, punctuation) if block_given?
- alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target
- alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
+
+ with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
+
+ alias_method without_method, target
+ alias_method target, with_method
+
+ case
+ when public_method_defined?(without_method)
+ public target
+ when protected_method_defined?(without_method)
+ protected target
+ when private_method_defined?(without_method)
+ private target
+ end
end
# Allows you to make aliases for attributes, which includes
@@ -38,7 +50,7 @@ class Module
# # has a title attribute
# end
#
- # class Email < ActiveRecord::Base
+ # class Email < Content
# alias_attribute :subject, :title
# end
#
@@ -50,8 +62,8 @@ class Module
# e.title # => "Megastars"
def alias_attribute(new_name, old_name)
module_eval <<-STR, __FILE__, __LINE__+1
- def #{new_name}; #{old_name}; end
- def #{new_name}?; #{old_name}?; end
+ def #{new_name}; self.#{old_name}; end
+ def #{new_name}?; self.#{old_name}?; end
def #{new_name}=(v); self.#{old_name} = v; end
STR
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb b/vendor/rails/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
new file mode 100644
index 00000000..683789d8
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
@@ -0,0 +1,31 @@
+class Module
+ # Declare an attribute accessor with an initial default return value.
+ #
+ # To give attribute :age the initial value 25 :
+ #
+ # class Person
+ # attr_accessor_with_default :age, 25
+ # end
+ #
+ # some_person.age
+ # => 25
+ # some_person.age = 26
+ # some_person.age
+ # => 26
+ #
+ # To give attribute :element_name a dynamic default value, evaluated
+ # in scope of self:
+ #
+ # attr_accessor_with_default(:element_name) { name.underscore }
+ #
+ def attr_accessor_with_default(sym, default = nil, &block)
+ raise 'Default value or block required' unless !default.nil? || block
+ define_method(sym, block_given? ? block : Proc.new { default })
+ module_eval(<<-EVAL, __FILE__, __LINE__)
+ def #{sym}=(value)
+ class << self; attr_reader :#{sym} end
+ @#{sym} = value
+ end
+ EVAL
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/vendor/rails/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index 8127150a..58ff3632 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -21,7 +21,7 @@ class Module # :nodoc:
end
def mattr_writer(*syms)
- options = syms.last.is_a?(Hash) ? syms.pop : {}
+ options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym}
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/module/delegation.rb b/vendor/rails/activesupport/lib/active_support/core_ext/module/delegation.rb
index b8cf4e0e..f6647eae 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -5,6 +5,7 @@ class Module
# or string). At least one method and the :to option are required.
#
# Delegation is particularly useful with Active Record associations:
+ #
# class Greeter < ActiveRecord::Base
# def hello() "hello" end
# def goodbye() "goodbye" end
@@ -20,10 +21,30 @@ class Module
#
# Multiple delegates to the same target are allowed:
# class Foo < ActiveRecord::Base
+ # belongs_to :greeter
# delegate :hello, :goodbye, :to => :greeter
# end
#
# Foo.new.goodbye # => "goodbye"
+ #
+ # Methods can be delegated to instance variables, class variables, or constants
+ # by providing the variable as a symbol:
+ # class Foo
+ # CONSTANT_ARRAY = [0,1,2,3]
+ # @@class_array = [4,5,6,7]
+ #
+ # def initialize
+ # @instance_array = [8,9,10,11]
+ # end
+ # delegate :sum, :to => :CONSTANT_ARRAY
+ # delegate :min, :to => :@@class_array
+ # delegate :max, :to => :@instance_array
+ # end
+ #
+ # Foo.new.sum # => 6
+ # Foo.new.min # => 4
+ # Foo.new.max # => 11
+ #
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb b/vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb
index 4af8f228..49176c12 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/name_error.rb
@@ -1,5 +1,5 @@
# Add a +missing_name+ method to NameError instances.
-class NameError < StandardError #:nodoc:
+class NameError #:nodoc:
# Add a method to obtain the missing name from a NameError.
def missing_name
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
@@ -14,4 +14,4 @@ class NameError < StandardError #:nodoc:
missing_name == name.to_s
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/numeric.rb b/vendor/rails/activesupport/lib/active_support/core_ext/numeric.rb
index 88fead7a..59da205b 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/numeric.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/numeric.rb
@@ -1,5 +1,5 @@
-require File.dirname(__FILE__) + '/numeric/time'
-require File.dirname(__FILE__) + '/numeric/bytes'
+require 'active_support/core_ext/numeric/time'
+require 'active_support/core_ext/numeric/bytes'
class Numeric #:nodoc:
include ActiveSupport::CoreExtensions::Numeric::Time
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/numeric/time.rb b/vendor/rails/activesupport/lib/active_support/core_ext/numeric/time.rb
index 93740046..aac698f7 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/numeric/time.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/numeric/time.rb
@@ -3,51 +3,70 @@ module ActiveSupport #:nodoc:
module Numeric #:nodoc:
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
#
- # If you need precise date calculations that doesn't just treat months as 30 days, then have
- # a look at Time#advance.
+ # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
+ # as well as adding or subtracting their results from a Time object. For example:
+ #
+ # # equivalent to Time.now.advance(:months => 1)
+ # 1.month.from_now
+ #
+ # # equivalent to Time.now.advance(:years => 2)
+ # 2.years.from_now
+ #
+ # # equivalent to Time.now.advance(:months => 4, :years => 5)
+ # (4.months + 5.years).from_now
#
- # Some of these methods are approximations, Ruby's core
+ # While these methods provide precise calculation when used as in the examples above, care
+ # should be taken to note that this is not true if the result of `months', `years', etc is
+ # converted before use:
+ #
+ # # equivalent to 30.days.to_i.from_now
+ # 1.month.to_i.from_now
+ #
+ # # equivalent to 365.25.days.to_f.from_now
+ # 1.year.to_f.from_now
+ #
+ # In such cases, Ruby's core
# Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and
# Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision
# date and time arithmetic
module Time
def seconds
- self
+ ActiveSupport::Duration.new(self, [[:seconds, self]])
end
alias :second :seconds
def minutes
- self * 60
+ ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
end
alias :minute :minutes
def hours
- self * 60.minutes
+ ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
end
alias :hour :hours
def days
- self * 24.hours
+ ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
end
alias :day :days
def weeks
- self * 7.days
+ ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
end
alias :week :weeks
def fortnights
- self * 2.weeks
+ ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
end
alias :fortnight :fortnights
def months
- self * 30.days
+ ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
end
alias :month :months
def years
- (self * 365.25.days).to_i
+ ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
end
alias :year :years
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/object.rb b/vendor/rails/activesupport/lib/active_support/core_ext/object.rb
index 3dd6deab..bbc7d816 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/object.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/object.rb
@@ -1,2 +1,4 @@
-require File.dirname(__FILE__) + '/object/extending'
-require File.dirname(__FILE__) + '/object/misc'
\ No newline at end of file
+require 'active_support/core_ext/object/conversions'
+require 'active_support/core_ext/object/extending'
+require 'active_support/core_ext/object/instance_variables'
+require 'active_support/core_ext/object/misc'
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/object/conversions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/object/conversions.rb
new file mode 100644
index 00000000..ad752f0f
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/object/conversions.rb
@@ -0,0 +1,14 @@
+class Object
+ # Alias of to_s .
+ def to_param
+ to_s
+ end
+
+ # Converts an object into a string suitable for use as a URL query string, using the given key as the
+ # param name.
+ #
+ # Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
+ def to_query(key)
+ "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/object/extending.rb b/vendor/rails/activesupport/lib/active_support/core_ext/object/extending.rb
index e15b4bf3..43a2be91 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/object/extending.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/object/extending.rb
@@ -1,47 +1,58 @@
-class Object #:nodoc:
- def remove_subclasses_of(*superclasses)
+class Object
+ def remove_subclasses_of(*superclasses) #:nodoc:
Class.remove_class(*subclasses_of(*superclasses))
end
- def subclasses_of(*superclasses)
+ def subclasses_of(*superclasses) #:nodoc:
subclasses = []
+
+ # Exclude this class unless it's a subclass of our supers and is defined.
+ # We check defined? in case we find a removed class that has yet to be
+ # garbage collected. This also fails for anonymous classes -- please
+ # submit a patch if you have a workaround.
ObjectSpace.each_object(Class) do |k|
- next if # Exclude this class if
- (k.ancestors & superclasses).empty? || # It's not a subclass of our supers
- superclasses.include?(k) || # It *is* one of the supers
- eval("! defined?(::#{k})") || # It's not defined.
- eval("::#{k}").object_id != k.object_id
- subclasses << k
+ if superclasses.any? { |superclass| k < superclass } &&
+ (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
+ subclasses << k
+ end
end
+
subclasses
end
-
- def extended_by
+
+ def extended_by #:nodoc:
ancestors = class << self; ancestors end
ancestors.select { |mod| mod.class == Module } - [ Object, Kernel ]
end
-
- def copy_instance_variables_from(object, exclude = [])
- exclude += object.protected_instance_variables if object.respond_to? :protected_instance_variables
-
- instance_variables = object.instance_variables - exclude.map { |name| name.to_s }
- instance_variables.each { |name| instance_variable_set(name, object.instance_variable_get(name)) }
- end
-
- def extend_with_included_modules_from(object)
+
+ def extend_with_included_modules_from(object) #:nodoc:
object.extended_by.each { |mod| extend mod }
end
- def instance_values
- instance_variables.inject({}) do |values, name|
- values[name[1..-1]] = instance_variable_get(name)
- values
- end
- end
-
unless defined? instance_exec # 1.9
- def instance_exec(*arguments, &block)
- block.bind(self)[*arguments]
+ module InstanceExecMethods #:nodoc:
+ end
+ include InstanceExecMethods
+
+ # Evaluate the block with the given arguments within the context of
+ # this object, so self is set to the method receiver.
+ #
+ # From Mauricio's http://eigenclass.org/hiki/bounded+space+instance_exec
+ def instance_exec(*args, &block)
+ begin
+ old_critical, Thread.critical = Thread.critical, true
+ n = 0
+ n += 1 while respond_to?(method_name = "__instance_exec#{n}")
+ InstanceExecMethods.module_eval { define_method(method_name, &block) }
+ ensure
+ Thread.critical = old_critical
+ end
+
+ begin
+ send(method_name, *args)
+ ensure
+ InstanceExecMethods.module_eval { remove_method(method_name) } rescue nil
+ end
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/object/instance_variables.rb b/vendor/rails/activesupport/lib/active_support/core_ext/object/instance_variables.rb
new file mode 100644
index 00000000..e07eb76c
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/object/instance_variables.rb
@@ -0,0 +1,22 @@
+class Object
+ # Available in 1.8.6 and later.
+ unless respond_to?(:instance_variable_defined?)
+ def instance_variable_defined?(variable)
+ instance_variables.include?(variable.to_s)
+ end
+ end
+
+ def instance_values #:nodoc:
+ instance_variables.inject({}) do |values, name|
+ values[name.to_s[1..-1]] = instance_variable_get(name)
+ values
+ end
+ end
+
+ def copy_instance_variables_from(object, exclude = []) #:nodoc:
+ exclude += object.protected_instance_variables if object.respond_to? :protected_instance_variables
+
+ vars = object.instance_variables.map(&:to_s) - exclude.map(&:to_s)
+ vars.each { |name| instance_variable_set(name, object.instance_variable_get(name)) }
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/object/misc.rb b/vendor/rails/activesupport/lib/active_support/core_ext/object/misc.rb
index ea8e3a1d..a3637d7a 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/object/misc.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/object/misc.rb
@@ -1,4 +1,9 @@
-class Object #:nodoc:
+class Object
+ unless respond_to?(:send!)
+ # Anticipating Ruby 1.9 neutering send
+ alias send! send
+ end
+
# A Ruby-ized realization of the K combinator, courtesy of Mikael Brockman.
#
# def foo
@@ -24,11 +29,31 @@ class Object #:nodoc:
value
end
+ # An elegant way to refactor out common options
+ #
+ # with_options :order => 'created_at', :class_name => 'Comment' do |post|
+ # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all
+ # post.has_many :unapproved_comments, :conditions => ['approved = ?', false]
+ # post.has_many :all_comments
+ # end
+ #
+ # Can also be used with an explicit receiver:
+ #
+ # map.with_options :controller => "people" do |people|
+ # people.connect "/people", :action => "index"
+ # people.connect "/people/:id", :action => "show"
+ # end
+ #
def with_options(options)
yield ActiveSupport::OptionMerger.new(self, options)
end
- def to_json
- ActiveSupport::JSON.encode(self)
+ # A duck-type assistant method. For example, ActiveSupport extends Date
+ # to define an acts_like_date? method, and extends Time to define
+ # acts_like_time?. As a result, we can do "x.acts_like?(:time)" and
+ # "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that
+ # we want to act like Time simply need to define an acts_like_time? method.
+ def acts_like?(duck)
+ respond_to? "acts_like_#{duck}?"
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/pathname.rb b/vendor/rails/activesupport/lib/active_support/core_ext/pathname.rb
index 9e78c273..4c5318ee 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/pathname.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/pathname.rb
@@ -1,5 +1,5 @@
require 'pathname'
-require File.dirname(__FILE__) + '/pathname/clean_within'
+require 'active_support/core_ext/pathname/clean_within'
class Pathname#:nodoc:
extend ActiveSupport::CoreExtensions::Pathname::CleanWithin
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/range.rb b/vendor/rails/activesupport/lib/active_support/core_ext/range.rb
index ca775115..0d2b169e 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/range.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/range.rb
@@ -1,5 +1,11 @@
-require File.dirname(__FILE__) + '/range/conversions'
+require 'active_support/core_ext/range/conversions'
+require 'active_support/core_ext/range/overlaps'
+require 'active_support/core_ext/range/include_range'
+require 'active_support/core_ext/range/blockless_step'
class Range #:nodoc:
include ActiveSupport::CoreExtensions::Range::Conversions
+ include ActiveSupport::CoreExtensions::Range::Overlaps
+ include ActiveSupport::CoreExtensions::Range::IncludeRange
+ include ActiveSupport::CoreExtensions::Range::BlocklessStep
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/range/blockless_step.rb b/vendor/rails/activesupport/lib/active_support/core_ext/range/blockless_step.rb
new file mode 100644
index 00000000..bc69263a
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/range/blockless_step.rb
@@ -0,0 +1,22 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Range #:nodoc:
+ # Return an array when step is called without a block.
+ module BlocklessStep
+ def self.included(base) #:nodoc:
+ base.alias_method_chain :step, :blockless
+ end
+
+ def step_with_blockless(value, &block)
+ if block_given?
+ step_without_blockless(value, &block)
+ else
+ returning [] do |array|
+ step_without_blockless(value) { |step| array << step }
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/range/conversions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/range/conversions.rb
index 677ba639..3d12605f 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/range/conversions.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/range/conversions.rb
@@ -1,21 +1,23 @@
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Range #:nodoc:
- # Getting dates in different convenient string representations and other objects
+ # Getting ranges in different convenient string representations and other objects
module Conversions
- DATE_FORMATS = {
+ RANGE_FORMATS = {
:db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
}
-
- def self.included(klass) #:nodoc:
- klass.send(:alias_method, :to_default_s, :to_s)
- klass.send(:alias_method, :to_s, :to_formatted_s)
+
+ def self.included(base) #:nodoc:
+ base.class_eval do
+ alias_method :to_default_s, :to_s
+ alias_method :to_s, :to_formatted_s
+ end
end
-
+
def to_formatted_s(format = :default)
- DATE_FORMATS[format] ? DATE_FORMATS[format].call(first, last) : to_default_s
+ RANGE_FORMATS[format] ? RANGE_FORMATS[format].call(first, last) : to_default_s
end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/range/include_range.rb b/vendor/rails/activesupport/lib/active_support/core_ext/range/include_range.rb
new file mode 100644
index 00000000..cd53cf15
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/range/include_range.rb
@@ -0,0 +1,22 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Range #:nodoc:
+ # Check if a Range includes another Range.
+ module IncludeRange
+ def self.included(base) #:nodoc:
+ base.alias_method_chain :include?, :range
+ end
+
+ def include_with_range?(value)
+ if value.is_a?(::Range)
+ operator = exclude_end? ? :< : :<=
+ end_value = value.exclude_end? ? last.succ : last
+ include?(value.first) && (value.last <=> end_value).send(operator, 0)
+ else
+ include_without_range?(value)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/range/overlaps.rb b/vendor/rails/activesupport/lib/active_support/core_ext/range/overlaps.rb
new file mode 100644
index 00000000..80ed1bba
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/range/overlaps.rb
@@ -0,0 +1,12 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Range #:nodoc:
+ # Check if Ranges overlap.
+ module Overlaps
+ def overlaps?(other)
+ include?(other.first) || other.include?(first)
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/string.rb b/vendor/rails/activesupport/lib/active_support/core_ext/string.rb
index b9f07552..5497b6f6 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/string.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/string.rb
@@ -1,15 +1,23 @@
-require File.dirname(__FILE__) + '/string/inflections'
-require File.dirname(__FILE__) + '/string/conversions'
-require File.dirname(__FILE__) + '/string/access'
-require File.dirname(__FILE__) + '/string/starts_ends_with'
-require File.dirname(__FILE__) + '/string/iterators'
-require File.dirname(__FILE__) + '/string/unicode'
+require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/string/conversions'
+require 'active_support/core_ext/string/access'
+require 'active_support/core_ext/string/starts_ends_with'
+require 'active_support/core_ext/string/iterators' unless 'test'.respond_to?(:each_char)
+require 'active_support/core_ext/string/unicode'
+require 'active_support/core_ext/string/xchar'
class String #:nodoc:
include ActiveSupport::CoreExtensions::String::Access
include ActiveSupport::CoreExtensions::String::Conversions
include ActiveSupport::CoreExtensions::String::Inflections
- include ActiveSupport::CoreExtensions::String::StartsEndsWith
- include ActiveSupport::CoreExtensions::String::Iterators
+ if RUBY_VERSION < '1.9'
+ include ActiveSupport::CoreExtensions::String::StartsEndsWith
+ else
+ alias starts_with? start_with?
+ alias ends_with? end_with?
+ end
+ if defined? ActiveSupport::CoreExtensions::String::Iterators
+ include ActiveSupport::CoreExtensions::String::Iterators
+ end
include ActiveSupport::CoreExtensions::String::Unicode
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/string/conversions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/string/conversions.rb
index cfb767d1..663ea874 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -5,15 +5,24 @@ module ActiveSupport #:nodoc:
module String #:nodoc:
# Converting strings to other objects
module Conversions
+ # 'a'.ord == 'a'[0] for Ruby 1.9 forward compatibility.
+ def ord
+ self[0]
+ end if RUBY_VERSION < '1.9'
+
# Form can be either :utc (default) or :local.
def to_time(form = :utc)
- ::Time.send(form, *ParseDate.parsedate(self))
+ ::Time.send("#{form}_time", *ParseDate.parsedate(self)[0..5].map {|arg| arg || 0})
end
def to_date
::Date.new(*ParseDate.parsedate(self)[0..2])
end
+
+ def to_datetime
+ ::DateTime.civil(*ParseDate.parsedate(self)[0..5].map {|arg| arg || 0} << 0)
+ end
end
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb b/vendor/rails/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb
index 67174019..39606697 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb
@@ -3,6 +3,13 @@ module ActiveSupport #:nodoc:
module String #:nodoc:
# Additional string tests.
module StartsEndsWith
+ def self.included(base)
+ base.class_eval do
+ alias_method :start_with?, :starts_with?
+ alias_method :end_with?, :ends_with?
+ end
+ end
+
# Does the string start with the specified +prefix+?
def starts_with?(prefix)
prefix = prefix.to_s
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb b/vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb
index 0e4c76fb..dd19fe54 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/string/unicode.rb
@@ -1,7 +1,7 @@
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module String #:nodoc:
- # Define methods for handeling unicode data.
+ # Define methods for handling unicode data.
module Unicode
# +chars+ is a Unicode safe proxy for string methods. It creates and returns an instance of the
# ActiveSupport::Multibyte::Chars class which encapsulates the original string. A Unicode safe version of all
@@ -39,4 +39,4 @@ module ActiveSupport #:nodoc:
end
end
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/string/xchar.rb b/vendor/rails/activesupport/lib/active_support/core_ext/string/xchar.rb
new file mode 100644
index 00000000..df186e42
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/string/xchar.rb
@@ -0,0 +1,11 @@
+begin
+ # See http://bogomips.org/fast_xs/ by Eric Wong
+ require 'fast_xs'
+
+ class String
+ alias_method :original_xs, :to_xs if method_defined?(:to_xs)
+ alias_method :to_xs, :fast_xs
+ end
+rescue LoadError
+ # fast_xs extension unavailable.
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/symbol.rb b/vendor/rails/activesupport/lib/active_support/core_ext/symbol.rb
index a3dc794d..54f541ad 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/symbol.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/symbol.rb
@@ -1,12 +1,14 @@
-class Symbol
- # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
- #
- # # The same as people.collect { |p| p.name }
- # people.collect(&:name)
- #
- # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
- # people.select(&:manager?).collect(&:salary)
- def to_proc
- Proc.new { |*args| args.shift.__send__(self, *args) }
+unless :test.respond_to?(:to_proc)
+ class Symbol
+ # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
+ #
+ # # The same as people.collect { |p| p.name }
+ # people.collect(&:name)
+ #
+ # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
+ # people.select(&:manager?).collect(&:salary)
+ def to_proc
+ Proc.new { |*args| args.shift.__send__(self, *args) }
+ end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/test.rb b/vendor/rails/activesupport/lib/active_support/core_ext/test.rb
new file mode 100644
index 00000000..c0b19bdc
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/test.rb
@@ -0,0 +1 @@
+require 'active_support/core_ext/test/unit/assertions'
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/test/unit/assertions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/test/unit/assertions.rb
new file mode 100644
index 00000000..2ae4b7d6
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/test/unit/assertions.rb
@@ -0,0 +1,62 @@
+module Test #:nodoc:
+ module Unit #:nodoc:
+ # FIXME: no Proc#binding in Ruby 2, must change this API
+ module Assertions #:nodoc:
+ # Test numeric difference between the return value of an expression as a result of what is evaluated
+ # in the yielded block.
+ #
+ # assert_difference 'Article.count' do
+ # post :create, :article => {...}
+ # end
+ #
+ # An arbitrary expression is passed in and evaluated.
+ #
+ # assert_difference 'assigns(:article).comments(:reload).size' do
+ # post :create, :comment => {...}
+ # end
+ #
+ # An arbitrary positive or negative difference can be specified. The default is +1.
+ #
+ # assert_difference 'Article.count', -1 do
+ # post :delete, :id => ...
+ # end
+ #
+ # An array of expressions can also be passed in and evaluated.
+ #
+ # assert_difference [ 'Article.count', 'Post.count' ], +2 do
+ # post :create, :article => {...}
+ # end
+ #
+ # A error message can be specified.
+ #
+ # assert_difference 'Article.count', -1, "An Article should be destroyed" do
+ # post :delete, :id => ...
+ # end
+ def assert_difference(expressions, difference = 1, message = nil, &block)
+ expression_evaluations = Array(expressions).collect{ |expression| lambda { eval(expression, block.send!(:binding)) } }
+
+ original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
+ yield
+ expression_evaluations.each_with_index do |expression, i|
+ assert_equal original_values[i] + difference, expression.call, message
+ end
+ end
+
+ # Assertion that the numeric result of evaluating an expression is not changed before and after
+ # invoking the passed in block.
+ #
+ # assert_no_difference 'Article.count' do
+ # post :create, :article => invalid_attributes
+ # end
+ #
+ # A error message can be specified.
+ #
+ # assert_no_difference 'Article.count', "An Article should not be destroyed" do
+ # post :create, :article => invalid_attributes
+ # end
+ def assert_no_difference(expressions, message = nil, &block)
+ assert_difference expressions, 0, message, &block
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/time.rb b/vendor/rails/activesupport/lib/active_support/core_ext/time.rb
index 1e1819f7..d87c4ec6 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/time.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/time.rb
@@ -8,10 +8,12 @@ class Time
end
end
-require File.dirname(__FILE__) + '/time/calculations'
-require File.dirname(__FILE__) + '/time/conversions'
+require 'active_support/core_ext/time/behavior'
+require 'active_support/core_ext/time/calculations'
+require 'active_support/core_ext/time/conversions'
class Time#:nodoc:
+ include ActiveSupport::CoreExtensions::Time::Behavior
include ActiveSupport::CoreExtensions::Time::Calculations
include ActiveSupport::CoreExtensions::Time::Conversions
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/time/behavior.rb b/vendor/rails/activesupport/lib/active_support/core_ext/time/behavior.rb
new file mode 100644
index 00000000..a5c0baac
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/time/behavior.rb
@@ -0,0 +1,13 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module Time #:nodoc:
+ module Behavior
+ # Enable more predictable duck-typing on Time-like classes. See
+ # Object#acts_like?.
+ def acts_like_time?
+ true
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/time/calculations.rb b/vendor/rails/activesupport/lib/active_support/core_ext/time/calculations.rb
index fb825130..7181bac3 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -4,7 +4,14 @@ module ActiveSupport #:nodoc:
# Enables the use of time calculations within Time itself
module Calculations
def self.included(base) #:nodoc:
- base.extend(ClassMethods)
+ base.extend ClassMethods
+
+ base.class_eval do
+ alias_method :plus_without_duration, :+
+ alias_method :+, :plus_with_duration
+ alias_method :minus_without_duration, :-
+ alias_method :-, :minus_with_duration
+ end
end
module ClassMethods
@@ -21,36 +28,56 @@ module ActiveSupport #:nodoc:
month % 2 == 0 ? 31 : 30
end
end
+
+ # Returns a new Time if requested year can be accommodated by Ruby's Time class
+ # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
+ # otherwise returns a DateTime
+ def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
+ ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
+ rescue
+ offset = utc_or_local.to_sym == :local ? ::DateTime.local_offset : 0
+ ::DateTime.civil(year, month, day, hour, min, sec, offset)
+ end
+
+ # wraps class method time_with_datetime_fallback with utc_or_local == :utc
+ def utc_time(*args)
+ time_with_datetime_fallback(:utc, *args)
+ end
+
+ # wraps class method time_with_datetime_fallback with utc_or_local == :local
+ def local_time(*args)
+ time_with_datetime_fallback(:local, *args)
+ end
end
# Seconds since midnight: Time.now.seconds_since_midnight
def seconds_since_midnight
self.to_i - self.change(:hour => 0).to_i + (self.usec/1.0e+6)
end
-
+
# Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
- # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
+ # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
# minute is passed, then sec and usec is set to 0.
def change(options)
::Time.send(
- self.utc? ? :utc : :local,
- options[:year] || self.year,
- options[:month] || self.month,
- options[:mday] || self.mday,
- options[:hour] || self.hour,
+ self.utc? ? :utc_time : :local_time,
+ options[:year] || self.year,
+ options[:month] || self.month,
+ options[:day] || self.day,
+ options[:hour] || self.hour,
options[:min] || (options[:hour] ? 0 : self.min),
options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec),
options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : self.usec)
)
end
-
- # Uses Date to provide precise Time calculations for years, months, and days. The +options+ parameter takes a hash with
- # any of these keys: :months, :days, :years.
+
+ # Uses Date to provide precise Time calculations for years, months, and days. The +options+ parameter takes a hash with
+ # any of these keys: :years, :months, :weeks, :days, :hours, :minutes, :seconds.
def advance(options)
- d = ::Date.new(year + (options.delete(:years) || 0), month, day)
- d = d >> options.delete(:months) if options[:months]
- d = d + options.delete(:days) if options[:days]
- change(options.merge(:year => d.year, :month => d.month, :mday => d.day))
+ d = to_date.advance(options)
+ time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
+ seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
+ seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance)
end
# Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
@@ -59,58 +86,43 @@ module ActiveSupport #:nodoc:
self.since(-seconds)
end
- # Returns a new Time representing the time a number of seconds since the instance time, this is basically a wrapper around
- #the Numeric extension. Do not use this method in combination with x.months, use months_since instead!
+ # Returns a new Time representing the time a number of seconds since the instance time, this is basically a wrapper around
+ # the Numeric extension. Do not use this method in combination with x.months, use months_since instead!
def since(seconds)
initial_dst = self.dst? ? 1 : 0
f = seconds.since(self)
final_dst = f.dst? ? 1 : 0
(seconds.abs >= 86400 && initial_dst != final_dst) ? f + (initial_dst - final_dst).hours : f
+ rescue
+ self.to_datetime.since(seconds)
end
alias :in :since
# Returns a new Time representing the time a number of specified months ago
def months_ago(months)
- months_since(-months)
+ advance(:months => -months)
end
+ # Returns a new Time representing the time a number of specified months in the future
def months_since(months)
- year, month, mday = self.year, self.month, self.mday
-
- month += months
-
- # in case months is negative
- while month < 1
- month += 12
- year -= 1
- end
-
- # in case months is positive
- while month > 12
- month -= 12
- year += 1
- end
-
- max = ::Time.days_in_month(month, year)
- mday = max if mday > max
-
- change(:year => year, :month => month, :mday => mday)
+ advance(:months => months)
end
# Returns a new Time representing the time a number of specified years ago
def years_ago(years)
- change(:year => self.year - years)
+ advance(:years => -years)
end
-
+
+ # Returns a new Time representing the time a number of specified years in the future
def years_since(years)
- change(:year => self.year + years)
+ advance(:years => years)
end
# Short-hand for years_ago(1)
def last_year
years_ago(1)
end
-
+
# Short-hand for years_since(1)
def next_year
years_since(1)
@@ -126,7 +138,7 @@ module ActiveSupport #:nodoc:
def next_month
months_since(1)
end
-
+
# Returns a new Time representing the "start" of this week (Monday, 0:00)
def beginning_of_week
days_to_monday = self.wday!=0 ? self.wday-1 : 6
@@ -134,13 +146,13 @@ module ActiveSupport #:nodoc:
end
alias :monday :beginning_of_week
alias :at_beginning_of_week :beginning_of_week
-
+
# Returns a new Time representing the start of the given day in next week (default is Monday).
def next_week(day = :monday)
days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
since(1.week).beginning_of_week.since(days_into_week[day].day).change(:hour => 0)
end
-
+
# Returns a new Time representing the start of the day (0:00)
def beginning_of_day
(self - self.seconds_since_midnight).change(:usec => 0)
@@ -148,43 +160,64 @@ module ActiveSupport #:nodoc:
alias :midnight :beginning_of_day
alias :at_midnight :beginning_of_day
alias :at_beginning_of_day :beginning_of_day
-
+
+ # Returns a new Time representing the end of the day (23:59:59)
+ def end_of_day
+ change(:hour => 23, :min => 59, :sec => 59)
+ end
+
# Returns a new Time representing the start of the month (1st of the month, 0:00)
def beginning_of_month
#self - ((self.mday-1).days + self.seconds_since_midnight)
- change(:mday => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
+ change(:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
end
alias :at_beginning_of_month :beginning_of_month
-
+
# Returns a new Time representing the end of the month (last day of the month, 0:00)
def end_of_month
#self - ((self.mday-1).days + self.seconds_since_midnight)
last_day = ::Time.days_in_month( self.month, self.year )
- change(:mday => last_day,:hour => 0, :min => 0, :sec => 0, :usec => 0)
+ change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 0)
end
alias :at_end_of_month :end_of_month
-
+
# Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00)
def beginning_of_quarter
beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month })
end
alias :at_beginning_of_quarter :beginning_of_quarter
-
+
# Returns a new Time representing the start of the year (1st of january, 0:00)
def beginning_of_year
- change(:month => 1,:mday => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
+ change(:month => 1,:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
end
alias :at_beginning_of_year :beginning_of_year
-
+
# Convenience method which returns a new Time representing the time 1 day ago
def yesterday
self.ago(1.day)
end
-
+
# Convenience method which returns a new Time representing the time 1 day since the instance time
def tomorrow
self.since(1.day)
end
+
+ def plus_with_duration(other) #:nodoc:
+ if ActiveSupport::Duration === other
+ other.since(self)
+ else
+ plus_without_duration(other)
+ end
+ end
+
+ def minus_with_duration(other) #:nodoc:
+ if ActiveSupport::Duration === other
+ other.until(self)
+ else
+ minus_without_duration(other)
+ end
+ end
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/core_ext/time/conversions.rb b/vendor/rails/activesupport/lib/active_support/core_ext/time/conversions.rb
index a4bd77cb..0ce90669 100644
--- a/vendor/rails/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/vendor/rails/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -4,29 +4,90 @@ module ActiveSupport #:nodoc:
# Getting times in different convenient string representations and other objects
module Conversions
DATE_FORMATS = {
- :db => "%Y-%m-%d %H:%M:%S",
- :short => "%d %b %H:%M",
- :long => "%B %d, %Y %H:%M",
- :rfc822 => "%a, %d %b %Y %H:%M:%S %z"
+ :db => "%Y-%m-%d %H:%M:%S",
+ :time => "%H:%M",
+ :short => "%d %b %H:%M",
+ :long => "%B %d, %Y %H:%M",
+ :long_ordinal => lambda { |time| time.strftime("%B #{time.day.ordinalize}, %Y %H:%M") },
+ :rfc822 => "%a, %d %b %Y %H:%M:%S %z"
}
- def self.included(klass)
- klass.send(:alias_method, :to_default_s, :to_s)
- klass.send(:alias_method, :to_s, :to_formatted_s)
- end
-
- def to_formatted_s(format = :default)
- DATE_FORMATS[format] ? strftime(DATE_FORMATS[format]).strip : to_default_s
+ def self.included(base)
+ base.class_eval do
+ alias_method :to_default_s, :to_s
+ alias_method :to_s, :to_formatted_s
+ end
end
+ # Convert to a formatted string - see DATE_FORMATS for predefined formats.
+ # You can also add your own formats to the DATE_FORMATS constant and use them with this method.
+ #
+ # This method is also aliased as to_s .
+ #
+ # ==== Examples:
+ # time = Time.now # => Thu Jan 18 06:10:17 CST 2007
+ #
+ # time.to_formatted_s(:time) # => "06:10:17"
+ # time.to_s(:time) # => "06:10:17"
+ #
+ # time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
+ # time.to_formatted_s(:short) # => "18 Jan 06:10"
+ # time.to_formatted_s(:long) # => "January 18, 2007 06:10"
+ # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
+ # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
+ def to_formatted_s(format = :default)
+ if formatter = DATE_FORMATS[format]
+ if formatter.respond_to?(:call)
+ formatter.call(self).to_s
+ else
+ strftime(formatter)
+ end
+ else
+ to_default_s
+ end
+ end
+
+ # Convert a Time object to a Date, dropping hour, minute, and second precision.
+ #
+ # ==== Examples
+ # my_time = Time.now
+ # # => Mon Nov 12 22:59:51 -0500 2007
+ #
+ # my_time.to_date
+ # #=> Mon, 12 Nov 2007
+ #
+ # your_time = Time.parse("1/13/2009 1:13:03 P.M.")
+ # # => Tue Jan 13 13:13:03 -0500 2009
+ #
+ # your_time.to_date
+ # # => Tue, 13 Jan 2009
def to_date
::Date.new(year, month, day)
end
- # To be able to keep Dates and Times interchangeable on conversions
+ # A method to keep Time, Date and DateTime instances interchangeable on conversions.
+ # In this case, it simply returns +self+.
def to_time
self
end
+
+ # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
+ #
+ # ==== Examples
+ # my_time = Time.now
+ # # => Mon Nov 12 23:04:21 -0500 2007
+ #
+ # my_time.to_datetime
+ # # => Mon, 12 Nov 2007 23:04:21 -0500
+ #
+ # your_time = Time.parse("1/13/2009 1:13:03 P.M.")
+ # # => Tue Jan 13 13:13:03 -0500 2009
+ #
+ # your_time.to_datetime
+ # # => Tue, 13 Jan 2009 13:13:03 -0500
+ def to_datetime
+ ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400))
+ end
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/dependencies.rb b/vendor/rails/activesupport/lib/active_support/dependencies.rb
index 2f6ae284..6a5cfbf2 100644
--- a/vendor/rails/activesupport/lib/active_support/dependencies.rb
+++ b/vendor/rails/activesupport/lib/active_support/dependencies.rb
@@ -1,7 +1,7 @@
require 'set'
-require File.dirname(__FILE__) + '/core_ext/module/attribute_accessors'
-require File.dirname(__FILE__) + '/core_ext/load_error'
-require File.dirname(__FILE__) + '/core_ext/kernel'
+require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/core_ext/load_error'
+require 'active_support/core_ext/kernel'
module Dependencies #:nodoc:
extend self
@@ -114,7 +114,7 @@ module Dependencies #:nodoc:
raise NameError, "#{path.inspect} is not a valid constant name!" unless
/^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path
- names = path.split('::')
+ names = path.to_s.split('::')
names.shift if names.first.empty?
# We can't use defined? because it will invoke const_missing for the parent
@@ -216,7 +216,7 @@ module Dependencies #:nodoc:
end
# Load the constant named +const_name+ which is missing from +from_mod+. If
- # it is not possible to laod the constant into from_mod, try its parent module
+ # it is not possible to load the constant into from_mod, try its parent module
# using const_missing.
def load_missing_constant(from_mod, const_name)
log_call from_mod, const_name
@@ -229,14 +229,14 @@ module Dependencies #:nodoc:
from_mod = Object
end
end
-
+
# If we have an anonymous module, all we can do is attempt to load from Object.
- from_mod = Object if from_mod.name.empty?
-
+ from_mod = Object if from_mod.name.blank?
+
unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
end
-
+
raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if from_mod.const_defined?(const_name)
qualified_name = qualified_name_for from_mod, const_name
@@ -383,7 +383,7 @@ module Dependencies #:nodoc:
class LoadingModule #:nodoc:
# Old style environment.rb referenced this method directly. Please note, it doesn't
- # actualy *do* anything any more.
+ # actually *do* anything any more.
def self.root(*args)
if defined?(RAILS_DEFAULT_LOGGER)
RAILS_DEFAULT_LOGGER.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases."
@@ -392,11 +392,9 @@ module Dependencies #:nodoc:
end
end
-protected
-
# Convert the provided const desc to a qualified constant name (as a string).
# A module, class, symbol, or string may be provided.
- def to_constant_name(desc)
+ def to_constant_name(desc) #:nodoc:
name = case desc
when String then desc.starts_with?('::') ? desc[2..-1] : desc
when Symbol then desc.to_s
@@ -406,23 +404,24 @@ protected
else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
end
end
-
- def remove_constant(const)
+
+ def remove_constant(const) #:nodoc:
return false unless qualified_const_defined? const
-
+
const = $1 if /\A::(.*)\Z/ =~ const.to_s
- names = const.split('::')
+ names = const.to_s.split('::')
if names.size == 1 # It's under Object
parent = Object
else
parent = (names[0..-2] * '::').constantize
end
-
+
log "removing constant #{const}"
- parent.send :remove_const, names.last
+ parent.instance_eval { remove_const names.last }
return true
end
-
+
+protected
def log_call(*args)
arg_str = args.collect(&:inspect) * ', '
/in `([a-z_\?\!]+)'/ =~ caller(1).first
@@ -438,9 +437,11 @@ protected
end
-Object.send(:define_method, :require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load)
-Object.send(:define_method, :require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency)
-Object.send(:define_method, :require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association)
+Object.instance_eval do
+ define_method(:require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load)
+ define_method(:require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency)
+ define_method(:require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association)
+end
class Module #:nodoc:
# Rename the original handler so we can chain it to the new one
@@ -459,39 +460,39 @@ class Module #:nodoc:
end
class Class
- def const_missing(class_id)
+ def const_missing(const_name)
if [Object, Kernel].include?(self) || parent == self
super
else
begin
begin
- Dependencies.load_missing_constant self, class_id
+ Dependencies.load_missing_constant self, const_name
rescue NameError
- parent.send :const_missing, class_id
+ parent.send :const_missing, const_name
end
rescue NameError => e
# Make sure that the name we are missing is the one that caused the error
- parent_qualified_name = Dependencies.qualified_name_for parent, class_id
+ parent_qualified_name = Dependencies.qualified_name_for parent, const_name
raise unless e.missing_name? parent_qualified_name
- qualified_name = Dependencies.qualified_name_for self, class_id
+ qualified_name = Dependencies.qualified_name_for self, const_name
raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e)
end
end
end
end
-class Object #:nodoc:
+class Object
alias_method :load_without_new_constant_marking, :load
- def load(file, *extras)
+ def load(file, *extras) #:nodoc:
Dependencies.new_constants_in(Object) { super(file, *extras) }
rescue Exception => exception # errors from loading file
exception.blame_file! file
raise
end
- def require(file, *extras)
+ def require(file, *extras) #:nodoc:
Dependencies.new_constants_in(Object) { super(file, *extras) }
rescue Exception => exception # errors from required file
exception.blame_file! file
diff --git a/vendor/rails/activesupport/lib/active_support/deprecation.rb b/vendor/rails/activesupport/lib/active_support/deprecation.rb
index 8499ad6e..c9f1884d 100644
--- a/vendor/rails/activesupport/lib/active_support/deprecation.rb
+++ b/vendor/rails/activesupport/lib/active_support/deprecation.rb
@@ -85,7 +85,7 @@ module ActiveSupport
module ClassMethods #:nodoc:
# Declare that a method has been deprecated.
def deprecate(*method_names)
- options = method_names.last.is_a?(Hash) ? method_names.pop : {}
+ options = method_names.extract_options!
method_names = method_names + options.keys
method_names.each do |method_name|
alias_method_chain(method_name, :deprecation) do |target, punctuation|
@@ -147,7 +147,9 @@ module ActiveSupport
# Stand-in for @request, @attributes, @params, etc which emits deprecation
# warnings on any method call (except #inspect).
class DeprecatedInstanceVariableProxy #:nodoc:
- instance_methods.each { |m| undef_method m unless m =~ /^__/ }
+ silence_warnings do
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
+ end
def initialize(instance, method, var = "@#{method}")
@instance, @method, @var = instance, method, var
diff --git a/vendor/rails/activesupport/lib/active_support/duration.rb b/vendor/rails/activesupport/lib/active_support/duration.rb
new file mode 100644
index 00000000..82b38e79
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/duration.rb
@@ -0,0 +1,96 @@
+module ActiveSupport
+ # Provides accurate date and time measurements using Date#advance and
+ # Time#advance, respectively. It mainly supports the methods on Numeric,
+ # such as in this example:
+ #
+ # 1.month.ago # equivalent to Time.now.advance(:months => -1)
+ class Duration < BasicObject
+ attr_accessor :value, :parts
+
+ def initialize(value, parts) #:nodoc:
+ @value, @parts = value, parts
+ end
+
+ # Adds another Duration or a Numeric to this Duration. Numeric values
+ # are treated as seconds.
+ def +(other)
+ if Duration === other
+ Duration.new(value + other.value, @parts + other.parts)
+ else
+ Duration.new(value + other, @parts + [[:seconds, other]])
+ end
+ end
+
+ # Subtracts another Duration or a Numeric from this Duration. Numeric
+ # values are treated as seconds.
+ def -(other)
+ self + (-other)
+ end
+
+ def -@ #:nodoc:
+ Duration.new(-value, parts.map { |type,number| [type, -number] })
+ end
+
+ def is_a?(klass) #:nodoc:
+ klass == Duration || super
+ end
+
+ # Returns true if other is also a Duration instance with the
+ # same value , or if other == value .
+ def ==(other)
+ if Duration === other
+ other.value == value
+ else
+ other == value
+ end
+ end
+
+ def self.===(other) #:nodoc:
+ other.is_a?(Duration) rescue super
+ end
+
+ # Calculates a new Time or Date that is as far in the future
+ # as this Duration represents.
+ def since(time = ::Time.now)
+ sum(1, time)
+ end
+ alias :from_now :since
+
+ # Calculates a new Time or Date that is as far in the past
+ # as this Duration represents.
+ def ago(time = ::Time.now)
+ sum(-1, time)
+ end
+ alias :until :ago
+
+ def inspect #:nodoc:
+ consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
+ [:years, :months, :days, :minutes, :seconds].map do |length|
+ n = consolidated[length]
+ "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
+ end.compact.to_sentence
+ end
+
+ protected
+
+ def sum(sign, time = ::Time.now) #:nodoc:
+ parts.inject(time) do |t,(type,number)|
+ if t.acts_like?(:time) || t.acts_like?(:date)
+ if type == :seconds
+ t.since(sign * number)
+ else
+ t.advance(type => sign * number)
+ end
+ else
+ raise ArgumentError, "expected a time or date, got #{time.inspect}"
+ end
+ end
+ end
+
+ private
+
+ def method_missing(method, *args, &block) #:nodoc:
+ value.send(method, *args)
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/inflections.rb b/vendor/rails/activesupport/lib/active_support/inflections.rb
index 8f3f8618..967722c2 100644
--- a/vendor/rails/activesupport/lib/active_support/inflections.rb
+++ b/vendor/rails/activesupport/lib/active_support/inflections.rb
@@ -12,7 +12,7 @@ Inflector.inflections do |inflect|
inflect.plural(/(hive)$/i, '\1s')
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
- inflect.plural(/(matr|vert|ind)ix|ex$/i, '\1ices')
+ inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
inflect.plural(/([m|l])ouse$/i, '\1ice')
inflect.plural(/^(ox)$/i, '\1en')
inflect.plural(/(quiz)$/i, '\1zes')
@@ -47,6 +47,7 @@ Inflector.inflections do |inflect|
inflect.irregular('child', 'children')
inflect.irregular('sex', 'sexes')
inflect.irregular('move', 'moves')
+ inflect.irregular('cow', 'kine')
inflect.uncountable(%w(equipment information rice money species series fish sheep))
end
diff --git a/vendor/rails/activesupport/lib/active_support/inflector.rb b/vendor/rails/activesupport/lib/active_support/inflector.rb
index 084c58d3..baa342f6 100644
--- a/vendor/rails/activesupport/lib/active_support/inflector.rb
+++ b/vendor/rails/activesupport/lib/active_support/inflector.rb
@@ -47,8 +47,15 @@ module Inflector
# irregular 'octopus', 'octopi'
# irregular 'person', 'people'
def irregular(singular, plural)
- plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
- singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
+ if singular[0,1].upcase == plural[0,1].upcase
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
+ else
+ plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
+ plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
+ singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
+ singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
+ end
end
# Add uncountable words that shouldn't be attempted inflected.
@@ -99,7 +106,7 @@ module Inflector
def pluralize(word)
result = word.to_s.dup
- if inflections.uncountables.include?(result.downcase)
+ if word.empty? || inflections.uncountables.include?(result.downcase)
result
else
inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
@@ -263,9 +270,9 @@ module Inflector
"#{number}th"
else
case number.to_i % 10
- when 1: "#{number}st"
- when 2: "#{number}nd"
- when 3: "#{number}rd"
+ when 1; "#{number}st"
+ when 2; "#{number}nd"
+ when 3; "#{number}rd"
else "#{number}th"
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/json.rb b/vendor/rails/activesupport/lib/active_support/json.rb
index d1203bd2..6c828293 100644
--- a/vendor/rails/activesupport/lib/active_support/json.rb
+++ b/vendor/rails/activesupport/lib/active_support/json.rb
@@ -1,48 +1,31 @@
-require 'active_support/json/encoders'
+require 'active_support/json/encoding'
+require 'active_support/json/decoding'
module ActiveSupport
- module JSON #:nodoc:
- class CircularReferenceError < StandardError #:nodoc:
- end
-
- # A string that returns itself as as its JSON-encoded form.
- class Variable < String #:nodoc:
- def to_json
- self
- end
- end
-
- # When +true+, Hash#to_json will omit quoting string or symbol keys
- # if the keys are valid JavaScript identifiers. Note that this is
- # technically improper JSON (all object keys must be quoted), so if
- # you need strict JSON compliance, set this option to +false+.
- mattr_accessor :unquote_hash_key_identifiers
- @@unquote_hash_key_identifiers = true
+ module JSON
+ RESERVED_WORDS = %w(
+ abstract delete goto private transient
+ boolean do if protected try
+ break double implements public typeof
+ byte else import return var
+ case enum in short void
+ catch export instanceof static volatile
+ char extends int super while
+ class final interface switch with
+ const finally long synchronized
+ continue float native this
+ debugger for new throw
+ default function package throws
+ ) #:nodoc:
class << self
- REFERENCE_STACK_VARIABLE = :json_reference_stack
-
- def encode(value)
- raise_on_circular_reference(value) do
- Encoders[value.class].call(value)
- end
+ def valid_identifier?(key) #:nodoc:
+ key.to_s =~ /^[[:alpha:]_$][[:alnum:]_$]*$/ && !reserved_word?(key)
end
-
- def can_unquote_identifier?(key)
- return false unless unquote_hash_key_identifiers
- key.to_s =~ /^[[:alpha:]_$][[:alnum:]_$]*$/
+
+ def reserved_word?(key) #:nodoc:
+ RESERVED_WORDS.include?(key.to_s)
end
-
- protected
- def raise_on_circular_reference(value)
- stack = Thread.current[REFERENCE_STACK_VARIABLE] ||= []
- raise CircularReferenceError, 'object references itself' if
- stack.include? value
- stack << value
- yield
- ensure
- stack.pop
- end
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/json/decoding.rb b/vendor/rails/activesupport/lib/active_support/json/decoding.rb
new file mode 100644
index 00000000..f24aa9e5
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/decoding.rb
@@ -0,0 +1,60 @@
+require 'yaml'
+require 'strscan'
+
+module ActiveSupport
+ module JSON
+ class ParseError < StandardError
+ end
+
+ class << self
+ # Converts a JSON string into a Ruby object.
+ def decode(json)
+ YAML.load(convert_json_to_yaml(json))
+ rescue ArgumentError => e
+ raise ParseError, "Invalid JSON string"
+ end
+
+ protected
+ # matches YAML-formatted dates
+ DATE_REGEX = /^\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?$/
+
+ # Ensure that ":" and "," are always followed by a space
+ def convert_json_to_yaml(json) #:nodoc:
+ scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, []
+ while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
+ case char = scanner[1]
+ when '"', "'"
+ if !quoting
+ quoting = char
+ pos = scanner.pos
+ elsif quoting == char
+ if json[pos..scanner.pos-2] =~ DATE_REGEX
+ # found a date, track the exact positions of the quotes so we can remove them later.
+ # oh, and increment them for each current mark, each one is an extra padded space that bumps
+ # the position in the final yaml output
+ total_marks = marks.size
+ times << pos+total_marks << scanner.pos+total_marks
+ end
+ quoting = false
+ end
+ when ":",","
+ marks << scanner.pos - 1 unless quoting
+ end
+ end
+
+ if marks.empty?
+ json.gsub(/\\\//, '/')
+ else
+ # FIXME: multiple slow enumerations
+ output = ([0] + marks.map(&:succ)).
+ zip(marks + [json.length]).
+ map { |left, right| json[left..right] }.
+ join(" ")
+ times.each { |i| output[i-1] = ' ' }
+ output.gsub!(/\\\//, '/')
+ output
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders.rb b/vendor/rails/activesupport/lib/active_support/json/encoders.rb
deleted file mode 100644
index c3e3619f..00000000
--- a/vendor/rails/activesupport/lib/active_support/json/encoders.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module ActiveSupport
- module JSON #:nodoc:
- module Encoders
- mattr_accessor :encoders
- @@encoders = {}
-
- class << self
- def define_encoder(klass, &block)
- encoders[klass] = block
- end
-
- def [](klass)
- klass.ancestors.each do |k|
- encoder = encoders[k]
- return encoder if encoder
- end
- end
- end
- end
- end
-end
-
-Dir[File.dirname(__FILE__) + '/encoders/*.rb'].each do |file|
- require file[0..-4]
-end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/core.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/core.rb
deleted file mode 100644
index df50c012..00000000
--- a/vendor/rails/activesupport/lib/active_support/json/encoders/core.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-module ActiveSupport
- module JSON #:nodoc:
- module Encoders #:nodoc:
- define_encoder Object do |object|
- object.instance_values.to_json
- end
-
- define_encoder TrueClass do
- 'true'
- end
-
- define_encoder FalseClass do
- 'false'
- end
-
- define_encoder NilClass do
- 'null'
- end
-
- ESCAPED_CHARS = {
- "\010" => '\b',
- "\f" => '\f',
- "\n" => '\n',
- "\r" => '\r',
- "\t" => '\t',
- '"' => '\"',
- '\\' => '\\\\',
- '<' => '\\074',
- '>' => '\\076'
- }
-
- define_encoder String do |string|
- '"' + string.gsub(/[\010\f\n\r\t"\\<>]/) { |s|
- ESCAPED_CHARS[s]
- }.gsub(/([\xC0-\xDF][\x80-\xBF]|
- [\xE0-\xEF][\x80-\xBF]{2}|
- [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
- s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
- } + '"'
- end
-
- define_encoder Numeric do |numeric|
- numeric.to_s
- end
-
- define_encoder Symbol do |symbol|
- symbol.to_s.to_json
- end
-
- define_encoder Enumerable do |enumerable|
- "[#{enumerable.map { |value| value.to_json } * ', '}]"
- end
-
- define_encoder Hash do |hash|
- returning result = '{' do
- result << hash.map do |key, value|
- key = ActiveSupport::JSON::Variable.new(key.to_s) if
- ActiveSupport::JSON.can_unquote_identifier?(key)
- "#{key.to_json}: #{value.to_json}"
- end * ', '
- result << '}'
- end
- end
-
- define_encoder Regexp do |regexp|
- regexp.inspect
- end
- end
- end
-end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/date.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/date.rb
new file mode 100644
index 00000000..853d9b86
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/date.rb
@@ -0,0 +1,5 @@
+class Date
+ def to_json(options = nil) #:nodoc:
+ %("#{strftime("%Y/%m/%d")}")
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/date_time.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/date_time.rb
new file mode 100644
index 00000000..f0b04344
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/date_time.rb
@@ -0,0 +1,5 @@
+class DateTime
+ def to_json(options = nil) #:nodoc:
+ %("#{strftime("%Y/%m/%d %H:%M:%S %z")}")
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/enumerable.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/enumerable.rb
new file mode 100644
index 00000000..720fd88f
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/enumerable.rb
@@ -0,0 +1,12 @@
+module Enumerable
+ # Returns a JSON string representing the enumerable. Any +options+
+ # given will be passed on to its elements. For example:
+ #
+ # users = User.find(:all)
+ # users.to_json(:only => :name)
+ #
+ # will pass the :only => :name option to each user.
+ def to_json(options = {}) #:nodoc:
+ "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ', '}]"
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/false_class.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/false_class.rb
new file mode 100644
index 00000000..bf084433
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/false_class.rb
@@ -0,0 +1,5 @@
+class FalseClass
+ def to_json(options = nil) #:nodoc:
+ 'false'
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/hash.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/hash.rb
new file mode 100644
index 00000000..774803d6
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/hash.rb
@@ -0,0 +1,50 @@
+class Hash
+ # Returns a JSON string representing the hash.
+ #
+ # Without any +options+, the returned JSON string will include all
+ # the hash keys. For example:
+ #
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
+ #
+ # {"name": "Konata Izumi", 1: 2, "age": 16}
+ #
+ # The keys in the JSON string are unordered due to the nature of hashes.
+ #
+ # The :only and :except options can be used to limit the
+ # attributes included, and will accept 1 or more hash keys to include/exclude.
+ #
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age'])
+ #
+ # {"name": "Konata Izumi", "age": 16}
+ #
+ # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1)
+ #
+ # {"name": "Konata Izumi", "age": 16}
+ #
+ # The +options+ also filter down to any hash values. This is particularly
+ # useful for converting hashes containing ActiveRecord objects or any object
+ # that responds to options in their to_json method. For example:
+ #
+ # users = User.find(:all)
+ # { :users => users, :count => users.size }.to_json(:include => :posts)
+ #
+ # would pass the :include => :posts option to users ,
+ # allowing the posts association in the User model to be converted to JSON
+ # as well.
+ def to_json(options = {}) #:nodoc:
+ hash_keys = self.keys
+
+ if options[:except]
+ hash_keys = hash_keys - Array(options[:except])
+ elsif options[:only]
+ hash_keys = hash_keys & Array(options[:only])
+ end
+
+ returning result = '{' do
+ result << hash_keys.map do |key|
+ "#{ActiveSupport::JSON.encode(key)}: #{ActiveSupport::JSON.encode(self[key], options)}"
+ end * ', '
+ result << '}'
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/nil_class.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/nil_class.rb
new file mode 100644
index 00000000..4763471a
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/nil_class.rb
@@ -0,0 +1,5 @@
+class NilClass
+ def to_json(options = nil) #:nodoc:
+ 'null'
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/numeric.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/numeric.rb
new file mode 100644
index 00000000..38713fb3
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/numeric.rb
@@ -0,0 +1,5 @@
+class Numeric
+ def to_json(options = nil) #:nodoc:
+ to_s
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/object.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/object.rb
new file mode 100644
index 00000000..6da0d1d1
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/object.rb
@@ -0,0 +1,6 @@
+class Object
+ # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
+ def to_json(options = {})
+ ActiveSupport::JSON.encode(instance_values, options)
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/regexp.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/regexp.rb
new file mode 100644
index 00000000..b6116b70
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/regexp.rb
@@ -0,0 +1,5 @@
+class Regexp
+ def to_json(options = nil) #:nodoc:
+ inspect
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/string.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/string.rb
new file mode 100644
index 00000000..28f11906
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/string.rb
@@ -0,0 +1,30 @@
+module ActiveSupport
+ module JSON
+ module Encoding
+ ESCAPED_CHARS = {
+ "\010" => '\b',
+ "\f" => '\f',
+ "\n" => '\n',
+ "\r" => '\r',
+ "\t" => '\t',
+ '"' => '\"',
+ '\\' => '\\\\',
+ '>' => '\u003E',
+ '<' => '\u003C',
+ '&' => '\u0026'
+ }
+ end
+ end
+end
+
+class String
+ def to_json(options = nil) #:nodoc:
+ '"' + gsub(/[\010\f\n\r\t"\\><&]/) { |s|
+ ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s]
+ }.gsub(/([\xC0-\xDF][\x80-\xBF]|
+ [\xE0-\xEF][\x80-\xBF]{2}|
+ [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
+ s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
+ } + '"'
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/symbol.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/symbol.rb
new file mode 100644
index 00000000..485112f9
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/symbol.rb
@@ -0,0 +1,5 @@
+class Symbol
+ def to_json(options = {}) #:nodoc:
+ ActiveSupport::JSON.encode(to_s, options)
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/time.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/time.rb
new file mode 100644
index 00000000..8eb9ff13
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/time.rb
@@ -0,0 +1,5 @@
+class Time
+ def to_json(options = nil) #:nodoc:
+ to_datetime.to_json(options)
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoders/true_class.rb b/vendor/rails/activesupport/lib/active_support/json/encoders/true_class.rb
new file mode 100644
index 00000000..037d812b
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoders/true_class.rb
@@ -0,0 +1,5 @@
+class TrueClass
+ def to_json(options = nil) #:nodoc:
+ 'true'
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/encoding.rb b/vendor/rails/activesupport/lib/active_support/json/encoding.rb
new file mode 100644
index 00000000..adfbfd5f
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/encoding.rb
@@ -0,0 +1,38 @@
+require 'active_support/json/variable'
+
+require 'active_support/json/encoders/object' # Require explicitly for rdoc.
+Dir["#{File.dirname(__FILE__)}/encoders/**/*.rb"].each do |file|
+ basename = File.basename(file, '.rb')
+ unless basename == 'object'
+ require "active_support/json/encoders/#{basename}"
+ end
+end
+
+module ActiveSupport
+ module JSON
+ class CircularReferenceError < StandardError
+ end
+
+ class << self
+ REFERENCE_STACK_VARIABLE = :json_reference_stack #:nodoc:
+
+ # Converts a Ruby object into a JSON string.
+ def encode(value, options = {})
+ raise_on_circular_reference(value) do
+ value.send(:to_json, options)
+ end
+ end
+
+ protected
+ def raise_on_circular_reference(value) #:nodoc:
+ stack = Thread.current[REFERENCE_STACK_VARIABLE] ||= []
+ raise CircularReferenceError, 'object references itself' if
+ stack.include? value
+ stack << value
+ yield
+ ensure
+ stack.pop
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/json/variable.rb b/vendor/rails/activesupport/lib/active_support/json/variable.rb
new file mode 100644
index 00000000..7fd23b0a
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/json/variable.rb
@@ -0,0 +1,10 @@
+module ActiveSupport
+ module JSON
+ # A string that returns itself as its JSON-encoded form.
+ class Variable < String
+ def to_json(options=nil)
+ self
+ end
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte.rb b/vendor/rails/activesupport/lib/active_support/multibyte.rb
index 9e17d064..27c0d180 100644
--- a/vendor/rails/activesupport/lib/active_support/multibyte.rb
+++ b/vendor/rails/activesupport/lib/active_support/multibyte.rb
@@ -1,7 +1,9 @@
-module ActiveSupport::Multibyte #:nodoc:
- DEFAULT_NORMALIZATION_FORM = :kc
- NORMALIZATIONS_FORMS = [:c, :kc, :d, :kd]
- UNICODE_VERSION = '5.0.0'
+module ActiveSupport
+ module Multibyte #:nodoc:
+ DEFAULT_NORMALIZATION_FORM = :kc
+ NORMALIZATIONS_FORMS = [:c, :kc, :d, :kd]
+ UNICODE_VERSION = '5.0.0'
+ end
end
-require 'active_support/multibyte/chars'
\ No newline at end of file
+require 'active_support/multibyte/chars'
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte/chars.rb b/vendor/rails/activesupport/lib/active_support/multibyte/chars.rb
index 90181509..b30214d9 100644
--- a/vendor/rails/activesupport/lib/active_support/multibyte/chars.rb
+++ b/vendor/rails/activesupport/lib/active_support/multibyte/chars.rb
@@ -41,6 +41,12 @@ module ActiveSupport::Multibyte #:nodoc:
@string
end
+ # Make duck-typing with String possible
+ def respond_to?(method)
+ super || @string.respond_to?(method) || handler.respond_to?(method) ||
+ (method.to_s =~ /(.*)!/ && handler.respond_to?($1)) || false
+ end
+
# Create a new Chars instance.
def initialize(str)
@string = str.respond_to?(:string) ? str.string : str
@@ -113,8 +119,14 @@ module ActiveSupport::Multibyte #:nodoc:
# +utf8_pragma+ checks if it can send this string to the handlers. It makes sure @string isn't nil and $KCODE is
# set to 'UTF8'.
- def utf8_pragma?
- !@string.nil? && ($KCODE == 'UTF8')
+ if RUBY_VERSION < '1.9'
+ def utf8_pragma?
+ !@string.nil? && ($KCODE == 'UTF8')
+ end
+ else
+ def utf8_pragma?
+ !@string.nil? && (Encoding.default_external == Encoding::UTF_8)
+ end
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb b/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb
index d79e69f0..66fe47a6 100644
--- a/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb
+++ b/vendor/rails/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb
@@ -140,6 +140,83 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
bidx ? (u_unpack(str.slice(0...bidx)).size) : nil
end
+ # Works just like the indexed replace method on string, except instead of byte offsets you specify
+ # character offsets.
+ #
+ # Example:
+ #
+ # s = "Müller"
+ # s.chars[2] = "e" # Replace character with offset 2
+ # s
+ # #=> "Müeler"
+ #
+ # s = "Müller"
+ # s.chars[1, 2] = "ö" # Replace 2 characters at character offset 1
+ # s
+ # #=> "Möler"
+ def []=(str, *args)
+ replace_by = args.pop
+ # Indexed replace with regular expressions already works
+ return str[*args] = replace_by if args.first.is_a?(Regexp)
+ result = u_unpack(str)
+ if args[0].is_a?(Fixnum)
+ raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
+ min = args[0]
+ max = args[1].nil? ? min : (min + args[1] - 1)
+ range = Range.new(min, max)
+ replace_by = [replace_by].pack('U') if replace_by.is_a?(Fixnum)
+ elsif args.first.is_a?(Range)
+ raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
+ range = args[0]
+ else
+ needle = args[0].to_s
+ min = index(str, needle)
+ max = min + length(needle) - 1
+ range = Range.new(min, max)
+ end
+ result[range] = u_unpack(replace_by)
+ str.replace(result.pack('U*'))
+ end
+
+ # Works just like String#rjust, only integer specifies characters instead of bytes.
+ #
+ # Example:
+ #
+ # "¾ cup".chars.rjust(8).to_s
+ # #=> " ¾ cup"
+ #
+ # "¾ cup".chars.rjust(8, " ").to_s # Use non-breaking whitespace
+ # #=> "   ¾ cup"
+ def rjust(str, integer, padstr=' ')
+ justify(str, integer, :right, padstr)
+ end
+
+ # Works just like String#ljust, only integer specifies characters instead of bytes.
+ #
+ # Example:
+ #
+ # "¾ cup".chars.rjust(8).to_s
+ # #=> "¾ cup "
+ #
+ # "¾ cup".chars.rjust(8, " ").to_s # Use non-breaking whitespace
+ # #=> "¾ cup   "
+ def ljust(str, integer, padstr=' ')
+ justify(str, integer, :left, padstr)
+ end
+
+ # Works just like String#center, only integer specifies characters instead of bytes.
+ #
+ # Example:
+ #
+ # "¾ cup".chars.center(8).to_s
+ # #=> " ¾ cup "
+ #
+ # "¾ cup".chars.center(8, " ").to_s # Use non-breaking whitespace
+ # #=> " ¾ cup  "
+ def center(str, integer, padstr=' ')
+ justify(str, integer, :center, padstr)
+ end
+
# Does Unicode-aware rstrip
def rstrip(str)
str.gsub(UNICODE_TRAILERS_PAT, '')
@@ -169,11 +246,17 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
# Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
# character.
def slice(str, *args)
- if (args.size == 2 && args.first.is_a?(Range))
- raise TypeError, 'cannot convert Range into Integer' # Do as if we were native
+ if args.size > 2
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
+ elsif (args.size == 2 && !(args.first.is_a?(Numeric) || args.first.is_a?(Regexp)))
+ raise TypeError, "cannot convert #{args.first.class} into Integer" # Do as if we were native
+ elsif (args.size == 2 && !args[1].is_a?(Numeric))
+ raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native
elsif args[0].kind_of? Range
cps = u_unpack(str).slice(*args)
cps.nil? ? nil : cps.pack('U*')
+ elsif args[0].kind_of? Regexp
+ str.slice(*args)
elsif args.size == 1 && args[0].kind_of?(Numeric)
u_unpack(str)[args[0]]
else
@@ -200,8 +283,8 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
# Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
# passing strings to databases and validations.
#
- # * str : The string to perform normalization on.
- # * form : The form you want to normalize in. Should be one of the following: :c, :kc, :d or :kd.
+ # * str - The string to perform normalization on.
+ # * form - The form you want to normalize in. Should be one of the following: :c, :kc, :d or :kd.
def normalize(str, form=ActiveSupport::Multibyte::DEFAULT_NORMALIZATION_FORM)
# See http://www.unicode.org/reports/tr15, Table 1
codepoints = u_unpack(str)
@@ -235,8 +318,8 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
# Used to translate an offset from bytes to characters, for instance one received from a regular expression match
def translate_offset(str, byte_offset)
- return 0 if str == ''
return nil if byte_offset.nil?
+ return 0 if str == ''
chunk = str[0..byte_offset]
begin
begin
@@ -338,6 +421,33 @@ module ActiveSupport::Multibyte::Handlers #:nodoc:
unpacked.flatten
end
+ # Justifies a string in a certain way. Valid values for way are :right , :left and
+ # :center . Is primarily used as a helper method by rjust , ljust and center .
+ def justify(str, integer, way, padstr=' ')
+ raise ArgumentError, "zero width padding" if padstr.length == 0
+ padsize = integer - size(str)
+ padsize = padsize > 0 ? padsize : 0
+ case way
+ when :right
+ str.dup.insert(0, padding(padsize, padstr))
+ when :left
+ str.dup.insert(-1, padding(padsize, padstr))
+ when :center
+ lpad = padding((padsize / 2.0).floor, padstr)
+ rpad = padding((padsize / 2.0).ceil, padstr)
+ str.dup.insert(0, lpad).insert(-1, rpad)
+ end
+ end
+
+ # Generates a padding string of a certain size.
+ def padding(padsize, padstr=' ')
+ if padsize != 0
+ slice(padstr * ((padsize / size(padstr)) + 1), 0, padsize)
+ else
+ ''
+ end
+ end
+
# Convert characters to a different case
def to_case(way, str)
u_unpack(str).map do |codepoint|
diff --git a/vendor/rails/activesupport/lib/active_support/option_merger.rb b/vendor/rails/activesupport/lib/active_support/option_merger.rb
index e266d156..1a4ff9db 100644
--- a/vendor/rails/activesupport/lib/active_support/option_merger.rb
+++ b/vendor/rails/activesupport/lib/active_support/option_merger.rb
@@ -1,25 +1,25 @@
module ActiveSupport
class OptionMerger #:nodoc:
- instance_methods.each do |method|
- undef_method(method) if method !~ /^(__|instance_eval|class)/
+ instance_methods.each do |method|
+ undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
end
-
+
def initialize(context, options)
@context, @options = context, options
end
-
+
private
def method_missing(method, *arguments, &block)
merge_argument_options! arguments
- @context.send(method, *arguments, &block)
+ @context.send!(method, *arguments, &block)
end
-
+
def merge_argument_options!(arguments)
arguments << if arguments.last.respond_to? :to_hash
@options.merge(arguments.pop)
else
@options.dup
- end
+ end
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/ordered_options.rb b/vendor/rails/activesupport/lib/active_support/ordered_options.rb
index 78d699cd..1542a247 100644
--- a/vendor/rails/activesupport/lib/active_support/ordered_options.rb
+++ b/vendor/rails/activesupport/lib/active_support/ordered_options.rb
@@ -1,26 +1,31 @@
# OrderedHash is namespaced to prevent conflicts with other implementations
module ActiveSupport
- class OrderedHash < Array #:nodoc:
- def []=(key, value)
- if pair = assoc(key)
- pair.pop
- pair << value
- else
- self << [key, value]
+ # Hash is ordered in Ruby 1.9!
+ if RUBY_VERSION >= '1.9'
+ OrderedHash = ::Hash
+ else
+ class OrderedHash < Array #:nodoc:
+ def []=(key, value)
+ if pair = assoc(key)
+ pair.pop
+ pair << value
+ else
+ self << [key, value]
+ end
end
- end
- def [](key)
- pair = assoc(key)
- pair ? pair.last : nil
- end
+ def [](key)
+ pair = assoc(key)
+ pair ? pair.last : nil
+ end
- def keys
- collect { |key, value| key }
- end
+ def keys
+ collect { |key, value| key }
+ end
- def values
- collect { |key, value| value }
+ def values
+ collect { |key, value| value }
+ end
end
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/reloadable.rb b/vendor/rails/activesupport/lib/active_support/reloadable.rb
deleted file mode 100644
index 084bda1e..00000000
--- a/vendor/rails/activesupport/lib/active_support/reloadable.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-require 'active_support/deprecation'
-
-# A deprecated mechanism to mark a class reloadable.
-#
-# Deprecated as of Rails 1.2.
-# All autoloaded objects are now unloaded.
-module Reloadable #:nodoc:
- class << self
- def included(base) #nodoc:
- unless base.ancestors.include?(Reloadable::Subclasses) # Avoid double warning
- ActiveSupport::Deprecation.warn "Reloadable has been deprecated and has no effect.", caller
- end
-
- raise TypeError, "Only Classes can be Reloadable!" unless base.is_a? Class
-
- unless base.respond_to?(:reloadable?)
- class << base
- define_method(:reloadable?) do
- ActiveSupport::Deprecation.warn "Reloadable has been deprecated and reloadable? has no effect", caller
- true
- end
- end
- end
- end
-
- def reloadable_classes
- ActiveSupport::Deprecation.silence do
- included_in_classes.select { |klass| klass.reloadable? }
- end
- end
- deprecate :reloadable_classes
- end
-
- # Captures the common pattern where a base class should not be reloaded,
- # but its subclasses should be.
- #
- # Deprecated as of Rails 1.2.
- # All autoloaded objects are now unloaded.
- module Subclasses #:nodoc:
- def self.included(base) #nodoc:
- base.send :include, Reloadable
- ActiveSupport::Deprecation.warn "Reloadable::Subclasses has been deprecated and has no effect.", caller
- (class << base; self; end).send(:define_method, :reloadable?) do
- ActiveSupport::Deprecation.warn "Reloadable has been deprecated and reloadable? has no effect", caller
- base != self
- end
- end
- end
-
- module Deprecated #:nodoc:
- def self.included(base)
- class << base
- define_method(:reloadable?) do
- ActiveSupport::Deprecation.warn "Reloadable has been deprecated and reloadable? has no effect", caller
- true # This might not have the desired effect, as AR::B.reloadable? => true.
- end
- end
- end
- end
-end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/test_case.rb b/vendor/rails/activesupport/lib/active_support/test_case.rb
new file mode 100644
index 00000000..be8f8b17
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/test_case.rb
@@ -0,0 +1,5 @@
+module ActiveSupport
+ class TestCase < Test::Unit::TestCase
+ include ActiveSupport::Testing::Default
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/testing.rb b/vendor/rails/activesupport/lib/active_support/testing.rb
new file mode 100644
index 00000000..1bf30cbb
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/testing.rb
@@ -0,0 +1 @@
+require 'active_support/testing/default'
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/testing/default.rb b/vendor/rails/activesupport/lib/active_support/testing/default.rb
new file mode 100644
index 00000000..d97a610c
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/testing/default.rb
@@ -0,0 +1,12 @@
+module ActiveSupport
+ module Testing
+ module Default
+ def run(*args)
+ #method_name appears to be a symbol on 1.8.4 and a string on 1.8.6
+ return if @method_name.to_s == "default_test"
+ super
+ end
+ end
+ end
+end
+
diff --git a/vendor/rails/activesupport/lib/active_support/values/time_zone.rb b/vendor/rails/activesupport/lib/active_support/values/time_zone.rb
index c67af609..b7df68fe 100644
--- a/vendor/rails/activesupport/lib/active_support/values/time_zone.rb
+++ b/vendor/rails/activesupport/lib/active_support/values/time_zone.rb
@@ -45,7 +45,7 @@ class TimeZone
# Adjust the given time to the time zone represented by +self+.
def adjust(time)
- time = time.to_time
+ time = time.to_time unless time.is_a?(::Time)
time + utc_offset - time.utc_offset
end
@@ -53,8 +53,9 @@ class TimeZone
# zone, and then adjusts it to return the corresponding time in the
# local time zone.
def unadjust(time)
- time = Time.local(*time.to_time.to_a)
- time - utc_offset + time.utc_offset
+ time = time.to_time unless time.is_a?(::Time)
+ time = time.localtime
+ time - utc_offset - time.utc_offset
end
# Compare this time zone to the parameter. The two are comapred first on
@@ -67,7 +68,7 @@ class TimeZone
# Returns a textual representation of this time zone.
def to_s
- "(GMT#{formatted_offset}) #{name}"
+ "(UTC#{formatted_offset}) #{name}"
end
@@zones = nil
@@ -76,7 +77,7 @@ class TimeZone
# Create a new TimeZone instance with the given name and offset.
def create(name, offset)
zone = allocate
- zone.send :initialize, name, offset
+ zone.send!(:initialize, name, offset)
zone
end
@@ -98,8 +99,8 @@ class TimeZone
[-36_000, "Hawaii" ],
[-32_400, "Alaska" ],
[-28_800, "Pacific Time (US & Canada)", "Tijuana" ],
- [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "La Paz",
- "Mazatlan", "Arizona" ],
+ [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "Mazatlan",
+ "Arizona" ],
[-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara",
"Mexico City", "Monterrey", "Central America" ],
[-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota",
diff --git a/vendor/rails/activesupport/lib/active_support/vendor.rb b/vendor/rails/activesupport/lib/active_support/vendor.rb
new file mode 100644
index 00000000..75c18062
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/vendor.rb
@@ -0,0 +1,14 @@
+# Prefer gems to the bundled libs.
+require 'rubygems'
+
+begin
+ gem 'builder', '~> 2.1.2'
+rescue Gem::LoadError
+ $:.unshift "#{File.dirname(__FILE__)}/vendor/builder-2.1.2"
+end
+
+begin
+ gem 'xml-simple', '~> 1.0.11'
+rescue Gem::LoadError
+ $:.unshift "#{File.dirname(__FILE__)}/vendor/xml-simple-1.0.11"
+end
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/blankslate.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/blankslate.rb
new file mode 100644
index 00000000..da6034d9
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/blankslate.rb
@@ -0,0 +1,113 @@
+#!/usr/bin/env ruby
+#--
+# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#++
+
+######################################################################
+# BlankSlate provides an abstract base class with no predefined
+# methods (except for \_\_send__ and \_\_id__ ).
+# BlankSlate is useful as a base class when writing classes that
+# depend upon method_missing (e.g. dynamic proxies).
+#
+class BlankSlate
+ class << self
+
+ # Hide the method named +name+ in the BlankSlate class. Don't
+ # hide +instance_eval+ or any method beginning with "__".
+ def hide(name)
+ if instance_methods.include?(name.to_s) and
+ name !~ /^(__|instance_eval)/
+ @hidden_methods ||= {}
+ @hidden_methods[name.to_sym] = instance_method(name)
+ undef_method name
+ end
+ end
+
+ def find_hidden_method(name)
+ @hidden_methods ||= {}
+ @hidden_methods[name] || superclass.find_hidden_method(name)
+ end
+
+ # Redefine a previously hidden method so that it may be called on a blank
+ # slate object.
+ def reveal(name)
+ bound_method = nil
+ unbound_method = find_hidden_method(name)
+ fail "Don't know how to reveal method '#{name}'" unless unbound_method
+ define_method(name) do |*args|
+ bound_method ||= unbound_method.bind(self)
+ bound_method.call(*args)
+ end
+ end
+ end
+
+ instance_methods.each { |m| hide(m) }
+end
+
+######################################################################
+# Since Ruby is very dynamic, methods added to the ancestors of
+# BlankSlate after BlankSlate is defined will show up in the
+# list of available BlankSlate methods. We handle this by defining a
+# hook in the Object and Kernel classes that will hide any method
+# defined after BlankSlate has been loaded.
+#
+module Kernel
+ class << self
+ alias_method :blank_slate_method_added, :method_added
+
+ # Detect method additions to Kernel and remove them in the
+ # BlankSlate class.
+ def method_added(name)
+ result = blank_slate_method_added(name)
+ return result if self != Kernel
+ BlankSlate.hide(name)
+ result
+ end
+ end
+end
+
+######################################################################
+# Same as above, except in Object.
+#
+class Object
+ class << self
+ alias_method :blank_slate_method_added, :method_added
+
+ # Detect method additions to Object and remove them in the
+ # BlankSlate class.
+ def method_added(name)
+ result = blank_slate_method_added(name)
+ return result if self != Object
+ BlankSlate.hide(name)
+ result
+ end
+
+ def find_hidden_method(name)
+ nil
+ end
+ end
+end
+
+######################################################################
+# Also, modules included into Object need to be scanned and have their
+# instance methods removed from blank slate. In theory, modules
+# included into Kernel would have to be removed as well, but a
+# "feature" of Ruby prevents late includes into modules from being
+# exposed in the first place.
+#
+class Module
+ alias blankslate_original_append_features append_features
+ def append_features(mod)
+ result = blankslate_original_append_features(mod)
+ return result if mod != Object
+ instance_methods.each do |name|
+ BlankSlate.hide(name)
+ end
+ result
+ end
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder.rb
similarity index 100%
rename from vendor/rails/activesupport/lib/active_support/vendor/builder.rb
rename to vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder.rb
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb
new file mode 100644
index 00000000..2935b6f1
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb
@@ -0,0 +1,20 @@
+#!/usr/bin/env ruby
+#--
+# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
+# All rights reserved.
+
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#++
+
+require 'blankslate'
+
+######################################################################
+# BlankSlate has been promoted to a top level name and is now
+# available as a standalone gem. We make the name available in the
+# Builder namespace for compatibility.
+#
+module Builder
+ BlankSlate = ::BlankSlate
+end
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/css.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/css.rb
new file mode 100644
index 00000000..e086a1b1
--- /dev/null
+++ b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/css.rb
@@ -0,0 +1,250 @@
+#!/usr/bin/env ruby
+#--
+# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
+# Copyright 2005 by Scott Barron (scott@elitists.net).
+# All rights reserved.
+#
+# Permission is granted for use, copying, modification, distribution,
+# and distribution of modified versions of this work as long as the
+# above copyright notice is included.
+#
+# Much of this is taken from Jim's work in xmlbase.rb and xmlmarkup.rb.
+# Documentation has also been copied and pasted and modified to reflect
+# that we're building CSS here instead of XML. Jim is conducting the
+# orchestra here and I'm just off in the corner playing a flute.
+#++
+
+# Provide a flexible and easy to use Builder for creating Cascading
+# Style Sheets (CSS).
+
+
+require 'builder/blankslate'
+
+module Builder
+
+ # Create a Cascading Style Sheet (CSS) using Ruby.
+ #
+ # Example usage:
+ #
+ # css = Builder::CSS.new
+ #
+ # text_color = '#7F7F7F'
+ # preferred_fonts = 'Helvetica, Arial, sans_serif'
+ #
+ # css.comment! 'This is our stylesheet'
+ # css.body {
+ # background_color '#FAFAFA'
+ # font_size 'small'
+ # font_family preferred_fonts
+ # color text_color
+ # }
+ #
+ # css.id!('navbar') {
+ # width '500px'
+ # }
+ #
+ # css.class!('navitem') {
+ # color 'red'
+ # }
+ #
+ # css.a :hover {
+ # text_decoration 'underline'
+ # }
+ #
+ # css.div(:id => 'menu') {
+ # background 'green'
+ # }
+ #
+ # css.div(:class => 'foo') {
+ # background 'red'
+ # }
+ #
+ # This will yield the following stylesheet:
+ #
+ # /* This is our stylesheet */
+ # body {
+ # background_color: #FAFAFA;
+ # font_size: small;
+ # font_family: Helvetica, Arial, sans_serif;
+ # color: #7F7F7F;
+ # }
+ #
+ # #navbar {
+ # width: 500px;
+ # }
+ #
+ # .navitem {
+ # color: red;
+ # }
+ #
+ # a:hover {
+ # text_decoration: underline;
+ # }
+ #
+ # div#menu {
+ # background: green;
+ # }
+ #
+ # div.foo {
+ # background: red;
+ # }
+ #
+ class CSS < BlankSlate
+
+ # Create a CSS builder.
+ #
+ # out:: Object receiving the markup.1 +out+ must respond to
+ # << .
+ # indent:: Number of spaces used for indentation (0 implies no
+ # indentation and no line breaks).
+ #
+ def initialize(indent=2)
+ @indent = indent
+ @target = []
+ @parts = []
+ @library = {}
+ end
+
+ def +(part)
+ _join_with_op! '+'
+ self
+ end
+
+ def >>(part)
+ _join_with_op! ''
+ self
+ end
+
+ def >(part)
+ _join_with_op! '>'
+ self
+ end
+
+ def |(part)
+ _join_with_op! ','
+ self
+ end
+
+ # Return the target of the builder
+ def target!
+ @target * ''
+ end
+
+ # Create a comment string in the output.
+ def comment!(comment_text)
+ @target << "/* #{comment_text} */\n"
+ end
+
+ def id!(arg, &block)
+ _start_container('#'+arg.to_s, nil, block_given?)
+ _css_block(block) if block
+ _unify_block
+ self
+ end
+
+ def class!(arg, &block)
+ _start_container('.'+arg.to_s, nil, block_given?)
+ _css_block(block) if block
+ _unify_block
+ self
+ end
+
+ def store!(sym, &block)
+ @library[sym] = block.to_proc
+ end
+
+ def group!(*args, &block)
+ args.each do |arg|
+ if arg.is_a?(Symbol)
+ instance_eval(&@library[arg])
+ else
+ instance_eval(&arg)
+ end
+ _text ', ' unless arg == args.last
+ end
+ if block
+ _css_block(block)
+ _unify_block
+ end
+ end
+
+ def method_missing(sym, *args, &block)
+ sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
+ if block
+ _start_container(sym, args.first)
+ _css_block(block)
+ _unify_block
+ elsif @in_block
+ _indent
+ _css_line(sym, *args)
+ _newline
+ return self
+ else
+ _start_container(sym, args.first, false)
+ _unify_block
+ end
+ self
+ end
+
+ # "Cargo culted" from Jim who also "cargo culted" it. See xmlbase.rb.
+ def nil?
+ false
+ end
+
+ private
+ def _unify_block
+ @target << @parts * ''
+ @parts = []
+ end
+
+ def _join_with_op!(op)
+ rhs, lhs = @target.pop, @target.pop
+ @target << "#{lhs} #{op} #{rhs}"
+ end
+
+ def _text(text)
+ @parts << text
+ end
+
+ def _css_block(block)
+ _newline
+ _nested_structures(block)
+ _end_container
+ _end_block
+ end
+
+ def _end_block
+ _newline
+ _newline
+ end
+
+ def _newline
+ _text "\n"
+ end
+
+ def _indent
+ _text ' ' * @indent
+ end
+
+ def _nested_structures(block)
+ @in_block = true
+ self.instance_eval(&block)
+ @in_block = false
+ end
+
+ def _start_container(sym, atts = {}, with_bracket = true)
+ selector = sym.to_s
+ selector << ".#{atts[:class]}" if atts && atts[:class]
+ selector << '#' + "#{atts[:id]}" if atts && atts[:id]
+ @parts << "#{selector}#{with_bracket ? ' {' : ''}"
+ end
+
+ def _end_container
+ @parts << "}"
+ end
+
+ def _css_line(sym, *args)
+ _text("#{sym.to_s.gsub('_','-')}: #{args * ' '};")
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder/xchar.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb
similarity index 90%
rename from vendor/rails/activesupport/lib/active_support/vendor/builder/xchar.rb
rename to vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb
index f2bfe17a..8bdbd058 100644
--- a/vendor/rails/activesupport/lib/active_support/vendor/builder/xchar.rb
+++ b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xchar.rb
@@ -10,7 +10,7 @@
module Builder
def self.check_for_name_collision(klass, method_name, defined_constant=nil)
- if klass.instance_methods.include?(method_name)
+ if klass.instance_methods.include?(method_name.to_s)
fail RuntimeError,
"Name Collision: Method '#{method_name}' is already defined in #{klass}"
end
@@ -27,7 +27,7 @@ module Builder
####################################################################
# XML Character converter, from Sam Ruby:
- # (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
+ # (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
#
module XChar # :nodoc:
@@ -73,8 +73,8 @@ module Builder
# See http://www.w3.org/TR/REC-xml/#charsets for details.
VALID = [
- [0x9, 0xA, 0xD],
- (0x20..0xD7FF),
+ 0x9, 0xA, 0xD,
+ (0x20..0xD7FF),
(0xE000..0xFFFD),
(0x10000..0x10FFFF)
]
@@ -86,14 +86,17 @@ end
######################################################################
# Enhance the Fixnum class with a XML escaped character conversion.
#
-class Fixnum #:nodoc:
+class Fixnum
XChar = Builder::XChar if ! defined?(XChar)
# XML escaped version of chr
def xchr
n = XChar::CP1252[self] || self
- n = 42 unless XChar::VALID.find {|range| range.include? n}
- XChar::PREDEFINED[n] or (n<128 ? n.chr : "#{n};")
+ case n when *XChar::VALID
+ XChar::PREDEFINED[n] or (n<128 ? n.chr : "#{n};")
+ else
+ '*'
+ end
end
end
@@ -102,7 +105,7 @@ end
# Enhance the String class with a XML escaped character version of
# to_s.
#
-class String #:nodoc:
+class String
# XML escaped version of to_s
def to_xs
unpack('U*').map {|n| n.xchr}.join # ASCII, UTF-8
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder/xmlbase.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb
similarity index 76%
rename from vendor/rails/activesupport/lib/active_support/vendor/builder/xmlbase.rb
rename to vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb
index b0485636..ace4b56d 100644
--- a/vendor/rails/activesupport/lib/active_support/vendor/builder/xmlbase.rb
+++ b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlbase.rb
@@ -5,12 +5,11 @@ require 'builder/blankslate'
module Builder
# Generic error for builder
- class IllegalBlockError < RuntimeError #:nodoc:
- end
+ class IllegalBlockError < RuntimeError; end
# XmlBase is a base class for building XML builders. See
# Builder::XmlMarkup and Builder::XmlEvents for examples.
- class XmlBase < BlankSlate #:nodoc:
+ class XmlBase < BlankSlate
# Create an XML markup builder.
#
@@ -24,12 +23,12 @@ module Builder
@indent = indent
@level = initial
end
-
+
# Create a tag named +sym+. Other than the first argument which
- # is the tag name, the arguements are the same as the tags
+ # is the tag name, the arguments are the same as the tags
# implemented via method_missing .
def tag!(sym, *args, &block)
- self.__send__(sym, *args, &block)
+ method_missing(sym.to_sym, *args, &block)
end
# Create XML markup based on the name of the method. This method
@@ -40,51 +39,50 @@ module Builder
attrs = nil
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
args.each do |arg|
- case arg
- when Hash
- attrs ||= {}
- attrs.merge!(arg)
- else
- text ||= ''
- text << arg.to_s
- end
+ case arg
+ when Hash
+ attrs ||= {}
+ attrs.merge!(arg)
+ else
+ text ||= ''
+ text << arg.to_s
+ end
end
if block
- unless text.nil?
- raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
- end
- _capture_outer_self(block) unless defined?(@self) && !@self.nil?
- _indent
- _start_tag(sym, attrs)
- _newline
- _nested_structures(block)
- _indent
- _end_tag(sym)
- _newline
+ unless text.nil?
+ raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
+ end
+ _indent
+ _start_tag(sym, attrs)
+ _newline
+ _nested_structures(block)
+ _indent
+ _end_tag(sym)
+ _newline
elsif text.nil?
- _indent
- _start_tag(sym, attrs, true)
- _newline
+ _indent
+ _start_tag(sym, attrs, true)
+ _newline
else
- _indent
- _start_tag(sym, attrs)
- text! text
- _end_tag(sym)
- _newline
+ _indent
+ _start_tag(sym, attrs)
+ text! text
+ _end_tag(sym)
+ _newline
end
@target
end
# Append text to the output target. Escape any markup. May be
- # used within the markup brakets as:
+ # used within the markup brackets as:
#
# builder.p { |b| b.br; b.text! "HI" } #=> HI
def text!(text)
_text(_escape(text))
end
-
+
# Append text to the output target without escaping any markup.
- # May be used within the markup brakets as:
+ # May be used within the markup brackets as:
#
# builder.p { |x| x << " HI" } #=> HI
#
@@ -99,7 +97,7 @@ module Builder
def <<(text)
_text(text)
end
-
+
# For some reason, nil? is sent to the XmlMarkup object. If nil?
# is not defined and method_missing is invoked, some strange kind
# of recursion happens. Since nil? won't ever be an XML tag, it
@@ -111,7 +109,7 @@ module Builder
end
private
-
+
require 'builder/xchar'
def _escape(text)
text.to_xs
@@ -121,20 +119,16 @@ module Builder
_escape(text).gsub(%r{"}, '"') # " WART
end
- def _capture_outer_self(block)
- @self = eval("self", block)
- end
-
def _newline
return if @indent == 0
text! "\n"
end
-
+
def _indent
return if @indent == 0 || @level == 0
text!(" " * (@level * @indent))
end
-
+
def _nested_structures(block)
@level += 1
block.call(self)
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder/xmlevents.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
similarity index 97%
rename from vendor/rails/activesupport/lib/active_support/vendor/builder/xmlevents.rb
rename to vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
index 15dc7b64..91fcd21e 100644
--- a/vendor/rails/activesupport/lib/active_support/vendor/builder/xmlevents.rb
+++ b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
@@ -45,7 +45,7 @@ module Builder
# +text+ call, so the client cannot assume that a single
# callback contains all the text data.
#
- class XmlEvents < XmlMarkup #:nodoc:
+ class XmlEvents < XmlMarkup
def text!(text)
@target.text(text)
end
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder/xmlmarkup.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb
similarity index 91%
rename from vendor/rails/activesupport/lib/active_support/vendor/builder/xmlmarkup.rb
rename to vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb
index 4cdcedb4..ec59dddc 100644
--- a/vendor/rails/activesupport/lib/active_support/vendor/builder/xmlmarkup.rb
+++ b/vendor/rails/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlmarkup.rb
@@ -38,6 +38,7 @@ module Builder
# xm.title("History") # History
# } #
# xm.body { #
+ # xm.comment! "HI" #
# xm.h1("Header") # Header
# xm.p("paragraph") # paragraph
# } #
@@ -46,7 +47,7 @@ module Builder
# == Notes:
#
# * The order that attributes are inserted in markup tags is
- # undefined.
+ # undefined.
#
# * Sometimes you wish to insert text without enclosing tags. Use
# the text! method to accomplish this.
@@ -77,7 +78,7 @@ module Builder
# tag! will also take text and attribute arguments (after
# the tag name) like normal markup methods. (But see the next
# bullet item for a better way to handle XML namespaces).
- #
+ #
# * Direct support for XML namespaces is now available. If the
# first argument to a tag call is a symbol, it will be joined to
# the tag to produce a namespace:tag combination. It is easier to
@@ -92,7 +93,7 @@ module Builder
# * XmlMarkup builds the markup in any object (called a _target_)
# that accepts the << method. If no target is given,
# then XmlMarkup defaults to a string target.
- #
+ #
# Examples:
#
# xm = Builder::XmlMarkup.new
@@ -109,16 +110,16 @@ module Builder
# xm = Builder::XmlMarkup.new
# x2 = Builder::XmlMarkup.new(:target=>xm)
# # Markup written to +x2+ will be send to +xm+.
- #
+ #
# * Indentation is enabled by providing the number of spaces to
# indent for each level as a second argument to XmlBuilder.new.
# Initial indentation may be specified using a third parameter.
#
# Example:
#
- # xm = Builder.new(:ident=>2)
+ # xm = Builder.new(:indent=>2)
# # xm will produce nicely formatted and indented XML.
- #
+ #
# xm = Builder.new(:indent=>2, :margin=>4)
# # xm will produce nicely formatted and indented XML with 2
# # spaces per indent and an over all indentation level of 4.
@@ -164,15 +165,15 @@ module Builder
# :target=>target_object ::
# Object receiving the markup. +out+ must respond to the
# << operator. The default is a plain string target.
- #
+ #
# :indent=>indentation ::
# Number of spaces used for indentation. The default is no
# indentation and no line breaks.
- #
+ #
# :margin=>initial_indentation_level ::
# Amount of initial indentation (specified in levels, not
# spaces).
- #
+ #
# :escape_attrs=>OBSOLETE::
# The :escape_attrs option is no longer supported by builder
# (and will be quietly ignored). String attribute values are
@@ -180,14 +181,14 @@ module Builder
# values (perhaps you are using entities in the attribute
# values), then give the value as a Symbol. This allows much
# finer control over escaping attribute values.
- #
+ #
def initialize(options={})
indent = options[:indent] || 0
margin = options[:margin] || 0
super(indent, margin)
@target = options[:target] || ""
end
-
+
# Return the target of the builder.
def target!
@target
@@ -208,18 +209,18 @@ module Builder
_indent
@target << ""
_newline
@@ -237,15 +238,15 @@ module Builder
def instruct!(directive_tag=:xml, attrs={})
_ensure_no_block block_given?
if directive_tag == :xml
- a = { :version=>"1.0", :encoding=>"UTF-8" }
- attrs = a.merge attrs
+ a = { :version=>"1.0", :encoding=>"UTF-8" }
+ attrs = a.merge attrs
end
_special(
- "#{directive_tag}",
- "?>",
- nil,
- attrs,
- [:version, :encoding, :standalone])
+ "#{directive_tag}",
+ "?>",
+ nil,
+ attrs,
+ [:version, :encoding, :standalone])
end
# Insert a CDATA section into the XML markup.
@@ -259,7 +260,7 @@ module Builder
_ensure_no_block block_given?
_special("", text, nil)
end
-
+
private
# NOTE: All private methods of a builder object are prefixed when
@@ -269,8 +270,8 @@ module Builder
def _text(text)
@target << text
end
-
- # Insert special instruction.
+
+ # Insert special instruction.
def _special(open, close, data=nil, attrs=nil, order=[])
_indent
@target << open
@@ -288,7 +289,7 @@ module Builder
@target << "/" if end_too
@target << ">"
end
-
+
# Insert an ending tag.
def _end_tag(sym)
@target << "#{sym}>"
@@ -298,27 +299,27 @@ module Builder
def _insert_attributes(attrs, order=[])
return if attrs.nil?
order.each do |k|
- v = attrs[k]
- @target << %{ #{k}="#{_attr_value(v)}"} if v
+ v = attrs[k]
+ @target << %{ #{k}="#{_attr_value(v)}"} if v # " WART
end
attrs.each do |k, v|
- @target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k)
+ @target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k) # " WART
end
end
def _attr_value(value)
case value
when Symbol
- value.to_s
+ value.to_s
else
- _escape_quote(value.to_s)
+ _escape_quote(value.to_s)
end
end
def _ensure_no_block(got_block)
if got_block
- fail IllegalBlockError,
- "Blocks are not allowed on XML instructions"
+ fail IllegalBlockError,
+ "Blocks are not allowed on XML instructions"
end
end
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/builder/blankslate.rb b/vendor/rails/activesupport/lib/active_support/vendor/builder/blankslate.rb
deleted file mode 100644
index 23b95170..00000000
--- a/vendor/rails/activesupport/lib/active_support/vendor/builder/blankslate.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env ruby
-#--
-# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
-# All rights reserved.
-
-# Permission is granted for use, copying, modification, distribution,
-# and distribution of modified versions of this work as long as the
-# above copyright notice is included.
-#++
-
-module Builder #:nodoc:
-
- # BlankSlate provides an abstract base class with no predefined
- # methods (except for \_\_send__ and \_\_id__ ).
- # BlankSlate is useful as a base class when writing classes that
- # depend upon method_missing (e.g. dynamic proxies).
- class BlankSlate
- class << self
-
- # Hide the method named +name+ in the BlankSlate class. Don't
- # hide +instance_eval+ or any method beginning with "__".
- def hide(name)
- undef_method name if
- instance_methods.include?(name.to_s) and
- name !~ /^(__|instance_eval)/
- end
- end
-
- instance_methods.each { |m| hide(m) }
- end
-end
-
-# Since Ruby is very dynamic, methods added to the ancestors of
-# BlankSlate after BlankSlate is defined will show up in the
-# list of available BlankSlate methods. We handle this by defining a
-# hook in the Object and Kernel classes that will hide any defined
-module Kernel #:nodoc:
- class << self
- alias_method :blank_slate_method_added, :method_added
-
- # Detect method additions to Kernel and remove them in the
- # BlankSlate class.
- def method_added(name)
- blank_slate_method_added(name)
- return if self != Kernel
- Builder::BlankSlate.hide(name)
- end
- end
-end
-
-class Object #:nodoc:
- class << self
- alias_method :blank_slate_method_added, :method_added
-
- # Detect method additions to Object and remove them in the
- # BlankSlate class.
- def method_added(name)
- blank_slate_method_added(name)
- return if self != Object
- Builder::BlankSlate.hide(name)
- end
- end
-end
diff --git a/vendor/rails/activesupport/lib/active_support/vendor/xml_simple.rb b/vendor/rails/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb
similarity index 99%
rename from vendor/rails/activesupport/lib/active_support/vendor/xml_simple.rb
rename to vendor/rails/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb
index 48b7289c..0de24c0e 100644
--- a/vendor/rails/activesupport/lib/active_support/vendor/xml_simple.rb
+++ b/vendor/rails/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb
@@ -11,11 +11,11 @@ require 'stringio'
class XmlSimple
include REXML
- @@VERSION = '1.0.9'
+ @@VERSION = '1.0.11'
# A simple cache for XML documents that were already transformed
# by xml_in.
- class Cache #:nodoc:
+ class Cache
# Creates and initializes a new Cache object.
def initialize
@mem_share_cache = {}
@@ -129,7 +129,7 @@ class XmlSimple
unless defaults.nil? || defaults.instance_of?(Hash)
raise ArgumentError, "Options have to be a Hash."
end
- @default_options = normalize_option_names(defaults, KNOWN_OPTIONS['in'] & KNOWN_OPTIONS['out'])
+ @default_options = normalize_option_names(defaults, (KNOWN_OPTIONS['in'] + KNOWN_OPTIONS['out']).uniq)
@options = Hash.new
@_var_values = nil
end
diff --git a/vendor/rails/activesupport/lib/active_support/version.rb b/vendor/rails/activesupport/lib/active_support/version.rb
index ff80ec6b..83fbaec6 100644
--- a/vendor/rails/activesupport/lib/active_support/version.rb
+++ b/vendor/rails/activesupport/lib/active_support/version.rb
@@ -1,8 +1,8 @@
module ActiveSupport
module VERSION #:nodoc:
- MAJOR = 1
- MINOR = 4
- TINY = 4
+ MAJOR = 2
+ MINOR = 0
+ TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end
diff --git a/vendor/rails/activesupport/lib/active_support/whiny_nil.rb b/vendor/rails/activesupport/lib/active_support/whiny_nil.rb
index cee510af..f614b41f 100644
--- a/vendor/rails/activesupport/lib/active_support/whiny_nil.rb
+++ b/vendor/rails/activesupport/lib/active_support/whiny_nil.rb
@@ -1,23 +1,23 @@
-# Extensions to nil which allow for more helpful error messages for
+# Extensions to nil which allow for more helpful error messages for
# people who are new to rails.
#
# The aim is to ensure that when users pass nil to methods where that isn't
# appropriate, instead of NoMethodError and the name of some method used
-# by the framework users will see a message explaining what type of object
+# by the framework users will see a message explaining what type of object
# was expected.
class NilClass
- WHINERS = [ ::ActiveRecord::Base, ::Array ]
-
+ WHINERS = [::Array]
+ WHINERS << ::ActiveRecord::Base if defined? ::ActiveRecord
+
@@method_class_map = Hash.new
-
+
WHINERS.each do |klass|
methods = klass.public_instance_methods - public_instance_methods
- methods.each do |method|
- @@method_class_map[method.to_sym] = klass
- end
+ class_name = klass.name
+ methods.each { |method| @@method_class_map[method.to_sym] = class_name }
end
-
+
def id
raise RuntimeError, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller
end
@@ -27,11 +27,11 @@ class NilClass
raise_nil_warning_for @@method_class_map[method], method, caller
end
- def raise_nil_warning_for(klass = nil, selector = nil, with_caller = nil)
+ def raise_nil_warning_for(class_name = nil, selector = nil, with_caller = nil)
message = "You have a nil object when you didn't expect it!"
- message << "\nYou might have expected an instance of #{klass}." if klass
+ message << "\nYou might have expected an instance of #{class_name}." if class_name
message << "\nThe error occurred while evaluating nil.#{selector}" if selector
-
+
raise NoMethodError, message, with_caller || caller
end
end
diff --git a/vendor/rails/activesupport/lib/activesupport.rb b/vendor/rails/activesupport/lib/activesupport.rb
new file mode 100644
index 00000000..69f36f79
--- /dev/null
+++ b/vendor/rails/activesupport/lib/activesupport.rb
@@ -0,0 +1 @@
+require 'active_support'
diff --git a/vendor/rails/activesupport/test/abstract_unit.rb b/vendor/rails/activesupport/test/abstract_unit.rb
index 8e237bea..2cfa245b 100644
--- a/vendor/rails/activesupport/test/abstract_unit.rb
+++ b/vendor/rails/activesupport/test/abstract_unit.rb
@@ -1,7 +1,20 @@
require 'test/unit'
$:.unshift "#{File.dirname(__FILE__)}/../lib"
+$:.unshift File.dirname(__FILE__)
require 'active_support'
+# Wrap tests that use Mocha and skip if unavailable.
+unless defined? uses_mocha
+ def uses_mocha(test_name)
+ require 'rubygems'
+ gem 'mocha', '>= 0.5.5'
+ require 'mocha'
+ yield
+ rescue LoadError
+ $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again."
+ end
+end
+
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
diff --git a/vendor/rails/activesupport/test/buffered_logger_test.rb b/vendor/rails/activesupport/test/buffered_logger_test.rb
new file mode 100644
index 00000000..5ce197eb
--- /dev/null
+++ b/vendor/rails/activesupport/test/buffered_logger_test.rb
@@ -0,0 +1,107 @@
+require File.dirname(__FILE__) + '/abstract_unit'
+require 'stringio'
+
+class BufferedLoggerTest < Test::Unit::TestCase
+ def setup
+ @message = "A debug message"
+ @integer_message = 12345
+ @output = StringIO.new
+ @logger = ActiveSupport::BufferedLogger.new(@output)
+ end
+
+ def test_should_log_debugging_message_when_debugging
+ @logger.level = Logger::DEBUG
+ @logger.add(Logger::DEBUG, @message)
+ assert @output.string.include?(@message)
+ end
+
+ def test_should_not_log_debug_messages_when_log_level_is_info
+ @logger.level = Logger::INFO
+ @logger.add(Logger::DEBUG, @message)
+ assert ! @output.string.include?(@message)
+ end
+
+ def test_should_add_message_passed_as_block_when_using_add
+ @logger.level = Logger::INFO
+ @logger.add(Logger::INFO) {@message}
+ assert @output.string.include?(@message)
+ end
+
+ def test_should_add_message_passed_as_block_when_using_shortcut
+ @logger.level = Logger::INFO
+ @logger.info {@message}
+ assert @output.string.include?(@message)
+ end
+
+ def test_should_convert_message_to_string
+ @logger.level = Logger::INFO
+ @logger.info @integer_message
+ assert @output.string.include?(@integer_message.to_s)
+ end
+
+ def test_should_convert_message_to_string_when_passed_in_block
+ @logger.level = Logger::INFO
+ @logger.info {@integer_message}
+ assert @output.string.include?(@integer_message.to_s)
+ end
+
+ def test_should_not_evaluate_block_if_message_wont_be_logged
+ @logger.level = Logger::INFO
+ evaluated = false
+ @logger.add(Logger::DEBUG) {evaluated = true}
+ assert evaluated == false
+ end
+
+ def test_should_not_mutate_message
+ message_copy = @message.dup
+ @logger.info @message
+ assert_equal message_copy, @message
+ end
+
+
+ [false, nil, 0].each do |disable|
+ define_method "test_disabling_auto_flush_with_#{disable.inspect}_should_buffer_until_explicit_flush" do
+ @logger.auto_flushing = disable
+
+ 4.times do
+ @logger.info 'wait for it..'
+ assert @output.string.empty?, @output.string
+ end
+
+ @logger.flush
+ assert !@output.string.empty?, @logger.buffer.size
+ end
+
+ define_method "test_disabling_auto_flush_with_#{disable.inspect}_should_flush_at_max_buffer_size_as_failsafe" do
+ @logger.auto_flushing = disable
+ assert_equal ActiveSupport::BufferedLogger::MAX_BUFFER_SIZE, @logger.auto_flushing
+
+ (ActiveSupport::BufferedLogger::MAX_BUFFER_SIZE - 1).times do
+ @logger.info 'wait for it..'
+ assert @output.string.empty?, @output.string
+ end
+
+ @logger.info 'there it is.'
+ assert !@output.string.empty?, @logger.buffer.size
+ end
+ end
+
+ def test_should_know_if_its_loglevel_is_below_a_given_level
+ ActiveSupport::BufferedLogger::Severity.constants.each do |level|
+ @logger.level = ActiveSupport::BufferedLogger::Severity.const_get(level) - 1
+ assert @logger.send("#{level.downcase}?"), "didn't know if it was #{level.downcase}? or below"
+ end
+ end
+
+ def test_should_auto_flush_every_n_messages
+ @logger.auto_flushing = 5
+
+ 4.times do
+ @logger.info 'wait for it..'
+ assert @output.string.empty?, @output.string
+ end
+
+ @logger.info 'there it is.'
+ assert !@output.string.empty?, @output.string
+ end
+end
diff --git a/vendor/rails/activesupport/test/caching_tools_test.rb b/vendor/rails/activesupport/test/caching_tools_test.rb
deleted file mode 100644
index e1cd4e43..00000000
--- a/vendor/rails/activesupport/test/caching_tools_test.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-require File.join(File.dirname(File.dirname(__FILE__)), 'lib/active_support/caching_tools.rb')
-
-class HashCachingTests < Test::Unit::TestCase
- def cached(&proc)
- return @cached if defined?(@cached)
-
- @cached_class = Class.new(&proc)
- @cached_class.class_eval do
- extend ActiveSupport::CachingTools::HashCaching
- hash_cache :slow_method
- end
- @cached = @cached_class.new
- end
-
- def test_cache_access_should_call_method
- cached do
- def slow_method(a) raise "I should be here: #{a}"; end
- end
- assert_raises(RuntimeError) { cached.slow_method_cache[1] }
- end
-
- def test_cache_access_should_actually_cache
- cached do
- def slow_method(a)
- (@x ||= [])
- if @x.include?(a) then raise "Called twice for #{a}!"
- else
- @x << a
- a + 1
- end
- end
- end
- assert_equal 11, cached.slow_method_cache[10]
- assert_equal 12, cached.slow_method_cache[11]
- assert_equal 11, cached.slow_method_cache[10]
- assert_equal 12, cached.slow_method_cache[11]
- end
-
- def test_cache_should_be_clearable
- cached do
- def slow_method(a)
- @x ||= 0
- @x += 1
- end
- end
- assert_equal 1, cached.slow_method_cache[:a]
- assert_equal 2, cached.slow_method_cache[:b]
- assert_equal 3, cached.slow_method_cache[:c]
-
- assert_equal 1, cached.slow_method_cache[:a]
- assert_equal 2, cached.slow_method_cache[:b]
- assert_equal 3, cached.slow_method_cache[:c]
-
- cached.slow_method_cache.clear
-
- assert_equal 4, cached.slow_method_cache[:a]
- assert_equal 5, cached.slow_method_cache[:b]
- assert_equal 6, cached.slow_method_cache[:c]
- end
-
- def test_deep_caches_should_work_too
- cached do
- def slow_method(a, b, c)
- a + b + c
- end
- end
- assert_equal 3, cached.slow_method_cache[1][1][1]
- assert_equal 7, cached.slow_method_cache[1][2][4]
- assert_equal 7, cached.slow_method_cache[1][2][4]
- assert_equal 7, cached.slow_method_cache[4][2][1]
-
- assert_equal({
- 1 => {1 => {1 => 3}, 2 => {4 => 7}},
- 4 => {2 => {1 => 7}}},
- cached.slow_method_cache
- )
- end
-end
diff --git a/vendor/rails/activesupport/test/class_inheritable_attributes_test.rb b/vendor/rails/activesupport/test/class_inheritable_attributes_test.rb
deleted file mode 100644
index 36914e2b..00000000
--- a/vendor/rails/activesupport/test/class_inheritable_attributes_test.rb
+++ /dev/null
@@ -1,141 +0,0 @@
-require 'test/unit'
-require File.dirname(__FILE__) + '/../lib/active_support/core_ext/class/inheritable_attributes'
-
-class ClassInheritableAttributesTest < Test::Unit::TestCase
- def setup
- @klass = Class.new
- end
-
- def test_reader_declaration
- assert_nothing_raised do
- @klass.class_inheritable_reader :a
- assert_respond_to @klass, :a
- assert_respond_to @klass.new, :a
- end
- end
-
- def test_writer_declaration
- assert_nothing_raised do
- @klass.class_inheritable_writer :a
- assert_respond_to @klass, :a=
- assert_respond_to @klass.new, :a=
- end
- end
-
- def test_accessor_declaration
- assert_nothing_raised do
- @klass.class_inheritable_accessor :a
- assert_respond_to @klass, :a
- assert_respond_to @klass.new, :a
- assert_respond_to @klass, :a=
- assert_respond_to @klass.new, :a=
- end
- end
-
- def test_array_declaration
- assert_nothing_raised do
- @klass.class_inheritable_array :a
- assert_respond_to @klass, :a
- assert_respond_to @klass.new, :a
- assert_respond_to @klass, :a=
- assert_respond_to @klass.new, :a=
- end
- end
-
- def test_hash_declaration
- assert_nothing_raised do
- @klass.class_inheritable_hash :a
- assert_respond_to @klass, :a
- assert_respond_to @klass.new, :a
- assert_respond_to @klass, :a=
- assert_respond_to @klass.new, :a=
- end
- end
-
- def test_reader
- @klass.class_inheritable_reader :a
- assert_nil @klass.a
- assert_nil @klass.new.a
-
- @klass.send(:write_inheritable_attribute, :a, 'a')
-
- assert_equal 'a', @klass.a
- assert_equal 'a', @klass.new.a
- assert_equal @klass.a, @klass.new.a
- assert_equal @klass.a.object_id, @klass.new.a.object_id
- end
-
- def test_writer
- @klass.class_inheritable_reader :a
- @klass.class_inheritable_writer :a
-
- assert_nil @klass.a
- assert_nil @klass.new.a
-
- @klass.a = 'a'
- assert_equal 'a', @klass.a
- @klass.new.a = 'A'
- assert_equal 'A', @klass.a
- end
-
- def test_array
- @klass.class_inheritable_array :a
-
- assert_nil @klass.a
- assert_nil @klass.new.a
-
- @klass.a = %w(a b c)
- assert_equal %w(a b c), @klass.a
- assert_equal %w(a b c), @klass.new.a
-
- @klass.new.a = %w(A B C)
- assert_equal %w(a b c A B C), @klass.a
- assert_equal %w(a b c A B C), @klass.new.a
- end
-
- def test_hash
- @klass.class_inheritable_hash :a
-
- assert_nil @klass.a
- assert_nil @klass.new.a
-
- @klass.a = { :a => 'a' }
- assert_equal({ :a => 'a' }, @klass.a)
- assert_equal({ :a => 'a' }, @klass.new.a)
-
- @klass.new.a = { :b => 'b' }
- assert_equal({ :a => 'a', :b => 'b' }, @klass.a)
- assert_equal({ :a => 'a', :b => 'b' }, @klass.new.a)
- end
-
- def test_inheritance
- @klass.class_inheritable_accessor :a
- @klass.a = 'a'
-
- @sub = eval("class FlogMe < @klass; end; FlogMe")
-
- @klass.class_inheritable_accessor :b
-
- assert_respond_to @sub, :a
- assert_respond_to @sub, :b
- assert_equal @klass.a, @sub.a
- assert_equal @klass.b, @sub.b
- assert_equal 'a', @sub.a
- assert_nil @sub.b
-
- @klass.b = 'b'
- assert_not_equal @klass.b, @sub.b
- assert_equal 'b', @klass.b
- assert_nil @sub.b
-
- @sub.a = 'A'
- assert_not_equal @klass.a, @sub.a
- assert_equal 'a', @klass.a
- assert_equal 'A', @sub.a
-
- @sub.b = 'B'
- assert_not_equal @klass.b, @sub.b
- assert_equal 'b', @klass.b
- assert_equal 'B', @sub.b
- end
-end
diff --git a/vendor/rails/activesupport/test/clean_logger_test.rb b/vendor/rails/activesupport/test/clean_logger_test.rb
index a15bfbca..5c9c8f91 100644
--- a/vendor/rails/activesupport/test/clean_logger_test.rb
+++ b/vendor/rails/activesupport/test/clean_logger_test.rb
@@ -40,41 +40,18 @@ class CleanLoggerTest < Test::Unit::TestCase
assert_equal "error\nfatal\nerror\nfatal\nunsilenced\n", @out.string
end
-end
-
-class CleanLogger_182_to_183_Test < Test::Unit::TestCase
- def setup
- silence_warnings do
- if Logger.method_defined?(:formatter=)
- Logger.send(:alias_method, :hide_formatter=, :formatter=)
- Logger.send(:undef_method, :formatter=)
- else
- Logger.send(:define_method, :formatter=) { }
- end
- load File.dirname(__FILE__) + '/../lib/active_support/clean_logger.rb'
- end
-
- @out = StringIO.new
- @logger = Logger.new(@out)
- @logger.progname = 'CLEAN LOGGER TEST'
+
+ def test_datetime_format
+ @logger.formatter = Logger::Formatter.new
+ @logger.datetime_format = "%Y-%m-%d"
+ @logger.debug 'debug'
+ assert_equal "%Y-%m-%d", @logger.datetime_format
+ assert_match(/D, \[\d\d\d\d-\d\d-\d\d#\d+\] DEBUG -- : debug/, @out.string)
end
-
- def teardown
- silence_warnings do
- if Logger.method_defined?(:hide_formatter=)
- Logger.send(:alias_method, :formatter=, :hide_formatter=)
- else
- Logger.send(:undef_method, :formatter=)
- end
- load File.dirname(__FILE__) + '/../lib/active_support/clean_logger.rb'
- end
- end
-
- # Since we've fooled Logger into thinking we're on 1.8.2 if we're on 1.8.3
- # and on 1.8.3 if we're on 1.8.2, it'll define format_message with the
- # wrong order of arguments and therefore print progname instead of msg.
- def test_format_message_with_faked_version
- @logger.error 'error'
- assert_equal "CLEAN LOGGER TEST\n", @out.string
+
+ def test_nonstring_formatting
+ an_object = [1, 2, 3, 4, 5]
+ @logger.debug an_object
+ assert_equal("#{an_object.inspect}\n", @out.string)
end
end
diff --git a/vendor/rails/activesupport/test/core_ext/array_ext_test.rb b/vendor/rails/activesupport/test/core_ext/array_ext_test.rb
index 87608de8..840a4c81 100644
--- a/vendor/rails/activesupport/test/core_ext/array_ext_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/array_ext_test.rb
@@ -1,7 +1,27 @@
require File.dirname(__FILE__) + '/../abstract_unit'
require 'bigdecimal'
+class ArrayExtAccessTests < Test::Unit::TestCase
+ def test_from
+ assert_equal %w( a b c d ), %w( a b c d ).from(0)
+ assert_equal %w( c d ), %w( a b c d ).from(2)
+ assert_nil %w( a b c d ).from(10)
+ end
+
+ def test_to
+ assert_equal %w( a ), %w( a b c d ).to(0)
+ assert_equal %w( a b c ), %w( a b c d ).to(2)
+ assert_equal %w( a b c d ), %w( a b c d ).to(10)
+ end
+end
+
class ArrayExtToParamTests < Test::Unit::TestCase
+ class ToParam < String
+ def to_param
+ "#{self}1"
+ end
+ end
+
def test_string_array
assert_equal '', %w().to_param
assert_equal 'hello/world', %w(hello world).to_param
@@ -11,6 +31,10 @@ class ArrayExtToParamTests < Test::Unit::TestCase
def test_number_array
assert_equal '10/20', [10, 20].to_param
end
+
+ def test_to_param_array
+ assert_equal 'custom1/param1', [ToParam.new('custom'), ToParam.new('param')].to_param
+ end
end
class ArrayExtToSentenceTests < Test::Unit::TestCase
@@ -19,11 +43,14 @@ class ArrayExtToSentenceTests < Test::Unit::TestCase
assert_equal "one", ['one'].to_sentence
assert_equal "one and two", ['one', 'two'].to_sentence
assert_equal "one, two, and three", ['one', 'two', 'three'].to_sentence
-
end
def test_to_sentence_with_connector
assert_equal "one, two, and also three", ['one', 'two', 'three'].to_sentence(:connector => 'and also')
+ assert_equal "one, two, three", ['one', 'two', 'three'].to_sentence(:connector => '')
+ assert_equal "one, two, three", ['one', 'two', 'three'].to_sentence(:connector => nil)
+ assert_equal "one, two, three", ['one', 'two', 'three'].to_sentence(:connector => ' ')
+ assert_equal "one, two, and three", ['one', 'two', 'three'].to_sentence(:connector => 'and ')
end
def test_to_sentence_with_skip_last_comma
@@ -32,11 +59,16 @@ class ArrayExtToSentenceTests < Test::Unit::TestCase
def test_two_elements
assert_equal "one and two", ['one', 'two'].to_sentence
+ assert_equal "one two", ['one', 'two'].to_sentence(:connector => '')
end
def test_one_element
assert_equal "one", ['one'].to_sentence
end
+
+ def test_one_non_string_element
+ assert_equal '1', [1].to_sentence
+ end
end
class ArrayExtToSTests < Test::Unit::TestCase
@@ -121,12 +153,12 @@ class ArrayToXmlTests < Test::Unit::TestCase
{ :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') }
].to_xml(:skip_instruct => true, :indent => 0)
- assert_equal "", xml.first(17), xml
+ assert_equal '', xml.first(30)
assert xml.include?(%(26 )), xml
assert xml.include?(%(820497600000 )), xml
assert xml.include?(%(David )), xml
assert xml.include?(%(31 )), xml
- assert xml.include?(%(1.0 )), xml
+ assert xml.include?(%(1.0 )), xml
assert xml.include?(%(Jason )), xml
end
@@ -135,7 +167,7 @@ class ArrayToXmlTests < Test::Unit::TestCase
{ :name => "David", :age => 26, :age_in_millis => 820497600000 }, { :name => "Jason", :age => 31 }
].to_xml(:skip_instruct => true, :indent => 0, :root => "people")
- assert_equal "", xml.first(16)
+ assert_equal '', xml.first(29)
end
def test_to_xml_with_options
@@ -176,7 +208,43 @@ class ArrayToXmlTests < Test::Unit::TestCase
{ :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') }
].to_xml(:skip_instruct => false, :indent => 0)
- assert(/^<\?xml [^>]*/.match(xml))
- assert xml.rindex(/<\?xml /) == 0
+ assert_match(/^<\?xml [^>]*/, xml)
+ assert_equal 0, xml.rindex(/<\?xml /)
+ end
+
+ def test_to_xml_with_block
+ xml = [
+ { :name => "David", :age => 26, :age_in_millis => 820497600000 },
+ { :name => "Jason", :age => 31, :age_in_millis => BigDecimal.new('1.0') }
+ ].to_xml(:skip_instruct => true, :indent => 0) do |builder|
+ builder.count 2
+ end
+
+ assert xml.include?(%(2 )), xml
end
end
+
+class ArrayExtractOptionsTests < Test::Unit::TestCase
+ def test_extract_options
+ assert_equal({}, [].extract_options!)
+ assert_equal({}, [1].extract_options!)
+ assert_equal({:a=>:b}, [{:a=>:b}].extract_options!)
+ assert_equal({:a=>:b}, [1, {:a=>:b}].extract_options!)
+ end
+end
+
+uses_mocha "ArrayExtRandomTests" do
+
+class ArrayExtRandomTests < Test::Unit::TestCase
+ def test_random_element_from_array
+ assert_nil [].rand
+
+ Kernel.expects(:rand).with(1).returns(0)
+ assert_equal 'x', ['x'].rand
+
+ Kernel.expects(:rand).with(3).returns(1)
+ assert_equal 2, [1, 2, 3].rand
+ end
+end
+
+end
diff --git a/vendor/rails/activesupport/test/core_ext/blank_test.rb b/vendor/rails/activesupport/test/core_ext/blank_test.rb
index 0fce470f..27b9813a 100644
--- a/vendor/rails/activesupport/test/core_ext/blank_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/blank_test.rb
@@ -1,11 +1,19 @@
require File.dirname(__FILE__) + '/../abstract_unit'
+class EmptyTrue
+ def empty?() true; end
+end
+
+class EmptyFalse
+ def empty?() false; end
+end
+
class BlankTest < Test::Unit::TestCase
- BLANK = [nil, false, '', ' ', " \n\t \r ", [], {}]
- NOT = [true, 0, 1, 'a', [nil], { nil => 0 }]
+ BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ]
+ NOT = [ EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], { nil => 0 } ]
def test_blank
- BLANK.each { |v| assert v.blank? }
- NOT.each { |v| assert !v.blank? }
+ BLANK.each { |v| assert v.blank?, "#{v.inspect} should be blank" }
+ NOT.each { |v| assert !v.blank?, "#{v.inspect} should not be blank" }
end
end
diff --git a/vendor/rails/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb b/vendor/rails/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
index 8b11e680..c0bb7acb 100644
--- a/vendor/rails/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
@@ -205,4 +205,20 @@ class ClassInheritableAttributesTest < Test::Unit::TestCase
assert_equal 1, @sub.a.keys.size
assert_equal 0, @klass.a.keys.size
end
+
+ def test_reset_inheritable_attributes
+ @klass.class_inheritable_accessor :a
+ @klass.a = 'a'
+
+ @sub = eval("class Inheriting < @klass; end; Inheriting")
+
+ assert_equal 'a', @klass.a
+ assert_equal 'a', @sub.a
+
+ @klass.reset_inheritable_attributes
+ @sub = eval("class NotInheriting < @klass; end; NotInheriting")
+
+ assert_equal nil, @klass.a
+ assert_equal nil, @sub.a
+ end
end
diff --git a/vendor/rails/activesupport/test/core_ext/class/delegating_attributes_test.rb b/vendor/rails/activesupport/test/core_ext/class/delegating_attributes_test.rb
new file mode 100644
index 00000000..f5b14364
--- /dev/null
+++ b/vendor/rails/activesupport/test/core_ext/class/delegating_attributes_test.rb
@@ -0,0 +1,105 @@
+require File.dirname(__FILE__) + '/../../abstract_unit'
+
+module DelegatingFixtures
+ class Parent
+ end
+
+ class Child < Parent
+ superclass_delegating_accessor :some_attribute
+ end
+
+ class Mokopuna < Child
+ end
+end
+
+class DelegatingAttributesTest < Test::Unit::TestCase
+ include DelegatingFixtures
+ attr_reader :single_class
+
+ def setup
+ @single_class = Class.new(Object)
+ end
+
+ def test_simple_reader_declaration
+ single_class.superclass_delegating_reader :only_reader
+ # The class and instance should have an accessor, but there
+ # should be no mutator
+ assert single_class.respond_to?(:only_reader)
+ assert single_class.public_instance_methods.map(&:to_s).include?("only_reader")
+ assert !single_class.respond_to?(:only_reader=)
+ end
+
+ def test_simple_writer_declaration
+ single_class.superclass_delegating_writer :only_writer
+ # The class should have a mutator, the instances shouldn't
+ # neither should have an accessor
+ assert single_class.respond_to?(:only_writer=)
+ assert !single_class.public_instance_methods.include?("only_writer=")
+ assert !single_class.public_instance_methods.include?("only_writer")
+ assert !single_class.respond_to?(:only_writer)
+ end
+
+ def test_simple_accessor_declaration
+ single_class.superclass_delegating_accessor :both
+ # Class should have accessor and mutator
+ # the instance should have an accessor only
+ assert single_class.respond_to?(:both)
+ assert single_class.respond_to?(:both=)
+ assert single_class.public_instance_methods.map(&:to_s).include?("both")
+ assert !single_class.public_instance_methods.map(&:to_s).include?("both=")
+ end
+
+ def test_working_with_simple_attributes
+ single_class.superclass_delegating_accessor :both
+ single_class.both= "HMMM"
+ assert_equal "HMMM", single_class.both
+ assert_equal "HMMM", single_class.new.both
+ end
+
+ def test_working_with_accessors
+ single_class.superclass_delegating_reader :only_reader
+ single_class.instance_variable_set("@only_reader", "reading only")
+ assert_equal "reading only", single_class.only_reader
+ assert_equal "reading only", single_class.new.only_reader
+ end
+
+ def test_working_with_simple_mutators
+ single_class.superclass_delegating_writer :only_writer
+ single_class.only_writer="written"
+ assert_equal "written", single_class.instance_variable_get("@only_writer")
+ end
+
+ def test_child_class_delegates_to_parent_but_can_be_overridden
+ parent = Class.new
+ parent.superclass_delegating_accessor :both
+ child = Class.new(parent)
+ parent.both= "1"
+ assert_equal "1", child.both
+
+ child.both="2"
+ assert_equal "1", parent.both
+ assert_equal "2", child.both
+
+ parent.both="3"
+ assert_equal "3", parent.both
+ assert_equal "2", child.both
+ end
+
+ def test_delegation_stops_at_the_right_level
+ assert_nil Mokopuna.some_attribute
+ assert_nil Child.some_attribute
+ Child.some_attribute="1"
+ assert_equal "1", Mokopuna.some_attribute
+ ensure
+ Child.some_attribute=nil
+ end
+
+ def test_delegation_stops_for_nil
+ Mokopuna.some_attribute = nil
+ Child.some_attribute="1"
+
+ assert_equal "1", Child.some_attribute
+ assert_nil Mokopuna.some_attribute
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/rails/activesupport/test/core_ext/class_test.rb b/vendor/rails/activesupport/test/core_ext/class_test.rb
index bcb96f5e..e1b38a1e 100644
--- a/vendor/rails/activesupport/test/core_ext/class_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/class_test.rb
@@ -33,4 +33,14 @@ class ClassTest < Test::Unit::TestCase
Class.remove_class(Y::Z::C)
assert_raises(NameError) { Y::Z::C.is_a?(Class) }
end
+
+ def test_retrieving_subclasses
+ @parent = eval("class D; end; D")
+ @sub = eval("class E < D; end; E")
+ @subofsub = eval("class F < E; end; F")
+ assert @parent.subclasses.all? { |i| [@sub.to_s, @subofsub.to_s].include?(i) }
+ assert_equal 2, @parent.subclasses.size
+ assert_equal [@subofsub.to_s], @sub.subclasses
+ assert_equal [], @subofsub.subclasses
+ end
end
diff --git a/vendor/rails/activesupport/test/core_ext/date_ext_test.rb b/vendor/rails/activesupport/test/core_ext/date_ext_test.rb
index 56161954..0023d48c 100644
--- a/vendor/rails/activesupport/test/core_ext/date_ext_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/date_ext_test.rb
@@ -2,19 +2,195 @@ require File.dirname(__FILE__) + '/../abstract_unit'
class DateExtCalculationsTest < Test::Unit::TestCase
def test_to_s
- assert_equal "21 Feb", Date.new(2005, 2, 21).to_s(:short)
- assert_equal "February 21, 2005", Date.new(2005, 2, 21).to_s(:long)
+ date = Date.new(2005, 2, 21)
+ assert_equal "2005-02-21", date.to_s
+ assert_equal "21 Feb", date.to_s(:short)
+ assert_equal "February 21, 2005", date.to_s(:long)
+ assert_equal "February 21st, 2005", date.to_s(:long_ordinal)
+ assert_equal "2005-02-21", date.to_s(:db)
+ assert_equal "21 Feb 2005", date.to_s(:rfc822)
+ end
+
+ def test_readable_inspect
+ assert_equal "Mon, 21 Feb 2005", Date.new(2005, 2, 21).readable_inspect
+ assert_equal Date.new(2005, 2, 21).readable_inspect, Date.new(2005, 2, 21).inspect
end
def test_to_time
assert_equal Time.local(2005, 2, 21), Date.new(2005, 2, 21).to_time
+ assert_equal Time.local_time(2039, 2, 21), Date.new(2039, 2, 21).to_time
end
- def test_to_time_on_datetime
- assert_equal Time.local(2005, 2, 21, 10, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12).to_time
+ def test_to_datetime
+ assert_equal DateTime.civil(2005, 2, 21), Date.new(2005, 2, 21).to_datetime
+ assert_equal 0, Date.new(2005, 2, 21).to_datetime.offset # use UTC offset
+ assert_equal ::Date::ITALY, Date.new(2005, 2, 21).to_datetime.start # use Ruby's default start value
end
def test_to_date
assert_equal Date.new(2005, 2, 21), Date.new(2005, 2, 21).to_date
end
+
+ def test_change
+ assert_equal Date.new(2005, 2, 21), Date.new(2005, 2, 11).change(:day => 21)
+ assert_equal Date.new(2007, 5, 11), Date.new(2005, 2, 11).change(:year => 2007, :month => 5)
+ assert_equal Date.new(2006,2,22), Date.new(2005,2,22).change(:year => 2006)
+ assert_equal Date.new(2005,6,22), Date.new(2005,2,22).change(:month => 6)
+ end
+
+ def test_beginning_of_week
+ assert_equal Date.new(2005,1,31), Date.new(2005,2,4).beginning_of_week
+ assert_equal Date.new(2005,11,28), Date.new(2005,11,28).beginning_of_week #monday
+ assert_equal Date.new(2005,11,28), Date.new(2005,11,29).beginning_of_week #tuesday
+ assert_equal Date.new(2005,11,28), Date.new(2005,11,30).beginning_of_week #wednesday
+ assert_equal Date.new(2005,11,28), Date.new(2005,12,01).beginning_of_week #thursday
+ assert_equal Date.new(2005,11,28), Date.new(2005,12,02).beginning_of_week #friday
+ assert_equal Date.new(2005,11,28), Date.new(2005,12,03).beginning_of_week #saturday
+ assert_equal Date.new(2005,11,28), Date.new(2005,12,04).beginning_of_week #sunday
+ end
+
+ def test_beginning_of_month
+ assert_equal Date.new(2005,2,1), Date.new(2005,2,22).beginning_of_month
+ end
+
+ def test_beginning_of_quarter
+ assert_equal Date.new(2005,1,1), Date.new(2005,2,15).beginning_of_quarter
+ assert_equal Date.new(2005,1,1), Date.new(2005,1,1).beginning_of_quarter
+ assert_equal Date.new(2005,10,1), Date.new(2005,12,31).beginning_of_quarter
+ assert_equal Date.new(2005,4,1), Date.new(2005,6,30).beginning_of_quarter
+ end
+
+ def test_end_of_month
+ assert_equal Date.new(2005,3,31), Date.new(2005,3,20).end_of_month
+ assert_equal Date.new(2005,2,28), Date.new(2005,2,20).end_of_month
+ assert_equal Date.new(2005,4,30), Date.new(2005,4,20).end_of_month
+
+ end
+
+ def test_beginning_of_year
+ assert_equal Date.new(2005,1,1).to_s, Date.new(2005,2,22).beginning_of_year.to_s
+ end
+
+ def test_months_ago
+ assert_equal Date.new(2005,5,5), Date.new(2005,6,5).months_ago(1)
+ assert_equal Date.new(2004,11,5), Date.new(2005,6,5).months_ago(7)
+ assert_equal Date.new(2004,12,5), Date.new(2005,6,5).months_ago(6)
+ assert_equal Date.new(2004,6,5), Date.new(2005,6,5).months_ago(12)
+ assert_equal Date.new(2003,6,5), Date.new(2005,6,5).months_ago(24)
+ end
+
+ def test_months_since
+ assert_equal Date.new(2005,7,5), Date.new(2005,6,5).months_since(1)
+ assert_equal Date.new(2006,1,5), Date.new(2005,12,5).months_since(1)
+ assert_equal Date.new(2005,12,5), Date.new(2005,6,5).months_since(6)
+ assert_equal Date.new(2006,6,5), Date.new(2005,12,5).months_since(6)
+ assert_equal Date.new(2006,1,5), Date.new(2005,6,5).months_since(7)
+ assert_equal Date.new(2006,6,5), Date.new(2005,6,5).months_since(12)
+ assert_equal Date.new(2007,6,5), Date.new(2005,6,5).months_since(24)
+ assert_equal Date.new(2005,4,30), Date.new(2005,3,31).months_since(1)
+ assert_equal Date.new(2005,2,28), Date.new(2005,1,29).months_since(1)
+ assert_equal Date.new(2005,2,28), Date.new(2005,1,30).months_since(1)
+ assert_equal Date.new(2005,2,28), Date.new(2005,1,31).months_since(1)
+ end
+
+ def test_years_ago
+ assert_equal Date.new(2004,6,5), Date.new(2005,6,5).years_ago(1)
+ assert_equal Date.new(1998,6,5), Date.new(2005,6,5).years_ago(7)
+ assert_equal Date.new(2003,2,28), Date.new(2004,2,29).years_ago(1) # 1 year ago from leap day
+ end
+
+ def test_years_since
+ assert_equal Date.new(2006,6,5), Date.new(2005,6,5).years_since(1)
+ assert_equal Date.new(2012,6,5), Date.new(2005,6,5).years_since(7)
+ assert_equal Date.new(2182,6,5), Date.new(2005,6,5).years_since(177)
+ assert_equal Date.new(2005,2,28), Date.new(2004,2,29).years_since(1) # 1 year since leap day
+ end
+
+ def test_last_year
+ assert_equal Date.new(2004,6,5), Date.new(2005,6,5).last_year
+ end
+
+ def test_next_year
+ assert_equal Date.new(2006,6,5), Date.new(2005,6,5).next_year
+ end
+
+ def test_yesterday
+ assert_equal Date.new(2005,2,21), Date.new(2005,2,22).yesterday
+ assert_equal Date.new(2005,2,28), Date.new(2005,3,2).yesterday.yesterday
+ end
+
+ def test_tomorrow
+ assert_equal Date.new(2005,2,23), Date.new(2005,2,22).tomorrow
+ assert_equal Date.new(2005,3,2), Date.new(2005,2,28).tomorrow.tomorrow
+ end
+
+ def test_advance
+ assert_equal Date.new(2006,2,28), Date.new(2005,2,28).advance(:years => 1)
+ assert_equal Date.new(2005,6,28), Date.new(2005,2,28).advance(:months => 4)
+ assert_equal Date.new(2005,3,21), Date.new(2005,2,28).advance(:weeks => 3)
+ assert_equal Date.new(2005,3,5), Date.new(2005,2,28).advance(:days => 5)
+ assert_equal Date.new(2012,9,28), Date.new(2005,2,28).advance(:years => 7, :months => 7)
+ assert_equal Date.new(2013,10,3), Date.new(2005,2,28).advance(:years => 7, :months => 19, :days => 5)
+ assert_equal Date.new(2013,10,17), Date.new(2005,2,28).advance(:years => 7, :months => 19, :weeks => 2, :days => 5)
+ assert_equal Date.new(2005,2,28), Date.new(2004,2,29).advance(:years => 1) #leap day plus one year
+ end
+
+ def test_next_week
+ assert_equal Date.new(2005,2,28), Date.new(2005,2,22).next_week
+ assert_equal Date.new(2005,3,4), Date.new(2005,2,22).next_week(:friday)
+ assert_equal Date.new(2006,10,30), Date.new(2006,10,23).next_week
+ assert_equal Date.new(2006,11,1), Date.new(2006,10,23).next_week(:wednesday)
+ end
+
+ def test_next_month_on_31st
+ assert_equal Date.new(2005, 9, 30), Date.new(2005, 8, 31).next_month
+ end
+
+ def test_last_month_on_31st
+ assert_equal Date.new(2004, 2, 29), Date.new(2004, 3, 31).last_month
+ end
+
+ def test_yesterday_constructor
+ assert_equal Date.today - 1, Date.yesterday
+ end
+
+ def test_tomorrow_constructor
+ assert_equal Date.today + 1, Date.tomorrow
+ end
+
+ def test_since
+ assert_equal Time.local(2005,2,21,0,0,45), Date.new(2005,2,21).since(45)
+ end
+
+ def test_ago
+ assert_equal Time.local(2005,2,20,23,59,15), Date.new(2005,2,21).ago(45)
+ end
+
+ def test_beginning_of_day
+ assert_equal Time.local(2005,2,21,0,0,0), Date.new(2005,2,21).beginning_of_day
+ end
+
+ def test_end_of_day
+ assert_equal Time.local(2005,2,21,23,59,59), Date.new(2005,2,21).end_of_day
+ end
+
+ def test_xmlschema
+ with_timezone 'US/Eastern' do
+ assert_match(/^1980-02-28T00:00:00-05:?00$/, Date.new(1980, 2, 28).xmlschema)
+ assert_match(/^1980-06-28T00:00:00-04:?00$/, Date.new(1980, 6, 28).xmlschema)
+ # these tests are only of interest on platforms where older dates #to_time fail over to DateTime
+ if ::DateTime === Date.new(1880, 6, 28).to_time
+ assert_match(/^1880-02-28T00:00:00-05:?00$/, Date.new(1880, 2, 28).xmlschema)
+ assert_match(/^1880-06-28T00:00:00-05:?00$/, Date.new(1880, 6, 28).xmlschema) # DateTimes aren't aware of DST rules
+ end
+ end
+ end
+
+ protected
+ def with_timezone(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
+ ensure
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+ end
end
diff --git a/vendor/rails/activesupport/test/core_ext/date_time_ext_test.rb b/vendor/rails/activesupport/test/core_ext/date_time_ext_test.rb
new file mode 100644
index 00000000..57697871
--- /dev/null
+++ b/vendor/rails/activesupport/test/core_ext/date_time_ext_test.rb
@@ -0,0 +1,230 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DateTimeExtCalculationsTest < Test::Unit::TestCase
+ def test_to_s
+ datetime = DateTime.new(2005, 2, 21, 14, 30, 0, 0)
+ assert_match(/^2005-02-21T14:30:00(Z|\+00:00)$/, datetime.to_s)
+ assert_equal "2005-02-21 14:30:00", datetime.to_s(:db)
+ assert_equal "14:30", datetime.to_s(:time)
+ assert_equal "21 Feb 14:30", datetime.to_s(:short)
+ assert_equal "February 21, 2005 14:30", datetime.to_s(:long)
+ assert_equal "Mon, 21 Feb 2005 14:30:00 +0000", datetime.to_s(:rfc822)
+ assert_equal "February 21st, 2005 14:30", datetime.to_s(:long_ordinal)
+ end
+
+ def test_readable_inspect
+ datetime = DateTime.new(2005, 2, 21, 14, 30, 0)
+ assert_equal "Mon, 21 Feb 2005 14:30:00 +0000", datetime.readable_inspect
+ assert_equal datetime.readable_inspect, datetime.inspect
+ end
+
+ def test_custom_date_format
+ Time::DATE_FORMATS[:custom] = '%Y%m%d%H%M%S'
+ assert_equal '20050221143000', DateTime.new(2005, 2, 21, 14, 30, 0).to_s(:custom)
+ Time::DATE_FORMATS.delete(:custom)
+ end
+
+ def test_to_date
+ assert_equal Date.new(2005, 2, 21), DateTime.new(2005, 2, 21).to_date
+ end
+
+ def test_to_datetime
+ assert_equal DateTime.new(2005, 2, 21), DateTime.new(2005, 2, 21).to_datetime
+ end
+
+ def test_to_time
+ assert_equal Time.utc(2005, 2, 21, 10, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0, 0).to_time
+ assert_equal Time.utc_time(2039, 2, 21, 10, 11, 12), DateTime.new(2039, 2, 21, 10, 11, 12, 0, 0).to_time
+ # DateTimes with offsets other than 0 are returned unaltered
+ assert_equal DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)), DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).to_time
+ end
+
+ def test_seconds_since_midnight
+ assert_equal 1,DateTime.civil(2005,1,1,0,0,1).seconds_since_midnight
+ assert_equal 60,DateTime.civil(2005,1,1,0,1,0).seconds_since_midnight
+ assert_equal 3660,DateTime.civil(2005,1,1,1,1,0).seconds_since_midnight
+ assert_equal 86399,DateTime.civil(2005,1,1,23,59,59).seconds_since_midnight
+ end
+
+ def test_beginning_of_week
+ assert_equal DateTime.civil(2005,1,31), DateTime.civil(2005,2,4,10,10,10).beginning_of_week
+ assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,11,28,0,0,0).beginning_of_week #monday
+ assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,11,29,0,0,0).beginning_of_week #tuesday
+ assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,11,30,0,0,0).beginning_of_week #wednesday
+ assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,12,01,0,0,0).beginning_of_week #thursday
+ assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,12,02,0,0,0).beginning_of_week #friday
+ assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,12,03,0,0,0).beginning_of_week #saturday
+ assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,12,04,0,0,0).beginning_of_week #sunday
+ end
+
+ def test_beginning_of_day
+ assert_equal DateTime.civil(2005,2,4,0,0,0), DateTime.civil(2005,2,4,10,10,10).beginning_of_day
+ end
+
+ def test_end_of_day
+ assert_equal DateTime.civil(2005,2,4,23,59,59), DateTime.civil(2005,2,4,10,10,10).end_of_day
+ end
+
+ def test_beginning_of_month
+ assert_equal DateTime.civil(2005,2,1,0,0,0), DateTime.civil(2005,2,22,10,10,10).beginning_of_month
+ end
+
+ def test_beginning_of_quarter
+ assert_equal DateTime.civil(2005,1,1,0,0,0), DateTime.civil(2005,2,15,10,10,10).beginning_of_quarter
+ assert_equal DateTime.civil(2005,1,1,0,0,0), DateTime.civil(2005,1,1,0,0,0).beginning_of_quarter
+ assert_equal DateTime.civil(2005,10,1,0,0,0), DateTime.civil(2005,12,31,10,10,10).beginning_of_quarter
+ assert_equal DateTime.civil(2005,4,1,0,0,0), DateTime.civil(2005,6,30,23,59,59).beginning_of_quarter
+ end
+
+ def test_end_of_month
+ assert_equal DateTime.civil(2005,3,31,23,59,59), DateTime.civil(2005,3,20,10,10,10).end_of_month
+ assert_equal DateTime.civil(2005,2,28,23,59,59), DateTime.civil(2005,2,20,10,10,10).end_of_month
+ assert_equal DateTime.civil(2005,4,30,23,59,59), DateTime.civil(2005,4,20,10,10,10).end_of_month
+ end
+
+ def test_beginning_of_year
+ assert_equal DateTime.civil(2005,1,1,0,0,0), DateTime.civil(2005,2,22,10,10,10).beginning_of_year
+ end
+
+ def test_months_ago
+ assert_equal DateTime.civil(2005,5,5,10), DateTime.civil(2005,6,5,10,0,0).months_ago(1)
+ assert_equal DateTime.civil(2004,11,5,10), DateTime.civil(2005,6,5,10,0,0).months_ago(7)
+ assert_equal DateTime.civil(2004,12,5,10), DateTime.civil(2005,6,5,10,0,0).months_ago(6)
+ assert_equal DateTime.civil(2004,6,5,10), DateTime.civil(2005,6,5,10,0,0).months_ago(12)
+ assert_equal DateTime.civil(2003,6,5,10), DateTime.civil(2005,6,5,10,0,0).months_ago(24)
+ end
+
+ def test_months_since
+ assert_equal DateTime.civil(2005,7,5,10), DateTime.civil(2005,6,5,10,0,0).months_since(1)
+ assert_equal DateTime.civil(2006,1,5,10), DateTime.civil(2005,12,5,10,0,0).months_since(1)
+ assert_equal DateTime.civil(2005,12,5,10), DateTime.civil(2005,6,5,10,0,0).months_since(6)
+ assert_equal DateTime.civil(2006,6,5,10), DateTime.civil(2005,12,5,10,0,0).months_since(6)
+ assert_equal DateTime.civil(2006,1,5,10), DateTime.civil(2005,6,5,10,0,0).months_since(7)
+ assert_equal DateTime.civil(2006,6,5,10), DateTime.civil(2005,6,5,10,0,0).months_since(12)
+ assert_equal DateTime.civil(2007,6,5,10), DateTime.civil(2005,6,5,10,0,0).months_since(24)
+ assert_equal DateTime.civil(2005,4,30,10), DateTime.civil(2005,3,31,10,0,0).months_since(1)
+ assert_equal DateTime.civil(2005,2,28,10), DateTime.civil(2005,1,29,10,0,0).months_since(1)
+ assert_equal DateTime.civil(2005,2,28,10), DateTime.civil(2005,1,30,10,0,0).months_since(1)
+ assert_equal DateTime.civil(2005,2,28,10), DateTime.civil(2005,1,31,10,0,0).months_since(1)
+ end
+
+ def test_years_ago
+ assert_equal DateTime.civil(2004,6,5,10), DateTime.civil(2005,6,5,10,0,0).years_ago(1)
+ assert_equal DateTime.civil(1998,6,5,10), DateTime.civil(2005,6,5,10,0,0).years_ago(7)
+ assert_equal DateTime.civil(2003,2,28,10), DateTime.civil(2004,2,29,10,0,0).years_ago(1) # 1 year ago from leap day
+ end
+
+ def test_years_since
+ assert_equal DateTime.civil(2006,6,5,10), DateTime.civil(2005,6,5,10,0,0).years_since(1)
+ assert_equal DateTime.civil(2012,6,5,10), DateTime.civil(2005,6,5,10,0,0).years_since(7)
+ assert_equal DateTime.civil(2182,6,5,10), DateTime.civil(2005,6,5,10,0,0).years_since(177)
+ assert_equal DateTime.civil(2005,2,28,10), DateTime.civil(2004,2,29,10,0,0).years_since(1) # 1 year since leap day
+ end
+
+ def test_last_year
+ assert_equal DateTime.civil(2004,6,5,10), DateTime.civil(2005,6,5,10,0,0).last_year
+ end
+
+ def test_next_year
+ assert_equal DateTime.civil(2006,6,5,10), DateTime.civil(2005,6,5,10,0,0).next_year
+ end
+
+ def test_ago
+ assert_equal DateTime.civil(2005,2,22,10,10,9), DateTime.civil(2005,2,22,10,10,10).ago(1)
+ assert_equal DateTime.civil(2005,2,22,9,10,10), DateTime.civil(2005,2,22,10,10,10).ago(3600)
+ assert_equal DateTime.civil(2005,2,20,10,10,10), DateTime.civil(2005,2,22,10,10,10).ago(86400*2)
+ assert_equal DateTime.civil(2005,2,20,9,9,45), DateTime.civil(2005,2,22,10,10,10).ago(86400*2 + 3600 + 25)
+ end
+
+ def test_since
+ assert_equal DateTime.civil(2005,2,22,10,10,11), DateTime.civil(2005,2,22,10,10,10).since(1)
+ assert_equal DateTime.civil(2005,2,22,11,10,10), DateTime.civil(2005,2,22,10,10,10).since(3600)
+ assert_equal DateTime.civil(2005,2,24,10,10,10), DateTime.civil(2005,2,22,10,10,10).since(86400*2)
+ assert_equal DateTime.civil(2005,2,24,11,10,35), DateTime.civil(2005,2,22,10,10,10).since(86400*2 + 3600 + 25)
+ assert_equal DateTime.civil(2005,2,22,10,10,11), DateTime.civil(2005,2,22,10,10,10).since(1.333)
+ assert_equal DateTime.civil(2005,2,22,10,10,12), DateTime.civil(2005,2,22,10,10,10).since(1.667)
+ end
+
+ def test_yesterday
+ assert_equal DateTime.civil(2005,2,21,10,10,10), DateTime.civil(2005,2,22,10,10,10).yesterday
+ assert_equal DateTime.civil(2005,2,28,10,10,10), DateTime.civil(2005,3,2,10,10,10).yesterday.yesterday
+ end
+
+ def test_tomorrow
+ assert_equal DateTime.civil(2005,2,23,10,10,10), DateTime.civil(2005,2,22,10,10,10).tomorrow
+ assert_equal DateTime.civil(2005,3,2,10,10,10), DateTime.civil(2005,2,28,10,10,10).tomorrow.tomorrow
+ end
+
+ def test_change
+ assert_equal DateTime.civil(2006,2,22,15,15,10), DateTime.civil(2005,2,22,15,15,10).change(:year => 2006)
+ assert_equal DateTime.civil(2005,6,22,15,15,10), DateTime.civil(2005,2,22,15,15,10).change(:month => 6)
+ assert_equal DateTime.civil(2012,9,22,15,15,10), DateTime.civil(2005,2,22,15,15,10).change(:year => 2012, :month => 9)
+ assert_equal DateTime.civil(2005,2,22,16), DateTime.civil(2005,2,22,15,15,10).change(:hour => 16)
+ assert_equal DateTime.civil(2005,2,22,16,45), DateTime.civil(2005,2,22,15,15,10).change(:hour => 16, :min => 45)
+ assert_equal DateTime.civil(2005,2,22,15,45), DateTime.civil(2005,2,22,15,15,10).change(:min => 45)
+ end
+
+ def test_advance
+ assert_equal DateTime.civil(2006,2,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 1)
+ assert_equal DateTime.civil(2005,6,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:months => 4)
+ assert_equal DateTime.civil(2005,3,21,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:weeks => 3)
+ assert_equal DateTime.civil(2005,3,5,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:days => 5)
+ assert_equal DateTime.civil(2012,9,28,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 7)
+ assert_equal DateTime.civil(2013,10,3,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :days => 5)
+ assert_equal DateTime.civil(2013,10,17,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5)
+ assert_equal DateTime.civil(2001,12,27,15,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:years => -3, :months => -2, :days => -1)
+ assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10).advance(:years => 1) #leap day plus one year
+ assert_equal DateTime.civil(2005,2,28,20,15,10), DateTime.civil(2005,2,28,15,15,10).advance(:hours => 5)
+ assert_equal DateTime.civil(2005,2,28,15,22,10), DateTime.civil(2005,2,28,15,15,10).advance(:minutes => 7)
+ assert_equal DateTime.civil(2005,2,28,15,15,19), DateTime.civil(2005,2,28,15,15,10).advance(:seconds => 9)
+ assert_equal DateTime.civil(2005,2,28,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(:hours => 5, :minutes => 7, :seconds => 9)
+ assert_equal DateTime.civil(2005,2,28,10,8,1), DateTime.civil(2005,2,28,15,15,10).advance(:hours => -5, :minutes => -7, :seconds => -9)
+ assert_equal DateTime.civil(2013,10,17,20,22,19), DateTime.civil(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5, :hours => 5, :minutes => 7, :seconds => 9)
+
+ end
+
+ def test_next_week
+ assert_equal DateTime.civil(2005,2,28), DateTime.civil(2005,2,22,15,15,10).next_week
+ assert_equal DateTime.civil(2005,3,4), DateTime.civil(2005,2,22,15,15,10).next_week(:friday)
+ assert_equal DateTime.civil(2006,10,30), DateTime.civil(2006,10,23,0,0,0).next_week
+ assert_equal DateTime.civil(2006,11,1), DateTime.civil(2006,10,23,0,0,0).next_week(:wednesday)
+ end
+
+ def test_next_month_on_31st
+ assert_equal DateTime.civil(2005, 9, 30), DateTime.civil(2005, 8, 31).next_month
+ end
+
+ def test_last_month_on_31st
+ assert_equal DateTime.civil(2004, 2, 29), DateTime.civil(2004, 3, 31).last_month
+ end
+
+ def test_xmlschema
+ assert_match(/^1880-02-28T15:15:10\+00:?00$/, DateTime.civil(1880, 2, 28, 15, 15, 10).xmlschema)
+ assert_match(/^1980-02-28T15:15:10\+00:?00$/, DateTime.civil(1980, 2, 28, 15, 15, 10).xmlschema)
+ assert_match(/^2080-02-28T15:15:10\+00:?00$/, DateTime.civil(2080, 2, 28, 15, 15, 10).xmlschema)
+ assert_match(/^1880-02-28T15:15:10-06:?00$/, DateTime.civil(1880, 2, 28, 15, 15, 10, -0.25).xmlschema)
+ assert_match(/^1980-02-28T15:15:10-06:?00$/, DateTime.civil(1980, 2, 28, 15, 15, 10, -0.25).xmlschema)
+ assert_match(/^2080-02-28T15:15:10-06:?00$/, DateTime.civil(2080, 2, 28, 15, 15, 10, -0.25).xmlschema)
+ end
+
+ def test_acts_like_time
+ assert DateTime.new.acts_like_time?
+ end
+
+ def test_local_offset
+ with_timezone 'US/Eastern' do
+ assert_equal Rational(-5, 24), DateTime.local_offset
+ end
+ with_timezone 'US/Central' do
+ assert_equal Rational(-6, 24), DateTime.local_offset
+ end
+ end
+
+ protected
+ def with_timezone(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
+ ensure
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+ end
+end
diff --git a/vendor/rails/activesupport/test/core_ext/duplicable_test.rb b/vendor/rails/activesupport/test/core_ext/duplicable_test.rb
new file mode 100644
index 00000000..5e34184a
--- /dev/null
+++ b/vendor/rails/activesupport/test/core_ext/duplicable_test.rb
@@ -0,0 +1,22 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DuplicableTest < Test::Unit::TestCase
+ NO = [nil, false, true, :symbol, 1, 2.3, BigDecimal.new('4.56')]
+ YES = ['1', Object.new, /foo/, [], {}, Time.now]
+
+ def test_duplicable
+ NO.each do |v|
+ assert !v.duplicable?
+ begin
+ v.dup
+ fail
+ rescue Exception
+ end
+ end
+
+ YES.each do |v|
+ assert v.duplicable?
+ assert_nothing_raised { v.dup }
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/test/core_ext/duration_test.rb b/vendor/rails/activesupport/test/core_ext/duration_test.rb
new file mode 100644
index 00000000..125232bd
--- /dev/null
+++ b/vendor/rails/activesupport/test/core_ext/duration_test.rb
@@ -0,0 +1,21 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class DurationTest < Test::Unit::TestCase
+ def test_inspect
+ assert_equal '1 month', 1.month.inspect
+ assert_equal '1 month and 1 day', (1.month + 1.day).inspect
+ assert_equal '6 months and -2 days', (6.months - 2.days).inspect
+ assert_equal '10 seconds', 10.seconds.inspect
+ assert_equal '10 years, 2 months, and 1 day', (10.years + 2.months + 1.day).inspect
+ assert_equal '7 days', 1.week.inspect
+ assert_equal '14 days', 1.fortnight.inspect
+ end
+
+ def test_minus_with_duration_does_not_break_subtraction_of_date_from_date
+ assert_nothing_raised { Date.today - Date.today }
+ end
+
+ def test_plus_with_time
+ assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration"
+ end
+end
diff --git a/vendor/rails/activesupport/test/core_ext/enumerable_test.rb b/vendor/rails/activesupport/test/core_ext/enumerable_test.rb
index 0590846b..af183dac 100644
--- a/vendor/rails/activesupport/test/core_ext/enumerable_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/enumerable_test.rb
@@ -8,8 +8,7 @@ end
class EnumerableTests < Test::Unit::TestCase
def test_group_by
names = %w(marcel sam david jeremy)
- klass = Class.new
- klass.send(:attr_accessor, :name)
+ klass = Struct.new(:name)
objects = (1..50).inject([]) do |people,|
p = klass.new
p.name = names.sort_by { rand }.first
@@ -38,24 +37,25 @@ class EnumerableTests < Test::Unit::TestCase
end
def test_nil_sums
- assert_raise(TypeError) { [5, 15, nil].sum }
+ expected_raise = TypeError
+
+ assert_raise(expected_raise) { [5, 15, nil].sum }
payments = [ Payment.new(5), Payment.new(15), Payment.new(10), Payment.new(nil) ]
- assert_raise(TypeError) { payments.sum(&:price) }
+ assert_raise(expected_raise) { payments.sum(&:price) }
+
assert_equal 60, payments.sum { |p| p.price.to_i * 2 }
end
-
+
def test_empty_sums
assert_equal 0, [].sum
assert_equal 0, [].sum { |i| i }
assert_equal Payment.new(0), [].sum(Payment.new(0))
end
-
+
def test_index_by
payments = [ Payment.new(5), Payment.new(15), Payment.new(10) ]
- assert_equal(
- {5 => payments[0], 15 => payments[1], 10 => payments[2]},
- payments.index_by(&:price)
- )
+ assert_equal({ 5 => payments[0], 15 => payments[1], 10 => payments[2] },
+ payments.index_by { |p| p.price })
end
end
diff --git a/vendor/rails/activesupport/test/core_ext/file_test.rb b/vendor/rails/activesupport/test/core_ext/file_test.rb
new file mode 100644
index 00000000..11025388
--- /dev/null
+++ b/vendor/rails/activesupport/test/core_ext/file_test.rb
@@ -0,0 +1,29 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class AtomicWriteTest < Test::Unit::TestCase
+
+ def test_atomic_write_without_errors
+ contents = "Atomic Text"
+ File.atomic_write(file_name, Dir.pwd) do |file|
+ file.write(contents)
+ assert !File.exist?(file_name)
+ end
+ assert File.exist?(file_name)
+ assert_equal contents, File.read(file_name)
+ ensure
+ File.unlink(file_name) rescue nil
+ end
+
+ def test_atomic_write_doesnt_write_when_block_raises
+ File.atomic_write(file_name) do |file|
+ file.write("testing")
+ raise "something bad"
+ end
+ rescue
+ assert !File.exist?(file_name)
+ end
+
+ def file_name
+ "atomic.file"
+ end
+end
diff --git a/vendor/rails/activesupport/test/core_ext/float_ext_test.rb b/vendor/rails/activesupport/test/core_ext/float_ext_test.rb
new file mode 100644
index 00000000..b74add51
--- /dev/null
+++ b/vendor/rails/activesupport/test/core_ext/float_ext_test.rb
@@ -0,0 +1,25 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class FloatExtRoundingTests < Test::Unit::TestCase
+ def test_round_for_positive_number
+ assert_equal 1, 1.4.round
+ assert_equal 2, 1.6.round
+ assert_equal 2, 1.6.round(0)
+ assert_equal 1.4, 1.4.round(1)
+ assert_equal 1.4, 1.4.round(3)
+ assert_equal 1.5, 1.45.round(1)
+ assert_equal 1.45, 1.445.round(2)
+ end
+
+ def test_round_for_negative_number
+ assert_equal( -1, -1.4.round )
+ assert_equal( -2, -1.6.round )
+ assert_equal( -1.4, -1.4.round(1) )
+ assert_equal( -1.5, -1.45.round(1) )
+ end
+
+ def test_round_with_negative_precision
+ assert_equal 123460.0, 123456.0.round(-1)
+ assert_equal 123500.0, 123456.0.round(-2)
+ end
+end
diff --git a/vendor/rails/activesupport/test/core_ext/hash_ext_test.rb b/vendor/rails/activesupport/test/core_ext/hash_ext_test.rb
index f4dbdf47..4d01faa5 100644
--- a/vendor/rails/activesupport/test/core_ext/hash_ext_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/hash_ext_test.rb
@@ -5,6 +5,7 @@ class HashExtTest < Test::Unit::TestCase
@strings = { 'a' => 1, 'b' => 2 }
@symbols = { :a => 1, :b => 2 }
@mixed = { :a => 1, 'b' => 2 }
+ @fixnums = { 0 => 1, 1 => 2 }
end
def test_methods
@@ -33,6 +34,11 @@ class HashExtTest < Test::Unit::TestCase
assert_raises(NoMethodError) { { [] => 1 }.symbolize_keys }
end
+ def test_symbolize_keys_preserves_fixnum_keys
+ assert_equal @fixnums, @fixnums.symbolize_keys
+ assert_equal @fixnums, @fixnums.dup.symbolize_keys!
+ end
+
def test_stringify_keys
assert_equal @strings, @symbols.stringify_keys
assert_equal @strings, @strings.stringify_keys
@@ -50,7 +56,7 @@ class HashExtTest < Test::Unit::TestCase
@symbols = @symbols.with_indifferent_access
@mixed = @mixed.with_indifferent_access
- assert_equal 'a', @strings.send(:convert_key, :a)
+ assert_equal 'a', @strings.send!(:convert_key, :a)
assert_equal 1, @strings.fetch('a')
assert_equal 1, @strings.fetch(:a.to_s)
@@ -63,9 +69,9 @@ class HashExtTest < Test::Unit::TestCase
hashes.each do |name, hash|
method_map.sort_by { |m| m.to_s }.each do |meth, expected|
- assert_equal(expected, hash.send(meth, 'a'),
+ assert_equal(expected, hash.send!(meth, 'a'),
"Calling #{name}.#{meth} 'a'")
- assert_equal(expected, hash.send(meth, :a),
+ assert_equal(expected, hash.send!(meth, :a),
"Calling #{name}.#{meth} :a")
end
end
@@ -139,7 +145,7 @@ class HashExtTest < Test::Unit::TestCase
assert_equal updated_with_mixed[:a], 1
assert_equal updated_with_mixed['b'], 2
- assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? {|hash| hash.keys.size == 2}
+ assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 }
end
def test_indifferent_merging
@@ -171,6 +177,23 @@ class HashExtTest < Test::Unit::TestCase
assert_equal hash.delete('a'), nil
end
+ def test_indifferent_to_hash
+ # Should convert to a Hash with String keys.
+ assert_equal @strings, @mixed.with_indifferent_access.to_hash
+
+ # Should preserve the default value.
+ mixed_with_default = @mixed.dup
+ mixed_with_default.default = '1234'
+ roundtrip = mixed_with_default.with_indifferent_access.to_hash
+ assert_equal @strings, roundtrip
+ assert_equal '1234', roundtrip.default
+ end
+
+ def test_indifferent_hash_with_array_of_hashes
+ hash = { "urls" => { "url" => [ { "address" => "1" }, { "address" => "2" } ] }}.with_indifferent_access
+ assert_equal "1", hash[:urls][:url].first[:address]
+ end
+
def test_stringify_and_symbolize_keys_on_indifferent_preserves_hash
h = HashWithIndifferentAccess.new
h[:first] = 1
@@ -182,6 +205,22 @@ class HashExtTest < Test::Unit::TestCase
assert_equal 1, h[:first]
end
+ def test_to_options_on_indifferent_preserves_hash
+ h = HashWithIndifferentAccess.new
+ h['first'] = 1
+ h.to_options!
+ assert_equal 1, h['first']
+ end
+
+
+ def test_indifferent_subhashes
+ h = {'user' => {'id' => 5}}.with_indifferent_access
+ ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}}
+
+ h = {:user => {:id => 5}}.with_indifferent_access
+ ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}}
+ end
+
def test_assert_valid_keys
assert_nothing_raised do
{ :failure => "stuff", :funny => "business" }.assert_valid_keys([ :failure, :funny ])
@@ -194,14 +233,6 @@ class HashExtTest < Test::Unit::TestCase
end
end
- def test_indifferent_subhashes
- h = {'user' => {'id' => 5}}.with_indifferent_access
- ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}}
-
- h = {:user => {:id => 5}}.with_indifferent_access
- ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}}
- end
-
def test_assorted_keys_not_stringified
original = {Object.new => 2, 1 => 2, [] => true}
indiff = original.with_indifferent_access
@@ -209,12 +240,70 @@ class HashExtTest < Test::Unit::TestCase
end
def test_reverse_merge
- assert_equal({ :a => 1, :b => 2, :c => 10 }, { :a => 1, :b => 2 }.reverse_merge({:a => "x", :b => "y", :c => 10}) )
+ defaults = { :a => "x", :b => "y", :c => 10 }.freeze
+ options = { :a => 1, :b => 2 }
+ expected = { :a => 1, :b => 2, :c => 10 }
+
+ # Should merge defaults into options, creating a new hash.
+ assert_equal expected, options.reverse_merge(defaults)
+ assert_not_equal expected, options
+
+ # Should merge! defaults into options, replacing options.
+ merged = options.dup
+ assert_equal expected, merged.reverse_merge!(defaults)
+ assert_equal expected, merged
+
+ # Should be an alias for reverse_merge!
+ merged = options.dup
+ assert_equal expected, merged.reverse_update(defaults)
+ assert_equal expected, merged
end
def test_diff
assert_equal({ :a => 2 }, { :a => 2, :b => 5 }.diff({ :a => 1, :b => 5 }))
end
+
+ def test_slice
+ original = { :a => 'x', :b => 'y', :c => 10 }
+ expected = { :a => 'x', :b => 'y' }
+
+ # Should return a new hash with only the given keys.
+ assert_equal expected, original.slice(:a, :b)
+ assert_not_equal expected, original
+
+ # Should replace the hash with only the given keys.
+ assert_equal expected, original.slice!(:a, :b)
+ assert_equal expected, original
+ end
+
+ def test_indifferent_slice
+ original = { :a => 'x', :b => 'y', :c => 10 }.with_indifferent_access
+ expected = { :a => 'x', :b => 'y' }.with_indifferent_access
+
+ [['a', 'b'], [:a, :b]].each do |keys|
+ # Should return a new hash with only the given keys.
+ assert_equal expected, original.slice(*keys), keys.inspect
+ assert_not_equal expected, original
+
+ # Should replace the hash with only the given keys.
+ copy = original.dup
+ assert_equal expected, copy.slice!(*keys)
+ assert_equal expected, copy
+ end
+ end
+
+ def test_except
+ original = { :a => 'x', :b => 'y', :c => 10 }
+ expected = { :a => 'x', :b => 'y' }
+
+ # Should return a new hash with only the given keys.
+ assert_equal expected, original.except(:c)
+ assert_not_equal expected, original
+
+ # Should replace the hash with only the given keys.
+ assert_equal expected, original.except!(:c)
+ assert_equal expected, original
+ end
end
class IWriteMyOwnXML
@@ -255,13 +344,14 @@ class HashToXmlTest < Test::Unit::TestCase
end
def test_one_level_with_types
- xml = { :name => "David", :street => "Paulina", :age => 26, :age_in_millis => 820497600000, :moved_on => Date.new(2005, 11, 15) }.to_xml(@xml_options)
+ xml = { :name => "David", :street => "Paulina", :age => 26, :age_in_millis => 820497600000, :moved_on => Date.new(2005, 11, 15), :resident => :yes }.to_xml(@xml_options)
assert_equal "", xml.first(8)
assert xml.include?(%(Paulina ))
assert xml.include?(%(David ))
assert xml.include?(%(26 ))
assert xml.include?(%(820497600000 ))
assert xml.include?(%(2005-11-15 ))
+ assert xml.include?(%(yes ))
end
def test_one_level_with_nils
@@ -280,6 +370,17 @@ class HashToXmlTest < Test::Unit::TestCase
assert xml.include?(%( ))
end
+ def test_one_level_with_yielding
+ xml = { :name => "David", :street => "Paulina" }.to_xml(@xml_options) do |x|
+ x.creator("Rails")
+ end
+
+ assert_equal "", xml.first(8)
+ assert xml.include?(%(Paulina ))
+ assert xml.include?(%(David ))
+ assert xml.include?(%(Rails ))
+ end
+
def test_two_levels
xml = { :name => "David", :address => { :street => "Paulina" } }.to_xml(@xml_options)
assert_equal "", xml.first(8)
@@ -297,7 +398,7 @@ class HashToXmlTest < Test::Unit::TestCase
def test_two_levels_with_array
xml = { :name => "David", :addresses => [{ :street => "Paulina" }, { :street => "Evergreen" }] }.to_xml(@xml_options)
assert_equal "", xml.first(8)
- assert xml.include?(%())
+ assert xml.include?(%())
assert xml.include?(%(Paulina ))
assert xml.include?(%(Evergreen ))
assert xml.include?(%(David ))
@@ -305,7 +406,7 @@ class HashToXmlTest < Test::Unit::TestCase
def test_three_levels_with_array
xml = { :name => "David", :addresses => [{ :streets => [ { :name => "Paulina" }, { :name => "Paulina" } ] } ] }.to_xml(@xml_options)
- assert xml.include?(%())
+ assert xml.include?(%())
end
def test_single_record_from_xml
@@ -319,9 +420,12 @@ class HashToXmlTest < Test::Unit::TestCase
2592000000
2003-07-16
2003-07-16T09:28:00+0000
- Have a nice day
+ --- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n
david@loudthinking.com
+ 1.5
+ 135
+ yes
EOT
@@ -334,9 +438,12 @@ class HashToXmlTest < Test::Unit::TestCase
:replies_close_in => 2592000000,
:written_on => Date.new(2003, 7, 16),
:viewed_at => Time.utc(2003, 7, 16, 9, 28),
- :content => "Have a nice day",
+ :content => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
:author_email_address => "david@loudthinking.com",
- :parent_id => nil
+ :parent_id => nil,
+ :ad_revenue => BigDecimal("1.50"),
+ :optimum_viewing_angle => 135.0,
+ :resident => :yes
}.stringify_keys
assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["topic"]
@@ -350,6 +457,7 @@ class HashToXmlTest < Test::Unit::TestCase
+
EOT
@@ -359,7 +467,8 @@ class HashToXmlTest < Test::Unit::TestCase
:id => nil,
:approved => nil,
:written_on => nil,
- :viewed_at => nil,
+ :viewed_at => nil,
+ :content => nil,
:parent_id => nil
}.stringify_keys
@@ -368,7 +477,7 @@ class HashToXmlTest < Test::Unit::TestCase
def test_multiple_records_from_xml
topics_xml = <<-EOT
-
+
The First Topic
David
@@ -412,7 +521,7 @@ class HashToXmlTest < Test::Unit::TestCase
:parent_id => nil
}.stringify_keys
- assert_equal expected_topic_hash, Hash.from_xml(topics_xml)["topics"]["topic"].first
+ assert_equal expected_topic_hash, Hash.from_xml(topics_xml)["topics"].first
end
def test_single_record_from_xml_with_attributes_other_than_type
@@ -437,6 +546,93 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"]
end
+
+ def test_empty_array_from_xml
+ blog_xml = <<-XML
+
+
+
+ XML
+ expected_blog_hash = {"blog" => {"posts" => []}}
+ assert_equal expected_blog_hash, Hash.from_xml(blog_xml)
+ end
+
+ def test_empty_array_with_whitespace_from_xml
+ blog_xml = <<-XML
+
+
+
+
+ XML
+ expected_blog_hash = {"blog" => {"posts" => []}}
+ assert_equal expected_blog_hash, Hash.from_xml(blog_xml)
+ end
+
+ def test_array_with_one_entry_from_xml
+ blog_xml = <<-XML
+
+
+ a post
+
+
+ XML
+ expected_blog_hash = {"blog" => {"posts" => ["a post"]}}
+ assert_equal expected_blog_hash, Hash.from_xml(blog_xml)
+ end
+
+ def test_array_with_multiple_entries_from_xml
+ blog_xml = <<-XML
+
+
+ a post
+ another post
+
+
+ XML
+ expected_blog_hash = {"blog" => {"posts" => ["a post", "another post"]}}
+ assert_equal expected_blog_hash, Hash.from_xml(blog_xml)
+ end
+
+ def test_xsd_like_types_from_xml
+ bacon_xml = <<-EOT
+
+ 0.5
+ 12.50
+ 1
+ 2007-12-25T12:34:56+0000
+
+ YmFiZS5wbmc=
+
+ EOT
+
+ expected_bacon_hash = {
+ :weight => 0.5,
+ :chunky => true,
+ :price => BigDecimal("12.50"),
+ :expires_at => Time.utc(2007,12,25,12,34,56),
+ :notes => "",
+ :illustration => "babe.png"
+ }.stringify_keys
+
+ assert_equal expected_bacon_hash, Hash.from_xml(bacon_xml)["bacon"]
+ end
+
+ def test_type_trickles_through_when_unknown
+ product_xml = <<-EOT
+
+ 0.5
+ image.gif
+
+
+ EOT
+
+ expected_product_hash = {
+ :weight => 0.5,
+ :image => {'type' => 'ProductImage', 'filename' => 'image.gif' },
+ }.stringify_keys
+
+ assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"]
+ end
def test_should_use_default_value_for_unknown_key
hash_wia = HashWithIndifferentAccess.new(3)
@@ -469,6 +665,40 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal expected, hash.to_xml(@xml_options)
end
end
+
+ def test_empty_string_works_for_typecast_xml_value
+ assert_nothing_raised do
+ Hash.send!(:typecast_xml_value, "")
+ end
+ end
+
+ def test_escaping_to_xml
+ hash = {
+ :bare_string => 'First & Last Name',
+ :pre_escaped_string => 'First & Last Name'
+ }.stringify_keys
+
+ expected_xml = 'First & Last Name First & Last Name '
+ assert_equal expected_xml, hash.to_xml(@xml_options)
+ end
+
+ def test_unescaping_from_xml
+ xml_string = 'First & Last Name First & Last Name '
+ expected_hash = {
+ :bare_string => 'First & Last Name',
+ :pre_escaped_string => 'First & Last Name'
+ }.stringify_keys
+ assert_equal expected_hash, Hash.from_xml(xml_string)['person']
+ end
+
+ def test_roundtrip_to_xml_from_xml
+ hash = {
+ :bare_string => 'First & Last Name',
+ :pre_escaped_string => 'First & Last Name'
+ }.stringify_keys
+
+ assert_equal hash, Hash.from_xml(hash.to_xml(@xml_options))['person']
+ end
end
class QueryTest < Test::Unit::TestCase
@@ -481,29 +711,33 @@ class QueryTest < Test::Unit::TestCase
end
def test_nil_parameter_value
- empty = Object.new
+ empty = Object.new
def empty.to_param; nil end
assert_query_equal 'a=', 'a' => empty
end
def test_nested_conversion
- assert_query_equal 'person%5Bname%5D=Nicholas&person%5Blogin%5D=seckar',
+ assert_query_equal 'person%5Blogin%5D=seckar&person%5Bname%5D=Nicholas',
:person => {:name => 'Nicholas', :login => 'seckar'}
end
-
+
def test_multiple_nested
assert_query_equal 'account%5Bperson%5D%5Bid%5D=20&person%5Bid%5D=10',
:person => {:id => 10}, :account => {:person => {:id => 20}}
end
-
+
def test_array_values
assert_query_equal 'person%5Bid%5D%5B%5D=10&person%5Bid%5D%5B%5D=20',
:person => {:id => [10, 20]}
end
+ def test_array_values_are_not_sorted
+ assert_query_equal 'person%5Bid%5D%5B%5D=20&person%5Bid%5D%5B%5D=10',
+ :person => {:id => [20, 10]}
+ end
+
private
def assert_query_equal(expected, actual, message = nil)
- assert_equal expected.split('&').sort, actual.to_query.split('&').sort
+ assert_equal expected.split('&'), actual.to_query.split('&')
end
end
-
diff --git a/vendor/rails/activesupport/test/core_ext/module/attr_accessor_with_default.rb b/vendor/rails/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
similarity index 92%
rename from vendor/rails/activesupport/test/core_ext/module/attr_accessor_with_default.rb
rename to vendor/rails/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
index 8ae52ad1..71039c0e 100644
--- a/vendor/rails/activesupport/test/core_ext/module/attr_accessor_with_default.rb
+++ b/vendor/rails/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb
@@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../../abstract_unit'
-class AttrWithDefaultTest < Test::Unit::TestCase
+class AttrAccessorWithDefaultTest < Test::Unit::TestCase
def setup
@target = Class.new do
def helper
diff --git a/vendor/rails/activesupport/test/core_ext/module/attr_internal_test.rb b/vendor/rails/activesupport/test/core_ext/module/attr_internal_test.rb
index 25b049f6..1bf831e4 100644
--- a/vendor/rails/activesupport/test/core_ext/module/attr_internal_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/module/attr_internal_test.rb
@@ -6,46 +6,46 @@ class AttrInternalTest < Test::Unit::TestCase
@instance = @target.new
end
- def test_attr_internal_reader
+ def test_reader
assert_nothing_raised { @target.attr_internal_reader :foo }
- assert !@instance.instance_variables.include?('@_foo')
+ assert !@instance.instance_variable_defined?('@_foo')
assert_raise(NoMethodError) { @instance.foo = 1 }
@instance.instance_variable_set('@_foo', 1)
assert_nothing_raised { assert_equal 1, @instance.foo }
end
- def test_attr_internal_writer
+ def test_writer
assert_nothing_raised { @target.attr_internal_writer :foo }
- assert !@instance.instance_variables.include?('@_foo')
+ assert !@instance.instance_variable_defined?('@_foo')
assert_nothing_raised { assert_equal 1, @instance.foo = 1 }
assert_equal 1, @instance.instance_variable_get('@_foo')
assert_raise(NoMethodError) { @instance.foo }
end
- def test_attr_internal_accessor
+ def test_accessor
assert_nothing_raised { @target.attr_internal :foo }
- assert !@instance.instance_variables.include?('@_foo')
+ assert !@instance.instance_variable_defined?('@_foo')
assert_nothing_raised { assert_equal 1, @instance.foo = 1 }
assert_equal 1, @instance.instance_variable_get('@_foo')
assert_nothing_raised { assert_equal 1, @instance.foo }
end
- def test_attr_internal_naming_format
+ def test_naming_format
assert_equal '@_%s', @target.attr_internal_naming_format
assert_nothing_raised { @target.attr_internal_naming_format = '@abc%sdef' }
@target.attr_internal :foo
- assert !@instance.instance_variables.include?('@_foo')
- assert !@instance.instance_variables.include?('@abcfoodef')
+ assert !@instance.instance_variable_defined?('@_foo')
+ assert !@instance.instance_variable_defined?('@abcfoodef')
assert_nothing_raised { @instance.foo = 1 }
- assert !@instance.instance_variables.include?('@_foo')
- assert @instance.instance_variables.include?('@abcfoodef')
+ assert !@instance.instance_variable_defined?('@_foo')
+ assert @instance.instance_variable_defined?('@abcfoodef')
ensure
@target.attr_internal_naming_format = '@_%s'
end
diff --git a/vendor/rails/activesupport/test/core_ext/module/attribute_accessor_test.rb b/vendor/rails/activesupport/test/core_ext/module/attribute_accessor_test.rb
index c3f6575f..7296cc5f 100644
--- a/vendor/rails/activesupport/test/core_ext/module/attribute_accessor_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/module/attribute_accessor_test.rb
@@ -2,32 +2,32 @@ require File.dirname(__FILE__) + '/../../abstract_unit'
class ModuleAttributeAccessorTest < Test::Unit::TestCase
def setup
- @module = Module.new do
+ m = @module = Module.new do
mattr_accessor :foo
mattr_accessor :bar, :instance_writer => false
end
@class = Class.new
- @class.send :include, @module
+ @class.instance_eval { include m }
@object = @class.new
end
-
+
def test_should_use_mattr_default
assert_nil @module.foo
assert_nil @object.foo
end
-
+
def test_should_set_mattr_value
@module.foo = :test
assert_equal :test, @object.foo
-
+
@object.foo = :test2
assert_equal :test2, @module.foo
end
-
+
def test_should_not_create_instance_writer
assert @module.respond_to?(:foo)
assert @module.respond_to?(:foo=)
assert @object.respond_to?(:bar)
assert !@object.respond_to?(:bar=)
end
-end
\ No newline at end of file
+end
diff --git a/vendor/rails/activesupport/test/core_ext/module/attribute_aliasing_test.rb b/vendor/rails/activesupport/test/core_ext/module/attribute_aliasing_test.rb
index 66ddbfe6..bd238795 100644
--- a/vendor/rails/activesupport/test/core_ext/module/attribute_aliasing_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/module/attribute_aliasing_test.rb
@@ -2,15 +2,24 @@ require File.dirname(__FILE__) + '/../../abstract_unit'
module AttributeAliasing
class Content
- attr_accessor :title
-
+ attr_accessor :title, :Data
+
+ def initialize
+ @title, @Data = nil, nil
+ end
+
def title?
!title.nil?
end
+
+ def Data?
+ !self.Data.nil?
+ end
end
class Email < Content
alias_attribute :subject, :title
+ alias_attribute :body, :Data
end
end
@@ -28,4 +37,22 @@ class AttributeAliasingTest < Test::Unit::TestCase
assert_equal "We got a long way to go", e.title
assert e.title?
end
+
+ def test_aliasing_to_uppercase_attributes
+ # Although it's very un-Ruby, some people's AR-mapped tables have
+ # upper-case attributes, and when people want to alias those names
+ # to more sensible ones, everything goes *foof*.
+ e = AttributeAliasing::Email.new
+
+ assert !e.body?
+ assert !e.Data?
+
+ e.body = "No, really, this is not a joke."
+ assert_equal "No, really, this is not a joke.", e.Data
+ assert e.Data?
+
+ e.Data = "Uppercased methods are teh suck"
+ assert_equal "Uppercased methods are teh suck", e.body
+ assert e.body?
+ end
end
diff --git a/vendor/rails/activesupport/test/core_ext/module_test.rb b/vendor/rails/activesupport/test/core_ext/module_test.rb
index 884b4403..fd1f99b3 100644
--- a/vendor/rails/activesupport/test/core_ext/module_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/module_test.rb
@@ -95,9 +95,9 @@ class ModuleTest < Test::Unit::TestCase
assert_equal [Yz::Zy, Yz, Object], Yz::Zy::Cd.parents
assert_equal [Yz, Object], Yz::Zy.parents
end
-
+
def test_local_constants
- assert_equal %w(Constant1 Constant3), Ab.local_constants.sort
+ assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s)
end
def test_as_load_path
@@ -108,8 +108,10 @@ end
module BarMethodAliaser
def self.included(foo_class)
- foo_class.send :include, BarMethods
- foo_class.alias_method_chain :bar, :baz
+ foo_class.class_eval do
+ include BarMethods
+ alias_method_chain :bar, :baz
+ end
end
end
@@ -129,17 +131,20 @@ module BarMethods
def quux_with_baz=(v)
send(:quux_without_baz=, v) << '_with_baz'
end
+
+ def duck_with_orange
+ duck_without_orange << '_with_orange'
+ end
end
class MethodAliasingTest < Test::Unit::TestCase
def setup
- Object.const_set(:FooClassWithBarMethod, Class.new)
- FooClassWithBarMethod.send(:define_method, 'bar', Proc.new { 'bar' })
+ Object.const_set :FooClassWithBarMethod, Class.new { def bar() 'bar' end }
@instance = FooClassWithBarMethod.new
end
def teardown
- Object.send(:remove_const, :FooClassWithBarMethod)
+ Object.instance_eval { remove_const :FooClassWithBarMethod }
end
def test_alias_method_chain
@@ -152,7 +157,7 @@ class MethodAliasingTest < Test::Unit::TestCase
assert_equal 'bar', @instance.bar
- FooClassWithBarMethod.send(:include, BarMethodAliaser)
+ FooClassWithBarMethod.class_eval { include BarMethodAliaser }
feature_aliases.each do |method|
assert @instance.respond_to?(method)
@@ -163,10 +168,15 @@ class MethodAliasingTest < Test::Unit::TestCase
end
def test_alias_method_chain_with_punctuation_method
- FooClassWithBarMethod.send(:define_method, 'quux!', Proc.new { 'quux' })
+ FooClassWithBarMethod.class_eval do
+ def quux!; 'quux' end
+ end
+
assert !@instance.respond_to?(:quux_with_baz!)
- FooClassWithBarMethod.send(:include, BarMethodAliaser)
- FooClassWithBarMethod.alias_method_chain :quux!, :baz
+ FooClassWithBarMethod.class_eval do
+ include BarMethodAliaser
+ alias_method_chain :quux!, :baz
+ end
assert @instance.respond_to?(:quux_with_baz!)
assert_equal 'quux_with_baz', @instance.quux!
@@ -174,14 +184,17 @@ class MethodAliasingTest < Test::Unit::TestCase
end
def test_alias_method_chain_with_same_names_between_predicates_and_bang_methods
- FooClassWithBarMethod.send(:define_method, 'quux!', Proc.new { 'quux!' })
- FooClassWithBarMethod.send(:define_method, 'quux?', Proc.new { true })
- FooClassWithBarMethod.send(:define_method, 'quux=', Proc.new { 'quux=' })
+ FooClassWithBarMethod.class_eval do
+ def quux!; 'quux!' end
+ def quux?; true end
+ def quux=(v); 'quux=' end
+ end
+
assert !@instance.respond_to?(:quux_with_baz!)
assert !@instance.respond_to?(:quux_with_baz?)
assert !@instance.respond_to?(:quux_with_baz=)
- FooClassWithBarMethod.send(:include, BarMethodAliaser)
+ FooClassWithBarMethod.class_eval { include BarMethodAliaser }
assert @instance.respond_to?(:quux_with_baz!)
assert @instance.respond_to?(:quux_with_baz?)
assert @instance.respond_to?(:quux_with_baz=)
@@ -201,11 +214,13 @@ class MethodAliasingTest < Test::Unit::TestCase
end
def test_alias_method_chain_with_feature_punctuation
- FooClassWithBarMethod.send(:define_method, 'quux', Proc.new { 'quux' })
- FooClassWithBarMethod.send(:define_method, 'quux?', Proc.new { 'quux?' })
- FooClassWithBarMethod.send(:include, BarMethodAliaser)
+ FooClassWithBarMethod.class_eval do
+ def quux; 'quux' end
+ def quux?; 'quux?' end
+ include BarMethodAliaser
+ alias_method_chain :quux, :baz!
+ end
- FooClassWithBarMethod.alias_method_chain :quux, :baz!
assert_nothing_raised do
assert_equal 'quux_with_baz', @instance.quux_with_baz!
end
@@ -216,14 +231,63 @@ class MethodAliasingTest < Test::Unit::TestCase
end
def test_alias_method_chain_yields_target_and_punctuation
- FooClassWithBarMethod.send(:define_method, :quux?, Proc.new { })
- FooClassWithBarMethod.send :include, BarMethods
- block_called = false
- FooClassWithBarMethod.alias_method_chain :quux?, :baz do |target, punctuation|
- block_called = true
- assert_equal 'quux', target
- assert_equal '?', punctuation
+ args = nil
+
+ FooClassWithBarMethod.class_eval do
+ def quux?; end
+ include BarMethods
+
+ FooClassWithBarMethod.alias_method_chain :quux?, :baz do |target, punctuation|
+ args = [target, punctuation]
+ end
end
- assert block_called
+
+ assert_not_nil args
+ assert_equal 'quux', args[0]
+ assert_equal '?', args[1]
+ end
+
+ def test_alias_method_chain_preserves_private_method_status
+ FooClassWithBarMethod.class_eval do
+ def duck; 'duck' end
+ include BarMethodAliaser
+ private :duck
+ alias_method_chain :duck, :orange
+ end
+
+ assert_raises NoMethodError do
+ @instance.duck
+ end
+
+ assert_equal 'duck_with_orange', @instance.instance_eval { duck }
+ assert FooClassWithBarMethod.private_method_defined?(:duck)
+ end
+
+ def test_alias_method_chain_preserves_protected_method_status
+ FooClassWithBarMethod.class_eval do
+ def duck; 'duck' end
+ include BarMethodAliaser
+ protected :duck
+ alias_method_chain :duck, :orange
+ end
+
+ assert_raises NoMethodError do
+ @instance.duck
+ end
+
+ assert_equal 'duck_with_orange', @instance.instance_eval { duck }
+ assert FooClassWithBarMethod.protected_method_defined?(:duck)
+ end
+
+ def test_alias_method_chain_preserves_public_method_status
+ FooClassWithBarMethod.class_eval do
+ def duck; 'duck' end
+ include BarMethodAliaser
+ public :duck
+ alias_method_chain :duck, :orange
+ end
+
+ assert_equal 'duck_with_orange', @instance.duck
+ assert FooClassWithBarMethod.public_method_defined?(:duck)
end
end
diff --git a/vendor/rails/activesupport/test/core_ext/numeric_ext_test.rb b/vendor/rails/activesupport/test/core_ext/numeric_ext_test.rb
index 1d04cf7f..a773339b 100644
--- a/vendor/rails/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/numeric_ext_test.rb
@@ -1,8 +1,9 @@
require File.dirname(__FILE__) + '/../abstract_unit'
-class NumericExtTimeTest < Test::Unit::TestCase
+class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase
def setup
@now = Time.now
+ @dtnow = DateTime.now
@seconds = {
1.minute => 60,
10.minutes => 600,
@@ -34,11 +35,85 @@ class NumericExtTimeTest < Test::Unit::TestCase
assert seconds.from_now >= now + seconds
end
end
+
+ def test_irregular_durations
+ assert_equal @now.advance(:days => 3000), 3000.days.since(@now)
+ assert_equal @now.advance(:months => 1), 1.month.since(@now)
+ assert_equal @now.advance(:months => -1), 1.month.until(@now)
+ assert_equal @now.advance(:years => 20), 20.years.since(@now)
+ assert_equal @dtnow.advance(:days => 3000), 3000.days.since(@dtnow)
+ assert_equal @dtnow.advance(:months => 1), 1.month.since(@dtnow)
+ assert_equal @dtnow.advance(:months => -1), 1.month.until(@dtnow)
+ assert_equal @dtnow.advance(:years => 20), 20.years.since(@dtnow)
+ end
+
+ def test_duration_addition
+ assert_equal @now.advance(:days => 1).advance(:months => 1), (1.day + 1.month).since(@now)
+ assert_equal @now.advance(:days => 7), (1.week + 5.seconds - 5.seconds).since(@now)
+ assert_equal @now.advance(:years => 2), (4.years - 2.years).since(@now)
+ assert_equal @dtnow.advance(:days => 1).advance(:months => 1), (1.day + 1.month).since(@dtnow)
+ assert_equal @dtnow.advance(:days => 7), (1.week + 5.seconds - 5.seconds).since(@dtnow)
+ assert_equal @dtnow.advance(:years => 2), (4.years - 2.years).since(@dtnow)
+ end
+
+ def test_time_plus_duration
+ assert_equal @now + 8, @now + 8.seconds
+ assert_equal @now + 22.9, @now + 22.9.seconds
+ assert_equal @now.advance(:days => 15), @now + 15.days
+ assert_equal @now.advance(:months => 1), @now + 1.month
+ assert_equal @dtnow.since(8), @dtnow + 8.seconds
+ assert_equal @dtnow.since(22.9), @dtnow + 22.9.seconds
+ assert_equal @dtnow.advance(:days => 15), @dtnow + 15.days
+ assert_equal @dtnow.advance(:months => 1), @dtnow + 1.month
+ end
+
+ def test_chaining_duration_operations
+ assert_equal @now.advance(:days => 2).advance(:months => -3), @now + 2.days - 3.months
+ assert_equal @now.advance(:days => 1).advance(:months => 2), @now + 1.day + 2.months
+ assert_equal @dtnow.advance(:days => 2).advance(:months => -3), @dtnow + 2.days - 3.months
+ assert_equal @dtnow.advance(:days => 1).advance(:months => 2), @dtnow + 1.day + 2.months
+ end
+
+ def test_duration_after_convertion_is_no_longer_accurate
+ assert_equal 30.days.to_i.since(@now), 1.month.to_i.since(@now)
+ assert_equal 365.25.days.to_f.since(@now), 1.year.to_f.since(@now)
+ assert_equal 30.days.to_i.since(@dtnow), 1.month.to_i.since(@dtnow)
+ assert_equal 365.25.days.to_f.since(@dtnow), 1.year.to_f.since(@dtnow)
+ end
+
+ def test_add_one_year_to_leap_day
+ assert_equal Time.utc(2005,2,28,15,15,10), Time.utc(2004,2,29,15,15,10) + 1.year
+ assert_equal DateTime.civil(2005,2,28,15,15,10), DateTime.civil(2004,2,29,15,15,10) + 1.year
+ end
+end
+
+class NumericExtDateTest < Test::Unit::TestCase
+ def setup
+ @today = Date.today
+ end
+
+ def test_date_plus_duration
+ assert_equal @today + 1, @today + 1.day
+ assert_equal @today >> 1, @today + 1.month
+ assert_equal @today.to_time.since(1), @today + 1.second
+ assert_equal @today.to_time.since(60), @today + 1.minute
+ assert_equal @today.to_time.since(60*60), @today + 1.hour
+ end
+
+ def test_chaining_duration_operations
+ assert_equal @today.advance(:days => 2).advance(:months => -3), @today + 2.days - 3.months
+ assert_equal @today.advance(:days => 1).advance(:months => 2), @today + 1.day + 2.months
+ end
+
+ def test_add_one_year_to_leap_day
+ assert_equal Date.new(2005,2,28), Date.new(2004,2,29) + 1.year
+ end
end
class NumericExtSizeTest < Test::Unit::TestCase
def test_unit_in_terms_of_another
relationships = {
+ 1024.bytes => 1.kilobyte,
1024.kilobytes => 1.megabyte,
3584.0.kilobytes => 3.5.megabytes,
3584.0.megabytes => 3.5.gigabytes,
@@ -54,4 +129,19 @@ class NumericExtSizeTest < Test::Unit::TestCase
assert_equal right, left
end
end
+
+ def test_units_as_bytes_independently
+ assert_equal 3145728, 3.megabytes
+ assert_equal 3145728, 3.megabyte
+ assert_equal 3072, 3.kilobytes
+ assert_equal 3072, 3.kilobyte
+ assert_equal 3221225472, 3.gigabytes
+ assert_equal 3221225472, 3.gigabyte
+ assert_equal 3298534883328, 3.terabytes
+ assert_equal 3298534883328, 3.terabyte
+ assert_equal 3377699720527872, 3.petabytes
+ assert_equal 3377699720527872, 3.petabyte
+ assert_equal 3458764513820540928, 3.exabytes
+ assert_equal 3458764513820540928, 3.exabyte
+ end
end
diff --git a/vendor/rails/activesupport/test/core_ext/object_and_class_ext_test.rb b/vendor/rails/activesupport/test/core_ext/object_and_class_ext_test.rb
index 9daf8d0a..f885d94b 100644
--- a/vendor/rails/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -11,6 +11,16 @@ class ClassJ < ClassI; end
class ClassK
end
module Nested
+ class << self
+ def on_const_missing(&callback)
+ @on_const_missing = callback
+ end
+ private
+ def const_missing(mod_id)
+ @on_const_missing[mod_id] if @on_const_missing
+ super
+ end
+ end
class ClassL < ClassK
end
end
@@ -41,9 +51,12 @@ class ClassExtTest < Test::Unit::TestCase
end
def test_subclasses_of
+ cj = ClassJ
assert_equal [ClassJ], Object.subclasses_of(ClassI)
ClassI.remove_subclasses
assert_equal [], Object.subclasses_of(ClassI)
+ ensure
+ Object.const_set :ClassJ, cj
end
def test_subclasses_of_should_find_nested_classes
@@ -52,7 +65,7 @@ class ClassExtTest < Test::Unit::TestCase
def test_subclasses_of_should_not_return_removed_classes
# First create the removed class
- old_class = Nested.send :remove_const, :ClassL
+ old_class = Nested.class_eval { remove_const :ClassL }
new_class = Class.new(ClassK)
Nested.const_set :ClassL, new_class
assert_equal "Nested::ClassL", new_class.name # Sanity check
@@ -60,10 +73,46 @@ class ClassExtTest < Test::Unit::TestCase
subclasses = Object.subclasses_of(ClassK)
assert subclasses.include?(new_class)
assert ! subclasses.include?(old_class)
+ ensure
+ Nested.const_set :ClassL, old_class unless defined?(Nested::ClassL)
+ end
+
+ def test_subclasses_of_should_not_trigger_const_missing
+ const_missing = false
+ Nested.on_const_missing { const_missing = true }
+
+ subclasses = Object.subclasses_of ClassK
+ assert !const_missing
+ assert_equal [ Nested::ClassL ], subclasses
+
+ removed = Nested.class_eval { remove_const :ClassL } # keep it in memory
+ subclasses = Object.subclasses_of ClassK
+ assert !const_missing
+ assert subclasses.empty?
+ ensure
+ Nested.const_set :ClassL, removed unless defined?(Nested::ClassL)
+ end
+
+ def test_subclasses_of_with_multiple_roots
+ classes = Object.subclasses_of(ClassI, ClassK)
+ assert_equal %w(ClassJ Nested::ClassL), classes.collect(&:to_s).sort
+ end
+
+ def test_subclasses_of_doesnt_find_anonymous_classes
+ assert_equal [], Object.subclasses_of(Foo)
+ bar = Class.new(Foo)
+ assert_nothing_raised do
+ assert_equal [bar], Object.subclasses_of(Foo)
+ end
end
end
class ObjectTests < Test::Unit::TestCase
+ def test_send_bang_aliases_send_before_19
+ assert_respond_to 'a', :send!
+ assert_equal 1, 'a'.send!(:size)
+ end
+
def test_suppress_re_raises
assert_raises(LoadError) { suppress(ArgumentError) {raise LoadError} }
end
@@ -96,6 +145,34 @@ class ObjectTests < Test::Unit::TestCase
assert object.respond_to?(:baz)
end
+ class DuckTime
+ def acts_like_time?
+ true
+ end
+ end
+
+ def test_duck_typing
+ object = Object.new
+ time = Time.now
+ date = Date.today
+ dt = DateTime.new
+ duck = DuckTime.new
+
+ assert !object.acts_like?(:time)
+ assert !object.acts_like?(:date)
+
+ assert time.acts_like?(:time)
+ assert !time.acts_like?(:date)
+
+ assert !date.acts_like?(:time)
+ assert date.acts_like?(:date)
+
+ assert dt.acts_like?(:time)
+ assert dt.acts_like?(:date)
+
+ assert duck.acts_like?(:time)
+ assert !duck.acts_like?(:date)
+ end
end
class ObjectInstanceVariableTest < Test::Unit::TestCase
@@ -105,11 +182,18 @@ class ObjectInstanceVariableTest < Test::Unit::TestCase
@source.instance_variable_set(:@baz, 'baz')
end
+ def test_instance_variable_defined
+ assert @source.instance_variable_defined?('@bar')
+ assert @source.instance_variable_defined?(:@bar)
+ assert !@source.instance_variable_defined?(:@foo)
+ assert !@source.instance_variable_defined?('@foo')
+ end
+
def test_copy_instance_variables_from_without_explicit_excludes
assert_equal [], @dest.instance_variables
@dest.copy_instance_variables_from(@source)
- assert_equal %w(@bar @baz), @dest.instance_variables.sort
+ assert_equal %w(@bar @baz), @dest.instance_variables.sort.map(&:to_s)
%w(@bar @baz).each do |name|
assert_equal @source.instance_variable_get(name).object_id,
@dest.instance_variable_get(name).object_id
@@ -118,7 +202,7 @@ class ObjectInstanceVariableTest < Test::Unit::TestCase
def test_copy_instance_variables_from_with_explicit_excludes
@dest.copy_instance_variables_from(@source, ['@baz'])
- assert !@dest.instance_variables.include?('@baz')
+ assert !@dest.instance_variable_defined?('@baz')
assert_equal 'bar', @dest.instance_variable_get('@bar')
end
@@ -131,8 +215,8 @@ class ObjectInstanceVariableTest < Test::Unit::TestCase
end
@dest.copy_instance_variables_from(@source)
- assert !@dest.instance_variables.include?('@bar')
- assert !@dest.instance_variables.include?('@quux')
+ assert !@dest.instance_variable_defined?('@bar')
+ assert !@dest.instance_variable_defined?('@quux')
assert_equal 'baz', @dest.instance_variable_get('@baz')
end
@@ -144,8 +228,15 @@ class ObjectInstanceVariableTest < Test::Unit::TestCase
end
def test_instance_exec_passes_arguments_to_block
- block = Proc.new { |value| [self, value] }
- assert_equal %w(hello goodbye), 'hello'.instance_exec('goodbye', &block)
+ assert_equal %w(hello goodbye), 'hello'.instance_exec('goodbye') { |v| [self, v] }
end
+ def test_instance_exec_with_frozen_obj
+ assert_equal %w(olleh goodbye), 'hello'.freeze.instance_exec('goodbye') { |v| [reverse, v] }
+ end
+
+ def test_instance_exec_nested
+ assert_equal %w(goodbye olleh bar), 'hello'.instance_exec('goodbye') { |arg|
+ [arg] + instance_exec('bar') { |v| [reverse, v] } }
+ end
end
diff --git a/vendor/rails/activesupport/test/core_ext/range_ext_test.rb b/vendor/rails/activesupport/test/core_ext/range_ext_test.rb
index e8890ac8..acb6baf4 100644
--- a/vendor/rails/activesupport/test/core_ext/range_ext_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/range_ext_test.rb
@@ -10,4 +10,54 @@ class RangeTest < Test::Unit::TestCase
date_range = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30)
assert_equal "BETWEEN '2005-12-10 15:30:00' AND '2005-12-10 17:30:00'", date_range.to_s(:db)
end
+
+ def test_overlaps_last_inclusive
+ assert((1..5).overlaps?(5..10))
+ end
+
+ def test_overlaps_last_exclusive
+ assert !(1...5).overlaps?(5..10)
+ end
+
+ def test_overlaps_first_inclusive
+ assert((5..10).overlaps?(1..5))
+ end
+
+ def test_overlaps_first_exclusive
+ assert !(5..10).overlaps?(1...5)
+ end
+
+ def test_should_include_identical_inclusive
+ assert((1..10).include?(1..10))
+ end
+
+ def test_should_include_identical_exclusive
+ assert((1...10).include?(1...10))
+ end
+
+ def test_should_include_other_with_exlusive_end
+ assert((1..10).include?(1...10))
+ end
+
+ def test_exclusive_end_should_not_include_identical_with_inclusive_end
+ assert !(1...10).include?(1..10)
+ end
+
+ def test_should_not_include_overlapping_first
+ assert !(2..8).include?(1..3)
+ end
+
+ def test_should_not_include_overlapping_last
+ assert !(2..8).include?(5..9)
+ end
+
+ def test_blockless_step
+ assert_equal [1,3,5,7,9], (1..10).step(2)
+ end
+
+ def test_original_step
+ array = []
+ (1..10).step(2) {|i| array << i }
+ assert_equal [1,3,5,7,9], array
+ end
end
diff --git a/vendor/rails/activesupport/test/core_ext/string_ext_test.rb b/vendor/rails/activesupport/test/core_ext/string_ext_test.rb
index cd2d12c3..427e7dfc 100644
--- a/vendor/rails/activesupport/test/core_ext/string_ext_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/string_ext_test.rb
@@ -1,9 +1,12 @@
require 'date'
require File.dirname(__FILE__) + '/../abstract_unit'
+require 'inflector_test_cases'
class StringInflectionsTest < Test::Unit::TestCase
+ include InflectorTestCases
+
def test_pluralize
- InflectorTest::SingularToPlural.each do |singular, plural|
+ SingularToPlural.each do |singular, plural|
assert_equal(plural, singular.pluralize)
end
@@ -11,25 +14,25 @@ class StringInflectionsTest < Test::Unit::TestCase
end
def test_singularize
- InflectorTest::SingularToPlural.each do |singular, plural|
+ SingularToPlural.each do |singular, plural|
assert_equal(singular, plural.singularize)
end
end
def test_titleize
- InflectorTest::MixtureToTitleCase.each do |before, titleized|
+ MixtureToTitleCase.each do |before, titleized|
assert_equal(titleized, before.titleize)
end
end
def test_camelize
- InflectorTest::CamelToUnderscore.each do |camel, underscore|
+ CamelToUnderscore.each do |camel, underscore|
assert_equal(camel, underscore.camelize)
end
end
def test_underscore
- InflectorTest::CamelToUnderscore.each do |camel, underscore|
+ CamelToUnderscore.each do |camel, underscore|
assert_equal(underscore, camel.underscore)
end
@@ -38,7 +41,7 @@ class StringInflectionsTest < Test::Unit::TestCase
end
def test_underscore_to_lower_camel
- InflectorTest::UnderscoreToLowerCamel.each do |underscored, lower_camel|
+ UnderscoreToLowerCamel.each do |underscored, lower_camel|
assert_equal(lower_camel, underscored.camelize(:lower))
end
end
@@ -48,36 +51,52 @@ class StringInflectionsTest < Test::Unit::TestCase
end
def test_foreign_key
- InflectorTest::ClassNameToForeignKeyWithUnderscore.each do |klass, foreign_key|
+ ClassNameToForeignKeyWithUnderscore.each do |klass, foreign_key|
assert_equal(foreign_key, klass.foreign_key)
end
- InflectorTest::ClassNameToForeignKeyWithoutUnderscore.each do |klass, foreign_key|
+ ClassNameToForeignKeyWithoutUnderscore.each do |klass, foreign_key|
assert_equal(foreign_key, klass.foreign_key(false))
end
end
def test_tableize
- InflectorTest::ClassNameToTableName.each do |class_name, table_name|
+ ClassNameToTableName.each do |class_name, table_name|
assert_equal(table_name, class_name.tableize)
end
end
def test_classify
- InflectorTest::ClassNameToTableName.each do |class_name, table_name|
+ ClassNameToTableName.each do |class_name, table_name|
assert_equal(class_name, table_name.classify)
end
end
def test_humanize
- InflectorTest::UnderscoreToHuman.each do |underscore, human|
+ UnderscoreToHuman.each do |underscore, human|
assert_equal(human, underscore.humanize)
end
end
+ def test_ord
+ assert_equal 97, 'a'.ord
+ assert_equal 97, 'abc'.ord
+ end
+
def test_string_to_time
assert_equal Time.utc(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time
assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time(:local)
+ assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time
+ assert_equal Time.local_time(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time(:local)
+ end
+
+ def test_string_to_datetime
+ assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_datetime
+ assert_equal 0, "2039-02-27 23:50".to_datetime.offset # use UTC offset
+ assert_equal ::Date::ITALY, "2039-02-27 23:50".to_datetime.start # use Ruby's default start value
+ end
+
+ def test_string_to_date
assert_equal Date.new(2005, 2, 27), "2005-02-27".to_date
end
@@ -129,17 +148,26 @@ class StringInflectionsTest < Test::Unit::TestCase
assert_equal %w(hello), hash.keys
end
- def test_starts_ends_with
+ def test_starts_ends_with_alias
s = "hello"
assert s.starts_with?('h')
assert s.starts_with?('hel')
assert !s.starts_with?('el')
+ assert s.start_with?('h')
+ assert s.start_with?('hel')
+ assert !s.start_with?('el')
+
assert s.ends_with?('o')
assert s.ends_with?('lo')
assert !s.ends_with?('el')
+
+ assert s.end_with?('o')
+ assert s.end_with?('lo')
+ assert !s.end_with?('el')
end
+ # FIXME: Ruby 1.9
def test_each_char_with_utf8_string_when_kcode_is_utf8
old_kcode, $KCODE = $KCODE, 'UTF8'
'€2.99'.each_char do |char|
diff --git a/vendor/rails/activesupport/test/core_ext/time_ext_test.rb b/vendor/rails/activesupport/test/core_ext/time_ext_test.rb
index 657be1ae..c0bd12d2 100644
--- a/vendor/rails/activesupport/test/core_ext/time_ext_test.rb
+++ b/vendor/rails/activesupport/test/core_ext/time_ext_test.rb
@@ -49,7 +49,7 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
end
end
- def test_begining_of_week
+ def test_beginning_of_week
assert_equal Time.local(2005,1,31), Time.local(2005,2,4,10,10,10).beginning_of_week
assert_equal Time.local(2005,11,28), Time.local(2005,11,28,0,0,0).beginning_of_week #monday
assert_equal Time.local(2005,11,28), Time.local(2005,11,29,0,0,0).beginning_of_week #tuesday
@@ -84,10 +84,9 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
end
def test_end_of_month
- assert_equal Time.local(2005,3,31,0,0,0), Time.local(2005,3,20,10,10,10).end_of_month
- assert_equal Time.local(2005,2,28,0,0,0), Time.local(2005,2,20,10,10,10).end_of_month
- assert_equal Time.local(2005,4,30,0,0,0), Time.local(2005,4,20,10,10,10).end_of_month
-
+ assert_equal Time.local(2005,3,31,23,59,59), Time.local(2005,3,20,10,10,10).end_of_month
+ assert_equal Time.local(2005,2,28,23,59,59), Time.local(2005,2,20,10,10,10).end_of_month
+ assert_equal Time.local(2005,4,30,23,59,59), Time.local(2005,4,20,10,10,10).end_of_month
end
def test_beginning_of_year
@@ -110,16 +109,22 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_equal Time.local(2006,1,5,10), Time.local(2005,6,5,10,0,0).months_since(7)
assert_equal Time.local(2006,6,5,10), Time.local(2005,6,5,10,0,0).months_since(12)
assert_equal Time.local(2007,6,5,10), Time.local(2005,6,5,10,0,0).months_since(24)
+ assert_equal Time.local(2005,4,30,10), Time.local(2005,3,31,10,0,0).months_since(1)
+ assert_equal Time.local(2005,2,28,10), Time.local(2005,1,29,10,0,0).months_since(1)
+ assert_equal Time.local(2005,2,28,10), Time.local(2005,1,30,10,0,0).months_since(1)
+ assert_equal Time.local(2005,2,28,10), Time.local(2005,1,31,10,0,0).months_since(1)
end
def test_years_ago
assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).years_ago(1)
assert_equal Time.local(1998,6,5,10), Time.local(2005,6,5,10,0,0).years_ago(7)
+ assert_equal Time.local(2003,2,28,10), Time.local(2004,2,29,10,0,0).years_ago(1) # 1 year ago from leap day
end
def test_years_since
assert_equal Time.local(2006,6,5,10), Time.local(2005,6,5,10,0,0).years_since(1)
assert_equal Time.local(2012,6,5,10), Time.local(2005,6,5,10,0,0).years_since(7)
+ assert_equal Time.local(2005,2,28,10), Time.local(2004,2,29,10,0,0).years_since(1) # 1 year since leap day
# Failure because of size limitations of numeric?
# assert_equal Time.local(2182,6,5,10), Time.local(2005,6,5,10,0,0).years_since(177)
end
@@ -128,6 +133,9 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).last_year
end
+ def test_next_year
+ assert_equal Time.local(2006,6,5,10), Time.local(2005,6,5,10,0,0).next_year
+ end
def test_ago
assert_equal Time.local(2005,2,22,10,10,9), Time.local(2005,2,22,10,10,10).ago(1)
@@ -167,6 +175,8 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_equal Time.local(2005,2,22,11,10,10), Time.local(2005,2,22,10,10,10).since(3600)
assert_equal Time.local(2005,2,24,10,10,10), Time.local(2005,2,22,10,10,10).since(86400*2)
assert_equal Time.local(2005,2,24,11,10,35), Time.local(2005,2,22,10,10,10).since(86400*2 + 3600 + 25)
+ # when out of range of Time, returns a DateTime
+ assert_equal DateTime.civil(2038,1,20,11,59,59), Time.utc(2038,1,18,11,59,59).since(86400*2)
end
def test_daylight_savings_time_crossings_forward_start
@@ -228,26 +238,50 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_equal Time.utc(2005,2,22,15,45), Time.utc(2005,2,22,15,15,10).change(:min => 45)
end
- def test_plus
+ def test_advance
assert_equal Time.local(2006,2,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 1)
assert_equal Time.local(2005,6,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:months => 4)
+ assert_equal Time.local(2005,3,21,15,15,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3)
+ assert_equal Time.local(2005,3,5,15,15,10), Time.local(2005,2,28,15,15,10).advance(:days => 5)
assert_equal Time.local(2012,9,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 7)
assert_equal Time.local(2013,10,3,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :days => 5)
+ assert_equal Time.local(2013,10,17,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5)
+ assert_equal Time.local(2001,12,27,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => -3, :months => -2, :days => -1)
+ assert_equal Time.local(2005,2,28,15,15,10), Time.local(2004,2,29,15,15,10).advance(:years => 1) #leap day plus one year
+ assert_equal Time.local(2005,2,28,20,15,10), Time.local(2005,2,28,15,15,10).advance(:hours => 5)
+ assert_equal Time.local(2005,2,28,15,22,10), Time.local(2005,2,28,15,15,10).advance(:minutes => 7)
+ assert_equal Time.local(2005,2,28,15,15,19), Time.local(2005,2,28,15,15,10).advance(:seconds => 9)
+ assert_equal Time.local(2005,2,28,20,22,19), Time.local(2005,2,28,15,15,10).advance(:hours => 5, :minutes => 7, :seconds => 9)
+ assert_equal Time.local(2005,2,28,10,8,1), Time.local(2005,2,28,15,15,10).advance(:hours => -5, :minutes => -7, :seconds => -9)
+ assert_equal Time.local(2013,10,17,20,22,19), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5, :hours => 5, :minutes => 7, :seconds => 9)
end
- def test_utc_plus
+ def test_utc_advance
assert_equal Time.utc(2006,2,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 1)
assert_equal Time.utc(2005,6,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:months => 4)
+ assert_equal Time.utc(2005,3,21,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3)
+ assert_equal Time.utc(2005,3,5,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5)
assert_equal Time.utc(2012,9,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 7, :months => 7)
assert_equal Time.utc(2013,10,3,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 7, :months => 19, :days => 11)
+ assert_equal Time.utc(2013,10,17,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5)
+ assert_equal Time.utc(2001,12,27,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:years => -3, :months => -2, :days => -1)
+ assert_equal Time.utc(2005,2,28,15,15,10), Time.utc(2004,2,29,15,15,10).advance(:years => 1) #leap day plus one year
+ assert_equal Time.utc(2005,2,28,20,15,10), Time.utc(2005,2,28,15,15,10).advance(:hours => 5)
+ assert_equal Time.utc(2005,2,28,15,22,10), Time.utc(2005,2,28,15,15,10).advance(:minutes => 7)
+ assert_equal Time.utc(2005,2,28,15,15,19), Time.utc(2005,2,28,15,15,10).advance(:seconds => 9)
+ assert_equal Time.utc(2005,2,28,20,22,19), Time.utc(2005,2,28,15,15,10).advance(:hours => 5, :minutes => 7, :seconds => 9)
+ assert_equal Time.utc(2005,2,28,10,8,1), Time.utc(2005,2,28,15,15,10).advance(:hours => -5, :minutes => -7, :seconds => -9)
+ assert_equal Time.utc(2013,10,17,20,22,19), Time.utc(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5, :hours => 5, :minutes => 7, :seconds => 9)
end
def test_next_week
- assert_equal Time.local(2005,2,28), Time.local(2005,2,22,15,15,10).next_week
- assert_equal Time.local(2005,2,29), Time.local(2005,2,22,15,15,10).next_week(:tuesday)
- assert_equal Time.local(2005,3,4), Time.local(2005,2,22,15,15,10).next_week(:friday)
- assert_equal Time.local(2006,10,30), Time.local(2006,10,23,0,0,0).next_week
- assert_equal Time.local(2006,11,1), Time.local(2006,10,23,0,0,0).next_week(:wednesday)
+ with_timezone 'US/Eastern' do
+ assert_equal Time.local(2005,2,28), Time.local(2005,2,22,15,15,10).next_week
+ assert_equal Time.local(2005,2,29), Time.local(2005,2,22,15,15,10).next_week(:tuesday)
+ assert_equal Time.local(2005,3,4), Time.local(2005,2,22,15,15,10).next_week(:friday)
+ assert_equal Time.local(2006,10,30), Time.local(2006,10,23,0,0,0).next_week
+ assert_equal Time.local(2006,11,1), Time.local(2006,10,23,0,0,0).next_week(:wednesday)
+ end
end
def test_next_week_near_daylight_start
@@ -269,19 +303,40 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
end
def test_to_s
- time = Time.local(2005, 2, 21, 17, 44, 30)
- assert_equal "2005-02-21 17:44:30", time.to_s(:db)
- assert_equal "21 Feb 17:44", time.to_s(:short)
- assert_equal "February 21, 2005 17:44", time.to_s(:long)
-
time = Time.utc(2005, 2, 21, 17, 44, 30)
- assert_equal "Mon, 21 Feb 2005 17:44:30 +0000", time.to_s(:rfc822)
+ assert_equal time.to_default_s, time.to_s
+ assert_equal time.to_default_s, time.to_s(:doesnt_exist)
+ assert_equal "2005-02-21 17:44:30", time.to_s(:db)
+ assert_equal "21 Feb 17:44", time.to_s(:short)
+ assert_equal "17:44", time.to_s(:time)
+ assert_equal "February 21, 2005 17:44", time.to_s(:long)
+ assert_equal "February 21st, 2005 17:44", time.to_s(:long_ordinal)
+ with_timezone "UTC" do
+ assert_equal "Mon, 21 Feb 2005 17:44:30 +0000", time.to_s(:rfc822)
+ end
+ end
+
+ def test_custom_date_format
+ Time::DATE_FORMATS[:custom] = '%Y%m%d%H%M%S'
+ assert_equal '20050221143000', Time.local(2005, 2, 21, 14, 30, 0).to_s(:custom)
+ Time::DATE_FORMATS.delete(:custom)
end
def test_to_date
assert_equal Date.new(2005, 2, 21), Time.local(2005, 2, 21, 17, 44, 30).to_date
end
+ def test_to_datetime
+ assert_equal Time.utc(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, 0, 0)
+ with_timezone 'US/Eastern' do
+ assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400), 0)
+ end
+ with_timezone 'NZ' do
+ assert_equal Time.local(2005, 2, 21, 17, 44, 30).to_datetime, DateTime.civil(2005, 2, 21, 17, 44, 30, Rational(Time.local(2005, 2, 21, 17, 44, 30).utc_offset, 86400), 0)
+ end
+ assert_equal ::Date::ITALY, Time.utc(2005, 2, 21, 17, 44, 30).to_datetime.start # use Ruby's default start value
+ end
+
def test_to_time
assert_equal Time.local(2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30).to_time
end
@@ -313,6 +368,32 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_equal 31, Time.days_in_month(12, 2005)
end
+ def test_time_with_datetime_fallback
+ assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30), Time.utc(2005, 2, 21, 17, 44, 30)
+ assert_equal Time.time_with_datetime_fallback(:local, 2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30)
+ assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0)
+ assert_equal Time.time_with_datetime_fallback(:local, 2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, DateTime.now.offset, 0)
+ assert_equal Time.time_with_datetime_fallback(:utc, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, 0, 0)
+ assert_equal Time.time_with_datetime_fallback(:local, 1900, 2, 21, 17, 44, 30), DateTime.civil(1900, 2, 21, 17, 44, 30, DateTime.now.offset, 0)
+ assert_equal Time.time_with_datetime_fallback(:utc, 2005), Time.utc(2005)
+ assert_equal Time.time_with_datetime_fallback(:utc, 2039), DateTime.civil(2039, 1, 1, 0, 0, 0, 0, 0)
+ assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30, 1), Time.utc(2005, 2, 21, 17, 44, 30, 1) #with usec
+ assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0)
+ assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value
+ end
+
+ def test_utc_time
+ assert_equal Time.utc_time(2005, 2, 21, 17, 44, 30), Time.utc(2005, 2, 21, 17, 44, 30)
+ assert_equal Time.utc_time(2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0)
+ assert_equal Time.utc_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, 0, 0)
+ end
+
+ def test_local_time
+ assert_equal Time.local_time(2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30)
+ assert_equal Time.local_time(2039, 2, 21, 17, 44, 30), DateTime.civil(2039, 2, 21, 17, 44, 30, DateTime.local_offset, 0)
+ assert_equal Time.local_time(1901, 2, 21, 17, 44, 30), DateTime.civil(1901, 2, 21, 17, 44, 30, DateTime.local_offset, 0)
+ end
+
def test_next_month_on_31st
assert_equal Time.local(2005, 9, 30), Time.local(2005, 8, 31).next_month
end
@@ -325,11 +406,15 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_nothing_raised { Time.now.xmlschema }
end
+ def test_acts_like_time
+ assert Time.new.acts_like_time?
+ end
+
protected
- def with_timezone new_tz='US/Eastern'
+ def with_timezone(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz
yield
ensure
- ENV['TZ'] = old_tz
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
end
end
diff --git a/vendor/rails/activesupport/test/dependencies_test.rb b/vendor/rails/activesupport/test/dependencies_test.rb
index ad3472bd..76f8453c 100644
--- a/vendor/rails/activesupport/test/dependencies_test.rb
+++ b/vendor/rails/activesupport/test/dependencies_test.rb
@@ -146,42 +146,42 @@ class DependenciesTest < Test::Unit::TestCase
def test_directories_manifest_as_modules_unless_const_defined
with_loading 'autoloading_fixtures' do
assert_kind_of Module, ModuleFolder
- Object.send :remove_const, :ModuleFolder
+ Object.send! :remove_const, :ModuleFolder
end
end
def test_module_with_nested_class
with_loading 'autoloading_fixtures' do
assert_kind_of Class, ModuleFolder::NestedClass
- Object.send :remove_const, :ModuleFolder
+ Object.send! :remove_const, :ModuleFolder
end
end
def test_module_with_nested_inline_class
with_loading 'autoloading_fixtures' do
assert_kind_of Class, ModuleFolder::InlineClass
- Object.send :remove_const, :ModuleFolder
+ Object.send! :remove_const, :ModuleFolder
end
end
def test_directories_may_manifest_as_nested_classes
with_loading 'autoloading_fixtures' do
assert_kind_of Class, ClassFolder
- Object.send :remove_const, :ClassFolder
+ Object.send! :remove_const, :ClassFolder
end
end
def test_class_with_nested_class
with_loading 'autoloading_fixtures' do
assert_kind_of Class, ClassFolder::NestedClass
- Object.send :remove_const, :ClassFolder
+ Object.send! :remove_const, :ClassFolder
end
end
def test_class_with_nested_inline_class
with_loading 'autoloading_fixtures' do
assert_kind_of Class, ClassFolder::InlineClass
- Object.send :remove_const, :ClassFolder
+ Object.send! :remove_const, :ClassFolder
end
end
@@ -190,7 +190,7 @@ class DependenciesTest < Test::Unit::TestCase
assert_kind_of Class, ClassFolder::ClassFolderSubclass
assert_kind_of Class, ClassFolder
assert_equal 'indeed', ClassFolder::ClassFolderSubclass::ConstantInClassFolder
- Object.send :remove_const, :ClassFolder
+ Object.send! :remove_const, :ClassFolder
end
end
@@ -199,7 +199,7 @@ class DependenciesTest < Test::Unit::TestCase
sibling = ModuleFolder::NestedClass.class_eval "NestedSibling"
assert defined?(ModuleFolder::NestedSibling)
assert_equal ModuleFolder::NestedSibling, sibling
- Object.send :remove_const, :ModuleFolder
+ Object.send! :remove_const, :ModuleFolder
end
end
@@ -208,7 +208,7 @@ class DependenciesTest < Test::Unit::TestCase
assert ! defined?(ModuleFolder)
assert_raises(NameError) { ModuleFolder::Object }
assert_raises(NameError) { ModuleFolder::NestedClass::Object }
- Object.send :remove_const, :ModuleFolder
+ Object.send! :remove_const, :ModuleFolder
end
end
@@ -316,7 +316,7 @@ class DependenciesTest < Test::Unit::TestCase
def nil_name.name() nil end
assert !Dependencies.autoloaded?(nil_name)
- Object.send :remove_const, :ModuleFolder
+ Object.class_eval { remove_const :ModuleFolder }
end
end
@@ -440,7 +440,7 @@ class DependenciesTest < Test::Unit::TestCase
assert ! Dependencies.autoloaded?(ModuleFolder::NestedClass)
end
ensure
- Object.send(:remove_const, :ModuleFolder) if defined?(ModuleFolder)
+ Object.class_eval { remove_const :ModuleFolder }
Dependencies.load_once_paths = []
end
@@ -475,7 +475,7 @@ class DependenciesTest < Test::Unit::TestCase
end
ensure
- Object.send :remove_const, :E if Object.const_defined?(:E)
+ Object.class_eval { remove_const :E }
end
def test_unloadable
@@ -513,31 +513,30 @@ class DependenciesTest < Test::Unit::TestCase
end
def test_new_constants_in_with_a_single_constant
- assert_equal(["Hello"], (Dependencies.new_constants_in(Object) do
- Object.const_set :Hello, 10
- end))
+ assert_equal ["Hello"], Dependencies.new_constants_in(Object) {
+ Object.const_set :Hello, 10
+ }.map(&:to_s)
assert Dependencies.constant_watch_stack.empty?
ensure
- Object.send :remove_const, :Hello rescue nil
+ Object.class_eval { remove_const :Hello }
end
def test_new_constants_in_with_nesting
outer = Dependencies.new_constants_in(Object) do
Object.const_set :OuterBefore, 10
- inner = Dependencies.new_constants_in(Object) do
- Object.const_set :Inner, 20
- end
- assert_equal ["Inner"], inner
+ assert_equal ["Inner"], Dependencies.new_constants_in(Object) {
+ Object.const_set :Inner, 20
+ }.map(&:to_s)
Object.const_set :OuterAfter, 30
end
- assert_equal ["OuterAfter", "OuterBefore"], outer.sort
+ assert_equal ["OuterAfter", "OuterBefore"], outer.sort.map(&:to_s)
assert Dependencies.constant_watch_stack.empty?
ensure
%w(OuterBefore Inner OuterAfter).each do |name|
- Object.send :remove_const, name rescue nil
+ Object.class_eval { remove_const name if const_defined?(name) }
end
end
@@ -557,7 +556,7 @@ class DependenciesTest < Test::Unit::TestCase
assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort
assert Dependencies.constant_watch_stack.empty?
ensure
- Object.send :remove_const, :M rescue nil
+ Object.class_eval { remove_const :M }
end
def test_new_constants_in_module_using_name
@@ -575,12 +574,12 @@ class DependenciesTest < Test::Unit::TestCase
assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort
assert Dependencies.constant_watch_stack.empty?
ensure
- Object.send :remove_const, :M rescue nil
+ Object.class_eval { remove_const :M }
end
def test_new_constants_in_with_inherited_constants
m = Dependencies.new_constants_in(:Object) do
- Object.send :include, ModuleWithConstant
+ Object.class_eval { include ModuleWithConstant }
end
assert_equal [], m
end
@@ -670,7 +669,7 @@ class DependenciesTest < Test::Unit::TestCase
end
ensure
- Object.send(:remove_const, :RaisesNoMethodError) if defined?(::RaisesNoMethodError)
+ Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) }
end
def test_autoload_doesnt_shadow_no_method_error_with_absolute_constant
@@ -683,7 +682,7 @@ class DependenciesTest < Test::Unit::TestCase
end
ensure
- Object.send(:remove_const, :RaisesNoMethodError) if defined?(::RaisesNoMethodError)
+ Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) }
end
def test_autoload_doesnt_shadow_name_error
@@ -707,16 +706,16 @@ class DependenciesTest < Test::Unit::TestCase
end
ensure
- Object.send(:remove_const, :RaisesNameError) if defined?(::RaisesNameError)
+ Object.class_eval { remove_const :RaisesNoMethodError if const_defined?(:RaisesNoMethodError) }
end
def test_remove_constant_handles_double_colon_at_start
Object.const_set 'DeleteMe', Module.new
DeleteMe.const_set 'OrMe', Module.new
- Dependencies.send :remove_constant, "::DeleteMe::OrMe"
+ Dependencies.remove_constant "::DeleteMe::OrMe"
assert ! defined?(DeleteMe::OrMe)
assert defined?(DeleteMe)
- Dependencies.send :remove_constant, "::DeleteMe"
+ Dependencies.remove_constant "::DeleteMe"
assert ! defined?(DeleteMe)
end
@@ -730,7 +729,7 @@ class DependenciesTest < Test::Unit::TestCase
end
ensure
Dependencies.load_once_paths = []
- Object.send :remove_const, :A rescue nil
+ Object.class_eval { remove_const :A if const_defined?(:A) }
end
def test_load_once_paths_should_behave_when_recursively_loading
diff --git a/vendor/rails/activesupport/test/inflector_test.rb b/vendor/rails/activesupport/test/inflector_test.rb
index c5f5b106..b0155d36 100644
--- a/vendor/rails/activesupport/test/inflector_test.rb
+++ b/vendor/rails/activesupport/test/inflector_test.rb
@@ -1,4 +1,5 @@
require File.dirname(__FILE__) + '/abstract_unit'
+require 'inflector_test_cases'
module Ace
module Base
@@ -8,214 +9,17 @@ module Ace
end
class InflectorTest < Test::Unit::TestCase
- SingularToPlural = {
- "search" => "searches",
- "switch" => "switches",
- "fix" => "fixes",
- "box" => "boxes",
- "process" => "processes",
- "address" => "addresses",
- "case" => "cases",
- "stack" => "stacks",
- "wish" => "wishes",
- "fish" => "fish",
-
- "category" => "categories",
- "query" => "queries",
- "ability" => "abilities",
- "agency" => "agencies",
- "movie" => "movies",
-
- "archive" => "archives",
-
- "index" => "indices",
-
- "wife" => "wives",
- "safe" => "saves",
- "half" => "halves",
-
- "move" => "moves",
-
- "salesperson" => "salespeople",
- "person" => "people",
-
- "spokesman" => "spokesmen",
- "man" => "men",
- "woman" => "women",
-
- "basis" => "bases",
- "diagnosis" => "diagnoses",
-
- "datum" => "data",
- "medium" => "media",
- "analysis" => "analyses",
-
- "node_child" => "node_children",
- "child" => "children",
-
- "experience" => "experiences",
- "day" => "days",
-
- "comment" => "comments",
- "foobar" => "foobars",
- "newsletter" => "newsletters",
-
- "old_news" => "old_news",
- "news" => "news",
-
- "series" => "series",
- "species" => "species",
-
- "quiz" => "quizzes",
-
- "perspective" => "perspectives",
-
- "ox" => "oxen",
- "photo" => "photos",
- "buffalo" => "buffaloes",
- "tomato" => "tomatoes",
- "dwarf" => "dwarves",
- "elf" => "elves",
- "information" => "information",
- "equipment" => "equipment",
- "bus" => "buses",
- "status" => "statuses",
- "status_code" => "status_codes",
- "mouse" => "mice",
-
- "louse" => "lice",
- "house" => "houses",
- "octopus" => "octopi",
- "virus" => "viri",
- "alias" => "aliases",
- "portfolio" => "portfolios",
-
- "vertex" => "vertices",
- "matrix" => "matrices",
-
- "axis" => "axes",
- "testis" => "testes",
- "crisis" => "crises",
-
- "rice" => "rice",
- "shoe" => "shoes",
-
- "horse" => "horses",
- "prize" => "prizes",
- "edge" => "edges"
- }
-
- CamelToUnderscore = {
- "Product" => "product",
- "SpecialGuest" => "special_guest",
- "ApplicationController" => "application_controller",
- "Area51Controller" => "area51_controller"
- }
-
- UnderscoreToLowerCamel = {
- "product" => "product",
- "special_guest" => "specialGuest",
- "application_controller" => "applicationController",
- "area51_controller" => "area51Controller"
- }
-
- CamelToUnderscoreWithoutReverse = {
- "HTMLTidy" => "html_tidy",
- "HTMLTidyGenerator" => "html_tidy_generator",
- "FreeBSD" => "free_bsd",
- "HTML" => "html",
- }
-
- CamelWithModuleToUnderscoreWithSlash = {
- "Admin::Product" => "admin/product",
- "Users::Commission::Department" => "users/commission/department",
- "UsersSection::CommissionDepartment" => "users_section/commission_department",
- }
-
- ClassNameToForeignKeyWithUnderscore = {
- "Person" => "person_id",
- "MyApplication::Billing::Account" => "account_id"
- }
-
- ClassNameToForeignKeyWithoutUnderscore = {
- "Person" => "personid",
- "MyApplication::Billing::Account" => "accountid"
- }
-
- ClassNameToTableName = {
- "PrimarySpokesman" => "primary_spokesmen",
- "NodeChild" => "node_children"
- }
-
- UnderscoreToHuman = {
- "employee_salary" => "Employee salary",
- "employee_id" => "Employee",
- "underground" => "Underground"
- }
-
- MixtureToTitleCase = {
- 'active_record' => 'Active Record',
- 'ActiveRecord' => 'Active Record',
- 'action web service' => 'Action Web Service',
- 'Action Web Service' => 'Action Web Service',
- 'Action web service' => 'Action Web Service',
- 'actionwebservice' => 'Actionwebservice',
- 'Actionwebservice' => 'Actionwebservice'
- }
-
- OrdinalNumbers = {
- "0" => "0th",
- "1" => "1st",
- "2" => "2nd",
- "3" => "3rd",
- "4" => "4th",
- "5" => "5th",
- "6" => "6th",
- "7" => "7th",
- "8" => "8th",
- "9" => "9th",
- "10" => "10th",
- "11" => "11th",
- "12" => "12th",
- "13" => "13th",
- "14" => "14th",
- "20" => "20th",
- "21" => "21st",
- "22" => "22nd",
- "23" => "23rd",
- "24" => "24th",
- "100" => "100th",
- "101" => "101st",
- "102" => "102nd",
- "103" => "103rd",
- "104" => "104th",
- "110" => "110th",
- "111" => "111th",
- "112" => "112th",
- "113" => "113th",
- "1000" => "1000th",
- "1001" => "1001st"
- }
-
- UnderscoresToDashes = {
- "street" => "street",
- "street_address" => "street-address",
- "person_street_address" => "person-street-address"
- }
-
- Irregularities = {
- 'person' => 'people',
- 'man' => 'men',
- 'child' => 'children',
- 'sex' => 'sexes',
- 'move' => 'moves',
- }
+ include InflectorTestCases
def test_pluralize_plurals
assert_equal "plurals", Inflector.pluralize("plurals")
assert_equal "Plurals", Inflector.pluralize("Plurals")
end
+ def test_pluralize_empty_string
+ assert_equal "", Inflector.pluralize("")
+ end
+
SingularToPlural.each do |singular, plural|
define_method "test_pluralize_#{singular}" do
assert_equal(plural, Inflector.pluralize(singular))
@@ -286,12 +90,13 @@ class InflectorTest < Test::Unit::TestCase
def test_classify
ClassNameToTableName.each do |class_name, table_name|
assert_equal(class_name, Inflector.classify(table_name))
+ assert_equal(class_name, Inflector.classify("table_prefix." + table_name))
end
end
def test_classify_with_symbol
assert_nothing_raised do
- assert_equal 'FooBar', Inflector.classify(:foo_bar)
+ assert_equal 'FooBar', Inflector.classify(:foo_bars)
end
end
@@ -315,8 +120,14 @@ class InflectorTest < Test::Unit::TestCase
assert_raises(NameError) { Inflector.constantize("InvalidClass\n") }
end
- def test_constantize_doesnt_look_in_parent
- assert_raises(NameError) { Inflector.constantize("Ace::Base::InflectorTest") }
+ if RUBY_VERSION < '1.9.0'
+ def test_constantize_does_lexical_lookup
+ assert_raises(NameError) { Inflector.constantize("Ace::Base::InflectorTest") }
+ end
+ else
+ def test_constantize_does_dynamic_lookup
+ assert_equal self.class, Inflector.constantize("Ace::Base::InflectorTest")
+ end
end
def test_ordinal
@@ -342,6 +153,39 @@ class InflectorTest < Test::Unit::TestCase
assert_equal(lower_camel, Inflector.camelize(underscored, false))
end
end
+
+ %w{plurals singulars uncountables}.each do |inflection_type|
+ class_eval "
+ def test_clear_#{inflection_type}
+ cached_values = Inflector.inflections.#{inflection_type}
+ Inflector.inflections.clear :#{inflection_type}
+ assert Inflector.inflections.#{inflection_type}.empty?, \"#{inflection_type} inflections should be empty after clear :#{inflection_type}\"
+ Inflector.inflections.instance_variable_set :@#{inflection_type}, cached_values
+ end
+ "
+ end
+
+ def test_clear_all
+ cached_values = Inflector.inflections.plurals, Inflector.inflections.singulars, Inflector.inflections.uncountables
+ Inflector.inflections.clear :all
+ assert Inflector.inflections.plurals.empty?
+ assert Inflector.inflections.singulars.empty?
+ assert Inflector.inflections.uncountables.empty?
+ Inflector.inflections.instance_variable_set :@plurals, cached_values[0]
+ Inflector.inflections.instance_variable_set :@singulars, cached_values[1]
+ Inflector.inflections.instance_variable_set :@uncountables, cached_values[2]
+ end
+
+ def test_clear_with_default
+ cached_values = Inflector.inflections.plurals, Inflector.inflections.singulars, Inflector.inflections.uncountables
+ Inflector.inflections.clear
+ assert Inflector.inflections.plurals.empty?
+ assert Inflector.inflections.singulars.empty?
+ assert Inflector.inflections.uncountables.empty?
+ Inflector.inflections.instance_variable_set :@plurals, cached_values[0]
+ Inflector.inflections.instance_variable_set :@singulars, cached_values[1]
+ Inflector.inflections.instance_variable_set :@uncountables, cached_values[2]
+ end
Irregularities.each do |irregularity|
singular, plural = *irregularity
diff --git a/vendor/rails/activesupport/test/inflector_test_cases.rb b/vendor/rails/activesupport/test/inflector_test_cases.rb
new file mode 100644
index 00000000..83fd4c8e
--- /dev/null
+++ b/vendor/rails/activesupport/test/inflector_test_cases.rb
@@ -0,0 +1,208 @@
+module InflectorTestCases
+ SingularToPlural = {
+ "search" => "searches",
+ "switch" => "switches",
+ "fix" => "fixes",
+ "box" => "boxes",
+ "process" => "processes",
+ "address" => "addresses",
+ "case" => "cases",
+ "stack" => "stacks",
+ "wish" => "wishes",
+ "fish" => "fish",
+
+ "category" => "categories",
+ "query" => "queries",
+ "ability" => "abilities",
+ "agency" => "agencies",
+ "movie" => "movies",
+
+ "archive" => "archives",
+
+ "index" => "indices",
+
+ "wife" => "wives",
+ "safe" => "saves",
+ "half" => "halves",
+
+ "move" => "moves",
+
+ "salesperson" => "salespeople",
+ "person" => "people",
+
+ "spokesman" => "spokesmen",
+ "man" => "men",
+ "woman" => "women",
+
+ "basis" => "bases",
+ "diagnosis" => "diagnoses",
+ "diagnosis_a" => "diagnosis_as",
+
+ "datum" => "data",
+ "medium" => "media",
+ "analysis" => "analyses",
+
+ "node_child" => "node_children",
+ "child" => "children",
+
+ "experience" => "experiences",
+ "day" => "days",
+
+ "comment" => "comments",
+ "foobar" => "foobars",
+ "newsletter" => "newsletters",
+
+ "old_news" => "old_news",
+ "news" => "news",
+
+ "series" => "series",
+ "species" => "species",
+
+ "quiz" => "quizzes",
+
+ "perspective" => "perspectives",
+
+ "ox" => "oxen",
+ "photo" => "photos",
+ "buffalo" => "buffaloes",
+ "tomato" => "tomatoes",
+ "dwarf" => "dwarves",
+ "elf" => "elves",
+ "information" => "information",
+ "equipment" => "equipment",
+ "bus" => "buses",
+ "status" => "statuses",
+ "status_code" => "status_codes",
+ "mouse" => "mice",
+
+ "louse" => "lice",
+ "house" => "houses",
+ "octopus" => "octopi",
+ "virus" => "viri",
+ "alias" => "aliases",
+ "portfolio" => "portfolios",
+
+ "vertex" => "vertices",
+ "matrix" => "matrices",
+ "matrix_fu" => "matrix_fus",
+
+ "axis" => "axes",
+ "testis" => "testes",
+ "crisis" => "crises",
+
+ "rice" => "rice",
+ "shoe" => "shoes",
+
+ "horse" => "horses",
+ "prize" => "prizes",
+ "edge" => "edges",
+
+ "cow" => "kine"
+ }
+
+ CamelToUnderscore = {
+ "Product" => "product",
+ "SpecialGuest" => "special_guest",
+ "ApplicationController" => "application_controller",
+ "Area51Controller" => "area51_controller"
+ }
+
+ UnderscoreToLowerCamel = {
+ "product" => "product",
+ "special_guest" => "specialGuest",
+ "application_controller" => "applicationController",
+ "area51_controller" => "area51Controller"
+ }
+
+ CamelToUnderscoreWithoutReverse = {
+ "HTMLTidy" => "html_tidy",
+ "HTMLTidyGenerator" => "html_tidy_generator",
+ "FreeBSD" => "free_bsd",
+ "HTML" => "html",
+ }
+
+ CamelWithModuleToUnderscoreWithSlash = {
+ "Admin::Product" => "admin/product",
+ "Users::Commission::Department" => "users/commission/department",
+ "UsersSection::CommissionDepartment" => "users_section/commission_department",
+ }
+
+ ClassNameToForeignKeyWithUnderscore = {
+ "Person" => "person_id",
+ "MyApplication::Billing::Account" => "account_id"
+ }
+
+ ClassNameToForeignKeyWithoutUnderscore = {
+ "Person" => "personid",
+ "MyApplication::Billing::Account" => "accountid"
+ }
+
+ ClassNameToTableName = {
+ "PrimarySpokesman" => "primary_spokesmen",
+ "NodeChild" => "node_children"
+ }
+
+ UnderscoreToHuman = {
+ "employee_salary" => "Employee salary",
+ "employee_id" => "Employee",
+ "underground" => "Underground"
+ }
+
+ MixtureToTitleCase = {
+ 'active_record' => 'Active Record',
+ 'ActiveRecord' => 'Active Record',
+ 'action web service' => 'Action Web Service',
+ 'Action Web Service' => 'Action Web Service',
+ 'Action web service' => 'Action Web Service',
+ 'actionwebservice' => 'Actionwebservice',
+ 'Actionwebservice' => 'Actionwebservice'
+ }
+
+ OrdinalNumbers = {
+ "0" => "0th",
+ "1" => "1st",
+ "2" => "2nd",
+ "3" => "3rd",
+ "4" => "4th",
+ "5" => "5th",
+ "6" => "6th",
+ "7" => "7th",
+ "8" => "8th",
+ "9" => "9th",
+ "10" => "10th",
+ "11" => "11th",
+ "12" => "12th",
+ "13" => "13th",
+ "14" => "14th",
+ "20" => "20th",
+ "21" => "21st",
+ "22" => "22nd",
+ "23" => "23rd",
+ "24" => "24th",
+ "100" => "100th",
+ "101" => "101st",
+ "102" => "102nd",
+ "103" => "103rd",
+ "104" => "104th",
+ "110" => "110th",
+ "111" => "111th",
+ "112" => "112th",
+ "113" => "113th",
+ "1000" => "1000th",
+ "1001" => "1001st"
+ }
+
+ UnderscoresToDashes = {
+ "street" => "street",
+ "street_address" => "street-address",
+ "person_street_address" => "person-street-address"
+ }
+
+ Irregularities = {
+ 'person' => 'people',
+ 'man' => 'men',
+ 'child' => 'children',
+ 'sex' => 'sexes',
+ 'move' => 'moves',
+ }
+end
diff --git a/vendor/rails/activesupport/test/json.rb b/vendor/rails/activesupport/test/json.rb
deleted file mode 100644
index 0274dd07..00000000
--- a/vendor/rails/activesupport/test/json.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-class Foo
- def initialize(a, b)
- @a, @b = a, b
- end
-end
-
-class TestJSONEmitters < Test::Unit::TestCase
- TrueTests = [[ true, %(true) ]]
- FalseTests = [[ false, %(false) ]]
- NilTests = [[ nil, %(null) ]]
- NumericTests = [[ 1, %(1) ],
- [ 2.5, %(2.5) ]]
-
- StringTests = [[ 'this is the string', %("this is the string") ],
- [ 'a "string" with quotes', %("a \\"string\\" with quotes") ]]
-
- ArrayTests = [[ ['a', 'b', 'c'], %([\"a\", \"b\", \"c\"]) ],
- [ [1, 'a', :b, nil, false], %([1, \"a\", \"b\", null, false]) ]]
-
- SymbolTests = [[ :a, %("a") ],
- [ :this, %("this") ],
- [ :"a b", %("a b") ]]
-
- ObjectTests = [[ Foo.new(1, 2), %({\"a\": 1, \"b\": 2}) ]]
-
- VariableTests = [[ ActiveSupport::JSON::Variable.new('foo'), 'foo'],
- [ ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")']]
- RegexpTests = [[ /^a/, '/^a/' ], [/^\w{1,2}[a-z]+/ix, '/^\\w{1,2}[a-z]+/ix']]
-
- constants.grep(/Tests$/).each do |class_tests|
- define_method("test_#{class_tests[0..-6].downcase}") do
- self.class.const_get(class_tests).each do |pair|
- assert_equal pair.last, pair.first.to_json
- end
- end
- end
-
- def setup
- unquote(false)
- end
-
- def teardown
- unquote(true)
- end
-
- def test_hash_encoding
- assert_equal %({\"a\": \"b\"}), { :a => :b }.to_json
- assert_equal %({\"a\": 1}), { 'a' => 1 }.to_json
- assert_equal %({\"a\": [1, 2]}), { 'a' => [1,2] }.to_json
-
- sorted_json =
- '{' + {:a => :b, :c => :d}.to_json[1..-2].split(', ').sort.join(', ') + '}'
- assert_equal %({\"a\": \"b\", \"c\": \"d\"}), sorted_json
- end
-
- def test_utf8_string_encoded_properly_when_kcode_is_utf8
- old_kcode, $KCODE = $KCODE, 'UTF8'
- assert_equal '"\\u20ac2.99"', '€2.99'.to_json
- assert_equal '"\\u270e\\u263a"', '✎☺'.to_json
- ensure
- $KCODE = old_kcode
- end
-
- def test_exception_raised_when_encoding_circular_reference
- a = [1]
- a << a
- assert_raises(ActiveSupport::JSON::CircularReferenceError) { a.to_json }
- end
-
- def test_unquote_hash_key_identifiers
- values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"}
- assert_equal %({"a": "a", 0: 0, "_": "_", 1: 1, "$": "$", "A": "A", "A0B": "A0B", "A0": "A0"}), values.to_json
- unquote(true) { assert_equal %({a: "a", 0: 0, _: "_", 1: 1, $: "$", A: "A", A0B: "A0B", A0: "A0"}), values.to_json }
- end
-
- protected
- def unquote(value)
- previous_value = ActiveSupport::JSON.unquote_hash_key_identifiers
- ActiveSupport::JSON.unquote_hash_key_identifiers = value
- yield if block_given?
- ensure
- ActiveSupport::JSON.unquote_hash_key_identifiers = previous_value if block_given?
- end
-
-end
diff --git a/vendor/rails/activesupport/test/json/decoding_test.rb b/vendor/rails/activesupport/test/json/decoding_test.rb
new file mode 100644
index 00000000..93560fb5
--- /dev/null
+++ b/vendor/rails/activesupport/test/json/decoding_test.rb
@@ -0,0 +1,41 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class TestJSONDecoding < Test::Unit::TestCase
+ TESTS = {
+ %q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
+ %q({returnTo:{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}},
+ %q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}},
+ %q({"returnTo":{"\/categories":1}}) => {"returnTo" => {"/categories" => 1}},
+ %({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]},
+ %({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]},
+ %({a: "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"},
+ %({a: "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"},
+ %({a: "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)},
+ %({a: "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)},
+ # no time zone
+ %({a: "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"},
+ # needs to be *exact*
+ %({a: " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "},
+ %([]) => [],
+ %({}) => {},
+ %(1) => 1,
+ %("") => "",
+ %("\\"") => "\"",
+ %(null) => nil,
+ %(true) => true,
+ %(false) => false,
+ %q("http:\/\/test.host\/posts\/1") => "http://test.host/posts/1"
+ }
+
+ TESTS.each do |json, expected|
+ define_method :"test_json_decoding_#{json}" do
+ assert_nothing_raised do
+ assert_equal expected, ActiveSupport::JSON.decode(json)
+ end
+ end
+ end
+
+ def test_failed_json_decoding
+ assert_raises(ActiveSupport::JSON::ParseError) { ActiveSupport::JSON.decode(%({: 1})) }
+ end
+end
diff --git a/vendor/rails/activesupport/test/json/encoding_test.rb b/vendor/rails/activesupport/test/json/encoding_test.rb
new file mode 100644
index 00000000..4e4fe570
--- /dev/null
+++ b/vendor/rails/activesupport/test/json/encoding_test.rb
@@ -0,0 +1,111 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class TestJSONEncoding < Test::Unit::TestCase
+ class Foo
+ def initialize(a, b)
+ @a, @b = a, b
+ end
+ end
+
+ TrueTests = [[ true, %(true) ]]
+ FalseTests = [[ false, %(false) ]]
+ NilTests = [[ nil, %(null) ]]
+ NumericTests = [[ 1, %(1) ],
+ [ 2.5, %(2.5) ]]
+
+ StringTests = [[ 'this is the ', %("this is the \\u003Cstring\\u003E")],
+ [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
+ [ 'http://test.host/posts/1', %("http://test.host/posts/1")]]
+
+ ArrayTests = [[ ['a', 'b', 'c'], %([\"a\", \"b\", \"c\"]) ],
+ [ [1, 'a', :b, nil, false], %([1, \"a\", \"b\", null, false]) ]]
+
+ SymbolTests = [[ :a, %("a") ],
+ [ :this, %("this") ],
+ [ :"a b", %("a b") ]]
+
+ ObjectTests = [[ Foo.new(1, 2), %({\"a\": 1, \"b\": 2}) ]]
+
+ VariableTests = [[ ActiveSupport::JSON::Variable.new('foo'), 'foo'],
+ [ ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")']]
+ RegexpTests = [[ /^a/, '/^a/' ], [/^\w{1,2}[a-z]+/ix, '/^\\w{1,2}[a-z]+/ix']]
+
+ DateTests = [[ Date.new(2005,2,1), %("2005/02/01") ]]
+ TimeTests = [[ Time.utc(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
+ DateTimeTests = [[ DateTime.civil(2005,2,1,15,15,10), %("2005/02/01 15:15:10 +0000") ]]
+
+ constants.grep(/Tests$/).each do |class_tests|
+ define_method("test_#{class_tests[0..-6].downcase}") do
+ self.class.const_get(class_tests).each do |pair|
+ assert_equal pair.last, pair.first.to_json
+ end
+ end
+ end
+
+ def test_hash_encoding
+ assert_equal %({\"a\": \"b\"}), { :a => :b }.to_json
+ assert_equal %({\"a\": 1}), { 'a' => 1 }.to_json
+ assert_equal %({\"a\": [1, 2]}), { 'a' => [1,2] }.to_json
+ assert_equal %({1: 2}), { 1 => 2 }.to_json
+
+ sorted_json = '{' + {:a => :b, :c => :d}.to_json[1..-2].split(', ').sort.join(', ') + '}'
+ assert_equal %({\"a\": \"b\", \"c\": \"d\"}), sorted_json
+ end
+
+ def test_utf8_string_encoded_properly_when_kcode_is_utf8
+ with_kcode 'UTF8' do
+ assert_equal '"\\u20ac2.99"', '€2.99'.to_json
+ assert_equal '"\\u270e\\u263a"', '✎☺'.to_json
+ end
+ end
+
+ def test_exception_raised_when_encoding_circular_reference
+ a = [1]
+ a << a
+ assert_raises(ActiveSupport::JSON::CircularReferenceError) { a.to_json }
+ end
+
+ def test_hash_key_identifiers_are_always_quoted
+ values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"}
+ assert_equal %w( "$" "A" "A0" "A0B" "_" "a" 0 1 ), object_keys(values.to_json)
+ end
+
+ def test_hash_should_allow_key_filtering_with_only
+ assert_equal %({"a": 1}), { 'a' => 1, :b => 2, :c => 3 }.to_json(:only => 'a')
+ end
+
+ def test_hash_should_allow_key_filtering_with_except
+ assert_equal %({"b": 2}), { 'foo' => 'bar', :b => 2, :c => 3 }.to_json(:except => ['foo', :c])
+ end
+
+ protected
+ def with_kcode(code)
+ if RUBY_VERSION < '1.9'
+ begin
+ old_kcode, $KCODE = $KCODE, 'UTF8'
+ yield
+ ensure
+ $KCODE = old_kcode
+ end
+ else
+ yield
+ end
+ end
+
+ def object_keys(json_object)
+ json_object[1..-2].scan(/([^{}:,\s]+):/).flatten.sort
+ end
+end
+
+uses_mocha 'JsonOptionsTests' do
+ class JsonOptionsTests < Test::Unit::TestCase
+ def test_enumerable_should_passthrough_options_to_elements
+ json_options = { :include => :posts }
+ ActiveSupport::JSON.expects(:encode).with(1, json_options)
+ ActiveSupport::JSON.expects(:encode).with(2, json_options)
+ ActiveSupport::JSON.expects(:encode).with('foo', json_options)
+
+ [1, 2, 'foo'].to_json(json_options)
+ end
+ end
+end
diff --git a/vendor/rails/activesupport/test/json_test.rb b/vendor/rails/activesupport/test/json_test.rb
deleted file mode 100644
index 8d9833ec..00000000
--- a/vendor/rails/activesupport/test/json_test.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-require File.dirname(__FILE__) + '/abstract_unit'
-
-class JsonFoo
- def initialize(a, b)
- @a, @b = a, b
- end
-end
-
-class TestJSONEmitters < Test::Unit::TestCase
- TrueTests = [[ true, %(true) ]]
- FalseTests = [[ false, %(false) ]]
- NilTests = [[ nil, %(null) ]]
- NumericTests = [[ 1, %(1) ],
- [ 2.5, %(2.5) ]]
-
- StringTests = [[ 'this is the string', %("this is the string") ],
- [ 'a "string" with quotes