Alternative to #to_a and Array(...)
Note: In Rails 3+ you can use Array.wrap
instead of the solution here.
In the past you could use Array(...)
or #to_a
to turn an object into an array, unless it already is an array:
Array(5) # => [5]
Array([5]) # => [5]
Unfortunately this idiom has issues and it also triggers deprecation warnings like this one:
warning: default `to_a' will be obsolete
For an alternative you can copy the attached file to `config/i...
Use form_for without the enclosing form tag
In rare cases you might need something like form_for
(for using form builder methods on the resulting block element) but without the surrounding form. One such case would be updating some of a form's fields via XHR.
You can simply use Rails' fields_for
to do things like this in your views (HAML here):
- fields_for @user do |form|
= form.label :email, 'E-Mail'
= form.text_field :email
You will only receive the form content you gave, no hidden inputs incl...
Stub methods on any instance of a class in Rspec 1 and Rspec 2
RSpec 1 (Rails 2)
With the most recent spec_candy.rb
helpers you can say:
User.stub_any_instance(:foo => :bar)
user = User.new
user.foo
# => :bar
RSpec 2 (Rails 3)
RSpec 2 comes with this feature built in:
User.any_instance.stub(:foo => :bar)
user = User.new
user.foo
# => :bar
RSpec 3
-------...
How to change the order of nested forms being rendered (especially blank forms)
Generally for nested forms, a blank form is placed below all other existing object forms. If you would like to change the position of the blank form(s) you can reorder the object's one-to-many association. For example you can put the blank form on top with the following snippet:
actors = movie.actors
actors.build
actors.unshift(actors.pop(1)) # won't work with Rails 4+
Because build_for_form
creates new objects and ap...
How to inspect controller filter chains in specs
Sometimes you need to look at the filter chain in specs. You can do it like that on Rails 2:
controller.class.filter_chain.map(&:method)
Note that we need to look at the controller's class since before_filter
and after_filter
stuff happens on the class level.
Also mind that the above code will give you all filters, both those run before and after an action. You can query before?
and after?
on the filter objects to scope down to only some of them:
controller.class.filter_chain.select(&:before?).map(&:method)
For Rails 3, ...
Unfreeze a frozen ActiveRecord
You can freeze any Ruby object to prevent further modification.
If you freeze an ActiveRecord and try to set an attribute you will an error like this:
can't modify frozen hash
This is because ActiveRecord delegates #freeze
to its attributes hash.
You can unfreeze most Ruby objects by creating a shallow copy of the frozen object by calling #dup
on it:
user = User.find(3)
user.freeze
unfrozen_user = user.dup
Notes for Rails 2 users
-----------------...
How to diff two strings in Ruby
When you need to use diff
in either some Ruby code or your Rails app, use the differ gem.
puts Differ.diff "foo", "boo"
# => {"boo" >> "foo"}
Usage
There are several variants available, all using the base method diff(to, from, separator = "\n")
.
You have diff_by_line
, diff_by_word
, diff_by_char
and may of course use your own separator:
puts Differ.diff 'Hauptsatz, und mein Nebensatz.', 'Hauptsatz, und dein Nebensatz.', ','
# => Hauptsatz,{" und dein Nebensatz." >> " un...
Fix "couldn't parse YAML" error after upgrading Bundler
If you just upgraded to Bundler 10.0.10 you might get the following error when bringing up Rails:
/usr/lib/ruby/1.9.1/psych.rb:148:in `parse': couldn't parse YAML at line 17 column 14 (Psych::SyntaxError)
This is caused by Rails localization files (en.yml
, de.yml
, etc.) using symbols for various translation strings, and Bundler 10.0.10 defaults to a new YAML engine which cannot handle symbols.
You can switch back to the old YAML engine by ...
How to render an html_safe string escaped
Once Rails knows a given string is html_safe
, it will never escape it. However, there may be times when you still need to escape it. Examples are some safe HTML that you pipe through JSON, or the display of an otherwise safe embed snippet.
There is no semantically nice way to do this, as even raw
and h
do not escape html_safe
strings (the former just marks its argument as html_safe
). You need to turn your string into an unsafe string to get the escaping love from Rails:
embed = javascript_tag('var foo = 1337;') # This is an h...
include_tags with the asset pipeline
You can include files from app/assets
or from the public
folder with javascript_include_tag
. The subtle difference that tells rails how to build the path correctly is a single slash at the beginning of the path:
<%= javascript_include_tag('ckeditor/config') %> # for assets/ckeditor/config.js
<%= javascript_include_tag('/ckeditor/ckeditor') %> # for public/ckeditor/ckeditor.js
This also applies to stylesheet_link_tag
.
Note that when you refer to a Javascript or stylesheet in /assets
you need to add it to [the list of asse...
Run specific version of bundler
You can specify the version of bundler
to execute a command (most often you need an older version of bundler, but don't want to uninstall newer ones):
bundle _1.0.10_ -v
Bundler version 1.0.10
An example is rails 3.2, which freezes bundler
at version ~> 1.0
:
Bundler could not find compatible versions for gem "bundler":
In Gemfile: rails (~> 3.2) was resolved to 3.2.0, which depends on bundler (~> 1.0)
Current Bundler version: bundler (1.13.6)
You can solve this with:
gem install bundler -v 1....
Best practices for REST API design
A rough guide how to implement a REST API.
The discussion here includes some interesting points as well:
- Timestamps: ISO8601 format ("2021-02-22T20:34:53.686Z")
- Google API guideline: https://google.aip.dev/
- Numbers: String vs. Number
The JSON number type is not a double. It's just a number of arbitrary size and precision in integer/decimal/E format that can be parsed as whatever the parser finds fitting.
- Pagination: Limit + Offset vs. Object ID / Pointer vs. System-Version...
HTML5: Allow to choose multiple files with a vanilla file picker
Modern browsers natively suppport file pickers that allow the user to choose multiple files at once. To activate this feature, set the multiple
attribute:
<input type="file" name="images[]" multiple />
Or in a Rails view:
<%= file_field_tag "images[]", multiple: true %>
This works in IE10+.
Make sure that the field name ends in []
so your server-side code will parse the incoming files into an array. Obviously this naming convention is not compatible with default Rails nested attribute setters, so you'll need to write a form ...
Execution of shell code in Ruby scripts
Deprecated ways to execute shell code in Ruby
This is just a reference for legacy code. For new code, always use capture3
.
%x{ } or backticks – quick and easy
Returns the standard output of running the given command in a subshell. This is an alias for `...`, and you can use string interpolation.
Example:
name = 'ls'
result = `which #{name}`
It does not escape anything you inject in the string, so be aware of possible security vulnerabilities...
simple_format helper for Javascript
The Javascript code below is a rough equivalent to the simple_format helper that ships with Rails:
function simpleFormat(str) {
str = str.replace(/\r\n?/, "\n");
str = $.trim(str);
if (str.length > 0) {
str = str.replace(/\n\n+/g, '</p><p>');
str = str.replace(/\n/g, '<br />');
str = '<p>' + str + '</p>';
}
return str;
}
Unlike the Rails helper, this does not preserve whitespace. You probably don't care.
Ruby 2.3.0 has a safe navigation operator
As announced before, Ruby has introduced a safe navigation operator with version 2.3.0. receiver&.method
prevents NoMethodError
s by intercepting method invocations on nil
.
user = User.last
user&.name # => "Dominik"
# When there is no user, i.e. user is nil:
user&.name # => nil
This might remind you of andand
, and indeed it behaves very similar. The only difference is in handling of `fa...
When using "render :text", set a content type
When your Rails controller action responds with only a simple text, render text: 'Hello'
may not be what you want. You should not even use it on Rails 4.1+ any more.
By default, a "text" response from a Rails controller will still be a sent as text/html
:
render text: 'Hello'
response.body # => "Hello"
response.content_type # => "text/html"
While this may not be too relevant for a Browser client, the response's content type is simply wrong if you want to send a plain-text response, and can cause trouble. \
For example, con...
Define a route that only responds to a specific format
You won't usually have to do this. It's OK to route all formats to a controller, and let the controller decide to which format it wants to respond.
Should you for some reason want to define a route that only responds to a given format, here is how you do it:
Rails 3
match 'sitemap.xml' => 'feeds#sitemap', :constraints => { :format => 'xml' }, :as => 'sitemap'
Rails 2
map.sitemap 'sitemap.xml', :controller => 'feeds', :action => 'sitemap', :format => 'xml'
Ruby < 2.4: Downcasing or upcasing umlauts
Using .downcase
or .upcase
on strings containing umlauts does not work as expected in Ruby versions before 2.4. It leaves the umlauts unchanged:
"Über".downcase
=> "Über"
"Ärger".downcase
=> "Ärger"
The very same applies for french accents (Thanks Guillaume!):
"Être ou ne pas être, telle est la question".downcase
=> "Être ou ne pas être, telle est la question"
Obviously, this leads to problems when comparing strings:
"Über".downcase == "über"
=> false
In Rails you can use ActiveSupports' [multib...
Katapult 0.3.0 released
Katapult 0.3.0 brings Rails 5 and Ruby 2.5 support with a new design, plus a ton of smaller features, fixes and improvements.
Features
- Generating a Rails 5.1.4 app on Ruby 2.5.0
- Dropped asset pipeline in favor of Webpacker
- The generated application now has a sleek, simple design based on Bootstrap
- Employing Unpoly
- New application model DSL shortcut
crud
for "create a model and a web UI with crud actions" - The generated application model is now a transformable e...
ActiveRecord 3+ auto-converts times to UTC by default. Hilarity ensues.
Remember how Rails 2 came with an awesome feature that broke all code using Time.now
or Time.parse
?
This behavior is now the default for Rails 3. Disable it by adding the following to your config/application.rb
:
config.active_record.default_timezone = :local
config.active_record.time_zone_aware_attributes = false
Understanding SQL compatibility modes in MySQL and MariaDB
MySQL and MariaDB have an SQL mode setting which changes how MySQL behaves.
The SQL mode value is comprised of multiple flags like "STRICT_TRANS_TABLES, NO_ZERO_IN_DATE"
. Each flag activates or disables a particular behavior.
The default SQL mode varies widly between versions of MySQL and MariaDB. In general, more recent versions of MySQL and MariaDB have stricter settings than older versions, and MySQL has stricter settings than the more liberal MariaDB.
If your app explodes ...
ActiveRecord meets database views with scenic
Using Scenic, you can bring the power of SQL views to your Rails application without having to switch your schema format to SQL. Scenic provides a convention for versioning views that keeps your migration history consistent and reversible and avoids having to duplicate SQL strings across migrations. As an added bonus, you define the structure of your view in a SQL file, meaning you get full SQL syntax highlighting in the editor of your choice and can easily test your SQL in the database console during development.
[https://robots.thoughtb...
How to: Validate dynamic attributes / JSON in ActiveRecord
PostgreSQL and ActiveRecord have a good support for storing dynamic attributes (hashes) in columns of type JSONB
. But sometimes you are missing some kind of validation or lookup possibility (with plain attributes you can use Active Record's built-in validations and have your schema.rb
).
One approach about being more strict with dynamic attributes is to use JSON Schema validations. Here is an example, where a project has the dynamic attributes analytic_stats
, that we can use to store analytics from an external measurement tool.
- A g...