Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts

Monday, April 21, 2008

Harvesting Fixtures from a Live Rails project

So I've put together a simple plugin for grabbing fixtures out of a Live Rails project.


Harvixture is a tool that can be used to extract data, in the form of fixtures,
from a Rails project. It is done by pointing the harvixture at a request_path
and dumping fixtures for all "found" ActiveRecord objects.

$ sudo gem install jeremyf-harvixture --source=http://gems.github.com/

$ cd path/to/rails/project

$ . harvixture run --request_path=/users/1/calendars./bug_100/users.yml
./bug_100/calendars.yml


Get the plugin here.
http://github.com/jeremyf/harvixture/tree/master

TODO:
- Possibly convert to a gem.

Monday, April 14, 2008

A little bash script hacking

Puttering around the blog-o-tubes, I discovered an awesome little post concerning git and your command line. The problem with the script is its too limiting. I use both svn and git. So enter my little hack.

In ~/.bashrc


function parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(git::\1)/'
}
function parse_svn_branch() {
parse_svn_url | sed -e 's#^'"$(parse_svn_repository_root)"'##g' | awk -F / '{print "(svn::"$1 "/" $2 ")"}'
}
function parse_svn_url() {
svn info 2>/dev/null | grep -e '^URL*' | sed -e 's#^URL: *\(.*\)#\1#g '
}
parse_svn_repository_root() {
svn info 2>/dev/null | grep -e '^Repository Root:*' | sed -e 's#^Repository Root: *\(.*\)#\1\/#g '
}
function parse_scm_branch() {
local value=$(parse_git_branch)
if [ -z "$value" ]; then
local value=$(parse_svn_branch)
fi
echo "$value"
}

export PS1="\[\033[00m\]\u@\h\[\033[01;34m\] \w \[\033[31m\]\$(parse_scm_branch) \[\033[00m\]$\[\033[00m\] "



For git directories

jeremyf@reclusive-geek ~/Documents/Repositories/git/ccs_portal (git::master) $


For svn directories

jeremyf@reclusive-geek ~/Documents/Repositories/svn/ccs_main/app/controllers (svn::ccs_main/trunk) $


If anyone knows how to tighten this script up, let me know.

Wednesday, August 08, 2007

Digging for method signatures

This evening I decided to write some UML for the permission sub-system that I'd been working on. I decided that I really should have it in dot format because 1) I want to learn it better, 2) its plain text and thus more "Pragmatic", 3) there are rails gems (i.e. railroad) that can generate dot format. So I wrote the dot file (with inspiration from a railroad output). During the process, I decided to include the method signatures, which definitely helps to explain what's going on.

Then, I decided that I'd really like my classes to be able to generate this UML (in particular I wanted the method signature). So with a little thinking, I started to roll my own, but it gets a bit complicated parsing the source file and determining what's going on. Fortunately, after 30 minutes, I realized that RDoc does the hard-work for me, and all I need to do is parse the html.

Enter RdocInspector. It takes a string and finds and collects the Public Class methods, Public Instance methods, and Protected Instance methods (as well as their arguements). The string is the result of File.read(some_rdoc_html_file).

My UML automation work is not yet done, but the issue of what methods are explicitly defined in a given file has been solved.

Wednesday, July 18, 2007

Mocha

So my previous post talked about overriding delegation. Then I remembered, Mocha. An honest to goodness stubbing/mocking library. Rather obviously, when testing units, I really don't want to fiddle around with the interaction of objects in each and every instance.

So instead of my convoluted example of conditionally_overriding_delegation, I can do the following (but hey you may have already known this):


class AbstractUnit
extend Forwardable
def_delegate :foo, :name

def has_name?
!(name.nil? || name.blank?)
end
end

class AbstractUnitTest < Test::Unit::TestCase
def setup
@object = AbstractUnit.new
end

# Perhaps a reasonable test for delegation?
def test_should_delegate_name_to_foo
assert_equal @object.name, @object.foo.name
end

def test_has_name_should_be_false_if_name_is_empty
@object.stubs(:name).returns('')
assert ! @object.has_name?
end

def test_has_name_should_be_false_if_name_is_nil
@object.stubs(:name).returns(nil)
assert ! @object.has_name?
end

def test_has_name_should_be_false_if_name_is_blank
@object.stubs(:name).returns('hello world')
assert @object.has_name?
end

end


Even in this case, Mocha is rather useful, I don't have to worry about testing the coupling that occurs due to delegation, and can test the objects responsibilities. Of course, now its time to look more into Mocha's documentation. It looks like there is some rather amazing stuff that can be done.

