Skip to content

Can't set custom non-db attributes using ActiveRecord::Base#[]= #4583

Closed
mptre opened this Issue Jan 21, 2012 · 8 comments

8 participants

@mptre
mptre commented Jan 21, 2012

This is probably not classified as an issue but I did notice a change in behavior after upgrading a Rails app from 3.1 to the newly released 3.2 version.

After running my test suite lots of errors occurred while trying to set a custom non-db attribute value using the ActiveRecord::Base#[]= method. See example below:

class User < ActiveRecord::Base
  # custom non-db attribute
  attr_accessor :activated

  before_save -> { self[:activated] = true if token? && !activated_at? }

  def activated?
    !activated.nil?
  end
end

The before_save lamba will throw the follwing exception:

ActiveModel::MissingAttributeError: can't write unknown attribute `activated'

Was this change of behavior intended with the 3.2 release?

Any clarity into this matter would be much appreciated!

@deltadd
deltadd commented Jan 23, 2012

Same issue.

@smclelland

Same issue here. Must have changed.

@bradphelan

Triggers this issue in my gem rocket_tag. My issue is tracked here

bradphelan/rocket_tag#2

My error manifests itself in the following way

class Foo < ActiveRecord::Base
    def bar=v
        @bar = v
    end
end

Foo.new :bar => 10

which used to work in Rails 3.1 though according to a close reading of the doc it should not have worked.

new(attributes = nil) {|self if block_given?| ...}

New objects can be instantiated as either empty (pass no construction parameter) or 
pre-set with attributes but not yet saved (pass a hash with key names matching the 
associated table column names). In both instances, valid attribute keys are determined 
by the column names of the associated table — hence you can‘t have attributes that
aren‘t part of the table columns.
@ryanhattam

Seems to stem from this commit
50d395f

@josevalim
Ruby on Rails member

The example in the issue description should no longer work but the example given by @bradphelan should work as he's using a virtual attribute. /cc @jonleighton do you agree?

@jonleighton
Ruby on Rails member

@josevalim I cannot repro @bradphelan's issue with the following:

gem 'rails', '3.2.0'
require 'active_record'

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

ActiveRecord::Schema.define do
  create_table :posts
end

class Post < ActiveRecord::Base
  attr_accessor :foo
end

p Post.new(:foo => "bar").foo

Maybe @bradphelan can help us with steps to reproduce for that.

Regarding the original issue, I should have added a deprecation I guess - just didn't really think to. I will add a deprecation to 3-2-stable.

@fredngo
fredngo commented Jul 27, 2012

So, moving forward, what is the correct way to write virtual attributes that are not DB-backed? I'm encountering this deprecation warning when working with virtual attributes.

@mptre
mptre commented Jul 29, 2012

The following snippet works for me without any deprecation warnings. Currently running Rails 3.2.6.

class User < ActiveRecord::Base
  attr_accessor :activated

  before_save -> { self.activated = true if changed? }
end
@nickveys nickveys referenced this issue in ErwinM/acts_as_tenant Feb 18, 2014
Merged

Rails 3.2 write_attribute fix #2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.