ActiveScaffold render :super

Leave a comment

You all may know that there is a quite flexible hierarchy for activescaffold s templates and partials.
If you would like to override one in general for your application copy and change it in app/views/active_scaffold_overrides/.
If you would like to override one just for one specific controller copy and change it in app/views/controller_name/.

In addition to do a full override you may also call parent s view eg. to wrap parent template/view or to change some locals. I will show you how to do that in this post.

Just a simple render :super call in your overwritten template/partial is all you need to activate this feature.

<%= render :super %>

If you would like to change a local variable you have to do the following and it depends on your activescaffold version:

:super with locals for ActiveScaffold 3.0

<%= render :super, :locals => {:headline => 'My _base_form headline'} %>

:super with locals for ActiveScaffold 3.1 and 3.2

<%= render :partial => :super, :locals => {:headline => 'My _base_form headline'} %>

ActiveScaffold Asset Pipeline Comparison

5 Comments

Today I would like to compare a activescaffold rails 3.0 controller index action waterfall chart with one of a rails 3.1 controller.
You will see a big difference in these charts because of the asset pipeline feature. Asset pipeline is merging your js and css assets into one big file which improves your webperformance because of reduced http requests.
In addition I m using css data-uri feature for background images. Please note that ie6 and ie7 do not support data-uris.

Rails 3.0 waterfall

Rails 3.1 waterfall

ActiveScaffold Unobtrusive Javascript and TinyMCE

2 Comments

My last post covered the topic how ActiveScaffold and Unobtrusive Javascript may fit together. This time I would like to give you an example.
It s Activescaffold’s TinyMCE Bridge. It s a perfect example, cause it s a pure javascript feature.
Let’s take a look at the bridge file using obtrusive javascript:

Loading tinymce editor

Obtrusive Javascript

def active_scaffold_input_text_editor(column, options)
    options[:class] = "#{options[:class]} mceEditor #{column.options[:class]}".strip
    html = []
    html << send(override_input(:textarea), column, options)
    html << javascript_tag("tinyMCE.execCommand('mceAddControl', false, '#{options[:id]}');") if request.xhr?
    html.join "\n"
end

We use an activescaffold input override to add javascript code to our textareas.

Unobtrusive Javascript

$('form.as_form').live('as:form_loaded', function(event) {
  var as_form = $(this).closest("form");
  as_form.find('textarea.as_mceEditor').each(function(index, elem) {
    tinyMCE.execCommand('mceAddControl', false, $(elem).attr('id'));
  });
  return true;
});

A listener to activescaffold form_load event, finding all textareas and adding tinyMCE control.

Submitting tinymce editor

Obtrusive Javascript

def onsubmit
    if ActiveScaffold.js_framework == :jquery
       submit_js = 'tinyMCE.triggerSave();$(\'textarea.mceEditor\').each(function(index, elem) { tinyMCE.execCommand(\'mceRemoveControl\', false, $(elem).attr(\'id\')); });' if using_tiny_mce?
    else
       submit_js = 'tinyMCE.triggerSave();this.select(\'textarea.mceEditor\').each(function(elem) { tinyMCE.execCommand(\'mceRemoveControl\', false, elem.id); });' if using_tiny_mce?
    end
    [super, submit_js].compact.join ';'
end

We use form submit method override to add javascript code, which will call tinyMCE.triggerSave method.

Unobtrusive Javascript

$('form.as_form').live('as:form_submit', function(event) {
  var as_form = $(this).closest("form");
  if (as_form.has('textarea.as_mceEditor').length > 0) {
    tinyMCE.triggerSave();
  }
  return true;
});

A listener to activescaffold form_submit event, finding all textareas using tinyMCE and calling triggerSave.

Removing tinymce editor

Obtrusive Javascript

def active_scaffold_includes(*args)
        if ActiveScaffold.js_framework == :jquery
          tiny_mce_js = javascript_tag(%|
var action_link_close = ActiveScaffold.ActionLink.Abstract.prototype.close;
ActiveScaffold.ActionLink.Abstract.prototype.close = function() {
$(this.adapter).find('textarea.mceEditor').each(function(index, elem) {
tinyMCE.execCommand('mceRemoveControl', false, $(elem).attr('id'));
});
action_link_close.apply(this);
};
|) if using_tiny_mce?
        else
        ....
        end
        super(*args) + (include_tiny_mce_if_needed || '') + (tiny_mce_js || '')
      end

