I added a custom devise sessions controller to associate records after someone logged in:
class SessionsController < Devise::SessionsController
before_create :associate_calculation
def associate_calculation
Calculation.find(self.calculation_id).user_id = self.id
Calculation.last.save!
end
end
Here are the request parameters:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"JOQQcCTB9tkVegDgHP/ww8hu5qSzNWlu+4HZZ9AmQGYVO60f3BliwEYT+HKAGPsOOqbipSgj/xSqcDLqueOPZw==", "user"=>{"calculation_id"=>"48759708645478633", "email"=>"jonas#slooob.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Signin"}
I also added attr_accessor :calculation_id to the User model.
Still, the corresponding calculation does not get associated to the signed in user.
I also tried to manually attach a calculation to a user:
class SessionsController < Devise::SessionsController
before_create :associate_calculation
def associate_calculation
Calculation.last.user_id = User.first.id
Calculation.last.save!
end
end
That did not work either.
def associate_calculation
c = Calculation.find(self.calculation_id)
c.user_id = self.id #self.id is probably also wrong. maybe current_user?
#Calculation.save! You can not save a class. You need to save an instance
c.save
end
Save the object not the class. You should have had an error so also make sure the method is actually being called by adding a trace to your log file and check the output.
I also suspect that self.id may not be what you are looking for either. Perhaps you should be looking for current_user.id? or whatever the name of the currently logged in user object is at this point. Also if you prefer using the model relationships then maybe something like
c.user = current_user
c.save
The above all assumes that you have the correct associations e.g. user has_many calculations and calculation belongs_to user and that your database reflects this. I say this because you mentioned:
I also added attr_accessor :calculation_id to the User model.
Which is clearly wrong and would never give you the desired results so it looks like you have a little confusion
just adding a method to your model would not have any effect at all. Even if it did work your solution would only ever allow one calculation for a user yet your code implies you would have many calculations.
This leads me on to think that you should not just be finding a calculation but should be creating one?
Did you set your routes properly? For the extended controller to take effect you would need somethign like
class SessionsController < Devise::SessionsController
def destroy
reset_session
super
end
end
# In your routes
devise_for :users do
get "/users/sign_out", :to => "sessions#destroy", :as => "destroy_user_session"
end
Related
I'm using acts_as_taggable_on plugin in conjunction with my User model.
acts_as_taggable_on :skills
Now, I have a custom controller called SkillsController to add skills via ajax.
class SkillController < ApplicationController
def add
current_user.skill_list.add(params[:skill])
current_user.save # Not saving!
end
end
and in routes.rb
get 'skill/:skill', to: 'skill#add'
I guess it has to do something with Strong Parameters, but I don't know how to solve it as it stands.
The current_user.save isn't working, how to solve it.
P.S current_user.errors shows #message is "too short" as per my validations. But how do I just save the skill_list without having to modify other attributes or running validations on them?
If you want to save current_user without validation check you can do just like as:
current_user.save(:validate => false)
This will work for you :)
With the koala gem I am trying to count checkins for a page. I am using rails.
In my user.rb I have a method for getting a new connection to the Facebook graph:
class User < ActiveRecord::Base
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
end
In my school.rb I have a method for counting the checkins:
class school < ActiveRecord::Base
def count_checkins(name)
checkins = #facebook.fql_query("SELECT checkins FROM page WHERE name = #{name}")
end
end
And I am calling it from the view like this:
<%= #school.count_checkins(#school.name) %>
But I get the following error:
undefined method `fql_query' for nil:NilClass
Dont really understand why I get this error, any help would be wonderful.
It looks like you haven't actually created the #facebook object inside your School model. We'd need to see the rest of your school.rb file to know for sure. I'd suggest you create the object inside your School.initialize() method like so:
def initialize(oauth_token)
unless oauth_token.nil?
#facebook = Koala::facebook::API.new(oauth_token)
end
end
In order for this to work, you'll need to pass the desired oauth_token to your School.new() call. Then you'll have one #facebook object for each School.
Edit
After looking at the gist, I realized that you had actually intantiated a User object, and called the facebook method on that. That is actually the better way to do it. The problem is, you're using #current_user, which would have to be setup as a property of the school model. You probably meant to use the helper function current_user instead.
def count_checkins(name)
u = current_user
u.#facebook.fql_query("SELECT checkins FROM page WHERE name = #{name}")
end
Try that and see what happens. At the very least, you should get a different error message.
Edit 2
So, now I'm thinking the current_user function should be called in controller code, not model code. This is because the current user is something that doesn't really exist except as part of an active request. Therefore, we should take User u as a parameter to the count_checkins function like so:
def count_checkins(name, u)
u.facebook.fql_query("SELECT checkins FROM page WHERE name = #{name}")
end
You'll need to change the code where you call count_checkins() too:
count_checkins(name, current_user)
That should do it. Let's see!
I have two models, User and PushupReminder, and a method create_a_reminder in my PushupReminder controller (is that the best place to put it?) that I want to have create a new instance of a PushupReminder for a given user when I pass it a user ID. I have the association via the user_id column working correctly in my PushupReminder table and I've tested that I can both create reminders & send the reminder email correctly via the Rails console.
Here is a snippet of the model code:
class User < ActiveRecord::Base
has_many :pushup_reminders
end
class PushupReminder < ActiveRecord::Base
belongs_to :user
end
And the create_a_reminder method:
def create_a_reminder(user)
#user = User.find(user)
#reminder = PushupReminder.create(:user_id => #user.id, :completed => false, :num_pushups => #user.pushups_per_reminder, :when_sent => Time.now)
PushupReminderMailer.reminder_email(#user).deliver
end
I'm at a loss for how to run that create_a_reminder method in my code for a given user (eventually will be in a cron job for all my users). If someone could help me get my thinking on the right track, I'd really appreciate it.
Thanks!
Edit: I've posted a sample Rails app here demonstrating the stuff I'm talking about in my answer. I've also posted a new commit, complete with comments that demonstrates how to handle pushup reminders when they're also available in a non-nested fashion.
Paul's on the right track, for sure. You'll want this create functionality in two places, the second being important if you want to run this as a cron job.
In your PushupRemindersController, as a nested resource for a User; for the sake of creating pushup reminders via the web.
In a rake task, which will be run as a cron job.
Most of the code you need is already provided for you by Rails, and most of it you've already got set in your ActiveRecord associations. For #1, in routes.rb, setup nested routes...
# Creates routes like...
# /users/<user_id>/pushup_reminders
# /users/<user_id>/pushup_reminders/new
# /users/<user_id>/pushup_reminders/<id>
resources :users do
resources :pushup_reminders
end
And your PushupRemindersController should look something like...
class PushupRemindersController < ApplicationController
before_filter :get_user
# Most of this you'll already have.
def index
#pushup_reminders = #user.pushup_reminders
respond_with #pushup_reminders
end
# This is the important one.
def create
attrs = {
:completed => false,
:num_pushups => #user.pushups_per_reminder,
:when_sent => Time.now
}
#pushup_reminder = #user.pushup_reminders.create(attrs)
respond_with #pushup_reminder
end
# This will handle getting the user from the params, thanks to the `before_filter`.
def get_user
#user = User.find(params[:user_id])
end
end
Of course, you'll have a new action that will present a web form to a user, etc. etc.
For the second use case, the cron task, set it up as a Rake task in your lib/tasks directory of your project. This gives you free reign to setup an action that gets hit whenever you need, via a cron task. You'll have full access to all your Rails models and so forth, just like a controller action. The real trick is this: if you've got crazy custom logic for setting up reminders, move it to an action in the PushupReminder model. That way you can fire off a creation method from a rake task, and one from the controller, and you don't have to repeat writing any of your creation logic. Remember, don't repeat yourself (DRY)!
One gem I've found quite useful in setting up cron tasks is the whenever gem. Write your site-specific cron jobs in Ruby, and get the exact output of what you'd need to paste into a cron tab (and if you're deploying via Capistrano, total hands-off management of cron jobs)!
Try setting your attr_accessible to :user instead of :user_id.
attr_accessible :user
An even better way to do this however would be to do
#user.pushup_reminders.create
That way the user_id is automatically assigned.
Use nested routes like this:
:resources :users do
:resources :pushup_reminders
end
This will give you params[:user_id] & params[:id] so you can find your objects in the db.
If you know your user via sessions, you won't need to nest your routes and can use that to save things instead.
Using restful routes, I would recommend using the create action in the pushup_reminders controller. This would be the most conventional and Restful way to do this kind of object creation.
def create
#user = User.find(params[:user_id]
#reminder = #user.pushup_reminders.create()
end
If you need to check whether object creation was successful, try using .new and .save
I'm a Rails newbie.... Here's what I'm trying to do....
I created a scaffold for notes (t.text :content, t.integer :user_id)
What I want to do now is only allow user's to view notes that they created. ie (== user_id)
In my /app/controllers/notes_controller.rb
I have the following:
class NotesController < ApplicationController
before_filter :authenticate
before_filter :correct_user
.
.
.
.
def correct_user
#noteuserid = Note.find(:conditions=>["note.user_id=?", #noteuserid])
redirect_to(root_path) unless current_user?(#noteuserid)
end
I'm having problems understanding how to write the following line: #noteuserid = Note.find(:conditions=>["note.user_id=?", #noteuserid])
Any ideas?
Thanks
In Rails 3:
Note.where(:user_id=>current_user)
Or, you can start with the user...
User.find(current_user_id).notes.find(note_id)
So, firstly you want to find the Note being accessed by the user, then check whether that Note is valid for the user. I would try something like this (assuming that your current_user? method checks whether a given user id matches the current logged in user:
def correct_user
current_note = Note.find(params[:id])
redirect_to(root_path) unless current_user?(current_note.user_id)
end
Also, you may want to watch out for filtering all actions in the controller with your correct_user filter as actions to create a note may not have an id of a note to check against. Additionally, when you are viewing a collection of notes you will need to filter differently (e.g. Note.find(:all, :conditions => { :user_id => current_user_id })). It may be more appropriate to apply the correct logic in specific actions rather than as a generic filter.
Finally, you could look at the cancan plugin which would do a lot of the hard work for you with code like this.
My Rails application have a User model and a Group model, where User belongs to a Group. Thanks to this, a user can be a admin, a manager, a subscriber, etc.
Until recently, when for example a new admin need to be create on the app, the process is just to create a new normal account, and then an admin sets the new normal account's group_id attribute as the group id of the admin... using some condition in my User controller. But it's not very clean, I think. Because for security, I need to add this kind of code in (for example) User#update:
class UsersController < ApplicationController
# ...
def update
#user = User.find(params[:id])
# I need to add some lines here, just as on the bottom of the post.
# I think it's ugly... in my controller. But I can not put this
# control in the model, because of current_user is not accessible
# into User model, I think.
if #user.update_attributes(params[:user])
flash[:notice] = "yea"
redirect_to root_path
else
render :action => 'edit'
end
end
# ...
end
Is there a clean way to do it, with a Rails plugin? Or without...
By more clean, I think it could be better if those lines from User#update:
if current_user.try(:group).try(:level).to_i > #user.try(:group).try(:level).to_i
if Group.exists?(params[:user][:group_id].to_i)
if Group.find(params[:user][:group_id].to_i).level < current_user.group.level
#user.group.id = params[:user][:group_id]
end
end
end
...was removed from the controller and the application was able to set the group only if a the current user's group's level is better then the edited user. But maybe I'm wrong, maybe my code is yet perfect :)
Note: in my User model, there is this code:
class User < ActiveRecord::Base
belongs_to :group
attr_readonly :group_id
before_create :first_user
private
def first_user
self.group_id = Group.all.max {|a,b| a.level <=> b.level }.id unless User.exists?
end
end
Do you think it's a good way? Or do you process differently?
Thank you.
i prefer the controller methods to be lean and small, and to put actual model logic inside your model (where it belongs).
In your controller i would write something along the lines of
def update
#user = User.find(params[:id]
if #user.can_be_updated_by? current_user
#user.set_group params[:user][:group_id], current_user.group.level
end
# remove group_id from hash
params[:user].remove_key(:group_id)
if #user.update_attributes(params[:user])
... as before
end
and in your model you would have
def can_be_updated_by? (other_user)
other_user.try(:group).try(:level).to_i > self.try(:group).try(:level).to_i
end
def set_group(group_id, allowed_level)
group = Group.find(group_id.to_i)
self.group = group if group.present? && group.level < allowed_level
end
Does that help?
Well if you have a User/Groups (or User/Roles) model there is no other way to go than that you have underlined.
If it is a one-to-many association you can choose to store the user group as a string and if it is a many-to-many association you can go for a bitmask but nonetheless either through business logic or admin choice you need to set the User/Group relation.
You can have several choices on how to set this relationship in a view.
To expand your model's capability I advice you to use CanCan, a very good authorization gem which makes it super easy to allow fine grain access to each resource in your rails app.