granite icon indicating copy to clipboard operation
granite copied to clipboard

Granite CLI?

Open watzon opened this issue 5 years ago • 4 comments

It would be beyond awesome to have a CLI for Granite that allows for easier setup and database management. I know right now Granite::Migrator is kind of hidden and mostly for testing, but if we could make it more useful and expose it via a CLI I think it could really change things. Right now the only ORM (that I know of) with a decent CLI is Avram, but it's pretty well integrated into Lucky and requires the full Lucky CLI rather than having its own thing.

Ideally a good CLI should include the ability to:

  • Create and drop a database
  • Create migrations and models
  • Run migrations
  • Do some kind of database seeding

Any thoughts on this? Personally I would find it useful for tourmaline. I could probably work on a PoC.

watzon avatar Jun 13 '20 19:06 watzon

@watzon Amber's CLI has all of the functionality you mentioned and uses Granite. amber db drop create migrate seed and amber generate model Post title:string body:text published:bool are the commands to do this. It uses micrate under the covers. You might be able to use it as a standalone solution.

drujensen avatar Jun 13 '20 19:06 drujensen

@watzon I researched this a little more and the Amber CLI has some dependencies that make it a little difficult to use in stand-alone mode. We might be able to remove some of these dependencies to make this more usable.

Here are the steps to use it as a standalone if you are interested.

Install amber and create a new crystal app:

brew install amber
crystal init app test
cd test

add dependencies to shard.yml:

dependencies:
  granite:
    github: amberframework/granite
    version: ~> 0.21.0
  mysql:
    github: crystal-lang/crystal-mysql
    version: ~> 0.11.1

Install dependencies:

shards install

Create config/application.cr:

require "granite/adapter/mysql"

Granite::Connections << Granite::Adapter::Mysql.new(name: "mysql", url: "mysql://root@localhost:3306/test")

Require it in the src/test.cr application:

require "../config/application.cr"

Create an empty config/environments/development.yml. I think we can remove this dependency:

# empty file

Generate a model:

amber g model User name:string address:text city:string  zipcode:string age:integer admin:bool

Create database and migrate:

amber db create migrate

I found a bug that the migration logs are not spitting out but they run. I will add a bug for that in the amber project.

drujensen avatar Jun 13 '20 20:06 drujensen

Right, I'm using micrate right now, I'm just not a fan of having to write the SQL queries by hand. The main issue is that the queries aren't always compatible with different adapters because of differences in syntax, datatypes, etc. So if you change the adapter you also have to change all your migrations as well. That's something that could be fixed by creating an abstraction in the form of a "Migration" class that lets you define a schema, and then have it decide the best way to transform that into a migration for the given adapter. It's also just so much more user friendly.

The main reason I don't want to use the amber cli right now is exactly the reasons you mentioned. It, like the lucky cli, is pretty tightly integrated into the framework. If Avram was a little more free standing and had a way to handle migrations without using the lucky cli I'd be using it, but unfortunately it has the same issues.

watzon avatar Jun 13 '20 20:06 watzon

I see. Unfortunately, the Migrator concept you described will not work in a real production environment. I built this in Granite about three years ago.

It would automatically look at your current schema and generate the proper DML to migrate it to the defined schema in your granite model objects. You would run the migration and the tool would add any new fields for you. It would also migrate the data for you if the name of the field was the same. Next, you would be responsible for migrating data between fields that were renamed. Finally, you would run the migration again but with a --destroy flag to remove any unused fields. It was an extremely DRY solution.

Sounds great, right?

The problem was that databases just don't work this way in the real world. You may have migrated your test database but your beta or production db may be 2 or 3 migrations old. You wouldn't know all the transformations that took place during development without some sort of migration scripts that described the steps. There is no way to know the intent of the developer without migration scripts that I know of.

I found that in practice, migration scripts are a better solution. If you are interested, I can dig up the old code and let you take a swing at it.

Regarding using DSL vs SQL DML, I understand the desire for cleaner code. That is a big factor in easier maintenance. I enjoy rails DSL for migrations and it is clean and easy to read. This is a really important to me. However, I also find myself constantly trying to remember how to define a create statement using the DSL. It's a toss up if I'm more productive using one vs the other.

Finally nothing disturbs me more than working with dev's that don't know SQL. They have never learned how a relational database works. They forget indexes and don't know how to get something into a proper normal form. They don't know how to prevent data integrity issues and never define foreign keys. All of this is because Rails makes the migration scripts DSL too simple and hides the complexity from the developer. Maybe it's just anecdotal evidence and I may be over reacting, but it's disturbing to me.

drujensen avatar Jun 13 '20 20:06 drujensen