Ok, we add a javascript code snippet to activescaffold includes in case using_tiny_mce? returns true. Javascript code will link into action link.close action management and will remove our tiny_mce_editor. To be honest I do not like that solution at all. :-)

Unobtrusive Version

$('form.as_form').live('as:form_unloaded', function(event) {
  var as_form = $(this).closest("form");
  as_form.find('textarea.as_mceEditor').each(function(index, elem) {
    tinyMCE.execCommand('mceRemoveControl', false, $(elem).attr('id'));
  });
  return true;
});

A listener to activescaffold form_unload event, finding all textareas and removing tinyMCE control.

Summary

Did we achieve our goals?
We ve separated javascript code from ruby code. which improves code quality and maintainability. You do not have to be an activescaffold expert anymore to add javascript functionality to forms, form_columns, list_columns. You just need to know javascript.

Hope that helps.

ActiveScaffold and Unobtrusive Javascript

Leave a comment

Rails 3.0 started using unobtrusive javascript. However, what does that actually mean for activescaffold? Well, basically it means that there should nt be any javascript code in our partials. Looks easy at the first glance, however, if you start actually to implement it..
Big advances of unobtrusive javascript are:

  • application works without javascript
  • html code seperated from javascript code
  • html id attribute is nt needed anymore for many operations

I ve mentioned at the beginning that going unobtrusive is nt that easy as it seems. The big issues you have are:

  • How to cleanly separate javascript code?
  • How may application work if javascript is disabled?
  • It s easy to manage click events, unobtrusively, but how to manage load events for ajax calls?

In this post I will focus on the last bullet point. It took a while until I was able to manage that issue.
Unobtrusive javascript needs a trigger event to add the javascript functionality. For example when you click a button. However, if you would like to add javascript functionality on load you are in kind of trouble for ajax applications, because load events of browsers do only fire if the whole page is loaded and not if snippets of html code are changed or added during an ajax call.

I ve solved that issue at least for activescaffold as follows.

ActiveScaffold will trigger the following events in case of page load and ajax load:

  • as:list_row_loaded
  • as:form_loaded
  • as:form_element_loaded

ActiveScaffold will trigger the following events in case of page unload and ajax unload:

  • as:list_row_unloaded
  • as:form_unloaded
  • as:form_element_unloaded

A simple use case would be the following: list columns may be configured for inplace editing, which is a pure javascript feature including the need to add it to the specified columns during as:list_row_load event:

$('tr.record').live('as:list_row_loaded', function(event) {
  $(this).closest("tr").find('td > span.in_place_editor_field').each(function(index) {
    ActiveScaffold.create_inplace_editor($(this));
  });
  return true;
});

In my next post I will show you how I ve migrated the tiny_mce bridge to unobtrusive javascript.

Hope you will benefit from this new javascript events, cause it will make the step to unobtrusive javascript a lot easier using activescaffold.

ActiveScaffold per Request Configuration for ActionColumns

1 Comment

If you take a look at Activescaffold per Request Configuration you will learn that you have to create a before_filter to be able to change column set per request.
Well, that does nt feel right, before_filters should be used for aspect oriented programming tasks such as authentication, logging,..

However, it was quite complicated to do it another way with Activescaffold.

I ve added a new feature to my fork which allows you to change column set configuration for a specific action without before_filters.

It s actually really easy, every action has a new method _columns, which you may override in your controller.

Let me give you some easy examples:

def update_columns
  columns = super
  if @record.id == 85
    columns.select {|column| column.name != :last_name}
  else
    columns
  end
end
def field_search_columns
  columns = super
  if sunny_weather
    columns.select {|column| column.name != :rain}
end

Wish you a great weekend.

ActiveScaffold and Rails 3.1

27 Comments

Today I would like to give you a short guide about how you may get ActiveScaffold up and running with Rails 3.1. Please be aware that ActiveScaffold is nt fully ported to Rails 3.1, so this guide is for early adopters who wanna try it out or help finishing development work.

