Ruby and Rails deprecation warnings and how to fix them

Add deprecation warnings and their solution or link to available solutions.

Global access to Rake DSL methods is deprecated. Please include Rake::DSL into classes and modules which use the Rake DSL methods.

Open your Rakefile and add the following line above YourApp::Application.load_tasks:

YourApp::Application.class_eval do
  include Rake::DSL
end

Use of ole/file_system is deprecated. Use ole/storage (the file_system api is recommended and enabled by default)...

Sanitize user-generated filenames and only send files inside a given directory

If in your application your users pass along params that result in filenames, like invoices/generated?number=123. This could be your (very careless) controller method:

def generated
  send_file File.join(Rails.root, 'shared', 'invoices', params[:number])
end

This allows your users not only to access those files but also any files your application can read, like this:

invoices/generated?number=../../../../../etc/passwd
# => send_file '/etc/passwd'

You do not want this. In most cases you should prefer a show met...

CSS3 Pie: Element not properly redrawn

Pie sometimes does not properly redraw elements upon changes. This often happens when the change comes from somewhere further up the DOM.

Consider something like:

<ul>
  <li class="active"><div class="content">Active element</div></li>
  <li class="inactive"><div class="content">Inactive element</div></li>
</ul>

with CSS
li .content {
-webkit-box-shadow: #666 0px 2px 3px;
-moz-box-shadow: #666 0px 2px 3px;
box-shadow: #666 0px 2px 3px;
behavior: url(/PIE.htc);

  back...

Speed up response time in development after a Sass change

When working with large Sass files you will notice that the first request after a change to a Sass file takes quite some time. This is because the CSS files are being generated from the Sass files the moment the application answers your request (Sass looks at the files and recompiles if the timestamp changed); it takes even longer when you build sprites with the Lemonade gem.

To avoid this, have Sass watch the files for changes and compile them into CSS files immediately. Th...

Synchronize a Selenium-controlled browser with Capybara

When you click a link or a press a button on a Selenium-controlled browser, the call will return control to your test before the next page is loaded. This can lead to concurrency issues when a Cucumber step involves a Selenium action and a Ruby call which both change the same resources.

Take the following step which signs in a user through the browser UI and then sets a flag on the user that was just signed in:

Given /^the user "([^"]*)" signed in (\d) days ago$/ do |name, days|
  visit new_session_path
  fill_in 'Username', :w...

Simple database lock for MySQL

Note: For PostgreSQL you should use advisory locks. For MySQL we still recommend the solution in this card.


If you need to synchronize multiple rails processes, you need some shared resource that can be used as a mutex. One option is to simply use your existing (MySQL) database.

The attached code provides a database-based model level mutex for MySQL. You use it by simply calling

Lock.acquire('string to synchronize on') do
  # non-th...

How to change MySQL's data directory

  1. Have a backup.

  2. Stop MySQL:

    sudo service mysql stop
    
  3. Move (or copy) your mysql directory. If you want /mnt/mysql to be the new directory, do it like this:

    sudo mv /var/lib/mysql /mnt/
    
  4. Open your MySQL configuration (sudo vim /etc/mysql/my.cnf) and change the datadir value to your new path (e.g. /mnt/mysql)

  5. Modify your AppArmor configuration:

    sudo vim /etc/apparmor.d/usr.sbin.mysqld
    

    Change/copy the lines granting access to /var/lib/mysql to your new path. Otherwise MySQL will not ...

Speed up file downloads with Rails, Apache and X-Sendfile

When you use the send_file method to send a local file to the browser, you can save resources on the application server by setting the :x_sendfile option to true. This option is activated by default for Rails 3, so you need to understand this.

What this option does is not to send any data at all, but rather set the local file path as a new response header:

X-Sendfile: /opt/www/awesome-project/shared/downloads/image.png

When the response comes back from Rails to...

Perform HTTP basic authentication in Cucumber (with or without Selenium)

This card describes a Cucumber step that lets you say:

When I perform basic authentication as "username/password" and I visit the admin area

The path component ("... the admin area") is parsed through your path_to helper in features/support/paths.rb.

Capybara

The step definition is part of Spreewald. The step has been tested with multiple versions of Capybara, Rack::Test and Selenium.

Webrat (legacy)

This is a simpler version of the step above:

When /...

Access the documentation of all locally installed gems

In case https://www.rubydoc.info/ is to slow or offline, you can also read a gem documentation offline.

Start a server with gem server and go to http://0.0.0.0:8808/. Here you will find a list of all installed gems and it is possible to navigate to the documentation if installed e.g. http://0.0.0.0:8808/doc_root/rubocop-0.77.0/


In case you set the configured RubyGems to not install documentation by default, you need to add generate the documentation for the specific gem.

gem install rubocop --document
`...

Concurrency issues with find-as-you-type boxes

Find-as-you-type boxes are usually built by observing changes in a text field, and querying the server via AJAX for search results or suggestions when the field has changed.

A common problem with this implementation is that there is no guarantee that AJAX responses are evaluated in the same order as the original requests. The effect for the user is that the search results are flashing back and forth while the user is typing the query, and when the user has stopped typing the last results don't always match the final query.

Workarounds
----...

Apache: Log the original client IP when your site sits behind a reverse proxy

When your site is mapped into the URL-space of another server using mod_proxy, ProxyPass and ProxyPassReverse, all requests in your Apache logs are logged with the IP address of the proxying server. The IP address of the original client doing the request is not logged, making it difficult to trace problems and run statistics.

Short answer

There is no easy way to fix this. Use the log of the proxying server instead, which logs the original client IPs you're looking for.

Long answer

You can fix this for your ac...

Apache: Redirect all requests from one host to another

In order to redirect all requests from redirecting-host.com to desired-host.com while keeping path and query params unchanged, change your Apache VHost to something like this:

ServerName desired-host.com
ServerAlias redirecting-host.com
RewriteEngine On
RewriteCond %{HTTP_HOST} !^desired-host.com$
RewriteRule ^.*$ http://desired-host.com%{REQUEST_URI} [R=301,L]

Take care to keep all those ^, $ and ! as seen in the example.

How to fix failing controller specs 91% of the time

If your controller spec never reaches your controller code:

  1. Make sure you are signed in.

  2. Make sure you are actually triggering a request by saying get :edit or something siliar.

  3. Know that views are not rendered by default for controller specs unless you tell them to (render_views).
    ^
    describe UsersController do
    describe '#edit' do
    it 'should work' do
    sign_in
    get :edit
    end
    end
    end

    define something like this in your spec_helper.rb:

    def sign_in(user = User....

Use the back button in Cucumber

In order to go back one page in your Cucumber tests, you can use the following step definition for Capybara:

When(/^I go back$/) do
  visit page.driver.request.env['HTTP_REFERER']
end

If you're on Webrat, this should work:

When(/^I go back$/) do
  visit request.env["HTTP_REFERER"])
end

An improved version of this step is now part of our gem spreewald on Github.

Useful collection of Sass mixins

This collection of Sass mixins enables cross-browser styling (including IE with CSS3PIE) with less lines of code.

This enables PIE for IE up to version 8 only (the first part is not possible in Haml, so use ERB):

<!--[if !IE]><!-->
  <%= stylesheet_link_tag 'screen', :media => 'screen' %>
<!--<![endif]-->
<!--[if lte IE 8]>
  <%= stylesheet_link_tag 'screen_with_pie', :media => 'screen' %>
<![endif]-->

These would be your two screen Sasses:

# screen_with_pie.sass

...

Bookmarklet to generate a commit message with Pivotal Tracker story ID and title

For clarity and traceability, your commit messages should include the ID and title of the Pivotal Tracker story you're working on. For example:

[#12345] Add Google Maps to user profiles
Optional further commit messages in the body

Also see Howto: Write a proper git commit message

To quickly generate such commit messages, add a new link "Commit" to your bookmarks and use the following Javascript as the link URL:

javascript:(function() { ...

Aliases for routes

The following initializer provides an :alias => "my_route_name" option to restful routes in your route.rb. This simply makes the same route also available under a different ..._path / ..._url helpers.

For example,

map.resources :notes, :alias => :snippets

Gives you

notes_path, notes_url, new_note_path... #as always
snippets_path, snippets_url, new_snippet_path... #from the alias

Put this into an initializer:

Request a non-HTML format in controller specs

If a controller action responds to other formats than HTML (XML, PDF, Excel, JSON, ...), you can reach that code in a controller spec like this:

describe UsersController do
  describe '#index' do
    it 'should be able to send an excel file' do
       # stubs and expectations go here
       get :index, :format => 'xls'
    end
  end
end

Remember that both the :format parameter and the HTTP_ACCEPT header can m...

Get the current layout's name in a view or partial

This returns the name (including path) of your current layout:

response.layout
=> "layouts/admin" # inside views that are using the 'admin' layout

You most likely do not need the full path, so go ahead and do this:

File.basename(response.layout)
=> "admin"

Generate a path or URL string from an array of route components

When using form_for you can give the form's target URL either as a string or an array:

form_for(admin_user_path(@user)) do ... end
# same as:
form_for([:admin, @user]) do ... end

Same for link_to:

link_to("Label", edit_admin_user_path(@user))
# same as
link_to("Label", [:edit, :admin, @user])

polymorphic_path and polymorphic_url

If you would like to generate a path or URL string from an array of route components just as form_for does, you can use polymorphic_path or polymorphic_url:

polymorphic...