travisliu/traim: Resource-oriented microframework for RESTful APIs

Use Traim to build a RESTful API for your ActiveRecord models with very little code.

Traim assumes your API resources will map 1:1 to your ActiveRecord models and database tables. This assumption usually falls apart after a few months into a project, so be ready to replace your Traim API with something more expressive afterwards.

Traim outputs a Rack application which you can either serve standalone or mount into your Rails app.

Deleting stale Paperclip attachment styles from the server

Sometimes you add Paperclip image styles, sometimes you remove some. In order to only keep the files you actually need, you should remove stale Paperclip styles from your server.

This script has been used in production successfully. Use at your own risk.

# Config #######################################################################
delete_styles = [:gallery, :thumbnail, :whatever]
scope = YourModel # A scope on the class with #has_attached_file
attachment_name = :image # First argument of #has_attached_file
noop ...

IRB: last return value

In the ruby shell (IRB) and rails console the return value of the previous command is saved in _ (underscore). This might come in handy if you forgot to save the value to a variable and further want to use it.

Example:

irb(main):001:0> 1 + 2
=> 3
irb(main):002:0> _
=> 3
irb(main):003:0> a = _
=> 3

RSpec's hash_including matcher does not support nesting

You can not use the hash_including argument matcher with a nested hash:

describe 'user' do
  let(:user) { {id: 1, name: 'Foo', thread: {id: 1, title: 'Bar'} }

  it do 
    expect(user).to match(
      hash_including(
        id: 1, thread: {id: 1}
      )
    )
  end
end  

The example will fail and returns a not very helpful error message:

expected {:id => 1, :name => "Foo", :thread => {:id => 1, :title => "Bar"}} to...

ActiveRecord::RecordNotFound errors allow you to query the :name and :id of the model that could not be found

ActiveRecord::RecordNotFound errors provide quite meaningful error messages that can provide some insight on application details. Consider the following:

ActiveRecord::RecordNotFound: Couldn't find Organisation::Membership with 'id'=12 [WHERE "organisation_memberships"."user_id" = 1]

You should probably not simply render those error messages to the user directly. Instead you you might want to re-raise your own errors. ActiveRecord::RecordNotFound provides you with methods :model and :id where you can get information about w...

Ruby: define a class with Struct.new

This card will show you a cool way to define a class using Struct.new.
A common usecase for Structs are temporary data structures which just hold state and don't provide behaviour. In many cases you could use a simple hash as a data structure instead. However, a Struct provides you with a nice constructor, attribute accessors and complains if you try to access undefined attributes. Structs are easy to compare (by attributes). A struct gives meaning to the data.

Disclaimer

Structs are great...

Speed up better_errors

If you use the Better Errors gem, you will sometimes notice that it can be very slow. This is because it sometimes renders a huge amount of data that will actually be hard to render for your browser.

You can significantly improve performance by adding this to config/initializers/better_errors:

if defined?(BetterErrors) && Rails.env.development?
  module BetterErrorsHugeInspectWarning
    def inspect_value(obj)
      inspected = obj.inspect
      if inspected.size > 20_000
        inspec...

Shoulda Matchers: how to test conditional validations

Shoulda Matchers don't provide canditional validations (validations with if: option). Here is how to write tests for the condition:

Class:

class Employee < ActiveRecored::Base
  validates :office, presence: true, if: manager?
  
  def manager?
    ...
  end
end

Test:

describe Employee do

  describe '#office' do
    
    context 'is a manager' do
      before { allow(subject).to receive(:manager?).and_return(true) }
      it { is_expected.to validate_presence_o...

MySQL 5.7.5 enables `ONLY_FULL_GROUP_BY` mode per default

When using GROUP BY, MySQL now complains if the SELECT includes columns which are not part of the GROUP BY.

Reason:

There could be multiple values for those columns per group but only one value can be picked for the results.

The default behaviour of MySQL prior to version 5.7 will not complain and arbitrarily choose a value. But this leads to non-deterministic results. So MySQL now has enabled the only_full_group_by setting by default to prevent this.

In Rails this could lead to some trouble, because scopes do not have sp...

Rails: How to write custom email interceptors

Nowadays it is fairly easy to intercept and modify mails globally before they are sent. All you have to do is register an interceptor class which responds to .delivering_email(message). This card will show you two common use cases.

Subject prefix:

Usually you want to prefix the subject line of emails with the current environment (except production) so you can differentiate between production mails and mails from other environments. Of course a...

Working with or without time zones in Rails applications

Rails supports time zones, but there are several pitfalls. Most importantly because Time.now and Time.current are completely different things and code from gems might use one or the other.

Especially configuring an application that cares only about one time zone is a bit tricky.

The following was tested on Rails 5.1 but should apply to Rails 4.2 as well.

Using only local time

Your life will be easier if your application does not need to support time zones. Disable them like this:

config.time_zone = 'Berlin' # Your local ...

Webmock's hash_including doesn't parse query values to string

Webmocks hash_including is similar to RSpec::Mocks::ArgumentMatchers#hash_including. Be aware that hash_including (webmock v3.0.1) doesn't parse integer values to String.

Without hash including you would say:

uri = URI('http://example.com/?foo=1&bar=2')
stub_request(:get, 'example.com').with(query: {foo: 1, bar: 2})
Net::HTTP.get(uri) # ===> Success

If you only want to check if foo is present you can use hash_including:

uri = URI('http://example.com/?foo=1&bar=2')
stub_request(:get, 'example.com').with(query: hash_i...

HTTP/2 push is tougher than I thought - JakeArchibald.com

TLDR: Browser implementations of HTTP/2 push are horrible. You might end up with worse performance than without pushing. However, the article includes a great explanation of how HTTP/2 push are supposed to integrate with browser APIs.

Quickly printing data in columns on your Ruby console

Dump this method into your Ruby console to quickly print data in columns. This is helpful for e.g. comparing attributes of a set of Rails records.

def tp(objects, *method_names)
  terminal_width = `tput cols`.to_i
  cols = objects.count + 1 # Label column
  col_width = (terminal_width / cols) - 1 # Column spacing

  Array(method_names).map do |method_name|
    cells = objects.map{ |o| o.send(method_name).inspect }
    cells.unshift(method_name)

    puts cells.map{ |cell| cell.to_s.ljust(col_width) }.join ' '
  end

  nil
end

Usag...

Using ActiveRecord with threads might use more database connections than you think

Database connections are not thread-safe. That's why ActiveRecord uses a separate database connection for each thread.

For instance, the following code uses 3 database connections:

3.times do
  Thread.new do
    User.first # first database access makes a new connection
  end
end

These three connections will remain connected to the database server after the threads terminate. This only affects threads that use ActiveRecord.

You can rely on Rails' various clean-up mechanisms to release connections, as outlined below. This may...

Storing trees in databases

This card compares patterns to store trees in a relation database like MySQL or PostgreSQL. Implementation examples are for the ActiveRecord ORM used with Ruby on Rails, but the techniques can be implemented in any language or framework.

We will be using this example tree (from the acts_as_nested_set docs):

root
|
+-- Child 1
|   |
|   +-- Child 1.1
|   |
|   +-- Child 1.2
|
+-- ...

How to use Parallel to speed up building the same html partial multiple times (for different data)

The parallel-gem is quite easy to use and can speed up rendering time if you want to render the same partial multiple times (e.g. for rendering long lists of things).

Parallel supports execution using forked processes (the default), threads (mind the GVL) and Ractors (some limitations for data sharing).

If your parallelized code talks to the database, you should [ensure not to leak database connections](https://makandracards.com/makandra/45360-using-activerecord-with-threads-will-leak-database-connect...

Rendering 404s for missing images via Rails routes

When you load a dump for development, records may reference images that are not available on your machine.

Requests to those images may end up on your application, e.g. if a catch-all route is defined that leads to a controller doing some heavy lifting. On pages with lots of missing images, this slows down development response times.

You can fix that by defining a Rails route like this:

if Rails.env.development?
  scope format: true, constraints: { format: /jpg|png|gif/ } do
    get '/*anything', to: proc { [404, {}, ['']] }

...

How to disable Chrome's save password bubble for Selenium tests

When filling out forms in Selenium tests, Chrome shows the (usual) bubble, asking to store those credentials.

While the bubble does not interfere with tests, it is annoying when debugging tests. Here are two ways to disable it:

Option 1: prefs

You can set profile preferences to disable the password manager like so:

prefs = {
  'credentials_enable_service' => false,
  'profile.password_manager_enabled' => false
}

Capybara::Selenium::Driver.new(app, browser: :chrome, prefs: prefs)

Sadly, there are no command line s...

Rails: Default generators

This is a visualization of the files that will be generated by some useful rails generators. Invoke a generator from command line via rails generate GENERATOR [args] [options]. List all generators (including rails generators) with rails g -h.

generator model migration controller entry in routes.rb views tests
scaffold
resource ✔ ...

ActiveRuby

Looks like ActiveState is trying to market a new Ruby distribution for Enterprises:

ActiveRuby Enterprise Edition is designed for businesses with large Ruby deployments in essential, mission-critical applications that, when down, could cost your business in lost revenue and a damaged reputation. Deploy Ruby with confidence knowing you're using the most secure, enterprise-grade builds for the platforms that power your business. You'll get priority access to our Ruby experts for technical support and best prac...

VCR: Inspecting a request

Using VCR to record communication with remote APIs is a great way to stub requests in tests. However, you may still want to look at the request data like the payload your application sent.

Using WebMock, this is simple: Make your request (which will record/play a VCR cassette), then ask WebMock about it:

expect(WebMock).to have_requested(:post, 'http://example.com').with(body: 'yolo')

Easy peasy.

Related cards

Thinkpad: Disable Bluetooth on start-up

Add the following to /etc/rc.local:

(sleep 3 && echo disable > /proc/acpi/ibm/bluetooth)&

Bluetooth icon will be active for a few seconds, then turn gray.

Some useful vim settings

Below is a list of some VIM settings I find useful. You can add them to your .vimrc.

source $VIMRUNTIME/mswin.vim                 " adds Ctrl-X, Ctrl-C, Ctrl-V; block visual mode is now Ctrl-Q
behave mswin

set autowriteall                             " autosave all files when a buffer is closed

set backupdir=~/.temp                        " dont pollute local directory with swap file, backup files etc
set dir=~/.temp

set undofile                                 " enable persistent undo
set undodir=~/.temp

set encoding=utf-8

...