I'm having an unusual problem in an application I am developing.
I am writing a controller/view to generate a sitemap.xml resource. It works perfectly the first time I test it by viewing it in the browser.
However the second time, when I refresh the view, no page results are returned and the sitemap is effectively empty.
If I make a change to the code. It could be as little as adding a blank line, or removing it, then the sitemap is correctly generated the first time. Any additional refreshes are empty.
This is the code:
The sitemap controller
class SitemapController < ApplicationController
layout nil
def index
#pages = Page.where(:publish => true)
#base_url = "http://#{request.host_with_port}"
headers['Content-Type'] = 'application/xml'
def index
respond_to do |format|
format.xml
end
end
end
end
Here is sitemap.xml.erb
<?xml version="1.0" ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<% #pages.each do |page| %>
<url>
<loc><%= "#{#base_url}/#{page.permalink}" %></loc>
<lastmod><%= page.updated_at %></lastmod>
<priority>0.5</priority>
</url>
<% end unless #pages.nil? %>
</urlset>
and the route
match "/sitemap.xml", :to => "sitemap#index", :defaults => {:format => :xml}
The odd thing is that it seems to be in the query in the controller.
#pages = Page.where(:publish => true)
This returns nil on consecutive attempts, but a similar query in other parts of the app works every time. I have tried using alternative methods such as Page.all, and Page.find :all but the problem persists.
I also uploaded this to the app on Heroku, wondering if it was something in my environment, but it happens there also.
Your SitemapController#index method is redefining itself. To clarify the problem:
def fn
def fn
2
end
1
end
fn
# => 1
fn
# => 2
Try instead:
class SitemapController < ApplicationController
layout nil
def index
#pages = Page.where(:publish => true)
#base_url = "http://#{request.host_with_port}"
headers['Content-Type'] = 'application/xml'
respond_to do |format|
format.xml
end
end
end
Also, the sitemap_generator gem works rather well.
Related
New to rails 3
I would like to create a contact form that people fill out, its saved to the db and then a thank you page comes up.
I would like to do this without scaffold so I can learn better, and I figure that by doing it this way it would be easer to setup so that people cannot try and look at other people's entries by modifying the url.
ideally it would keep their state in the session or cookie so that they would end up on the thanks page if they came back.
Have been trying to do this for about 3 days and reading/googling tons, but between the new routes redirect_to controller stuff in rails3 havn't managed to figure it out.
Routes.rb
Contact::Application.routes.draw do
resources :contactees, :only => [:new, :create]
# to make sure crud doesn't have the routest I don't want
get 'contactees/submitted'
root :to => 'contactees#new'
contactees_controller.rb
ContacteesController < ApplicationControler
def share
end
def new
#contactee = Contactee.new
end
def create
#contactee = Contactee.new(params[:contactee])
if #contactee.save
redirect_to submitted_contactee
else
render action: "new"
end
end
end
Views
contactees
_form.html.erb
new.html.erb
submitted.html.erb
Get rid of the submitted route, you don't need it. Perhaps something like this?
def new
render :submitted if session[:contacted]
end
def create
#contactee = Contactee.new(params[:contactee])
if #contactee.save
session[:contacted] = true
render :submitted
else
render action: "new"
end
end
I'm making a conventional forum in Rails to practice. I have a Topic model and a nested Post model. Topics can have many Posts.
Topics#Show has a list of #topic.posts and then a new Post form.
# Topics#Show
def show
#topic = Topic.find(params[:id])
#post = #topic.posts.new
end
Submitting a new post sends it to Posts#Create
# Posts#Create
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.new(params[:post])
#post.user = current_user
if #post.save
redirect_to #topic, :notice => "Successfully created post."
else
render :action => 'new' # <-- Unsure what to do here
end
end
If the Post fails to save, I want it to render Topics#Show and display the validation errors there.
From what I understand, params don't persist through a redirect_to because a 302 redirect starts a new request.
You should render the topics/show view. So instead of
render :action => 'new' # <-- Unsure what to do here
Do:
render :template => 'topics/show'
Use render :template => "topics/show" and be sure to set up the #topic variable identically to how you do it in the TopicsController#show action. You will not be able to call this show method from the PostsController though.
okay, so basically, I have a normal form for my model:
= form_for #operator do |f|
blah blah blah
In my operators controller, i have this:
def new
#operator = Operator.new
#operator.build_user
respond_to do |format|
format.html {}
end
end
def create
#user = User.create(params[:operator].delete(:user))
#user.update_attributes(:login => #user.email)
#operator = Operator.new(params[:operator].merge(:user => #user))
respond_to do |format|
if #operator.save
format.html {redirect_to new_operator_aircraft_path(#operator)}
else
format.html { render :action => "new", :error => #operator.errors }
end
end
end
very basic stuff. I have some validates_presence_of stuff in my model so naturally when I submit my form, it should show me that I have errors(and keep the fields I have filled up)
Right so far? yeah. The problem is, it seems I am posting to /operators and that's what renders. I seem to have forgotten about what happens in Rails2.3+ but shouldn't I be redirected to /operators/new again? or was that the intended behavior all along?
Here's what I think you are asking:
After I submit a form with errors, why does the URL
read "/operators" rather than
"/operators/new".
Thanks to resourceful routing, when submitting a form via POST to "/operators" the create action is called on the OperatorsController. If you encounter errors when saving your operator, you've instructed the controller to render the new action within the same request.
render :action => "new", :error => #operator.errors
This means a redirect is not occurring and therefore the URL remains "/operators".
If a redirect were to occur, you would lose all the state information of the #operator object in the current request, including the errors you encountered as well as the form values you just submitted.
In other words, working as intended.
I have an application, on which I have two user interfaces.
The first one is for normal users and the second one is for iphone users.
Everything was working fine until i refactored my code within controller to use the respond_with declarative instead of respond_to.
The application is still working for the html interface(:format => :html) but not on the iphone interface(:format => :iphone).
On the iphone, when I do the following action (:index, :new, :edit, :show) it works.
But when i do (:create, :update, :destroy), I get errors saying the template is not found(create.iphone.haml for example).
On my controller I have
respond_to :html, :iphone
And then for example, the edit and the update action
def edit
#refund = Refund.find(params[:id])
respond_with(#refund)
end
def update
#refund = Refund.find(params[:id])
if #refund.update_attributes(params[:refund])
flash[:notice] = 'Refund was successfully updated.'
end
respond_with(#refund, :location => project_refunds_path(#project))
end
In fact, I would like the :iphone format is handle as :html is ... and not by calling the to_format method as it is specified into the doc.
Solved it by myself.
Just need to add this to an initializer file :
ActionController::Responder.class_eval do
alias :to_iphone :to_html
end
What if you do:
respond_with(#refund, :location => project_refunds_path(#project)) do |format|
format.iphone { whatever you had here before refactoring }
end
I'm new to Rails, and a bit confused about routes:
I have a Devices controller:
#devices_controllers.rb
class DevicesController < ApplicationController
def index
#devices = Device.all
end
def show
#device = Device.find(params[:id])
end
def new
#device = Device.new
end
def create
#device = Device.new(params[:device])
if #device.save
flash[:notice] = "Successfully created device."
redirect_to #device
else
render :action => 'new'
end
end
def edit
#device = Device.find(params[:id])
end
def update
#device = Device.find(params[:id])
if #device.update_attributes(params[:device])
flash[:notice] = "Successfully updated device."
redirect_to #device
else
render :action => 'edit'
end
end
def destroy
#device = Device.find(params[:id])
#device.destroy
flash[:notice] = "Successfully destroyed device."
redirect_to devices_url
end
def custom_action
"Success"
end
I'd like to access the "custom_action" action via a url like this:
http://foo.bar/devices/custom_action
I've added this line to my routes.rb file:
match 'devices/custom_action' => 'devices#custom_action'
However, when I try the URL in the browser, I get this error:
ActiveRecord::RecordNotFound in DevicesController#show
Couldn't find Device with ID=custom_action
It seems to be going to #show action instead of #custom_action. If a user id is not supplied, and I go to http://foo.bar/devices/custom_action, I'd like it to go #custom_action.
I've read Rails Routing from the Outside, but still can't still seem to figure out the problem.
I think the problem may be because of the order in which you have defined your routes.
I suspect you have resources :devices in your routes.rb. In addition, I suspect you have defined your custom route after this. If you type rake routes into your console/terminal, you will see that there is already a route defined for the following pattern:
GET /devices/:id
This route is a product of resources :devices, which is taking precedence over your custom route. Referring back to the Edge Guides, specifically in 1.1. Connecting URLs to Code, it states that the request will be dispatched to the first matching route. So a simple fix would be to define your custom route before resources :devices.