Auto_complete through User.name

I'm attempting an auto_complete by user.name, but it's not as simple as it sounds. I've followed the great Railscast on the topic, and I do have the ajaxiness working just fine when I search by ID (I was doing this for testing purposes - I don't want users using IDs to find each other, luckily). My auto_complete field needs to work the same as the GitHub 'Collaborator' addition form (or the Facebook search), whereby we find the user by user.name. My problem is that the user.name attribute is only a method in my user.rb, it is not a unique attribute, nor does it exist in the users table. So, we can't identify a user through it, we need the ID.

Further to this, the app has a Profile model for storing additional user info (I'm beginning to hate this design - is it really necessary? I'm only doing it because I was scared in to it from a security standpoint). The Profile model has first_name and last_name. The User model only has the :name attribute through the Profile model;

# user.rb def name   "#{profile.first_name} #{profile.last_name}" end

So, on the front-end, my auto_complete form needs to find the users by the user.name attribute, but when we select a user from the auto_completed results, we also need to have the user.id hidden away somewhere (a hidden field, perhaps?) whilst the user.name is displayed in the auto_completed_text_field as visual feedback for the end user.

Any ideas on how I achieve this?

model_auto_completer addresses this:

   http://agilewebdevelopment.com/plugins/model_auto_completer

I happen to be the author but I'd recommend it if I wasn't anyway.

Xavier Noria wrote:

I can help. Can you describe the User model and the form you want to autocomplete users in?

Xavier Noria wrote:

Thanks, Xavier. The description of the plugin states it would do the trick. I did come across model_auto_completer in my reading-rounding prior to installing auto_complete, but I couldn't find much in the way of example use cases. Are there any blog posts on it? I've tried Googling but I couldn't find anything that I (as a newbie) could follow along (I'm struggling to follow the docs on Rubyforge).

I can help. Can you describe the User model and the form you want to autocomplete users in?

Hi Xavier,

Sorry it's taken so long to get back to your kind offer! This feature was put at the back of the queue after I hit the problems outlined above.

The auto_complete is being used in combination with an Invitation model. Users invite each other to a Group and, right now, they select the recipients (the ones they have permission to send invitations to) using a select.

