jamesalmond.com blog

Thoughts of a thinker

Using simple_form in a Rails 3 engine

| Comments

I’ve been developing an eco-system of engines recently and want to share some components across engines. One of those was simple_form. Unsurprisingly, it’s quite simple to do!

In a file in your lib folder, let’s say my-engine/simple-form.rb, require simple_form and add the config generated underneath. Then, make sure this file is required when your engine is loaded and hey presto, simple forms in your views! I’ve used the default Twitter Bootstrap config here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# lib/my-engine/simple-form.rb
require 'simple_form'
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
  # Wrappers are used by the form builder to generate a
  # complete input. You can remove any component from the
  # wrapper, change the order or even add your own to the
  # stack. The options given below are used to wrap the
  # whole input.
  config.wrappers :default, :class => :input,
    :hint_class => :field_with_hint, :error_class => :field_with_errors do |b|
    ## Extensions enabled by default
    # Any of these extensions can be disabled for a
    # given input by passing: `f.input EXTENSION_NAME => false`.
    # You can make any of these extensions optional by
    # renaming `b.use` to `b.optional`.

    # Determines whether to use HTML5 (:email, :url, ...)
    # and required attributes
    b.use :html5

    # Calculates placeholders automatically from I18n
    # You can also pass a string as f.input :placeholder => "Placeholder"
    b.use :placeholder

    ## Optional extensions
    # They are disabled unless you pass `f.input EXTENSION_NAME => :lookup`
    # to the input. If so, they will retrieve the values from the model
    # if any exists. If you want to enable the lookup for any of those
    # extensions by default, you can change `b.optional` to `b.use`.

    # Calculates maxlength from length validations for string inputs
    b.optional :maxlength

    # Calculates pattern from format validations for string inputs
    b.optional :pattern

    # Calculates min and max from length validations for numeric inputs
    b.optional :min_max

    # Calculates readonly automatically from readonly attributes
    b.optional :readonly

    ## Inputs
    b.use :label_input
    b.use :hint,  :wrap_with => { :tag => :span, :class => :hint }
    b.use :error, :wrap_with => { :tag => :span, :class => :error }
  end

  config.wrappers :bootstrap, :tag => 'div', :class => 'control-group', :error_class => 'error' do |b|
    b.use :html5
    b.use :placeholder
    b.use :label
    b.wrapper :tag => 'div', :class => 'controls' do |ba|
      ba.use :input
      ba.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' }
      ba.use :hint,  :wrap_with => { :tag => 'p', :class => 'help-block' }
    end
  end

  config.wrappers :prepend, :tag => 'div', :class => "control-group", :error_class => 'error' do |b|
    b.use :html5
    b.use :placeholder
    b.use :label
    b.wrapper :tag => 'div', :class => 'controls' do |input|
      input.wrapper :tag => 'div', :class => 'input-prepend' do |prepend|
        prepend.use :input
      end
      input.use :hint,  :wrap_with => { :tag => 'span', :class => 'help-block' }
      input.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' }
    end
  end

  config.wrappers :append, :tag => 'div', :class => "control-group", :error_class => 'error' do |b|
    b.use :html5
    b.use :placeholder
    b.use :label
    b.wrapper :tag => 'div', :class => 'controls' do |input|
      input.wrapper :tag => 'div', :class => 'input-append' do |append|
        append.use :input
      end
      input.use :hint,  :wrap_with => { :tag => 'span', :class => 'help-block' }
      input.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' }
    end
  end

  # Wrappers for forms and inputs using the Twitter Bootstrap toolkit.
  # Check the Bootstrap docs (http://twitter.github.com/bootstrap)
  # to learn about the different styles for forms and inputs,
  # buttons and other elements.
  config.default_wrapper = :bootstrap

  # Define the way to render check boxes / radio buttons with labels.
  # Defaults to :nested for bootstrap config.
  #   :inline => input + label
  #   :nested => label > input
  config.boolean_style = :nested

  # Default class for buttons
  config.button_class = 'btn'

  # Method used to tidy up errors.
  # config.error_method = :first

  # Default tag used for error notification helper.
  config.error_notification_tag = :div

  # CSS class to add for error notification helper.
  config.error_notification_class = 'alert alert-error'

  # ID to add for error notification helper.
  # config.error_notification_id = nil

  # Series of attempts to detect a default label method for collection.
  # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]

  # Series of attempts to detect a default value method for collection.
  # config.collection_value_methods = [ :id, :to_s ]

  # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
  # config.collection_wrapper_tag = nil

  # You can define the class to use on all collection wrappers. Defaulting to none.
  # config.collection_wrapper_class = nil

  # You can wrap each item in a collection of radio/check boxes with a tag,
  # defaulting to :span. Please note that when using :boolean_style = :nested,
  # SimpleForm will force this option to be a label.
  # config.item_wrapper_tag = :span

  # You can define a class to use in all item wrappers. Defaulting to none.
  # config.item_wrapper_class = nil

  # How the label text should be generated altogether with the required text.
  # config.label_text = lambda { |label, required| "#{required} #{label}" }

  # You can define the class to use on all labels. Default is nil.
  config.label_class = 'control-label'

  # You can define the class to use on all forms. Default is simple_form.
  # config.form_class = :simple_form

  # You can define which elements should obtain additional classes
  # config.generate_additional_classes_for = [:wrapper, :label, :input]

  # Whether attributes are required by default (or not). Default is true.
  # config.required_by_default = true

  # Tell browsers whether to use default HTML5 validations (novalidate option).
  # Default is enabled.
  config.browser_validations = false

  # Collection of methods to detect if a file type was given.
  # config.file_methods = [ :mounted_as, :file?, :public_filename ]

  # Custom mappings for input types. This should be a hash containing a regexp
  # to match as key, and the input type that will be used when the field name
  # matches the regexp as value.
  # config.input_mappings = { /count/ => :integer }

  # Default priority for time_zone inputs.
  # config.time_zone_priority = nil

  # Default priority for country inputs.
  # config.country_priority = nil

  # Default size for text inputs.
  # config.default_input_size = 50

  # When false, do not use translations for labels.
  # config.translate_labels = true

  # Automatically discover new inputs in Rails' autoload path.
  # config.inputs_discovery = true

  # Cache SimpleForm inputs discovery
  # config.cache_discovery = !Rails.env.development?
end

What if you could only tweet 5 times a day?

| Comments

About 3 years ago I splashed out and bought myself a DSLR camera. Whilst the shiny new camera had a point-and-shoot mode I got stuck in and started using the manual settings, playing with the aperture, focus, shutter speed and white balance. After a while I felt I wasn’t getting the results I was hoping for and I started scouring the forums and photography websites to find out what I should be doing and came across the following advice:

At first, play with all the settings, see what they do, see how they work together, take as many photos as you can. Try and work out why some work and why some don’t. Then, when you want to move on to the next level, set aside some time (say, a couple of hours, half a day) to go out and take photographs. During that time you’re only allowed to press the shutter button a maximum of 5 times. 5 exposures. No more.

The quote is heavily paraphrased (or possibly pulled from multiple sources) as I can’t remember exactly where I read it, but the intent is the same: introduce a constraint into the way you work to make you think about things differently. In the above case it’s simulating the constraint of having a physical film camera, replacing the seemingly infinite memory cards, forcing you to consider the settings, light, position, angle, white balance and focus before you commit to pressing the button. There’s a small amount of punishment (waste of film, one less photo to take) if it doesn’t work out. It changes the way you think.

So, what if you could only tweet positive things? What if you only tweet without using the letter ‘e’? What if you could only tweet 5 times a day?

Simple Rails route helpers

| Comments

Sometimes you end up with route configurations that repeat through your routes.rb file. Taking inspiration from Devise I added some little route helpers to the app I’m working on.

Before:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MyApp::Application.routes.draw do
  resources :pages do
    get :delete, :on => :member
    resources :images
    resources :tags
  end
  resources :people do
    get :delete, :on => :member
    resources :images
    resources :tags
  end
  resources :places do
    get :delete, :on => :member
    resources :images
    resources :tags
  end
end

If we add a simple helper to the routes:

1
2
3
4
5
6
7
8
9
10
11
12
13
module MyApp::RoutesHelpers
  def default_routes_for(*models)
    models.each do |model|
      resources model do
        get :delete, :on => :member
        resources :images
        resources :tags
      end
    end
  end
end

ActionDispatch::Routing::Mapper.send(:include, MyApp::RoutesHelpers)

Then we can use the following rotues:

1
2
3
MyApp::Application.routes.draw do
  default_routes_for(:pages, :people, :places)
end

A trivial example, maybe, but a simple little tidy up!

Cucumber for gem documentation

| Comments

I released my first gem, Dozuki, a few weeks ago (work in progress!), both because I thought the functionality I was extracting could be useful to others but also as a bit of a test-bed for trying some new things with testing.

Alongside playing with some different ways of writing mocked and stubbed tests, I wanted to have a go at writing some Cucumber features that could also be used as Relish-style documentation.

My experience of Cucumber in the Rails world is using it for customer-facing acceptance/integration tests with the steps driving the interface of a web app using something like Capybara.

With Dozuki I wanted the Cucumber to be a way of clearly specifying what a feature should do, a way of integration testing the code and, to avoid duplicating effort, a set of documentation on how to use the code. Not too far from the intent of my normal features but the difference here is the way the steps are expressed. Let’s have a look at extracting an integer from an XML document:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Feature: Getting integers from the document
  In order to provide simpler way of getting integers from a node
  As a traverser
  I want to access nodes using the int method and an xpath
  
  Scenario: getting the int of a single node
    When I parse the XML:
      """
        <root>
          <name>St. George's Arms</name>
          <average_price>20.32</average_price>
          <number_of_beers>2</number_of_beers>
        </root>
      """
    And I call "int('/root/number_of_beers')" on the document
    Then the result should be 2

For a standard web app I’d try to not couple my steps too closely to the implementation, making the features a little less brittle (e.g. using “When I add the post” instead of the more explicit “When press ‘Add Post’”). Here I want the exact opposite. I want my features to be coupled directly to the implementation so I can demonstrate how to use it. To help with that I’ve put the XML explicitly in each step. This stops the reader from having to look in background steps for each scenario and allows you to change the XML between scenarios. Secondly, I’ve used an actual code snippet on the penultimate line. But you’ll notice that the amount of code I’ve used is minimal. Too keep the feature readable, I’ve only used the amount of code needed to demonstrate the feature I’m currently testing and documenting; we’re going to need another feature and set of scenarios for the creation of a document.

Let’s have a look at the steps behind the features:

1
2
3
4
5
6
7
8
9
10
11
When /^I parse the XML:$/ do |string|
  @doc = Dozuki::XML.parse(string)
end

When /^I call "([^"]*)" on the document$/ do |code|
  @result = @doc.instance_eval(code)
end

Then /^the result should be (\d+)$/ do |int|
  @result.should == int.to_i
end

The only noteworthy step here is the second, allowing us to execute the code we’ve specified in the scenario against the document created in the previous step by simply evaluating the code against the instance. The other two should be self-explanatory.

In another example we might want to assert that a certain piece of code raises an error:

1
2
3
4
5
6
7
8
9
10
Scenario: getting the int of a non-existent node
  When I parse the XML:
    """
      <root>
        <name>St. George's Arms</name>
        <average_price>20.32</average_price>
        <number_of_beers>2</number_of_beers>
      </root>
    """
  Then calling "int('//something/missing')" on the document should raise a "NotFound" error

Here we’ve got to do something a bit cleverer than the previous step as the raised error has to be caught:

1
2
3
4
5
6
7
8
Then /^calling "([^"]*)" on the document should raise a "([^"]*)" error$/ do |code, error|
  begin
    @doc.instance_eval(code)
    fail "Expected error #{error}, nothing raised"
  rescue Dozuki::XML.const_get(error) => e
    @error = e
  end
end

Here the code is being evaluated against the instance as before, but if no error is raised then we force a failure. The step only matches against the error we’ve defined by getting the Exception constant from the gem’s scope using the name set in the scenario. The error is also assinged to an instance variable in case we need to inspect it in further steps.

These steps and features work well and remain fairly simple but what about methods that take a block as an argument? How do we write feature for those without the implementation details getting in the way? An example in Dozuki is a simplified XPath each method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Scenario: using each to traverse a document and getting the integer elements
  When I parse the XML:
    """
      <root>
        <name>St. George's Arms</name>
        <average_price>20.32</average_price>
        <number_of_beers>2</number_of_beers>
        <rooms>
          <room>5</room>
          <room>7</room>
        </rooms>
      </root>
    """
  And I call "each('/root/rooms/room').as_int" on the document and collect the results
  Then the results should contain 5
  And the results should contain 5

To avoid having to define the code which handles the called block in the scenario it’s been hidden with “and collect the results”. If we put the block in the scenario we’d also have to deal with the assertion on the results or collecting them into some container in the scenario too. I figured this would be a bit messy. The implementation for these steps gets a bit hairy:

1
2
3
When /^I call "([^"]*)" on the document and collect the results$/ do |code|
  @results = @doc.instance_eval("results = [];" + code + "{|res| results << res}; results;")
end

Help! What’s happening here? OK, well we’re defining an array, executing the code from the scenario and adding a block to the function call that appends items passed to the block to an array… all in a string executed against the instance. Riiighhht..

There’s more features you can browse in the Dozuki GitHub repository. Take a look!

Conclusion

Well, that’s my first attempt at Cucumber as documentation. What do you think?

The first obstacle I came across was finding a balance between the amount of code in the document and the amount of natural language. Too much natural language you lose the benefit of knowing how to run the code, too much code and you lose the ‘running commentary’ and it’s less expressive. My intention was to use enough code to cover that ‘feature’ of the gem and not cover other features.

Secondly, and I didn’t spend too long thinking about this, the block examples result in some funky steps. The less code in the example means a bit more work behind the scenes but I think it’s worth it and hopefully the intent is understood. I also tried to keep the steps as simple and readable as possible so anyone who wanted to do some digging might find out a bit more. I think the block one might be stretching it a bit..!

Hopefully I’ve covered enough examples to give other people an idea of how to start having a go at this. Overall, I think it’s something I’d pursue. The simple examples are easy to follow, easy to maintain, document the code and function as tests. Surely worth something?

Pygmentize errors with Jekyll

| Comments

I’ve recently switched my blog to Jekyll and I’ve set up the system on a number of machines. The first machine was a seamless install but on my second machine I can across the following error when trying to generate pages with code using Pygments:

1
no such file or directory - pygmentize -l text -f html -O encoding=utf-8

It turns out I’d installed Pygments after installing the Jekyll gem without realising. Uninstalling the Jekyll gem, installing Pygments and reinstalling the Jekyll gem. Sorted!

Resourceful routing with Rails

| Comments

Resource routing in Rails is most often used for nesting related ActiveRecord objects, such as a post having many comments:

1
2
3
4
5
6
7
8
9
10
11
12
class Post < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

#routes
resources :posts do
  resources :comments
end

The resource method of the router hooks up the “RESTful” urls with a set of standard set of controller methods and HTTP verb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
post_comments GET    /posts/:post_id/comments(.:format)          {:action=>"index", :controller=>"comments"}
              POST   /posts/:post_id/comments(.:format)          {:action=>"create", :controller=>"comments"}
 new_post_comment GET    /posts/:post_id/comments/new(.:format)      {:action=>"new", :controller=>"comments"}
edit_post_comment GET    /posts/:post_id/comments/:id/edit(.:format) {:action=>"edit", :controller=>"comments"}
 post_comment GET    /posts/:post_id/comments/:id(.:format)      {:action=>"show", :controller=>"comments"}
              PUT    /posts/:post_id/comments/:id(.:format)      {:action=>"update", :controller=>"comments"}
              DELETE /posts/:post_id/comments/:id(.:format)      {:action=>"destroy", :controller=>"comments"}
        posts GET    /posts(.:format)                            {:action=>"index", :controller=>"posts"}
              POST   /posts(.:format)                            {:action=>"create", :controller=>"posts"}
     new_post GET    /posts/new(.:format)                        {:action=>"new", :controller=>"posts"}
    edit_post GET    /posts/:id/edit(.:format)                   {:action=>"edit", :controller=>"posts"}
         post GET    /posts/:id(.:format)                        {:action=>"show", :controller=>"posts"}
              PUT    /posts/:id(.:format)                        {:action=>"update", :controller=>"posts"}
              DELETE /posts/:id(.:format)                        {:action=>"destroy", :controller=>"posts"}

These structures are map nice and clearly to the default CRUD actions of your AR objects and how to use this functionality is covered in other tutorials.

Once you step outside of the default CRUD actions it’s not immediately clear how to add further functionality to your routes structure. One of the options is to add extra methods to the resource’s collection or methods as a member of each object. Let’s take the concept of setting the status of a blog post from “draft” to “published”, with the status of the post being stored as a string in the post object. We’ll need a page to view the current status and a URL to post to that updates the status.

1
2
3
4
5
6
resources :posts do
  member do
    get :status
    put :update_status
  end
end

which gives us the following URLs:

1
2
3
4
5
6
7
8
9
   post_status GET    /posts/:post_id/status(.:format)        {:action=>"status", :controller=>"posts"}
post_update_status PUT    /posts/:post_id/update_status(.:format) {:action=>"update_status", :controller=>"posts"}
         posts GET    /posts(.:format)                        {:action=>"index", :controller=>"posts"}
               POST   /posts(.:format)                        {:action=>"create", :controller=>"posts"}
      new_post GET    /posts/new(.:format)                    {:action=>"new", :controller=>"posts"}
     edit_post GET    /posts/:id/edit(.:format)               {:action=>"edit", :controller=>"posts"}
          post GET    /posts/:id(.:format)                    {:action=>"show", :controller=>"posts"}
               PUT    /posts/:id(.:format)                    {:action=>"update", :controller=>"posts"}
               DELETE /posts/:id(.:format)                    {:action=>"destroy", :controller=>"posts"}

In this example I’ve chosen not to use the Post’s update URL to set the status. By using a separate controller method we can also use a separate model method (rather than the default ‘update_attributes’). This means that when we set a new status we can handle any business logic around a status change within that specific method and not within a pile of spaghetti callback methods. I’ll save the ‘ActiveRecord spaghetti callback methods are evil’ post for another day!

The following code sets up the controller actions that handle the new routes:

1
2
3
4
5
6
7
8
9
10
11
12
13
class PostsController < ApplicationController

  # with corresponding posts/status.html.haml
  def status
    @post = Post.find(params[:id])
  end

  def update_status
    post = Post.find(params[:id])
    post.update_status(params[:new_status])
    redirect_to post_path(post)
  end
end

Hooray! Minus some of the AR implementation, we’ve now got status updates. Whilst this example is fairly simple, when we start adding more functionality to posts both our controller and routes start to get a bit clumsy and bloated. Let’s add some search functionality to our posts. Iwant a page that has the search box on that posts its options to the app, the app will then convert those options into some URL parameters that we can access as a get method (allowing our users to bookmark a search and refresh without issue):

1
2
3
4
5
6
7
8
9
10
11
resources :posts do
  member do
    get  :status
    put  :update_status
  end
  collection do
    get  :search
    post  :new_search
    get  :show_search
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
   status_post GET    /posts/:id/status(.:format)        {:action=>"status", :controller=>"posts"}
update_status_post PUT    /posts/:id/update_status(.:format) {:action=>"update_status", :controller=>"posts"}
  search_posts GET    /posts/search(.:format)            {:action=>"search", :controller=>"posts"}
  new_search_posts POST   /posts/new_search(.:format)        {:action=>"new_search", :controller=>"posts"}
 show_search_posts GET    /posts/show_search(.:format)       {:action=>"show_search", :controller=>"posts"}
         posts GET    /posts(.:format)                   {:action=>"index", :controller=>"posts"}
               POST   /posts(.:format)                   {:action=>"create", :controller=>"posts"}
      new_post GET    /posts/new(.:format)               {:action=>"new", :controller=>"posts"}
     edit_post GET    /posts/:id/edit(.:format)          {:action=>"edit", :controller=>"posts"}
          post GET    /posts/:id(.:format)               {:action=>"show", :controller=>"posts"}
               PUT    /posts/:id(.:format)               {:action=>"update", :controller=>"posts"}
               DELETE /posts/:id(.:format)               {:action=>"destroy", :controller=>"posts"}

And our Posts controller also needs some more methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class PostsController < ApplicationController

  # other Post CRUD methods

  # with corresponding posts/status.html.haml
  def status
    @post = Post.find(params[:id])

  end
  def update_status
    post = Post.find(params[:id])
    post.update_status(params[:new_status])
    redirect_to post_path(post)
  end

  def search
    # renders the search form
  end
  def new_search
    redirect_to posts_show_search_path Post.process_search_params(params[:search])
  end
  def show_search
    @posts = Post.search_from_params(params)
  end
end

We now have search! But there’s also a few things that make this example less than ideal. The more functionality we add to Posts the more routes we have to define manually and the more methods we have in our Posts controller. This results in controllers that are hard to navigate due to size, routes that are defined by how the developer decided to name them that day (e.g. new_search could be create_search etc.) and the Posts controller is now responsible for much more than it should ! So, how can we fix this? We need some uniform system of representing concepts in our system.

Re-thinking resources

Resources in the Rails routing world are often mapped directly to persistence or ActiveRecord objects. Let’s redefine the use of resource to mean a individual ‘concept’ within the system, something that has its own behaviour or affects another part of the system. Our persistence objects still map to resources as before but other concepts such as actions on those objects or specific behaviours of those objects can also map to resources. Get back to the example will probably make it a bit clearer. Within our example a Post still remains a resource as it was previously. We can use the methods and routes created by the definition of Post as a resource to view, create and alter the definition of a post. For example, changing the basic attributes such as title or body. But, given our new definition of resource we can now start to consider other things resources. As the status attribute doesn’t really define the post but defines the state the post is in it’s a good candidate for having the functionality to change it being moved from the Posts controller. This allows us to deal with the implications of changing the state separately from the mundane actions of simply updating attributes; a division of responsibility from the previous example. We now have the methods we need to show the current status (GET show) and update the status (PUT update). We can also consider the search as a resource. Now, this one maps a little more closely to the idea of a resource as we previously defined, but doesn’t map directly to a persistence model. We have and action that shows the form (GET new), we have a method that accepts posted parameters (POST create) and we have a method that displays a specific search given an identifier (the search term) (GET show). We can now tidy up our routes a bit:

1
2
3
4
resources :posts do
  resource :status, :controller => :posts_status_controller
  resources :searches, :controller => :posts_searches_controller
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 post_status POST   /posts/:post_id/status(.:format)            {:action=>"create", :controller=>"posts_status_controller"}
 new_post_status GET    /posts/:post_id/status/new(.:format)        {:action=>"new", :controller=>"posts_status_controller"}
edit_post_status GET    /posts/:post_id/status/edit(.:format)       {:action=>"edit", :controller=>"posts_status_controller"}
             GET    /posts/:post_id/status(.:format)            {:action=>"show", :controller=>"posts_status_controller"}
             PUT    /posts/:post_id/status(.:format)            {:action=>"update", :controller=>"posts_status_controller"}
             DELETE /posts/:post_id/status(.:format)            {:action=>"destroy", :controller=>"posts_status_controller"}
   post_searches GET    /posts/:post_id/searches(.:format)          {:action=>"index", :controller=>"posts_searches_controller"}
             POST   /posts/:post_id/searches(.:format)          {:action=>"create", :controller=>"posts_searches_controller"}
 new_post_search GET    /posts/:post_id/searches/new(.:format)      {:action=>"new", :controller=>"posts_searches_controller"}
edit_post_search GET    /posts/:post_id/searches/:id/edit(.:format) {:action=>"edit", :controller=>"posts_searches_controller"}
 post_search GET    /posts/:post_id/searches/:id(.:format)      {:action=>"show", :controller=>"posts_searches_controller"}
             PUT    /posts/:post_id/searches/:id(.:format)      {:action=>"update", :controller=>"posts_searches_controller"}
             DELETE /posts/:post_id/searches/:id(.:format)      {:action=>"destroy", :controller=>"posts_searches_controller"}
       posts GET    /posts(.:format)                            {:action=>"index", :controller=>"posts"}
             POST   /posts(.:format)                            {:action=>"create", :controller=>"posts"}
    new_post GET    /posts/new(.:format)                        {:action=>"new", :controller=>"posts"}
   edit_post GET    /posts/:id/edit(.:format)                   {:action=>"edit", :controller=>"posts"}
        post GET    /posts/:id(.:format)                        {:action=>"show", :controller=>"posts"}
             PUT    /posts/:id(.:format)                        {:action=>"update", :controller=>"posts"}
             DELETE /posts/:id(.:format)                        {:action=>"destroy", :controller=>"posts"}

There’s two things to note with this example. Firstly the status is defined as a singular resource as we don’t have a collection of statuses, so we don’t need the URLs that have the unique IDs. It’s routes like /posts/1/status/edit or PUTting to /posts/1/status to update a post that seem to make perfect sense to me. Secondly, we’ve specified the names of the controllers. If we only had one search in the system and one status concept we wouldn’t have to specify the names of our controllers and we could just name them to match the name of the resource. But while we’re at this dividing responsibility game, let’s make it clear what the responsibility of the controller is by naming it explicitly. We can also move the actions of from the previous single controller into the new controllers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class PostsStatusController < ApplicationController
  def show
    @post = Post.find(params[:id])
  end
  def update
    post = Post.find(params[:id])
    post.update_status(params[:new_status])
    redirect_to post_path(post)
  end
end

class PostsSearchController < ApplicationController
  def new
    # renders the search form
  end
  def create
    redirect_to posts_show_search_path Post.process_search_params(params[:search])
  end
  def show
    @posts = Post.search_from_params(params)
  end
end

Starting to look a little cleaner, right? And also, we haven’t defined any methods outside of the normal (create, show, edit, update, delete) which makes all our controllers consistent and predictable. We’ve also got some nice routes that look reasonably RESTful (I’ve tried to avoid using the term RESTful as I don’t want to get into the semantics of REST, but I’m feeling reckless today!). We can now also move our views out of the posts folder into the separate posts_searches and posts_status folders; much less clutter. Hopefully this (rather long!) post has given you an idea of how to structure your controllers and routes in a manageable, consistent and predictable way.

Summary (TL;DR)

Resource routing in Rails can be used outside of persistence or AR objects to provide predictable, clean and RESTful routes for your application’s behaviour. This practice encourages the separation of concerns by creating new controllers, methods and views which are responsible for specific behaviours of the system making the code easier to understand, navigate and test.

Another example

1
2
3
4
5
6
resources :bookings do
  resource :cancellation, :controller => :booking_cancellation_controller
end

# Cancellation page: /bookings/1/cancellation/new (BookingsCancellationController GET new)
# Process the cancellation: /bookings/1/cancellation (BookingsCancellationController POST create)

Simpler XML parsing with Dozuki

| Comments

