Random Developments And Random Toys

Devise serialize into session trick

I always thought Devise is a great example of a remarkably flexible and modular behaviour implementation. To a level it is not fully documentable. Instead, after you got started with the basics, its source code serves best as documentation (and is a good read overall).

One of the many extension points which has attracted my attention before is serialize_into_session / serialize_from_session resource class methods.

As of now, the default implementations of these methods are:

def serialize_into_session(record)
  [record.to_key, record.authenticatable_salt]
end

def serialize_from_session(key, salt)
  record = to_adapter.get(key)
  record if record && record.authenticatable_salt == salt
end

So - to put things simply - it serializes Devise resource instance (i.e. user account) into a unique database identifier and vice versa. And the authenticatable_salt part is used to force close all sessions if user changes his/her password.

Both are then called by Warden:

warden_config.serialize_into_session(mapping.name) do |record|
  mapping.to.serialize_into_session(record)
end

warden_config.serialize_from_session(mapping.name) do |args|
  mapping.to.serialize_from_session(*args)
end

Notice that Devise does not really care about serialize_from_session argument number or structure. Whatever is returned by serialize_into_session it will be passed to serialize_from_session.

This means that by overriding both we can serialize Devise resource into a vector of information containing more than just primary database key. We can pretty much put all the user account attributes into the session and extra. Extras could be an identity provider name used to start the session, two factor authentication flag / details and similar.

By overriding these methods we can also implement anonymous ephemeral user session without persisting it to database. The implementation of this could look like this:

class Account < ApplicationRecord
  devise :database_authenticatable, :registerable

  class << self
    def serialize_into_session(record)
      if record.persisted?
        [record.to_key, record.authenticatable_salt]
      else
        [record.to_key, record.authenticatable_salt, { attributes: record.attributes }]
      end
    end

    def serialize_from_session(key, salt, opts = {})
      if opts.key?("attributes")
        new(opts["attributes"])
      else
        record = to_adapter.get(key)
        record if record && record.authenticatable_salt == salt
      end
    end
  end
end

Start of anonymous session then becomes something like this:

account = Account.new(id: SecureRandom.uuid, name: 'Nameless Account')
sign_in account
redirect_to after_sign_in_path_for(account)

After the “nameless user” decides to actually sign up, code could be something like this:

current_account.name = params[:name]
current_account.email = params[:email]
current_account.password = params[:password]
current_account.password_confirmation = params[:password_confirmation]
current_account.save
sign_in current_account, force: true
redirect_to after_sign_in_path_for(current_account)

Writing actual application code does require some mindset adjustment when current_account can be both incomplete instance and persisted record. Other than that it works rather well. Nice effect of this is unified tracking / logging of both anonymous and registered users without breakup around the registration.

Extending ActiveRecord associations

Ever found yourself having difficulty to decide if method belongs to parent or child model?

Let’s say we have a Blogger and Article models. And profile section of our “blogging platform” displays list of articles published this current month.

Our option 1 is doing it in Blogger model:

class Blogger < ApplicationRecord
  has_many :articles

  def articles_this_month
    articles.where('created_at > ?', Time.current.beginning_of_month)
  end
end

And somewhere in our controller:

@articles = blogger.articles_this_month

Looks fine, but with domain models getting more complicated we may find our Blogger model polluted by articles_this, articles_that methods.

Option 2 is implement it in Article model using class method (or alternatively scope):

class Article < ApplicationRecord
  belongs_to :blogger

  def self.this_month
    where('created_at > ?', Time.current.beginning_of_month)
  end
end

And somewhere in our controller:

@articles = blogger.articles.this_month

Again, nothing wrong with that. Except maybe the fact that Article.this_month does not make too much sense on its own. In a way it pollutes Article model api with methods which don’t make sense without context of parent Blogger.

It’s almost as if our this_month method does not fit on both of our models and belongs to the relationship between them. Here we come to a somewhat little known ActiveRecord feature and our option 3: extending relationship between the models with custom methods.

