Leaving CityView
August 6, 2021 in cityview, career

After almost four years at CityView, I have made the difficult decision to leave.

CityView is one of Victoria’s best software development companies. It’s full of smart people who know what they are doing and deeply care about it.

I worked with some very kind, thoughtful, and wise people at CityView, and I will miss each one of them.

At CityView, I worked on some critical projects such as Electronic Plans Review and Virtual Inspections.

CityView has been very kind to allow me to work from India for the past year so that I could stay close to my family during the pandemic.

I am leaving with wonderful memories and will always cherish my time spent at CityView, who has been like a second family to me.

Farewell, team.

Joining Arrays in Ruby
August 5, 2021 in ruby

While reading one of the old blog posts from the Riding Rails weblog, I came across a nifty way to join the values in an array.

Typically, you’d do this to join arrays:

> %w(1 2 3).join(", ")
=> "1, 2, 3"

However, multiplying an array with a separator accomplishes the same.

> %w(a b c d e) * "-"
=> "a-b-c-d-e"

> %w(a b c d e) * ", "
=> "a, b, c, d, e"

Is it concise? Definitely. Readable and expressive? I am not sure. However, it rhymes with the Ruby way of giving the programmer freedom to choose.

When to Create a Custom Class
August 4, 2021 in ruby

These are my notes from the second chapter of Polished Ruby Programming by Jeremy Evans. It explores when creating a custom class or a data structure is a good idea and when it’s not.

How you design and structure your classes has a huge effect on how intuitive and maintainable your code is.

Choosing to create a custom class is always a trade-off, in that all classes result in some conceptual overhead. Everyone who works with the code now has to learn how to use the class correctly and be productive while using it.

Benefits of creating a custom class

  1. It encapsulates state and logic and controls access to its data.
  2. It provides a simple way for calling functions on the instances of that class.

If others are going to access the data and the logic, then it makes sense to encapsulate it into a custom class. However, if it’s just an internal implementation detail used by another class with its own implementation, then creating a custom class is probably unnecessary, especially if one of the core classes in Ruby provides the same features or API.

Another thing to keep in mind is how many places you will be using this class. If multiple places in the codebase will need it, a custom class is appropriate. However, if it’s only used at a single location and doesn’t need to be directly accessed by others, you shouldn’t create a custom class for it (yet).

(I don’t necessarily agree with the above point, being a big fan of domain-driven design. I think custom classes reduce the conceptual overhead and let you express the concepts in a more powerful way. I prefer to introduce new classes even if they are used once internally)

Polished Ruby Programming
August 3, 2021 in ruby

I started reading Polished Ruby Programming by Jeremy Evans, author of the popular sequel and roda libraries. This is an advanced Ruby book, diving into the design principles, best practices, and trade-offs while programming Ruby. So far, it hasn’t disappointed. Here are my notes from the first chapter.

When to use core classes?

Only create custom classes when the benefits outweigh the costs.

With core classes, your code is more intuitive and in general, will perform better as it results in less indirection.

Use custom classes when you have to encapsulate the logic to write maintainable code. However, the benefits of encapsulation are not greater than the loss of intuition and performance.

Start with the core classes, and only add a custom class when you see a clear advantage.

Strings or Symbols?

Ruby is focused on programmer happiness and productivity, so it will often automatically convert a string to a symbol if it needs a symbol, or a symbol to a string if it needs a string.

A string in Ruby is a series of characters or bytes, useful for storing text or binary data. Unless the string is frozen, you can append to it, modify existing characters in it, or replace it with a different string.

A symbol in Ruby is a number with an attached identifier that is a series of characters or bytes.

The general principle is to use symbols when you need an identifier in your code, and strings when you need text or data.

Arrays, Hashes, or Sets?

Use an array if you need a simple list of values that you are iterating over or using the collection as a queue or a stack.

If you need a mapping of one or more objects to one or more objects, use a hash.

If you want a large list of unique objects and want to see whether a given object is contained in it, use a set.

Use Structs

It’s one of the underappreciated core class in Ruby. Struct allows you to create classes with one or more fields and automatically creates accessors for each field.

Using Struct, you can replace the following code:

class Artist
  attr_accessor :name, :albums

  def initialize(name, albums)
    @name = name
    @albums = albums
  end
end

with

Artist = Struct.new(:name, :albums)

Though Struct is lighter on memory than a regular class, it has slower accessor methods. It used to be faster than accessor methods in older versions of Ruby, but regular classes and attr_accessor methods have gotten significantly faster.

For maximum performance, stick to regular classes.

Overriding Constructors in Ruby
July 29, 2021 in ruby

Everything in Ruby is an object, including the class. When you create a new class, you are basically creating an instance of class Class. Ruby even allows you to override the new method on a class, allowing you to customize the creation of all objects.

class Person
  # person-specific code
end

When Ruby encounters the above code, it creates an instance of type Class. It then assigns this object to a global constant named Person.

When you call Person.new to create a new person, the #new method on the Class is called, by default. However, like everything in Ruby, you can override it.