Monday, July 16, 2007

assert_interface

I've been reading quite a bit on programming these days. And one of the things I'm liking more and more is the concept of programming for the interface.
Now I use Ruby and not Java or C# or other languages, so the idea of the interface is a bit different. I can implement them, kind of, via a module.
However, I want to make sure that my objects quack appropriately. Enter assert_interface(object, module). The object is assumed to be an
instance of a class. The module is assumed to be "well-formed" for interfaces

module WellFormedInterface
def self.included(base)
base.send(:include, InstanceMethods)
base.extend(ClassMethods)
end

module ClassMethods
end
module InstanceMethods
end
end

class AbstractUnit
include WellFormedInterface
end




class Test::Unit::TestCase
# Given an object (i.e. Foo.new), and a module (i.e. SomethingCool)
# assert that all methods explicitly defined in the module
# are present in the object.
#
# This isn't the end all test, as you are still responsible for
# testing the implementation of the interface
def assert_interface(object, mod)
methods = {object => [], object.class => []}

mod::InstanceMethods.instance_methods(false).each do |method|
methods[object] << method unless object.respond_to?(method)
end

mod::ClassMethods.instance_methods(false).each do |method|
methods[object.class] << method unless object.class.respond_to?(method)
end

msg = []
methods.each_pair do |key, value|
msg << "Expected #{key} to respond to the following :#{value.join(' :')}" unless value.empty?
end

if msg.empty?
assert true, 'Hey, it all worked'
else
flunk msg.join('. ')
end
end
end

Monday, May 21, 2007

Corrupt Session?

The system that I have been working on for the past while had a particularly vexing problem. Every morning, I had to restart the mongrel cluster. The reason, each morning the login process just stopped working. It was especially annoying, because I had one shot each day to attempt to fix it (to apply the fix, I had to restart the server). After a two weeks of various attempts at correcting the problem, we finally stumbled upon the problem.


class ApplicationController < ActionController::Base
session :session_expires => 12.hours.from_now
end


I was assuming that a session would gracefully expire 12 hours after first being created. In fact, what was happening is the session would expire 12 hours after the server restarted. So once twelve hours would pass, each request would generate a new session that would in essence be immediately expired. Not exactly the behavior I was looking for.

So if you are looking for corrupt session, session corruption, or unresponsive mongrel it may just be related to the code above.

Wednesday, May 09, 2007

A "Best Migration Practice"

So today, I discovered a "good idea" for migrations.

Put your database schema changes in one migration, then your data changes in the next. This becomes self-evident when you mix the two and watch as part way through your migration, the data change fails and suddenly your tables have been altered.

Sunday, April 01, 2007

Complex views and conventional trickery

This past month, I've been working on a Content Management System (CMS). The CMS is comprised of many sites, each of which have many subwebs (permissioned collections), each site has many pages, each page has many content blocks. The navigation tree of a subweb is a polymorphic relation. So when generating the navigation in the CMS Admin, I use the following method:


module PagesHelper
# Generate a link to the show page of navigation node
def link_to_node(node)
link_to( h(node.display_title), send(*node.route_for_show), :class => node.published? ? 'published' : 'unpublished' )
end
end


In the above example, node the send(*node.route_for_show). I ask the node (which is either a subweb or a page to tell me how to generate the path.


class Page < ActiveRecord::Base
def route_for_show
return :page_path, subweb, self
end
end
class Subweb < ActiveRecord::Base
def route_for_show
return :subweb_path, self
end
end


So the module knows the method to generate its path, but can't actually build the URL. So it passes the information back to the helper, which can generate the path.

I don't know if this is the best way to do it, but its working for me (and I've used this pattern for rendering links for another polymorphic relation).

Tuesday, February 20, 2007

The ever evolving test suite

I've been working on a rather http://www2.blogger.com/img/gl.link.giflarge rails project and have watched my testing style evolve. And this next step, inspired by Jay Fields has really got me excited. Take a peek.


class Test::Unit::TestCase
class << self
def test(*names, &block)
test_name = :"test_#{names.join('_').gsub(/[^\w ]+/, '').gsub(/ +/, '_')}"
raise ArgumentError, "#{test_name} is already defined" if self.instance_methods.include?( test_name.to_s )
if block_given?
define_method( test_name, &block )
else
define_method( test_name) do
$stderr.puts "Incomplete test '#{names.join(', ')}' @ #{caller.last}"
end
end
end
end
end


