Very nice!
When the full_name has no spaces, the first_name and last_name will become equal! Is it possible to add an extra validation rule onto the virtual attribute?
Yep, you can treat it just like a normal attribute and add a validation like this:
--
def validate
validate.errors.add(:full_name, 'must have a space') unless full_name.include? ' '
end
--
However, the problem is the getter method may not represent exactly how the user typed it. So it's best to store the user submitted data in an instance variable so you can return it in the getter method when it's set.
how easy would it be to add an attribute that all activerecord objects would have?
If you put the methods in ActiveRecord::Base they will be available to all models. You can do this through a plugin which I show in this episode:
http://railscasts.com/episodes/33
If you want to do this the other way around. Say fullname in database (just example) and you want to show first/last name. Do you add the get/set for first+lastname and merge them with a before save hook?
@Tom, yep, that's how I would do it.
Is there some way of having (virtual) attributes passed from the view without defining a method in the model? I.e. I have several virtual attributes that the user enters into a form in the view, and then in the model I use a method to query a web service before saving the results from that web service to the actual db (i.e. method is called with :before_save).
Ok I'm a newb but I'm a persistent one, so I found out after a while. Here it is, in case someone else stumbles on it. Just do:
attr_acessible :some_virtual_attribute
attr_accessor :some_virtual_attribute
etc
which I guess makes it seem like a regular instance variable, which can be accessed plainly as 'some_virtual_attribute' or with self.some_virtual_attribute.
How could I get this to work with middle names?
IOW, give the user the option to enter either an initial, or a full middle name.
My guess is that some Regex might be needed, but perhaps there is a cleaner way
Intersting that you didn't show the controller at all. I guess when you use form_for the parameters are automatically associated with the model object, not just named after the model object?
If so, why would the controller still do something like @user.update_attributes(:parmams) ? (or do you avoid that step somehow?)
Does the virtual attribute change the params? I'm confused about how this is working.
doh, I think I got it.
I guess the virtual object setters get called when Rails trys to create a new object in the controller using the full_name attribute. It happens after it hits the controller, not before it (or maybe during the controller create action is a better way of thinking about it).
Is there a difference between setting method headers like this:
def full_name=(name)
and
def full_name(=name).
Thank you!
@Vincent
I've tried both in the irb and the second one doesn't work:
irb(main):018:0> def full_name(=name)
irb(main):019:1> end
SyntaxError: compile error
(irb):18: syntax error, unexpected '=', expecting ')'
The other (def full_name=(name)) works without problems
As mentioned above, the simple snippet will turn "Franklin Delano Roosevelt" into ["Franklin", "Delano Roosevelt"]. Here's a snippet which takes the last non-whitespace as the last name:<pre><code>
def clean(n, re = /\s+|[^[:alpha:]\-]/)
return n.gsub(re, ' ').strip
end
# Returns [first_name, last_name] (or '' for first name if there isn't one).
# Leading/trailing spaces and non-alpha non-dashes ignored.
def first_last_from_name(n)
parts = clean(n).split(' ')
[parts.slice(0..-2).join(' '), parts.last]
end</code></pre>
However, as someone who can't check in at the automatic kiosks in airports because the credit card thinks my last name is "IV", I <a href="http://vizsage.com/blog/2008/01/parsing-names-with-honorifics.html">wrote a version that works with honorifics</a> (like 'Esq.' or 'Jr.'): http://vizsage.com/blog/2008/01/parsing-names-with-honorifics.html
Oof -- I tried pasting in code and it came out unreadable. Sorry.
For a method that will extensibly handle multiple names and names with honorifics like 'Esq.' or 'Jr.', please see:
http://vizsage.com/blog/2008/01/parsing-names-with-honorifics.html
I'm having issues getting product.pictures.update_attributes() to work sans-javascript.
I have a radio button next to each image on the product editor page, but I am only able to get it to work if i explicitly send a value of 1 or 0 for "is_default" flag for each picture.
I'd like to know if there is a means of doing this w/o requiring javascript.
How would this be different if you wanted to map two virtual attributes to one real attribute? In this example, how would the code change if your database stored full_name and you wanted separate virtual attributes for first_name and last_name?
@just_curious
Maybe the below could work? May require further refactoring...
def first_name
full_name.split(' ', 2)[0]
end
def first_name=(name)
first_last_name(name + ' ')
end
def last_name
full_name.split(' ', 2)[1]
end
def last_name=(name)
first_last_name(' ' + name)
end
def first_last_name(name)
if self.full_name.empty?
self.full_name = name
else
buffer = []
self.full_name.split(' ').zip(name.split(' '), 2) { |old_name, new_name|
buffer << (new_name.nil? ? old_name : new_name)
}
self.full_name = buffer.join(' ')
end
end
Oh this version should also work and simpler...
def last_name()
self.full_name.split(', ')[0]
end
def last_name=(x)
lname, fname = self.full_name.split(', ')
self.full_name = [x.strip, fname].join(', ') unless lname == x.strip
end
def first_name()
self.full_name.split(', ')[1]
end
def first_name=(x)
lname, fname = self.full_name.split(', ')
self.full_name = [lname, x.strip].join(', ') unless fname == x.strip
end
Thanks for this suggestion -- in my unit tests for this it fails if 'name' in fullname=(name) is a single word. It doubles the word. So you'd get user.fullname = 'Joe' and user.fullname => 'Joe Joe'!
Great video! In relation to testing, is it possible to use virtual attributes within fixtures? How does one get around this?



