views:

331

answers:

2

What I'm trying to do is the following:

At anyone time a user can have 1 active profile. This active profile must be authorized by an administrator to make sure that it is compliant with the rules and regulations of the site. When a user edits their profile their public profile is unaffected until the administrator signs off their changes. If they make an edit while their profile is in review, their edits are applied to the outstanding profile for review and is pushed to the back of the queue.

My models look something like this:

class Profile < AR:B
    belongs_to :user
end

class User < AR:B
    has_many :profiles do
        def active
            ...
        end
        def latest
        end
    end
    def profile
        self.profiles.active
    end
end

There is one small twist... the user should not be able to edit the profile directly, as the profiles collection is not exposed. Instead they edit their user and the profile fields are shown there.

What's the best way to manage this? Currently I'm using:

accepts_nested_attributes_for :profiles

In the user, but that seems quite hacky. Ideally most of this logic would live in the model, but the other thing I'm flirting with is the use of a presenter.

Any thoughts would be greatly appreciated, let me know if you need more information as a comment and I'll update this post appropriately.

+2  A: 

Maybe you should try having two relationships from user to profile. One is the one they can edit through your User interface, and the other is the one that is approved by the administrator.

It could work something like:

class User < AB:B

has_one :profile #the user-editable one one
has_one :active_profile, :class_name=>"profile" #the one shown

end

Each changes on the User profile through the form would then show for the Admin (using and observer or maybe just and "after_save" filter). When it aproves it, the changes are then dumped to the active_profile one, and shown somewhere.

That way, you can have a clean form interface, and whenever they edit it again, they see the latest (but not approved) profile. You can also order the Queue using the updated_at column to gain the "their edits are applied to the outstanding profile for review and is pushed to the back of the queue" funcionality.

Yaraher
Originally I thought of doing it this way, but dismissed it because I thought it wasn't ideal, however your write up has convinced me that it's the right way to do it and will most likely make my view and the logic in the model simpler.
jonnii
I should probably have mentioned that in this circumstance the FK will need to be on the user model to differentiate between the two profiles, so it'll be a belongs_to not a has_one.
jonnii
Yes, you are correct on that one. Although a bit wonky on the semantics, BD-related that would be correct.
Yaraher
+1  A: 

I'd go about this by having the user Model have a relationship with two profiles as suggested above as well. One "Approved" profile, and the one for editing that goes into your admin queue.

However, in order to handle transitions between "pending" profiles and "approved" profiles i'd suggest possibly adding in a State Machine to handle the transitions. The AASM gem has been good for me in a recent project. (http://github.com/rubyist/aasm/tree/master), and I believe Edge Rails has just baked State Machinage right in as well. (http://github.com/rails/rails/commit/aad5a30bf25d8a3167afd685fc91c99f4f09cc57)

Your model could look something like this:

class User < AR:B

has_one :active_profile 
has_one :pending_profile

include ActiveRecord:: StateMachine

state_machine do
   state :approved
   state :pending
   state :rejected

   event :update_profile_pending do
    transitions :to => :pending, :from => [:approved], :on_transition => :send_to_admin_queue
  end

   event :update_profile_approved do
    transitions :to => :approved, :from => [:pending], :on_transition => :update_current_profile
   end

   event :update_to_rejected do
    transitions :to => :rejected, :from => [:pending]
  end
end

def send_to_admin_queue
  //method to handlesending pending profiles to admin for approval
end

def update_current_profile
 //method to handle updated profiles with changes
end

end

You could then call User.update profile pending! or User.update profile approved! to transition between your profile states and use the transition callbacks to handle sending the editing data between your active profile and pending profile.

As far as using the nested_attributes_for with your actual form, I don't think it's a hack, I've used it as well to update nested attributes and it'd work fine. In this case though you may not need too since you have 2 profiles (one public, one pending).

Just an idea! Thinking out loud here!

Nick L
Thanks for the answer. I saw that the state machine stuffed got rolled into edge... I'm really looking forward to rails 3.0!
jonnii