Spec "content_for" calls in helpers

This only applies to RSpec below version 1.3.2. The issue has been fixed in RSpec 1.3.2, and most likely RSpec 2 and later versions.


When you have a helper that calls content_for and want to check its behavior you should probably write a feature instead. If you still want to do it, mind the following.

Consider this helper:

module LayoutHelper
  def title(string)
    content_for :title, string
    string
  end
end

Somewhere in the layout we'd then say something like this: `<%= yield :title %...

ActiveRecord: When aggregating nested children, always exclude children marked for destruction

When your model is using a callback like before_save or before_validation to calculate an aggregated value from its children, it needs to skip those children that are #marked_for_destruction?. Otherwise you will include children that have been ticked for deletion in a nested form.

Wrong way

class Invoice < ApplicationRecord
  has_many :invoice_items
  accepts_nested_attributes_for :invoice_items, :allow_destroy => true # the critical code 1/2
  before_save :calculate_and_store_amount                              # the crit...

What collapsing margins are, how they work and when margins do not collapse

Imagine you have 2 HTML boxes. The first one has a margin-bottom of let's say 30px and the second one a margin-top of 20px. After rules of collapsing margins have been applied we have a margin of 30px (not 50px) between these two boxes . This is because no addition of both margins takes place but the maximum of both is applied. This behavior is called collapsing margins.

Oftentimes it is a good behavior but collapsing margins can be annoying, too. For example child el...

RestClient sends XML Accept header by default

REST Client is a nice, simple HTTP client library for Ruby.

When you do a simple GET request like that:

RestClient.get 'http://example.com/'

it will result in this request beeing sent to www.example.com:

