Basic Named Scopes
Cal it tiny, I don’t care. I’ve made a gem named BasicNamedScopes.
Basic Usage
I was fed up with writing:
Post.all(:conditions => { :published => true }, :select => :title, :include => :author)
So with BasicNamedScopes, you can now write:
Post.conditions(:published => true).select(:title).with(:author)
All named scopes are called the same, except for include, which is now called with, because include is a reserved method.
Reuse them by making class methods:
class Post < ActiveRecord::Base def self.published conditions(:published => true) end def self.visible conditions(:visible => true) end def self.index published.visible end end
Also, the all-method is a named scope now, so you can chain after callling all, for greater flexibility.
Post.all.published
Arrays can be used as multple parameters too, sparing you some brackets.
Post.with(:author, :comments).conditions("name LIKE ?", query)
The read_only and lock scopes default to true, but can be adjusted.
Post.readonly # => same as Post.all(:readonly => true) Post.readonly(false) # => same as Post.all(:readonly => false)
Why?
NamedScopes are really handy and they should play a more central theme in ActiveRecord. While I heard that Rails 3 will support similar syntax, there is no reason to wait any longer.
I find defining named scopes very ugly, especially when dealing with parameters. Just compare the amount of curly braces!
# Using normal named scope:
named_scope :name_like, lambda { |query| { :conditions => ["name LIKE ?", query] } }
# Using BasicNamedScopes
def self.name_like(query)
conditions("name LIKE ?", query)
end
Also, regular named scopes don’t support using other named scopes at all!
I found myself implementing these named scopes (mostly conditions, but others too) so often, that a little gem like this would be the obvious choice. Use it if a gem like searchlogic is overkill for your needs.
Installing
The gem is called “basic_named_scopes”. You know how to install it.
gem install basic_named_scopes
Use it in Rails:
config.gem "basic_named_scopes"
Update
The syntax is fully compatible with ActiveRecord 3, and if you’re using ActiveRecord 3, you don’t need to use this gem.
Cucumber 0.5 and my little commit
The strangest thing happened to me this week. I was working on a little side project at work. It seemed like a nice time to try out some new gems (Bundler and Devise: love it! InheritedResources, not that much, Formtastic, very nice). I was experimenting with Cucumber and writing Dutch features too.
I had figured that the supplied dutch keywords in languages.yml were not very practical. It supplied “Gegeven” as a naive translation of “Given“. Although this is correctly translated, it is very unpractical to form Dutch sentences with it. The only to really start a sentence with “Gegeven” is to write “Gegeven het feit dat…” (“Given the fact that…“). It’s very strange to say “Gegeven dat ik een profiel heb” (“Given I have a profile“) in Dutch.
I decided to use the synonym “Stel” like in “Stel ik heb een profiel“. Not entirely correct either because it misses a comma after “Stel“, but much better, in my honest opinion. So I forked cucumber, changed languages.yml and used this as custom git repository in my Gemfile.
Apparently I was in the middle of a Release Candidate, so what I had committed on github. The Rails integration was extracted out in that version (like RSpec does) to the “cucumber-rails” gem. This gave about an evening of confusion, but I got it to work eventually.
Two days later, we had visitors at our company, from another Ruby company, talking about cooperation on future projects. I introduced myself and he was trying to remember if he’d seen anything on the interwebs by me. That seems to be common. When meeting people on conferences it’s always the same question: “Do I know any of your gems?”.
Anyway, he had actually read my name recently, namely in the commit log of cucumber. Without asking or sending a pull request, they had added my commit to the 0.5 release of cucumber. A pleasant surprise! Aslak Hellesøy, you’re a very observant GitHub user!
By the way, you can still use “Gegeven“, as it is just an alias.
I am constantly wrestling with the best way write cucumber features down. Does anyone have Dutch features? Do you write them down with your customers or are they for developers only? Can you share some of them? Here are some of mine:
Scenario: Inloggen Stel ik ben uitgelogd En ik heb een account voor "gebruiker@test.com" met het wachtwoord "geheim" En ik ben op de inlogpagina Als ik de volgende velden invul: | E-mailadres | gebruiker@test.com | | Wachtwoord | geheim | En ik op "Inloggen" druk Dan zie ik de melding "Je bent ingelogd" Scenario: Verkeerd wachtwoord Stel ik ben uitgelogd En ik heb een account voor "gebruiker@test.com" met het wachtwoord "geheim" En ik ben op de inlogpagina Als ik de volgende velden invul: | E-mailadres | gebruiker@test.com | | Wachtwoord | verkeerd | En ik op "Inloggen" druk Dan zie ik de foutmelding "Ongeldig e-mailadres of wachtwoord"
By the way, don’t forget to order the RSpec and Friends book. It’ll be released this februari and is a really good read if you want to learn BDD, RSpec or Cucumber.
Prawn and controller tests
There is a real annoying gotcha in using controller tests to test an action that renders a pdf with Prawn. You’ll get a NoMethodException on “nil.downcase”. The troubling part is that it totally puts you off by providing the wrong lines and backtrace.
This has been mentioned somewhere on some mailinglists, but to make it a bit more findable, I’d thought I’d post it here.
The solutionworkaround is to set the server protocol, like this:
[sourcecode language=ruby]
it “should show the pdf” do
request.env["SERVER_PROTOCOL"] = “http”
get :show, :id => “report”, “format” => “pdf”
response.should render_template(:show)
end
[/sourcecode]
Configuring Autotest
I have a big test suite in the current Rails application I’m working on. I have 2340 examples in RSpec, taking over 2 minutes to run. This is an absolute pain to run. Luckily there is autotest (or autospec if you’re running RSpec, like I am), which tests only the changed files. I’ve grown to be totally dependent on this behavior, and I can’t imagine programming without it anymore.
I also do TDD, which means that I write a failing test first, and then program until it passes. But Autotest’s flow is that, once you’ve fixed a failing test or spec, it reruns the entire the suite to see if you’re solution doesn’t have any side effects. Normally this is fine, but with this kind of test suite, I cannot afford to wait for it to complete.
So, after going through Autotest’s code, I’ve decided to stub out this behavior. You can still trigger a complete rerun of the entire suite by pressing Ctrl+C, but it doesn’t do that every time you go green. It’s a bit of a monkey patch, but it works just right.
The autotest-growl gem clears the terminal. I don’t like that, because I like to see a bit of history. That’s why I changed that behavior too.
Here’s my ~/.autotest file:
[sourcecode language='ruby']
# Use file system hooks on OS X
require ‘autotest/fsevent’
# Don’t run entire test suite when going from red to green
class Autotest
def tainted
false
end
end
# Use Growl support
require ‘autotest/growl’
# Don’t clear the terminal, when using Growl
module Autotest::Growl
@@clear_terminal = false
end
[/sourcecode]
While browsing through the code of Autotest I also found that it also looks for a .autotest file in the current working directory. So if you want to apply these changes to one project only, you can define this file locally for the project. I didn’t know that!
Red -> Green -> Eat... eh... RefactorPlugin release: Root table
Yes, I’ve written another plugin for Rails. It’s about so called root tables. It seem to be making them often. I want a list with options to choose from and some easy way to manage that list, which is a tedious task. That’s why I made a plugin to do this for me.
What I have so far is:
- Automatic validations and relations
- Completely configurable, with sensible defaults
- A management interface
- Works with acts_as_list and supports drag and drop sorting
- I18n support