Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top