Asset Pipeline Basics
The Rails asset pipeline improves delivery of application assets (javascripts, stylesheets, images, fonts). Here are some basic facts about its inner workings.
No magic
Manifests are the handle on your assets:
app/assets/stylesheets/application.css # use via: stylesheet_link_tag 'application'
The asset pipeline only considers files you explicitly require within your manifest files. The most common directives used in manifests are require some/file
and require_tree some/directory
. Paths may be **relative to the current director...
Nested Spreewald patiently blocks are now patient
In Spreewald 1.10.4+, nested patiently
blocks are now patient.
Here is an example:
patiently do
outer_code
patiently do
inner_code
end
end
On spreewald 1.11.2+ the inner block will wait for the full configured wait time (by default 5 seconds). The outer patiently
block would now be out of time, but it will always be retried at least a second time. This behavior allows with_scope
to be patient, and it must be patient, as explained below.
In versions 1.10.4 - 1.11.1, inner blocks would keep giving the ou...
How to call overwritten methods of parent classes in Backbone.js
When you are working with Backbone models and inheritance, at some point you want to overwrite inherited methods but call the parent's implementation, too.
In JavaScript, there is no simple "super
" method like in Ruby -- so here is how to do it with Backbone.
Example
BaseClass = Backbone.Model.extend({
initialize: function(options) {
console.log(options.baseInfo);
}
});
MyClass = BaseClass.extend({
initialize: function(options) {
console.log(options.myInfo);
}
});
ne...
Bootstrap 4 is coming
What's new
- Moved from Less to Sass. Bootstrap now compiles faster than ever thanks to Libsass, and we join an increasingly large community of Sass developers.
-
Improved grid system. We’ve added a new grid tier to better target mobile devices and completely overhauled our semantic mixins.
Opt-in flexbox support is here. The future is now—switch a boolean variable and recompile your CSS to take advantage of a flexbox-based grid system and components. - Dropped wells, thumbnails, and panels for cards. Cards are a brand new co...
How to make Webpacker compile once for parallel tests, and only if necessary
Webpack is the future. We're using it in our latest Rails applications.
For tests, we want to compile assets like for production.
For parallel tests, we want to avoid 8 workers compiling the same files at the same time.
When assets did not change, we do not want to spend time compiling them.
Here is our solution for all that.
Its concept should work for all test suites.
Copy the following to config/initializers/webpacker_compile_once.rb
. It will patch Webpacker, but only for the test
environment:
# Avoid hardcoded asset host...
Testing focus/blur events with Cucumber
This is a problem when using Selenium with Firefox. We recommend using ChromeDriver for your Selenium tests.
This setup allows to test focus/blur events in Cucumber tests (using Selenium). For background information, see How to solve Selenium focus issues.
Cucumber step definition:
# This step is needed because in Selenium tests, blur events are not triggered
# when the browser has no focus.
When /^I unfocus the "(.*?)" field to trigger ja...
Force absolute URLs in views throughout a response
This is more tricky than it should be because url_for
, asset_path
, etc. all rely on different mechanisms.
Anyway, you can use the attached trait like this:
class ExampleController < ApplicationController
does 'host_enforcement', :for => 'some_action'
end
Short explanation:
-
asset_host
is used for links to stylesheets and javascripts -
asset_host
belongs toActionController::Base
-- changes are persistent and will not be reset after a request -
rewrite_options
is used by the..._path
methods in the views
^...
Spreewald: Content-Disposition not set when testing a download's filename
Precondition
- You are not using javascript tests
- The file is served from a public folder (not via controller)
Problem description
If you deliver files from a public folder it might be that the Content-Disposition
header is not set. That's why the following spreewald step might raise an error:
Then I should get a download with filename "..."
expected: /filename="some.pdf"$/
got: nil (using =~) (RSpec::Expectations::ExpectationNotMetError)
Solution
One solution...
Disable built-in dragging of text and images
Most browsers have built-in drag and drop support for different page elements like text and images. While this may be useful in most situations, it may become annoying in others. If you e.g. want to allow the user to scroll/move horizontally within a container by grabbing an item and moving the mouse, you will notice that nothing will move and you'll instead start dragging that element.
To disable this, add the following CSS to your content:
-webkit-user-drag: none
user-drag: none
-webkit-user-drag
is only fully supported in ...
Drag'n'drop in trees: I went to town
For my Gem Session project Holly I ran the Ironman of drag'n'drop implementations:
- Dragging in nested lists
- User-definable order of items
- Complicated item elements with super-custom CSS and other Javascript functionality
- Items that can be both leaves and containers of other items
- has_ancestry on the server side
Things I learned:
- Be ready to write a lot of CSS. You need to indicate what is being dragged, where it will be dropped, if it is dropped above, below o...
Rails 3: Make "link_to :remote => true" replace HTML elements with jQuery
In Rails 2, you could use link_to_remote ... :update => 'id'
to automatically replace the content of $('#id')
.
To do the same in Rails 3, include usual rails-ujs JavaScript, and put this into your application.js
:
$(function() {
$('[data-remote][data-replace]')
.data('type', 'html')
.live('ajax:success', function(event, data) {
var $this = $(this);
$($this.data('replace')).html(data);
$this.trigger('ajax:replaced');...
Clicking issues with chromedriver
ChromeDriver clicking works by simulating a mouse click in the middle of the element's first client rect (or bounding client rect if it doesn't have a first client rect). (Clicking issues)
So if you are trying to click on an element, chromedriver
tries to click at the position where it first finds that element.
This can lead to some problems and unexpected behavior like:
-
Element is not clickable at point ...
erros - Another element which is now located at...
Unpoly 2: Don't try to download files through AJAX requests
Rails has the handy controller method send_file which lets us download files easily. We can decide whether the file should be downloaded (disposition: 'attachment'
) or shown in the browser (disposition: 'inline'
). The default is disposition: 'attachment'
.
Downloading files will not work when you are calling the controller action from an AJAX request. The browser will try to render the file and insert it in the DOM, which is never what you want.
Unpoly 2
Unpoly (sin...
When does Webpacker compile?
Webpack builds can take a long time, so we only want to compile when needed.
This card shows what will cause Webpacker (the Rails/Webpack integration) to compile your assets.
When you run a dev server
While development it is recommended to boot a webpack dev server using bin/webpack-dev-server
.
The dev server compiles once when booted. When you access your page on localhost
before the initial compilation, the page may load without assets.
The ...
AngularJS directive to format a text with paragraphs and new lines
If you are using Angular and want something like Rails' simple_format
which HTML-formats a plain-text input into paragraphs and line breaks, this directive is for you.
Any HTML fragments inside that text will still be escaped properly.
Use it like this, where your text
attribute specifies something available in your current scope:
<simple-format text="email.message"></simple-format>
This is the directive, in CoffeeScript syntax:
@app.directive 'simpleFor...
Make jQuery.animate run smoother with almost no code changes
jQuery comes with .animate()
that lets you transition some CSS selectors:
function floatIn($element) {
$element.css({ 'opacity': 0, 'margin-top': 200px });
$element.animate({ 'opacity': 1, 'margin-top': 0 }, { duration: 500 });
}
The animation is implemented using setInterval
and Javascript. This works great, but it's not as smooth as a CSS transition.
Fortunately the animate
API can be mapped almo...
Cucumber: Wait until CKEditor is loaded
I had to deal with JavaScript Undefined Error
while accessing a specific CKEditor instance to fill in text.
Ensure everything is loaded with
patiently do
page.execute_script("return isCkeditorLoaded('#{selector}');").should be_true
end
Example
The fill in text
snippet for Cucumber:
When /^I fill in the "([^\"]+)" WYSIWYG editor with:$/ do |selector, html|
patiently do
page.execute_script("return isCkeditorLoaded('#{selector}');").should be_true
end
html.gsub!(/\n+/, "") # otherwise: unterminated string lit...
How to open a new tab with Selenium
Until recently, you could open a new tab via window.open
when using execute_script
in Selenium tests. It no longer works in Chrome (will show a "popup blocked" notification).
This is because browsers usually block window.open
unless the user interacted with an element for security reasons. I am not sure why it did work via Selenium before.
Here is an approach that will insert a link into the page, and have Selenium click it:
path = "/your/path/here"
id = "helper_#{SecureRandom.hex(8)}"
execute_script <<-JAVASCRIPT
...
Clean up application servers when deploying
Our development process makes us deploy very often. As the number of releases grows, junk clogs up the hard drive of our application servers:
- Old release code
- Old
tmp
folders with compiled view templates etc. - Precompiled assets (Javascripts, images...) that no longer exist. When using the asset pipeline, Capistrano will symlink the
public/assets
directory toshared/assets
. This is cool since we can still serve previous assets after a new release, in the window where browser caches might still have references to old assets. But i...
Pre-releasing a Ruby gem
When a Ruby version gem has a letter in its version number, it is considered a pre-release:
1.0.0.rc1
2.3.0.alpha2
3.0.0.beta3
4.0.0.pre.rc2
Even if a pre-release gem has the highest version number, it is never installed unless the user explictily requested the version:
gem install foobar --version="=2.3.0.alpha2"
Also bundle update
will never update a stable version to a pre-release version unless the user explicitly requests it in the Gemfile
:
gem 'foobar', '=2.3.0.alpha2'
A note on Semanti...
Looping through iterators in Coffeescript
Some modern Javascript APIs return iterators instead of arrays.
In plain Javascript you can loop through an iterator using
for...of
:
var iterator = ...;
for (var value of iterator) {
console.log(value);
}
While there is a for...of
construct in Coffeescript, it iterates through property/value pairs and **...
CSS: How to force background images to scale to the container, ignoring aspect ratio
You can scale background images in CSS to the container size using background-size
(Demo).
Commonly, we use contain
or cover
because we want to preserve the image's aspect ratio.
If you do not want to do that, simply provide scaling values for X and Y:
background-size: 100% 100%
(a simple 100%
would mean 100% auto
and respect the image's aspect ratio)
SVGs with a viewBox will force their aspect ratio
The above may not work for you when ...
How to build a fully custom TinyMCE 5 dialog
TinyMCE is a WYSIWYG editor which is quite customizable.
- Add a custom button to the tinyMCE toolbar and tell tinyMCE to open a dialog with the route to your dialog's view.
tinymce.init({
// ...
toolbar: 'myCustomButton',
setup: function(editor) {
editor.ui.registry.addButton('myCustom Button', {
...
Bookmarklet to generate a commit message with Pivotal Tracker story ID and title
For clarity and traceability, your commit messages should include the ID and title of the Pivotal Tracker story you're working on. For example:
[#12345] Add Google Maps to user profiles
Optional further commit messages in the body
Also see Howto: Write a proper git commit message
To quickly generate such commit messages, add a new link "Commit" to your bookmarks and use the following Javascript as the link URL:
javascript:(function() { ...