Rails After Validated Save Go to Edit Path - ruby-on-rails

This seems like a fairly simple problem to me but I have been having some issues.
In one of my views I use something like
<% if current_page?(:controller => "activities", :action => "new") %>
*Do something here*
<% end %>
and it does something specific on the new page for a form. Easy enough and it works great.
Unfortunately, I've found that when you have a "new activity" form (assume normal scaffolding controller), the url will go from
http://localhost:3000/activities/new
after submitting an error prone form to
http://localhost:3000/activities
but it will still show the new activity form with the respective errors. So basically everything works how it is supposed to EXCEPT that I need the url to be http://localhost:3000/activities/new for the current_page? function to recognize that it is indeed a new form page.
I'm wondering if there is some kind of work around to this issue. Thanks!
OH and here is the controller code, in case anybody needs to see it
Controller Code
def new
#activity = Activity.new
end
def create
#activity = Activity.new(params[:activity])
if #activity.save
flash[:notice] = "Successfully created activity."
redirect_to #activity
else
render :action => 'new'
end
end

Think you will need to check for create as well as new
<% if current_page?(:controller => "activities", :action => "new") or current_page?(:controller => "activities", :action => "create") %>
not so pretty maybe wrap it up in a helper method?

You could also check if the created at field is blank. As it won't be set till the activity is created.

Related

Unable to save merged articles

So I'm trying to create a feature for Typo (blogging app) that merges two articles in one. For some reason, I can't manage to save the merged article. I have followed several threads here, read over and over Rails and Ruby docs... And Can't figure out why it doesn't work
Besides finding what's wrong with my code, I'd like to know best solutions to see what's going on 'under the hood', to debug the code. Eg: See when methods are called, what parameters are passed...
Here is my code:
View:
<% if #article.id && #user_is_admin %>
<h4>Merge Articles</h4>
<%=form_tag :action => 'merge_with', :id => #article.id do %>
<%= label_tag 'merge_with', 'Article ID' %>
<%= text_field_tag 'merge_with' %>
<%= submit_tag 'Merge' %>
<% end %>
<% end %>
Controller
def merge_with
unless Profile.find(current_user.profile_id).label == "admin"
flash[:error] = _("You are not allowed to perform a merge action")
redirect_to :action => index
end
article = Article.find_by_id(params[:id])
debugger
if article.merge_with(params[:merge_with])
flash[:notice] = _("Articles successfully merged!")
redirect_to :action => :index
else
flash[:notice] = _("Articles couldn't be merged")
redirect_to :action => :edit, :id => params[:id]
end
end
Model:
def merge_with(other_article_id)
other_article = Article.find_by_id(other_article_id)
if not self.id or not other_article.id
return false
end
self.body = self.body + other_article.body
self.comments << other_article.comments
self.save!
other_article = Article.find_by_id(other_article_id)
other_article.destroy
end
Thanks in advance, and sorry if this is a rookie question :)
You did not mentioned what problem you are facing while saving, you just said you could not manage to save so I can't help you with that unless you provide some stack trace.
I will mention a few things though:
first is in your controller method you have multiple redirection code like redirect_to :action => index without any return from method so I think you will get multiple redirect or render error at some point like when unless executes and redirects but code continues the execution and throws error so try to reduce these redirects or mention it like redirect_to :action => index and return.
Then in model merge_with you are assigning other_article twice, you don't need the second one.
about debugging, you can create some puts line inside code and check it in rails server console to verify that the condition is executed like in controller method after if article.merge_with you can put:
puts "merge sucess"
and check console when merge action is called, if you see "merge sucess" then if block executed.
OR
use byebug like you used debugger. It will stop the execution where it will find the byebug word and will give access to a live session in rails console.
if you put it where you have debugger you can access the console and do the operations manually like run:
article.merge_with(params[:merge_with])
then see what happens. or put before self.save! in model and save it manually in console and check errors like self.errors.messages.
Stack trace is also helpful to see line by line code execution and identify the error.
I will update this if you post any info about what error you are facing

Rails View doesn't update until refreshed but controller is updating database fine

