Middleman does not fingerprint asset paths by default
We're using Middleman for some static sites like our blog.
Despite being very similar to Rails, Middleman does not add a fingerprint hash to its asset paths by default. This means that when you write this:
<%= javascript_include_tag 'all.js' %>
... you always get the same path, regardless of the contents of all.js:
<script src='/javascripts/all.js'>
Because browsers tend to cache assets for a while, this means that users might not get your changes until their cac...
Rack dies when parsing large forms
- Rack has a limit for how many form parameters it will parse.
- This limit is 65536 by default.
- There is a bug in Rack that will incorrectly count the number of input fields in nested forms. In my case a form with 1326 input fields was enough to break the default limit.
- If Rack thinks your request is too large, the request will fail with a low-level Ruby message like Fix: "undefined method `bytesize' for #" or the standard Rails error box.
- You ...
ActiveModel::Errors inherits from hash and behaves unexpectedly
ActiveModel::Errors is used to handle validation errors on Rails objects. If you inspect an instance, it feels like a hash (to be more precise, it inherits from Hash):
errors = ActiveModel::Errors.new(Object.new)
=> {}
>>
?> errors.add(:base, "foo")
=> ["foo"]
>> errors.add(:base, "bar")
=> ["foo", "bar"]
>>
?> errors
=> {:base=>["foo", "bar"]}
If you need to hack anything with these errors, beware that it behaves in a special way. If you iterate over the errors it will decompose arrays.
For ...
Ruby 2.3 new features
Ruby 2.3.0 has been around since end of 2015. It brings some pretty nice new features! Make sure to read the linked post with its many examples!
Hash#fetch_values
Similar to Hash#fetch, but for multiple values. Raises KeyError when a key is missing.
attrs = User.last.attributes
attrs.fetch_values :name, :email
Hash#to_proc
Turns a Hash into a Proc that returns the corresponding value when called with a key. May be useful with enumerators like #map:
attrs.to_proc.call(:name)
attrs.keys.grep(/name/).map &attrs...
Take care when merging with params
Be careful when using params.merge as params is a HashWithIndifferentAccess.
Why?
Usually this should not be an issue but it turns crazy if you try to include associated models deeper than 1 level:
options = params.merge(:include => { :user => :avatar })
Post.paginate options
When inspecting the merged params you will get something like this:
{ :include=> { "user" => :avatar }, :page => 23 }
Here the :user symbol in the hash of inclusions turned into a "user"...
Delete all MySQL records while keeping the database schema
You will occasionally need to clean out your database while keeping the schema intact, e.g. when someone inserted data in a migration or when you had to kill -9 a frozen test process.
Old Capybara versions already have the Database Cleaner gem as dependency. Otherwise add database_cleaner to your *Gemfile`. This lets you say this from the Rails console:
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.cl...
Use Memoizer instead of ActiveSupport::Memoizable
ActiveSupport::Memoizable will be removed from Rails and has a lot of strange caveats that will ruin your day.
Use the Memoizer gem instead. It works in all past and future Railses and has none of the annoying "features" of ActiveSupport::Memoizable. It just does memoization and does it well.
The syntax is similiar also:
class Foo
include M...
Unexpected behavior when changing both an association and its foreign key attribute in ActiveRecord
When you set both a record's association and that association's foreign key attribute, Rails does not realize you are talking about the same thing. The association change will win in the next save, even if the foreign key attribute was changed after the association.
As an example, assume you have these two models:
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
validates_presence_of :group_id
belongs_to :group
end
We will now load a User and change both its `g...
Split an array into columns
You know that you can collect an array as groups using in_groups or in_groups_of.
Maybe you want to fetch those values in "columns" where the first value lives in the first column, the second one in the second, etc. until it wraps, so that for example the fourth value is in the first of three columns.
Put the attached file into config/initializers/ to be able to say in_columns on any Array:
>> [1, 2, 3, 4, 5, 6, 7].in_columns(3)
=> [[1, 4, 7], [2, 5], [...
Capybara 2.0 has been released
The gem author Jonas Nicklas highlights in a Google Groups post that the release
- is not backwards compatible to 1.x versions of Capybara
- does not support Ruby 1.8.x anymore
- removes confusion with Rails' built in integration tests (you put capybara rspec integration tests into the
spec/feature/...folder) and the:typemetadata has been changed from:requestto:feature - throws exceptions when trying to interact with an element whose identifier is...
Marry Capybara with SSL-enabled applications
Capybara does not play nice with sites that have some actions protected by SSL, some not. A popular way to implement this in Rails is using the ssl_requirement plugin by DHH, which redirects a requests from HTTP to HTTPS if the requested action requires SSL and vice versa.
Capybara follows the redirect, but seems to forget the changed protocol for the next request. The only hack-free workaround right now is to use URLs in lieu of paths everywhere (links, form actions).
For a hackful fi...
Measuring sql query time of a piece of code using ActiveSupport::Notifications
ActiveSupport::Notifications provides an instrumentation API for Ruby. It is used throughout rails to publish instrumentation events that include information about each part of a request/response cycle.
Have a look at your application log file - yes, those are those events. The cool thing is that you can subscribe to those events.
There is also a convenience method that allows you to subscribe to those events only for the time of executing a block of code. Thus you can capture all sql queries that are triggered when executing your block....
Install a local Gemfile on a remote server
Call with the server's hostname (and user if you have no SSH agent), e.g.
install-gems-remotely my.server.com
# or without agent:
install-gems-remotely me@my.server.com
When you call it from a rails directory, it uploads your Gemfile, Gemfile.lock as well as the gemspecs of all vendored gems in to a temporary folder on the server and does a bundle install there.
If you need to install gems from anothere Gemfile, just do it like this:
BUNDLE_GEMFILE=Gemfile.something; install-gems-remotely my.server.com
This scri...
How to encode or decode quoted-printable strings
E-mails are usually encoded using Quoted Printable. Here is how to decode or encode such strings.
You probably know Quoted Printable from e-mail bodies appearing in Rails logs, where =s become =3Ds in URLs, or where long lines are split up and trailed by = after each split.
Decode Quoted Printable
Decoding such strings is actually quite simple using plain Ruby:
"foo=3Dbar".unpack('M')[0]
# => "foo=bar"
Note that unpack will return an array. Our result is the 1st item.
...
Multi-line Ruby block in Haml
There are several options, but most of them are impractical. The best way is to use the :ruby filter:
:ruby
puts javascript_include_tag(
'lib/jquery-1.6.1.min.js',
'lib/jquery-rails-ujs.js',
'lib/jquery-ui-1.8.13.custom.min.js',
'lib/jquery.ui.datepicker-de.js',
'lib/jquery-ui-timepicker-addon.min.js',
'lib/jquery.tools.min.js',
'application.js',
'google-maps.js',
:cache => true
)
...
randym/axlsx · GitHub
Axlsx is an incredible gem to generate "Office Open XML" spreadsheet files (XLSX). Does not break on large spreadsheets and supports a ton of features like graphs.
API looks mature and existing code is easy to migrate when coming from the spreadsheet gem.
The documentation of some methods is a bit out of date, but you'll find your way around the gem's code.
No support for reading files, however. :( If you want to open XLSX spreadsheets (for example to confirm your output in tests), you can use [roo](h...
How to remove RSpec "old syntax" deprecation warnings
RSpec 3.0 deprecates the :should way of writing specs for expecting things to happen.
However, if you have tests you cannot change (e.g. because they are inside a gem, spanning multiple versions of Rails and RSpec), you can explicitly allow the deprecated syntax.
Fix
Inside spec/spec_helpber.rb, set rspec-expectations’ and/or rspec-mocks’ syntax as following:
RSpec.configure do |config|
# ...
config.mock_with :rspec do |c|
c.syntax = [:should, :expect]
...
Fixing "A copy of Klass has been removed from the module tree but is still active"
This can happen during development when classes without automatic reloading are pointing to classes with automatic reloading. E.g. some class in lib is calling Model.static_method.
Workaround A
Stop referencing autoloaded classes from static files. If you can't, see workaround B and C.
Workaround B
Make sure the offending file (the one referencing the autoloaded class) is autoloaded, too. You may do this:
# config/application.rb
config.paths.add 'offending/file/parent/directory', eager_load: true
Workaroun...
String#indent: Know your definitions!
String#indent is not a standard Ruby method. When you use it, be sure to know where this method comes from. Many Gems shamelessly define this method for internal usage, and you'll never know when it may be removed (since it's usually not part of the Gem's API).
Unless you're using Rails 4 (which brings String#indent in ActiveSupport), you'll be best of defining it yourself. This card has it for you.
Gems that define String#indent (incomplete)
----------------------------...
Fix Capistrano warnings: Missing public directories
I got these warnings while deploying a Rails 3.2 app with asset pipeline enabled:
*** [err :: host.tld] find: `/opt/www/hollyapp.com/releases/20120503115342/public/images': No such file or directory
*** [err :: host.tld] find: `/opt/www/hollyapp.com/releases/20120503115342/public/stylesheets': No such file or directory
*** [err :: host.tld] find: `/opt/www/hollyapp.com/releases/20120503115342/public/javascripts': No such file or directory
Folders like public/javascripts might not exist if you're using the asset pipeline (...
Machinist's #make breaks on has_many associations when defining method `empty?`
Observed on Rails 2.3 and machinist 1.0.6
Like the title says, when you define the method empty? like in the following example, you may not longer use collection.make.
class Book
has_many :pages
def empty?
pages.empty?
end
end
Assuming
b1 = Book.find(1)
b2 = Book.find(2)
instead of expected
b1.pages.make #=> #<Page id: 1, book_id: 1>
b2.pages.make #=> #<Page id: 2, book_id: 2>
you'll get
b1.pages.make #=> #<Page id: 1, book_id: 3>
b2.pages.make #=> #<Page id: 2,...
MySQL 5.6 slightly changes DECIMAL data type
About
A MySQL DECIMAL column is used when it is important to preserve exact precision. It takes two parameters, where precision is the total number of digits and scale the number of digits to the right of the decimal point. A DECIMAL(6,2) column may store numbers up to 9,999.99.
In Rails, a decimal column definition looks like this: t.decimal :amount, :precision => 6, :scale => 2.
Issue
MySQL prior to 5.6 stored leading zeros (0003.1) and +/- characters (+2.1) within the column. However, **it would permit storing ...
Alternative to url_for's deprecated :overwrite_params option
If you have the following deprecation warning after upgrading to rails >= 2.3.10
DEPRECATION WARNING: The :overwrite_params option is deprecated. Specify all the necessary parameters instead.
that is for example caused by
url_for( :overwrite_params => { :order => 'name', :dir => 'asc' } )
you can fix this by using params.merge {:my_param_to_overwrite => 'foo' }.
To fix the example above the code could look like:
url_for( params.merge { :order => 'name...
Mysterious "margin" below an image
Consider the following HTML & CSS:
<div><img src='http://makandra.com/images/makandra-ruby-on-rails.png' /></div>
^
img {
background-color: red;
}
div {
border: 1px solid black;
}
This will leave a margin of about 5px between the lower edge of the image and the containing div, although there are no paddings or margins set, and there's no whitespace. The reason is, the image will vertically align baseline, and the space below the image is just kept for descenders (the part of letters below the basel...