What the above method does is allows for very "fast and loose" test definitions. For example, I can do the following:


class Foo < Test::Unit::TestCase
# This is a place holder test, and when ran will
# print a warning
test 'this will print a warning when the test runs'

# This is a genuine test
test 'this will assert true' do
assert true
end

# This is another genuine test, but I'm providing visually
# putting the test in another namespace
test :index => 'should render a list' do
assert_template 'list'
end
end


Ultimately, I'm extremely satisfied with how this works. My functional tests can have test definitions with a prefix of the action.

I have updated my Textmate so I can Run Focused Unit Tests, as well as having test highlighted like other keywords (alias, class, require, etc.)

Wednesday, February 07, 2007

Hpricot, screen_scrape, and horrible pages

I'm an avid Dungeons and Dragons player and have decided I'm going to make a Rails application that will help consolidate the rules for personal use.

So I decided to do a screen scrape http://www.wizards.com/default.asp?x=dnd/lists/feats . I wanted the list of feats. To get to the table, and each row of feats, here was the xPath I used:

"/html/body/table[2]/tr[2]/td[3]/table/tr[3]/td/table[4]/tr/td/table/tr"

Of particular note, I was having trouble with the tbody in xPath, so I removed those references. Let me tell you, without Firebug's copy xPath, I would've went insane getting this out.

So I ask, why the heck does Wizards of the Coast use table based layouts. They hurt my brain hurt.

If you are interested here is the code that I wrote. It doesn't have any underlying tests, but I ran the import without fail, so for now, its good enough for me.


class Feat < ActiveRecord::Base
validates_presence_of :name, :description
belongs_to :rule_source

class << self
# Run the import, first getting the RuleSources
def import!
File.open(File.dirname(__FILE__) + '/feats.html', 'w') do |f|
f.puts (open("http://www.wizards.com/default.asp?x=dnd/lists/feats").read)
end
doc = File.open(File.dirname(__FILE__) + '/feats.html') {|f| Hpricot(f) }

RuleSource.parse_rule_source(doc)

parse_feats(doc)
end
private
def parse_feats(doc)
feats = (doc/"/html/body/table[2]/tr[2]/td[3]/table/tr[3]/td/table[4]/tr/td/table/tr")
feats.pop # Get rid of the results count

feats.each_with_index do |feat, index|
parse_feat(feat) if index > 1
end
end
def parse_feat(feat)
... Do the actual parsing of the row ...
end
end
end

class RuleSource < ActiveRecord::Base
validates_presence_of :code, :name

class << self
def parse_rule_source(doc, publisher = nil)
(doc/"div.keyboxscroll/table/tr").each do |source|
if source_code = (source/"td[1]")
source_code = source_code.inner_html.strip
end
if source_name = (source/"td[2]/i")
source_name = source_name.inner_html.strip
end

find_or_create_by_code_and_name( source_code, source_name )
end
end
end
end

Tuesday, January 30, 2007

Single Table Infuriation

Single Table Inheritance in Rails is an excellent pattern that works really well, except when it doesn't.


class Parent < ActiveRecord::Base
end

class Child < Parent
end

class GrandChild < Child
end


Each of the above classes are defined in their own files (app/models). There are times in development mode that I've noticed that Child.find(:all) will generate the following SQL:


SELECT * FROM parents WHERE (type='Child')


In Rails 1.2 the fix for this is perhaps a little obfuscated.


class Parent < ActiveRecord::Base
require_dependency 'child'
end

class Child < Parent
require_dependency 'grand_child'
end

class GrandChild < Child
end


Now, the other part of this solution is that you probably want to call require_dependency at the end of the class definition as there may be methods in the parent object that the child object needs. I suppose you could re-open the class at the end of the file.

Wednesday, January 24, 2007

capture, binding and other diabolical machinations

For the South Bend Ruby Group I volunteered to talk about form builders for February. At the time of volunteering, I knew close to nothing about form builders. I decided to take the upcoming meeting as a reason for learning about the builders. And now I'm hooked.

But that is not the point of this particular post. What I'm talking about is the bizarre magic involved in the form builder. In a project that I've been working on we have a need for rendering lists both in tables and in lists. However we'd really like to re-use the view logic in generating the list. Its pretty much a matter of hot-swapping the tags appropriately.


