-
valid_attribute
4 April 2011I’ve been writing model tests for years. Like most I started out with Shoulda which was great. As I moved over to RSpec thankfully Shoulda moved over as well.
After a while I began to hit some walls with Shoulda, specifically when I wanted to use some of the matchers for non-ActiveRecord models. I previously wrote a gem called remarkable_mongoid that provided relation and validation matchers for Mongoid. I originally wanted to use Shoulda as it was my library of choice but the ActiveModel validators were part of the Shoulda ActiveRecord set. So I refactored Shoulda to separate the ActiveModel validator matchers from the ActiveRecord validator matchers. Unfortunately it never got any attention. (I’ve since deleted my local repo) I decided to go with Remarkable which had the validators properly separated.
My views on model testing have shifted since I wrote remarkable_mongoid. Specifically I think relation matchers are crap. At the time I thought the same of validation matchers. My problem with validation matchers wasn’t with the necessity of them but how they were being implemented. Shoulda and Remarkable had similar APIs for how their matchers worked. Their validation matchers feel too tightly coupled with the implementation of the validations themselves. I would find myself writing a spec and a model like this:
I don’t know about you, but this feels like a unnecessary duplication of effort. And this morning I just about had it. So I spiked some code and came up with valid_attribute The same validations from above would work like this:
Now when comparing these two examples it might not be immediately obvious on the advantage of valid_attribute. But lets look at it from developing with BDD.
I want to create a Company model, and I’m going to test first. I’ll start by describing valid and invalid criteria. Then add the necessary validations to make the tests pass.
But now I need to add some more validations to the password attribute.
So we don’t add any more matchers, only update the acceptable values. When I am testing my models I care about asserting that certain data is valid and other data is invalid. valid_attribute makes this dead simple to do just that.
I also do not think having a #.not_with method on the matcher makes any sense. RSpec already has should_not. And it should be used rather than passing false values to a matcher that expects to return true.
-
Client Side Validations 3.0
30 March 2011client_side_validations on Github
The README and the Wiki are very detailed. Please check them out as well!
Client Side Validations 3.0 is out! Adding client side validations to an existing Rails 3 project is as simple as: (assuming you are already using jQuery >= 1.4.1)
- requiring the gem…
# Your Gemfile gem 'client_side_validations'
- running the install generator…
rails g client_side_validations:install
- adding the rails.validations.js javascript file to your layout
- activating in a given form
- Follow the best practices for client side validations developed by Luke Wroblewski
- Automatically extract and apply validation rules defined on the server to the client.
- In the cases where a server-side validation rule would not work on the client (i.e. conditional callbacks like :if, :unless) then do not attempt client side validations. Fall back to the server side validation.
- The client side validation error rendering should be indistinguishable from the server side validation error rendering.
- Wide browser compliancy. I’ve tested with IE8, seems to work OK.
- Work with any ActiveModel::Validations based model
- Validate nested fields
- Support custom validations
- Support custom FormBuilders like SimpleForm and Formtastic
- Client side validation callbacks
And it works! Wait… you don’t see anything?
Okay, so by default ActionView::Base.field_error_proc will wrap invalid fields with a ‘field_with_error’ class div. We need to extend this to also show the error message. The generator added config/initializers/client_side_validations.rb You need to uncomment the ActionView::Base.field_error_proc override. Now restart your rails app and presto-changeo you’ve got client side validations!
If you’re using an alternate FormBuilder like like SimpleForm or Formtastic you won’t need to uncomment anything. The error messages will render inline automatically.
Let’s take a step back and look at the goals of this gem:
Project Goals
Breaking these down…
Follow the best practices for client side validations developed by Luke Wroblewski
As Luke points out in his blog post Live Validations (or client side validations) are great for increasing conversion rates. He details how validating after a user exits a field yields the highest conversion rates (as well as the least confusion). Client Side Validations follows this philiosophy.
Automatically extract and apply validation rules defined on the server to the client
ActiveModel in Rails 3 allows us to reflect upon a given model’s validations. Client Side Validations takes advantage of this to package the rules and i18n messages for use on the client.
In the cases where a server-side validation rule would not work on the client (i.e. conditional callbacks like :if, :unless) then do not attempt client side validations. Fall back to the server side validation
Some validations don’t make sense to attempt on the client. Instead of trying to get around this ClientSideValidations ignores these cases with the assumption the server will always be able to handle it better.
The client side validation error rendering should be indistinguishable from the server side validation error rendering
Considering that we’re allowing some validations to pass to the server we need to ensure that how the errors render on a server side must match exactly how the errors render on the client side. To this end Client Side Validations uses the power of jQuery to properly render the error for a given FormBuilder.
Wide browser compliancy. I’ve tested with IE8, seems to work OK
Pretty self-explanatory.
Work with any ActiveModel::Validations based model
As long as your model is using ActiveModel::Validations it should work. This includes ActiveRecord 3, Mongoid 2.0. MongoMapper is in the process of adopting ActiveModel.
Validate nested fields
If your forms have nested fields via field_for all validations should apply without any issue.
Support custom validations
If you write your own validations they are fully supported with just a small amount of code. The Wiki has a detailed guide on how to add custom validator support
Support custom FormBuilders like SimpleForm and Formtastic
Client Side Validations support SimpleForm and Formtastic. Most of the customization options should also be supported. Your own custom FormBuilder can be supported too, look out for a guide on the wiki soon.
Client side validation callbacks
Control the behavior of how the error messages appear or how a form handles failing validation with callbacks. The Wiki has a detailed guide on the callbacks
I hope people enjoy using this library. The re-write has taken a few months of my time and I’m happy to share the work with everybody. If you have any issues at all please don’t hesitate to open an Issue on Github or contact me with any questions!
- requiring the gem…
-
Convert Prawn generated PDFs to an image with RMagick
10 November 2010You’ll see some complaining from RMagick about how the Prawn generated PDF is not 100% up to Adobe standards but that can be ignored.
pdf = Prawn::Document.new { text 'This is only a test.' }
base64 = Base64.encode64(pdf.render)
image = Magick::Image.read_inline(base64_pdf).first
image.write('test.png') -
Rails Camp New England #3
27 July 2010Registration is open!
When: Sept 10 - Sept 13
Where: Stowe, Vt Ski-Adventure Lodge http://ski-adventure.com/
How much: $140 (+ eventbrite fees)
Why: Hacking and funJoin your other New England Rubyists for the 3rd Rails Camp New England!
-
remarkable_mongoid
10 July 2010I just released remarkable_mongoid. A set of Association and Validation RSpec matchers to use with Mongoid.
Check out the README for instructions.
Please note that this gem requires the 2.0 beta of Mongoid and installs the 4.0 alpha of Remarkable/ActiveModel. Despite their beta/alpha statuses I’ve been using both for over 2 weeks now and they feel very stable.
-
Rails Camp New England # 3
1 July 2010The time has come to start planning for another Rails Camp NE. Here are my thoughts on the next camp:
Where: Vermont
When: Sometime After Labor Day (dependent upon scheduling a venue and other conflicting Ruby events)While our last venue was fantastic it maybe have been too big for the size group that we attract. So I’d like to find a venue similar to the first RCNE (basically a large party house). Any help searching for a venue that people can give is much appreciated. Ideally I’d also like the prices to come back towards the $120/head that the first RCNE was instead of the ~$150/head. The best way to offset this is with sponsorship.
I think the max size we’ll target is about 30 people. So if anybody plans on helping find a venue please keep that number in mind.
Hopefully we can have a venue locked down pretty soon.
-
Custom Subdomains in Rails 3
19 June 2010Rails 3 supports subdomains out of the box, which is great. But did you know that the constraints in the Router can actually match patterns too? This means instead of hardcoding each subdomain into your routes you can allow your customers to decide their own subdomains.
However, we have to be careful with pattern matching on the subdomain. There are obvious subdomains we don’t want to match. Like ‘www’, ”, nil, and others that we may reserve. In this case using a pattern match might not be best.
Thankfully the Rails 3 Router constraints can also take objects. As long as the object responds to Object.matches?. The request is passed to the method and you can act on it in any way. The following is the solution that I’ve found works for me.
I created a ‘lib/sub_domain.rb’ with the following code:
# lib/sub_domain.rb class SubDomain def self.matches?(request) case request.subdomain when 'www', '', nil false else true end end endIn my routes.rb file I can now wrap all routes I want under a custom subdomain
# config/routes.rb TestApp::Application.routes.draw do |map| constraints(SubDomain) do root :to => "customers#index" end root :to => "home#index" endFinally, I create a SubDomainController from which all controllers under the subdomain constraint can inherit from
# app/controllers/sub_domain_controller.rb class SubDomainController < ApplicationController before_filter :get_customer_from_subdomain private def get_customer_from_subdomain @customer = Customer.find_by_subdomain!(request.subdomain) end end# app/controllers/customers_controller.rb class CustomersController < SubDomainController def index ... end endHaving the SubDomainController is a nice way for me to encapsulate behavior that I want every subdomain to have. One such idea would be customer specific layouts. (or themes)
Check out Phil McClure’s post on localhost subdomains if you want to use this functionality in your development and test environments.
Update
To link to your dynamic subdomains you can completely overwrite the :host option in the url helper:
root_url(nil, {:host => "subdomain.somedomain.com"})This is not ideal. It constrains us to this particular domain. What we need is to be able to pass a :subdomain option to the url helper. (btw, you need to use the url helpers for linking to subdomains and not the path helpers)
So I quickly wrote up this code. Just add it to your ApplicationController and it will be available to your entire app:
class ApplicationController < ActionController::Base ... private # To write subdomains on the url helpers: # root_url(nil, {:subdomain => "subdomain"}) def url_for(options = nil) case options when Hash if subdomain = options.delete(:subdomain) if request.subdomain.empty? options[:host] = "#{subdomain}.#{request.host_with_port}" else options[:host] = request.host_with_port.sub(request.subdomain, subdomain) end end end super end endSo now you can do:
root_url(nil, {:subdomain => "subdomain"}) -
RVM Gemsets + Bundler == Awesome
14 June 2010So I’m going to assume that you’re already familiar with RVM, Gemsets and Bundler. If you don’t know about them please follow the links and read up.
Mixing RMV Gemsets and Bundler together is a kickass way to maintain environment isolation for your projects. First you want to create a .rvmrc file in your project’s root directory. RVM will traverse up the directory structure until it finds a .rvmrc file and will use that.
I usually just put the following into the file:
rvm default@project_nameThis will change to my default ruby and set the gemset to ‘project_name’. Then I create a Gemfile and put all of my library dependencies in there. Then run:
> bundle installNow I have a nice and isolated development environment. I can switch between multiple projects and always know that I won’t run into any versioning issues with the installed gems.
One slight problem with this is if I’m including my .rvmrc file in my projects. Someone switches to it and get’s an error from RVM saying that the gemset doesn’t exist. Not that big a deal, they can go and create it. But it would be sweet if we could tell rvm to create the gemset if it doesn’t already exist.
I pinged Wayne Seguin (the guy that wrote RVM) about this and he suggested the following solution:
rvm --create use default@project_nametldr; RVM Gemsets + Bundler == Awesome (and they kick ass)
Update:
That last .rvmrc will keep printing out the ruby@gemset being used after every directory change. Kind of annoying. So just suppress the output:rvm --create use default@project_name > /dev/null -
A cry for standardized versioning in Ruby (pretty please?)
24 May 2010Yesterday Rails 2.3.6 was released, today 2.3.7 was released. This morning the team I am on attempted to update our Rails 2.3.5 apps (we have several) to 2.3.7. It’s been several hours and we’re still not updated yet.
When I see a version update of 2.3.5 to 2.3.6 as a developer I should be able to assume that the update is fairly marginal. In fact, in almost every case it should be a drop-in replacement. Historically hasn’t been the case with Rails.
From a versioning point of view the significance in changes between 2.3.5 -> 2.3.6 and 2.3.6 -> 2.3.7 should be roughly equal. But as you can see here and here this is not the case. However, what I do know is that 2.3.5 was released on Nov 30, 2009. 2.3.6 was released on May 23, 2010. 2.3.7 was released on May 24, 2010. Perhaps time is the best metric? In that case would it make sense to adopt something similar to the Ubuntu versioning scheme? (Year.Month.Day.patch)
Rails aside, poor version management is systemic in the Ruby community. Despite pushing for code perfection in every other sense the Ruby community as a whole has entirely ignored proper versioning. If we are to be taken seriously by the development community at large our versioned releases must have meaning behind them.
Semantic Versioning seems to be the most logical approach to me. It would be nice to see a community movement towards this, or some versioning standard. Rails 3.0 would be a great starting point to push for a versioning standard in the Ruby community.
-
Moved from Blogspot to Tumblr
9 May 2010The move was incredibly easy thanks to this tool:
http://terrymhung.com/jtran/tumblr/import-blogger-to-tumblr.php