Wednesday, February 20, 2008

Argument of code

Just finished a tasty refactoring of authentication code. Here's the original:


# To skip this in a subclassed controller:
# skip_before_filter :check_authorized
#
# Aaa! To many indentions!
#
def check_authorization
return true if current_user_super_user?
unless
current_user.roles.detect do |role|
role.rights.detect do |right|
if right.controller == controller_name
right.actions.split(' ').detect do |action|
(action == "all") or (action == action_name)
end
end
end
end
flash[:notice] = "You are not authorized to view the page you requested"
request.env["HTTP_REFERER"] ? (redirect_to :back) : (redirect_to home_path)
return false
end
end


And here is the refactoring. Comical function names!


# To skip this in a subclassed controller:
# skip_before_filter :check_authorized
#
def check_authorization
return true if current_user_super_user?
unless current_user_has_rights?
flash[:notice] = "You are not authorized to view the page you requested"
request.env["HTTP_REFERER"] ? (redirect_to :back) : (redirect_to home_path)
return false
end
end

def current_user_has_rights?
current_user.roles.detect do |role|
current_user_has_rights_as role
end
end

def current_user_has_rights_as(role)
role.rights.detect do |right|
next if right.controller != controller_name
current_user_has_rights_to_action(rights.actions)
end
end

def current_user_has_rights_to_action(actions)
actions.split(' ').detect do |action|
(action == "all") or (action == action_name)
end
end


Of course, I was refactoring it because it was broken and I couldn't make heads or tails of who was winning in what seemed like an argument of code. So I broke it up and made everyone do their little thing in words I might remember when I come back to this code. And now we have peace.

Refactoring and thoughts

Just refactored the app without a net -- yeesh, feels so hackish, looking forward to getting tests working so I can do the agile thing like everyone else.

Refactored the authentication and implemented authorization at the controller action level, based on a Rails Recipe. Looks good and seems to be working again (I hate not having tests!!!)

One thing I noticed was that I did all kinds of bad practices to refactor the code and it wasn't difficult to gets the app working again. That's totally badass that I can hack at the code with a blunt machete and still end up with something that works. (I hate not having tests!!!!)

My thoughts: I love resouces_controller, what a kick-ass plugin! It handles enclosing resources (the opposite side of scope for associations) and all kinds of restful routing. I'm still learning how to use it but it looks truly phenomenal. Like it keeps the requests safe and channeled into proper actions. Way cool!

Found a plugin/gem/generator called SecurityModel. Looks like another plugin, like acts_as_state_machine, that I can hack and hopefully give back to the community with enhancements and tests. It overrides Rails accessors to implement permissions at the model attribute level. Way cool and apparently it works but it's trying to do too much, implementing authentication and multi-level access. It would be better if the code for the model security were extracted and refactored into something that was focused and stable with good testing.

I think these two plugins (acts_as_state_machine and ModelSecurity) will be part of the core of the new app so I may create two development projects so I can study them and build specs and refactor them. That will help me understand what they do and get on the BDD band wagon and hopefully whip them into a new shape that I can offer back to the community.

Sent an email to the Heroku folks introducing the app. Heroku looks like a potentially good home for the app since it offers scaling, easier deployment, the ability to modify the code remotely from practically any machine, seemingly stable/no fuss hosting and access to hella bad ass coders and funding. Looking forward to hopefully launching the app there.

Sunday, February 17, 2008

BDD choices

Investigating alternatives to rSpec and have found the following:

Shoulda -- http://thoughtbot.com/projects/shoulda -- a plugin for Test::Unit framework that adds BDD methods.

Mocha -- http://mocha.rubyforge.org/ -- for stubbing and mocking.

I know that rSpec has a large following but there seem to be some issues with it:
  • It runs as a separate rake task which creates problems for tools like Heckle that run tests and analyzes test output
  • It has some funky patterns that involve lambda in which the readable language breaks down (http://giantrobots.thoughtbot.com/2007/3/9/specin-rspec-with-rails)
  • I've read blogs in which the authors love rSpec and BDD but advise running Test::Unit in addition
More information: http://giantrobots.thoughtbot.com/2007/4/6/shoulda-coulda-woulda

I'll probably go with the Shoulda plugin. It's in the recent Advanced Rails Recipes book so it must be legit. (Seriously, I hope it sticks around but at least I can get going on it now.)

Current step, next step and CMS

At this point, I have user accounts working with roles management.

Eventually, I'll need some form of CMS for the site's "static" pages. It looks like Goldberg (http://www.goldberg.240gl.org/home) is a good choice but it might be the best choice for some of the other websites I need to build. Roxanne (http://www.m42.ch/projects/roxanne/information.php) may be a better choice since it's simpler.

So next steps:
  • Need to start BDD for equipment
  • CSV import of equipment inventory tree
  • Addresses model for users (user has_many addresses)
  • Phones model for users (user has_many phones)
  • Comments (polymorphic)
  • Categories (polymorphic, self-referential tree)
  • Rights (role has_and_belongs_to_many rights)
I believe that any of these next steps can be designed via BDD.

Saturday, February 16, 2008

Implementation Start

After watching tons of screencasts and reading books and hacking some Rails 1.2 websites, I began implementation of the reservation system app by creating a User model and Role model. Added restful authentication , OpenID authentication and state machine to keep track of account activation.

Installed rSpec but have not begun adhering to BDD yet since the authentication plugin generated a whole bunch of code in my user model/controller and sessions controller. BDD seems to work best before writing a bunch of code so I plan to use it once I get user and roles and rights working.

Got it working with a fairly decent CSS design. Pretty cool to see the first inklings of the app. The RoR is pretty cool how the framework makes it easy to create pretty sophisticated behavior with just a few lines of code.

So far, I've installed the following plugins:

- acts_as_state_machine (I'd like to hack an extension to this plugin to make it more powerful, I think the app will be making extensive use of this plugin.)

- haml (Although I'd rather use Markaby since it's Ruby, I like how Haml lets me create XHTML compliant pages that enforce indentation for specification and output.)

- navigation_helper (I decided to use this plugin to create a tabbed navigation system that keeps track of authentication and routing. I'm hoping that it will be useful for sidebar navigation as well.)

- resources_controller (Lets me create standard RESTful CRUD with one line. Lazy!)

- rspec and rspec_on_rails (I'm hoping this is not a rabbit hole. It seems like it's ignoring the entire test unit framework. I'm seeing more activity for other frameworks like "shoulda" that builds BDD on Test Unit, but I'm also seeing posts about rSpec being adapted for Test Unit. We'll see.)

- open_id_authentication (Haven't tested this yet but I intend to allow OpenID for user authentication and signup.)

Haven't gotten user signup working yet, haven't tried it out yet. Need to get SMTP working on my macbook so I can send out activation and invitation emails.

Next up is getting roles assignment working and adding the rights management by adding a Rights model and RightsRoles table.