<% list_for(@list) do | l | %>
<% l.caption 'Hello World' %>
<% l.header_row(:id => 'list_head') do %>
<%= l.header_cell 'title' %>
<%= l.header_cell 'edit' %>
<%= l.header_cell 'delete' %>
<% end %>
<% l.row( :class => 'spam', :id => 'hot_dog') do | list_item | %>
<%= l.cell l.title %>
<%= l.cell 'edit_link' %>
<%= l.cell 'delete_link' %>
<% end %>
<% end %>


We opted to create a builder class. You call :list_for, and can optionally pass a builder class (like the one below). Of particular note there is use of bindings and captures. I'm still a bit dazzled by what all is going on (and wrote the tests to make sure it all works). Below is my understanding of what is going on.

1) There is the :concat method (as defined in the ActionView::Helpers::TextHelper module). We are taking the results of first parameter and appending it to the proc's binding. The proc's binding is the result of the stuff in that proc. In the case of the above :list_for its all of the text generated :list_for's block.

2) There is the :capture method (as defined in ActionView::Helpers::CaptureHelper module). This little critter packages up the input block as text. I tried using block.call but this didn't work as intended (I got double the block text). If I didn't want the option of passing options to each of the elements, I could conceptually simplify the method by doing a manual opening of a tag, block.call, then manually closing the tag. This could be a performance piece that I will implement at a later point (though I'll need a Hash#to_markup_attributes method).



def list_for(object, *args, &block)
raise ArgumentError, "Missing block" unless block_given?
options = args.last.is_a?(Hash) ? args.pop.symbolize_keys! : {}
builder = options.delete(:builder) || ListTableBuilder
builder.new(object, self, options, &block)
end

# <table>
# <caption>
# <tr>
# <th />
# </tr>
# <tr>
# <td />
# </tr>
# </table>
class ListTableBuilder

# Welcome to the insanely bizarre initialize function
def initialize(collection, template, options, &proc)
@collection, @template, @proc = collection, template, proc
@options = options.reverse_merge( :class => 'list', :id => 'list')
@template.concat( @template.content_tag('table', @template.capture(self, &proc) , @options ), @proc.binding)
end

# :options are applied to content_tag('td')
def cell(text, options = {})
@template.content_tag('td', text, options)
end

# :options are applied to content_tag('th')
def header_cell(title, options = {})
@template.content_tag('th', title, options)
end

# :options are applied to content_tag('caption')
def caption(text, options={})
@template.concat( @template.content_tag('caption', text, options), @proc.binding )
end

# :options are applied to content_tag('tr')
def header_row(options={}, &block)
raise ArgumentError, "Missing block" unless block_given?
@template.concat( @template.content_tag('tr', @template.capture(&block), options) , block.binding)
end

# Options
# The options will be added to each row.
# :class will append and not destroy the internal classes
# :id will be used as a prefix for the internal id generation
def row(options={}, &block)
raise ArgumentError, "Missing block" unless block_given?
options.symbolize_keys!
@collection.each do | c |
row_options = options.reverse_merge( :class => '' )
row_options[:class] += ' ' + @template.cycle('odd_row', 'even_row')
row_options[:id] += '_' + @template.send(:dom_id, c)
@template.concat( @template.content_tag('tr', @template.capture(c, &block ), row_options), block.binding )
end
end
end

Monday, January 22, 2007

Ruby Cookbook from O'Reilly

Ruby Cookbook: Recipes for Object-Oriented Scripting
by Lucas Carlson & Leonard Richardson

ISBN 0-596-52369-6

I haven't read this book cover-to-cover, but I have given it a very thorough inspection, skimming through sections to find points of interest and then stopping to read those recipes. So, actually, I've probably read a whole bunch of the book and didn't realize it.

The book is written in a very concise manner, with plenty of examples, lots of useful code, excellent cross-references and a very generous license that allows you to use the code from the book (albeit with some restrictions).

Each recipe is presented as a quartet of Problem, Solution, Discussion and See Also sections. Each problem is presented succinctly, and the solutions dive right into the required code (with code comments explaining some of the more complicated lines of code, but not explaining the self-documenting lines). The discussion section of each solution really digs into what is going on, code choices that were made, variations, and basically anything else that might be interesting. The see also section cross-references other recipes that may be pertinent to the given problem.

All told, this book does an amazing job of going through a wide range of problems, from Strings & Numbers to Meta-Programming & Rails and Graphics. This book is loaded with useful bits of information and I would highly recommend adding it to your collection.

Wednesday, January 17, 2007

Some Weird Weird Ruby (or nest Classes and Inheritence)

Thinking cap time. I am working on a CMS and the short of it is that a Page will have many PageBlock objects. The PageBlock is versioned and can have multiple things associated with it (i.e. Links or Files or Other Stuff, thankfully each page block is homogenous). I decided that the Links/Files will be serialized. So I wrote a serializing helper object. This serializing object


class PageBlock < ActiveRecord::Base
class << self
# I want to be able to configure the underlying DataSerializer
# in the containing class
delegate :set_valid_serialized_keys, :to => :data_serializing_class
def data_serializing_class
self::DataSerializer
end
end
class DataSerializer
class << self
def set_valid_serialized_keys( *attrs )
write_inheritable_attribute :valid_serialized_keys, attrs
class_inheritable_reader :valid_serialized_keys
end
end

def add_data(data = {})
data.assert_valid_keys(valid_serialized_keys)
... do the magic ...
end
end
end


Now

$ PageBlock.valid_serialized_keys
=> NoMethodError: undefined method `valid_serialized_keys' for PageBlock::DataSerializer:Class



class PageLinkBlock < PageBlock
set_valid_serialized_keys :path, :name
end



$ PageBlock.valid_serialized_keys
=> [:path, :name]


DOH!!! Man that is definitely not the behavior I want. I guess my nested Class has some behavior that I'm not aware of. I'm certain the behavior is intentional, I just didn't know about it

So the solution?


class PageBlock < ActiveRecord::Base
class << self
# I want to be able to configure the underlying DataSerializer
# in the containing class
delegate :set_valid_serialized_keys, :to => :data_serializing_class

def data_serializing_class
self::DataSerializer
end

def inherited(page_block_subclass)
page_block_subclass.module_eval <<-EOV
class DataSerializer < PageBlock::DataSerializer
end
EOV
super
end

end
class DataSerializer
class << self
def set_valid_serialized_keys( *attrs )
write_inheritable_attribute :valid_serialized_keys, attrs
class_inheritable_reader :valid_serialized_keys
end
end

def add_data(data = {})
data.assert_valid_keys(valid_serialized_keys)
... do the magic ...
end
end
end


Then I define PageLinkBlock and


$ PageBlock.valid_serialized_keys
=> NoMethodError: undefined method `valid_serialized_keys' for PageBlock::DataSerializer:Class


Alas, the behavior that I want. Time to dig out the Pickaxe Book to see if there is a reference to this.

Tuesday, January 16, 2007

Helper Module Testing

First and foremost


sudo gem install ZenTest


Then in your app/test/test_helper.rb add


require 'test/rails'


Then make a test class

class FooHelper
def print_hello_world
content_tag('p', 'Hello World!')
end
end

class FooHelperTest < Test::Rails::HelperTestCase
def test_method_goes_here
assert_equal '<p>Hello World</p>', print_hello_world
end
end


Of particular note, the name of the class that inherits from Test::Rails::HelperTestCase is very important. If the inheriting class name is MyHelperTest, make sure a module named MyHelper exists.

Wednesday, January 03, 2007

Dear RDoc,

One thing that I've quickly taken for granted is RDoc. Prior to RDoc, I lamented that the documentation that I wrote in code was impossible to discover, and thus the in-code documentation was useless for non-programmers. Thus, in order to "say" what the system did, additional documentation needed to be created. So now there were two sets of documents, both saying what the system did, but not communicating with each other.

Oh the humanity!

Enter RDoc. I can now rake that information and generate a useful document defining the system. It gets even better if I start using should-language, either in specify or test context (i.e. specify 'student should have a name' or def test_student_should_have_a_name). Then I can rake just the method names and get a document that says concisely what the system should do.

Of course this leads itself to writing test stubs earlier in the project process. Even better, it is possible for a project manager to write some of these specifications.

Suddenly, the tests, which everyone wants and needs, become useful not only for documentation but also for communication and um... testing. And better yet, the documentation is not discarded nor duplicated.

Saturday, December 23, 2006

Contextual Decoration of a Ruby object

One of my current work problems is that I have an Account class that needs optional decoration if it has been initialized in the context of an Import. I was kicking around an Observer or AspectR but the latter library wreaks of Java, and the former just isn't as seamless as I'd like. Also, I need to be mindful of multiple threads. Below is the solution I came up with, without the problem specific code.

The situation is actually more complicated than what I am modeling below.


class Import
attr_reader :start_at, :stop_at
class << self
def run!(&block)
Thread.new do
Thread.current['import_log'] = import_log = self.new
import_log.start!
yield block if block
import_log.stop!
end
end
end
[:start, :stop].eacha do |m|
define_method("#{m}!") do
instance_variable_set("@#{m}_at", Time.now)
end
end

def log_change(message)
... magical stuff happens here
end
end

class Account < ActiveRecord::Base
attr_reader :import_log
def initialize
@import_log = Thread.current['import_log']
super
end

def after_save
import_log.log_change("Saved Account with ID=#{self.id}") if import_log
end
end


What is actually happening is that I have numerous objects that use the "is_a_import_model" interface. If a class "is_a_import_model" then its initialize method needs to be overwritten to account for the import_log. There is also a StreamParser that is reading an XML document to process the lots of imports. If the StreamParser is created in the context of an Import.run!, it needs to log and rescue all exceptions, otherwise, it re-raises those exceptions.

Tuesday, December 12, 2006

Protected Find

One of the greatest professional compliments I've ever received was regarding a system that I designed and developed. The purpose was to provide an interface for numerous reporting systems to funnel reporting data into a unified location. I designed the system so that all data was retrieved via task specific finders (i.e. :find_agreement_number, :find_current_document_type). As a result, if the database changed, the methods could still be used (albeit with a tweak to the internal logic). Since I was working in a compiled environment where changing a table meant recompiling the functions that directly accessed the table, the need for public finders was required.

The compliment I received was that 6 months after the design was done, the vendor's system that we were integrating with required a major upgrade and changes. And in order to make my system work, they just pointed the finders to a different table, and presto things were working again.

Now what does this have to do with Ruby on Rails? Today I was having a conversation with a co-worker regarding find. After some discussion, I came out and said I really wish :find were a protected method. Find is a very powerful method that allows you to fully access all data from anywhere. This power exposes your your view, controller, and other models to the risk that your model will change. To reduce this risk, I feel it is important to create specific finders in your model and test them. Also, if you use :find_by_name (a free method on any table that has a :name column), you should also write a test for this (at a minimum making sure that your model responds to that particular find).

The entire goal of creating specific finders is to make the maintenance of a project easier. In addition, it is much easier, in Rails, to test models than controllers or views.

Saturday, December 09, 2006

Pimping a Controller

I've recently started working on a REST based rails project. I've been thinking about the Rails model associations, and in particular has_many :through, which is used to help expose the join between two models. Given the following three models:



class User < ActiveRecord::Base
has_many :permissions
has_many :roles, :through => :permissions
end
class Permission < ActiveRecord::Base
belongs_to :role
belongs_to :user
end
class Role < ActiveRecord::Base
has_many :permissions
has_many :users, :through => :permissions
end



I want to be able to look at permissions in a few different ways (and roles and users for that matter). Now, I can modify my PermissionsController to set the object that will be used for all find calls. By default, the PermissionsController will use Permission.find, but if I swap the Permission object for @finder_object, I can set that @finder_object before_filter. See below:



class PermissionsController < ActiveRecord::Base
before_filter :determine_finder_object

def index
@permissions = @finder_object.find(:all)
# respond to stuff, though you might want to use different templates
# based on the finder object
end

def determine_finder_object
if params[:user_id]
@finder_object = User.find(params[:user_id]).permissions
elsif params[:role_id]
@finder_object = Role.find(params[:role_id]).permissions
else
@finder_object = Permission
end
end
end



Now, is this the best practice? I don't know, however, it feels like its something to explore. One noticeable caveat is that if you are using a REST-based controller, you will want to look at all of your Object.new calls and think about replacing them with @finder_object.build.

As an aside, the above controller would probably end up with several chunks of code that were:


if params[:user_id]
#do something for users
elsif params[:role_id]
#do something for roles
else
#do something in the generic
end


What can I do to encapsulate this particular logic? I dug up the code from the :respond_to controller instance method, and created a construct for:



determine do |d|
d.user_id { #do something for users}
d.role_id { #do something for roles}
end



I'm not satisfied with it, but am wondering what I might be able to do.

Tuesday, November 28, 2006

Peepcode's Restful Rails

I just finished watching Peepcode's Restful-Rails video. I highly recommend it. I've followed the RESTful push by the rails team, but I haven't seen the true impact. And now, having watched the video, I'm in the mood to re-factor the existing code for my project. I'm going to refrain from this refactoring because of the complications of the existing code-base. However, I'm curious to see what can come of this. The next project I work on will make use of REST.

http://peepcode.com/articles/2006/10/08/restful-rails