Rails 4 adds (and enables by default) partial inserts to ActiveRecord. This means saving a new record will only write to columns believed to be changed. (CHANGELOG extract below).
state_machine sets its initial value in a way which bypasses ActiveRecord's change/dirty checking, and so that column is not included in the insert.
For a nullable column, this results in a null being stored instead of the initial value.
For a not-null column, this results in an insert error.
Proposal
state_machine should set the initial value such that ActiveRecord sees it as a changed attribute.
Does this sound sane? If so I'm happy to look into the implementation.
Reproduction
rails (4.0.0.beta1)
state_machine (1.2.0)
pg (0.15.1)
# Migration
class CreateWidgets < ActiveRecord::Migration
def change
create_table :widgets do |t|
t.string :status, null: false
t.timestamps
end
end
end
# Model
class Widget < ActiveRecord::Base
state_machine :status, initial: "new"
end
# Without NOT NULL constraint.
w = Widget.new
w.status # => "new"
w.changes # => {}
w.save # => true
w.status # => "new"
w.reload.status # => nil <-- vanished!
# With NOT NULL constraint.
Widget.create! # => ActiveRecord::StatementInvalid:
# PG::Error: ERROR:
# null value in column "status" violates
# not-null constraint
-- Generated SQL in both cases:
INSERT INTO "widgets" ("created_at", "updated_at")
VALUES ($1, $2)
RETURNING "id"
When inserting new records, only the fields which have been changed from the defaults will actually be included in the INSERT statement. The other fields will be populated by the database.
This is more efficient, and also means that it will be safe to remove database columns without getting subsequent errors in running app processes (so long as the code in those processes doesn't contain any references to the removed column).
The partial_updates configuration option is now renamed to partial_writes to reflect the fact that it now impacts both inserts and updates.
Update: fixed in rails/rails#9489
Rails 4 adds (and enables by default) partial inserts to ActiveRecord. This means saving a new record will only write to columns believed to be changed. (CHANGELOG extract below).
state_machine sets its initial value in a way which bypasses ActiveRecord's change/dirty checking, and so that column is not included in the insert.
For a nullable column, this results in a null being stored instead of the initial value. For a not-null column, this results in an insert error.
Proposal
state_machine should set the initial value such that ActiveRecord sees it as a changed attribute.
Does this sound sane? If so I'm happy to look into the implementation.
Reproduction
ActiveRecord CHANGELOG
From the ActiveRecord CHANGELOG: