Skip to main content

Inviting Users with devise_invitable Discussion

General • Asked by Chris Oliver

What made you laugh at 7:25? Is that a bird?! When do we get meet her/him? :)

That's actually my cat. :) He does some chirp-y sounding meows when he sees things outside like birds or squirrels. I'll have to let him in one of the videos soon.


Cool video and very clear explanations !
Maybe you can give me a hint on how you would deal with a not so different event management app I'm working on :
User manage its Collaborators like on its phone, meaning full control on Collaborator profiles, and add them to an Event. Two dedicated models are used since Collaborator isn't unique (many User can create a Collaborator which is the same person). The thing is a User is notified of each event with a Collaborator that share the same email, and can choose to take control of it (Collaborator become User). This is the only way I found to have a fully working app before all collaborators joined it as user. It works, but I'm kind of stuck when, on a project, I have to loop through User and Collaborator to show the Event team efficiently. Is there a good way to do this ? Thank you for your very helpful advices


Great stuff. Probably saved me a lot of digging around time. Thanks.

Would have liked to see some aspect of testing addressed. I have come to appreciate good tests, and am still getting comfortable with good testing habits and strategies. I really like how you show real-world stumbling blocks and how to navigate them well. Seeing similar struggle and success with testing would be helpful for me.

I do see devise has some example tests posted, so maybe those are enough to get me going.

Thanks again!


Such a great episode, thank you!


Great episode. Could we have the souce code, please?


Can you upload this to github?


I am using rails 4 and got the error below. Any ideas what I am doing wrong? I used the attr_accessor :email in the User.rb model. I have that feeling that I missed something really simple but I am having a brain freeze LOL

NameError in TeamsController#create

undefined local variable or method `email' for #<teamuser:0x007fcef90e37b8>

Extracted source (around line #8):

def set_user_id

existing_user = User.find_by(email: email)

self.user = if existing_user.present?

existing_user

else

BTW, In my case this is a CRM application so we are inviting users to Teams rather than Projects obviously


Awesome episode as usual Chris.

How would you modify this to do a single click invite? i.e. say you are looking through a list of users and you want to "invite them" to your project or w/e, how would you do that?

Just create a new action on ProjectUsers controller, pass the email associated with that link that was pressed and then execute `User.invite!`?

The reason I am asking is that I was trying to pass params to the devise_invitable controller and it seems to be more tricky than you would expect it to be on the surface, so I am trying to explore other ways to do do what i am trying to do.

Yeah exactly. I would create my own controller and pass over the user_id which can then look up the user and associate them. Since the user might already have an account, you might just send them an email notice saying they now have access rather than an invite since they already have an account. You could also do an approval process there before it's finally accepted, kind of up to you.

Chris, was able to get emails sent to already-existing users this way below from the ProjectUser model

def set_user_id
existing_user = User.find_by(email: email)
self.user = if existing_user.present?
              existing_user
            else
              User.invite!(email: email)
            end
if existing_user.present?
  InviteMailer.existing_user_invite(email).deliver
end

end

About your post here about creating a separate controller, is there a better way to deal with existing users and identify project params? I don't think you can do so from the model here above so assuming that's why you said do it from controller. But issue I have or misunderstanding is how to invite both nonexisting users (new users) and already existing users from the same form input so you wouldn't have to set up two different ones - one for new devise invitable users and a separate one for already-existing users. Seems unnecessary or confusing to users. Tried to overried the devise controller but kind of confusing here and doesn't act as expected from the devise action that is there to invite already existing users. Tried to add it from their documentation but only messes up other things here I'm guessing because of the model setup and inheritance issues. Any quick solution so can send send projectuser or project params to the already-existing user in the email? Thanks a lot.


Chris great episode, thank you so much! One question, everything works great on my end. But, the emails don't actually reach their destinations. Is that because we are in development or do we need to have something extra installed in our app? Thanks mate!

If you've got a real Actionmailer config setup to hit a real smtp server, you can have it send real emails in development. If they aren't arriving, then you will want to double check your configuration to make sure that it's authenticating correctly and you see the emails being sent from your email provider logs.


If you want add "remove user" functionality, here is an example.

In project_users_controller.rb :

def destroy
@project.project_users.find(params[:id]).destroy
redirect_to projects_path, notice: "Member removed"
end

In projects/show.html.erb :

<h4>Users</h4>
<% @project.project_users.each do |project_user| %>
<div><%= project_user.user.email %>
<td><%= link_to "Remove member",
project_project_user_path(@project, project_user), method: :delete %></td></div>
<% end %>

Thanks for sharing Anthony! I'm sure people will find that useful. :)

And to add "member already exist".

In models/project_user.rb :

validates_uniqueness_of :user_id, :scope => [:project_id]

Thank you so much for sharing, I've been working on this for so long. Still don't quite have it, but i'm getting close, thanks!


The video seems different then the text. I get an error when following the text. When we make the edit to app/views/projects/show.html.erb. I also was getting an error when I followed the video alone. I had to combine what was in the text and video to make it to where I am at now(still not done). Can we get a repo or an update on this? I actually joined for this very lesson.

If I follow the text I get this error:
NoMethodError in Projects#show, undefined method `email' for #

Oh this was b/c in the text it says to add the email to the user model but in the video you add the email to the project_user model.


How do you actually check that role field later, i.e. vs `current_user`?

I figured it out (not perfect, but it works):

@owner = @project.project_users.where( role: "owner" ).first.user

Does anyone know how to pass project params so they can be used in the email view? 

Chris thanks for this great series. If you wanted to allow some projects to be publicly viewable (i.e. not just for invited people who get invited through devise invitable), is there an easy way to toggle this with a 'public/private' option. Just having trouble figuring out how to model in the database. Can create new projects - that's great - but it seems that all new projects must be associated with invited project users. How to have the option to have some publicly available so that not just project users can view them? Any help would be greatly appreciated. Thanks.

Generally for that, you'd want a public or private boolean on the Project.

Then you'd change your query for finding the project. A high level example:

def set_project
  @project = Project.find(params[:id])

    # Private projects require use to verify the user has access.
    if @project.private?
      raise ActiveRecord::NotFoundError unless @project.users.include?(current_user)
    end
end

Ideally, you'd use something like Pundit to do the authorization. That way it can be applied always and not be forgotten somewhere.


Thanks Chris. Makes it less complicated than having a separate user has_many projects table if that's even possible given the already-existing join table. I will check out the pundit gem also through Cancancan seems to be working fine so far for me. For the devise invitable links that are sent out in the email notifications here, they all seem to go to root page or sign in page but maybe I'll check out the documentation to see if there's a way to get the url pointing to the project itself after signing in but if you have any advice on that would appreciate it. Your great series saves a lot of time and really appreciate your insights.

Yeah, basically private projects need a ProjectUser association to keep track of who has access. A public one can ignore that since it's open to everyone.

If you're already using CanCan, keep using it. Pundit's just an alternative.

Devise invitable does have a method for doing that, it's in the docs somewhere. 👍

And glad I can be of help!


Chris if you wanted to change button text based on whether someone has been invited to a group (saying already invited for instance) would you go the route of just doing it through js entirely like in the button ainimations or would you do css selectors based on database values that proved invitation? I guess it must br a combination of both? Just seems more complex in this case of invitations going out because it isn't a simple boolean or some other db object that id being referenced by css with id selectors but instead there's an actual invitation outstanding to a particular person . Is this too much database referencing to efficiently and quickly render say an index page of people who have the correct invite or invited buttons? Just wondering how to go about this useful button feature without slowing or messing up an index page. Your insights are always valuable - thank you

I would use the database values. It's not going to slow you down to do that, since you'll already have the database records in memory to display them on the page. You can then do whatever you find easiest to change the buttons.

I would probably just use an if statement to check the different statuses of a user's invitation and then display different buttons accordingly.


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 20,000+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.