Strong parameters issue with custom controller? - ruby-on-rails

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 :)

Related

Devise: Associate record on login

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

Changing JSON field names in Rails responses

I need to allow for customization of json output per account. One person may like the standard json response. Another may wish to have the exact same feed except they need the "name" field called "account_name" or something.
So with a standard user they may be happy with
#model.to_json(only: [:name, :title, :phone])
Another user may wish to see their json response as
#model.to_json(only: [:name (AS ACCOUNT_NAME) , :title, :phone])
I have found solutions that override the as_json/to_json in the model but those seem to be solutions that effect everyone. How can I change them just on a per access/per account basis?
I think in your case, it is better to push the logic to the view layer to make the code clean by using Jbuilder.
so instead of override to_json method you can do something like below:
# app/views/users/show.json.jbuilder
json.model_name do
json.title #current_user.title
json.phone #current_user.phone
if current_user.type_account_name? # need to implement this logic somewhere in your app
json.account_name #current_user.name
else
json.name #current_user.name
end
end
update
the controller looks something like this
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
end

Rails: activeadmin overriding create action

I have an activeadmin resource which has a belongs_to :user relationship.
When I create a new Instance of the model in active admin, I want to associate the currently logged in user as the user who created the instance (pretty standard stuff I'd imagine).
So... I got it working with:
controller do
def create
#item = Item.new(params[:item])
#item.user = current_curator
super
end
end
However ;) I'm just wondering how this works? I just hoped that assigning the #item variable the user and then calling super would work (and it does). I also started looking through the gem but couldn't see how it was actually working.
Any pointers would be great. I'm assuming this is something that InheritedResources gives you?
Thanks!
I ran into a similar situation where I didn't really need to completely override the create method. I really only wanted to inject properties before save, and only on create; very similar to your example. After reading through the ActiveAdmin source, I determined that I could use before_create to do what I needed:
ActiveAdmin.register Product do
before_create do |product|
product.creator = current_user
end
end
Another option:
def create
params[:item].merge!({ user_id: current_curator.id })
create!
end
You are right active admin use InheritedResources, all other tools you can see on the end of the page.
As per the AA source code this worked for me:
controller do
def call_before_create(offer)
end
end

Create Rails model with argument of associated model?

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

Rails 3 - Form Validation - Moving Logic to an Callback or Observer?

Hey all, I'm having issues with validation that I've been working on the last couple of days with no luck. What I have is that I have a model that requires the user to input a URL. I've done this with the following in my model:
validates :url, :presence => true
When a user submits their form, I take their URL and open it with Nokogiri to pull basic things like the webpage title. I'm currently doing this with my Create method in the controller. Code looks like so:
def create
require 'open-uri'
#page = Page.new(params[:page])
doc = Nokogiri::HTML(open(#page.url))
The problem I run in to, is that if the user enters a blank form, Nokogiri will error out as it runs even though I've tried to validate the form.
My question is, should I be moving this sort of logic to a callback or an observer? I'm pretty new to rails but would there be a way for me to work with my data/instance variables from a callback/observer? I've failed simply using #page, but was wondering if there was a way I'm supposed to pass it into the callback/observer if this is where this sort of logic is supposed to be placed?
Thanks
Would be better to put this in the model.
Controller method does something like
def create
#page = Page.new(params[:page])
respond_with #page
end
and in the model you have
class Page < ActiveRecord::Base
...
before_save :pull_info_from_url
def pull_info_from_url
doc = Nokogiri::HTML(open(self.url))
...
end
end
the before_save callback is run after validations, so if the presence-check fails, this code doesn't get executed, and the form with errors is shown instead.

Resources