Execution of shell code in Ruby scripts
Deprecated ways to execute shell code in Ruby
This is just a reference for legacy code. For new code, always use capture3.
%x{ } or backticks – quick and easy
Returns the standard output of running the given command in a subshell. This is an alias for `...`, and you can use string interpolation.
Example:
name = 'ls'
result = `which #{name}`
It does not escape anything you inject in the string, so be aware of possible security vulnerabilities...
How to diff two strings in Ruby
When you need to use diff in either some Ruby code or your Rails app, use the differ gem.
puts Differ.diff "foo", "boo"
# => {"boo" >> "foo"}
Usage
There are several variants available, all using the base method diff(to, from, separator = "\n").
You have diff_by_line, diff_by_word, diff_by_char and may of course use your own separator:
puts Differ.diff 'Hauptsatz, und mein Nebensatz.', 'Hauptsatz, und dein Nebensatz.', ','
# => Hauptsatz,{" und dein Nebensatz." >> " un...
Using Bumbler to Reduce Runtime Dependencies - The Lean Software Boutique
Tool to show you which gems are slow to load:
➜ git:(master) ✗ bundle exec bumbler
[################################################# ]
(49/65) travis-lint...
Slow requires:
110.21 render_anywhere
147.33 nokogiri
173.83 haml
179.62 sass-rails
205.04 delayed_job_active_record
286.76 rails
289.36 mail
291.98 capistrano
326.05 delayed_job
414.27 pry
852.13 salesforce_bulk_api
When using "render :text", set a content type
When your Rails controller action responds with only a simple text, render text: 'Hello' may not be what you want. You should not even use it on Rails 4.1+ any more.
By default, a "text" response from a Rails controller will still be a sent as text/html:
render text: 'Hello'
response.body # => "Hello"
response.content_type # => "text/html"
While this may not be too relevant for a Browser client, the response's content type is simply wrong if you want to send a plain-text response, and can cause trouble. \
For example, con...
Best practices for REST API design
A rough guide how to implement a REST API.
The discussion here includes some interesting points as well:
- Timestamps: ISO8601 format ("2021-02-22T20:34:53.686Z")
- Google API guideline: https://google.aip.dev/
- Numbers: String vs. Number
The JSON number type is not a double. It's just a number of arbitrary size and precision in integer/decimal/E format that can be parsed as whatever the parser finds fitting.
- Pagination: Limit + Offset vs. Object ID / Pointer vs. System-Version...
rspec_candy 0.4.0 released
- Now supports RSpec 3 and Rails 4
- Drops support for
state_machine, which has some issues with Rails 4 and isn't actively maintained
Webpack: Automatically generating an icon font from .svg files
Over the years we have tried several solution to have vector icons in our applications. There are many ways to achieve this, from SVGs inlined into the HTML, SVGs inlined in CSS, JavaScript-based solutions, to icon fonts.
Out of all these options, the tried and true icon font seems to have the most advantages, since
- icon fonts are supported everywhere
- they perform well and require no JavaScript at all
- their icons align nicely with text
- their icons automatically inherit color and size of the surrounding text
The big issue used to b...
Ruby < 2.4: Downcasing or upcasing umlauts
Using .downcase or .upcase on strings containing umlauts does not work as expected in Ruby versions before 2.4. It leaves the umlauts unchanged:
"Über".downcase
=> "Über"
"Ärger".downcase
=> "Ärger"
The very same applies for french accents (Thanks Guillaume!):
"Être ou ne pas être, telle est la question".downcase
=> "Être ou ne pas être, telle est la question"
Obviously, this leads to problems when comparing strings:
"Über".downcase == "über"
=> false
In Rails you can use ActiveSupports' [multib...
Understanding SQL compatibility modes in MySQL and MariaDB
MySQL and MariaDB have an SQL mode setting which changes how MySQL behaves.
The SQL mode value is comprised of multiple flags like "STRICT_TRANS_TABLES, NO_ZERO_IN_DATE". Each flag activates or disables a particular behavior.
The default SQL mode varies widly between versions of MySQL and MariaDB. In general, more recent versions of MySQL and MariaDB have stricter settings than older versions, and MySQL has stricter settings than the more liberal MariaDB.
If your app explodes ...
How to: Validate dynamic attributes / JSON in ActiveRecord
PostgreSQL and ActiveRecord have a good support for storing dynamic attributes (hashes) in columns of type JSONB. But sometimes you are missing some kind of validation or lookup possibility (with plain attributes you can use Active Record's built-in validations and have your schema.rb).
One approach about being more strict with dynamic attributes is to use JSON Schema validations. Here is an example, where a project has the dynamic attributes analytic_stats, that we can use to store analytics from an external measurement tool.
- A g...
VCR and the webdrivers gem
If you're using the webdrivers gem and VCR together, depending on your configuration, VCR will yell at you regulary.
The webdrivers gem tries to update your webdrivers on your local machine. To do so, it checks the internet for newer versions, firing an HTTP-request to e.g. https://chromedriver.storage.googleapis.com
You can "fix" this in multiple ways:
-
Update your drivers on your machine with
RAILS_ENV=test rake webdrivers:chromedriver:update -
Ignore the driver update-URL in your ...
Debugging flickering VCR tests
We often use VCR to stub external APIs. Unfortunately VCR can have problems matching requests to recorded cassettes, and these issues are often hard to debug.
VCR's error messages mostly look like this and are not very helpful:
An HTTP request has been made that VCR does not know how to handle:
POST http://another-site.de:9605/json/index
VCR fails if the request does not exactly look like the request it has recorded. If the request is d...
Performance analysis of MySQL's FULLTEXT indexes and LIKE queries for full text search
When searching for text in a MySQL table, you have two choices:
- The LIKE operator
- FULLTEXT indexes (which currently only work on MyISAM tables, but will one day work on InnoDB tables. The workaround right now is to extract your search text to a separate MyISAM table, so your main table can remain InnoDB.)
I always wondered how those two methods would scale as the number of records incr...
How to make Rational#to_s return strings without denominator 1 again
The way Rational#to_s works on Ruby has changed from Ruby 1.9 on. Here is how to get the old behavior back.
You may want this for things where Rationals are being used, like when subtracting Date objects from one another.
What's happening?
Converting a Rational to a String usually does something like this:
1.8.7 > Rational(2, 3).to_s
=> "2/3"
1.9.3 > Rational(2, 3).to_s
=> "2/3"
2.0.0 > Rational(2, 3).to_s
=> "2/3"
However, when you have a Rational that simplifies to an integer, you will only get a St...
Capturing signatures on a touch device
If you need to capture signatures on an IPad or similar device, you can use Thomas J Bradley's excellent Signature Pad plugin for jQuery.
To implement, just follow the steps on the Github page.
The form
If you have a model Signature with name: string, signature: text, you can use it with regular rails form like this:
- form_for @signature, :html => { :class => 'signature_form' } do |form|
%dl
%dt
= form...
Careful with '||=' - it's not 'memoize'
When you do something like this in your code:
def var_value
@var ||= some_expensive_calculation
end
Be aware that it will run some_expensive_calculation every time you call var_value if some_expensive_calculation returns nil.
This illustrates the problem:
def some_expensive_calculation
puts "i am off shopping bits!"
@some_expensive_calculations_result
end
When you set @some_expensive_calculations_result to nil, ||= runs some_expensive_calculation every time....
Unpoly: Testing values for presence or blankness
In Ruby on Rails, all objects have a useful blank? method. It returns true for nil but also for empty strings or empty arrays. There is also a universal method present? which returns true for all values that are not blank?.
In JavaScript you need to roll your own implementation of blank? and present?.
If your application uses [Unpoly](...
Find geocoded records that are close to a location (radius search)
When you have objects in your database that hold latitude and longitude and you want to find others that are close to given coordinates you can use the Graticule gem.
Graticule
Graticule offers several methods to compute the distance between two geo-dated objects but fetching records from the database that are within a given radius of a location is a bit trickier:
def close_destinations(latitude, longitude)
distance_sql = Graticule::Distance::Spherical.to_sql(:latitude => l...
There is no real performance difference between "def" and "define_method"
You can define methods using def or define_method. In the real world, there is no performance difference.
define_method is most often used in metaprogramming, like so:
define_method :"#{attribute_name}_for_realsies?" do
do_things
end
Methods defined via define_method are usually believed to have worse performance than those defined via def.
Hence, developers sometimes prefer using class_eval to define methods using def, like this:
class_eval "def #{attribute_name}_for_realsies?; do_things; end"
You can be...
UI Sortable on table rows with dynamic height
UI sortable helps reordering items with drag 'n drop. It works quite fine.
Proven configuration for sorting table rows
When invoking the plugin, you may pass several options. This set is working fine with table rows:
$tbody.sortable # Invoke on TBODY when ordering tables
axis: 'y' # Restrict drag direction to "vertically"
cancel: 'tr:first-child:last-child, input' # Disable sorting a single tr to prevent jumpy table headers
containment: 'parent' # Only drag within this container
placehol...
Manually uploading files via AJAX
To upload a file via AJAX (e.g. from an <input type='file'>) you need to wrap your params in a FormData object.
You can initialize a FormData using the contents of a form:
var form = document.querySelector('form.my-form') // Find the <form> element
var formData = new FormData(form); // Wrap form contents
Or you can construct it manually, param by param:
var fileInput = document.querySelector('form input[type=file]');
var attachment = fileInput.files[0];
var f...
Putting static content on Cloudfront
We recently decided to put static content for HouseTrip.com to Amazon Cloudfront for a faster user experience. This happens fully automatically on deploy and is transparent in development. Together with a heavy use of sprites this sped up page load time quite nicely.
These are a couple of the problems you need to solve in order to do this:
- There is no good way to invalidate Cloudfront cached assets, and Cloudfront will ignor...
How to: Use Ace editor in a Webpack project
The Ace editor is a great enhancement when you want users to supply some kind of code (HTML, JavaScript, Ruby, etc).
It offers syntax highlighting and some neat features like auto-indenting.
For Webpack 3+
Integrate as described in the documentation. For example load ace Editor like this:
function loadAceEditor() {
return import(/* webpackChunkName: "ace" */ 'ace-builds/src-noconflict/ace').then(() => {
return import(/* webpackChunkName: "ace" */ 'ace-builds/webpack-r...
Cronjobs: "Craken" is dead, long live "Whenever"
Our old solution for cronjobs, the "craken" plugin, is no longer maintained and does not work on Rails 3.2+.
We will instead use the whenever gem.
"Whenever" works just like "craken", by putting your rake tasks into the server's cron table. Everything seems to work just like we need it.
Installation for new projects
-
Add "whenever" to your
Gemfile:group :deploy do gem 'whenever', require: false end -
Add it to your
config/deploy.rb:
...