Rails 4.2 Foreign Key Support
The migration DSL now supports adding and removing foreign keys. They are dumped to schema.rb as well. At this time, only the mysql, mysql2 and postgresql adapters support foreign keys. @rubyonrails
Workings
add_foreign_key(:comments, :users)
adds a database constraint
ALTER TABLE "comments" ADD CONSTRAINT comments_user_id_fk FOREIGN KEY ("user_id") REFERENCES "user" ("id")
Disadvantage
Foreign key constraints double the validatio...
JavaScript events: target vs currentTarget
tl;dr: Use event.currentTarget
unless you are absolutely certain that you need event.target
.
Since it hasn't been written down in this deck before, here it goes:
When working with JavaScript Event
objects, the DOM element that triggered the event is attached to them. [1]
However, there are 2 "opinions" on which element that would be:
- The element that the user interacted with (
event.target
), - or the element that the event listener is bound to (
event.currentTarget
).
Note that both can be, but not...
How to capture changes in after_commit
Your after_commit
callbacks will not know about changes, as Rails discards them when committing.
The linked article shows a clever trick to work around that: It uses an after_save
method that looks at changes
and writes its decision to an instance variable. That instance variable can then be used in the after_commit
method.
Note that while callbacks like after_save
are not affected, there are valid reasons for using only after_commit
, and not after_save
. Enqueueing a Sidekiq job is just one of them.
Rails 5+
You can use ...
24 little known CSS facts
This blew my mind today:
Please make sure to check browser support for CSS features on Can I Use and the Mozilla Development Network before using.
Favorites
-
outline-offset
: specify how far away from the element an outline is rendered -
::first-letter
: matches the first letter insid...
emcien/iso_latte
Sometimes you need to run background jobs that you can't make important guarantees about - they may run out of memory and get killed, or produce segmentation faults, or exit! directly - and you need to be able to clean up after such problems.
IsoLatte is a gem that allows a block of code to be executed in a subprocess. Exceptions get passed back to the parent process through a pipe, and various exit conditions are handled via configurable callbacks.
Protip: Clone large projects multiple times
Large projects usually have large test suites that can run for a long time.
This can be annoying as running tests blocks you from picking up the next story -- but it doesn't have to be that way!
Simply clone your project's repo twice (or even more often).
When your work on a feature branch is done, simply push that branch and check it out on your 2nd copy to run tests there.
You can pick up a new story and work on that on your "main" project directory.
If you do it right, you will even be able to run tests in both your 2nd copy and your m...
FactoryGirl: How to easily create users with first and last name
In most of our applications, users have their first and last name stored in separate columns. However, specifying them separately quickly gets annoying, especially when proxying them from cucumber_factory:
Given there is a user with the first name "Dominik" and the last name "Schöler"
Wouldn't it be nice if you could just say:
Given there is a user with the name "Dominik Schöler"
and have FactoryGirl assign first and last name automatically? The code below achieves that!
##...
How to disable auto-complete on login forms
Disabling auto-complete in login forms is probably a bad idea, since it encourages weak passwords.
If you are still forced to implement this (maybe due to legal or policy requirements), this is how:
Prevent browsers from saving the password in the first place. Disabling autocomplete does not improve security.
How to prevent password saving:
To prevent the browser from saving passwords (and usernames), you need to:
- copy username and password to hidden form fields before submitting the login form
- c...
Faster debugging with RubyMine macros
In my RubyMine I have recorded two macros for debugging and linked them to some keyboard shortcuts. Since I believe everyone could benefit from having those I wanted to share this.
The first one simply inserts
binding.pry
and the second one
.tap { |object| binding.pry }
for when you do not have a reference to the object you want to inspect.
In order to record a macro you simply follow the path Edit > Macros > Start Macro Recording
.
Then you simply type binding.pry
or whatever you want to record and stop recor...
Casting ActiveRecord scopes or instances to ActiveType extended model classes
When working with ActiveType you will often find it useful to cast an ActiveRecord instance to its extended ActiveType::Record
variant.
Starting with active_type 0.4.0 you can use ActiveType.cast
for this:
class User < ActiveRecord::Base
...
end
class SignUp < ActiveType::Record[User]
...
end
user = User.find(1)
sign_up = ActiveType.cast(user, SignUp)
sign_up.is_a?(SignUp) # => true
This is basically like [ActiveRecord#becomes
](http://apidock.com/rails/v4.2.1/ActiveRecord/...
Rails 3 ActiveRecord::Persistence#becomes does not copy changed attributes
Note: ActiveRecord::Base#becomes
has a lot of quirks and inconsistent behavior. You probably want to use ActiveType.cast
instead.
This issue will be encountered when relying on attribute_was
methods of ActiveModel::Dirty
after casting a model which has defaults to a form model, for example.
In my case a record with an assignable_values legacy value beca...
Databases don't order rows unless you tell them so
There is no such thing as a "default order" of rows in database tables.
For instance, when you paginate a result set: When using LIMIT
, it is important to use an ORDER BY
clause that constrains the result rows into a unique order. Otherwise you will get an unpredictable subset of the query's rows. You might be asking for the tenth through twentieth rows, but tenth through twentieth in what ordering? The ordering is unknown, unless you specified ORDER BY
.
In Rails, if you use Record.first
or Record.last
, it will default to orderin...
High Performance Browser Networking: HTTP/2
HTTP/2 will make our applications faster, simpler, and more robust—a rare combination—by allowing us to undo many of the HTTP/1.1 workarounds previously done within our applications and address these concerns within the transport layer itself. Even better, it also opens up a number of entirely new opportunities to optimize our applications and improve performance!
Querying model errors in Rails 4
ActiveModel
supplies an errors object that behaves similar to a Hash. It can be used to add errors to a record, as well as to query the record for registered errors. This object is returned when calling <object>.errors
:
errors = @user.errors # => #<ActiveModel::Errors ...>
Here are some helpful messages of its API:
[<attribute name>]
-
Returns an array of error messages on that attribute. Example:
errors[:name] => ['is missing']
-
add_on_blank(<attribute list>)
(similarlyadd_on_empty
) -
Registers an error ...
sessionStorage: Per-window browser storage
All major browsers (IE8+, FF3.5+, Safari 4+, any Chrome) support sessionStorage
, a JavaScript storage object that
- survives page reloads and browser restores,
- but is different per new tab/window (in contrast to
localStorage
which is shared across all tabs).
MDN says:
The
sessionStorage
object is most useful for hanging on to temporary data that should be saved and restored if the browser is accidentally refreshed
Demo
Example usage:
Show details of TLS/SSL connections of remote hosts
sslscan
is a nice tool to show details about TLS/SSL connections:
~> sslscan some-host-at.makandra.de
Testing SSL server some-host-at.makandra.de on port 443
Supported Server Cipher(s):
Failed SSLv3 256 bits ECDHE-RSA-AES256-GCM-SHA384
Failed SSLv3 256 bits ECDHE-ECDSA-AES256-GCM-SHA384
Failed SSLv3 256 bits ECDHE-RSA-AES256-SHA384
Failed SSLv3 256 bits ECDHE-ECDSA-AES256-SHA384
Rejected SSLv3 256 bits ECDHE-RSA-AES256-SHA
...
Prefered Server Cipher(s):
TLSv1 128 bits ECDHE-RSA-A...
Upgrading from Capistrano 2 to 3
Capistrano 3 is a major rework of the framework and requires several adjustments to your deploy configuration files. The biggest change is that they moved away from their custom DSL and use Rake
instead. For connecting with and operating on the servers, they bring a new gem SSHKit
which does the heavy lifting. It's SSHKit's DSL that is used anywhere inside the Rake tasks. See #Resources at the bottom for examples.
Step 1: Upgrade guide
For migration from 2 to 3, follow this tutorial: [Capistrano 3 Upgrade Guide](https://semaphorec...
FactoryBot: Passing attributes to associated records using transient attributes
FactoryBot.define do
factory :parent do
transient do
child_name nil
child_allowed_to_drive false
end
child do
association(:child, name: child_name, allowed_to_drive: child_allowed_to_drive)
end
end
factory :child do
name 'Child'
allowed_to_drive false
end
end
# Usage
daughter = FactoryBot.create(:parent, child_name: 'Lisa').child
daughter.name # => 'Lisa'
daughter.allowed_to_drive? # => false
son = FactoryBot.create(:parent, child_name: 'Benedikt', child_allowed_to_drive: tr...
Savon testing: How to expect any message
When using Savon to connect a SOAP API, you may want to use Savon::SpecHelper
to mock requests in your tests as described in their documentation.
When sending a message body, the savon
mock object requires a message to be set, like this:
savon.expects(:action_name).with(message: { user_id: 123 }).returns('<some xml>')
If you want to stub only the returned XML and do not care about request arguments, you can not omit with
as Savon's helper will complain:
savo...
Customizable date (and time) picker: Rome
Datetime picker that offers:
- simple UI without a specific framework
- several of customization options
- allows custom date/time validations
Localization happens via moment.js (which is a Dependency anyway).
However, you won't be happy trying to customize it too much:
- It does not support full custom templates, you can only set classes of its elements. If you require extra containers, you are out of luck.
- It offers only a few events, and you can not distinguish if users pick a date, switch to another month, or click outside of the p...
Testing setTimeout and setInterval with Jasmine
Jasmine has a jasmine.clock()
helper that you can use to travel through time and trigger setTimeout
and setInterval
callbacks:
beforeEach(function() {
timerCallback = jasmine.createSpy("timerCallback");
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it("causes a timeout to be called", function() {
setTimeout(function() {
timerCallback();
}, 100);
expect(timerCallba...
ActiveRecord: How to use ActiveRecord standalone within a Ruby script
Re-creating a complex ActiveRecord scenario quickly without setting up a full-blown Rails app can come in handy e.g. when trying to replicate a presumed bug in ActiveRecord with a small script.
# Based on http://www.jonathanleighton.com/articles/2011/awesome-active-record-bug-reports/
# Run this script with `$ ruby my_script.rb`
require 'sqlite3'
require 'active_record'
# Use `binding.pry` anywhere in this script for easy debugging
require 'pry'
# Connect to an in-memory sqlite3 database
ActiveRecord::Base.establish_connection(
ad...
PostgreSQL vs MySQL: How to UPDATE using a JOIN
When you want to UPDATE
a table with information from an associated table, you can JOIN
the associated table into the statement.
Example
Let's say you have a database schema where an Employee belongs_to :department
:
+-----------------+
| Employee | +------------+
|-----------------| n 1 | Department |
| email |-------------------|------------+
| department_id | | name |
+-----------------+ +------------+
Because of perfo...
PSA: "index: true" in Rails migrations does not work as you'd expect
Several Rails migration methods accept index: true
as an option to create an index. In some cases (like #add_column
), this option is silently discarded. Know what you are doing, or use #add_index
instead.
Example
Consider the following migration.
class CreateExamples < ActiveRecord::Migration
def change
create_table :examples do |t|
t.references :category, index: true
t.boolean :positive, index: true
t.integer :number_of_participants, index: true
end
add_reference :examples, :user, index: tr...