I am trying to perform some integration testing to see if the article gets updated. Here is my integration test:
test "update existing article" do
# create an article
#article = Article.new
#article.title = "title"
#article.abstract = "abstract"
#article.description = "description"
if #article.save
post "/articles/edit", :id => #article.id
assert assigns(:article)
end
end
And here is my articles_controller implementation:
def edit
#article = Article.find(params[:id])
respond_to do |format|
format.html
format.xml { render :xml => #article }
end
end
The last line in test about assigns fails. From my understanding assigns(:article) means that the variable #article will be populated.
Take a look at this line:
post "/articles/edit", :id => #article.id
The problem is that the edit action takes a GET, not a POST, so it's probably never getting called. try:
get edit_article_path, :id => #article.id
(note that if you're running a controller test, it's best to use a symbol for the action name.)
Related
When I try to delete #article, I am getting this error: ActiveRecord::RecordNotFound in ArticlesController#destroy, Couldn't find Article with id=1... But when I go back on Index page, #article is deleted.
What should I do?
Destroy method:
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to 'root'
end
Link for delete(/show.html.erb):
<%= link_to "delete", article_path(#article), method: :delete %>
If you need more files, I will upload them..
Try using:
redirect_to :root if #article.destroy
If you want to avoid errors when hitting the destroy action for a record that is already deleted you could use the following:
def destroy
if article = Article.find_by(id: params[:id])
article.destroy
end
redirect_to root_path
end
find_by returns nil if it doesn't find the record while find throws an exception. nil is considered 'falsey' by ruby which will bypass the body of the if when the record is not found.
It is also best to use a path helper to avoid having 'magic' strings in your code that you have to remember to update if you decide to change your routes file.
Simply Just change a little in the redirect_to line
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to :action => :index #this will redirect to article index page if delete action was successful
end
Like #bhanua1 say
try using redirect to :root if #article.destroy
The functionality I'm trying to build allows Users to Visit a Restaurant.
I have Users, Locations, and Restaurants models.
Locations have many Restaurants.
I've created a Visits model with user_id and restaurant_id attributes, and a visits_controller with create and destroy methods.
Thing is, I can't create an actual Visit record. Any thoughts on how I can accomplish this? Or am I going about it the wrong way.
Routing Error
No route matches {:controller=>"restaurants", :location_id=>nil}
Code:
Routes:
location_restaurant_visits POST /locations/:location_id/restaurants/:restaurant_id/visits(.:format) visits#create
location_restaurant_visit DELETE /locations/:location_id/restaurants/:restaurant_id/visits/:id(.:format) visits#destroy
Model:
class Visit < ActiveRecord::Base
attr_accessible :restaurant_id, :user_id
belongs_to :user
belongs_to :restaurant
end
View:
<% #restaurants.each do |restaurant| %>
<%= link_to 'Visit', location_restaurant_visits_path(current_user.id, restaurant.id), method: :create %>
<% #visit = Visit.find_by_user_id_and_restaurant_id(current_user.id, restaurant.id) %>
<%= #visit != nil ? "true" : "false" %>
<% end %>
Controller:
class VisitsController < ApplicationController
before_filter :find_restaurant
before_filter :find_user
def create
#visit = Visit.create(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
respond_to do |format|
if #visit.save
format.html { redirect_to location_restaurants_path(#location), notice: 'Visit created.' }
format.json { render json: #visit, status: :created, location: #visit }
else
format.html { render action: "new" }
format.json { render json: #visit.errors, status: :unprocessable_entity }
end
end
end
def destroy
#visit = Visit.find(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
#restaurant.destroy
respond_to do |format|
format.html { redirect_to location_restaurants_path(#restaurant.location_id), notice: 'Unvisited.' }
format.json { head :no_content }
end
end
private
def find_restaurant
#restaurant = Restaurant.find(params[:restaurant_id])
end
def find_user
#user = current_user
end
end
I see a lot of problems here. The first is this line of code in your VisitController's create action (and identical line in your destroy action):
#visit = Visit.create(params[:user_id => #user.id, :restaurant_id => #restaurant.id])
params is a hash, so you should be passing it a key (if anything), not a bunch of key => value bindings. What you probably meant was:
#visit = Visit.create(:user_id => #user.id, :restaurant_id => #restaurant.id)
Note that you initialize #user and #restaurant in before filter methods, so you don't need to access params here.
This line of code is still a bit strange, though, because you are creating a record and then a few lines later you are saving it (if #visit.save). This is redundant: Visit.create initiates and saves the record, so saving it afterwards is pretty much meaningless. What you probably want to do is first initiate a new Visit with Visit.new, then save that:
def create
#visit = Visit.new(:user_id => #user.id, :restaurant_id => #restaurant.id)
respond_to do |format|
if #visit.save
...
The next thing I notice is that you have not initiated a #location in your create action, but you then reference it here:
format.html { redirect_to location_restaurants_path(#location), notice: 'Visit created.' }
Since you will need the location for every restaurant route (since restaurant is a nested resource), you might as well create a method and before_filter for it, like you have with find_restaurant:
before_filter :find_location
...
def find_location
#location = Location.find(params[:location_id])
end
The next problem is that in your view your location_restaurant_path is passed the id of current_user and of restaurant. There are two problems here. First of all the first argument should be a location, not a user (matching the order in location_restaurant_path). The next problem is that for the _path methods, you have to pass the actual object, not the object's id. Finally, you have method: :create, but the method here is referring to the HTTP method, so what you want is method: :post:
link_to 'Visit', location_restaurant_visits_path(#location, restaurant.id), method: :post
You'll have to add a find_location before filter to your RestaurantController to make #location available in the view here.
There may be other problems, but these are some things to start with.
location_id is nil and the path definition doesn't say (/:location_id) forcing a non-nil value there in order to route to that path; create a new route without location_id if you can derive it from a child's attribute (i.e. a restaurant_id refers to a Restaurant which already knows its own location_id).
In my Ruby on Rails application, each group has_many :expenses. I have nested my routes, so expenses are entered only as child entities of their parent groups. Here's an excerpt from routes.rb.
resources :groups do
resources :expenses
end
I cannot figure out how to render the 'new' action in the case of an expense not saving when it is submitted through /groups/:group_id/expenses/new. In my expenses_controller.rb, here is how the create action is defined:
def create
#expense = Expense.new(params[:expense])
#expense.group_id = params[:group_id]
if #expense.save
redirect_to group_expense_path(#expense.group.id, #expense.id)
else
render 'new'
end
end
Everything works fine if I satisty expense validation and #expense.save winds up working. However, when it fails and the code tries to render 'new' I get:
undefined method `expenses_path' for #<#<Class:0x007fd408b1fd58>:0x007fd408f21ca8>
So, I am assuming I have something about my nested routing wrong. How do I return the user to the new form but still display to him/her through the flash[] params the errors with the data they originally attempted to submit?
The problem is that #group is not initialized
So in your controller just do
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Looks like you need to explicitly specify the url for form_for in your view.
Something likeā¦
<%= form_for #expense, :url => group_expenses_path(#group.id) do |f| %>
...
<% end %>
In your <%= form_for %> you have used #group for url, because expenses belongs_to groups. But inside your create action in the controller you have not defined what is #group, so first you should define it as:
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Also I would suggest to use respond_to in your controller:
respond_to do |format|
if #expense.save
format.html { redirect_to group_expense_path(#group.id, #expense.id), :notice => "Any msg you want" }
else
format.html { render :action => "new" }
end
end
All of these are in your create action inside the controller.
Also for different rendering methods look up: http://guides.rubyonrails.org/layouts_and_rendering.html
Hope this helps!
I'm having an issue where my article_controller.rb's create method is redirecting to the index when the article.save fails due to invalid input by the user. The articles creation url is /articles/new but when the submit fails, I'm redirected to /articles. The form is still available in /articles exactly as it was on /articles/new. The desired behavior would be to return to the /articles/new with whatever the user may have entered repopulated in the form. Is there a way to do this? Here are some of the code snippets to illustrate what's going on.
Here is the article new method:
def new
#article = Article.new
respond_to do |format|
format.html
end
end
Here is the article create method:
def create
#article = current_user.articles.new(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to(#article, :notice => 'Article was successfully created.') }
else
format.html { render 'new' }
end
end
end
Here is the form:
<%= form_for(#article) do |f| %>
.....
<% end %>
I'm eventually hoping to get this working with a :remote => :true call in the form_for, but just want to get it working first the way it is. Any suggestions?
Try
format.html { render :action => "new" }
And if you are using Rails 3+, try writing your controller something like this DRY.
class ArticlesController < ApplicationController
respond_to :html
def new
#article = Article.new
respond_with #article
end
def create
#article = Article.new(params[:article])
#article.save
respond_with(#article)
end
end
I'm using Ruby on Rails 2.3.8 and I've got a registration form in which I receive a parameter as follows: /registration/4, which 4 is the id of a user who recommended the user that is about to register in the website.
The problem is that if the validation fails when the user submits the registation (the form renders to the controller users, action create_particular) the site will redirect to /users/create_particular, and therefore I lose the parameter with value 4 that I had before. Besides, I want the user to stay at the same url, which is /registration/4
How can I do that?
Then you should rewrite your create method. You should use redirect_to :back instead of render :action
UPD
def new
#word = Word.new(params[:word])
#word.valid? if params[:word]
end
def create
#word = Word.new(params[:word])
if #word.save
redirect_to #word
else
redirect_to new_word_path(:word => params[:word] )
end
end
Looks quite dirty, but this is just a scratch
UPD 2
This is really not the best solution, but it works
# routes.rb
match 'words/new' => 'words#create', :via => :post, :as => :create_word
# words_controller
def new
#word = Word.new
end
def create
#word = Word.new(params[:word])
respond_to do |format|
if #word.save
format.html { redirect_to(#word, :notice => 'Word was successfully created.') }
else
format.html { render :action => "new" }
end
end
end
# views/words/new.html.erb
<%= form_for(#word, :url => create_word_path) do |f| %>
...
<% end %>
Submit to the current URI (e.g. action=""). When the submission is valid, redirect. POST->Redirect->GET is a good habit.
From the top of my head:
Edit your controller (registrations_controller.rb file). Create method by default contains following piece of code:
if #registration.save
format.html { }
format.xml { }
else
format.html { }
format.xml { }
end
Add redirect_to (:back) between brackets to else format.html{}
Ok I solved the problem by doing the following:
1) I created two routes with the same path, but with different conditions method (one it's post and the other one is set to get)
2) I changed the form in order to post to the POST action defined above
3) I added render => :my_action when the validation fails
So that's pretty much it.
Thanks anyway for all your help.
Hidden field. That user ID param has a name by which you extract it in your controller, right? So just put that value in a hidden field of the same name, then it will survive a round-trip.
For example:
<%= hidden_field_tag :referring_user_id, params[:referring_user_id] %>