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
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:
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...
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...
A solid and unobtrusive plugin for form field placeholders
jquery-placeholder
is a simple jQuery plugin that enables form placeholders in browsers that do not support them natively, i.e. IE < 10.
Properties
- Works in IE6.
- Automatically checks whether the browser natively supports the HTML5 placeholder attribute for input and textarea elements. If this is the case, the plugin won’t do anything. If @placeholder is only supported for input elements, the plugin will leave those alone and apply to textareas exclusively. (This is the case for Safari 4, Opera 11.00, and possibly other browsers.)
...
Ruby bug: Symbolized Strings Break Keyword Arguments in Ruby 2.2
TL;DR Under certain circumstances, dynamically defined symbols may break keyword arguments in Ruby 2.2. This was fixed in Ruby 2.2.3 and 2.3.
Specifically, when …
- there is a method with several keyword arguments and a double-splat argument (e.g.
def m(foo: 'bar, option: 'will be lost', **further_options)
) - there is a dynamically created
Symbol
(e.g.'culprit'.to_sym
) that is created before the method is parsed - the method gets called with both the
option
and aculprit
keyword argument
… then the `optio...
What Ruby’s ||= (Double Pipe / Or Equals) Really Does
It is a common misunderstanding that all [op]=
-operators work the same way, but actually they don't.
||=
and &&=
Those are special cases, because the assignment will only happen if the first variable passes the check (false
or nil
for ||
and true
for &&
).
a ||= b # => a || (a = b)
a &&= b # => a && (a = b)
But still, if reading a
has any side effects, they will take place regardless of to what a
resolves.
Other [op]=
Assignment will always take place, no matter the value of a
.
Using mime types with send_file
When using send_file
(for example for attachments of any kind), make sure your application knows the correct mime types so that all browsers can handle the files. It is much more convenient for users if they can decide to open a file directly instead of having to save it first.
For Rails >= 3.2
Simply put your mime types in config/initializers/mime_types.rb
. send_file
will take care of everything else.
For Rails < 3.2
Put your mime types in config/initializers/mime_types.rb
. Additionally, tell send_file
to use them (for ex...
RSpec: Tagging examples and example groups
In RSpec you can tag examples or example groups with any tags you like simply by saying
describe ReportCreator, slow: true do
# ..
end
describe ReportCreator do
it 'generates reports', slow: true do
# ...
end
end
You can then only run examples with these tags.
rspec --tag slow
rspec -t slow
# Using the parallel_tests gem
rake "parallel:spec[,,--tag slow]"
Or you can run all examples except the ones with a certain tag:
rspec --tag ~slow # note the ~
rspec -t ~slow
# Using the parallel_tests gem
r...
Ruby: How to grow or shrink an array to a given size
If you want to grow a Ruby Array, you might find out about #fill
but it is not really what you are looking for. [1]
For arrays of unknown size that you want to grow or shrink to a fixed size, you need to define something yourself. Like the following.
Array.class_eval do
def in_size(expected_size, fill_with = nil)
sized = self[0, expected_size]
sized << fill_with while sized.size < expected_size
sized
end
end
Use it like this:
>> [1, 2, 3].in_size(5)
=> [1, 2, 3, nil, nil]
...
Don't forget: Automatically remove join records on has_many :through associations
Bad
# Given the following models
class Image < ActiveRecord::Base
has_many :album_images
has_many :albums, through: :album_images
end
class Album < ActiveRecord::Base
has_many :album_images
has_many :images, through: :album_images
end
# Join model
class AlbumImage < ActiveRecord::Base
belongs_to :album
belongs_to :image
end
Destroying a record in this setup will only remove the record itself, and leave orphaned join records behind.
image = Image.last
image.destroy # removes only the `image` record,
...