I’ve just pushed a gem! It’s called Dozuki. It’s an extraction of a simple wrapping layer over the Nokogiri XML library that reduces the amount of method chaining needed to accomplish simple tasks like extracting integers and floats, or iterating through nodes. It’s quite a close extraction to its original purpose at the moment, so it’s not that fully featured. It’s also going to be used as a bit of a test bed for trying out different types of testing, currently focussing on Cucumber-based feature documentation. Take a look, I’m quite happy with the tests so far!

Heroku-style cache purges on deployment with Varnish

| Comments

I’ve been using Varnish to put caches in front of service components recently and I was updating our deployment scripts to reflect that. One thing I wanted to achieve is the Heroku-style cache-flush on re-deployment. To do this we just need to add a simple line to the scripts, but before we can do that we need to enable the Varnish management interface by adding an option to the varnish startup command:

varnishd -f /my/config.vcl -s malloc,25M -a 0.0.0.0:9090 -T 0.0.0.0:9092

The -T option tells varnish on which address and port to expose the management interface; in this case port 9092 on localhost. We can then send commands to the management interface. Available commands are listed as part of the varnishd documentation. The command we’re interested in is “url.purge” which takes a regex parameter that matches paths to delete. If you want to purge all the cached pages then just run the following in your deploy script (probably somewhere towards the end, don’t want to flush the cache on a broken deploy!):

1
varnishadm -T 0.0.0.0:9092 "url.purge .*"

If your cache is large then it’s probably not a good idea to delete absolutely everything. For example, if you only wanted to expire all your stylesheets then you could use something like this:

1
varnishadm -T 0.0.0.0:9092 "url.purge .css$"

That purges every file in the cache that ends in .css. Happy caching!

Caching multiple backends on the same server with Varnish

| Comments

I’ve been looking at taking our caching practices past the simple drop-in solution of rack-cache today after we had some problems with caching asynchronous requests. As long as we don’t need detailed per-app configuration and can just rely on simple cache expiry headers there are some benefits of running a ubiquitous caching layer over our apps. After having previously seen it used on Heroku, I took a look at Varnish, the “web accelerator”. It’s fairly easy to get set up, so I won’t go into it here, just take a look at the tutorials.

One thing that I wanted to do that I couldn’t see an obvious solution for was running a caching layer for multiple apps that can be accessed on different ports. Varnish is generally started using something similar to the following command:

1
varnishd -f /my/config/dir/varnish.vcl -s malloc,50M -a 0.0.0.0:80

Which starts a varnish instance accessible on port 80. Running this command again with a different port and different config doesn’t seem to start a new Varnish process. To get around this all you have to do is name your Varnish process (and put it on a different port!):

1
varnishd -f /my/config/dir/varnish.vcl -s malloc,50M -a 0.0.0.0:80 -n app_2
1
varnishd -f /my/config/dir/varnish_alternative.vcl -s malloc,50M -a 0.0.0.0:9090 -n app_1

Now we can add as many caches as we want! Maybe I missed something in the docs, but it took me a while to work this out, hope it helps.

Are ‘best practices’ a best practice?

| Comments

I’ve decided that I don’t like the the term ‘best practice’. I come across it a lot when reading blog posts and articles about Ruby (not that this is a problem restricted solely to the Ruby community) and I don’t believe it’s a helpful turn of phrase.

The phrase ‘best practice’ suggests that there is no better way to solve the problem, that the solution should be applied to all applications or areas that share a similar structure and this simply cannot be the case. There are lots of things that are described as ‘best practices’ that are not suitable for all applications. Some abstractions are an abstraction too far for a small system. Some solutions solve the problem in a similar to another solution, but are not necessarily better; they’re just different. Some best practices strike me as being plain wrong, but that’s probably because it wouldn’t be appropriate for me to apply them on the kind of systems I work on.

Something can only be called a best practice for a specific problem and anyone who searches for solutions to their problems will understand when I say “nobody ever has quite the same problem as you”.

I believe dropping blanket terms like best practice and objectively discussing a proposed solution in terms of a specific problem is a far more constructive, and less misleading, way to present a practice. People don’t need to be patronised by telling them what’s best for them, they can work it out for themselves!

Valid point or pedantic semantics?