RottenSoftware

Random thoughs about software development

Adding new entity

Today we will work on extending Shyshka application with the possibility of adding examples to the words. To each word we will be able to add as many examples of usages as we wish.

What are associations?

In simple words, associations model relationships between entities. In our case, our one entity will be Word and the second one will be Example. Each Word can have many Examples but every Example can have only one Word associated to it.

Associations in Hanami

Since associations represents how data is linked in the database, we define associations in repositories.

Unlike associations in Ruby on Rails framework, associations does not add any public method to the repository. Instead, Hanami prefers explicit interface - which means that if we want to perform specific action, like adding word with examples, we need to explicitly add method to perform this operation. This design choice is supposed to prevent repositories from having extensive and mostly unused public API.

Another difference between Ruby on Rails and Hanami associations is lacking of eager loading - if we want to load a Word with associated Examples we have to add additional method to do it. If we don’t add this method, then the result of the association will be nil.

Adding has_many association

The first we need to do is creating a new entity called Example.

bundle exec hanami generate migration create_examples

Open newly created migration file and add this code:

Hanami::Model.migration do
  change do
    create_table :examples do
      primary_key :id
      foreign_key :word_id, :words, on_delete: :cascade, null: false

      column :text, String, null: false

      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false
    end    
  end
end

Most of the code above is familiar - we define a new table called examples, set primary_key and some columns. The new thing here is foreign_key - we specify here a column that will link Example with a Word. By setting on_delete: :cascade we define how this entry should behave when associated Word will be deleted - in our case we want to delete Examples as well.

To run the migration type in the console:

bundle exec hanami db prepare

Now, let’s generate Example model. We will do this by typing:

bundle exec hanami generate model example

Next, let’s open WordRepository and define the association with Example here:

class WordRepository < Hanami::Repository
  associations do
    has_many :examples
  end

  def create_with_examples(data)
    assoc(:examples).create(data)
  end

  def add_example(word, data)
    assoc(:examples, word).add(data)
  end  

  def find_with_examples(id)
    aggregate(:examples).where(id: id).as(Word).one
  end
end

We have defined a has_many association between Word and Example models and we have added three new methods:

  • create_with_examples for creating Word with Example at one go
  • add_example for adding Example to an existing Word
  • find_with_examples for retrieving Word with all Examples preloaded

Summary

Today we’ve stepped out of basic CRUD app and we’ve extended our app with the first association. In the next post, we will prepare our frontend for adding and displaying words with associated examples.

Written on May 8, 2017
>