Calling a Model method from controller - ruby-on-rails

I have a user view where I allow a user to upload his own image or allow him to import image from his LinkedIn profile. My LinkedIn OAuth works fine and returns the url of the image located on linkedin servers. I have a picture_from_url method in my User model which is called by UsersController in it's update method. I set the url to a sessions variable and then send that to the model method in the controller. This ,however, is not updating the image for the user. I have the papertrail gem setup so he can use the image from his computer and change his profile picture just fine.
My Model method
def picture_from_url(url)
encoded_url = URI.encode(url.to_s)
avatar= URI.parse(encoded_url)
end
My Controller Call
def update
#user = User.find(params[:id])
#user.picture_from_url(session[:linkedin_picture])
respond_to do |format|
if can?(current_user, #user)
if #user.update_attributes(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render_success #user }
else
format.html { render action: 'edit' }
format.json { render_error #user }
end
else
format.html { render action: 'edit', status: :unprocessable_entity }
format.json { render_error #user }
end
end
end

You're not actually saving it (as you said).
You supply your picture_from_url method with a URL, encode and parse it. The last thing you do with it is set it to avatar. I'm guessing that's an attribute on your User model. You're not saving it though.
The following line in your controller doesn't save it either:
#user.update_attributes(user_params)
The above line is updating your #user record with the specified user_params and the URL isn't specified in it.
So, you either have to save your #user record when you call the picture_from_url method like this:
def picture_from_url(url)
encoded_url = URI.encode(url.to_s)
update_attributes avatar: URI.parse(encoded_url)
end
Or you can add the formatted URL (which you can retrieve from the picture_from_url method) to the user_params hash which is than saved when update_attributes is called in your controller, like this:
def picture_from_url(url)
encoded_url = URI.encode(url.to_s)
URI.parse encoded_url
end
Change the picture_from_url method to the above in your User model (technically you don't have to do this, but it is probably more readable) and your update action to the following:
def update
#user = User.find(params[:id])
user_params.merge! avatar: #user.picture_from_url(session[:linkedin_picture])
...
end

Related

Ruby on rails: previous url helper for entire application

Is there an easy way to write a helper method to always update the previously visited url in the session. I have tried the method below but the url saved is always the current one. I would like to be able to use this helper in all my controllers for redirect.
#application_controller.rb
class ApplicationController < ActionController::Base
before_filter :my_previous_url
def my_previous_url
session[:previous_url] = request.referrer
end
helper_method :my_previous_url
end
I have used it in my update method in the User controller as seen below but it always redirects to the same opened url (kind of looks like refresh was hit).
def update
if current_user.admin == true and #user.update(user_params)
redirect_to my_previous_url, notice: "Password for User #{#user.username} has Successfully been Changed."
return
elsif current_user.admin == false and #user.update(user_params)
session[:user_id] = nil
redirect_to login_path, notice: "Password for User #{#user.username} has Successfully been Changed. Please Log-In Using the New Password."
return
end
respond_to do |format|
if #user.update(user_params)
changed = true
format.html { redirect_to logout_path }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
request.referer isn't what you want here as it will be set on page redirects, thus losing the page you came from originally. I think that you have an implied requirement that it should return the last visited url which was different to the current one, is that the case? Also, i think that you would only want to set it for GET requests, otherwise you risk sending people back to the wrong url, since they will be sent back with a GET request. I'm assuming here that the purpose of this previous_url is to give people a "back" link.
Also, don't get the method to set the previous_url mixed up with the method to read it back out again.
I would do it like this:
#application_controller.rb
class ApplicationController < ActionController::Base
before_filter :set_previous_url
helper_method :previous_url
def set_previous_url
if request.method == :get && session[:previous_url] != session[:current_url]
session[:previous_url] == session[:current_url]
session[:current_url] = request.url
end
end
def previous_url
session[:previous_url]
end
end

Can't overwrite the active admin default redirect path

I am using active admin in my application. In my controller, I have an action update with redirect_to function. But while updating, it threw me an error.
Render and/or redirect were called multiple times in this action. Please note that you may only call render or redirect, and at most once per action. Also note that neither redirect nor render terminates execution of the action, so if you want to exit an action after redirecting, you need to do something like redirect_to(...) and return.
def update
#user = User.find(params[:id])
#user.update
mailers.notify(#user).deliver
redirect_to user_path(#user)
end
I tried
only redirect_to
redirect_to() and return
but nothing works.
before_filter :only =>[:create,:update] do
if self.action_name.to_sym == :create
#user = User.new(params[:user])
else
#user = User.find(params[:id])
end
I fix it by adding this to my update action .This works for me fine.
def update
update!do |format|
format.html { redirect_to_user_path(#user)}
end
What is your purpose? Why you use mailers.notify(#user).deliver inside the update action?
Move the notification to after_update filter in your User model, smth like this:
after_update :send_notifications
def send_notifications
Mailer.notify(self).deliver
end
Change the update method to smth like this:
def update
super do |format|
redirect_to user_path(#user), notice: "Your notice message" and return if resource.valid?
end
end

Nested Controller's create Action respond_to Fails To Render :new If Save Fails

I have a nested Photo controller with the following action:
def create
#photo = #gallery.photos.new(photo_params)
if #photo.save
flash[:notice] = "Created new photo"
else
flash[:error] = "Couldn't create photo"
end
respond_with [#gallery, #photo]
end
If the photo params are valid, it redirects to gallery_photo_path correctly, however if the params are invalid and #photo doesn't save successfully, rather than rendering the :new template as I would expect, it redirects to /galleries/1/photos which doesn't exist.
If I don't use respond_with and hardcode the render, everything works fine:
def create
#photo = #gallery.photos.new(photo_params)
if #photo.save
flash[:notice] = "Created new photo"
redirect_to gallery_photo_path #gallery, #photo
else
flash[:error] = "Couldn't create photo"
render :new
end
end
Why does respond_to fail to render the new template when the save fils as outlined in the docs?
Notes
I am using respond_to :html in the controller and all other actions behave as expected.
The docs state to use the respond_with with the objects as parameters, not with an explicit array, so
respond_with [#gallery, #photo]
should be
respond_with(#gallery, #photo)

Instantiate instance variable in helper method from controller

I have a helper which instantiates a model and renders a form. This form should be available to any view in the application
# support_form_helper
def support_form
#support_stats = SupportForm::Stat.find(get_stats_id)
#enquiry = SupportForm::Enquiry.new(stats_id: #support_stats.id)
render partial: 'support_form/enquiries/form'
end
And its rendered in the view:
# some_view.html.erb
<%= support_form %>
This is fine until I want to submit the form and validate it in the controller.
# enquiries_controller.rb
def create
#enquiry = SupportForm::Enquiry.new(params[:support_form_enquiry])
topic = #enquiry.topic
#stat = SupportForm::Stat.find(#enquiry.stats_id)
#stat.stats[topic] = #stat.stats[topic].to_i.next
respond_to do |format|
if #enquiry.valid? && #stat.save
format.html { redirect_to(root_path) }
else
format.html { redirect_to(:back) }
end
end
end
This is where I can't render the previous view with the errors attached to the invalid object. The helper gets invoked again and initializes a new #enquiries object, without the errors obviously.
How can I render the form in many views across an application and still return to the view with the object together with errors when it is invalid?
I found an answer which answers my question but its a bad idea:
Render the action that initiated update
def create
#enquiry = SupportForm::Enquiry.new(params[:support_form_enquiry])
topic = #enquiry.topic
#stat = SupportForm::Stat.find(#enquiry.stats_id)
#stat.stats[topic] = #stat.stats[topic].to_i.next
if #enquiry.valid? && #stat.save
redirect_to(root_path)
else
render Rails.application.routes.recognize_path(request.referer).values.join("/")
end
end
The problem is that there will likely be instance variables in the view that submitted the form and I would have to be able to instantiate all the instance variable in the application then.....not possible.
Currently I'm considering putting the errors in the flash hash... not something I want to do. With the original object returned i can repopulate the fields with the users input.
When you use redirect_to, rails will kick off a whole new controller & view sequence. Use
render "path/to/template/from/view/folder"`
instead.
A typical create action using this pattern would look like (for a 'post' object in this case):
def create
#post = Post.new(params[:post])
#created = #post.save
respond_to do |format|
if #created
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to post_path(#post) }
format.js
else
format.html { render :action => :new }
format.js
end
end
end
Notice how if it's successfully created we do a full redirect to the "show" page for the post, but if it's not successful we just do a render.
You should probably modify your support_form helper so that it only creates a new #enquiry if it hasn't been created already:
def support_form
#support_stats = SupportForm::Stat.find(get_stats_id)
#enquiry ||= SupportForm::Enquiry.new(stats_id: #support_stats.id)
render partial: 'support_form/enquiries/form'
end
||= is shorthand for "equals itself or". If it hasn't been defined (or is nil or false) then it will fail the first part of the or and pass through to the second, where the object is created.
In your form partial, also, you should make sure you're using form_for, which will submit to the create or update action depending on whether the object has been saved already.

New to jQuery/Ajax how can I add a user relation to creating a new review?

I have been trying to get to grips with jQuery and been following a railscast on adding an Ajax add review form, which works fine but I would now like to add into it the ability for a review to belong to a user as well as a venue.
Reviews controller
def create
#review = Review.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
views\reviews\create.js.erb
$("#new_review").before('<div id="flash_notice"><%= escape_javascript(flash.delete(:notice)) %></div>');
$("#reviews_count").html("<%= pluralize(#review.venue.reviews.count, 'Review') %>");
$("#reviews").append("<%= escape_javascript(render(:partial => #review)) %>");
$("#new_review")[0].reset();
I have tried changing the controller to:
def create
#review = #current_user.reviews.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
but it just wont submit, with no errors.
I think I have the models set correctly with belongs_to and has_many, I think this is a controller issue I'll add other code bits if needed.
Development log
NoMethodError (undefined method `reviews' for nil:NilClass):
app/controllers/reviews_controller.rb:14:in `create'
Thanks for any help!
It appears that your error is residing with #current_user. According to your development log, #current_user is nil when you call #current_user.reviews on it. I would say track down where this #current_user instance variable is being set and find out why it is nil. Now, what kind of authentication are you using? Most authentication plugins, especially those used by Ryan Bates of the Railscasts you mentioned, use a local variable, say just current_user, as the means to access the currently signed in user. I know I do in all my code.
So, rewrite the line as
#review = current_user.reviews.create!(params[:review])
and see if that works. If it doesn't, change it back and then track down where this #current_user is being set. Chances are good it is being set in a before_filter :method_name at the beginning of your controller.
Calling create! (with exclamation mark) will throw an exception and thus abort your create action if saving fails. Check your log/development.log for these exceptions.
Use build instead of create and lose the exclamation mark.
def create
#review = #current_user.reviews.build(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end

Resources