I assume that you ve got Rails 3.1/3.2 already installed on your computer.

1. rails new as_rails31
2. please add below the line gem ‘jquery_rails’ in your gem file following code:
Official Gems:

gem 'render_component_vho'
gem 'active_scaffold_vho'

Github Gems:

gem 'render_component_vho', :git => 'git://github.com/vhochstein/render_component.git'
gem 'active_scaffold_vho', :git => 'git://github.com/vhochstein/active_scaffold.git'

3. bundle install
4. bundle exec rake db:create
5. rails g active_scaffold Team name:string position:integer
6. bundle exec rake db:migrate
7. Edit file /app/assets/javascripts/application.js

//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require active_scaffold
//= require_tree .

8. Edit file /app/assets/stylesheets/application.css

 *= require active_scaffold
 *= require_self
 *= require_tree . 

Hope you will enjoy.

ActiveScaffold: FieldSearch Column Overrides

4 Comments

ActiveScaffold is quite powerful, but sometimes it might not fit your needs. That s why activescaffold offers you many options to extend/change activescaffold s features to your needs.
I would like to talk about FieldSearch Column Overrides today, as an example how you may extend activescaffold.

We will use my howto application as a starting point: howto installation.

Please activate field_search and human conditions in players controller:

active_scaffold :player do |conf|
  conf.actions.exclude :search
  conf.actions.add :field_search
  conf.field_search.columns = [:date_of_birth]
  conf.field_search.human_conditions = true
end

If you start the application you may try fieldsearch. date_of_birth is using activescaffold’s default date search ui.
Let s assume we have a special requirement and we would like to show a select box showing all existing date_of_birth values.

How to do that? It s really quite simple and it takes as only three steps:

search column override

Responsible for rendering our specific search_ui.

players_helper.rb
def date_of_birth_search_column(record, html_options)
  selected = html_options.delete :value
        
  players = Player.select('distinct date_of_birth').except(:order).order('date_of_birth DESC').all
  select_options = players.collect do |player|
    [ l(player.date_of_birth), player.date_of_birth ]
  end
  options = { :selected => selected,
                  :include_blank => as_(:_select_)}
  select(:record, :date_of_birth, select_options, options, html_options)
end

human condition column override

Responsible for rendering a special human condition.

players_helper.rb
def date_of_birth_human_condition_column(value, options)
  "#{Player.human_attribute_name(:date_of_birth)} = #{I18n.l(controller.class.condition_value_for_datetime(value, :to_date))}"
end

condition_for column override

Responsible for generating our special sql condition.

players_controller.rb
def self.condition_for_date_of_birth_column(column, value, like_pattern)
  ["#{column.search_sql} = ?", column.column.type_cast(value)]
end

My example used an override for one specific column, however you may as well override an existing search_ui type (such as :select or :multiselect) or just create your own search_ui type….

ActiveScaffold: singular polymorphic association inline actionlink

3 Comments

Quite a complicated subject for a post. :-) Hope at least some of you have an idea about the topic of this post.
Activescaffold supports concept of an inline actionlink, which is basically just a list column as a hyperlink. Out of the box it is used for associations to have a one click path to associated records. Unfortunetly, it was nt supported for singular polymorphic associations and in one of my projects I was missing this feature. I had to implement a log feature using a database as store. Each log statement should include a reference to the source. source was a perfect candidate for a polymorphic association.
I just had to extend activescaffold to support singular polymorphic assocation inline actionlinks.

I ve prepared a simple example app including invoice and applog model for those of you who are interesting in trying out.

Setup Example Application

  1. Download: one_step_active_scaffold
  2. Download: polymorphici_blog.rb and rename it to model_setup.rb
  3. Execute following command: ruby one_step_activescaffold.rb polymorphic_app jquery
  4. cd polymorphic_app
  5. rails s
  6. http://localhost:3000/invoices
  7. Create new invoice with a log entry
  8. http://localhost:3000/app_logs

ActiveScaffold: Actionlink Parameters

4 Comments

A real quick one for today.

