views:

180

answers:

1

I have attribute name of model Person.

I want to use html-form with fields: first_name and surname:

<%= f.text_field first_name%>
<%= f.text_field surname%>

And I want to compose these virtual attributes to model attribute name.

What's the best way to do it?

I tried to use composed_of, but failed...

class Person < ActiveRecord::Base
  composed_of :name, :class_name => 'Name', :mapping => %w(name output_name)
end

class Name
  attr_reader :output_name
  def initialize(first_name, surname)
    @output_name = first_name + surname
  end
end

@person.attributes= {"name(1s)" => 'Alex', "name(2s)" => 'Bolduin' }
@person.name.should == 'Alex Bolduin'

expected: "Alex Bolduin",
got: #<Name:0x000000049d4c08 @output_name="Alex Bolduin"> (using ==)

I use Ruby on Rails 3, but I think solution is similar for Rails 3 and Rails 2.3

+1  A: 

You should be able to make a pretend column doing something like this in the model:

class Person < ActiveRecord::Base
    def name
      "#{self.first_name} #{self.surname}"
    end

    def name=(fullname)
      first,last = fullname.split(' ')  # or some smarter way to split
      self.first_name = first
      self.surname = last
    end
end

It should be accessible the same way as any other column, and when you try to set name it will just call the setter methods for first_name and surname.


If you want to do the opposite, you can still use the same trick:

class Person < ActiveRecord::Base
    def first_name
      name.split(' ')[0]
    end

    def surname
      name.split(' ')[1]
    end

    def first_name=(first)
      name = [first, name.split(' ')[1]].join(' ')
    end

    def surname=(sname)
      name = [name.split(' ')[0], sname].join(' ')
    end

end

The only thing potentially bad about this is that string manipulation occurs once for each read and twice for each write. You could alternatively catch things in the controller by adding something like this to create/update before @person.save:

@person.name = [first_name, surname].join(' ')
Karl
I want to do opposite thing, i want to first_name= and surname= to make composite name
petRUShka
so the composite name is the one stored in the database?
Karl
yes, only name.
petRUShka
Updated my answer to describe the opposite case. Looking at your original error though, you should be able to get the expected output by doing `@person.name.output_name`, and your test would be `@person.name.output_name.should == 'Alex Bolduin'`.
Karl