Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override rails configurations in regard to pluralized tables and snake cased columns

So, I'm building an app where I have a backend written in Rails and a client written in Vue with Amplify. My database is MySQL and I'm using AWS AppSync with a GraphQL as data source (pointing to my database).

The AWS Amplify has a framework that allows me to generate the schemas based on the table names and columns with one simple command: amplify api add-graphql-datasource. But because I'm using rails migrations, my database is using Rails conventions: pluralized tables with snake cased columns.

Now, the problem with that is the GraphQL schemas are all ugly and not using the correct conventions (singular names for the types and inputs, with camel cased props). Example:

My backend has the following migration:

class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      t.belongs_to :site, null: false
      t.string :title
      t.string :url
      t.text :body

      t.timestamps
    end
  end
end

And the schema generated for this is:

type posts {
  id: Int!
  site_id: Int!
  title: String
  url: String
  body: String
  created_at: AWSDateTime!
  updated_at: AWSDateTime!
}

type Query {
  getPosts(id: Int!): posts
  listPostss: [posts]
  // ...
}

schema {
  query: Query
  // ...
}

Not to mention this:

input CreatepostsInput {
  id: Int!
  site_id: Int!
  title: String
  url: String
  body: String
  created_at: AWSDateTime!
  updated_at: AWSDateTime!
}

So, AWS Amplify is new, it's not mature as Rails, and on top of that I didn't find any adapter or transformer to handle the problem in the client... my hope is to find a way to handle it on Rails.

I need to be able to completely change the Rails conventions without breaking anything: migrations, associations, how to manage associations (create_xxx, build_xxx).

This app is really new, so I can recreate all the migrations from scratch.

Thanks

like image 863
Amanda Ferrari Avatar asked Nov 04 '19 19:11

Amanda Ferrari


1 Answers

I can see some things you can do:

Tables:

In your migration, you can change the table name, but now you need to let your model know what's the table name with self.table_name.

# migration
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :post do |t| # singular 'post'
      ...
    end
  end
end

#model
class Post
  self.table_name = "post" # i think you can also use a symbol :post
end

Attributes:

You need to avoid using Rails migration methods that follow Rails conventions like t.belongs_to or t.references or t.timestamps.

# migration
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :post do |t|
      # do not use this below
      # t.belongs_to :site, null: false
      t.bigint :siteId, null: false
      t.string :title
      t.string :url
      t.text :body

      # do not use this below
      # t.timestamps
      t.datetime :createdAt, default: ->{'CURRENT_TIMESTAMP'}
      t.datetime :updatedAt, default: ->{'CURRENT_TIMESTAMP'}
    end
  end
end

Relationships:

You also need to update your relationships in your models

class Post
  belongs_to :site, foreign_key: 'siteId'
end

More information can be found in the Rails API. Make sure you check the documenation for other relationship methods.

Timestamps:

Since timestamps columns (created_at, updated_at) are not the ones expected by ActiveRecord anymore, you might need to override the ActiveRecord::Timestamp module to have them continue working as you would expect. One of the easiest option is to update your ApplicationRecord or a single model class with the following:

class ApplicationRecord # or class Post
  before_create :set_timestamps
  before_save :set_timestamps

  private
  def set_timestamps
    self.createdAt = DateTime.current if self.new_record?
    self.updatedAt = DateTime.now
  end
end

Or this other option taken from https://stackoverflow.com/a/52276865/1845602

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  class << self
    private

    def timestamp_attributes_for_create
      super << 'my_created_at_column'
    end

    def timestamp_attributes_for_update
      super << 'my_updated_at_column'
    end
  end
end

Auto generated Inputs:

I might be wrong here, but it seems the table name is being injected into the input name like Create<table>Input, so if that's the case, you can name your table Post instead of post.

input CreatepostsInput {
}
like image 127
Edgar Ortega Avatar answered Nov 02 '22 15:11

Edgar Ortega