Getting userID from session into newly created records

What’s the current best practice for getting the userID from the sesson,
and having it be part of every newly created/modified record?

I have an app with a Club model and a People model, and a “membership”
join table.

class Club < ActiveRecord::Base
has_many(:memberships)
has_many(:members, class_name: ‘People’, through: :memberships)
end

I want all the tables, including the membership join table, to have a
“creator” field that gets populated every time the record is created
updated. I’d especially like to do stuff in my controller like the
following:
c = Club.find(name: => ‘The Rubyists’)
c.members << People.find(name: [‘Joe’,‘Ann’])

and have it populate the membership table correctly.

Google has found me various articles/posts related to various versions
of rails, some of which say something like “if you’re doing this,
you’re Doing It Wrong”, and many of which suggest putting information
in a thread-local variable.

I worry that some of these suggestions I’ve find make assumptions about
how Rails works under the hood that may not hold true for every
deployment method.

Any ideas?
In addition to general advice, I’d be interested in any gems that try to
provide a solution to this.

  • Is the user logged in? Are you using devise gem? If so (recommend),
    you have access to the current user using the current_user controller
    variable (provided by devise). Using devise with a proper authentication
    system is the way to go. There are alternatives to devise too.

More responses inline:

On Oct 20, 2014, at 1:34 PM, Brian S.
[email protected] wrote:

I want all the tables, including the membership join table, to have a
“creator” field that gets populated every time the record is created
updated.

Is created or updated? That sounds like updated_by_user_id (or
last_updated_by). There are gems for that, and you should use them. I
think maybe even devise may have that built-in. In which case you may
not need to worry about that stuff in your controller.

I’d especially like to do stuff in my controller like the
following:

Too much domain logic in the Controller is a common antipattern. Avoid
too much domain logic in your controllers. They should be light and pass
off all domain logic to context objects or model objects (or some other
domain-layer).

c = Club.find(name: => ‘The Rubyists’)
c.members << People.find(name: [‘Joe’,‘Ann’])

Generally you always find things by IDs (unless you’re searching for
something based on a user query). Not sure why you would want to do a
find by a name.

I could see use case for this, but I’d be concerned with access control
here. The cancan or cancancan gem help with access control.

and have it populate the membership table correctly.

Google has found me various articles/posts related to various versions
of rails, some of which say something like “if you’re doing this,
you’re Doing It Wrong”, and many of which suggest putting information
in a thread-local variable.

I’ve seen this pattern before (back in the Rails 1 and Rails 2 days), it
is generally frowned upon at this point. Simply passing the current_user
down to the model from the controller is perfectly fine in my opinion.

Consider having a class (plain-old-ruby-object) that is responsible for
adding members to clubs. So in your controller, you’d write some code
like this (this example is ridiculous, but is for illustrating a point)

club = Club.find(params[:id])
joe_user = User.JOE
members = [current_user, joe_user]
creator = ClubMemberCreator.new(club, members)
creator.create!

The ClubMemberCreator object may do a bunch of things for you
(manipulate the stats about the club, spawn some worker jobs to perform
operations on the memberships, send some emails to the new members
welcoming them!). By putting it in its own object, you are abstracting
the quickly changing logic from the slow- change logic. (see Coplien
Lean Architecture), and you also moving domain logic out of the
controller so it can be re-used by another controller doing the same
thing. This is a good pattern and makes a lot of sense in a large app.

I worry that some of these suggestions I’ve find make assumptions about
how Rails works under the hood that may not hold true for every
deployment method.

That’s a good thing to worry about. Using the best tool for the job,
your best judgement, and knowledge about what has worked in the past to
guide you in making the least-objectionable choice.


Jason Fleetwood-Boldt
[email protected]

All material (c) Jason Fleetwood-Boldt 2014. Public conversations may be
turned into blog posts (original poster information will be made
anonymous). Email [email protected] with questions/concerns about
this.

[I thought that I’d avoid the distraction of critiquing my app in general by only posting code relevant to my question, but whatever–there’s good advice here from Jason]

On Mon, 20 Oct 2014 13:58:29 -0400
Jason Fleetwood-Boldt [email protected] wrote:

I want all the tables, including the membership join table, to have a
“creator” field that gets populated every time the record is created
updated.

Is created or updated? That sounds like updated_by_user_id (or

Oops… that was supposed to be “created/updated”

last_updated_by). There are gems for that, and you should use them. I

Any suggestions of specific gems? (Question for anyone, not
specifically Jason)
My current authentication system doesn’t provide this, and I’d
prefer a solution that doesn’t require me to change my authentication
system.
Maybe “updated_by_user_id” is the magic google-fu.

c = Club.find(name: => ‘The Rubyists’)
c.members << People.find(name: [‘Joe’,‘Ann’])

Generally you always find things by IDs (unless you’re searching for
something based on a user query). Not sure why you would want to do a
find by a name.

This was not a direct copy from my app. This was just a simple
example of what I’m trying to do, (I’d hoped) with an emphasis on my
use of the “<<” operator. And actually, it’d probably need to
s/find/where/g

In particular, there’s no obvious method for passing a “creator” field
when you’re using the “<<” operator like that.