“...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...”

Rob Lacey (contact@robl.me)
Senior Software Engineer, Brighton, UK

Static Google Maps

Since we’re heading off to Germany in the morning, here’s a quick stab at the Google Maps Image APIs Static Maps . Looks fairly straight forward if you have the Lat/Long of the places you want to create markers for.

https://maps.googleapis.com/maps/api/staticmap?zoom=6&size=900×500&maptype=roadmap&markers=color:blue|label:Brighton|50.84274,-0.106829&markers=color:green|label:Essen|51.450001,7.016700

Redirect / to another path in Nginx

I needed to tweak my Nginx config to redirect / (slash) only on a Wordpress site to a completely different location but leave all other routing intact. There we have it.

server {
  listen 80;
  server_name here.robl.me;
  root /var/www/wordpress;
  index index.html index.htm index.php;

  location = / {
    rewrite ^ http://there.robl.me permanent;
  }

  location / {
    try_files $uri $uri/ /index.php?$args;
    # auth_basic "Restricted";
    # auth_basic_user_file /etc/nginx/.htpasswd;
  }

  location ~ \.php$ {
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
  }
}

Wrote some PHP for the first time in about 8 years.

I think I liked it. Maybe. Associative Arrays and the like

<?php
$array = array(
  'units' => array(
    '1117411' => array(
      'is_shipped_at' => 'true',
      'shipped_at'    => '2014-10-14 T14:56:55+00:00'
    )
  )
);
var_dump($array);
?>
Robs-iMac:snarf rl$ php -f data.php
array(1) {
  ["units"]=>
  array(1) {
    [1117411]=>
    array(2) {
      ["is_shipped_at"]=>
      string(4) "true"
      ["shipped_at"]=>
      string(26) "2014-10-14 T14:56:55+00:00"
    }
  }
}

JBuilder extensions


class Jbuilder

  # don't repeat yourself. when WillPaginate
  def paginate!(collection, &block)
    if collection.respond_to?(:page)
      set! :total_entries, collection.total_entries
      set! :page, collection.current_page
      set! :per_page, collection.per_page
    else
      set! :total_entries, collection.count
      set! :page, 1
      set! :per_page, collection.count
    end
    set! :entries, collection, &block
  end

  # don't return null, should should be an empty string
  def _set_value(key, value)
    raise NullError, key if @attributes.nil?
    unless @ignore_nil && value.nil?
      if value.nil?
        @attributes[@key_formatter.format(key)] = ''
      else
        @attributes[@key_formatter.format(key)] = value
      end
    end
  end
  
  # when you want to skip a record when iterating over a collection
  def _map_collection(collection)
    return [] if collection.nil?

    collection.map do |element|
      _scope{ yield element }
    end.reject(&:blank?)
  end

end

Resetting counter cache

Ran into a problem whereby the counter cache on one of our associations was out of sync. Simple …just update the counter cache with the count of the association?

rails >> u User.find(1)
rails >> u.update_attribute(:roles_count, 1)
ActiveRecord::ActiveRecordError: roles_count is marked as readonly
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/activerecord-3.2.18/lib/active_record/persistence.rb:194:in `update_column'
	from (irb):17
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands/console.rb:47:in `start'
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands/console.rb:8:in `start'
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands.rb:41:in `<top (required)>'
	from /data/app/current/script/rails:6:in `require'
	from /data/app/current/script/rails:6:in `<main>'
rails >> u.update_column(:roles_count, 1)
ActiveRecord::ActiveRecordError: roles_count is marked as readonly
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/activerecord-3.2.18/lib/active_record/persistence.rb:194:in `update_column'
	from (irb):17
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands/console.rb:47:in `start'
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands/console.rb:8:in `start'
	from /data/app/shared/bundled_gems/ruby/2.1.0/gems/railties-3.2.18/lib/rails/commands.rb:41:in `<top (required)>'
	from /data/app/current/script/rails:6:in `require'
	from /data/app/current/script/rails:6:in `<main>'
rails pro >> User.readonly_attributes
=> #<Set: {"roles_count"}>

Seems you can’t update the value, I was surprised that update_column wouldn’t work either. I guess it makes sense to protect this value since it’s supposed to be kept in sync and only in rare occasions should it be out of sync. However, you can use the reset_counters class method.

rails >> u = User.find(1)
rails pro >> User.reset_counters u.id, :roles
  User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
   (0.4ms)  SELECT COUNT(*) FROM `roles` INNER JOIN `role_users` ON `roles`.`id` = `role_users`.`role_id` WHERE `role_users`.`user_id` = 1
   (0.9ms)  UPDATE `users` SET `roles_count` = 1 WHERE `users`.`id` = 355411
=> true

Calling super with no arguments

Suppose I want to overwrite a method an apply a optional argument that the original method didn’t accept.

class Something < ActiveRecord::Base

  def to_s(or_other = nil)
      if or_other
        Other.new.to_s
      else
        super
      end
  end

end

Seems fine. However, when you call to_s you’d get an error like wrong number of arguments (1 for 0). The reason is because without supply arguments to super they, by default, use the arguments passed to the overridden method. Since the original method didn’t accept any arguments previously then this fails. In order to avoid this you must call super() to deliberatly call it without arguments.

class Something < ActiveRecord::Base

  def to_s(or_other = nil)
      if or_other
        Other.new.to_s
      else
        super()
      end
  end

end

Created a new OmniAuth Strategy for PledgeMusic

Just wanted to test out OmniAuth by plugging into PledgeMusic authentication.

github. braindeaf/omniauth-pledgemusic

Well it works and you get an access token out, nice and simple. Wouldn’t mind re-styling the OmniAuth::Form to be a bit more in keeping with the decor.

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.