I am currently working with a legacy DB in MSSQL and at the same time preparing a future migration to PostgreSQL.
The table has the following structure in MSSQL
| Table name | Column name | Column type |
|---|---|---|
| CatGeneral | ||
| CatalogID | varchar(20) | |
| ItemID | varchar(80) | |
| ItemDescription | nvarchar(255) | |
| transactionDate | datetime | |
| lastUpdateDate | datetime | |
| userId | varchar(3) | |
| status | varchar(3) | |
| statusWeb | varchar(1) |
and the following structure in PostgreSQL
| Table name | Column name | Column type |
|---|---|---|
| catgeneral | ||
| catalogid | character varying(20) | |
| itemid | character varying(80) | |
| itemdescription | character varying(255) | |
| transactiondate | timestamp without time zone | |
| lastupdatedate | timestamp without time zone | |
| userid | character varying(3) | |
| status | character varying(3) | |
| statusweb | character varying(1) |
As you can realize the table and column names are not in the format normally used in Rails or Hanami apps.
The first step in Hanami is create a model
$ hanami generate model CatalogItem
If I open a hanami console the follwing error appears:
$ hanami c
[ROM::Relation[CatalogItems]] failed to infer schema. Make sure tables exist before ROM container is set up. This may also happen when your migration tasks load ROM container, which is not needed for migrations as only the connection is required (schema parsing returned no columns, table "catalog_items" probably doesn't exist)
Hanami expects a table named catalog_items but our table name is CatGeneral. To tell Hanami to use this table name we have to modify CatalogItemRepository class as follows
class CatalogItemRepository < Hanami::Repository
self.relation = :catgeneral
end
We can now open the console without any errors and create a new instance of the repository:
$ hanami c
2.5.1 :001 > repo = CatalogItemRepository.new
=> #<CatalogItemRepository relations=[:catgeneral]>
2.5.1 :002 >
And we can get the first record:
2.5.1 :004 > ap repo.first
[medik] [INFO] [2018-04-28 08:40:36 -0500] (0.000756s) SELECT "catalogid", "itemid", "itemdescription", "transactiondate", "lastupdatedate", "userid", "status", "statusweb" FROM "catgeneral" ORDER BY "catgeneral"."catalogid", "catgeneral"."itemid" LIMIT 1
{
:catalogid => "ALTA_POR",
:itemid => "0",
:itemdescription => "Alta por:",
:transactiondate => 2018-03-18 20:22:45 UTC,
:lastupdatedate => 2018-03-18 20:22:45 UTC,
:userid => "X",
:status => "A",
:statusweb => "A"
}
=> nil
Notice that we are using the excellent awesome_print gem to display the record using ap repo.first. It looks great in your console.
However the attribute names are not in snake_case. To fix this we use a schema mapping in the repository.
class CatalogItemRepository < Hanami::Repository
self.relation = :catgeneral
mapping do
attribute :catalog_id, from: :catalogid
attribute :item_id, from: :itemid
attribute :item_description, from: :itemdescription
attribute :transaction_date, from: :transactiondate
attribute :lastupdate_date, from: :lastupdatedate
attribute :user_id, from: :userid
attribute :status, from: :status
attribute :status_web, from: :statusweb
end
end
Now the attribute names are nicer.
2.5.1 :002 > ap repo.first
[medik] [INFO] [2018-04-28 08:49:14 -0500] (0.001329s) SELECT "catalogid", "itemid", "itemdescription", "transactiondate", "lastupdatedate", "userid", "status", "statusweb" FROM "catgeneral" ORDER BY "catgeneral"."catalogid", "catgeneral"."itemid" LIMIT 1
{
:catalog_id => "ALTA_POR",
:item_id => "0",
:item_description => "Alta por:",
:transaction_date => 2018-03-18 20:22:45 UTC,
:lastupdate_date => 2018-03-18 20:22:45 UTC,
:user_id => "X",
:status => "A",
:status_web => "A"
}
=> nil
It is interesting to notice that we have to include a maping for all the columns in our table even though there is no need to change the name, as is the case of column status.
Not including all the columns causes an error when loading the console.
Hanami expects the primary key to be an integer, however in this case it is a string so we need to provide a custom schema in our entity class.
class CatalogItem < Hanami::Entity
attributes do
attribute :item_id, Types::String
end
end
However this has a side effect, only the declared attributes are part of our entity class.
2.5.1 :002 > ap repo.first
[medik] [INFO] [2018-04-29 14:58:38 -0500] (0.001380s) SELECT "catalogid", "itemid", "itemdescription", "transactiondate", "lastupdatedate", "userid", "status", "statusweb" FROM "catgeneral" ORDER BY "catgeneral"."catalogid", "catgeneral"."itemid" LIMIT 1
{
:item_id => "0"
}
=> nil
So we have to declare all the required attributes again.
Since this is a legacy database, this behavior allows us to select which attributes will be part of our Entity leaving out columns that are not used anymore.
I have to investigate the following:
- How can we provide default values for fields not declares in the Entity class?
- How can we create records that use string uuids as primary keys?