“...I've been working since 2008 with Ruby / Ruby on Rails, love a bit of Elixir / Phoenix and learning Rust. I also poke through other people's code and make PRs for OpenSource Ruby projects that sometimes make it. Currently working for InPay who are based in Denmark...”

Rob Lacey
Senior Software Engineer, UK

Extending RedCloth to use custom tags.

After finding the GitHub jQuery Repo Widget I want to embed the widget in my blog posts. The body of which uses RedCloth So I needed to add a custom tag which could turn.

github. JoelSutherland/GitHub-jQuery-Repo-Widget

into

<div class="github-widget" data-repo="JoelSutherland/GitHub-jQuery-Repo-Widget"></div>

You can extend RedCloth’s HTML formatter by placing something like this in config/initializer/redcloth_extensions.rb for example.

require 'redcloth'

module RedCloth
  module Extensions
    def github(options)
      html = %Q{<div class="github-widget" data-repo="#{options[:text]}"></div>\n}
    end
  end
end
RedCloth::Formatters::HTML.send(:include, RedCloth::Extensions)

And there you have it.

github. JoelSutherland/GitHub-jQuery-Repo-Widget

Ruby Time#to_i and MySQL UNIX_TIMESTAMP

In a recent project I wanted to use a date to be a component of a hashed string in order to create a unique hash for a password reset page. I want the hash to expire when the user’s password is reset, in this case when the user’s last_logged_in datetime changes. I can generate the hash simply.

class User < ActiveRecord::Base
....
  def password_token
    unless new_record?
      Digest::SHA1.hexdigest([id, password_hash, salt, last_logged_in.to_i].join)
    end
  end
....
end

I don’t want to store this hash in the database (we might want to in the future to optimise the look up). So I need to generate the hash in SQL, but how to I do the equivalent to Time#to_i in SQL.

[29] pry(main)> Time.parse('2014-10-03 01:17:21 +0100').to_i
=> 1412295441

I initially thought I was looking for TO_SECONDS as per the MySQL Date and Time functions it seems it should be UNIX_TIMESTAMP

mysql> SELECT TO_SECONDS('2014-10-03 01:17:21 +0100');
+-----------------------------------------+
| TO_SECONDS('2014-10-03 01:17:21 +0100') |
+-----------------------------------------+
|                             63579518241 |
+-----------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> SELECT UNIX_TIMESTAMP('2014-10-03 01:17:21 +0100');
+---------------------------------------------+
| UNIX_TIMESTAMP('2014-10-03 01:17:21 +0100') |
+---------------------------------------------+
|                                  1412295441 |
+---------------------------------------------+
1 row in set, 1 warning (0.00 sec)

Found the answer at HOW TO GET THE CURRENT EPOCH TIME . And now I can write the finder method.

class User < ActiveRecord::Base
....
  class << self
    ....
    def with_password_token(password_token)
      where("SHA1(CONCAT(users.id, IF(users.password_hash IS NOT NULL, users.password_hash,''), IF(users.salt IS NOT NULL, users.salt, ''), IF(users.last_logged_in, UNIX_TIMESTAMP(users.last_logged_in), ''))) = ?", password_token).first
    end
    ....
  end
....
end

ActiveRecord scopes and lambdas

Had another annoying issue with posts on this blog not appearing immediately after being published.

class Post < ActiveRecord::Base
  scope :published, where(['published_at IS NOT NULL AND published_at < ?', Time.now])
end
</pre></code>

So any Post  with a published_at in the past should be found in Post.published.all for example. The post is missing. I thought this was an issue in *production* so I enabled logging and redeployed.

<pre><code>
Robl::Application.configure do
.....
  # Set to :debug to see everything in the log.
  # config.log_level = :info
  config.log_level = :debug
.....

Looking at the log it seems fine initially, but then I realise that the timestamp for the published_at date is the same ‘2014-09-29 09:40:59’ seconds after the the page is reloaded.

D, [2014-10-02T21:51:22.979289 #4499] DEBUG -- :    (0.3ms)  SELECT COUNT(*) FROM `posts` WHERE (published_at IS NOT NULL AND published_at < '2014-09-29 09:40:59')
D, [2014-10-02T21:52:56.305813 #4499] DEBUG -- :   Post Load (0.2ms)  SELECT `posts`.* FROM `posts` WHERE (published_at IS NOT NULL AND published_at < '2014-09-29 09:40:59') ORDER BY published_at DESC LIMIT 25 OFFSET 0
...
D, [2014-10-02T21:52:56.384903 #4499] DEBUG -- :    (0.2ms)  SELECT COUNT(*) FROM `posts` WHERE (published_at IS NOT NULL AND published_at < '2014-09-29 09:40:59')
...

Since Time.now is apparently standing still a bug in the model scope definition is incorrect. The where doesn’t evaluate Time.now every time the scope is called it evaluates it when the class is loaded. The ActiveRecord Query needs to be placed inside a lambda in order that its contents are evaluated when the scope is called.


class Post < ActiveRecord::Base
  scope :published, -> { where(['published_at IS NOT NULL AND published_at < ?', Time.now]) }
end

And success. Time to redeploy.

Customise attribute names of ActiveRecord objects

Ran into a problem where by the name of the field in our database doesn’t properly reflect the human name of the attribute.

e.g. User#some_id

[12] pry(main)> User.human_attribute_name :some_id
=> "Some"
[13] pry(main)> user = User.new
=> #<User id: nil, some_id: nil>
[14] pry(main)> user.errors.full_messages
=> ["Some should be valid"]

So ‘Some’ is a bit meaningless. But we can change this in the i18n config files like so.

en:
  activerecord:
    attributes:
      user:
        some_id: Something much more awesome
</pre></code>

And then you get this....

<pre><code>
[17] pry(main)> User.human_attribute_name :some_id
=> "Something much more awesome"
[18] pry(main)> user = User.new
=> #<User id: nil, some_id: nil>
[10] pry(main)> user.errors.full_messages
=> ["Something much more awesome should be valid"]

Sweet!

Are you vulnerable to Shellshock

The recently reported Bash vulnerability Shellshock should be tackled as soon as possible on all Unix based systems. For me it was my personal server, client servers, desktops, laptops and all servers in our production cluster. That’s a lot of servers.

env x='() { :;}; echo vulnerable' bash -c 'echo this is a test'

If you’re vulnerable you’ll see

vulnerable
this is a test

If you’re all good you’ll see

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
this is a test

If you’re vulnerable take steps to upgrade bash as soon as possible.

Change your Rails time and date formats in i18n translation locales

Needed to tweak the time format from the default “Sat, 27 Sep 2014 17:32:10 +0000” to “September 27, 2014 at 17:32”. You can do this in config/locales/en.yml like so

en:
  site_name: "RobL"

  date:
    formats:
      default: "%Y-%m-%d"
      short: "%b %d"
      long: "%B %d, %Y"
  time:
    formats:
      default: "%B %d, %Y at %H:%m"

Don't subclass Struct.new

Ran into a really irritating issue this week. While trying to test a new install of Resque . Attempting to run a worker kept return a ‘subclass mismatch error’ on some classes in our Rails application. Our Blah::User class was seemingly being reloaded and on the second reload the anonymous parent class had a mismatch. Make sense.

module Blah
  class User < Struct.new(:id, :email)
    def to_s
      "#{id} #{email}"
    end
  end
end

According to this Reddit post you shouldn’t subclass structs. Taking another look at this the above seems incorrect, I never noticed it before but the above would create a new anonymous Struct class and would immediately subclass it. The anonymous class on it’s own is not required, and it appears is the cause of this class loading oddness. The problem is resolved when we define the Struct without subclassing and we haven’t got any anonymous Struct classes using up memory.

module Blah
  User = Struct.new(:id, :email) do
    def to_s
      "#{id} #{email}"
    end
  end
end

xterm-256color: unknown terminal type

Trying to run `top` on an Ubuntu server from OSX Mountain Lion results in the following.

root@server:~# top
'xterm-256color': unknown terminal type.

Thanks to Corentin Leclerc for the solution http://blog.corentinleclerc.com/terminal-sur-macos-x-lion-xterm-256color-unkn

Add the following to ~/.profile or ~/.bash_profile

export TERM="xterm"

Restart your terminal, or open a new tab. Magic.

root@server:~# top

top - 10:52:46 up 31 days, 16:34,  1 user,  load average: 0.04, 0.02, 0.00
Tasks:  57 total,   1 running,  56 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:    786652k total,   772360k used,    14292k free,   181920k buffers
Swap:        0k total,        0k used,        0k free,    46000k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                                                                                                                              
26479 rails     20   0 89604  57m 2428 S  0.3  7.5   0:04.08 ruby1.8                                                                                                                                                                                               
    1 root      20   0  1952  508   72 S  0.0  0.1   0:13.04 init                                                                                                                                                                                                  
    2 root      15  -5     0    0    0 S  0.0  0.0   0:00.12 kthreadd

Recover committed (deleted) file in Git

What if, just suppose, you deleted a file in error, made the commit to Git, then several commits later you realise you needed the file all along. How do you get it back?

Well, you find the last commit for that file and then checkout the file with the revision number. In just a few lines then…

Robs-iMac:repo rl$ file='app/views/previously_deleted.html.haml'
Robs-iMac:repo rl$ echo $file
app/views/previously_deleted.html.haml
Robs-iMac:repo rl$ git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

Raspberry Pi - part 1

This year my main festive gift from the mighty Kat was a long awaited Raspberry Pi. Just never seemed to have the funds to nab one myself and I am pretty chuffed. If you’ve not heard of one then you’ve been living under a rock for the last year. The Raspberry Pi is basically just a very small, barebones computer. In fact its so minimal that it doesn’t even come with a case.

Raspberry Pi

You can see on the board that the mounted interfaces are a dual USB port, micro-USB power supply, HDMI output to monitor, audio out, video out, GPIO pins.

You might have enough bits and bobs lurking in your cupboard full of cables and old hardware to get going but alas I do not. So in order to get started I need some way of hooking this up to a monitor, keyboard and powering the thing.

USB Keyboard

No PS2 ports so its all USB, so a USB Keyboard is needed. Relatively cheap, I’ve spotted a great wireless one with a touch pad for £25 but for now this one will do.

Monitor

While the Raspberry Pi can be easily used with a modern television I can’t see myself sitting in the front room while I’m playing with it for the time being. You can use any monitor or television with an HDMI or DVI connection. So you’ll need a cable

My spare monitor is a VGA which won’t work without an adapter to convert the signal. £30 for an adapter is a bit much really. I’ve secured a DVI monitor from a friend for £10, so I’ve opted for the HDMI to DVI while I’m playing.

SD Card / Operating System

You’ll need an SD Card to install the operating system on. Fortunately we have an old 4Gb card from a Camera. So cost £0. Yay. average cost on Amazon about £6 so not bad.

Install it yourself

You can follow the tutorial on the Raspberry Pi Downloads page to copy an OS onto a card.

Pre-installed

You can get an 8Gb (and upwards) SD Card from ThePiHut with one of two distributions from £8.99.

  • Raspbian – pre-installed Raspian is an optimised version of Debian, containing LXDE, Midori, development tools and example source code for multimedia functions.
  • OpenElec / XBMC – pre-installed Open Embedded Linux Entertainment Center, or OpenELEC for short, is a small Linux distribution built from scratch as a platform to turn your computer into a complete XBMC media center.

That’s a start I’ve ordered what I need so now I have to sit and wait. I’ve got a few ideas about what I’d like to build.

  • MAME centre
  • Media centre
  • Spotify streaming straight into the HiFi

So I’ll do a bit of research and buy a book or two.

Resources

There’s plenty of resources out there as well as some physical publications to get started with.

More useful links…