I recently had a bunch of duplicate code in my controller specs and wanted to share a solution I arrived at that DRYed things up significantly. Feel free to use it and abuse it and if you have recommendations on how to improve it, let me know.
First, the quick requirements and background: all controller methods must explicitly demonstrate that guests do not have access to certain resources and are told, upon any attempt to access them, that they must login in order gain access.
The first pass was to write the tests, get all to pass, then look to consolidate and improve the suites. Here's the first pass:
These specs duplicate an awfully large amount of code. This is an issue because more LoC translate into more maintenance, more potential areas to make mistakes, and more places that need to be updated when things change. (For example, what if we want to flash an alert of "OH NOZ, SIGN IN FIRST PLZ"? I could find and replace, but that should be handled in one place.)
In order to remedy these issues, I added a controller macro that includes #it_should_block_access_to(action, options = {}), which is passed an action (:index, :create, :update), maps each to a default method (:get, :post, :put) if none is specified, and generates the appropriate routing (member vs collections). It also handles format specification, such as :json, or setting any other arbitrary params via the options hash.
Here's the refactored code:
The controller specs are cleaner, a big chunk of code duplication has been removed, and there is a main handler for these tests that does not (in my opinion) add too much complexity into the testing suite.
You Should Check This Out
Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts
Tuesday, August 9, 2011
Saturday, January 30, 2010
Don't Be A Fatty (Rails Controller)
WYMC
(why you might care is my effort to help you make your decision to read this posting more efficient. WYMC is a quick summary and the high level questions I pose so you can determine if you'd like to read the whole article or not)
---
During a project's build, I decided to use the ApplicationController / session store to implement a high level, expandable filtering system. Design decision allowed quick development, but fattened up the ApplicationController and feels more like "The PHP Way" rather than "The Rails Way". Critiques of the currently implemented system and viable alternatives requested.
(why you might care is my effort to help you make your decision to read this posting more efficient. WYMC is a quick summary and the high level questions I pose so you can determine if you'd like to read the whole article or not)
---
During a project's build, I decided to use the ApplicationController / session store to implement a high level, expandable filtering system. Design decision allowed quick development, but fattened up the ApplicationController and feels more like "The PHP Way" rather than "The Rails Way". Critiques of the currently implemented system and viable alternatives requested.
As some of you know, I accidentally bid on a consulting project a month or so ago and was awarded the mandate. Oops. One of the major requirements for this application was a filtering system that was user specific and semi-persistent (filters needed to hang around while the user was logged in, despite jumping around to different views). The filtering system needed to be easily added to a model / view as per the client's request and adding / removing which criteria were "filterable" needed to be immensely flexible (as it was not set in stone what dimensions we would want to filter upon for any of the given models). The number of total users using this application at any one time would be very low. Oh yeah, and all this needed to be built super fast.
With an eye on speed of initial development and ease to quickly and seamlessly expand / extend down the line I set off down the coding road. I ended up with the following work flow for the filtering system. I look to you, internet, for feedback.
I leveraged the
ApplicationController and a before_filter to scan for any new filters being added or removed via the params hash on each page load (ApplicationController#handle_filters). This method either adds or removes a specified filter to or from the session hash, for example:session["#{controller_name.downcase.singularize}_filters"].push( params[:filter_to_add] )I thought about associating the filters with the currently logged in User, but this seemed a bit overkill, especially given my time constraint. The session, while somewhat bush league, seemed like a good fit.
Next I created a
shared/filter_controls partial that can be dropped into any model's view, which renders a standard filter control with a pre-populated list of acceptable filters for the current controller / model. Now filters can be added / removed from the session and a nice UI component is readily available. How do they actually trim down the items returned? To make a model "filterable" I added a
#find_all_matching method and pass it the model-specific session key (Dinosaur.find_all_matching(session[:dinosaur_filters]). This method finds all if the passed filter hash is blank, otherwise it processes the filters and sets up the appropriate set of conditions to apply to the query.Positives: quick build; dead simple to add a filter; ApplicationController handles where to put the specified filter based upon the current controller; filters are semi-persistent; making a model "filterable" just requires adding a custom method to process any associated filters.
Negatives: Feels more like "The PHP Way" rather than "The Rails Way"; Fat ApplicationController:
Gross. Look at all that meat.
Given the current system, where are areas of improvement? Given more time to develop, what would be a superior set up?
Labels:
rails
Tuesday, January 5, 2010
Riding the Rails Train
A few weeks back I decided to bite the bullet and get on the Rails Train. Complete confusion was my first thought upon reading through a few Rails tutorials. There is no end to the number of articles about Rails on the web (now there is one more!) and I was feeling a little lost trying to determine where to start. So I figured I'd start with the basics and began reading about Ruby (created by Matz in Japan), the base language of Ruby on Rails.
I purchased Ruby by Oreilly and read through it diligently, computer always at the ready to test out examples from the tome. Ruby takes a little while to get your head around, especially if you are a closet, wannabe coder n00b like myself. For example, I was used to iterative loops such as (for PHP):
for($i=0; $i > myCollectionOfThings.len(); $i++) { prints myCollectionOfThings[$i] }
What the heck are blocks? Yield? Bah! Needless to say, it took a few days to get used to.
So now with just enough Ruby under my belt to make me dangerous, I moved on to Rails. I started with the basics again, reading through each section of the Ruby on Rails website. I was thrust into a land of migrations, YAML, DRYing, REST, MVC (model, view, controller), routing, and other magical concepts.
As I pushed forward from topic to topic, making progress here, hitting a wall there, it started to make sense why the Rails logo-of-choice is a train. I started to feel like a coding conductor, all my objects easily aboard my locomotive, quickly eating up the track and rushing towards project creation (wow, that was possibly the nerdiest thing I've said in a long, long while). I remember being amazing, even feeling a little sorry for myself for not getting aboard sooner! I started to see projects written in other languages would take immensely longer to develop, and worse, would not be as elegant, well organized, and easily shared. The MVC framework and "Convention Over Configuration" principle guide Rails developers, from the newbies to the veterans, on how a basic web-app is structured, how to use as little code as is efficiently and "readibly" possible, and what goes where.
The train metaphor was also driven home recently when I was trying to deploy an app on a remote server. I felt like I was in the middle of a train wreck. As wonderful and beautiful as Rails is, it is also a complicated organism with a lot of deep, moving parts. Rolling out an application has even more layers to it and computers don't really do "hey, you *almost* got the configuration right", but rather just crash.
But after spending about and hour or two tracking down my issue and debugging (more to come on this via another post), I pulled myself from the twisted metal and wreckage, relieved and happy again. Rails does a great job of telling you where it is getting tripped up so you can Sherlock Holms your way out of it (the issue ended up being an outdated gem the hosting service was forcing me to unknowingly use, rather than my nice, shiny, up-to-date ones). Using a different technology likely would have taken me several times longer.
Gems bring another element to Rails that really should make anyone thinking about getting into Rails jump on in. The iPhone is the beautiful gadget, but the apps put it head and shoulders above the rest. Same with Rails. There is a vibrant, talented, and witty group of developers that contribute amazing add-ons to a Rails application ("There's a gem for that" anyone?).
It really amazes me how abstracted web development has become. A year or two ago I was writing straight HTML, crafting SQL statements, writing login scripts, and the like. Now its all HAML, find_by_this's, acts_as_auth, etc. While I initially felt a bit disconnected by this, the benefits it brings are immense: less time worrying about stuff that has been done and tested a thousand times leaves more time to build a better service and be a better developer.
I purchased Ruby by Oreilly and read through it diligently, computer always at the ready to test out examples from the tome. Ruby takes a little while to get your head around, especially if you are a closet, wannabe coder n00b like myself. For example, I was used to iterative loops such as (for PHP):
for($i=0; $i > myCollectionOfThings.len(); $i++) { prints myCollectionOfThings[$i] }
What the heck are blocks? Yield? Bah! Needless to say, it took a few days to get used to.
So now with just enough Ruby under my belt to make me dangerous, I moved on to Rails. I started with the basics again, reading through each section of the Ruby on Rails website. I was thrust into a land of migrations, YAML, DRYing, REST, MVC (model, view, controller), routing, and other magical concepts.
As I pushed forward from topic to topic, making progress here, hitting a wall there, it started to make sense why the Rails logo-of-choice is a train. I started to feel like a coding conductor, all my objects easily aboard my locomotive, quickly eating up the track and rushing towards project creation (wow, that was possibly the nerdiest thing I've said in a long, long while). I remember being amazing, even feeling a little sorry for myself for not getting aboard sooner! I started to see projects written in other languages would take immensely longer to develop, and worse, would not be as elegant, well organized, and easily shared. The MVC framework and "Convention Over Configuration" principle guide Rails developers, from the newbies to the veterans, on how a basic web-app is structured, how to use as little code as is efficiently and "readibly" possible, and what goes where.
The train metaphor was also driven home recently when I was trying to deploy an app on a remote server. I felt like I was in the middle of a train wreck. As wonderful and beautiful as Rails is, it is also a complicated organism with a lot of deep, moving parts. Rolling out an application has even more layers to it and computers don't really do "hey, you *almost* got the configuration right", but rather just crash.
But after spending about and hour or two tracking down my issue and debugging (more to come on this via another post), I pulled myself from the twisted metal and wreckage, relieved and happy again. Rails does a great job of telling you where it is getting tripped up so you can Sherlock Holms your way out of it (the issue ended up being an outdated gem the hosting service was forcing me to unknowingly use, rather than my nice, shiny, up-to-date ones). Using a different technology likely would have taken me several times longer.
Gems bring another element to Rails that really should make anyone thinking about getting into Rails jump on in. The iPhone is the beautiful gadget, but the apps put it head and shoulders above the rest. Same with Rails. There is a vibrant, talented, and witty group of developers that contribute amazing add-ons to a Rails application ("There's a gem for that" anyone?).
It really amazes me how abstracted web development has become. A year or two ago I was writing straight HTML, crafting SQL statements, writing login scripts, and the like. Now its all HAML, find_by_this's, acts_as_auth, etc. While I initially felt a bit disconnected by this, the benefits it brings are immense: less time worrying about stuff that has been done and tested a thousand times leaves more time to build a better service and be a better developer.
Labels:
nerd alert,
rails,
ruby
Subscribe to:
Posts (Atom)
