Technology Musings

August 25, 2008

Snippets / Rails Sorting

JB

I always have trouble formulating exactly how sorts should work on Rails controllers that are called from sortable_element.  Therefore, I created the following method for ApplicationController which helps out a lot:

  def sort_assoc_members(assoc, ary, position_method = :position)
ary.each_index do |ary_idx|
assoc.find(ary[ary_idx]).update_attributes(position_method => (ary_idx + 1))
end    
end

Then, to use it, just create a sort action like this:

def reorder
sort_assoc_members(WhateverItem, params[:whatever_list])
end

This makes life much easier.

August 13, 2008

Snippets / TinyMCE + Prototype AJAX + RAILS

JB

NOTE - I have no idea why the formatting here is all messed up. It should be usable anyway since the snippets are fairly small

Using TinyMCE from Ruby on Rails is sometimes a bit tricky.  We tend to assume that all HTML components work the same - they can be added and removed at will.  However, TinyMCE components have to be explicitly added and removed - and as such they can be really painful.

In addition, TinyMCE components have to be saved back to the form in order to be serialized by Prototype (which is used by Rails for remote forms - remote_form_for, link_to_remote, etc.).

Therefore, I came up with the following, which doesn't completely remove the pain, but it at least eases it somewhat.  First of all, it modifies the Prototype library slightly to trigger a tinyMCE.triggerSave(true, true) every time a form is serialized (i.e. for remote forms and links).  Now you don't have to remember to do that anymore! Next, it provides a simple RJS helper to unregister/reregister TinyMCE editors when doing Ajax page manipulation in Rails.

Anyway, here are the steps:

Step 1

Put the following code into application.js:

//Register all TinyMCE instances on the page
function register_all_tiny_mce_editors() {
tinyMCE.init({
mode : "textareas",
theme : "advanced",
editor_selector : "editor"
});    
//NOTE - if you have other registration functions, put them here, too.
}
//Unregister all TinyMCE instances on the page
function unregister_all_tiny_mce_editors() {
tinyMCE.triggerSave(true, true);
for(editor_id in tinyMCE.editors) {
editor_elem = $(editor_id);
if(editor_elem) {
editor = tinyMCE.editors[editor_id];
tinyMCE.remove(editor)
}
}
} 
//Convince Prototype to do a TriggerSave anytime it is doing form serialization
var original_form_serialize_elements = Form.serializeElements;
Form.serializeElements = function(elements, gethash) {
try {
//Copy TinyMCE values back onto the regular form elements
tinyMCE.triggerSave(true, true);                
} catch(err) {
//Don't do anything - just means TinyMCE isn't loaded
}
return original_form_serialize_elements(elements, gethash);
}

Step 2

Put the following code into your layout:

<%# Modify this to wherever you put TinyMCE %>
<%= javascript_include_tag "tiny_mce/tiny_mce.js" %>
<script type="text/javascript">
tinyMCE.init({
mode : "textareas",
theme : "advanced",
editor_selector : "editor"
});
<%# Add/modify the init statement to include any other options you want  %>
<%# Be sure to use the same code as you used above in the register_all_tiny_mce_editors() function %>
</script>
<%# PUT ALL OTHER JAVASCRIPT INCLUDES ___AFTER___ THIS ONE OR ELSE TINYMCE COULD BREAK!! %>

Step 3

Now, put the following in application_helper.rb:

  def tmce_update(page, &block)
page << "unregister_all_tiny_mce_editors();"
yield
page << "register_all_tiny_mce_editors();"
end

Step 4

Now, in all RJS files which could cause TinyMCE textareas to appear/disappear, you can do the following:

tmce_update(page) do
#Perform RJS operations which affect TinyMCE areas here 
end

And now all TinyMCE areas will start behaving for you!  Note that this ONLY works with RJS-based Ajax calls - it doesn't work if you use the :update parameter, but you shouldn't be using that anyway.  Always do Ajax updates with RJS!