Ruby mixins explained

Hi Devs,

In this post i will try to explain how to use ruby mixins for DRYing up your code. Let’s say I have Car which can drive and honk. It can find a car with vin number or return count.

class Car
  def self.find(vin)
    puts "return car with VIN number #{vin}"
  end

  def self.count
    puts "return number of cars"
  end
  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end
end

# A Car can drive, honk

car = Car.new
car.drive
car.honk

# Find car by its VIN number
Car.find("123")
# Get total number of cars
Car.count

Now we get a requirement to add Trucks to our inventory which do most of the things aCar does. Let’s start DRYing the code.

First I will extract instance methods using a module and use include to make them available. In ruby we use include to add instance level methods from a module.

module VehicleBehavior
  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end
end

class Car
  include VehicleBehavior

  def self.find(vin)
    puts "return car with VIN number #{vin}"
  end

  def self.count
    puts "return number of cars"
  end
end

Making progress. Now i will extract the class level methods into a module and use theextend keyword to make them available. In ruby we use extend to add class level methods from a module.

module VehicleBehavior
  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end
end

module VehicleData
  def self.find(vin)
    puts "return car with VIN number #{vin}"
  end

  def self.count
    puts "return number of cars"
  end
end

class Car
  include VehicleBehavior
  extend VehicleData
end

Good. But is there a way to combine both modules into one ? yes.

Every time a class includes module – Ruby will trigger the self.included method on that module. It will also pass class as a parameter. In our case the Car class will be the klass argument.

module Vehicle
  def self.included(klass)
    klass.extend(ClassMethods)
  end

  module ClassMethods
    def find(vin)
      puts "return car with VIN number #{vin}"
    end

    def count
      puts "return number of cars"
    end
  end

  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end
end

class Car
  include Vehicle
end

Looking good. But is there is a cleaner way to do this YesActiveSupport::Concern

require 'active_support/concern'

module Vehicle
  extend ActiveSupport::Concern

  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end

  class_methods do
    def find(vin)
      puts "return car with VIN number #{vin}"
    end

    def count
      puts "return number of cars"
    end
  end
end

class Car
  include Vehicle
end

car = Car.new

Car.find("blah")
car.drive
car.honk

ActiveSupport::Concern makes the syntax better and also has the advantage of  gracefully handling module dependencies.

Now our Truck class is as simple as this

class Truck
  include Vehicle
end

Pretty cool right? Less code! Less bugs!! YaY.

Rails date validation pit falls

When ever you want to validate a date field against a time frame don’t forget to add context to it. For example I have the following class

Movie – name, release_date and collections

class Movie
  validates :release_date, presence: true
  validate :release_date_future?

  private

  def release_date_future?
    if release_date < Date.today
      errors.add(:release_date, "can't be in the past")
    end
  end
end

This Class looks absolutely legit. Let’s say I have a Movie object called Avengers endgame.

Movie.new(name: ‘Avengers endgame’, release_date: ’04-26-2019′).save

So far so good but when i want to update the `collections` at later point of time(after the movie got released) i can never do that because the Movie obj goes invalid (as it once again checks the release_date with current_date).

so the right thing to do is to add context to the date validation. Like we are interested in only while creation.

validate :release_date_future?, on: :create

Hope this helps.

Rails: Kick a logged in user(devise) out of his session

Have you ever thought of kicking a specific logged in user out of their session (for some weird reason).

If you are using devise for authentication then the short answer is you can’t.

I tried many ways to hack around to fool devise into thinking that the user session is expired but no luck. The user session cannot be accessed by other users (like from the rails console or database level).

The closest i thought I came is to trick the Timeoutable  hook. But it depends on last_request_at which is taken from the user session.  I tried messing with db fields like

current_sign_in_at, last_sign_in_at but realized that devise does not look at these fields once the user logs in.

Over all, the conclusion is that we can’t mess around with Devise which does its job well.

Note: You can still clear all the sessions for all the users using the following ways, depending on where you stored the session :

  1. Cookie Store (default) :

    Fleet::Application.config.session_store :cookie_store, key: _change_me_session.

    When you change the key the old sessions expire.

  2. Redis as session store: redis-cli flushall or delete sessions using a wildcard if we know part of the key $redis.del $redis.keys('session*').
  3. Database: If the sessions are stored in the database rake db:sessions:clear.

 

Rails: can not be used with :count => 1. key ‘one’ is missing.

If you ever encountered this error probably you are trying to add/access a I18n key for a `enum` attribute. For example if my model has a `enum` value like below.

class Shipment < ApplicationRecord
   enum delivery_option: {"arrival_notice":"arrival_notice", "arrival_schedule": "arrival_schedule"}
end

If we want to internationalize the `values` as

Call/notify before delivery and Delivery appointment required

. In your en.yml you need to specify as below.

en:
  activerecord:
    attributes:
      shipment:
        delivery_option:
          one: 'Delivery Option'
          arrival_notice: "Call/notify before delivery"
          arrival_schedule: "Delivery appointment required"

I18n expects the name of-the field as the first key/value.If you forget that then you see this error. Happy coding!

When to define default constructor in java

In Object Oriented Programming calling constructor is the first step of initialization an object. There are 2 types of constructors – default and parameterized.

Default – Constructor that take zero argument is called Default Constructor. Every class needs a default constructor. A default (no-argument) constructor is automatically created only when you do not define one yourself.

Parameterized – Constructor that take arguments are called parameterized Constructors.

They can be added as per our requirement.

When you declare a parameterized constructor you must add default constructor as java stops creating one.

How to configure postgres data base in a spring boot application

There are 2 steps involved in configuring postgres sql data base to a spring boot application assuming we already have data base setup.

  1. Add postgres sql dependency to pom.xml.

postgres_pom

2. We need to tell Spring container to look into postgres by changing the application.properties.

application.properties

So the list of possible options for ddl-auto are

  • validate: validate the schema, makes no changes to the database.
  • update: update the schema.
  • create: creates the schema, destroying previous data.
  • create-drop: drop the schema when the SessionFactory is closed explicitly, typically when the application is stopped.

I added spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true as the server is throwing following warning while startup.

java.sql.SQLFeatureNotSupportedException: Method org.postgresql.jdbc42.Jdbc42Connection.createClob() is not yet implemented.

Now restart server and you should see successful db connection logs. When the connection is successful spring container tries to validate the entities in the project to the db tables.

Happy coding!

Update and Validate a rails hstore column

Rails Update hstore column

For example if i have a hstore column like below

settings public.hstore DEFAULT '"currency"=>"USD"'::public.hstore NOT NULL,

Now I will update the column to add preferences in a migration as below

class AddPreferencesToUser < ActiveRecord::Migration[5.1]
  def change
    reversible do |dir|
      dir.up do
        update <<-SQL
          UPDATE users SET settings = settings || hstore('preferences', '');
SQL
end
end
end

 Validation

Assume we want to validate preferences to be one of  facebook, twitter, linkedin these.

the validation looks this. as enum doesn’t work with hstore key well.

store_accessor :preferences validates :preferences, inclusion: ['facebook', 'twitter', 'linkedin'], allow_blank: true

see you next time!