RSpec: Where to put shared example groups
Shared example groups are a useful RSpec feature. Unfortunately the default directory structure generated by rspec-rails
has no obvious place to put them.
I recommend storing them like this:
spec/models/shared_examples/foo.rb
spec/models/shared_examples/bar.rb
spec/models/shared_examples/baz.rb
spec/controllers/shared_examples/foo.rb
spec/controllers/shared_examples/bar.rb
spec/controllers/shared_examples/baz.rb
To ma...
Migrating to RSpec 2 from RSpec 1
You will need to upgrade to RSpec >= 2 and rspec-rails >= 2 for Rails 3. Here are some hints to get started:
- In RSpec 2 the executable is
rspec
, notspec
. - RSpec and rspec-rails have been completely refactored internally. All RSpec classes have been renamed from
Spec::Something
toRSpec::Something
. This also means that everyrequire 'spec/something'
must now berequire 'rspec/something'
. - In
spec_helper.rb
,Spec::Runner.configure
becomesRSpec.configure
- It has become really hard to extend specific example groups ...
Upgrading Ruby from 1.8.7 to 2.3.5
Suggested Workflow
Set the ruby version in .ruby-version
to 2.3.5, then perform these steps one by one, fixing errors as they occur:
- Update gems as listed below, and bundle
- Boot a Rails console - see below for a list of changes you will probably need
- Run Specs with
--backtrace
option - Run Cucumber features (with Geordi's
--debug
option) - When all tests are green, look through your Gemfile and remove as many version constraints as possible.
- Boot the application in different environements to spot further issues, e...
Disabling Spring when debugging
Spring is a Rails application preloader. When debugging e.g. the rails
gem, you'll be wondering why your raise
, puts
or debugger
debugging statements have no effect. That's because Spring preloads and caches your application once and all consecutive calls to it will not see any changes in your debugged gem.
Howto
Disable spring with export DISABLE_SPRING=1
in your terminal. That will keep Spring at bay in that terminal session.
In Ruby, [you can only write environment variables that subproc...
How to: Solve gem loaded specs mutex
Use bundler > 1.15
to fix Gem::LOADED_SPECS_MUTEX (NameError)
.
Given the following project:
ruby -v
ruby 1.8.7
bundler -v
Bundler version 1.13.7
gem -v
1.8.30
rails -v
Rails 3.2.22.1
Running specs or features resulted in:
uninitialized constant Gem::LOADED_SPECS_MUTEX (NameError)
The previous settings described in Maximum version of Rubygems and Bundler for Ruby 1.8.7 and Rails 2.3 (even the rails version was rails 3.2 and not 2.3) seems not to work here, so I used (also described in the ca...
When sessions, cookies and Clearance tokens expire and how to change it
Expiration of Rails sessions
By default Rails sessions expire when the user closes her browser window.
To change this edit your config/initializers/session_store.rb
like this:
ActionController::Base.session = {
:key => '...',
:secret => '...'
:expire_after => 10.years
}
In older Railses the initializer is not available. Set the option in the environment.rb
instead:
config.action_controller.session = {
:key => '...',
:secret => '...'
...
Caution: `.includes` can make `.ids` non-unique.
This can happen with a very simple model:
class Note
has_many :attachments
end
Everything looks normal:
Note.all.to_a.size # => 8
Note.all.ids.size # => 8
Then .includes
leads to weird results:
Note.all.includes(:attachments).to_a.size # => 8
Note.all.includes(:attachments).ids.size # => 12
If a note has 5 attachments, its id will be included 5 times.
With .preload
it works as expected:
Note.all.preload(:attachments).to_a.size # => 8
Note.all.preload(:attachments).ids.size # => 8
Note
I crea...
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 %...</p>
Force absolute URLs for parts of a view or controller
You know that you can force absolute URLs throughout a response. Now you want to modify URLs similarly, but only in parts of a view (or controller) logic. Here is how.
Note: this has only been tested on a Rails 2 application. It should work similarly for Rails 3.
Put this into your ApplicationController
:
def rewrite_options(*args)
options = super
options.merge!(:only_path => false) if @with_full_urls
options
end...
How to disable cookies in cucumber tests
Unfortunately, Capybara does not offer a switch to disable cookies in your test browser. However, you can work around that by using a tiny Rack middleware -- it works for both Selenium and non-Selenium tests.
Wouldn't it be nice to say something like this?
Given cookies are disabled
When I try to sign in
Then I should see "Can't sign you in. Please enable cookies."
You can! Put the code below into some place like lib/rack/cookie_stripper.rb
.
module Rack
class CookieStripper
ENABLED = false
...
Fix warning: No secret option provided to Rack::Session::Cookie
You will get this when you are using the latest version of Rails with a recent version of Rack:
SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
This poses a security threat. It is strongly recommended that you
provide a secret to prevent exploits that may be possible from crafted
cookies. This will not be supported in future versions of Rack, and
future versions will even invalidate your existing user cookies.
The warning is caused by Rails calling Rack incorrectly. [It is unclear](https://github.c...
IRB's multi-line autocomplete and how to disable it
Recent IRB versions include a multi-line autocomplete which may be helpful to novice users but can be distracting.
Cycling through options works by pressing the Tab key (as usual), and for some methods you also get some kind of documentation, though the quality of results is usually not on par with your IDE of choice.
I have found that it also slows down my IRB in some cases, or that pressing the Backspace key does not always reliably remove characters, which I find more annoying than useful.
You may disable multi-line autocomplete by
- ...
Safely chain scopes with hash conditions
There is a nasty bug in all version of Rails 2 and some versions of Rails 3.x where two chained scopes with hash conditions on the same attribute would overwrite each other.
This is a horrible security issue if you are using scopes to limit what a user may see or change.
Workaround
If you are using an affected Rails version and cannot switch to a fixed version, you can use this manual workaround....
Gem development: When your specs don't see dependencies from your Gemfile
When you develop a gem and you have a Gemfile
in your project directory, you might be surprised that your gem dependencies aren't already required in your specs. Here is some info that should help you out:
- Bundler actually doesn't automatically require anything. You need to call
Bundler.require(:default, :your_custom_group1, ...)
for that. The reason why you never had to write this line is that Rails does this for you when it boots the environment. - That also means that if you have an embedded Rails app in your
spec
folder (like [h...
Ruby constant lookup: The good, the bad and the ugly
In Ruby, classes and modules are called constants. This card explains how Ruby resolves the meaning of a constant.
The good
E. g. in the following example, Array
could mean either Foo::Array
or simply Array
:
class Foo
def list
Array.new
end
end
What Ruby does here is to see if the name Array
makes sense inside of Foo::
, and if that fails, resolves it to ::Array
(without a namespace).
The bad
This is relevant for old Ruby versions. Ruby 2.5+ removes top-level constant lookup whi...
How to remove properties of ActiveRecord scopes
When dealing with AR scopes, you can remove conditions, order, etc by using the unscope
method.
It is available on Rails 4+.
Examples
Consider an exemplary User
class as follows. For the examples below, we will use a scope that applies all its constraints.
class User < ActiveRecord::Base
scope :active, -> { where(locked: false) }
scope :admins, -> { where(role: 'admin') }
scope :ordered, -> { order(:name) }
end
users = User.active.admins.ordered
^
SELECT "users".* FROM "users" WHERE "use...
Spreewald 4.3.3 released
Field error steps
Spreewald's The ... field should have an error
and The ... field should have the error ...
steps now have built-in support for Rails and Bootstrap (v3-v5) error classes. When using Bootstrap, it is no longer necessary to overwrite the steps in your project.
At the same time, support for formtastic has been removed as there were no real use cases. Due to that, no breaking change was introduced, as the amount of users affected by this should be zero (it was neither in the documentation nor tested).
Users may now add...
Minidusen: Low-tech record filtering with LIKE queries
We have a new gem Minidusen which extracts Dusen's query parsing and LIKE
query functionality.
Minidusen can no longer index text in MySQL FULLTEXT columns, which was hardly used and didn't always help performance due to the cost of reindexing.
Minidusen is currently compatible with MySQL, PostgreSQL, Rails 3.2, Rails 4.2 and Rails 5.0.
Basic Usage
Our example will be a simple address book:
class Contact < ActiveRecord::Base
validates_presence_of :name, :street, :city, :e...
Retrieving the class an ActiveRecord scope is based on
Edge Rider gives your relations a method #origin_class
that returns the class the relation is based on.
This is useful e.g. to perform unscoped record look-up.
Post.recent.origin_class
# => Post
Note that #origin_class
it roughly equivalent to the blockless form of #unscoped
from Rails 3.2+, but it works consistently across all Rails versions. #unscoped
does not exist for Rails 2 and is broken in Rails 3.0.
Thread-safe collections in Ruby
When using threads, you must make your code thread-safe. This can be done by either locking (mutexes) all data shared between threads, or by only using immutable data structures. Ruby core classes like String
or Array
are not immutable.
There are several gems providing thread-safe collection classes in Ruby.
concurrent-ruby
The concurrent-ruby gem provides thread-safe versions of Array
and Hash
:
sa = Concurrent::Array.new # supports standard Array.new forms
sh = Co...
Email validation regex
There is a practical short list for valid/invalid example email addresses - Thanks to Florian L.! The definition for valid emails (RFC 5322) can be unhandy for some reasons, though.
Since Ruby 2.3, Ruby's URI lib provides a built-in email regex URI::MailTo::EMAIL_REGEXP
. That's the best solution to work with.
/\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[...
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));
});
Bulk-change multiple table rows in a migration
Using rename_column
, remove_column
, etc. more than once in a migration makes that migration run slower than it should. Use change_table
instead.
Consider this migration:
add_column :users, :name, :string
remove_column :users, :first_name
remove_column :users, :last_name
rename_column :users, :cool, :awesome
Migrating in this case means that all those commands are processed step by step, causing 4 SQL statements to change the table. In turn, your database needs to modify the table structure 4 times. When working on hu...