Creating the inverse of a Rails migration

Let's say you need to revert a migration that happened a while back. You'd create a new migration that removes what was added back then in the up path, while its down path restores the old functionality.

While you could just copy&paste the down and up parts of it to the inverse part of the new migration, you may not want to do that. Especially when the up/down paths already contained some logic (that executed update statements on the created column, for example), copying does not feel right.

Someone already added the logic how to...

Gatekeeping: Guide for developer

If your project manager wants to do gatekeeping on a project, as a developer you need to follow the following guidelines (e.g. by using something like this issue checklist template).

In order to reduce the number of rejects we get from clients, we want to review all code written before it goes to the staging server.

Note

This process is tailored to our specific needs and tools at makandra. While it will certainly not apply to all (especially larger teams), we think it...

TeamViewer 7 finally works with multiple screens under Linux

TeamViewer 6 and lower had an issue where they would see a multi-monitor Linux setup as a single wall of pixels. This is fixed in version 7. The guest can now select the currently active screen from the TeamViewer menu.

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...

will_paginate on complex scopes may be slow (workaround)

will_paginate triggers a database query to determine the total number of entries (i.e. to let you display the number of search results). When you paginate complex scope (e.g. that has many includes), this query may take several seconds to complete.

If you encounter this behavior, a solution is to calculate the total count yourself and pass it to the pagination call:

scope = User.complex_scope_full_of_includes
total_number_of_users = scope.count
@users = scope.paginate(:total_entr...

In MySQL, a zero number equals any string

In MySQL comparing zero to a string 0 = "any string" is always true!

So when you want to compare a string with a value of an integer column, you have to cast your integer value into a string like follows:

SELECT * from posts WHERE CAST(posts.comments_count AS CHAR) = '200' 

Of course this is usually not what you want to use for selecting your data as this might cause some expensive database operations. No indexes can be used and a full table scan will always be triggered.

If possible, cast the compared value in your application to...

Find an ActiveRecord by any column (useful for Cucumber steps)

The attached patch lets you find a record by a string or number in any column:

User.find_by_anything('carla')
User.find_by_anything('email@domain.de')
User.find_by_anything(10023)

There's also a bang variant that raises ActiveRecord::NotFound if no record matches the given value:

User.find_by_anything!('carla')

Boolean and binary columns are excluded from the search because that would be crazy.

I recommend copying the attachment to features/support/find_by_anything.rb, since it is most useful in Cucumber step def...

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...

Fixing Graticule's "distance" for edge cases

Ever seen this error when using Graticule?

Numerical argument out of domain - acos

Similarly to the to_sql problem for some edge cases, Graticule::Distance::Spherical.distance (and possibly those of Graticule's other distance computation classes) is subject to Float rounding errors.

This can cause the above error, when the arc cosine of something slightly more than 1.0 is to be computed, e.g. for the (zero) distance b...

Mysql::Error: SAVEPOINT active_record_1 does not exist: ROLLBACK TO SAVEPOINT active_record_1 (ActiveRecord::StatementInvalid)

Possible Reason 1: parallel_tests - running more processes than features

If you run old versions of parallel_tests with more processes than you have Cucumber features, you will get errors like this in unexpected places:

This is a bug caused by multiple processes running the same features on the same database.

The bug is fixed in versions 0.6.18+.

Possib...

Sunspot for Solr fails with '400 Bad Request' in 'adapt_response'

If Sunspot does not work and fails with a backtrace similar to this:

/project/shared/bundle/ruby/1.8/gems/rsolr-1.0.6/lib/rsolr/client.rb:227:in `adapt_response'
/project/shared/bundle/ruby/1.8/gems/rsolr-1.0.6/lib/rsolr/client.rb:164:in `execute'
/project/shared/bundle/ruby/1.8/gems/rsolr-1.0.6/lib/rsolr/client.rb:158:in `send_and_receive'
(eval):2:in `post'

then the schema.xml that is shipped with Sunspot is not loaded into Solr correctly.

Often the latter can be found in /etc/solr/conf/schema.xml. So copy Sunspo...

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 ...

Using Solr with Sunspot

This describes all the steps you'll need to get Solr up and running for your project using the Sunspot gem.

Prepare Sunspot on your development machine

What you want in your Gemfile:

gem 'sunspot_rails'
gem 'sunspot_solr'
gem 'progress_bar' # for sunspot:solr:reindex

Now define what should be indexed within Solr from your ActiveRecord models, e.g.,

class Article << ActiveRecord::Base

  searchable do
    text :title
 ...

gammons/fake_arel - GitHub

Gem to get Rails 3's new ActiveRecord query interface (where, order) and the new scope syntax (chaining scope definitions) in Rails 2.

You also get #to_sql for scopes.

New geordi script: migrate-all

Use the command geordi migrate to migrate your databases and to prepare them before running tests. The abbrevation geordi m works as well.

  • It will run rake db:migrate if parallel_tests does not exist in your Gemfile
  • Otherwise it runs b rake db:migrate and then executes b rake parallel:prepare if parallel_tests was found in your Gemfile.

How to revert features for deployment, merge back, and how to stay sane

Removing features and merging those changes back can be painful. Here is how it worked for me.\
tl;dr: Before merging back: reinstate reverted features in a temporary branch, then merge that branch.

Scenario

Consider your team has been working on several features in a branch, made many changes over time and thus several commits for each feature.\
Now your client wants you to deploy while there are still stories that were rejected previously and can't be deployed.
...

Improved gitpt now part of geordi

Our gitpt script to generate git commits from Pivotal Tracker stories has been tweaked and polished and is now part of the geordi gem.

Install the freshly released version 0.7 now:

gem install geordi

This update will bring you commit with an initial "setup wizard" (that asks for your PT API key and initials) and prettier output: stories are colored by their state and thos...

CSS box-shadow not working in IE9 inside tables with collapsing borders

Though Internet Explorer 9 supports the box-shadow CSS property there is a nasty bug which sometimes prevents it from rendering the shadow properly.

Consider this HTML:

<table style="border-collapse: collapse">
  <tr>
    <td>
      <div style="box-shadow: 0 0 10px #f00">Hello universe</div>
    </td>
  </tr>
</table>

While it works in other browsers, IE9 is not showing any shadow. For some reason, it requires border-collapse: separate for the table to be set:

<table style="border-collapse: separate" c...

Don't compare datetimes with date ranges in MySQL and PostgreSQL

When selecting records in a date range, take care not to do it like this:

start_date = Date.parse('2007-05-01')
end_date = Date.parse('2007-05-31')
LogItem.where(:created_at => start_date .. end_date)

The problem is that created_at is a datetime (or Time in Ruby), while start_date and end_date are simple dates. In order to make sense of your query, your database will cast your dates to datetimes where the time component is 00:00:00. Because of this the query above will lose records created from `2007-05-31 00:00:0...

Use Time.current / Date.current / DateTime.current on projects that have a time zone

Basically, you now need to know if your project uses a "real" time zone or :local, and if config.active_record.time_zone_aware_attributes is set to false or not.

  • With time zones configured, always use .current for Time, Date, and DateTime.

    ActiveRecord attributes will be time-zoned, and .current values will be converted properly when written to the database.
    Do not use Time.now and friends. Timezone-less objects will not be converted properly when written to the database.

  • With no/local time zone use Time.now, `...

acts_as_taggable_on: Match records tagged with all, any or none of the given tags

The tagged_with scope comes with many awesome options to modify your search:

User.tagged_with("awesome", "cool")                     # Users that are tagged with awesome and cool
User.tagged_with("awesome", "cool", :exclude => true)   # Users that are not tagged with awesome or cool
User.tagged_with("awesome", "cool", :any => true)       # Users that are tagged with awesome or cool
User.t...

Always sort lists that are visible in the UI

This might seem obvious, but I'm rejecting stories because of this on a regular basis.

Whenever a list (e.g. an index of records or the options of a select box) is visible in the UI that list must be sorted. Unsorted lists are completely unusable once they contain more than 5 items.

Make Less interpret the escape codes in a logfile

The unix command line tool less is a good choice for browsing logfiles. In the standard configuration, though, it does not interpret the escape sequences used in the rails logfiles. To enable this type:

less -R my_logfile.log

You can also have an alias to save yourself the typing

alias less='less -R'