Machinist: Refer to another named blueprint inside a blueprint
Note: We are talking about Machinist 1 here, Machinist 2 may have solved this or might require a different approach.
Machinist allows named blueprints (e.g. User.blueprint(:admin)
) that inherit from the master blueprint (User.blueprint
).
If you also want to inherit from another blueprint (e.g. if "vip" should load "premium" and the master blueprint) you can do this:
User.blueprint(:vip) do
# Fields for the vip blueprint go her...
MySQL: Disable query cache for database profiling
If you want to see how long your database queries actually take, you need to disable MySQL's query cache. This can be done globally by logging into a database console, run
SET GLOBAL query_cache_type=OFF;
and restart your rails server.
You can also disable the cache on a per query basis by saying
SELECT SQL_NO_CACHE * FROM ...
You also probably want to disable Rails internal (per-request) cache. For this, wrap your code with a call to ActiveRecord::Base.uncached
. For example, as an around_filter
:
d...
Even with bundler your gem order can be significant
Even when you're using bundler, it might be significant in which order your gems are listed in your Gemfile
. This can happen when gems are running around calling require
or require_dependency
on other gems or application classes when loaded (don't do that!).
A known culprit of this is the (otherwise wonderful) resource_controller gem, which requires ApplicationController
when loaded. When your ApplicationController
requires later-loaded gems when loaded, Rails will not boot.
He...
Taking advantage of RSpec's "let" in before blocks
Inside before
:each
blocks you can refer to variables that you introduce via let
later on.
They do not need to be defined ahead of your before block and can be different for individual sections.
It works just like that:
describe User do
describe '#locked?' do
before :each do
subject.should_receive(:current_plan).and_return plan
end
context 'when expiring today' do
let(:plan) { stub(:expiry => Date.today) }
it 'should be false' do
subject....
Kill a dead SSH shell
If a SSH shell dies (from timeout for example), you cannot kill it with the usual CTRL-C
or CTRL-Z
. Instead, press
[ENTER]~.
(That is ENTER TILDE PERIOD
).
Cucumber step to match table rows with Capybara
These steps are now part of Spreewald.
This note describes a Cucumber step that lets you write this:
Then I should see a table with the following rows:
| Bruce Wayne | Employee | 1972 |
| Harleen Quinzel | HR | 1982 |
| Alfred Pennyworth | Engineering | 1943 |
If there are additional columns or rows in the table that are not explicitely expected, the step won't complain. It does however expect the rows to be ordered as stat...
Be careful with Array(...)
A popular ruby idiom I keep stumbling upon is
def do_some_thing_for(values)
values = Array(values)
values.each { ... }
end
The intent is to allow the user to pass in arrays as well as scalar values, since
Array("foo") == ["foo"]
Array(["foo", "bar"]) == ["foo", "bar"]
But Array()
actually calls to_a
on its arguments, which may not always do what you expect. For example
Array("foo \n bar") == ["foo \n", "bar"]
Reload the page in your Cucumber features
Both these approaches will keep your GET
parameters -- and will only work for GET
requests.
-
Capybara:
When /^I reload the page$/ do visit [ current_path, page.driver.request.env['QUERY_STRING'] ].reject(&:blank?).join('?') end
-
Webrat:
When /^I reload the page$/ do visit url_for(request.params) end
For a step that distinguishes between drivers (Selenium, Rack::Test, Culerity), check [n4k3d.com](http://n4k3d.com/blog/2011/02/02/reloading-the-page-in-cucumber-with-capybara-and-seleniu...
RSpec's context method is broken
RSpec's context
(which is basically an alias for describe
) takes over your whole application. No object may have its own context
method, or you will always receive errors like
"No description supplied for example group declared on ~/project/app/..."
The easiest workarounds:
- do not name any method
context
- use
describe
instead ofcontext
in your specs, and put this into yourspec_helper.rb
:\
Spec::DSL::Main.class_eval do
if method_defined? :context
undef :context
end
end
Save ActiveRecord models without callbacks or validations (in Rails 2 and Rails 3)
Rails 2
You can use
record.send(:update_without_callbacks)
or
record.send(:create_without_callbacks)
This can be used as a lightweight alternative to machinist's make
or FactoryGirl's create
, when you just need objects in the database but don't care about any callbacks or validations. Note that create_without_callbacks
does not return the object, so you might want to do
record = Record.new.tap(&:create_without_callbacks)
Rails 3
Rails 3 no longer comes with update_without_callbacks
or `crea...
Fix permissions of temporary RTeX files (which are group and world-readable)
We use RTeX for PDF exports.
While converting LaTeX to PDF, RTeX opens a temporary file which has problematic permissions: Both group and world can read those files.
Although the temp files should go away they sometimes live longer than one would expect.
We patched RTeX to fix this (and have more secure permissions). Place the code below into config/initializers/rtex.rb
Recent RSpec features you might not know about
With its
you can switch the subject of an example to a method value of the current subject:
describe Array do
its(:length) { should == 0 }
end
stub_chain
is the tie to go along with should_receive_chain's tux:
object.stub_chain(:first, :second, :third).and_return(:this)
You can restore the original implementation of stubbed methods with unstub
:
object.stub(:foo => 'bar')
# ...
object.unstub(:foo)
In recent RSpecs ...
Security issues with hash conditions in Rails 2 and Rails 3
Find conditions for scopes can be given either as an array (:conditions => ['state = ?', 'draft']
) or a hash (:conditions => { 'state' => 'draft' }
). The later is nicer to read, but has horrible security implications in some versions of Ruby on Rails.
Affected versions
Version | Affected? | Remedy |
---|---|---|
2.3.18 | yes | Use chain_safely workaround |
3.0.20 | no | ... |
Run a rake task in all environments
Use like this:
power-rake db:migrate VERSION=20100913132321
By default the environments development
, test
, cucumber
and performance
are considered. The script will not run rake on a production
or staging
environment.
This script is part of our geordi gem on github.
Run a single example group in RSpec
To only run a single describe
/context
block in a long spec, you can say
spec spec/models/note_spec.rb:545
... where the describe
block starts at line 545.
Note: This will only run examples that are direct children of this block, not descendants further down (when nesting describe
/context
blocks).
You may also pass the line of an it
block to run this exact one.
Ruby: How to collect a Hash from an Array
There are many different methods that allow mapping an Array to a Hash in Ruby.
Array#to_h
with a block (Ruby 2.6+)
You can call an array with a block that is called with each element. The block must return a [key, value]
tuple.
This is useful if both the hash key and value can be derived from each array element:
users = User.all
user_names_by_id = users.to_h { |user| [user.id, user.name] }
{
1 => "Alice",
2 => "Bob"
}
Array#to_h
on an array of key/value tuples (Ruby 2.1+)
Converts an Array ...
Testing state_machine callbacks without touching the database
You should test the callback methods and its correct invocation in two separate tests. Understand the ActiveRecord note before you move on with this note.
Say this is your Spaceship
class with a transition launch
and a release_docking_clamps
callback:
class Spaceship
state_machine :state, :initial => :docked do
event :launch do
transition :docked => :en_route
end
before_transition :on => :launch, :do => :release_doc...
Strip carriage returns in submitted textareas
When submitting textarea
s, browsers sometimes include carriage returns (\r
) instead of just line feeds (\n
) at the end of each line. I don't know when this happens, and most of the time it doesn't matter.
In cases where it does matter, use the attached trait to remove carriage returns from one or more attributes like this:
class Note
does 'strip_carriage_returns', :prose, :code
end
Here is the test that goes with it:
describe Note do
describe 'before_validation' do...
Fix a spec that only runs when called directly
When a spec only runs when it is called directly, but not as part of the whole test suite, make sure the filename is foo_spec.rb
instead of just foo.rb
.
Using RSpec stubs and mocks in Cucumber
By default, Cucumber uses mocha. This note shows to use RSpec stubs and mocks instead.
Rspec 1 / Rails 2
Put the following into your env.rb
:
require 'spec/stubs/cucumber'
Rspec 2 / Rails 3
Put the following into your env.rb
:
require 'cucumber/rspec/doubles'
Note: Since Cucumber 4 it is important to require these lines in the env.rb
and not any other file in support/*
to register the hooks after any other After
hook in support/*
. Otherwise your doubles are removed, while other After
steps requi...
Know your Haml comments
There are two distinct ways of commenting Haml markup: HTML and Ruby.
HTML comments
This will create an HTML comment that will be sent to the client (aka browser):
/= link_to 'Example', 'www.example.com'
This produces the following HTML:
<!-- = link_to 'Example', 'www.example.com' -->
Only use this variant if you need the comment to appear in the HTML.
Ruby comments
This will comment code so it will not be sent to the client:
-# = link_to 'foo'
99% of the time you'll be adding notes f...
Debug Ruby code
This is an awesome gadget in your toolbox, even if your test coverage is great.
-
gem install ruby-debug
(Ruby 1.8) orgem install debugger
(Ruby 1.9) - Start your server with
script/server --debugger
- Set a breakpoint by invoking
debugger
anywhere in your code - Open your application in the browser and run the code path that crosses the breakpoint
- Once you reach the breakpoint, the page loading will seem to "hang".
- Switch to the shell you started the server with. That shell will be running an irb session where you can step thr...
Override e-mail recipients in ActionMailer
Our gem Mail Magnet allows you to override e-mail recipients in ActionMailer so all mails go to a given address.
This is useful for staging environments where you want to test production-like mail delivery without sending e-mails to real users.
Use Shoulda's validate_uniqueness_of matcher correctly
This raises "Could not find first Keyword":
describe Keyword do
it { should validate_uniqueness_of(:text) }
end
Do this instead:
describe Keyword do
it 'should have a unique #text' do
Keyword.make
should validate_uniqueness_of(:text)
end
end
This is the intended behavior.