0

I'm really confused about virtual attributes in Rails 3.2 and all my research haven't help making things clearer.

# input model
class Input < ActiveRecord::Base

  # Attributes --------------------
  attr_accessible :title, :parent
  attr_accessor :parent

  def parent=(id)
    wrtite_attribute(:parent, id.to_i)
    self.parent = id.to_i
    self[:parent] = id.to_i
    @parent = id.to_i # seems to be the only one working. Why?
  end

end

# inputs controller
class InputsController < ApplicationController

  def new
    @input = Input.new({
      start_date: @current_scope_company.creation_date,
      parent: 'bla'
    })

    @input.parent = 'bla'
    @input[;parent] = 'bla'

  end
end

# inputs table
create_table "inputs", :force => true do |t|
  t.string   "title"
  t.datetime "created_at",                        :null => false
  t.datetime "updated_at",                        :null => false
end

Above, I have compiled pretty much all the alternatives I found on the internet. It is NOT the code I run, just couple versions of the same thing. Though, whatever I try, I get the following warning:

DEPRECATION WARNING: You're trying to create an attribute 'parent'. Writing arbitrary attributes on a model is deprecated. Please just use 'attr_writer' etc.

Sometimes, I even get a stack level too deep. I'd love to understand how attributes work.

1/ attr_accessor is attr_writer plus attr_reader right? Why am I asked to use attr_writer in the warning?

2/ How am I supposed to write attributes from the model (and why)

3/ How am I supposed to write attributes from the controller (and why)

Thanks a lot!

Update

After further test, it looks like the proper way to do it is @parent = id.to_i. I would still love to get explanation why. I'm really confused why self. wouldn't work.

karellm
  • 1,843
  • 1
  • 20
  • 23

1 Answers1

1

Atttr_accessor should be fine. So in your model, it looks like you're trying to do the same thing in four different ways:

def parent=(id)
  write_attribute(:parent, id.to_i)
  self.parent = id.to_i
  self[:parent] = id.to_i
  @parent = id.to_i            # <- all these are redundant and scary
end

(Furthermore, in your controller it looks like you're also trying to do the same thing in different ways, unnecessarily.) Also, I'm pretty sure the syntax 'def parent=(id)' is only for real attributes. Looking at this railscast and from my own experience, you just do something like this for a virtual attribute:

def parent
  @id.to_i
end

Where is "id" coming from that's supposed to go into 'parent'? Virtual attributes have to somehow related to something that's actually being stored in the db, either through a real attribute or through an association's real attribute. If you explain a bit more of what parent is actually supposed to be calculated from, it'd be easier to help.

ussferox
  • 490
  • 5
  • 10
  • This is not the code I run, just four exemples of the same thing I've run across. I'm just confused what is what and which to use. I watched this railscast, but it will still throw the deprecation warning. `self.parent` being the problem. – karellm Nov 23 '13 at 01:39
  • haha oh okay. so what exactly is parent supposed to be calculated from? where is id coming from? – ussferox Nov 23 '13 at 01:41
  • also, the reason youre getting the warning is parent=(id) tries to create a real attribute, which isnt what you want to do. try the method i gave above. – ussferox Nov 23 '13 at 01:42
  • parent is a field in my form that let's me define a relationship. It is an unconventional way to use relationships, but I'm kinda forced by the existing structure. It receives an id do some magic to set the relationship and that's it. I don't need it in the DB because the relation ship is already expressed somewhere else (it is not a belongs_to relationship). – karellm Nov 23 '13 at 01:46
  • In the railscast, price_in_dollar is not a real attribute. The real one is price_in_cents. parent= isn't technically limited to real attributes, it is a method like any other. attr_writer or attr_accessor is creating this function in the back from what I understand. – karellm Nov 23 '13 at 01:52
  • yeah so if its a relationship, parent isnt an attribute. parent_id would be the attribute (but thats not in your schema). why dont you do a hidden field like f.hidden_field :parent_id, :value => x.id. Or, merge the attributes when you're creating the input, like input_param.merge(parent: @parent) – ussferox Nov 23 '13 at 02:17
  • also just saw that its not a belongs_to so there wouldnt be a parent_id in the table. could you actually give your models and form so we have an idea of what you're talking about? – ussferox Nov 23 '13 at 02:24
  • Don't mean to be rude but it is not the point of my question. It is not important how/why I use it, I just want to understand the deprecation warning and how I can set virtual parameters in ruby/rails. – karellm Nov 23 '13 at 03:10
  • So my point is that I'm not convinced you're using virtual params correctly and there's probably a better way (but since you're not explaining the deetz, who knows). Did you even try the method I suggested in my answer? Furthermore, attribute=(incoming_attr) I believe does usually refer to ActiveRecord attributes - see http://api.rubyonrails.org/classes/ActiveRecord/Base.html; unless you elsewhere somehow define parent in the model, which it doesn't look like you do. And furthermore, if you explained the context, we could probably help you write a better method for parent anyway. – ussferox Nov 23 '13 at 08:03
  • anyway, check out this: http://stackoverflow.com/questions/12080868/setting-getting-virtual-attributes-in-rails-model. notice how when they do attr=(blah), they are also defining attr as a method itself. – ussferox Nov 23 '13 at 08:17
  • write_attribute is used in both your links, still it is deprecated. Your suggestion is a method that return a model attribute, not setting one. Again not my question. – karellm Nov 23 '13 at 13:54