Capistrano: exclude custom bundle groups for production deploy

Capistrano is by default configured to exclude the gems of the groups development and test when deploying to the stages production and staging. Whenever you create custom groups in your Gemfile, make sure to exclude these, if they should not be deployed to the servers. The gems of these groups might not be loaded by rails, however, the deployment process will take longer as the gems will be downloaded and installed to the server.

e.g. to exclude the groups cucumber and deploy, add the following to `config/deploy/production.rb...

Postgres: DISTINCT ON lets you select only one record per ordered attribute(s) for each group

  • To retrieve only unique combinations of the selected attributes: You can omit rows, where all selected columns are equal with the DISTINCT statement.
  • To retrieve the group wise maximum of certain columns: You can keep only one record for each group with the DISTINCT ON statement, to omit equal rows within each specified group.

Use case

You have a query where you want only one record for a set of specifically ordered attributes.

How to use?

Let's say we look at the example how to query only the latest post for each user:
...

Terminator setup for Procfile-based applications for more comfortable debugging

We use foreman to start all necessary processes for an application, which are declared in a Procfile. This is very convenient, but the outputs of all processes get merged together. Especially while debugging you might not want other processes to flood your screen with their log messages.

The following setup allows you to start Terminator in a split view with the Rails server running in the left pane and all remaining processes running via foreman in the right pane. It was heavily inspired by [this card](https://makandracards.com/makandr...

How to send HTTP requests using cURL

  • Reading a URL via GET:

    curl http://example.com/
    
  • Defining any HTTP method (like POST or PUT):

    curl http://example.com/users/1 -XPUT
    
  • Sending data with a request:

    curl http://example.com/users -d"first_name=Bruce&last_name=Wayne"
    

    If you use -d and do not set an HTTP request method it automatically defaults to POST.

  • Performing basic authentication:

    curl http://user:password@example.com/users/1
    
  • All together now:

    curl http://user:password@example.com/users/1 -XPUT -d"screen_name=batman"
    

...

How to reload a belongs_to association

To reload a single-item association in Rails 5+, call #reload_<association>:

post.reload_author

In older Railses you can say

post.author(true)

Simple database lock for MySQL

Note: For PostgreSQL you should use advisory locks. For MySQL we still recommend the solution in this card.


If you need to synchronize multiple rails processes, you need some shared resource that can be used as a mutex. One option is to simply use your existing (MySQL) database.

The attached code provides a database-based model level mutex for MySQL. You use it by simply calling

Lock.acquire('string to synchronize on') do
  # non-th...

Generate a strong secret from the shell

A good tool to generate strong passwords and secrets is "apg". You can get it with

sudo apt-get install apg

To create a strong secret for sessions, hashed Paperclip paths, etc. say

apg -m128 -a1 -E\'\"

Arguments explained:

  • The -m parameter defines the secret length
  • -a1 makes apg choose from all 7-bit ASCII characters instead of just the alphabet
  • -E\'\" excludes quote characters so you can easily paste the secret into a Ru...

Haml Whitespace Preservation (or: Fixing Textarea Indentation in Haml)

Haml renders HTML with indentation reflecting the nesting level of elements. When it comes to white-space preserving content, this may become a problem: the content will be rendered including the HTML indentation.

Problematic: Preserved Indentation

.nest
  %span Reference
  %pre
    = content
<div class="nest">
    <span>Reference</span>
    <pre>
        Hello
        World
    </pre>
</div>

Image

Better: Without Extra Indentation

Render with tilde ~ instead of equal...

ActiveRecord: Query Attributes

tl;dr
You can use attribute? as shorthanded version of attribute.present?, except for numeric attributes and associations.

Technical Details

attribute? is generated for all attributes and not only for boolean attributes.

These methods are using #query_attribute under the hood. For more details you can see ActiveRecord::AttributeMethods::Query.

In most circumstances query_attribute is working like attribute.present?. If your attribute is responding to :zero? then you have to be aware that `query_attri...

Installing old versions of mysql2 on Ubuntu 20.04+

