I am using the obfuscate_id gem ( https://github.com/namick/obfuscate_id ).
We obfuscate ID's by inserting one line into the top of each model:
obfuscate_id
It works great and as expected. My ID's are obfuscated.
However, as part of some logic in my ApplicationController, I have some logic to check the current user and each controller has access to these methods as helpers:
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
However, I get an error:
Couldn't find User with id=5164061535
It doesn't seem to be able to convert the obfuscated ID back to its normal form for a find().
How can I get the controllers to recognise this obfuscation that's made in each model.
My user model is like so:
class User < ActiveRecord::Base
# This part obfuscates the user ID
obfuscate_id
has_one :profile, dependent: :destroy
has_many :pins
has_many :replies, through: :pins
end
Any ideas how I can get the ApplicationController to recognise this? Doing find() in each controller itself is fine, but as ApplicationController doesn't have its own model, it doesn't seem to know of it.
Thanks,
Michael.
Try this...
#current_user ||= User.find(User.deobfuscate_id(session[:user_id])) if session[:user_id]
Weirdly enough, I ended up trying this:
#current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]
And it worked! But, why?
User.find() in itself was not working with this gem. So, although it's now working, it concerns me a little as to why exactly.
If anyone could add anything here that'd be great.
Thanks!
Related
Feel free to say if you think something is wrong.
I extended Devise Registration controller to create a Profile object to every new user:
class Users::RegistrationsController < Devise::RegistrationsController
def new
resource = build_resource({})
resource.profile = Profile.new
resource.profile.user_id = #user.id
respond_with resource
end
They both are has_one - has_one related and in database:
create_table :profiles do |t|
t.belongs_to :user, index: { unique: true }, foreign_key: true
end
So to get the right profile of current user, I must:
private
def set_profile
#profile = Profile.where(user_id: current_user.id).first
end
And this kinda solves the problem - seems other users cant go around this query and access other profiles (or CAN THEY?), but for other resources I use Pundit to control authorisation, so now it feels a bit messy.
So thats one concern. Other - I still don't know how to act when there is no user logged, because if visiting any restricted resource, this:
private
def set_some_resource
end
end
Throws - "undefined method `id' for nil:NilClass) - how is best to avoid this?
Thanks for any advices.
You may want to start by reading the Rails guides on assocations.
To create a one to one association you use belongs_to on the side with the foreign key column and has_one on the other.
class User
has_one :profile
end
class Profile
belongs_to :user
end
ActiveRecord then automatically links the records together. In general you should avoid setting ids (or getting associated records by ids) explicitly and instead use the assocations:
class Users::RegistrationsController < Devise::RegistrationsController
# ...
def new
# calls Devise::RegistrationsController#new
super do |user|
user.profile.new
end
end
end
Devise is pretty nifty and lets you pass a block to tap into the flow instead of copypasting the whole action.
Simularily you would fetch the current users profile with:
private
def set_profile
#profile = current_user.profile
end
You can set if the callback should be called by using the if: option.
before_action :set_profile, if: :user_signed_in?
But if the action requires authentication you should make sure that it is after :authenticate_user! anyways which will halt the filter chain.
And this kinda solves the problem - seems other users cant go around
this query and access other profiles (or CAN THEY?), but for other
resources I use Pundit to control authorisation, so now it feels a bit
messy.
You don't need to use Pundit to authorize creating a profile or fetching the current users profile. Since the profile is fetched via the user the is no way for another user to access it (well without hacking).
what you might want to authorize is the show, index, edit etc actions if you create a ProfilesController.
I'm a freshman to learn Rails and working on my first project about online book writing.
I've already made the MVC of user,book and section. I wanna create a button called "Author Place",which can show all the pieces written by the current logged in user.
I wanna ask a simple question. How can I make a condition with the current username to select the current author's works from the book database. Should I put this code in controller or view?
Code as follow.
current_user method of the ApplicationController:
protect_from_forgery
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
The Section model :
class Section < ActiveRecord::Base
attr_accessible :book_id, :section_content, :section_tag, :user_username
belongs_to :book
belongs_to :user
end
The Section controller :
class SectionsController < ApplicationController
def userpieces
#sections=Section.find(:all, :conditions=>"user_username=current_user.username") # This part doesn't work
end
end
Or any suggestions with some other way to do this?
Assuming you have a corresponding has_many :sections association in your User model, try this:
#sections = current_user.sections
As depa and izuriel mentioned, you should be able to get it simply if your model relation is correctly set.
Anyway, if you wish to get it in the way you try please use:
#sections=Section.find(:all, :conditions => ["user_username= ?",current_user.username])
Please note, in rails 3, .find(:all is deprecated, please use .all instead.
So I know this question has been ask a ton of times but my question goes a little bit further.
When modeling my application I have two types of users that have a polymorphic association to the user model. Such as:
class User < ActiveRecord::Base
belongs_to :profileable, :polymorphic => true
end
class User_Type_1 < ActiveRecord::Base
has_one :user, :as => :profileable
end
class User_Type_2 < ActiveRecord::Base
has_one :user, :as => :profileable
end
The reason I did this, instead of an STI, is because User_Type_1 has something like 4 fields and User_Type_2 has something like 20 fields and I didn't want the user table to have so many fields (yes 24-ish fields is not a lot but I'd rather not have ~20 fields empty most of the time)
I understand how this works, my question is I want the sign up form to only be used to sign up users of type User_Type_1 but the sign in form to be used to both. (I will have an admin side of the application which will create users of User_Type_2)
I know I can use the after_sign_in_path_for(resource) override in AppicationController somehow to redirect to the right part of the site on sign in. Something like:
def after_sign_in_path_for(resource)
case current_user.profileable_type
when "user_type_1"
return user_type_1_index_path
when "user_type_2"
return user_type_1_index_path
end
end
So I guess my questions are how would I make the form to work with Devise and only allow signups of type User_Type_1 and then sign them in after sign_up?
Also, if I am going about this the wrong way, what is the right way?
I was able to answer my own question and am putting it here so that maybe it can help someone else with the same problem.
The login problem was easy, just use the default devise login and the after_sign_in_path_for in ApplicationController as described above
I realized the answer to the form question as typing it out here:
I just created a normal form for the User_Type_1 with nested attributes for User
and had it post to the UserType1Controller
Then saved both objects and called the sign_in_and_redirect helper from Devise
class UserType1Controller < ApplicationController
...
def create
#user = User.new(params[:user])
#user_type_1 = UserType1.new(params[:patron])
#user.profileable = #user_type_1
#user_type_1.save
#user.save
sign_in_and_redirect #user
end
...
end
Then the after_sign_in_path_for method from above sent it to the right place and it was all good.
I have a User & Profile Models. A user has_one profile IE.
class User < ActiveRecord::Base
has_secure_password
# Relationships
has_one :profile
class Profile < ActiveRecord::Base
# Relationships
belongs_to :user
I am then trying to test to see if the user has a profile. If not redirect them to the profile controller ie.
class User::BaseController < ApplicationController
# Filters
before_filter :check_for_profile
# Layout
layout "backend"
# Helpers
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def check_for_profile
if current_user.profile.empty?
redirect_to new_user_profile_path, :notice => "Please create a profile first."
end
end
end
No matter what I try I'm getting the error.
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.empty?
I'm pretty sure my relationships are right. Can anyone shed some light on what I'm doing wrong ?
Thank you in advance.
Lee
try profile.blank? instead. empty? is not defined for nil.
Check out the "blank?" method at the following link. The "present?" method should also be considered - they're basically the same.
http://api.rubyonrails.org/classes/Object.html#method-i-blank-3F
I'm trying to design a comment system for my RoR blogging site, and I am having some conceptual problems with the architecture. As far as models are concerned, I have Blogposts, Users, and Comments.
A User has_many Blogposts
A Blogpost belongs_to one User
A Blogpost has_many Comments
A Comment may or may not belong to a registered User (I want people not registered with the site to be able to comment as well).
My question is this: in order to enforce the link between a comment and a blogpost, I create each new comment (#comment) through the blogpost association (#blogpost.comments.build(:args)). However, I do not know how to associate a particular registered User with his/her comment. I left the user_id attribute OUT of the attr_accessible for the Comment model because I wanted to prevent the possibility of people attributing comments to the wrong users.
Any ideas on how best to implement a commenting system with such a relation? Thanks so much in advance!
Assuming:
User has_many comments
Comment belongs_to user
In your controller when saving the comment, you can simply do:
#comment.user = current_user if current_user
#comment.save
If the comment is done by an unregistered user #comment.user just stays empty.
You can just have an association :
User has_many comments through blog_posts
So, now you can do :
current_user.comments
Another way to do it is via blog_post:
current_user.blog_post.comments
Moreover, you can use the nice act_as_commentable plugin :)
https://github.com/jackdempsey/acts_as_commentable
There's no need to have user_id as attr_accessible if you have access to the currently logged in user in your save or post new comment methods.
If they aren't logged in then you expect current user to be empty / false.
This should be available if you're using any of the authentication plugins such as authlogic or devise. In my experience with authlogic you typically have a current_user method in your ApplicationController.
class ApplicationController
helper_method :current_user_session, :current_user
private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
end
Above code from the Authlogic quick example
You can add an association between Comment and User, then create the comment with current_user:
# User.rb
has_many :comments
# Comment
belongs_to :user
Setting up the associations only really adds the association methods, so there's no problem with creating Comment without a logged in user. You don't want to build the comment off of current_user as current_user.comments.create(...), because that will throw a NilClass error if nobody is logged in.
#user = current_user # #user should be nil if commenter is not logged in
# be fancy and use a block
#blogpost.comments.create(params[:comment]) do |c|
c.user = #user
end
As long as there is no validation for User in Comment, the nil user should just pass through without trouble.