By popular (cue laughter) demand, I have added whitelisting to the popular (cue hysterical laughter) param_protected plugin. It is done via the param_accessible method.
I have also added support to properly handle array params.
For more details (i.e. documentation), please see the README file.
Monday, March 24, 2008
param_accessible
Wednesday, March 19, 2008
An HTML checkbox that submits when unchecked.
I call this... The Fake Checkbox! :)
See, the problem is a normal HTML checkbox only submits to the server when it's checked. If it's unchecked, nothing gets sent to the server. Sometimes I want a value (like, umm, false?) sent to the server whether it's checked or unchecked.
So how do we go about making a checkbox that does this? The idea is simple -- make a normal checkbox with Javascript attached to it that updates a hidden input field when checked or unchecked.
Making a Rails helper to do this is simple -- put this in application_helper.rb.
def fake_check_box_tag(name, checked_values, is_checked, html_options = {})
onchange = html_options[:onchange] || ""
checked_value = checked_values[:checked] || 'true'
unchecked_value = checked_values[:unchecked] || 'false'
unique_id = rand.to_s[-5, 5]
poser_id = "fake_check_box_poser_#{unique_id}"
value_id = "fake_check_box_value_#{unique_id}"
onchange = onchange + "; doFakeCheckBoxClick(this, #{unique_id}, '#{checked_value}', '#{unchecked_value}')"
html_options.delete(:object_id)
html_options[:onchange] = onchange
html_options[:id] = poser_id
html = ''
html += hidden_field_tag name, is_checked ? checked_value : unchecked_value, :id => value_id
html += check_box_tag nil, 'true', is_checked, html_options
html
end
Then you need to define this Javascript function.
function doFakeCheckBoxClick(me, unique_id, checked_value, unchecked_value) {
unique_id = 'fake_check_box_value_' + unique_id
if (me.checked)
$(unique_id).value = checked_value;
else
$(unique_id).value = unchecked_value;
}
Now you can call it
fake_check_box_tag "person[is_female]", { :checked => 'yes',
:unchecked => 'no' },
true,
{ :class => "x-form_field",
:onchange => "alert('checkbox clicked!')" }
In this example, if the checkbox is checked, the following will be true in your action
params[:person][:is_female] == 'yes'
If it isn't checked, then this will be true
params[:person[:is_female] == 'no'
The 3rd parameter (true) says that the checkbox will initially be checked.
Monday, January 21, 2008
Rail Plugin: param_protected
What is it?
It is a Ruby on Rails plugin that provides a param_protected method on controllers much like how there is an attr_protected method for models.
It is a very simple -- all it does it filter out specified parameters from a request.
Why?
Good question... you can ready about why attr_protected sucks here, or you can just read my following little rant...
What's the goal of attr_protected? It's suppose to be protecting us from user input, not from ourselves. When I used attr_protected, I had to refactor tons of code in models, controllers and tests (that already worked well) to not use the mass attribute setters.
Was this massive code refactoring really worth the protection from the very few places were I do something like:
User.new(params[:user])
# or
User.update_attributes(params[:user])
Truth of the matter, I was hardly ever passing params (or a subset thereof) to a mass attribute setter. So no, it wasn't worth the massive refactoring job.
How?
Install from
http://stochasticbytes.com/svn/param_protected/trunk
It is a Rails plugin, so you should use script/plugin install to install it.
From there it's simple. You simply call param_protected from any controller definition.
class MyController < ApplicationController
param_protected :user_id
...
end
That will filter out user_id from params in all your actions in that controller.
You can also give it an array of param names to filter:
param_protected [:user_id, :some_other_param]
You can also pass a second argument to specify which actions to apply the filter to:
param_prtoected :user_id, :only => :some_action
param_protected :user_id, :only => [:some_action, :another_action]
param_protected :user_id, :except => :some_action
param_protected :user_id, :except => [:some_action, :another_action]
You can protect nested params also (removes params[:user][:user_id]):
param_protected 'user/user_id'
Caveats (IMPORTANT!!!!)
Because param_protected is really a before filter (uses prepend_before_filter), you must take special care to ensure that it runs before any of your other before filters!! If it is not, some of your before filters might have access to the protected params.
Tests
rake test
Should work (from the plugin's root dir), though it's far from comprehensive.
Documentation
Please see the README for usage instructions and examples.
Saturday, December 1, 2007
Directory Project plugin for Kate
What is it?
This is a plugin for Kate that mimics TextMate's directory tree view and find files features. In other words, it's a simple project managing plugin where a directory defines a project. See the "Features" section below for screenshots and the "Settings" sections for an explanation of each option.
Requirements
This plugin requires that Kate (obviously) and Pate be installed. If you are having trouble installing Pate on an Ubuntu system, please see this post.
Installation
svn co http://stochasticbytes.com/svn/directory_project
cd directory_project
./install
If that doesn't work, all you need to do mkdir -p ~/.kde/share/apps/kate/pyplugins/directory_project and copy directory_project.py into it.
Features
Tree view of whatever directory is currently opened.

Search-as-you-type find files dialog.

(no picture)
Useful configuration options.

Settings
There are two types of settings: project specific and global. Global options are applied to all directory projects while project specific settings are different for each one. Directory Project remembers the project specific settings between projects via an ini file in
~/.kde/share/apps/kate/pyplugins/directory_project/directory_project.conf
Probably the most important setting is the search type setting. It dictates how the find files dialog generates matches.
Search type exact only hits exact matches. For instance, searching for doc_test will yield the following matches (matching text highlighted):
doc_test.rb
email_doc_test.rb
Search type character matches by character, preserving the order of the characters. Spaces are ignored in the query string for this search type. This search type is the default behavior in TextMate. Searching for doc test yields:
document_test
dashboard_controller_test.rb
Search type word matchs by word, preserving the order of the words. Words are space separated in the query string. This is the default search type for Directory Project. Searching for doc test yields:
document_test.rb
document_controller_test.rb
The name filters settings is a space separated list of file name pattern matches. Any file that does not match one of these patterns will not show up in Directory Project's tree browser or find files dialog. The default setting is * which matches all files. If you only want to match Ruby and HTML files, you can change the name filters to *.rb *.html
The Ignore Files/Directories is a list of files and directories names that will be ignored by Directory Project. Adding a directory here will stop Directory Project from including anything below that directory.
TODO
Make an option to have the find files dialog search on the relative path instead of just the file name, so you can distinguish between app/views/emails/index.html and app/views/documents/index.html by searching for email index.
Wednesday, November 14, 2007
Installing Pate for Kate
Summary
You need to do some hacking to get Pate installed on a (K)ubuntu 7.10 system.
Background
Pate is a really cool plugin for Kate (which is a text editor for my favorite desktop environment). It is a Kate plugin that allows you to write other Kate plugins in Python (instead of C++). It also comes with a few Python-written plugins, mainly to demonstrate what you can do with it.
I had a very hard time installing Pate. The reason being that the author is a Suse user and his configure script
Installation Process
You need to install development headers/libraries for Kate, Sip, KDE Python bindings, QT Python bindings, etc.
sudo aptitude install cmake sip4 python-sip4-dev python-kde3-dev python-qt-dev kate-plugins kdelibs-dev kdebase-dev
If I'm missing anything, please let me know and I'll update this.
Pate-0.5
cd PATE_SRC
wget www.stochasticbytes.com/pate/pate-0.5-ubuntu-patches.tar.gz
tar xzf pate-0.5-ubuntu-patches.tar.gz
Now configure && cd build && make && sudo make install should work as expected.
Here is a link to my Pate-0.5 patches.
Pate-0.5.1
This version isn't available (yet) via the main Pate webpage, but here's a link for you.
cd PATE_SRC
wget www.stochasticbytes.com/pate/pate-0.5.1-ubuntu-patches.tar.gz
tar xzf pate-0.5.1-ubuntu-patches.tar.gz
Now configure && cd build && make && sudo make install should work as expected.
Here is a link to my Pate-0.5.1 patches.
Why?
Why did I go through the trouble of making a blog post about this rather than submit my changes back to the author? Two reasons:
- It's an excuse to make a blog post!
- The author and the kwrite-devel mailing list didn't seem too interested in my plight.
Monday, October 22, 2007
Better Rails Migrations: Retroactive Migrations (r_migrations)
Managing migration version numbers among multiple developers using the same database is a pain in the ass. You know the drill, you svn up, create a migration, try to run db:migrate but low and behold, the db version is already 10 versions above your migration's.
This plugin offers a simple solution: create and manage a "history" table that records which migrations have and have not been run and modify rake db:migrate to behave accordingly.
Simply put, this plugin allows for unrun migrations with version numbers less than or equal to the current database version to be retroactively run via rake db:migrate.
Forcing migrations to run (even if they've already been run)
It also offers a "force migration" feature. I know this is "wrong" and considered bad Rails development practice, but consider this situation. You find out that an old migration that has already been run requires a minor change. Rather than create a whole new migration, wouldn't you rather just fix the old one and rerun it? Well now you can:
rake db:migrate:force down=009_my_migration.rb
(vim 009_my_migration.rb and fix it)
rake db:migrate:force up=009_my_migration.rb
Use this feature with caution, it is conducive of laziness and bad practices.
Installation
UPDATE!!
This plugin has been branched to work with Rails 2.0.x. The url for the version that works with Rails 1.2.x is http://stochasticbytes.com/svn/r_migrations/branches/1.2.x
Automatic
script/plugin install http://stochasticbytes.com/svn/r_migrations/trunk
Manual
cd RAILS_ROOT
svn co http://stochasticbytes.com/svn/r_migrations/trunk vendor/plugins/r_migrations
ruby vendor/plugins/r_migrations/install.rb
If you are installing the plugin for an existing project, check to make sure that the history table got populated properly by running rake db:migrate:history:list.
If you are installing the plugin for a fresh project, the history table will be created automatically for you.
Some Rake tasks (so you can see it working)
This plugin provides some Rake tasks to examine and manage the history table.
rake db:migrate:history:clear
Clears the history table. Use if you want to rerun all your migrations starting at version 1
rake db:migrate:history:init
Clears the history table and populates it with all the migrations defined in db/migrate up to the current database version.
rake db:migrate:history:list
Print out the contents of the history table, just for your own curiosity (and debugging purposes :P)
Documentation
You can view the README file that is included with the plugin or read it online here. Really though, it doesn't say anything more than this blog post.
Sunday, October 21, 2007
Better AppConfig Plugin for Rails
There are plenty of "AppConfig" plugins for Rails already out there, so why did I make one?
1) Break the config files out into different files.
config/app_config.yml # common
config/environments/<environment name>.yml # environment specific
2) Allows for nested sections and preserves object member notation for them.
AppConfig.subsection.item -> 'xyz'
3) Allows for lists (arrays).
(config file)
servers: [ google.com, yahoo.com ]
(application code)
AppConfig.servers[0] -> 'google.com'
AppConfig.servers[1] -> 'yahoo.com'
4) Allows for embedded Ruby.
(config file)
computed_value: <%= 1 + 2 + 3 %>
(application code)
AppConfig.computed_value -> 6
Installation
script/plugin
script/plugin install http://stochasticbytes.com/svn/app_config
or with SVN
cd RAILS_ROOT
svn co http://stochasticbytes.com/svn/app_config vendor/plugins/app_config
Documentation
See the README included with the plugin or read the online documentation here.
Testing
All the unit tests should pass.
cd RAILS_ROOT/vendor/plugins/app_config
rake test