Removed superfluous 'tracks' directory at the root of the repository.

Testing commits to github.
This commit is contained in:
bsag 2008-05-20 21:28:26 +01:00
parent 6a42901514
commit 4cbf5a34d3
2269 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,72 @@
== rel-0.3.2
* FIXED: Error when trying to convert nil from hash (ticket #8)
* FIXED: Got tests running inside a Rails app
* UPDATED: Refactored BehaviourScriptConverter
== rel-0.3.1
* FIXED: Issues with Rails edge have now been resolved. Have changed the UnobtrusiveJavascript module to UJS. This has one API change - use UJS::Routes in routes.rb
* FIXED: Major issue when restarting server (server just hangs) due to serializing of behaviour scripts to the session. Objects are now converted to and from a hash before serializing to the session. All reports suggest that this has fixed the issue. Please raise a ticket if you get any more weird errors.
* FIXED: remote_function was incorrectly quoting JS (ticket #3)
* FIXED: :external => false now works for apply_behaviours (ticket #5)
* FIXED: element.replace_html inside Ruby behaviour block wasn't working, this is now fixed (ticket #4)
* FIXED: behaviours_url now works correctly when using mongrel url-prefix (ticket #6)
* FIXED: Resolved some documentation issues
== rel-0.3
* FIXED: Problems with rake:unobtrusive_javascript:install (ticket #12 on old trac)
* FIXED: Problems with back button (result of caching feature) (ticket #10 on old trac)
* Refactored a lot of the code and increased test coverage significantly
* NEW: Out-of-the box caching using HTTP ETags and advanced behaviour caching
* NEW: Apply multiple behaviours at once with apply_behaviours
* NEW: Behaviour helpers to easily and unobtrusively apply scriptaculous effects
* UPDATED: Updated lowpro.js library
* UPDATED: Added :external option support to tag_options
* NEW: :prevent_default option to cancel original event behaviour such as link following and form submission
== rel-0.2.2
* FIXED: Change the way tag_options detects javascript events, using blank? instead of include? as some Rails helpers insert an empty onclick handler.
== rel-0.2.1
* FIXED: Added an about.yml file - bad Luke - practice what you preach!
* FIXED: Fixed the plugin in edge rails by explicitly requiring the controller file
== rel-0.2
* UPDATED: register_js_behaviour has been renamed to apply_behaviour and now takes an optional options hash and a block, which allows you to write your behaviours in pure Ruby. register_js_behaviour remains as an alias for backwards compatibility but is officially deprecated; it will probably be removed in the next release. There are also aliases for the American spelling of behaviour for our friends on the other side of the pond.
* NEW: You can now attach behaviours directly to elements using content_tag and tag by passing in javascript event handlers in the HTML options hash (:onclick, :onmousedown etc.) - they'll be extracted to the external behaviours file automatically.
* UPDATED: The core Rails AJAX/Javascript helpers (link_to_remote, button_to_function, link_to_function, form_remote_tag etc.) now work out of the box.
* NEW: There is no need to explicitly specify an HTML ID for the elements you want to attach behaviour to - if you don't, one will be generated automatically for you.
* NEW: Options to render behaviour rules directly in your page inside script blocks instead of in the external behaviour file.
* FIXED: Behaviours inside AJAX-loaded partials will now work.
* UPDATED: event:Selectors and domReady javascript libraries are replaced with the lowPro.js library by Dan Webb
* UPDATED: Javascript behaviours now have access to a single variable - event. To access the element the event was applied to, use this.
* UPDATED: Behaviours can now cancel the default action by returning false as well as using Event.stop(event)
* FIXED: The required javascript files will be copied to your public folder automatically on installation. There is also a rake task for copying files across when upgrading the plugin.
* NEW: Lots more documentation!

View file

@ -0,0 +1,73 @@
= Unobtrusive Javascript for Rails Version 0.3.2
Please see www.ujs4rails.com for full documentation, articles and updates.
The Unobtrusive Javascript plugin for Rails allows you to attach Javascript behaviour and events to your page elements using pure Ruby, from within your controller or view, without clogging up your rendered HTML with Javascript - either inline or in the head of your document.
== Installation
The recommended way of installing the plugin is to take advantage of Subversion's svn:externals feature. From within your Rails app root, enter the following on the command line:
$ ./script/plugin install -x http://opensource.agileevolved.com/svn/root/rails_plugins/unobtrusive_javascript/tags/rel-0.2 unobtrusive_javascript
If you are upgrading from a previous version remember to update your applications javascripts with:
rake unobtrusive_javascript:install
Finally, add the necessary routes to config/routes.rb by adding:
UJS.routes
To the Routes.draw block.
== Including the necessary libraries and behaviours file
The only modification you need to make to your app is to add the following line somewhere in the head of your app's layout:
<%= javascript_include_tag 'prototype', :unobtrusive %>
You're now up and running with unobtrusive javascript functionality.
== Registering javascript behaviours
The plugin offers a simple, low-level function for attaching behaviours to elements of your page using the +register_js_behaviour+ function.
<%= apply_behaviour "#coolink:click", "alert('Hello world!')" %>
You can also pass a block to the function that lets you write your javascript behaviours using ruby:
<%= apply_behaviour "#sayhellothenfade:click" do |page, element, event|
page.alert "Hello, I'm gonna fade..."
element.visual_effect :fade
end %>
See the API documentation for more usage examples.
=== Rendering behaviours inside script blocks instead of an external file
By default, all unobtrusive behaviours will be rendered in an external javascript file, generated at runtime. Sometimes, you may want to embed the behaviours directly in a page, inside <tt>script</tt> tags. You can do this by passing the option <tt>:external => false</tt> to register_js_behaviour.
<%= apply_behaviour "#coolink:click", "alert('Hello world!')", :external => false %>
One of the main reasons to render javascript inside javascript tags instead of the external file is when you are embedding behaviours inside partials that are loaded into your page using AJAX. Because the external javascript file will already have been generated, any subsequent behaviours inside your AJAX-loaded partials will not register. For this reason the behaviours need to be rendered inside normal script tags. The unobtrusive_javascript plugin takes care of this for you automatically - any behaviours registered inside an AJAX-loaded partial will be rendered inside a script element without having to specify <tt>:external => false</tt> manually.
== Attaching behaviours directly to elements using Rails' tag generators
Rails comes with two built-in tag generator helpers - tag and content_tag. You can use these helpers to generate HTML elements and attach behaviour to them by directly specifying the events as part of the helpers' html options hash. The behaviours will be extracted and moved into the external behaviours file instead of being rendered inline.
<%= content_tag "div", "Click Me", :onclick => "alert('You clicked me')" %>
The full range of javascript events are available through this method.
The above functionality is achieved with a patch to Rails' tag_options function. Because of this patch, most of Rails' javascript/ajax helpers (or any helper that generates HTML using the tag_options function and that accepts an HTML options hash) will work in an unobtrusive fashion out of the box. The list of helpers that currently work unobtrusively include:
* link_to_remote
* link_to_function
* button_to_function
* form_remote_tag
* submit_to_remote
== Credits
The unobtrusive_javascript plugin is by Luke Redpath (http://www.lukeredpath.co.uk) and Dan Webb (http://www.vivabit.com/bollocks). The plugin also makes use of the Low Pro JavaScript library also by
Dan Webb, parts of which were heavily inspired by Justin Palmer's excellent event:Selectors (http://encytemedia.com/eventselectors/).

View file

@ -0,0 +1,33 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
require 'fileutils'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test the unobtrusive_javascript plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for the unobtrusive_javascript plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'UJS'
rdoc.options << '--line-numbers' << '--inline-source' << '--accessor=cattr_accessor'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end
desc 'Updates application with the necessary javascripts for unobtrusive_javascript.'
task :update_scripts do
FileUtils.cp Dir['assets/javascripts/*.js'], '../../../public/javascripts'
end
desc 'Removes the javascripts for the plugin.'
task :remove_scripts do
FileUtils.rm %{lowpro.js}.collect { |f| "../../../public/javascripts/" + f }
end

View file

@ -0,0 +1,6 @@
author: Luke Redpath & Dan Webb
summary: Makes adding javascript behaviour easy and enhances existing Rails javascript/ajax helpers.
homepage: http://www.ujs4rails.com
plugin: http://source.ujs4rails.com
license: MIT
version: 0.3

View file

@ -0,0 +1,307 @@
LowPro = {};
LowPro.Version = '0.1';
// Adapted from DOM Ready extension by Dan Webb
// http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype
// which was based on work by Matthias Miller, Dean Edwards and John Resig
//
// Usage:
//
// Event.onReady(callbackFunction);
Object.extend(Event, {
_domReady : function() {
if (arguments.callee.done) return;
arguments.callee.done = true;
if (Event._timer) clearInterval(Event._timer);
Event._readyCallbacks.each(function(f) { f() });
Event._readyCallbacks = null;
},
onReady : function(f) {
if (!this._readyCallbacks) {
var domReady = this._domReady;
if (domReady.done) return f();
if (document.addEventListener)
document.addEventListener("DOMContentLoaded", domReady, false);
/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
document.getElementById("__ie_onload").onreadystatechange = function() {
if (this.readyState == "complete") { domReady(); }
};
/*@end @*/
if (/WebKit/i.test(navigator.userAgent)) {
this._timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) domReady();
}, 10);
}
Event.observe(window, 'load', domReady);
Event._readyCallbacks = [];
}
Event._readyCallbacks.push(f);
}
});
if (!Element.addMethods)
Element.addMethods = function(o) { Object.extend(Element.Methods, o) };
// Extend Element with observe and stopObserving.
Element.addMethods({
observe : function(el, event, callback) {
Event.observe(el, event, callback);
},
stopObserving : function(el, event, callback) {
Event.stopObserving(el, event, callback);
}
});
// Replace out existing event observe code with Dean Edwards' addEvent
// http://dean.edwards.name/weblog/2005/10/add-event/
Object.extend(Event, {
observe : function(el, type, func) {
el = $(el);
if (!func.$$guid) func.$$guid = Event._guid++;
if (!el.events) el.events = {};
var handlers = el.events[type];
if (!handlers) {
handlers = el.events[type] = {};
if (el["on" + type]) {
handlers[0] = el["on" + type];
}
}
handlers[func.$$guid] = func;
el["on" + type] = Event._handleEvent;
if (!Event.observers) Event.observers = [];
Event.observers.push([el, name, func, false]);
},
stopObserving : function(el, type, func) {
if (el.events && el.events[type]) delete el.events[type][func.$$guid];
},
_handleEvent : function(e) {
var returnValue = true;
e = e || Event._fixEvent(window.event);
var handlers = this.events[e.type], el = $(this);
for (var i in handlers) {
el.$$handleEvent = handlers[i];
if (el.$$handleEvent(e) === false) returnValue = false;
}
return returnValue;
},
_fixEvent : function(e) {
e.preventDefault = Event._preventDefault;
e.stopPropagation = Event._stopPropagation;
return e;
},
_preventDefault : function() { this.returnValue = false },
_stopPropagation : function() { this.cancelBubble = true },
_guid : 1
});
// Allows you to trigger an event element.
Object.extend(Event, {
trigger : function(element, event, fakeEvent) {
element = $(element);
fakeEvent = fakeEvent || { type : event };
this.observers.each(function(cache) {
if (cache[0] == element && cache[1] == event)
cache[2].call(element, fakeEvent);
});
}
});
// Based on event:Selectors by Justin Palmer
// http://encytemedia.com/event-selectors/
//
// Usage:
//
// Event.addBehavior({
// "selector:event" : function(event) { /* event handler. this refers to the element. */ },
// "selector" : function() { /* runs function on dom ready. this refers to the element. */ }
// ...
// });
//
// Multiple calls will add to exisiting rules. Event.addBehavior.reassignAfterAjax and
// Event.addBehavior.autoTrigger can be adjusted to needs.
Event.addBehavior = function(rules) {
var ab = this.addBehavior;
Object.extend(ab.rules, rules);
if (ab.autoTrigger) {
this.onReady(ab.load.bind(ab));
}
Ajax.Responders.register({
onComplete : function() {
if (Event.addBehavior.reassignAfterAjax)
setTimeout(function() { ab.load() }, 10);
}
});
};
Object.extend(Event.addBehavior, {
rules : {}, cache : [],
reassignAfterAjax : true,
autoTrigger : true,
load : function() {
this.unload();
for (var selector in this.rules) {
var observer = this.rules[selector];
var sels = selector.split(',');
sels.each(function(sel) {
var parts = sel.split(/:(?=[a-z]+$)/), css = parts[0], event = parts[1];
$$(css).each(function(element) {
if (event) {
$(element).observe(event, observer);
Event.addBehavior.cache.push([element, event, observer]);
} else {
if (!element.$$assigned || !element.$$assigned.include(observer)) {
if (observer.attach) observer.attach(element);
else observer.call($(element));
element.$$assigned = element.$$assigned || [];
element.$$assigned.push(observer);
}
}
});
});
}
},
unload : function() {
this.cache.each(function(c) {
Event.stopObserving.apply(Event, c);
});
}
});
Event.observe(window, 'unload', Event.addBehavior.unload.bind(Event.addBehavior));
// Behaviors can be bound to elements to provide an object orientated way of controlling elements
// and their behavior. Use Behavior.create() to make a new behavior class then use attach() to
// glue it to an element. Each element then gets it's own instance of the behavior and any
// methods called onxxx are bound to the relevent event.
//
// Usage:
//
// var MyBehavior = Behavior.create({
// onmouseover : function() { this.element.addClassName('bong') }
// });
// Event.addBehavior({ 'a.rollover' : MyBehavior });
Behavior = {
create : function(members) {
var behavior = Class.create();
behavior.prototype.initialize = Prototype.K;
Object.extend(behavior.prototype, members);
Object.extend(behavior, Behavior.ClassMethods);
return behavior;
},
ClassMethods : {
attach : function(element) {
var bound = new this;
bound.element = $(element);
this._bindEvents(bound);
return bound;
},
_bindEvents : function(bound) {
for (var member in bound)
if (member.match(/^on(.+)/) && typeof bound[member] == 'function')
bound.element.observe(RegExp.$1, bound[member].bindAsEventListener(bound));
}
}
};
// Original code by Sylvian Zimmer
// http://www.sylvainzimmer.com/index.php/archives/2006/06/25/speeding-up-prototypes-selector/
// Optimises execution speed of the $$ function. Rewritten for readability by Justin Palmer.
//
// Turn off optimisation with LowPro.optimize$$ = false;
LowPro.SelectorLite = Class.create();
LowPro.SelectorLite.prototype = {
initialize: function(selectors) {
this.results = [];
this.selectors = [];
this.index = 0;
for(var i = selectors.length -1; i >= 0; i--) {
var params = { tag: '*', id: null, classes: [] };
var selector = selectors[i];
var needle = selector.length - 1;
do {
var id = selector.lastIndexOf("#");
var klass = selector.lastIndexOf(".");
var cursor = Math.max(id, klass);
if(cursor == -1) params.tag = selector.toUpperCase();
else if(id == -1 || klass == cursor) params.classes.push(selector.substring(klass + 1))
else if(!params.id) params.id = selector.substring(id + 1);
selector = selector.substring(0, cursor);
} while(cursor > 0);
this.selectors[i] = params;
}
},
get: function(root) {
this.findElements(root || document, this.index == (this.selectors.length - 1));
return this.results;
},
findElements: function(parent, descendant) {
var selector = this.selectors[this.index], results = [], element;
if(selector.id) {
element = $(selector.id);
if(element && (selector.tag == '*' || element.tagName == selector.tag) &&
(element.childOf(parent))) {
results = [element];
}
} else {
results = $A(parent.getElementsByTagName(selector.tag));
}
if(selector.classes.length == 1) {
results = results.select(function(target) {
return $(target).hasClassName(selector.classes[0]);
});
} else if(selector.classes.length > 1) {
results = results.select(function(target) {
var klasses = $(target).classNames();
return selector.classes.all(function(klass) {
return klasses.include(klass);
});
});
}
if(descendant) {
this.results = this.results.concat(results);
} else {
++this.index;
results.each(function(target) {
this.findElements(target, this.index == (this.selectors.length - 1));
}.bind(this));
}
}
}
LowPro.$$old=$$;
LowPro.optimize$$ = true;
function $$(a,b) {
if (LowPro.optimize$$ == false || b || a.indexOf("[")>=0)
return LowPro.$$old.apply(this, arguments);
return new LowPro.SelectorLite(a.split(/\s+/)).get();
}

View file

@ -0,0 +1,21 @@
require 'actionview_helpers_patches'
require 'prototype_helper_patches'
require 'scriptaculous_helper_patches'
require 'asset_tag_helper_patches'
require 'tag_helper_patches'
require 'behaviour_caching'
require 'ujs'
# make plugin controllers and views available to app
config.load_paths += %W(#{UJS::PLUGIN_CONTROLLER_PATH})
Rails::Initializer.run(:set_load_path, config)
# add methods to action controller base
ActionController::Base.send(:include, UJS::ControllerMethods)
# load in the helpers and caching code
ActionController::Base.send(:helper, UJS::Helpers)
ActionController::Base.send(:include, UJS::BehaviourCaching)
# require the controller
require 'unobtrusive_javascript_controller'

View file

@ -0,0 +1 @@
`rake update_scripts`

View file

@ -0,0 +1,17 @@
module ActionView::Helpers
def self.current_controller=(controller)
@@current_controller = controller
end
def self.current_controller
@@current_controller
end
end
class ActionView::Helpers::InstanceTag
include UJS::Helpers
def current_controller
ActionView::Helpers.current_controller
end
end

View file

@ -0,0 +1,34 @@
module ActionView::Helpers::AssetTagHelper
alias_method :rails_javascript_include_tag, :javascript_include_tag
# Adds a new option to Rails' built-in <tt>javascript_include_tag</tt>
# helper - <tt>:unobtrusive</tt>. Works in the same way as <tt>:defaults</tt> - specifying
# <tt>:unobtrusive</tt> will make sure the necessary javascript
# libraries and behaviours file +script+ tags are loaded. Will happily
# work along side <tt>:defaults</tt>.
#
# <%= javascript_include_tag :defaults, :unobtrusive %>
#
# This replaces the old +unobtrusive_javascript_files+ helper.
def javascript_include_tag(*sources)
if sources.delete :unobtrusive
sources = sources.concat(
#['lowpro', behaviours_url] ## This is the original. Hacking to make asset_packager more effective
[behaviours_url]
).uniq
end
rails_javascript_include_tag(*sources)
end
protected
def behaviours_url
action_path = case @controller.request.path
when '', '/'
'/index'
else
@controller.request.path
end
"/behaviours#{action_path}.js"
end
end

View file

@ -0,0 +1,70 @@
module UJS
module BehaviourCaching
def self.included(base)
base.extend ControllerClassMethods
end
attr_accessor :cache_behaviours
module ControllerClassMethods
# Also file caching of the behaviour for hidden actions. This actually
# write the javascript file into the public directory (or given cache dir)
# in the same style as page caching. Which means requests for the behaviour
# will not hit the application at all. Use this for reasonably static behaviours.
#
# NB. If you use caches_page or caches_action your behaviours will automatically ne
# cached in this way. expire_page and expire_action also automatically expire the
# cached behaviour.
#
# caches_behaviour :index, :register
#
# See expire_behaviour to expire cached behaviours.
def caches_behaviour(*actions)
actions.each do |action|
class_eval "prepend_before_filter { |c| c.cache_behaviours = (c.action_name == '#{action}') }"
end
end
alias_method :caches_behavior, :caches_behaviour
def caches_page(*actions)
caches_behaviour(*actions)
super
end
def caches_action(*actions)
caches_behaviour(*actions)
super
end
end
# Expires the behaviours for the given url options.
#
# expire_behaviour :action => 'index'
def expire_behaviour(options={})
self.class.expire_page "/behaviour#{url_for(options)}.js"
end
alias_method :expire_behavior, :expire_behaviour
def expire_page(options={})
expire_behaviour(options)
super
end
def expire_action(options={})
if options[:action].is_a?(Array)
options[:action].dup.each do |action|
expire_behaviour(options.merge({ :action => action }))
end
else
expire_behaviour(options)
end
super
end
end
end

View file

@ -0,0 +1,52 @@
require 'digest/md5'
class UnobtrusiveJavascriptController < ActionController::Base
skip_before_filter :initialise_js_behaviours
skip_after_filter :store_js_behaviours
after_filter :perform_any_caching
after_filter :reset_js_behaviours
# Renders the external javascript behaviours file
# with the appropriate content-type.
def generate
headers['Content-Type'] = 'text/javascript'
if js_behaviours
generate_etag
modified? ? render_script : render_304
else
render :text => "", :layout => false
end
end
protected
def perform_any_caching
if behaviour_caching_enabled?
self.class.cache_page js_behaviours.to_s, request.path
end
end
private
def generate_etag
headers['ETag'] = Digest::MD5.hexdigest(js_behaviours.to_s)
end
def modified?
request.env['HTTP_IF_NONE_MATCH'] != headers['ETag']
end
def render_script
render :text => js_behaviours.to_s, :layout => false
end
def render_304
render :nothing => true, :status => 304
end
def behaviour_caching_enabled?
js_behaviours && js_behaviours.cache?
end
end

View file

@ -0,0 +1,72 @@
module ActionView::Helpers::PrototypeHelper
class JavaScriptRef
def initialize(ref); @ref = ref; end
def to_json; @ref; end
end
def javascript_variable(name)
JavaScriptRef.new(name)
end
def build_observer(klass, name, options={})
set_default_external!(options)
javascript = build_observer_js(klass, name, options)
unless options.delete :external
javascript_tag(javascript)
else
@controller.apply_behaviour("##{name}", javascript)
return ''
end
end
def build_observer_js(klass, name, options)
if options[:with] && !options[:with].include?("=")
options[:with] = "'#{options[:with]}=' + value"
else
options[:with] ||= 'value' if options[:update]
end
callback = options[:function] || remote_function(options)
javascript = "new #{klass}(#{name.to_json}, "
javascript << "#{options[:frequency]}, " if options[:frequency]
javascript << "function(element, value) {"
javascript << "#{callback}}"
javascript << ", '#{options[:on]}'" if options[:on]
javascript << ")"
javascript
end
def remote_function(options)
javascript_options = options_for_ajax(options)
update = ''
if options[:update] and options[:update].is_a?Hash
update = []
update << "success:'#{options[:update][:success]}'" if options[:update][:success]
update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
update = '{' + update.join(',') + '}'
elsif options[:update]
update << "'#{options[:update]}'"
end
function = update.empty? ?
"new Ajax.Request(" :
"new Ajax.Updater(#{update}, "
url_options = options[:url]
url_options = url_options.merge(:escape => false) if url_options.is_a? Hash
url = url_options.is_a?(JavaScriptRef) ? url_options.to_json : "'#{url_for(url_options)}'"
function << "#{url}"
function << ", #{javascript_options})"
function = "#{options[:before]}; #{function}" if options[:before]
function = "#{function}; #{options[:after]}" if options[:after]
function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
return function
end
end

View file

@ -0,0 +1,62 @@
module ActionView::Helpers::ScriptaculousHelper
def draggable_element(element_id, options={})
set_default_external!(options)
external = options.delete :external
prepare_script(element_id, draggable_element_js(element_id, options).chop!, external)
end
def draggable_element_js(element_id, options = {}) #:nodoc:
%(new Draggable(#{element_id.to_json}, #{options_for_javascript(options)});)
end
def drop_recieving_element(element_id, options={})
set_default_external!(options)
external = options.delete :external
prepare_script(element_id, drop_receiving_element_js(element_id, options).chop!, external)
end
def drop_receiving_element_js(element_id, options = {}) #:nodoc:
options[:with] ||= "'id=' + encodeURIComponent(element.id)"
options[:onDrop] ||= "function(element){" + remote_function(options) + "}"
options.delete_if { |key, value| ActionView::Helpers::PrototypeHelper::AJAX_OPTIONS.include?(key) }
options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept]
options[:hoverclass] = "'#{options[:hoverclass]}'" if options[:hoverclass]
%(Droppables.add(#{element_id.to_json}, #{options_for_javascript(options)});)
end
def sortable_element(element_id, options={})
set_default_external!(options)
external = options.delete :external
prepare_script(element_id, sortable_element_js(element_id, options).chop!, external)
end
def sortable_element_js(element_id, options = {}) #:nodoc:
options[:with] ||= "Sortable.serialize(#{element_id.to_json})"
options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
options.delete_if { |key, value| ActionView::Helpers::PrototypeHelper::AJAX_OPTIONS.include?(key) }
[:tag, :overlap, :constraint, :handle].each do |option|
options[option] = "'#{options[option]}'" if options[option]
end
options[:containment] = array_or_string_for_javascript(options[:containment]) if options[:containment]
options[:only] = array_or_string_for_javascript(options[:only]) if options[:only]
%(Sortable.create(#{element_id.to_json}, #{options_for_javascript(options)});)
end
protected
def prepare_script(element_id, js, external=true)
unless external
javascript_tag(js)
else
@controller.apply_behaviour "##{element_id}", js
return ''
end
end
end

View file

@ -0,0 +1,37 @@
module ActionView::Helpers::TagHelper
include UJS::Helpers
JAVASCRIPT_EVENTS = %w(click mouseup mousedown dblclick mousemove mouseover mouseout submit change keypress keyup keydown load)
alias_method :rails_tag_options, :tag_options
protected
# Patch to the built-in Rails tag_options method. Looks for any
# javascript event handlers, extracts them and registers them
# as unobtrusive behaviours.
#
# This behaviour affects any built-in Rails helpers that generate
# HTML. Event extraction behaviour can be bypassed by passing in
# <tt>:inline => true</tt> as part of a helper's HTML options hash.
def tag_options(opts, escape = true)
set_default_external!(opts)
if opts[:external]
JAVASCRIPT_EVENTS.each do |event|
unless opts["on#{event}"].blank?
opts['id'] = generate_html_id unless opts['id']
apply_behaviour("##{opts['id']}:#{event}", opts["on#{event}"]) unless opts["on#{event}"].nil?
opts.delete("on#{event}")
end
end
opts.delete(:external)
end
rails_tag_options(opts, escape)
end
# Generate a unique id to be used as the HTML +id+ attribute.
def generate_html_id
@tag_counter ||= 0
@tag_counter = @tag_counter.next
"#{UJS::Settings.generated_id_prefix}#{@tag_counter}"
end
end

View file

@ -0,0 +1,32 @@
module UJS
PLUGIN_NAME = 'unobtrusive_javascript'
PLUGIN_PATH = "#{RAILS_ROOT}/vendor/plugins/#{PLUGIN_NAME}"
PLUGIN_ASSET_PATH = "#{PLUGIN_PATH}/assets"
PLUGIN_CONTROLLER_PATH = "#{PLUGIN_PATH}/lib/controllers"
class Settings
# All elements with attached behaviours that do not
# have an HTML +id+ attribute will have one
# generated automatically, using the form _prefix_x_,
# where the default prefix is "uj_element_" and x is an
# automatically incremented number. You can set the
# generated prefix to anything you like by setting it in your
# environment.rb file:
#
# UJS::Settings.generated_id_prefix = "my_prefix_"
cattr_accessor :generated_id_prefix
@@generated_id_prefix = "uj_element_"
end
class << self
# Adds routes to your application necessary for the plugin to function correctly.
# Simply add the following inside your Routes.draw block in routes.rb:
# UJS::routes
# This is now *mandatory*.
def routes
ActionController::Routing::Routes.add_route "/behaviours/*page_path", :controller => "unobtrusive_javascript", :action => "generate"
end
alias_method :use_fake_script_links, :routes
end
end

View file

@ -0,0 +1,62 @@
module UJS
# The helper methods in this module can all be passed as arguments to apply_behaviour and allow
# you to reproduce common functionailty such as drag and drop and sorting in a simple way. Think
# of them as the apply_javascript equivilent to the Scriptaculous helpers.
#
# Usage:
#
# apply_behaviour '.products', make_draggable
# apply_behaviour '.help', make_remote_link( :action => 'showhelp' )
# apply_behaviour '.todolist', make_sortable
#
module BehaviourHelper
# Make a link send an Ajax request. options identical to linkt_to_remote except that
# :url defaults to the href attribute of the link.
def make_remote_link(options={})
options[:url] ||= javascript_variable('this.href');
"this.observe('click', function(event) {\n#{remote_function(options)};\nreturn false;\n});"
end
# Make a form submit via an Ajax request. options identical remote_form except that
# :url defaults to the action attribute of the form.
def make_remote_form(options={})
options[:url] ||= javascript_variable('this.action');
options[:with] ||= 'Form.serialize(this)'
"this.observe('submit', function(event) {\n#{remote_function(options)};\nreturn false;\n});"
end
# Observe a form or form field (specified with the type argument) using the given options
# which are the same as observe_form and observe_field.
def make_observed(type, options={})
obs = (type.to_s == 'form') ? 'Form' : 'Form.Element'
if options[:frequency] && options[:frequency] > 0
build_observer_js("#{obs}.Observer", javascript_variable('this'), options)
else
build_observer_js("#{obs}.EventObserver", javascript_variable('this'), options)
end
end
# Makes the children of the element sortable.
def make_sortable(options={})
sortable_element_js(javascript_variable('this'), options)
end
# Makes the element draggable.
def make_draggable(options={})
draggable_element_js(javascript_variable('this'), options)
end
# Makes the element a drop target.
def make_drop_receiving(options={})
drop_receiving_element_js(javascript_variable('this'), options)
end
def make_autocomplete # :nodoc:
end
def make_in_place_editor # :nodoc:
end
end
end

View file

@ -0,0 +1,58 @@
class UJS::BehaviourScript
attr_reader :rules
attr_writer :reapply_after_ajax
def initialize(cache=false, reapply_after_ajax=true)
@rules, @cache, @reapply_after_ajax = [], cache, reapply_after_ajax
end
def add_rule(selector, javascript, cancel_default=false)
javascript = javascript << cancel_default_js if cancel_default
@rules << [selector, javascript] unless rule_exists(selector, javascript)
end
def cache?
@cache
end
def enable_cache
@cache = true
end
def reapply_after_ajax?
@reapply_after_ajax
end
# Renders behaviour block and option JavaScript.
def to_s
(@rules && !@rules.empty?) ? "Event.addBehavior({\n#{rule_js}\n});" + option_js : ''
end
# Uses behaviour script converter to conver to a hash for session storage
def to_hash
UJS::BehaviourScriptConverter.convert_to_hash(self)
end
protected
# Renders a collection of behaviour rules in javascript format
def rule_js
@rules.uniq.collect { |sel, js| behaviour_rule(sel, js) }.join(",\n")
end
# Renders behaviour rule javascript for the behaviours file
def behaviour_rule(selector, behaviour)
"\"#{selector}\": function(event) {\n#{behaviour}\n}"
end
def option_js
reapply_after_ajax? ? '' : "\nEvent.addBehavior.reapplyAfterAjax = false;"
end
def cancel_default_js
" return false;"
end
def rule_exists(selector, javascript)
@rules.detect{|r| r[0] == selector && r[1] == javascript} != nil
end
end

View file

@ -0,0 +1,23 @@
class UJS::BehaviourScriptConverter
def initialize(script)
@script = script
end
# Converts a BehaviourScript object into a custom hash format
def self.convert_to_hash(script)
self.new(script).to_hash
end
# Converts a hash-converted BehaviourScript back to a BehaviourScript again
def self.convert_from_hash(script_hash)
script = UJS::BehaviourScript.new(script_hash[:options][:cache], script_hash[:options][:reapply_after_ajax])
script_hash[:rules].each { |r| script.add_rule(r[0], r[1]) }
script
end
# Convert behaviour script to a hash
def to_hash
{ :options => { :cache => @script.cache?, :reapply_after_ajax => @script.reapply_after_ajax? },
:rules => @script.rules }
end
end

View file

@ -0,0 +1,55 @@
module UJS::ControllerMethods
def self.included(base)
base.class_eval do
before_filter :store_controller_for_helpers
before_filter :initialise_js_behaviours
after_filter :store_js_behaviours
end
end
# Lets you register javascript behaviours from within
# the controller. For a description of the different options
# available, see UJS::Helpers#apply_behaviour (note:
# this function does not take a block like the view helper version)
def apply_behaviour(selector, behaviour, opts={})
opts.reverse_merge!(:prevent_default => false)
@js_behaviours.add_rule(selector, behaviour, opts[:prevent_default])
end
def cache_behaviours
@js_behaviours.enable_cache
end
def reapply_behaviours_after_ajax=(val)
@js_behaviours.reapply_after_ajax = val
end
# Make American and English spellings both work for apply_behaviour
alias_method :apply_behavior, :apply_behaviour
protected
# Initialises the javascript behaviours
def initialise_js_behaviours
@js_behaviours = UJS::BehaviourScript.new(@cache_behaviours)
end
# Clears the array of registered javascript behaviours
def reset_js_behaviours
session[:js_behaviours] = nil
end
# Returns a BehaviourScript from the behaviours serialized to the session
def js_behaviours
return nil if session[:js_behaviours].nil?
UJS::BehaviourScriptConverter.convert_from_hash(session[:js_behaviours])
end
# Stores all registered javascript behaviours in the session as a hash
def store_js_behaviours
session[:js_behaviours] = @js_behaviours.to_hash
end
def store_controller_for_helpers
ActionView::Helpers.current_controller = self
end
end

View file

@ -0,0 +1,152 @@
require File.dirname(__FILE__) + '/behaviour_helper'
require File.dirname(__FILE__) + '/javascript_proxies'
module UJS::Helpers
include UJS::BehaviourHelper
include UJS::JavascriptProxies
# This is the core functionality of the plugin;
# it allows you to attach javascript behaviour to your page
# elements in an unobtrusive fashion. It takes three options:
# * +selector+ - CSS selector and event. For a full overview of
# the selector syntax, see the event:Selectors website.
# http://encytemedia.com/event-selectors
# * +behaviour+ - The javascript that you want to attach to the element
# and event specified by +selector+ as a string of javascript.
# * +opts+ - A hash of additional options.
#
# Attaching a behaviour to an element on your page is as simple as
# specifying the element and the event you want the behaviour attached
# to, using the CSS selector format, and passing in a string of javascript:
#
# <% apply_behaviour "#coollink:click", "alert('Hello World')" %>
#
# You can also make use of any of the built-in Rails helpers that
# generate Javascript:
#
# <% apply_behaviour "#coolink:click", visual_effect(:highlight, "coollink") %>
#
# You will have access to two javascript variables inside your javascript string:
# * +this+: returns a reference to the HTML element that the behaviour was attached to
# * +event+: the event the behaviour was attached to:
#
# <% apply_behaviour "#coollink:click", "alert('You clicked ' this.id); Event.stop(event)" %>
#
# The following options can be set using the opts hash:
#
# * <tt>:external</tt> - If true, the behaviour will be attached to the external behaviour file.
# If false, it will be rendered directly in the page inside +script+ tags. Defaults to true.
#
# When setting <tt>:external</tt> to false, you <em>must</em> call the register_js_behaviour from
# within ERb <em>output</em> blocks. If <tt>:external</tt> is true, you can use either output or non-output blocks.
#
# If you set :prevent_default to true the default action of the event is stopped (similar to calling event.stop).
# This is very useful for hijacking links in order to provide some other behaviour.
#
# You can also pass a block to the function instead of a string of javascript -
# the block will be passed in a JavascriptGenerator object (+page+), the element (optional)
# and the event (optional). You can use the Javascript generator
# to write your attached behaviour using Ruby:
#
# <% apply_behaviour "#coollink:click" do |page, element, event|
# page.alert("Hi there, I'm going to fade away...")
# page.visual_effect :fade, element
# end %>
def apply_behaviour(selector, *args, &block) #:yields: page, element, event
opts = args.last.is_a?(Hash) ? args.pop : {}
set_default_external!(opts)
behaviour = normalise_behaviour_string(args.first || '')
if block_given?
generator = new_javascript_generator
args = [generator, element_proxy(generator), event_proxy(generator)][0..block.arity-1]
@template.instance_exec(*args, &block)
behaviour = generator.to_s
end
if !opts[:external]
render_inline_behaviour_block(selector, behaviour, opts)
else
@controller.apply_behaviour(selector, behaviour, opts) and return ''
end
end
# Allow you to apply multiple behaviours with block syntax.
#
# apply_behaviours do
# on 'a:click', 'alert("boo")'
# on 'abbr:mouseover', 'showAbbr(this)'
# end
def apply_behaviours(&block)
mapper = BehaviourMapper.new(self).instance_eval &block
mapper.to_s
end
# Make sure American and English spellings both work
alias_method :apply_behavior, :apply_behaviour
alias_method :apply_behaviors, :apply_behaviours
protected
# A convenient mapper class for applying
# multiple behaviours at once.
class BehaviourMapper
def initialize(base)
@base = base
@output = ''
end
def on(*args, &block)
@output << @base.apply_behaviour(*args, &block)
end
# delegate all other method calls to the view
def method_missing(meth, *args)
@base.send meth, *args
end
def to_s
@output
end
end
# Renders a block of javascript behaviours inside +script+ tags
# directly within the page
def render_inline_behaviour_block(selector, behaviour, opts)
script = UJS::BehaviourScript.new
script.add_rule(selector, behaviour)
javascript_tag(script.to_s)
end
# Use this to set the default :external option for helper calls.
# it set external to false if its an xhr request or false if it's a normal request.
def set_default_external!(options)
options.reverse_merge!(:external => !current_controller.request.xhr?)
end
# Returns a new JavascriptArgumentProxy for the element the behaviour
# has been applied to.
def element_proxy(generator)
UJS::JavascriptProxies::JavascriptArgumentProxy.new(UJS::JavascriptProxies::ReferencedJavascriptElementProxy, generator, 'this')
end
# Returns a new JavascriptArgumentProxy for the event the behaviour has
# been applied to.
def event_proxy(generator)
UJS::JavascriptProxies::JavascriptArgumentProxy.new(UJS::JavascriptProxies::JavascriptEventProxy, generator, 'event')
end
# Returns a new JavasScriptGenerator object
def new_javascript_generator
ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template) { }
end
def current_controller
@controller
end
def normalise_behaviour_string(behaviour)
behaviour << ';' unless behaviour =~ /;$/
behaviour
end
end

View file

@ -0,0 +1,57 @@
module ActionView
module Helpers
class JavaScriptProxy
public :method_missing
protected
def wrap(function, *args)
args = [function_chain[-1].chomp(';')].concat(args.collect(&:to_json)).join(', ')
replace_line("#{function.to_s}(#{args});")
end
def replace_line(new_line)
function_chain[-1] = new_line
end
end
end
end
module UJS
module JavascriptProxies
class ReferencedJavascriptElementProxy < ActionView::Helpers::JavaScriptElementProxy
def initialize(generator, var)
@generator = generator
@generator << var
end
def reload; end
end
class JavascriptArgumentProxy
def initialize(proxy, *contructor_args)
@proxy, @args = proxy, contructor_args
end
def method_missing(meth, *args)
proxy = @proxy.new(*@args)
proxy.__send__(meth, *args)
end
end
class JavascriptEventProxy < ActionView::Helpers::JavaScriptProxy
def stop
wrap('Event.stop')
end
def element
wrap('Event.element')
end
end
end
end

View file

@ -0,0 +1,15 @@
namespace :unobtrusive_javascript do
PLUGIN_ROOT = File.dirname(__FILE__) + '/../'
desc 'Installs required javascript files to the public/javascripts directory.'
task :install do
FileUtils.cp Dir[PLUGIN_ROOT + '/assets/javascripts/*.js'], RAILS_ROOT + '/public/javascripts'
end
desc 'Removes the javascripts for the plugin.'
task :remove do
FileUtils.rm %{lowpro.js}.collect { |f| RAILS_ROOT + "/public/javascripts/" + f }
end
end

View file

@ -0,0 +1,32 @@
require File.dirname(__FILE__) + '/test_helper'
require 'asset_tag_helper_patches'
class JavascriptIncludeTagWithUnobtrusiveOption < Test::Unit::TestCase
include ActionView::Helpers::AssetTagHelper
include ActionView::Helpers::TagHelper
def setup
initialize_test_request
@output = javascript_include_tag(:unobtrusive).split("\n")
end
def test_should_render_script_tag_for_lowpro
assert @output.include?('<script src="/javascripts/lowpro.js?" type="text/javascript"></script>')
end
def test_should_render_script_tag_for_current_requests_behaviour
assert @output.include?('<script src="/behaviours/controller_stub.js?" type="text/javascript"></script>')
end
def test_should_render_index_behaviour_when_request_path_is_just_a_forward_slash
@controller.request.stubs(:path).returns('/')
@output = javascript_include_tag(:unobtrusive).split("\n")
assert @output.include?('<script src="/behaviours/index.js?" type="text/javascript"></script>')
end
def test_should_render_index_behaviour_when_request_path_is_blank_as_a_result_of_a_url_prefix
@controller.request.stubs(:path).returns('')
@output = javascript_include_tag(:unobtrusive).split("\n")
assert @output.include?('<script src="/behaviours/index.js?" type="text/javascript"></script>')
end
end

View file

@ -0,0 +1,180 @@
require File.dirname(__FILE__) + '/test_helper'
require 'prototype_helper_patches'
require 'scriptaculous_helper_patches'
require 'ujs/behaviour_helper'
class MakeSortableTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::ScriptaculousHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include UJS::BehaviourHelper
def setup
initialize_test_request
end
def test_should_output_sortable_javascript
output = make_sortable
assert_equal sortable_element_js(javascript_variable('this')), output
end
def test_should_pass_arguments_through
output = make_sortable :onUpdate => 'function() { alert("updated") }'
assert_equal 'Sortable.create(this, {onUpdate:function() { alert("updated") }});', output
end
end
class MakeRemoteLinkTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::ScriptaculousHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include UJS::BehaviourHelper
def setup
initialize_test_request
end
def test_should_generate_ajax_request
output = make_remote_link
assert_match(/new Ajax\.Request/, output)
end
def test_should_default_to_element_href
output = make_remote_link
assert_match(/\(this\.href/, output)
end
def test_should_respond_to_given_options
output = make_remote_link( :update => 'fartknocker' )
assert_match(/new Ajax\.Updater\('fartknocker'/, output)
end
end
class MakeRemoteFormTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::ScriptaculousHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include UJS::BehaviourHelper
def setup
initialize_test_request
end
def test_should_generate_ajax_request
output = make_remote_form
assert_match(/new Ajax\.Request/, output)
end
def test_should_default_to_form_action
output = make_remote_form
assert_match(/\(this\.action/, output)
end
end
class MakeDraggableTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::ScriptaculousHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include UJS::BehaviourHelper
def setup
initialize_test_request
end
def test_should_create_draggable_instance
output = make_draggable
assert_match(/new Draggable/, output)
end
def test_should_pass_this
output = make_draggable
assert_match(/\(this/, output)
end
def test_should_respond_to_options
output = make_draggable( :revert => true )
assert_match(/revert\:true/, output)
end
end
class MakeDropRecievingTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::ScriptaculousHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include UJS::BehaviourHelper
def setup
initialize_test_request
end
def test_should_add_to_droppables
output = make_drop_receiving
assert_match(/Droppables\.add/, output)
end
def test_should_pass_this
output = make_drop_receiving
assert_match(/\(this/, output)
end
def test_should_generate_a_url_from_options
output = make_drop_receiving( :url => { :action => "bingo" } )
assert_match(/controller_stub\/bingo/, output)
end
end
class MakeObservedTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::ScriptaculousHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include UJS::BehaviourHelper
def setup
initialize_test_request
end
def test_should_make_form_observer
output = make_observed(:form)
assert_match(/new Form\.EventObserver/, output)
end
def test_should_make_field_observer
output = make_observed(:field)
assert_match(/new Form\.Element\.EventObserver/, output)
end
def test_should_pass_this
output = make_observed(:field)
assert_match(/\(this/, output)
end
def test_should_make_a_timed_observer_if_frequency_passed
output = make_observed(:form, :frequency => 3 )
assert_match(/new Form.Observer/, output)
assert_match(/3,/, output)
end
def test_should_generate_a_url_from_options
output = make_observed(:field, :url => { :action => "bingo" } )
assert_match(/controller_stub\/bingo/, output)
end
def test_should_respond_to_options
output = make_observed(:field, :function => 'alert("boo")' )
assert_match(/function\(element, value\) \{alert\("boo"\)\}/, output)
end
end

View file

@ -0,0 +1,72 @@
require File.dirname(__FILE__) + '/test_helper'
class DefaultBehaviourScriptConversionTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
@output = UJS::BehaviourScriptConverter.convert_to_hash(@script)
end
def test_should_have_no_rules_and_the_correct_default_options
assert_equal({ :options => { :cache => false, :reapply_after_ajax => true },
:rules => [] }, @output)
end
end
class EmptyBehaviourScriptWithDifferentOptionsConversionTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new(true, false)
@output = UJS::BehaviourScriptConverter.convert_to_hash(@script)
end
def test_should_have_no_rules_and_the_correct_options
assert_equal({ :options => { :cache => true, :reapply_after_ajax => false },
:rules => [] }, @output)
end
end
class BehaviourScriptWithOneRuleConversionTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
@script.add_rule('div.foo:click', 'alert("TEST")')
@output = UJS::BehaviourScriptConverter.convert_to_hash(@script)
end
def test_should_have_one_behaviour_and_correct_options
assert_equal({ :options => { :cache => false, :reapply_after_ajax => true },
:rules => [
['div.foo:click', 'alert("TEST")']
] }, @output)
end
end
class BehaviourScriptWithTwoRuleConversionTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
@script.add_rule('div.foo:click', 'alert("TEST")')
@script.add_rule('div.bar:click', 'alert("TEST 2")')
@output = UJS::BehaviourScriptConverter.convert_to_hash(@script)
end
def test_should_have_one_behaviour_and_correct_options
assert_equal({ :options => { :cache => false, :reapply_after_ajax => true },
:rules => [
['div.foo:click', 'alert("TEST")'],
['div.bar:click', 'alert("TEST 2")']
] }, @output)
end
end
class BehaviourScriptFromHashTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
@script.add_rule('div.foo:click', 'alert("TEST")')
@script.add_rule('div.bar:click', 'alert("TEST 2")')
@converted_script = UJS::BehaviourScriptConverter.convert_from_hash(@script.to_hash)
end
def test_should_equal_the_script_it_was_converted_from_in_the_first_place
assert_equal @script.cache?, @converted_script.cache?
assert_equal @script.reapply_after_ajax?, @converted_script.reapply_after_ajax?
assert_equal @script.rules, @converted_script.rules
end
end

View file

@ -0,0 +1,98 @@
require File.dirname(__FILE__) + '/test_helper'
require 'ujs/behaviour_script'
class NewBehaviourScriptTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
end
def test_should_render_nothing_on_to_string
assert_equal "", @script.to_s
end
def test_should_not_be_cached
assert !@script.cache?
end
def test_should_be_reapplied_after_an_ajax_request
assert @script.reapply_after_ajax?
end
end
class BehaviourScriptWithOneRuleTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
@script.add_rule("div.header:click", "alert('Hello World')")
end
def test_should_render_the_rule_as_a_javascript_event_on_to_s
expected_js = "Event.addBehavior({\n\"div.header:click\": function(event) {\nalert('Hello World')\n}\n});"
assert_equal expected_js, @script.to_s
end
end
class BehaviourScriptWithTwoRulesTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
@script.add_rule("div.header:mouseover", "alert('Hello World')")
@script.add_rule("div.header:mouseout", "alert('Goodbye World')")
end
def test_should_render_all_rules_as_javascript_events_on_to_s
expected_js = "Event.addBehavior({\n\"div.header:mouseover\": function(event) {\nalert('Hello World')\n},"
expected_js = expected_js + "\n\"div.header:mouseout\": function(event) {\nalert('Goodbye World')\n}\n});"
assert_equal expected_js, @script.to_s
end
end
class BehaviourScriptWithRuleThatCancelsDefaultTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
@script.add_rule("div.header:mouseover", "alert('Hello World');", true)
end
def test_should_render_rule_with_return_false_appended_on_to_s
expected_js = "Event.addBehavior({\n\"div.header:mouseover\": function(event) {\nalert('Hello World'); return false;\n}\n});"
assert_equal expected_js, @script.to_s
end
end
class BehaviourScriptWithNoRulesTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
end
def test_should_render_nothing_on_to_s
assert_equal "", @script.to_s
end
end
class BehaviourScriptWithRulesSetToNotReapplyAfterAjaxTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new
@script.reapply_after_ajax = false
@script.add_rule("div.header:click", "alert('Hello World')")
end
def test_should_append_reapply_javascript_to_end_of_rules_javascript_on_to_s
expected_js = "Event.addBehavior({\n\"div.header:click\": function(event) {\nalert('Hello World')\n}\n});"
expected_js = expected_js + "\nEvent.addBehavior.reapplyAfterAjax = false;"
assert_equal expected_js, @script.to_s
end
end
class BehaviourScriptToHashTest < Test::Unit::TestCase
def setup
@script = UJS::BehaviourScript.new(true, false)
@script.add_rule("div.header:mouseover", "alert('Hello World')")
@script.add_rule("div.header:mouseout", "alert('Goodbye World')")
end
def test_should_return_converted_behaviour_script
assert_equal({ :options => { :cache => true, :reapply_after_ajax => false },
:rules => [
['div.header:mouseover', "alert('Hello World')"],
['div.header:mouseout', "alert('Goodbye World')"]
] }, @script.to_hash)
end
end

View file

@ -0,0 +1,19 @@
test:
adapter: sqlite3
dbfile: test.sqlite3.db
# adapter: sqlite
# dbfile: test.sqlite.db
# adapter: mysql
# host: localhost
# username:
# password:
# database: test
# adapter: postgresql
# host: localhost
# username:
# password:
# database: test

View file

@ -0,0 +1,8 @@
Rails::Initializer.run do |config|
config.cache_classes = true
config.whiny_nils = true
config.action_controller.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.action_mailer.delivery_method = :test
config.action_mailer.perform_deliveries = true
end

View file

@ -0,0 +1,4 @@
ActionController::Routing::Routes.draw do |map|
map.connect ':controller/:action/:id'
UJS::routes
end

View file

@ -0,0 +1,3 @@
ActiveRecord::Schema.define(:version => 2) do
end

View file

@ -0,0 +1,48 @@
require File.dirname(__FILE__) + '/test_helper'
class ControllerWithControllerMethodsInjected < Test::Unit::TestCase
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
end
def test_should_add_a_before_filter_that_creates_a_new_behaviour_script
assert ControllerStub.before_filters.include?(:initialise_js_behaviours)
assert_instance_of UJS::BehaviourScript, assigns(:js_behaviours)
assert_equal "", assigns(:js_behaviours).to_s
end
def test_should_store_applied_behaviours_in_the_behaviour_script
@controller.apply_behaviour("div.foo", "alert('foo')")
assert_equal 1, assigns(:js_behaviours).rules.size
end
def test_should_add_an_after_filter_that_stores_the_behaviour_script_in_the_session_as_a_hash
assert ControllerStub.after_filters.include?(:store_js_behaviours)
assert_equal session[:js_behaviours], assigns(:js_behaviours).to_hash
end
def test_should_not_store_behaviour_script_in_the_session_if_js_behaviours_is_nil
@controller.send(:reset_js_behaviours)
assert_nil @controller.send(:js_behaviours)
end
def test_should_turn_behaviour_script_caching_on_when_cache_behaviours_is_called
@controller.cache_behaviours
assert assigns(:js_behaviours).cache?
end
def test_should_toggle_reload_after_ajax_when_set
@controller.reapply_behaviours_after_ajax = false
assert !assigns(:js_behaviours).reapply_after_ajax?
@controller.reapply_behaviours_after_ajax = true
assert assigns(:js_behaviours).reapply_after_ajax?
end
def test_should_also_allow_american_spelling_for_apply_behaviour
@controller.apply_behavior("div.foo", "alert('foo')")
assert_equal 1, assigns(:js_behaviours).rules.size
end
end

View file

@ -0,0 +1,186 @@
require File.dirname(__FILE__) + '/test_helper'
class ApplyingBehaviourWithStringOfJavascriptTest < Test::Unit::TestCase
include UJS::Helpers
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
@output = apply_behaviour("#mydiv:click", "alert('hello world')")
end
def test_should_store_registered_behaviour
assert_equal 1, assigns(:js_behaviours).rules.size
assert_equal "#mydiv:click", assigns(:js_behaviours).rules.first[0]
assert_equal "alert('hello world');", assigns(:js_behaviours).rules.first[1]
end
end
class ApplyingBehaviourThatIsRendererdInlineTest < Test::Unit::TestCase
include UJS::Helpers
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::TagHelper
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
@output = apply_behaviour("#mydiv:click", "alert('hello world')", :external => false)
end
def test_should_not_store_registered_behaviour
assert_equal 0, assigns(:js_behaviours).rules.size
end
end
class PreventDefaultBehaviourOptionTest < Test::Unit::TestCase
include UJS::Helpers
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::TagHelper
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
@output = apply_behaviour("#mydiv:click", "alert('hello world')", :prevent_default => true)
end
def test_should_return_false_with_prevent_default
assert_equal ['#mydiv:click', "alert('hello world'); return false;"], assigns(:js_behaviours).rules.last
end
end
class ApplyingBehaviourWithBlockTest < Test::Unit::TestCase
include UJS::Helpers
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
end
def test_should_use_page_argument
apply_behaviour '#thing' do |page|
page.alert('hello')
end
assert_equal '#thing', assigns(:js_behaviours).rules.last[0]
assert_equal "alert(\"hello\");", assigns(:js_behaviours).rules.last[1]
end
def test_should_use_element_argument
apply_behaviour '#thing' do |page, element|
element.hide
end
assert_equal '#thing', assigns(:js_behaviours).rules.last[0]
assert_equal "this.hide();", assigns(:js_behaviours).rules.last[1]
end
def test_should_use_event_argument
apply_behaviour '#thing' do |page, element, event|
event.stop
end
assert_equal '#thing', assigns(:js_behaviours).rules.last[0]
assert_equal "Event.stop(event);", assigns(:js_behaviours).rules.last[1]
end
def test_should_use_allow_multiple_calls
apply_behaviour '#thing' do |page, element, event|
page.alert('hiding thing')
element.hide
element.show
event.stop
end
assert_equal '#thing', assigns(:js_behaviours).rules.last[0]
assert_equal "alert(\"hiding thing\");\nthis.hide();\nthis.show();\nEvent.stop(event);", assigns(:js_behaviours).rules.last[1]
end
def test_should_allow_options_with_block_without_specifying_string
apply_behaviour '#thing2', :prevent_default => true do |page|
page.alert('boo')
end
assert_equal '#thing2', assigns(:js_behaviours).rules.last[0]
assert_equal "alert(\"boo\"); return false;", assigns(:js_behaviours).rules.last[1]
end
def test_should_allow_element_proxy_methods_to_be_called
apply_behaviour '#thing3' do |page, element|
element.replace_html '<strong>Wow!</strong>'
end
assert_equal '#thing3', assigns(:js_behaviours).rules.last[0]
assert_equal "this.update(\"<strong>Wow!</strong>\");", assigns(:js_behaviours).rules.last[1]
end
end
class MultipleBehavioursAppliedAtOnceTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::ScriptaculousHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include UJS::BehaviourHelper
include UJS::Helpers
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
apply_behaviours do
on "div.foo", "alert('foo')"
on "div.bar", "alert('bar')"
end
end
def test_should_all_get_registered_in_the_behaviour_script
assert_equal 2, assigns(:js_behaviours).rules.size
end
def test_should_work_with_apply_behaviour_helpers
apply_behaviours do
on "ul.sortable", make_sortable
end
assert_equal 3, assigns(:js_behaviours).rules.size
end
end
class MultipleBehavioursAppliedAtOnceWithExternalFalseTest < Test::Unit::TestCase
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::ScriptaculousHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include UJS::BehaviourHelper
include UJS::Helpers
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
@output = apply_behaviours do
on "div.foo", "alert('foo')", :external => false
on "div.bar", :external => false do |page|
page.alert('bar')
end
end
end
def test_should_output_behaviours
assert_not_equal '', @output
assert_match(/<script/, @output)
assert_match(/alert\('foo'\)/, @output)
assert_match(/alert\("bar"\)/, @output)
end
end

View file

@ -0,0 +1,3 @@
require File.dirname(__FILE__) + '/test_helper'
# TODO

View file

@ -0,0 +1,26 @@
# We are always a test environment and should never be anything else
ENV["RAILS_ENV"] ||= "test"
require File.join(File.dirname(__FILE__), 'ptk')
# Set up RAILS_ROOT to #{plugin_path}/test
unless defined?(RAILS_ROOT)
root_path = PTK::LoadPath.expand(__FILE__, '..', '..')
unless RUBY_PLATFORM =~ /mswin32/
require 'pathname'
root_path = Pathname.new(root_path).cleanpath(true).to_s
end
RAILS_ROOT = root_path
end
# add #{plugin_path}/test/lib
PTK::LoadPath.add RAILS_ROOT, 'lib'
# add #{plugin_path}/lib
PTK::LoadPath.add RAILS_ROOT, '..', 'lib'
require 'rubygems'
require 'test/unit'
require 'active_support'

View file

@ -0,0 +1,11 @@
require 'action_pack'
require 'action_controller'
require 'action_controller/test_process'
ActionController::Base.ignore_missing_templates = true
if PTK::Configuration.load :routes
ActionController::Routing::Routes.reload rescue nil
end
class ActionController::Base; def rescue_action(e) raise e end; end

View file

@ -0,0 +1,4 @@
require 'action_mailer'
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true

View file

@ -0,0 +1,27 @@
require 'active_record'
require 'active_record/fixtures'
ActiveRecord::Base.logger = Logger.new(File.join(RAILS_ROOT, 'test.log'))
# Load the database.yml from #{plugin_path}/test/config if it exists
if file = PTK::Configuration.find_path(:database)
config = YAML::load_file(file)
ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
# Load a schema if it exists
if schema = PTK::Configuration.find_path(:schema)
load(schema)
# Setup fixtures if the directory exists
if fixtures = PTK::Configuration.find_path(:fixtures)
PTK::LoadPath.add fixtures
Test::Unit::TestCase.fixture_path = fixtures
Test::Unit::TestCase.use_instantiated_fixtures = false
end
end
end

View file

@ -0,0 +1,31 @@
# We require the initializer to setup the environment properly
unless defined?(Rails::Initializer)
if File.directory?("#{RAILS_ROOT}/../../../rails")
require "#{RAILS_ROOT}/../../../rails/railties/lib/initializer"
else
require 'rubygems'
require_gem 'rails'
require 'initializer'
end
Rails::Initializer.run(:set_load_path)
end
# We overwrite load_environment so we can have only one file
module Rails
class Initializer
def load_environment
end
end
end
# We overwrite the default log to be a directory up
module Rails
class Configuration
def default_log_path
File.join(root_path, "#{environment}.log")
end
end
end
# We then load it manually
PTK::Configuration.load :environment

View file

@ -0,0 +1,148 @@
require 'singleton'
module PTK
class Configuration
class << self
def load(config, fatal = false)
if (file = PTK::PathSet.instance.send(config)) == :ignore then false
elsif File.exists?(file)
require file
true
elsif fatal then raise LoadError, "PTK could not find #{file}"
else
STDERR.puts "PTK::WARNING: could not find #{file}"
false
end
end
def find_path(config, fatal = false)
if (file = PTK::PathSet.instance.send(config)) == :ignore then false
elsif File.exists?(file) then file
elsif fatal then raise LoadError, "PTK could not find #{file}"
else
STDERR.puts "PTK::WARNING: could not find #{file}"
false
end
end
def draw
yield PTK::PathSet.instance
end
end
end
class PathSet
include Singleton
attr_accessor :ptk_prefix
attr_accessor :config
attr_accessor :fixtures
attr_accessor :environment
attr_accessor :schema
attr_accessor :database
attr_accessor :routes
def initialize
self.ptk_prefix = 'ptk'
self.config = File.join(RAILS_ROOT, 'config')
self.fixtures = File.join(RAILS_ROOT, 'fixtures')
self.environment = File.join(self.config, 'environment.rb')
self.database = File.join(self.config, 'database.yml')
self.schema = File.join(self.config, 'schema.rb')
self.routes = File.join(self.config, 'routes.rb')
end
end
class Initializer
# The init.rb in the root directory of the plugin will be loaded by default
attr_accessor :init
# The specific environmental frameworks of a plugin, such as needing the ActionController
# ActionMailer or ActiveRecord gems to be preloaded. A special requirement called
# 'environment' will load tests as though they were in the test environment of a normal
# Rails application.
attr_accessor :frameworks
def frameworks
[@frameworks].flatten
end
# Suites are test extensions including assertions and various tools for easier testing
attr_accessor :suites
def suites
[@suites].flatten
end
# A container for the PathSet instance
attr_reader :paths
def initialize
self.init = true
self.frameworks = :none
self.suites = :all
@paths = PTK::PathSet.instance
end
def self.run(command = :process)
initializer = PTK::Initializer.new
yield initializer if block_given?
initializer.send(command)
end
def process
initialize_frameworks
initialize_suites
initialize_plugin
end
def initialize_frameworks
return if frameworks.include?(:none)
self.frameworks = [:rails] if frameworks.include?(:rails)
frameworks.each { |lib| require_ptk File.join('gem', lib.to_s) }
end
def initialize_suites
return if suites.include?(:none)
self.suites = all_suites if suites.include?(:all)
suites.each { |lib| require_ptk File.join('suite', lib.to_s) }
end
def initialize_plugin
return unless self.init
require File.join(RAILS_ROOT, '..', 'init')
end
protected
def all_suites
Dir.glob(File.join(RAILS_ROOT, 'lib', 'ptk', 'suite', '*.rb')).inject([]) do |a, file|
a << File.basename(file, '.rb').to_sym
a
end
end
def require_ptk(library)
file = paths.ptk_prefix.empty? ? library : File.join(paths.ptk_prefix, library)
require file
end
end
class LoadPath
def self.expand(file, *dirs)
File.join(*([File.expand_path(File.dirname(file))] << dirs))
end
def self.add(*dirs)
path = File.expand_path(File.join(*dirs))
$:.unshift path
$:.uniq!
end
end
end

View file

@ -0,0 +1,23 @@
class Test::Unit::TestCase
# http://project.ioni.st/post/217#post-217
#
# def test_new_publication
# assert_difference(Publication, :count) do
# post :create, :publication => {...}
# # ...
# end
# end
#
# modified by mabs29 to include arguments
def assert_difference(object, method = nil, difference = 1, *args)
initial_value = object.send(method, *args)
yield
assert_equal initial_value + difference, object.send(method, *args), "#{object}##{method}"
end
def assert_no_difference(object, method, *args, &block)
assert_difference object, method, 0, *args, &block
end
end

View file

@ -0,0 +1,10 @@
require 'action_controller/test_process'
require 'ujs/controller_methods'
class ControllerStub < ActionController::Base
def index
render :nothing => true
end
end
ControllerStub.send(:include, UJS::ControllerMethods)

View file

@ -0,0 +1,29 @@
# Do not comment out this line; it sets the RAILS_ROOT constant and load paths, not Rails itself
require File.join(File.dirname(__FILE__), 'lib', 'ptk', 'boot')
PTK::Initializer.run do |setup|
# You can also redefine the paths of certain directories and files, namely:
#setup.paths.config = File.join(RAILS_ROOT, 'config')
#setup.paths.fixtures = File.join(RAILS_ROOT, 'fixtures')
#setup.paths.database = File.join(setup.paths.config, 'database.yml')
#setup.paths.schema = File.join(setup.paths.config, 'schema.rb')
#setup.paths.routes = File.join(setup.paths.config, 'routes.rb')
#setup.paths.environment = File.join(setup.paths.config, 'environment.rb')
# If any of these paths are set to ':ignore', no warnings will appear if they are missing.
# Frameworks are the gems from Rails which you need PTK to load for your plugin.
# The special :rails framework creates a fully-fledged Rails environment and requires
# the environment.rb file.
# Valid options are: :action_controller, :action_mailer, :active_record, :rails
setup.frameworks = :action_controller # :active_record, :action_controller
# Extra libraries of assertions and other common methods that provide more testing
# utilities. To hand-pick which suites you want, uncomment the below
#setup.suites = :difference
# If for some particular reason you do not want your plugin's init to be called
# at the end of this block, uncomment the below:
setup.init = false
end

View file

@ -0,0 +1,59 @@
require File.dirname(__FILE__) + '/test_helper'
class ContentTagWithJavascriptEventsTest < Test::Unit::TestCase
include ActionView::Helpers::TagHelper
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
@output = content_tag('div', 'my test div', {
:id => 'my_test_div', :class => 'some_class',
:onclick => 'alert("foo")',
:onmouseup => 'alert("foo")',
:onmousedown => 'alert("foo")',
:ondblclick => 'alert("foo")',
:onmouseover => 'alert("foo")',
:onmouseout => 'alert("foo")',
:onload => 'alert("foo")',
:onmousemove => 'alert("foo")'
})
end
def test_result_should_not_contain_any_inline_javascript_events
assert_equal '<div class="some_class" id="my_test_div">my test div</div>', @output
end
def test_should_have_javascript_events_registered_as_unobtrusive
assert_equal 8, assigns(:js_behaviours).rules.size
assert assigns(:js_behaviours).rules.include?(['#my_test_div:click', 'alert("foo");'])
assert assigns(:js_behaviours).rules.include?(['#my_test_div:mouseup', 'alert("foo");'])
assert assigns(:js_behaviours).rules.include?(['#my_test_div:mousedown', 'alert("foo");'])
assert assigns(:js_behaviours).rules.include?(['#my_test_div:dblclick', 'alert("foo");'])
assert assigns(:js_behaviours).rules.include?(['#my_test_div:mouseover', 'alert("foo");'])
assert assigns(:js_behaviours).rules.include?(['#my_test_div:mouseout', 'alert("foo");'])
assert assigns(:js_behaviours).rules.include?(['#my_test_div:load', 'alert("foo");'])
assert assigns(:js_behaviours).rules.include?(['#my_test_div:mousemove', 'alert("foo");'])
end
end
class TagOptionsForFormTextFieldsTest < Test::Unit::TestCase
include ActionView::Helpers::FormTagHelper
include ActionView::Helpers::FormHelper
include ActionView::Helpers::TagHelper
def setup
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
@text_field_tag = text_field_tag('login')
@text_field = text_field(:user, :first_name)
end
def test_should_not_contain_any_inline_javascript_events
assert_equal '<input id="login" name="login" type="text" />', @text_field_tag
assert_equal '<input id="user_first_name" name="user[first_name]" size="30" type="text" />', @text_field
end
end

View file

@ -0,0 +1,25 @@
ENV["RAILS_ENV"] = "test"
# load plugin test kit
require File.join(File.dirname(__FILE__), 'ptk_helper')
# load stubba and other stubs
require 'stubba'
require 'stubs/controller_stub'
# load plugin files
require 'actionview_helpers_patches'
require 'prototype_helper_patches'
require 'scriptaculous_helper_patches'
require 'asset_tag_helper_patches'
require 'tag_helper_patches'
require 'behaviour_caching'
$:.unshift(File.join(File.dirname(__FILE__), '../lib/'))
def initialize_test_request
@controller = ControllerStub.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :index
end

View file

@ -0,0 +1,95 @@
require File.dirname(__FILE__) + '/test_helper'
# inject the controller methods to add the appropriate filters
# before loading in the unobtrusive js controller otherwise its
# skip_ filters will be fubarred.
ActionController::Base.send(:include, UJS::ControllerMethods)
require 'controllers/unobtrusive_javascript_controller'
class BehaviourGenerationControllerTest < Test::Unit::TestCase
def test_should_always_skip_the_initialise_js_behaviours_filter
assert !UnobtrusiveJavascriptController.before_filters.include?(:initialise_js_behaviours)
end
def test_should_always_skip_the_store_js_behaviours_filter
assert !UnobtrusiveJavascriptController.before_filters.include?(:store_js_behaviours)
end
def test_should_always_perform_any_caching_after_a_request
assert UnobtrusiveJavascriptController.after_filters.include?(:perform_any_caching)
end
def test_should_always_reset_any_js_behaviours_after_a_request
assert UnobtrusiveJavascriptController.after_filters.include?(:reset_js_behaviours)
end
end
class BehaviourGenerationWithNoBehavioursTest < Test::Unit::TestCase
def setup
@controller = UnobtrusiveJavascriptController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
UnobtrusiveJavascriptController.any_instance.stubs(:js_behaviours).returns(UJS::BehaviourScript.new)
get :generate
end
def test_should_set_content_type_to_text_javascript
assert_equal "text/javascript", @response.headers["Content-Type"]
end
def test_should_render_nothing
assert_equal "", @response.body
end
end
class BehaviourGenerationWithAppliedBehavioursTest < Test::Unit::TestCase
def setup
@behaviours = UJS::BehaviourScript.new
@behaviours.add_rule("div.foo", "alert('foo')")
@behaviours.add_rule("div.bar", "alert('bar')")
UnobtrusiveJavascriptController.any_instance.stubs(:js_behaviours).returns(@behaviours)
@controller = UnobtrusiveJavascriptController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
get :generate
end
def test_should_set_content_type_to_text_javascript
assert_equal "text/javascript", @response.headers["Content-Type"]
end
def test_should_render_behaviour_script_output
assert_equal @behaviours.to_s, @response.body
end
def test_should_generate_an_etag_for_the_behaviours
assert_equal Digest::MD5.hexdigest(@behaviours.to_s), @response.headers["ETag"]
end
end
class BehaviourGenerationWithUnchangedBehavioursTest < Test::Unit::TestCase
def setup
@behaviours = UJS::BehaviourScript.new
@behaviours.add_rule("div.foo", "alert('foo')")
@behaviours.add_rule("div.bar", "alert('bar')")
UnobtrusiveJavascriptController.any_instance.stubs(:js_behaviours).returns(@behaviours)
@controller = UnobtrusiveJavascriptController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@request.env['HTTP_IF_NONE_MATCH'] = Digest::MD5.hexdigest(@behaviours.to_s)
get :generate
end
def test_should_set_content_type_to_text_javascript
assert_equal "text/javascript", @response.headers["Content-Type"]
end
def test_should_render_nothing
assert_equal " ", @response.body
end
def test_should_send_304_not_modified_http_status
assert_response 304
end
end

View file

@ -0,0 +1 @@
`rake remove_scripts`