This is the first time I've encountered this problem.
I have a view which submits a post request to a controller which updates two tables.
def update
if request.post?
if #circuit
# update
#circuit.update_attributes params[:circuit]
#logical_interface = LogicalInterface.new params[:logical_interface]
#logical_interface.save
#redirect_to :action => 'update', :id => #circuit.id
#success = "Updated." if #circuit.valid?
else
# attempt create
end
end
end
These three lines are what I've added to the controller:
#logical_interface = LogicalInterface.new params[:logical_interface]
#logical_interface.save
redirect_to :action => 'update', :id => #circuit.id # this was added because the view wasn't being updated until refreshed
If I keep the redirect, the view will be updated accordingly but I get no Updated. message in the #success variable.
If I comment out the redirect, the circuit form fields at the top of my form will update but not the table of logical_interfaces that I am adding to but I still get the Updated. success message. Everything is in the view directly, no partials are used.
Hopefully I've explained it properly but if anyone is unsure then I can update the question to go into more detail.
The form is just:
<%= form_tag :controller => "circuit", :action => "update" %>
...
</form>
In the form I use two objects circuit and logical_interface to split up the inputs so that in the controller I can update the circuit and create a new logical_interface.
try to adjust positions of redirect_to and #success, I think redirect_to should be the last line of the block.
And If you use redirect_to, you will lose all your instance variables, so better way is using flash.
in your controller:
flash[:notice] = "Updated." if #circuit.valid?
redirect_to :action => 'update', :id => #circuit.id
in your page:
<p><%= flash[:notice]%></p>

Reload page or div using RJS

I have a 'like'-counter in a rails project. Whenever one clicks this 'like'-link I want to add one to the like_counter in the database. The logic works well and the database is updated, but I cannot figure out how to set up the Ajax Request correctly so it refreshes the page or the div when completed.
In my view this looks as follows:
<%= link_to_remote 'like', :url => {
:controller => 'projects',
:action => 'like_it',
:id=> #project.id }%>
The controller:
def like_it
#project = Project.find(params[:id])
#project.update_like
render :update do |page|
page.reload
end
end
the update_like method is in the model and just adds one to the counter and saves the project (this part works).
As for the page.reload I Firefox throws an RJS-Error: ReferenceError: Reload is not defined. And the page is not reloaded
What do I do wrong? Is there a more distinguished way to just reload the div containing the counter? Any help is much appreciated!
Try:
render :update do |page|
page << "window.location.reload()"
end

Prompt a user to login after he takes a certain action

One thing you can do on my rap lyric explanation site is "like" explanations (once you're logged in):
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1645.png
I'd like to show the "Like" links to users who aren't logged in, and then, when a non-logged in user clicks "Like", show him a lightbox with a "Login or Register" form (like Digg / Reddit)
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1650.png
What's the best way to accomplish this?
Currently I'm using this approach:
Clicking "Like" POSTs to /annotations/:id/vote (the POST body indicates whether the user is liking or "unliking").
The vote Annotation controller action has a require_user before_filter that looks like this:
def require_user
unless current_user
store_desired_location
flash[:notice] = "You'll need to login or register to do that"
redirect_to login_path # map.login '/login', :controller => 'user_sessions', :action => 'new'
return false
end
end
user_sessions#new looks like this:
def new
#user_session = UserSession.new
respond_to do |format|
format.html {}
format.js {
render :layout => false
}
end
end
The problem is that the redirect doesn't seem to work correctly over javascript:
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1700.png
How do I get this to redirect correctly?
Also, is this the right general approach? Another thought I had was to attach a different handler to the "Like" links in javascript when there was no logged in user (but I don't think this method scales well to other actions that I'd like to handle the same way)
There's a few problems to overcome here.
Browsers in general do not allow redirecting to a POST request.
redirect_to doesn't preserve format without additional input.
Store location does not preserve form data.
All these problems can be solved by eliminating redirects.
Here is how I've handed it in the past:
Instead of redirecting in required_user, render. If a before filter redirects or renders the pending action is cancelled. (No need to return false either). Unfortunately going this route blurs controller boundaries. But allows for simple html fallback, and lends its self to DRYness.
The high level view of the new work flow will be:
Request to annotations#vote (POST)
required_user filter fails
render new session
submit login information and original POST data back to annotations#vote (POST)
new filter in vote captures session information and logs in. vote proceeds as expected. If login fails return to 3.
annotations#vote redirects/renders as it should
Start by reworking the require_user to render the user_sessions#new template.
def require_user
unless current_user
flash[:notice] = "You'll need to login or register to do that"
#user_session ||= UserSession.new
respond_to do |format|
format.html {render :template => 'user_sessions/new'}
format.js {
render :template => 'user_sessions/new', :layout => false
}
end
end
end
The #user_session ||= UserSession.new ensures we can return validation errors to the form.
Now we've got to beef up your user_session#new template so that it can remember the action. Also if you plan on using lightboxes, this should be a partial rendered rendered by relevant RJS or the new.html.erb.
First we create a partial to create hidden fields preserving the POST data that would have been lost in a redirect:
<% if params[:controller] == "annotations" %>
<% content_for :old_form do %>
<%= hidden_field_tag "annotation[song_id]", params[:annotation][:song_id] %>
<%= hidden_field_tag "annotation[vote]", params[:annotation][:vote] %>
<% end %>
<% end %>
Then render that partial in the login partial that will occupy your lightbox:
<%= render :partial => vote_form_replica %>
<% url = params[:controller] == "user_sessions ? user_sessions_url : {} %>
<% form_tag #user_session, :url => url do |f| %>
<%= yield :old_form %>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= submit_tag %>
<%end%>
The empty hash for url in the form_tag looks like an error, but isn't. It ensures that the form data is posted to the url that rendered the form. Which at this point should be annotations/:id/vote
Now for the new filter to login. Essentially it will be doing what ever UserSessionsController#create does without the render/redirect. The following is copied from the RESTful authentication plugin.
def authenticate
self.current_user = User.authenticate(params[:login], params[:password])
if logged_in?
if params[:remember_me] == "1"
current_user.remember_me unless current_user.remember_token?
cookies[:auth_token] = { :value => self.current_user.remember_token,
:expires => self.current_user.remember_token_expires_at }
end
end
end
All that's left is to make sure the filter order is right.
before_filter :authenticate, :require_user, :only => :vote
N.B.: You're probably not going to use this version of require_user without this version of authenticate so it makes sense to combine them into a single filter.
And that's it. The way this has been set up allows for robust DRY easily reuseable code. By placing the new filters into ApplicationController they're available in any controller. From this point, adding this functionality to any other controllers/actions takes only 3 simple steps:
Create a new partial modelled after the vote_form_replica partial.
Add the corresponding render statement to the new session template.
Apply the filters to your actions.
I would approach this in the way you describe at the bottom of your question. Before displaying the page initially, check if the user is logged in. If they are, the "Like" links should use their normal behavior. If not, bind a click event to show the register/login panel. There's nothing about this that can't be reused. In fact, we use this exact method at my job. Any user action that requires authentication either follows its normal behavior or pops up a generic login panel depending on login state at the time the page loads.

Rails RESTful controller and rendering after custom action

How can I render after executing an action in a restful controller instead of redirecting.
I have a controller with standard actions, and I added a special action that adds data to the resource in question, via a form on the #show page (Think comments on a post). I am validating the input from the form and want to re-render the show action on error and redirect to the show action on success.
I want to render to save the user from inputting their info twice, but when I try to render the show action with an error in the flash[:notice] I get an error saying that I am not specifying an ID. When I do specify an ID, it tries to render a new template that doesn't exist yet.
I am thinking that it should be a as simple as:
def add_comment
if my_validation?
save the object
redirect_to :action => "show", :id => params[:id]
else
render :action => "show", :id => params[:id]
end
end
This is not my actual code, just something I put together just now as an example.
The best way is to re-render the :new
def create
#obj = TheObject.new(params[:object])
render :action => :new unless #obj.save
end
And in the new.html.erb
<% form_for :obj,
:url => object_url(#obj), :html => {:method => :post} do |f| %>
<%= f.text_field :name %>
<% end %>
That way, the inputs in the form will be pre-filled with what the user entered.
Create a new data object and add the values from the form, before you rerender, think it would work then. If you still get problems, try setting a boolean for editing new vs. existing rows, or create two different views entirely.
I've done it before but I don't quite remember how. Sometimes when I used the very typical use of the MVC pattern, it was allmost "automagical", othertimes (as I had to use an old quirky database) I had to code all the magic myself; sometimes usin the .new? function (or what it was called) on the ActiveRecord object, othertimes I used temporary "magic values" for ID (typically alphabetic strings for invalid id values.
(I appologize if I made some mistakes, it's a while since I coded Rails code...)

Resources