Why do generators let me make mistakes on column types?

My turn to file a WTF. I’ve been meaning to fix this for a while, but never get around to it. If you do this: bin/rails g scaffold user string:name Rails will generate a scaffold for user with a column named “string” and a type of “name”.

I just did it now on a test app:

[aaron@tc-lan-adapter ~/g/no-fallback (master)]$ be bin/rails g scaffold user string:name
      invoke  active_record
      create    db/migrate/20200529182157_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    resource_route
      invoke    test_unit
      create      test/controllers/users_controller_test.rb
      create      test/system/users_test.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/users/index.json.jbuilder
      create      app/views/users/show.json.jbuilder
      create      app/views/users/_user.json.jbuilder
      invoke  assets
      invoke    scss
      create      app/assets/stylesheets/users.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss
[aaron@tc-lan-adapter ~/g/no-fallback (master)]$ 

If we cat the migration, it’s clearly wrong:

$ cat db/migrate/20200529182157_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :users do |t|
      t.name :string

      t.timestamps
    end
  end
end

Why does the generator let me do this? We should be able to detect that “name” isn’t a valid column type and stop the generators before they make a bunch of files that are now wrong.

15 Likes

:smile: I’ve run into this as well https://github.com/rails/rails/pull/39478

3 Likes

Happens to me a LOT.

The other one that I always seem to mess up is: references:user

1 Like

Yes :slight_smile: , although I usually use the alias belongs_to: belongs_to:user

That’s why I’m grateful for
bin/rails d scaffold user
It is an extra step. :slight_smile:

2 Likes

Happens to me all over the Rails ecosystem. Makes me think the problem is with me — that I need a language/framework that validates user input; that fails fast and clearly (not silently proceeds).

But so many people really enjoy the Ruby/Rails ecosystem as is, I think I’m not a good fit.

1 Like

I think that, given that the OP is a Rails maintainer, it’s pretty safe to conclude that it has less to do with “good fit” and more to do with not enough attention being paid to keeping folks from stubbing their toes!

2 Likes

For me it’s just been “oops, I’ll fix Rail later”, but I run across this issue so infrequently that I forget. At an individual level, I’m sure people don’t frequently use generators, but on aggregate I’m sure this is a common issue.

Anyway, thanks @petrik for taking a stab at this!

1 Like

One thing that’s kind of interesting is that the example of invoking the generator with the incorrect order of <column_type>:<column_name>

bin/rails g scaffold user string:name

ends up producing t.name :string in the migration file where the order is reversed (t.<column_name> :<column_type>).

Perhaps a reason people might think to run bin/rails g scaffold user string:name is that they’re thinking of <column_type>, <column_name> in the order the migration should show (t.string :name) (and they don’t realize the generator expects the opposite order).

3 Likes

The ordering consistencies is an interesting thing to bring up. There are several different orders that one can write depending on the notation one uses:

add_column :table, <column_name>, <column_type>

(which also is the same order when using change_column)

And is the same order as the generic t.column …:

t.column <column_name>, <column_type>

But then gets reversed in the “short-hand” version…:

t.<column_type> <column_name>

…which was introduced back in 2007: Added short-hand declaration style to migrations (inspiration from Se… · rails/rails@4cbbebb · GitHub

The inconsistencies seem more difficult to straighten out than improving the warnings :+1:

3 Likes

Oh my god, I thought I’m only one who suffering from it. I always thought why having thor gem we have so inconsistent console tooling. Same with hidden rake -T commands. For example it doesn’t shows up rake db:migrate:reset

2 Likes

@woto You are not alone :raised_hands: I think the hidden/deliberately-undocumented rake tasks perhaps deserve their own May of WTFs post.

4 Likes

I suppose it would be fair to restrict generators to only known types, but keep in mind that Postgres et. al. support custom column types.

Database extensions like PostGIS/activerecord-postgis-adapter define custom column types as well, so if Rails does start enforcing known column types for migration generators, it would be nice to have a defined interface for gems to “register” new ones, similar to how ActiveRecord::Type works.

2 Likes