class Class
  alias create new

  def new(*args)
    puts "Creating a new #{self.name}"
    create(*args)
  end
end

class Person; end

ak = Person.new

# Creating a new Person
# #<Person:0x00007fde9e869280>
puts ak

Knowing that classes are instances of Class allows you to dynamically create classes on the fly.

Person = Class.new do
  def greet
    "hello"
  end
end

ak = Person.new
puts ak.greet    # hello
Ruby Sweetness
July 28, 2021 in ruby

In my journey to learn and master Ruby, I keep coming across new syntactic sugar every day that makes me happy. This morning, I stumbled upon the double-splat ** operator while learning about the FileUtils standard library.

Now, I’d already seen the splat *, which converts one or more arguments to an array, so I was curious what the double-splat did.

The results were impressive.

def run(a, *b, **c)
  pp [a, b, c]
end

# [10, [], {}]
run 10 

# [10, [20], {}]
run 10, 20 

# [10, [20, 30, 40], {}]
run 10, 20, 30, 40

# [10, [20, 30, 40, [50, 60]], {}]
run 10, 20, 30, 40, [50, 60]

# [10, [20, 30, 40], {:name=>"akshay", :age=>29}]
run 10, 20, 30, 40, name: "akshay", age: 29

# [10, [], {:name=>"akshay", :age=>29}]
run 10, name: "akshay", age: 29

The double-splat operator was introduced in Ruby 2.0. It does the same thing for keyword arguments what the splat operator does for plain arguments. It converts all the key-value pairs to a single hash.

Are you not entertained?

Active Record Queries
July 25, 2021 in rails

There are two types of finder methods in Active Record; those that return a single instance of the model, e.g. find and first, and those that return return an instance of ActiveRecord::Relation representing a collection of instances, e.g. where and group. This post summarizes the query methods that return a single instance.

  • find

Retrieve the object with the given primary key. Raises ActiveRecord::RecordNotFound if no matching record is found.

irb(main):001:0> TimeReport.find(23)
   (1.5ms)  SELECT sqlite_version(*)
  TimeReport Load (0.3ms)  SELECT "time_reports".* FROM "time_reports" WHERE "time_reports"."id" = ? LIMIT ?  [["id", 23], ["LIMIT", 1]]
=> #<TimeReport id: 23, ...>

irb(main):002:0> TimeReport.find(230)
  TimeReport Load (0.3ms)  SELECT "time_reports".* FROM "time_reports" WHERE "time_reports"."id" = ? LIMIT ?  [["id", 230], ["LIMIT", 1]]