On some of our older projects, we use the mysql2 gem. Unfortunately, versions 0.2.x (required for Rails 2.3) and versions 0.3.x (required for Rails 3.2) can no longer be installed on Ubuntu 20.04. Trying this either leads to errors when compiling the native extension, or a segfaults when using it.

For Rails 4.2, mysql2 version 0.4.10 seems to work okay. If you still have issues, upgrade to 0.5.x, which should be compatible.

To install it, you have to switch the mysql2 gem to our fork. In your Gemfile, ...

Fix for mysql2 error "Incorrect MySQL client library version! This gem was compiled for x.x.x but the client library is y.y.y."

This should be fixed in the latest LTS-branches of our mysql2 fork, 0.2.x-lts and 0.3.x-lts.

Use

gem 'mysql2', git: 'https://github.com/makandra/mysql2', branch: '0.2.x-lts' # for Rails 2.x
gem 'mysql2', git: 'https://github.com/makandra/mysql2', branch: '0.3.x-lts' # for Rails 3.x

in your Gemfile, and do a

bundle update mysql2

Background

mysql2 used to check that the client library used at runtime actually matches the one it was compiled against. However, at least on Ubunt...

How to enable SSL in development with Passenger standalone

Here is how to start your Rails application to accept both HTTP and HTTPS in development.

  1. gem install passenger

  2. Create a self-signed SSL certificate. Store the generated files in config/passenger-standalone-ssl.

  3. Create a Passengerfile.json at the project root with this content (or save the attached file):

    {
      "ssl": true,
      "ssl_port": 3001,
      "ssl_certificate": "config/passenger-standalone-ssl/server.crt",
    

    ...

Bash: How to grep logs for a pattern and expand it to the full request

Example

I, [2024-01-21T06:22:17.484221 #2698200]  INFO -- : [4cdad7a4-8617-4bc9-84e9-c40364eea2e4] test
I, [2024-01-21T06:22:17.484221 #2698200]  INFO -- : [4cdad7a4-8617-4bc9-84e9-c40364eea2e4] more
I, [2024-01-21T06:22:17.484221 #2698200]  INFO -- : [6e047fb3-05df-4df7-808e-efa9fcd05f87] test
I, [2024-01-21T06:22:17.484221 #2698200]  INFO -- : [6e047fb3-05df-4df7-808e-efa9fcd05f87] more
I, [2024-01-21T06:22:17.484221 #2698200]  INFO -- : [53a240c1-489e-4936-bbeb-d6f77284cf38] nope
I, [2024-01-21T06:22:17.484221 #2698200]  INFO -- ...

CarrierWave: How to remove GIF animation

When accepting GIF images, you will also accept animated GIFs. Resizing them can be a time-consuming task and will block a Rails worker until the image is processed.

Save yourself that trouble, and simply tell ImageMagick to drop any frames but the first one.

Add the following to your uploader class:

process :remove_animation

private

def remove_animation
  if content_type == 'image/gif'
    manipulate! { |image| image.collapse! }
  end
end

You may also define that process for specific versions only (e.g. only for thum...

Inspecting the page content in a Cucumber session

When you need to see the content of a page (i.e. not all the HTML but the relevant text body)

  • you can do pp (html_content)
    • pp will format the html String human readable pretty printed
  • where html_content can be replaced by one of the following commands:

Rails

body or response.body

Capybara:

  • page.driver.html.content
  • page.body

Webrat:

Nokogiri::HTML(response.body).content

The returned strings can be cleaned up by calling .gsub(/^\s*$/, '').squeeze("\n") on them.\
Although this may be useful for d...

How to silence Puma for your feature tests

When RSpecs runs the first feature spec, you may see log output like this:

Capybara starting Puma...
* Version 6.5.0, codename: Sky's Version
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:39949

You can disable this behavior by tweaking Capybara's Puma server in your spec/support/capybara.rb:

Capybara.server = :puma, { Silent: true }

Note

You don't need to configure this if you're using system tests with modern versions of Rails. They do [exactly the same](https://github.com/rails/rails/blob/ma...

Writing strings as Carrierwave uploads

When you have string contents (e.g. a generated binary stream, or data from a remote source) that you want to store as a file using Carrierwave, here is a simple solution.

While you could write your string to a file and pass that file to Carrierwave, why even bother? You already have your string (or stream).
However, a plain StringIO object will not work for Carrierwave's ActiveRecord integration:

>> Attachment.create!(file: StringIO.new(contents))
TypeError: no implicit conversion of nil into String

This is because Carrierwav...

When Date.today and Date.tomorrow return the same day...

... you probably have a time zone issue.

When you get

Timecop.travel(Date.parse("2011-11-11 00:00") do
  Time.current  # Thu, 10 Nov 2011 23:00:01 UTC +00:00
  Time.now      # Fri Nov 11 00:00:02 +0100 2011
  Date.today    # Fri, 11 Nov 2011
  Date.tomorrow # Fri, 11 Nov 2011
end

you probably haven't defined a zime zone yet.

So might fix this by adding the following lines to your application.rb:

class Application < Rails::Application
  config.time_zone = 'Berlin' # or whatever your time zone
end

It se...

Ruby: Debugging a method's source location and code

Access the Method object

Dead simple: Get the method object and ask for its owner:

"foo".method(:upcase)
# =>  #<Method: String#upcase> 

"foo".method(:upcase).owner
# => String

Look up a method's source location

Ruby 1.9 adds a method Method#source_location that returns file and line number where that method is defined.

class Example; def method() end; end
# => nil

Example.new.method(:method).source_location
# => ["(irb)", 11] 
 
"foo".method(:upcase).source_location
# => nil # String#upcase is a native method...

Specify Gemfile for bundle

Bundler allows you to specify the name of the Gemfile you want to bundle with the BUNDLE_GEMFILE environment variable.

BUNDLE_GEMFILE=Gemfile.rails.7.2 bundle

By default, bundler will look for a file called Gemfile in your project, but there may be cases where you want to have multiple Gemfiles in your project, which cannot all be named Gemfile. Let's say for example, you maintain a gem and want to run automated tests against multiple rails versions. When you need to bundle one of your secondary Gemfiles, the solution above ...

ActiveStorage: How to add a new preprocessed named version

Given there is a user with an attachable avatar:

class User < ApplicationRecord
  has_one_attached :avatar
end

If you want to add a preprocessed version follow these steps:

  1. Add the named version and deploy
class User < ApplicationRecord
  has_one_attached :avatar do |attachable|
    attachable.variant :preview, resize_to_fit: [177, 177 * 9 / 16], preprocessed: true
  end
end
  1. Preprocess this version for all existing records `bundle exec rails runner 'User.find_each { |user| user.avatar.variant(:preview).proc...

Organizing custom Date(Time) formats

Large Rails projects tend to define multiple custom ways to format Dates or DateTimes. This often leads to duplicated format strings, wrong formats and unnecessary introduction of new formats.

to_fs also supports to refer to those formats by name e.g. to_formatted_s(:iso_8601) or to_formatted_s(:short).
to_fs is an alias for to_formatted_s.

Those names are defined in Time::DATE_FORMATS and it's possible to add your own formats. There is a how to in the official [docs](https://api.rubyonrails.org/classes/Date.html#method-i-t...

Cast an ActiveRecord to a subclass or superclass

Note: ActiveRecord::Base#becomes has a lot of quirks and inconsistent behavior. You probably want to use ActiveType.cast instead.


ActiveRecord models have with a method becomes(klass) which you can use to cast the record into an instance of its subclasses or superclass. This is useful because some parts of Rails reflect on the class of an instance, e....

OpenAI TTS: How to generate audio samples with more than 4096 characters

OpenAI is currently limiting the Audio generating API endpoint to text bodies with a maximum of 4096 characters.
You can work around that limit by splitting the text into smaller fragments and stitch together the resulting mp3 files with a CLI tool like mp3wrap or ffmpeg.

Example Ruby Implementation

Usage

input_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices neque."
output_mp3_path = Rails.root.join("tts/ipsum...