GET / HTTP/1.1
Accept: */*; q=0.5, application/xml
Accept-Encoding: gzip, deflate
Host: www.example.com

The application/xml accept header might lead to unexpected results on your server. You can force REST Client to ask the server for default text/html that way:

RestC...

Auto-generate Cucumber navigation paths

Don't you just hate to write Cucumber path helpers to be able to say this?

When I go to the user form for "foo@bar.de"               # goes to edit_user_path(User.find_by_anything!('foo@bar.de'))
When I go to the form for the user "foo@bar.de"           # goes to edit_user_path(User.find_by_anything!('foo@bar.de'))
When I go to the form for the user above"                 # goes to edit_user_path(User.last)
When I go to the project page for "World Domination"      # goes to project_path(Project.find_by_anything!('World Domination')
...

High-level Javascript frameworks: Backbone vs. Ember vs. Knockout

This is a very general introduction to MV* Javascript frameworks. This card won't tell you anything new if you are already familiar with the products mentioned in the title.

As web applications move farther into the client, Javascript frameworks have sprung up that operate on a higher level of abstraction than DOM manipulation frameworks like jQuery and Prototype. Such high-level frameworks typically offer support for client-side view rendering, routing, data bindings, etc. This is useful, and when you write a moderately complex Javascript ...

How to overwrite and reset constants within Cucumber features

In order to save the original value of a constant, set the new value and restore the old value after a scenario was completed, you can use the following helper. It takes care of saving the old constant value, setting the new one without throwing warnings and resets the value with an After hook.

This module also enables you to introduce new global constants.
Since these newly defined constants do not have any value to be reset to,
they simply are deleted (remove_const) once the respective Cucumber step finishes.

You can copy the file at...

Cucumber step to test that a tooltip text exists in the HTML

Tooltips that are delivered through HTML attributes are encoded. Decode entities before checking for their presence.

Capybara:

Then /^there should( not)? be a(n encoded)? tooltip "([^"]*)"$/ do |negate, encoded, tooltip|
  tooltip = HTMLEntities.new.encode(tooltip) if encoded
  Then "I should#{negate} see \"#{tooltip}\" in the HTML"
end

Note

This step uses the htmlentities gem described in another card.

Ma...

Flexible overflow handling with CSS and JavaScript

You can use text-overflow to truncate a text using CSS but it does not fit fancy requirements.

Here is a hack for the special case where you want to truncate one of two strings in one line that can both vary in length, while fully keeping one of them. See this example screenshot where we never want to show an ellipsis for the distance:

![Flexible overflow with optional ellipsis](https://makandracards.com/makandra/5885-a-flexible-overflow-ellipsis/at...

Use CSS "text-overflow" to truncate long texts

When using Rails to truncate strings, you may end up with strings that are still too long for their container or are not as long as they could be. You can get a prettier result using stylesheets.

The CSS property text-overflow: ellipsis has been around for quite a long time now but since Firefox did not support it for ages, you did not use it. Since Firefox 7 you can!

Note that this only works for single-line texts. If you want to truncate tests across multiple lines, use a JavaScript solution like...

Test whether a form field exists with Cucumber and Capybara

The step definition below lets you say:

 Then I should see a field "Password"
 But I should not see a field "Role"

Here is the step definition:

Then /^I should( not)? see a field "([^"]*)"$/ do |negate, name|
  expectation = negate ? :should_not : :should
  begin
    field = find_field(name)
  rescue Capybara::ElementNotFound
    # In Capybara 0.4+ #find_field raises an error instead of returning nil
  end
  field.send(expectation, be_present)
end

Note that you might have to adapt the step defi...

Why your javascripts should be executed after the dom has been loaded

Most of the JavaScript snippets have code that manipulates the DOM. For that reason dom manipulating javascript code should have been executed after the DOM has loaded completely. That means when the browser has finished HTML parsing and built the DOM tree. At that time, you can manipualte the DOM although not all resources (like images) are fully loaded.

The following snippets show how you can do this with plain JavaScript, jquery or prototype ([dom ready ...

Enabling view rendering for controller specs

Views are normally (for good reason) not rendered in controller specs. If you need it to happen, use:

RSpec 1 (Rails 2):

integrate_views

RSpec 2 (Rails 3):

render_views

Note that you can't use that inside it blocks but need to put it in the nesting example group, like this:

    describe '#update' do
      conte...

How to print Github wiki pages

I have no idea how it's supposed to work (or why the don't have a print CSS), but this works for pages written with Markdown:

  1. "Edit" the wiki page
  2. Copy all text
  3. Run a Markdown interpreter and pipe its result, e.g.: kramdown > /tmp/github.html
  4. Paste your markdown
  5. Press Ctrl-D to finalize your input
  6. Open the generated HTML file and print it.

O_o

How to embed images in higher resolutions for printing

When you print out a HTML pages, all raster images (like PNGs) will appear aliased. This is because a printer's resolution is usually much higher than that of a computer screen.

If an image absolutely must look awesome when printed, a solution is to embed the image in much higher solution than needed (e.g. four times the horizontal resolution), then scale it down to the desired width using CSS.

Note that this will slightly alter the image's appearance on the screen because browsers will scale down the image [using an anti-aliasing method](...

Detect mobile or touch devices on both server and client

Although it's tempting flirt with detecting mobile/touch devices with CSS media queries or Javascript feature detection alone, this approach will be painful when heavily customizing a feature beyond just tweaking the looks. Eventually you will want want the same detection logic to be available on both server and client side.

This card shows how to get a Ruby method touch_device? for your Rails views and a method TouchDevice.isPresent() for your Javascripts.

Note that we are detecting touch devices by grepping the user agent, and the ke...

Fix AssociationTypeMismatch

When you're getting this error, one possibility is that you've created a select field for an association instead of the associated object's id. Example:

form.select :unit, Unit.for_select

will be expected to deliver a real Unit object, whereas

form.select :unit_id, Unit.for_select

will make Rails typecast the String value from the select field to the unit's ID.

How to deal with MethodNotAllowed errors

One of the most common production errors are ActionController::MethodNotAllowed errors. They usually happen when someone reloads a form by pressing enter/return in the URL field, or by opening JavaScript links incorrectly.

The attached initializer provides a default way to deal with this.

You'll get the following behaviour:

  • if the incorrect request has a HTTP_REFERER coming from the same application, set a flash, and redirect back
  • if the incorrect request has no HTTP_REFERER or one coming from an external source, set a flash...

Re-enable submit buttons disabled by the :disable_with option

Submit buttons in Rails come with a useful option :disable_with which will disable the button when clicked and change its label to something like "Please wait...".

An annoying side effect of that feature is that when you use the back button to return to the form, the submit button will be greyed out and disabled.

A solution is to re-enable the submit button before leaving the page. This works in Rails 3:

$(window).unload(function() {
  $.rails.enableFormElements($($.rails.formSubmitSelector));
});

ERB templates and comments

When you use one line Ruby comments in ERB templates you should never do this (notice the whitespace in front of #):

<% # my comment %>

<div>my html</div>

This leads to strange html output. To avoid long debugging sessions, you should never have a whitespace before the # character (but newline is allowed)

<%# this works as expected %>

<%
    # this works, too
    # foo bar baz
%>

User friendly form design

There are two types of forms: User forms, e.g. for sign up, and Administration forms, to maintain data. They are quite different in purpose: The former must be simple and quick to fill in, whereas the latter should organize a lot of data in a reasonable way.

Guidelines

  • Simple is win.
  • Align forms vertically, one element below the other.
  • Never explain your form. If it is too complicated, redesign.
  • Skimp on lines and boxes. If you need them, something is wrong with your form.

A form element needs

  • Label. Above the field i...