We will take a closer look at which options you have to pass addional url params to action_links.
My example is based upon my post: ActiveScaffold Confirmation of Actionlinks

Static Parameters

A static parameter is a simple key-value pair inside of a hash you may define for your actionlink:

class PlayersController < ApplicationController
  active_scaffold :player do |conf|
    ....
    conf.action_links.member.add 'fire', :confirm => 'are_you_sure', :type => :member, :method => :put, :position => false, :ignore_method => :ignore_fire_action?, :parameters => {:static => 'static'}
    ....
  end
end

Reload players controller in your browser and you see the new parameter ‘static’ attached to each fire action_link.

Dynamic Parameters

Static Parameters are nice, but well they are static.
However, it s also possible to do dynamic one s a s well and actually it s quite easy.

class PlayersController < ApplicationController
  active_scaffold :player do |conf|
    ....
    conf.action_links.add 'fire', :confirm => 'are_you_sure', :type => :member, :method => :put, :position => false, :ignore_method => :ignore_fire_action?, :parameters => {:static => 'static'}, :dynamic_parameters => Proc.new {{:dynamic => random_number}}
    ....
  end
end

We ve added the following to our action_link definition: :dynamic_parameters => Proc.new {{:dynamic => random_number}}
A simple Proc Object which is returning a hash including a key ‘dynamic’ with a value… random_number…. ??
Let me explain: our Proc object is called in scope of your helpers, which means you have access to any helper methods.
Therefore we define a helper method ‘random_number’ in our players_helper:

players_helper.rb
module PlayersHelper
  def random_number
    400 + rand(100)
  end
end

Wish you a great weekend.

ActiveScaffold: Single Table Inheritance (STI)

6 Comments

Today, I would like to show you an improved way to manage Single Table Inheritance with ActiveScaffold. It s a real time safer.
STI Models should hopefully have a big overlap of attributes, which is the base assumption for my implementation of STI in ActiveScaffold.

Basically, we will show an index view of parent sti controller, in which member actions such as edit, show,delete are linking to corresponding specific sti controller. Create link opens a popup menu showing the different sti models.

That s the concept let s try it out. I will use an example from the book: “Agile Web Development with Rails” written by Dave Thomas and David Heinemeier Hansson.
That example consist of the following models:

  1. Person (sti_parent)
  2. Customer
  3. Employee (Parent of Manager)
  4. Manager

It s quite tedious to set up an application with these models, therefore we will shorten that process.

Setup Example Application

  1. Download: one_step_active_scaffold
  2. Download: sti_blog.rb and rename it to model_setup.rb
  3. Execute following command: ruby one_step_activescaffold.rb sti jquery

Great, we ve prepared our example application, now we have to configure our controllers

Controller Inheritance

ActiveScaffold configuration can be inherited, which is quite useful for sti, because many columns are shared.

class CustomersController < PeopleController
class EmployeesController < PeopleController
class ManagersController < EmployeesController

Definition of column set for each Controller

We have to define for each controller, which columns we would like to use. Please note that we do not have to define anything specific for manager controller because it should use same settings as employee controller.

class PeopleController < ApplicationController
  active_scaffold :person do |conf|
    conf.columns = [:type, :name, :email]
  end
end 
class CustomersController < PeopleController
  active_scaffold :customer do |conf|
    conf.columns = [:type, :name, :email, :balance]
    conf.update.columns = [:name, :email, :balance]
    conf.create.columns = [:name, :email, :balance]
  end
end
class EmployeesController < PeopleController
  active_scaffold :employee do |conf|
     conf.columns = [:type, :name, :email, :boss, :department]
     conf.update.columns = [:name, :email, :boss, :department]
     conf.create.columns = [:name, :email, :boss, :department]
  end
end 

Parent Controller STI Child Models

One last step, we have to tell the parent controller, which sti_models he should be able to manage.

class PeopleController < ApplicationController
  active_scaffold :person do |conf|
    ....
    conf.sti_children = [:employee, :customer, :manager]
    ....
  end

Our Application is ready, start the server and go to: localhost:3000/people.

Hopefully, you will benefit as much as I did.

Older Entries

Follow

Get every new post delivered to your Inbox.