2

I'm writing my first rather simple Ruby on Rails (3.2) application. The idea is to manage email accounts. The schema is rather simple:

t.string   "email"
t.string   "password"

The ActiveRecord model is defined as:

class Mailuser < ActiveRecord::Base
  attr_accessible :email, :password

I've been using scaffolding with all the form_for(@mailuser) and @mailuser.update_attributes(params[:mailuser]) magic. That works well.

Now the quirk is that the user should be allowed to only edit the user part (e.g. "foo") of an email while the domain (e.g. "example.org") must stay untouched. The email field however contains the complete email address (e.g. "foo@example.org"). So in the edit form I don't display the fields

  • email
  • password

but rather

  • userpart
  • password

where the userpart is shown as @mailuser.userpart (editable) + domain (not editable).

So I thought I'd solve that by adding a getter and setter for the user part like this:

class Mailuser < ActiveRecord::Base
   attr_accessible :email, :password

   # Getter for the user part (left of '@')
   def userpart
      email.split('@')[0]
   end

   # Setter for the user part (left of '@')
   def userpart=(new_userpart)
      email = email.sub(/(.+)@/, new_userpart+"@")
   end

In fact getting the @mailuser.userpart works well. And it's even displayed properly in the form_for(@mailuser). But a problem occurs when saving the form using @mailuser.update_attributes(params[:mailuser]) in the controller's update method. Rails apparently tries to update the @mailuser object from the attributes made available through attr_accessible. But the userpart attribute does not reflect an actual database field.

So I wonder what is the proper way to add such methods to ActiveRecord models to add functionality? I'm used to Pylons (a Python MVC framework) and I could use such additional methods all the way.

(In fact you wonder: I can't split the user and domain parts by changing the database schema. The database is used by other applications so I'm stuck with the way it looks.)

Thanks in advance.

  • can you post your form? – Zippie Apr 08 '13 at 23:26
  • The form is basically (sorry, stackoverflow doesn't allow it to be nicer) <%= form_for(@mailuser) do |f| %>
    <%= f.label :userpart %>
    <%= f.text_field :userpart %>@<%= @mailuser.domain.name %>
    <%= f.label :password %>
    <%= f.password_field :password %>
    <%= f.submit %>
    <% end %>
    – Christoph Haas Apr 09 '13 at 08:29

2 Answers2

2

You want to add attr_accessible :userpart so that userpart can be updated by update_attributes mass assignment.

Added: userpart is an example of a virtual attribute if you want to ask Mr. Google for a more detailed explanation.

Fred
  • 8,582
  • 1
  • 21
  • 27
  • Thank you - googling for _virtual attribute_ helped me a lot. Apparently I get working code if I replace `email = email.sub(/(.+)@/, new_userpart+"@")` by `self.email = email.sub(/(.+)@/, new_userpart+"@")`. I thought that _self_ refers to class attributes instead of instance attributes. So I don't understand why this works. But apparently it does. – Christoph Haas Apr 09 '13 at 08:37
  • You might want to see http://stackoverflow.com/questions/10464793/what-is-the-right-way-to-override-a-setter-method-in-ruby-on-rails – Fred Apr 09 '13 at 09:54
  • Thanks. That refers to overriding the behavior of attributes. I rather intended to add virtual attributes. But thanks for the link. – Christoph Haas Apr 09 '13 at 11:39
  • Yeah, I learned something about self and virtual attributes! Good question. – Fred Apr 09 '13 at 18:38
0

The key to the solution are virtual attributes. In my example the actual solution was:

  • assign self.email instead of email to change the value of the instance variable. Using just email just created a local variable. (I wasn't aware that leaving off the self is just a convenience feature of Ruby that can lead to confusion as in this case.)
  • use attr_accessible :userpart in the model

This is well explained in the (non-free) http://railscasts.com/episodes/16-virtual-attributes-revised?autoplay=true screencast. I wonder where virtual attributes are explained in the official Rails documentation.

  • I wonder though what the difference between `@email` and `self.email` is in the model. Is that a Rails specialty? Because I thought that both mean the same in Ruby classes. – Christoph Haas Apr 09 '13 at 12:13
  • http://stackoverflow.com/questions/5183664/why-isnt-self-always-needed-in-ruby-rails-activerecord – Fred Apr 09 '13 at 18:37