How to deal with 'parent id missing' error in nested forms
tl;dr
- Use form models to handle this problem
- Or soften the validation to
validates_presence_of :parent
Usually you would validate presence of parent object id, like in this example:
class Parent < ActiveRecord::Base
has_many :nested, :inverse_of => :parent
accepts_nested_attributes_for :nested
end
class Nested < ActiveRecord::Base
belongs_to :parent
validates_presence_of :parent_id # <-
end
With the parent already persisted creating nesteds still works fine.
But one will encounter a *'parent id missing' er...
How to silence thin boot messages
Each time thin boots, it prints a boot message :
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on localhost:36309, CTRL+C to stop
If you are running parallel tests with thin, this will clutter you output. Disable thin logging with these lines:
# e.g. in features/support/thin.rb
require 'thin'
Thin::Logging.silent = true
Note that this disables all logging in tests. Instead, you also might set a different logger with `Thin::Loggi...
How to split config/routes.rb in Rails 4
A word of caution
There should rarely be a reason for you to split up config/routes.rb. If you need to, probably your whole application should be split up.
Split it anyway
Rails::Engine looks at config.paths['config/routes.rb'] and registers its value with app.routes_reloader. This means you could put routing files anywhere and then require them. However, I recommend to put any routing files into config/routes/:
# config/routes/example.rb
Rails.application.routes.draw do
resources :example
end
After creating y...
marco-polo improves your Rails console prompt
MarcoPolo shows your app name and environment in your console prompt so you don't accidentally break production
Officially supporting IRB (standard rails console) and pry (via pry-rails gem).
Example:
$ rails console
Loading development environment (Rails 4.2.1)
agencyapp(dev)>
List of Helpful RubyMine Shortcuts
Navigation
CTRL + SHIFT + ALT + N-
Search for any symbol in your application, like CSS classes, Ruby classes, methods, helpers etc.
CTRL + SHIFT + N-
Search for filename in your application (also dependencies)
CTRL + E-
Open a list of recently opened files
ALT + POS1-
Open a the navigation bar as a context menu. Allows you to quickly navigate between files.
CTRL + G-
Go to line
Actions
:...
PostgreSQL: Be careful when creating records with specific ids
In tests, it is sometimes useful to create records with specific ids. On PostgreSQL this can cause problems:
Usually, PostgreSQL uses an "autoincrement" sequences to provide sequential ids for new database rows. However, these sequences will not increment if you insert a record "by hand". This will cause an error:
record = Record.create!
record.id # => 100, next automatic id will be 101
Record.create!(id: record.id + 1) # okay, but next automatic id will still be 101
Record.create! ...
Jasmine: Testing complex types for equality
Jasmine comes with two matchers that test for equality. The first is toBe:
expect(first).toBe(second)
toBe passes when first === second. Unfortunately this is useless for non-primitive values because JavaScript is a horrible language.
However, Jasmine comes with another matcher toEqual:
expect(first).toEqual(second)
This matcher behaves as a human would expect for types like the following:
- Arrays
- Objects
- Nested array/object constructs
- Regular expressions...
Mocking time in Jasmine specs
The easiest way to freeze or travel through time in a Jasmine spec is to use the built-in jasmine.clock().
- After
jasmine.clock().install()you can use it to controlsetTimeoutandsetInterval. - Using
jasmine.clock().mockDate()you can mocknew Date()(which returns the current time in Javascript)
While you can use SinonJS Fake timers, using the built-in Jasmine clock will save you an extra dependency.
Pierce through Javascript closures and access private symbols
If you are writing any amount of Javascript, you are probably using closures to hide local state, e.g. to have private methods.
In tests you may find it necessary to inspect a variable that is hidden behind a closure, or to mock a private method using Jasmine spies.
You can use the attached Knife helper to punch a hole into your closure, through which you can read, write or mock local symbols:
klass = (->
privateVariable = 0
privateMethod = ->
...
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 fix: iPad does not trigger click event on some elements
iPads will not trigger click events for all elements. You can fix that, but you don't want to know why.
Example: Capturing clicks on table rows (use case would be clicking the 1st link inside it for better UI). Consider this setup:
<table>
<tr>
<td>hello</td>
</tr>
</table>
^
$(document).on('click', 'tr', function () {
alert('row clicked')
});
While this works on a desktop browser, clicking the row/cell on an iPad will just do nothing.
Turns out, the iPad will only trigger such events for ...
Parallel Rspec with RTeX
Running projects parallel makes some trouble with PDF generation. Use geordi rspec spec to force sequential tests for the whole application or failed specs only.
geordi rspec
RTeX::Document::GenerationError in '...'
Could not find result PDF document.pdf after generation.
Check .../document.log
The document will show you, that RTeX tries to generate a PDF document out of a HTML file, which won't work.
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...
Material Design Lite
CSS (+ some Javascript) framework, implementing Google's material design for static web pages.
Can be used for plain websites without requiring a full blown Javascript framework, unlike the (also excellent) Polymer paper elements, or Angular material.
Prelimiary impression:
I would recommend against using it at this stage, for a couple of reasons:
- It is much less complete than you might expect from a CSS framewor...
A non-weird replacement for grouped_collection_select
Rails comes with grouped_collection_select that appears to be useful, but isn't.
As an alternative, consider the flat_grouped_collection_select found below. It takes a third argument that extracts the group from each element in the collection:
= form.flat_grouped_collection_select :user_id, users, :department, :id, :full_name
Here is the monkey-patch:
class ActionView::Helpers::FormBuilder
def flat_grouped_collection_selec...
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...
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 ...
Reverse-proxying web applications with Apache 2.4+
Note: Making a reverse proxy with nginx is much more straightforward.
A reverse proxy is a "man in the middle" server that tunnels requests to another server. You can use for things like:
- Expose a local service that you cannot directly reach over the internet
- "Change" the domain or path of a web application by rewriting them on the fly
- Instantly change servers that respond to a name or ...
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
localStoragewhich is shared across all tabs).
MDN says:
The
sessionStorageobject is most useful for hanging on to temporary data that should be saved and restored if the browser is accidentally refreshed
Demo
Example usage:
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...
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...
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...
Why you see a GET "/__identify__" request in Capybara tests
You might wonder about this request in your test.log:
Started GET "/__identify__" for 127.0.0.1 at 2015-04-29 18:00:02 +0100
This is what happens: For drivers like Selenium, Capybara will boot up a Thin or Webrick server in a separate thread. It then makes a GET request to /__identify__ to see if the server is ready to accept requests.
Since you don't have a route that responds to /__identify, Capybara will wrap your Rails app in...
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...