Unfreeze a frozen ActiveRecord

You can freeze any Ruby object to prevent further modification.

If you freeze an ActiveRecord and try to set an attribute you will an error like this:

can't modify frozen hash

This is because ActiveRecord delegates #freeze to its attributes hash.

You can unfreeze most Ruby objects by creating a shallow copy of the frozen object by calling #dup on it:

user = User.find(3)
user.freeze
unfrozen_user = user.dup

Notes for Rails 2 users
-----------------...

RSpec: Tagging examples and example groups

In RSpec you can tag examples or example groups with any tags you like simply by saying

describe ReportCreator, slow: true do
  # ..
end

describe ReportCreator do
  it 'generates reports', slow: true do
    # ...
  end
end

You can then only run examples with these tags.

rspec --tag slow
rspec -t slow

# Using the parallel_tests gem
rake "parallel:spec[,,--tag slow]"

Or you can run all examples except the ones with a certain tag:

rspec --tag ~slow # note the ~
rspec -t ~slow

# Using the parallel_tests gem
r...

Ruby: How to grow or shrink an array to a given size

If you want to grow a Ruby Array, you might find out about #fill but it is not really what you are looking for. [1]
For arrays of unknown size that you want to grow or shrink to a fixed size, you need to define something yourself. Like the following.

Array.class_eval do

  def in_size(expected_size, fill_with = nil)
    sized = self[0, expected_size]
    sized << fill_with while sized.size < expected_size
    sized
  end

end

Use it like this:

>> [1, 2, 3].in_size(5)
=> [1, 2, 3, nil, nil]

...

Rubymine: Code folding

Code folding is a very useful feature to me. It gives me a quick overview over a file and keeps me from scolling like a hamster in its wheel.

Keyboard shortcuts:

Collapse/expand current code block

strg -/+

Collapse/expand the whole file

strg ctrl -/+

When diving into Cucumber features or huge Ruby classes, I usually collapse all and the gradually expand what I need.

Thread-safe collections in Ruby

When using threads, you must make your code thread-safe. This can be done by either locking (mutexes) all data shared between threads, or by only using immutable data structures. Ruby core classes like String or Array are not immutable.

There are several gems providing thread-safe collection classes in Ruby.

concurrent-ruby

The concurrent-ruby gem provides thread-safe versions of Array and Hash:

sa = Concurrent::Array.new # supports standard Array.new forms
sh = Co...

Rails has a built-in slug generator

Today I learned that Ruby on Rails has shipped with a built-in slug generator since Rails 2.2:

> "What Up Dog".parameterize
=> "what-up-dog" 

> "foo/bar".parameterize
=> "foo-bar" 

> "äöüß".parameterize
=> "aouss" 

Also see: Normalize characters in Ruby.

skorks/nesty

Nested exceptions for Ruby:

When you rescue an error and then re-raise your own, you don't have to lose track of what actually occured, you can keep/nest the old error in your own and the stacktrace will reflect the cause of the original error.

This is awesome when you classes convert exception classes. I now always subclass Nesty::NestedStandardError instead of StandardError for my own error classes.

About Exception#cause

Ruby 2.1 has a built-in mechanism with Exception#cause, which serves a similiar purpos...

Differences between transactions and locking

Web applications can be used by multiple users at the same time. A typical application server like Passenger has multiple worker processes for a single app. In a distributed deployment setup like we use at makandra you will even have multiple application servers, each with their own worker pool.

This means that your code needs to deal with concurrent data access. The two main tools we use to cope with concurrency are database transactions and distributed locks. These two are not interchangeable. You ca...

Custom loggers in Ruby and Rails

File logger

If you need to log to a file you can use Ruby's Logger class:

require 'logger'

log = Logger.new('log/mylog.log')
log.info 'Some information'
log.debug 'Debugging hints'
log.error StandardError.new('Something went wrong')

Logger does a number of things well:

  • Message type (info / debug / error) is logged
  • Log entries are timestamped
  • Writing log output is synchronized between threads
  • Logged errors are printed with full backtraces

If you don't like the output format, you can define a custom formatter.

I ha...

Dusen (>0.5) now with "exclude from search"

Dusen (our search gem) is now capable of excluding words, phrases and qualified fields from search.
E.g. search for

  • included -excluded
  • "search this" -"not that"
  • topic:"Yes" -topic:"No"

This will soon also work in makandra cards!

Linux: Kill a process matching a partial name

This is useful to kill processes like ruby my-script.rb:

pkill -f my-script.rb

With great power comes great responsibility.

Slack integration for deployments via Capistrano

You can hook into Slack when using Capistrano for deployment. The slackistrano gem does most of the heavy lifting for you. Its default messages are unobtrusive and can be adjusted easily.

When deploying, it posts to a Slack channel like this:

Example

How to integrate

Integrating Slackistrano with Capistrano 3 is fairly simple.

  1. In your Slack, open menu → A...

Ruby: Removing leading whitespace from HEREDOCs

If you're on Ruby 2.3+ there's a <<~ operator to automatically unindent HEREDOCs:

str = <<~MESSAGE
  Hello Universe!
  This is me.
             Bye!
MESSAGE

If you have an older Ruby, you can use the String#strip_heredoc method from ActiveSupport. See Summarizing heredoc in ruby and rails for an example.

Technically...

Exporting to Excel from Rails without a gem

See this Railscast.

Basically you can simply write views like index.xlsx.erb:

<?xml version="1.0"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
  xmlns:o="urn:schemas-microsoft-com:office:office"
  xmlns:x="urn:schemas-microsoft-com:office:excel"
  xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
  xmlns:html="http://www.w3.org/TR/REC-html40">
  <Worksheet ss:Name="Sheet1">
    <Table>
      <Row>
        <Cell><Data ss:Type="String">ID</Data></Ce...

Ruby: Enumerable#partition

If you want to sort values from an enumerable into two arrays based on whether they match a certain criteria or not, Enumerable#partition can come in handy.

# Enumerable#partition returns two arrays, 
# the first containing the elements of enum 
# for which the block evaluates to true, 
# the second containing the rest.

(1..6).partition { |v| n.even? }  #=> [[2, 4, 6], [1, 3, 5]]

Works well with destructuring assignment, too.

even, odd = (1..6).partition { |n| n.ev...

How to coordinate distributed work with MySQL's GET_LOCK

The linked article explains how to get a database-wide lock without creating table rows:

This article explains how I replaced file-based methods to ensure only one running instance of a program with MySQL’s GET_LOCK function. The result is mutual exclusivity that works in a distributed environment, and it’s dead simple to implement.

Ruby implementation

An implementation as a Rubygem seems to be [with_advisory_lock](https:...

Using PostgreSQL and jsonb with Ruby on Rails

Postgres 9.4 introduces a new column type: jsonb. json and jsonb columns store data differently, so just compare the two when you want to store JSON data and choose the one that matches your use case best.

Rails 4.2 includes support for jsonb columns, too. The article outlines different ways on how to interact with the serialized object.

Ruby: Counting occurrences of an item in an array / enumerable

Enumerable#count can do three things.

  • With no argument provided, it returns the number of items.
  • With an argument, it returns the number of items matching the given value.
  • With a block, it counts the number of elements yielding a truthy value.
ary = [1, 2, 4, 2]
ary.count #=> 4
ary.count(2) #=> 2
ary.count { |x| x % 2 == 0 } #=> 3

Ruby: Flatten arrays by only one level

Array#flatten by default flattens an array recursively. To only flatten the array for e.g. one level, it takes an optional argument.

# Flattens the array recursively
>> [1, [2, [3]]].flatten
=> [1, 2, 3]

# Flattens the array the given number of times
>> [1, [2, [3]]].flatten(1)
=> [1, 2, [3]]

Fix error UDPSocket.open: wrong number of arguments (0 for 1)

I got the following error after updating the selenium-webdriver gem:

wrong number of arguments (0 for 1) (ArgumentError)
/home/pointoo-dev/.rvm/gems/ruby-1.8.7-p374/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/common/platform.rb:183:in `open'
/home/pointoo-dev/.rvm/gems/ruby-1.8.7-p374/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/common/platform.rb:183:in `ip'
/home/pointoo-dev/.rvm/gems/ruby-1.8.7-p374/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/common/platform.rb:196:in `interfaces'

It was caused...

grosser/rspec-instafail

Gem to show failing specs instantly.
Unlike the --fail-fast option it doesn't abort abort on the first failure, but keeps running other examples after print out the failure.

I haven't tried it with parallel_tests.

Beware: Coffeescript "in" is not the Javascript "in"

The Javascript in operator does what Hash#has_key? does in Ruby: Return whether an object has a property.
However, Coffeescript has its own in operator that checks for array inclusion. To check whether an object has a property, use of:

Javascript

'name' in {name: 'Horst'} # => true

Coffeescript

# wrong
'name' in {name: 'Horst'} # => false

# correct
'name' of {name: 'Horst'} # => true
1 in [1,2,3] # => true

True story.

Ruby: Find a hash key given it's value

To find a hash key by it's value, i.e. reverse lookup, one can use Hash#key. It's available in Ruby 1.9+.

Hash#key(value) → key
# => Returns the key of the first occurrence of a given value.
     If the value is not found, returns nil.

hash = { "a" => 100, "b" => 200, "c" => 300, "d" => 300 }
hash.key(200)   #=> "b"
hash.key(300)   #=> "c"
hash.key(999)   #=> nil

Delegating an instance method to a class method in Ruby

With ActiveSupport you can say:

class Robot
  def self.likes_humans?
    'Nope.'
  end

  delegate :likes_humans?, to: :class
end

Robot.likes_humans?
# => 'Nope.'

Robot.new.likes_humans?
# => 'Nope.'