has_many relationship documentation describes option :extend:

Specifies a module or array of modules that will be extended into the association object returned. Useful for defining methods on associations, especially when they should be shared between multiple association objects.

Our module then:

module BloggerArticles
  def this_month
    where('created_at > ?', Time.current.beginning_of_month)
  end
end

And the Blogger model:

class Blogger < ApplicationRecord
  has_many :articles, extend: BloggerArticles
end

Code somewhere in controller:

@articles = blogger.articles.this_month

Now neither Blogger nor Article has any out of place “this month” filter related methods. Instead, filter is now part of relationship between the models.

So, to summarise: models are not the only places to put your domain code. Relationship can also be a good option for that. And when used well - can be great way to build natural and well scoped domain APIs.

Adding delays to reduce ESP8266 power consumption

I’m currently working on a small ESP8266 gadget which runs UDP server and does some actions based on packets received.

Main loop goes a bit like this (note: I’m using Arduino core):

void loop() {
    size_t size = udp.parsePacket();
    if (size > 0) {
        // handle the packet
    }
}

Looks fine until you remember that ESP8266 is a 32bit micro running at pretty whopping (for $1 price) 80Mhz frequency. So what does it actually do when there’s no packets coming in?

To start with, let’s figure out how many loops it does per second. This can be done by adding a counter and getting frequency by dividing it from time spent once in a while. Or.. If you have a scope, by flicking GPIO pin and measuring it:

void loop() {
    digitalWrite(5, HIGH);
    size_t size = udp.parsePacket();
    if (size > 0) {
        // handle the packet
    }
    digitalWrite(5, LOW);
}

Result:

Loop frequency

107 thousand times / second! When with my app I can barely expect tens of packets / second. Which pretty much means that we’re burning CPU cycles needlessly.

Let’s add some delays (4ms in total) then and see what happens:

void loop() {
    digitalWrite(5, HIGH);
    delay(2);
    size_t size = udp.parsePacket();
    if (size > 0) {
        // handle the packet
    }
    digitalWrite(5, LOW);
    delay(2);
}

Result:

Loop frequency with some delays

Loop now runs at little more reasonable ~250 times / second. Should deal with tens of packets / second without human noticeable delay.

And the best part of that is power consumption change (logged by R&S HMC8043 power supply):

Current consumption logged by HMC8043

Without delays - constant almost 80mA current consumption. With delays - baseline 20mA current with spikes during modem wake ups.

Project details with Jupyter notebook to generate the plot here. And a very useful Espressif board post about esp8266 power modes.

Aisler - new KiCad friendly PCB prototyping service

So I’ve been raving about OSH Park and how good they are for about a year now. However there’s one tiny issue with them - cheapest service turnaround (from upload to PCBs arriving by post) is roughly 3 weeks if you live in EU.

From this year’s FOSDEM “KiCad project status” talk I learned that there’s new player in this - AISLER. They support manufacturing PCBs directly from KiCad’s ‘.kicad_pcb’ files, specs and are similar, turnaround is ~2 weeks if you’re in EU (guys are based in Germany).

Already got one small PCB from them - ESP8266 based RGBW strip controller. Smooth process, great quality and 2 week turnaround (which will let me to iterate on my hobbyist projects quicker).

KiCad render

PCB from AISLER

Assembled board with a genuine bodge

(Not so) interesting I2C PWM RGB led driver NCP5623

I was looking to something that could offload doing PWM from microcontroller + save precious I/O pins and found NCP5623:

Triple Output I2C Controlled RGB LED Driver

The NCP5623 mixed analog circuit is a triple output LED driver dedicated to the RGB illumination or backlight LCD display.

Sounds great, by my TL/DR of the datasheet would be: 5 bit PWM resolution is not enough if you plan doing artsy slow dimming.

Learned the hard way by actually getting parts + TSSOP−14 breakout to build test circuit. Slow fading is visibly choppy, 32 levels of brightness is just not enough.

But still, it’s an interesting chip.