简体   繁体   中英

How to read attribute only if it is present?

I am trying to display a model attribute only if it is present. If it is not, then a placeholder should be displayed. This is what I've got:

class Person < ActiveRecord::Base

  def name
    if self.name.blank?
      "[You have no name yet]"
    else
      read_attribute(:name)
    end 
  end

end

However, I am getting a stack level too deep error.

How can this be done?

Thanks for any help.

I agree with Ishank but you can call super to use Rails' getter and then use ActiveSupport's presence method which will return the value if it is present? or otherwise return nil (which will trigger the statement after the || ).

def name
  super.presence || "[You have no name yet]"
end

To be clear, stack level too deep is happening because you are checking self.name.blank? - when you use self.name here, that is calling the name method on self (which is the method you are currently in) - so that results in an infinite loop.

This should not be a part of Model. You should write this method in your views. You can have something like @person.name || "You have no name yet" @person.name || "You have no name yet"

You are getting exception stack level too deep because read_attribute[:name] again calls the name method.

Also a thing to keep in mind for using self . According to Ruby style guide :

Avoid self where not required. (It is only required when calling a self write accessor.)

# bad
def ready?
  if self.last_reviewed_at > self.last_updated_at
    self.worker.update(self.content, self.options)
    self.status = :in_progress
  end
  self.status == :verified
end

# good
def ready?
  if last_reviewed_at > last_updated_at
    worker.update(content, options)
    self.status = :in_progress
  end
  status == :verified
end

Can you try this for stack too deep?

def name
    if self.first.name.blank?
        "[You have no name yet]"
    else
        read_attribute[:name]
    end 
end

Personally, i always do something like this, using self[:name] as a way of accessing the database rather than the .name method:

def name
  if self[:name].blank?
    "[You have no name yet]"
  else
    self[:name]
  end 
end

I am still using rails 2.2, so this may function differently for you.

Having said that, a cleaner and more transparent way to do it is to set "[You have no name yet]" as the default value for the name column in the database. Then you don't need to override the accessor method, which always feels a bit dirty to me.

It is not a good practise to include presentation logic in data models.

You should instead use decorators, view objects, or similar, or just do it in the view, but not in the model.

Examples using the Draper gem:

class PersonDecorator < Draper::Decorator
  delegate_all

  def name
    object.name.presence || I18n.t('warnings.no_name_yet')
  end
end

In the view:

<%= @person.name.presence || I18n.t('warnings.no_name_yet') %>

See the "Introduce View Objects" section in http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM