I am fairly new to rails and am just getting into using cool gems and APIs. I have been made aware that this community values the contents of questions and answers, or it is expected that they are constructed in a specific way. The short way to ask my question and the long way are provided. Please let me know which is preferred here! It is a real question though!
SHORT VERSION
I have a users_controller and User object with a username attribute in a rails app. How do I create global variables for these users that is dynamically based on their username? Example: I want with my user (id = 1, first_name = "Rob", username = "rocky") to be callable as #rocky. So what would go below in my Users_controller that is based on the first code line below working for me in terminal:
#rocky = User.find_by_username("rocky")
WHATGOESHERE = User.find_by_username(params[:username]}
or should I be using this in some shape or form in place of params[:username]
#"#{user.username}"
Below is the longer version of my question. It is more detailed and follows more closely how I approached the issue. The first one... that I wrote second, is more concise but that's not always what people want... please let me know which is preferred on this site. Thanks!!
LONG VERSION
I need some clarity on a few things. I am using a gem called "has_friendship" to create friendships between my users (link to gem- https://github.com/sungwoncho/has_friendship ).
First, this is the documentations example of how to request a friendship, starting with the creation of the users.
#mac = User.create(name: "Mac")
#dee = User.create(name: "Dee")
# #mac sends a friend request to #dee
#mac.friend_request(#dee)
This is where I first became confused. My users don't have a "name" field. But that's ok. I managed to figure out that I just need to assign my created users global variables as they do... since my users will be interacting with each other behind their "username" attribute. So first question, How do I assign a dynamic variable name to each user? In the documentation, they are hard-coding in the names "Mac" and "Dee." I need to have this global variable be created upon the creation of the object.. So my plan is to do this in the controller. Here I am already defining #users and #user in users#show
#users = User.all
#user = User.includes(:wallet).find_by_id(params[:id])
So my thought process is that the left side of the equation should be the name of what you're naming and the right side is what that name is referring to. So for the right side, I'd think to put
User.find_by_username(params[:username])
as when in the terminal, if I replace the content in parenthesis with an actual username in quotes, it brings up that user's info. So how do I write the left side. I would think the left side is something like this:
#"#{params[:username]}"
So in full I currently have the following in my users_controller to assign global variables to my users based on their username atttribute...
#"#{params[:username]}" = User.find_by_username(params[:username])
This, especially the left side, does not look at all right to me. So I've looked around on google a bunch and the only other thing I can find that looks like the right way to do this is by using "instance_variable_set" but everything I've looked at doesn't make total sense for my situation... (as usual.. ha)
Ok what I get to know from your question is you want to use friend_request method to associate two users.
For this, you don't need to assign them to any variables. You can directly do this by something like this -
Suppose there are two user's
id=1 first_name='Rocky' username='rocky'
id=2 first_name='Nimish' username='nimish'
User.find_by(username: 'rocky').friend_request(User.find_by(username: 'nimish'))
OR
User.find_by_username('rocky').friend_request(User.find_by_username( 'nimish'))
Also, If you want to assign them to instance variable then it is not necessary to create an instance variable corresponding to the username value
You can simply assign them to #user and #requested_user and then
#user.friend_request(#requested_user)
Related
Working through authentication in RailsCasts, there was one where I didn't understand something completely fundamental and important (it's pro, sorry - you need to be subscribed to access it).
He creates a user model with an email and encrypted password (has_secure_password).
Then he makes a new controller called Sessions, and declares it as a resource in the config. i.e.
resources :sessions
Then, inside the sessions controller, he defines the create method like this:
user = find user and authenticate
if user
session[:user_id] = user.id
else ...
But sessions as a model doesn't exist. For instance, if I open up my console and write
sessions[:user_id] = "hello"
it throws. Does anyone have an explanation or a link to this very basic concept that I'm missing?
Thanks!
Edit: Thanks Sanfor. Typo fixed, also for markup plus most importantly an answer!
I suppose you have copied the session as sessions in your question, is that correct? The screen cast is revised, so you'd need to be subscribed what I'm not nowadays so can't confirm it more than what comments say.
Now to the actual answer, the session is Rails internal reference to the actual session on hand as described here and for that reason you can't see the model for it created.
Simplistic explanation:
session is just a hash and stored as a cookie. (Unless you specifically instructed Rails to store it in the database). Models are typically ActiveRecord based and have some behavior.
You can add to it by simply
session[:some_thing] = "Info for session"
session[:store_this_too] = "Some other info to track for this session"
The session hash is created by the controller-related class/modules and rails console doesn't load them. Therefore, it is not available in the console.
Here's a pretty old Railscasts which explains a bit more. And this which takes the model-based approach. Bear in mind they are from the old days.
So lets say I have a form for submitting a new post.
The form has a hidden field which specify's the category_id. We are also on the show view for that very category.
What I'm worried about, is that someone using something like firebug, might just edit the category id in the code, and then submit the form - creating a post for a different category.
Obviously my form is more complicated and a different scenario - but the idea is the same. I also cannot define the category in the post's create controller, as the category will be different on each show view...
Any solutions?
EDIT:
Here is a better question - is it possible to grab the Category id in the create controller for the post, if its not in a hidden field?
Does your site have the concept of permissions / access control lists on the categories themselves? If the user would have access to the other category, then I'd say there's no worry here since there's nothing stopping them from going to that other category and doing the same.
If your categories are restricted in some manner, then I'd suggest nesting your Post under a category (nested resource routes) and do a before_filter to ensure you're granted access to the appropriate category.
config/routes.rb
resources :categories do
resources :posts
end
app/controllers/posts_controller
before_filter :ensure_category_access
def create
#post = #category.posts.new(params[:post])
...
end
private
def ensure_category_access
#category = Category.find(params[:category_id])
# do whatever you need to do. if you don't have to validate access, then I'm not sure I'd worry about this.
# If the user wants to change their category in their post instead of
# going to the other category and posting there, I don't think I see a concern?
end
URL would look like
GET
/categories/1/posts/new
POST
/categories/1/posts
pst is right- never trust the user. Double-check the value sent via the view in your controller and, if it does't match something valid, kick the user out (auto-logout) and send the admin an email. You may also want to lock the user's account if it keeps happening.
Never, ever trust the user, of course ;-)
Now, that being said, it is possible to with a very high degree of confidence rely on hidden fields for temporal storage/staging (although this can generally also be handled entirely on the server with the session as well): ASP.NET follows this model and it has proven to be very secure against tampering if used correctly -- so what's the secret?
Hash validation aka MAC (Message Authentication Code). The ASP.NET MAC and usage is discussed briefly this article. In short the MAC is a hash of the form data (built using a server -- and perhaps session -- secret key) which is embedded in the form as a hidden field. When the form submission occurs this MAC is re-calculated from the data and then compared with the original MAC. Because the secrets are known only to the server it is not (realistically) possible for a client to generate a valid MAC from the data itself.
However, I do not use RoR or know what modules, if any, may implement security like this. I do hope that someone can provide more insight (in their own answer ;-) if such solutions exist, because it is a very powerful construct and easily allows safe per-form data association and validation.
Happy coding.
This is the code I'm currently using:
profile_controller.rb:
#user = User.first :conditions => [ "lower(username) = ?", params[:id].downcase ]
show.html.haml:
.footer #{#user.bets.count} -# This displays 0, even though I can see that the user has multiple bets associated with his username in the db
My users sign in with Twitter and their usernames could be in any case variation (e.g. BOB, bob, BoB, Bob etc.) - and naturally, any information associated with their username should be accordingly displayed regardless of how the users have signed in.
One strange bug I'm running into is that all associated information with the username seems to be lost once a user signs out, and signs back in; however, this happens seemingly at random times. That is, a user can sign out and sign back in several times without issue, but once in a while, seems to lose all associated information in the profile.
Any tips on how I could go about ensuring this isn't the case? Is the controller code I posted correct, or should I be checking for another parameter other than param[:id]?
EDIT: Thanks for the comments, guys. It turns out that I was overwriting the user information if they just sign in with different case variations. I cleaned this up, and it works now.
Perhaps you are looking to do something like:
#user = User.where(:username => params[:username].downcase).first
You could try explicitly including the bets when retrieving the user:
#user = User.includes(:bets).where("lower(username) = ?", params[:id].downcase).first
which will automatically retrieve all related bets.
Normally using #user.bets it is lazy loaded, but once it is loaded, it just uses the bets in memory. To explicitly load all bets again from database, you could also write:
#user.bets.reload.count
I'm building a referral program for my Ruby on Rails app, such that a user can share a link that contains their user ID (app.com/?r=ID). If a referrer ID is present when a visitor lands on app's homepage, the signup form on the homepage contains a hidden field that populates with the referrer's ID. The controller then detects the ID and creates a new referral in a referral table if the referred visitor signs up. It works, and here's that chunk of code:
#referrer = User.find(params[:r]) rescue nil
unless #referrer.nil?
#referral = Referral.new(:referrer_id=>#referrer.id)
end
Pretty simple stuff, but it's pretty easy to break (ex: if visitor navigates away from the homepage, referrer ID is lost). I feel like cookies could be a more robust method, where a cookie containing the referrer's ID is stored on the referred user's computer for x days. This is pretty commonplace, especially with affiliate programs like Groupon, but I have never worked with cookies and have no idea where to start.
Also, is there any good way to mask or change the URLs of the referral system? Instead of having app.com/?r=1842, I would prefer something like app.com/x39f3 <- a randomly generated sequence of numbers associated with a given user, without the ?r= portion.
Any help is greatly appreciated. Thanks!
To answer the cookie question, it's quite easy to set them:
cookies['app-referrer-id'] = params[:r]
And then it's the same format to read them back (but without the assignment). I would suggest putting this code in a before_filter in your application controller. This way, the cookie will be set irrespective of the page on which your visitor first lands on your site.
With regards to changing the structure of the urls to the suggested format, you would need to have the referral codes match a specific pattern, otherwise you are likely to run into routing problems. If, for example, they matched the format of 3 letters followed by three numbers, you could put the following your routes file:
match '/:referrer_id' => 'app#index', :constraints => {:referrer_id => /[a-zA-Z]{3}[0-9]{3}/}
The reference to app#index should be changed to the controller in which you handle referrals and you can access the referrer_id through params[:referrer_id].
Hope this is of some use.
Robin
I want to learn how to create my own authentication system, please provide some guidance if am doing this wrong.
I will create a Module in my /lib folder /lib/auth.rb
I will require this module in my ApplicationController.
when a user enters their email + password, I will call a method that will do a lookup in the user's table for a user with the same email, I will then compare the passwords. (i'll add encryption with salt later).
If the user entered the correct credentials, I will create a row in the Sessions table, and then write the session GUID to a cookie.
Now whenever I need to check if the user is logged in, or I need the user object, I will check if the cookie exists, if it does, I will lookup the session table for a row with the same guid, if it exists, I will return the session row and then load the User object.
I realize there are many suggestions one can give, but in a nutshell does this sound like a workable solution?
Now to make this usable, I will have to make some helper methods in my ApplicationController right?
How will I access the current_user from within my views?
P.S I know of other authentication systems, I just want to learn how to create my own.
The basic logic you're following is correct. Of course you can always expand on this with features that you need. For instance, you'll need helper methods for things like "logged_in?" and "current_user". Also, you might want to add session expiry, or session retention as a "remember me" feature.
Go for it, you won't learn authentication systems better than building your own then figuring what's wrong with it.
You should really check out the authlogic gem on github.
http://github.com/binarylogic/authlogic
It also has great instructions on how to set up your users.
After Faisal said what I would say, I only give you answer to the last part of your question:
"How will I access the current_user from within my views?"
try something like this:
class User < ...
def self.current=(u)
#current = u
end
def self.current
#current
end
end
In your views (or any part of your code) you can call User.current. Your controller has to assign a validated user to User.current. Your filters can react to "if User.current.nil?" and so on.
If you want to be thread safe, you may use a thread variable instead of #current:
Thread.current[:current_user] = u