I was using the Railscast technique, the one without the controller before_filter. There was an auto_complete on the field in the view, the response was directed towards a 'recipients' action in the Invitation controller, this collected users by ID (seeing as I can't use user.name, yet) and had a js formatted response & template which rendered a mini user partial for each user, in @recipients, collected by the find.

I may be removing my Profile model altogether as, despite people saying how it can be cleaner and more secure, just makes the code more complex and introduces more finds for me (and makes accessing highly using User info, like we're doing here, potentially tricky). So I may have the user.name as a column in the Users table soon - but I still won't be able to use the name attribute as something that could set the 'recipient' of an invitation.

Do you have a blog post on how to do this? I struggled with the instructions because they didn't seem to tell me what I needed to do. e.g. I'm told to 'assume' the auto_complete_belongs_to_for_book_author_name, but is that something I need to install? This is something tons of people would want to use (there has to be quite a few apps out there that don't adhere to unique a 'login' or 'username' per user), so I'm sure that getting (gitting) it on GitHub, and doing a blog post on the 'finding by user.name and identifying by ID' scenario, would prove pretty popular.

The auto_complete is being used in combination with an Invitation model. Users invite each other to a Group and, right now, they select the recipients (the ones they have permission to send invitations to) using a select.

So that's the form of the invitation with possibly more fields such as the group ID and a message body? If yes, single or multiple recipients?

Can you send the relationships between the involved models User, Invitation, Group, More? I mean the relevant models and their related has_many/belongs_to.

e.g. I'm told to 'assume' the auto_complete_belongs_to_for_book_author_name, but is that something I need to install?

You need to install the plugin and, eventually, dependencies. That's explained in its documentation:

   http://model-ac.rubyforge.org/

Xavier Noria wrote:

The auto_complete is being used in combination with an Invitation model. Users invite each other to a Group and, right now, they select the recipients (the ones they have permission to send invitations to) using a select.

So that's the form of the invitation with possibly more fields such as the group ID and a message body? If yes, single or multiple recipients?

Can you send the relationships between the involved models User, Invitation, Group, More? I mean the relevant models and their related has_many/belongs_to.

e.g. I'm told to 'assume' the auto_complete_belongs_to_for_book_author_name, but is that something I need to install?

You need to install the plugin and, eventually, dependencies. That's explained in its documentation:

   http://model-ac.rubyforge.org/

Invitations only go to one recipient at a time.

Yes, everything else about the app works - it really is just this problem of a fetching users by @user.name. I'm literally just about to remove the Profile model that is associated with the User - e.g. @user.profile.first_name, @user.profile.last_name - so I'll soon have a user.name in the database soon). Right now, however, the User.rb contains a 'name' method which joins the profile.first_name and profile.last_name together to create @user.name (it doesn't work for me - hence why I'm going to remove it).

I was struggling with the instructions because the English was difficult for me to follow. From the plugin description, it sounds like this plugin was built for what I want, I just don't understand the docs. I appreciate it explains the methods but, unless I'm being stupid again, it doesn't seem to have a clear step by step installation, e.g. can I change how the model_auto_completer_result interprets my records in the view?

OK. I am going to assume there's an Invitation model that has a user ID in a field modelled as

   class Invitation       belongs_to :recipient, ...    end

Once the plugin(s) are installed and the application rebooted insert this into the invitation form:

   <%= belongs_to_auto_completer :invitation, :recipient, :name %>

In the current controller implement an action

   def auto_complete_belongs_to_for_invitation_recipient_name      @recipients = @current_user.friends.all(...) # whatever      render :inline => '<%= model_auto_completer_result(@recipients, :name) %>'    end

It doesn't matter whether users have a "name" attribute, it is enough they respond to :name for the inline template to work. Say

   class User      def name        [last_name, first_name].compact.join(", ")      end    end

I've written all of this off the top of my head, there may be some errors, you need to change the association name if needed, you may need to configure a route if the controller is restful... but you see the idea.

Xavier Noria wrote:

Invitations only go to one recipient at a time.

OK. I am going to assume there's an Invitation model that has a user ID in a field modelled as

   class Invitation       belongs_to :recipient, ...    end

Once the plugin(s) are installed and the application rebooted insert this into the invitation form:

   <%= belongs_to_auto_completer :invitation, :recipient, :name %>

In the current controller implement an action

   def auto_complete_belongs_to_for_invitation_recipient_name      @recipients = @current_user.friends.all(...) # whatever      render :inline => '<%= model_auto_completer_result(@recipients, :name) %>'    end

It doesn't matter whether users have a "name" attribute, it is enough they respond to :name for the inline template to work. Say

   class User      def name        [last_name, first_name].compact.join(", ")      end    end

I've written all of this off the top of my head, there may be some errors, you need to change the association name if needed, you may need to configure a route if the controller is restful... but you see the idea.

Thanks. I got round to removing the Profile model altogether and my @user.name attribute is created using a before_create and before_update that combines the @user.first_name & @user.last_name in the User.rb. So I have 'proper' :name column, now.

However, I've followed your instructions, and I'm seeing this;

'undefined method `reflect_on_association' for NilClass:Class'

And it's cited here: <%= belongs_to_auto_completer :invitation, :recipient, :name %> in my /invitations/_form.html.erb

I have this in the application.rb (this was just for testing purposes - I'll put it in the correct controllers later - there's a couple);

  def auto_complete_belongs_to_for_invitation_recipient_name     @recipients = current_user.subscribers     render :inline => '<%= model_auto_completer_result(@recipients, :name) %>'   end

Any ideas?

Thanks. I got round to removing the Profile model altogether and my @user.name attribute is created using a before_create and before_update that combines the @user.first_name & @user.last_name in the User.rb.

Do you know there's before_save?

However, I've followed your instructions, and I'm seeing this;

'undefined method `reflect_on_association' for NilClass:Class'

And it's cited here: <%= belongs_to_auto_completer :invitation, :recipient, :name %> in my /invitations/_form.html.erb

I guess @invitation is nil. The helper expects an actual Invitation instance to figure stuff about the association. @invitation = Invitation.new is enough.

I have this in the application.rb (this was just for testing purposes - I'll put it in the correct controllers later - there's a couple);

def auto_complete_belongs_to_for_invitation_recipient_name    @recipients = current_user.subscribers    render :inline => '<%= model_auto_completer_result(@recipients, :name) %>' end

Don't put it there, because methods inherited from ApplicationController are not actions by definition. By the same price put the method in InvitationsController.

Thanks a lot for this one. You just saved me a lot of time.

Mark

Xavier Noria wrote:

Thanks. I got round to removing the Profile model altogether and my @user.name attribute is created using a before_create and before_update that combines the @user.first_name & @user.last_name in the User.rb.

Do you know there's before_save?

However, I've followed your instructions, and I'm seeing this;

'undefined method `reflect_on_association' for NilClass:Class'

And it's cited here: <%= belongs_to_auto_completer :invitation, :recipient, :name %> in my /invitations/_form.html.erb

I guess @invitation is nil. The helper expects an actual Invitation instance to figure stuff about the association. @invitation = Invitation.new is enough.

I have this in the application.rb (this was just for testing purposes - I'll put it in the correct controllers later - there's a couple);

def auto_complete_belongs_to_for_invitation_recipient_name    @recipients = current_user.subscribers    render :inline => '<%= model_auto_completer_result(@recipients, :name) %>' end

Don't put it there, because methods inherited from ApplicationController are not actions by definition. By the same price put the method in InvitationsController.

Hi Xavier, sorry it's taken me so long to get back to this one. I'm now seeing this;

undefined method `model_auto_completer_result' for #<ActionView::Base:0x3d79058>

This is my controller (before_filter)

  def auto_complete_belongs_to_for_invitation_recipient_name     @invitation = Invitation.new     @recipients = current_user.friends     render :inline => '<%= model_auto_completer_result(@recipients, :name) %>'   end

This is my form;

<%= belongs_to_auto_completer :invitation, :recipient, :name %>

Any more ideas?

That was added in the last release (April 3).

Xavier Noria wrote:

Hi Neil, nope the current release is from April 3, you have the last release.

I wondered whether you were editing the text field and with the cursor still there (so the field does not loose focus) you inmediately hit the submit button.

-- fxn (from Berlin)

Xavier Noria wrote:

Xavier, do you mean I should check my plugin version is up to date? I downloaded it less than a week ago.

Hi Neil, nope the current release is from April 3, you have the last release.

I wondered whether you were editing the text field and with the cursor still there (so the field does not loose focus) you inmediately hit the submit button.

-- fxn (from Berlin)

Cool, that's one option down. I'll check my implementation again, but, the app was breaking before I got to the view - so I didn't see or click on the auto complete field. Obviously other people here have this working (is it 2.1 compatible?) so I'll give it another go later.

When I tried this (instead of using the model_auto_complete);

render :inline => 'hello world'

I got the 'hello world' in the view (so no more error messages) but it was the only thing in the view.

Yeah, it works with 2.1 for sure.

Xavier Noria wrote:

Neil Cauldwell wrote:

Xavier Noria wrote:

Obviously other people here have this working (is it 2.1 compatible?) so I'll give it another go later.

Yeah, it works with 2.1 for sure.

Thanks - the plugin is up to date, I know it works on my version of Rails, so I'll let you know how I get on.

Xavier, I'm almost there now, except that the render :inline => '<%= model_auto_completer_result(@recipients, :name) %>' is taking over all the view, rather than just rendering within the text field - so I'm only seeing the names of the potential recipients of the Invitation in the view, and nothing else ('hello world' style). This is how it is set up at the moment;

# /invitations/_form.html.erb

<% form_for :invitation, :url => group_invitations_path(@group) do |f| %>   <%= belongs_to_auto_completer :invitation, :recipient, :name %>   <%= f.submit 'Send Invitation' %> <% end %>

# groups controller

  before_filter :auto_complete_belongs_to_for_invitation_recipient_name ...   private

  def auto_complete_belongs_to_for_invitation_recipient_name     @invitation = Invitation.new     @recipients = @current_user.friends     render :inline => '<%= model_auto_completer_result(@recipients, :name) %>'   end

If I comment out the render :inline => '<%= model_auto_completer_result(@recipients, :name) %>' I get the normal layout back. Is it a problem with loading the auto_complete_belongs_to_for as a before_filter?

Just remove the before_filter macro call, and make auto_complete_belongs_to_for_invitation_recipient_name public.

That method is called automatically by the helper through Ajax when the user enters something into the field. You typically won't call it explicitly.