Traceback (most recent call last):
        1: from (irb):2:in `<main>'
ActiveRecord::RecordNotFound (Couldn't find TimeReport with 'id'=230)

You can also provide multiple primary keys (in an optional array) to fetch an array of results.

# SELECT * FROM customers WHERE (customers.id IN (1,10))
irb> customers = Customer.find([1, 10]) # OR Customer.find(1, 10)
  • take

Retrieve a single record without any specific order. You can also pass the number of records you want.

irb(main):012:0> TimeReport.take
  TimeReport Load (0.3ms)  SELECT "time_reports".* FROM "time_reports" LIMIT ?  [["LIMIT", 1]]
=> #<TimeReport id: 1, date: "2023-11-14 00:00:00.000000000 +0000", hours_worked: 0.75e1, employee_id: 1, job_group: "A", report_id: 43>
  • first

Fetch the first row, ordered by the primary key, by default. If you pass a number, Active Record will fetch that many number of rows.

irb(main):013:0> TimeReport.first
  TimeReport Load (0.4ms)  SELECT "time_reports".* FROM "time_reports" ORDER BY "time_reports"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<TimeReport id: 1,...

If you don’t want to order the results by primary key, first order them using the order method and then call first(n) to fetch the first n records.

# SELECT * FROM customers ORDER BY customers.first_name ASC LIMIT 1
Customer.order(:first_name).first

last acts in a similar way, except that it will fetch the last row in the result set.

  • find_by

Use this query method to find the first record that matches a condition. For example, to find the first customer with the name John,

# SELECT * FROM customers WHERE (customers.first_name = 'Lifo') LIMIT 1
Customer.find_by first_name: "John"

Alternatively, you can also write,

Customer.where(first_name: "John").take
Running Rails Migrations
July 22, 2021 in rails

In one of my previous posts, we saw how to create migrations in Rails to update the database schema. However, creating a migration on its own doesn’t update the database. You have to run the migration to make the changes. This post summarizes all the commands that modify the database.

rails db:migrate

This command runs the change method for all the migrations that have not run yet, in the order based on migration’s date. It also updates the db/schema.rb file to match the database structure.

rails db:rollback

Reverts the last migration. If you made a mistake and want to go back to the state before running the migration, use this command. Provide the STEP=n option if you want to revert last n migrations. You can run db:migrate after making corrections to the migration.

However, the Rails guides recommend that it’s not a good idea to edit an existing migration, especially if it has already been run on a production database. What you should do is create a new migration that performs the changes you need.

rails db:setup

This command creates the database, loads the schema, and initializes it with the seed data.

rails db:reset

Drop the database and set it up again. Equivalent to running db:drop and db:setup in sequence.

rails db:seed

Migrations can also add, modify, or delete data in the database. However, to add seed data after a database is created, you can use the ‘seeds’ feature in the database. Simply add some sample data in db/seeds.rb, and run the db:seed command.

Schema Files

Rails stores the current structure of the database schema in the db/schema.rb file. Schema files are also handy to check the attributes of a model. This information is not in the model’s code and is frequently spread across several migrations, but it’s outlined in the schema file.

Schema files are commonly used to create new databases, and it’s recommended to check them into source control.

By default, the schema file uses the :ruby format, but you can set it to :sql. This will save the schema in db/structure.sql file, using a database-specific tool, e.g. pg_dump for PostgreSQL and SHOW CREATE TABLE for MySQL.

rails db:schema:load

To create a new instance of your database, you can simply run the rails db:schema:load command. It’s better than running the entire migration history, as it may fail to apply correctly.

The :ruby format cannot express everything in the database schema, such as triggers, stored procedures, etc. Setting the format to :sql will ensure that an accurate schema is generated and a perfect copy of the database schema is created upon running db:schema:load.

The db/schema.rb or db/structure.sql is a snapshot of the current state of your database and is the authoritative source for rebuilding that database. This allows you to delete old migration files.

Ruby Standard Library: CSV
July 21, 2021 in ruby

Here are my notes from the standard library documentation for the CSV module. I took the notes while simultaneously reading the docs and programming, so they aren’t formatted very well, but they capture the essence of working with CSV data in Ruby.

CSV stands for comma-separated values.

A CSV file represents a table that contains text values separated by commas. In simple words, CSV data is a text representation of a table.

A row separator, typically a newline character \n separates the rows. A column separator, typically a comma ,, separates the columns.

If a value needs a comma, enclose the comma in quotes.

Examples

CSV data with 4 rows and 3 columns (includes the header rows)

Company, Founder, CEO
Microsoft,Bill Gates,Satya Nadella
Google,"Larry Page, Sergey Brin",Sunder Pichai
Amazon,Jeff Bezos,Andy Jassy

CSV data with 3 rows and 2 columns.

foo,0\nbar,1\nbaz,2\n

CSV in Ruby

The CSV class in the Ruby standard library helps us parse:

  1. a string representing CSV data
  2. a file containing CSV data
  3. an IO object providing CSV data

It can also generate CSV data as a String.

For most basic purposes, the singleton (static) methods on the CSV class will do.

Parsing CSV String

  1. CSV.parse(csv_string)

Returns an Array of Array of Strings. The first array represents the table, and the inner arrays represent the rows. Each String inside the Array represents a field value.

  1. CSV.parse_line(csv_string)

Returns the first row, which contains the header columns.

  1. String.parse_csv

Ruby’s monkey-patching also allows it to extend a String to add the parse_csv method. This method also returns the first row.

The following example illustrates the above methods.

require "csv"

data = %(Company,Founder,CEO
Microsoft,Bill Gates,Satya Nadella
Google,"Larry Page and Sergey Brin",Sunder Pichai
Amazon,Jeff Bezos,Andy Jassy)

# [["Company", "Founder", "CEO"], ["Microsoft", "Bill Gates", "Satya Nadella"], ["Google", "Larry Page and Sergey Brin", "Sunder Pichai"], ["Amazon", "Jeff Bezos", "Andy Jassy"]]
p CSV.parse(data)

# ["Company", "Founder", "CEO"]
p CSV.parse_line(data)

# ["Company", "Founder", "CEO"]
p data.parse_csv

Parsing CSV file

The read method on the CSV class takes the file path and returns the CSV data.

require "csv"

CSV.read("Data/companies.csv")

If you want to iterate over each row in a CSV file, use the foreach method.

path = "Data/companies.csv"

CSV.foreach(path) do |row|
  p row
end

If you want a data structure instead of arrays, the table method returns the entire CSV data as a CSV::Table object.

path = "Data/companies.csv"

table = CSV.table(path)   # #<CSV::Table mode:col_or_row row_count:4>
p table.headers   # [:company, :founder, :ceo]

Generate CSV Data

The generate method on the CSV class generates the CSV data. You can use it as follows:

output = CSV.generate do |csv|
  csv << %w{ Company Founder }
  csv << ["37Signals", "Jason Fried"]
  csv << ["Fog Creek Software", "Joel Spolsky"]
end

# Company,Founder
# 37Signals,Jason Fried
# Fog Creek Software,Joel Spolsky
puts output

You can also generate a single line from an array, using the generate_line method.

values = %w{ hello world }
output = CSV.generate_line(values)

# hello,world
puts output

Finally, the CSV class also extends Array, so you can call to_csv to build a CSV string from an Array.

values = %w{ hello world }
output = values.to_csv

# hello,world
puts output