I've read everything I can find, and I'm still stumped.
I'm trying to use a before_filter to catch users who are not logged in, and send them to an alternative index page. The idea is that users who are logged in will see a listing of their own articles when they hit index.html.erb, users who are not logged in will be redirect to a showall.html.erb page that lists the articles but does not let them read them (and hits them with some ads).
I added a route:
resources :articles do
get "showall"
resources :comments
end
'Rake routes' shows a route article_showall. I have a partial _showall.html.erb in the views/articles folder (it only contains the text 'showall is working!'). If I render showall in another view (<%= render "showall" %>), it works fine.
This is the applicable part of my controller:
class ArticlesController < ApplicationController
skip_before_action :authorize, only: [:index, :show, :showall]
before_filter :require_user, :only => [:index]
def require_user
unless User.find_by(id: session[:user_id])
render 'showall', :notice => "Please log in to read articles."
end
end
def index
#articles = current_user.articles
end
def showall
#articles = Article.all
end
When I run it (while not logged in), it get the following error:
Missing template articles/showall, application/showall with....etc
I'm stumped. Why can I render 'showall' in a view, but I get Missing Template error when I refer to it in my controller? Thank you in advance for any help!
David and user4703663 are right. Your problem is that you named the template to be a partial but are rendering it as a template. You could either remove the underscore from the file name and leave your code as it is, or leave the filename as it is and "render partial: 'showall'" from your index view.
edit: I should add that the missing template error should not instill dread in your heart. It's guiding you to your mistake, directly. Don't stress it and just try to remember what it meant next time. It's almost always a typo, or loading a partial but naming it as a template or vice versa, or forgetting a subfolder for a relative path or something like that. It's normal, and the interpreter spits those errors to help you, not to oppress you. 😄
Replace
render 'showall', :notice => "Please log in to read articles."
with
render :file => '/partial/showall', :notice => "Please log in to read articles."
Great, fixed that problem, and now it spits back
undefined method `each' for nil:NilClass
even though the 'showall' is nearly identical to 'index'.
SIGH
Related
In my Rails routes.rb file I'm wanting to do something like the following.
get '/:id' => 'pages#show'
get '/:id' => 'articles#show'
So that if a visitor types in
http://www.example.com/about-this-site
The pages controller in the above example would get first shot at handling it. Then if not, the next controller in line would get a shot.
REASONs for wanting to do this:
1) I'm trying to port my Wordpress site over without establishing new urls for all my pages and blog posts. As it stands, all of my blog post files and pages are accessed directly off the root uri '/' folder.
2) Because I'm not able to, it's a learning thing for me. But, I want to do it without a hack.
How about redirecting to the second controller from your first controller?
in PagesController
def show
unless Page.find_by(id: params[:id])
redirect_to controller: :articles, action: :show, id: params[:id]
end
end
in ArticlesController
def show
# Handle whatever logic here...
end
Edit
If you really don't want to redirect then you can consolidate the logic into a single action:
def show
if Page.find_by(id: params[:id])
render :show
elsif Article.find_by(id: params[:id])
render controller: :articles, action: :show
else
# Handle missing case, perhaps a 404?
end
end
However, I'd recommend using a redirect if possible. It's a cleaner solution and keeps your controller code isolated.
I'm trying to build a link shortener. The intended behavior is that on the first page (new) the user inserts his long link and presses a button, then he gets redirected to an another page called result, where a preset message will be waiting for him, along with both his short and long link.
I'm struggling with controllers, however, as no matter what I do something always comes wrong. Right now my controller looks like this:
class UrlsController < ApplicationController
def new
#short_url = Url.new
end
def create
#short_url = Url.new(url_params)
if #short_url.save
flash[:short_id] = #short_url.id
redirect_to "/urls/result"
else
render action: "new"
end
end
def show
Url.find(params[:id])
##short_url_yield =
redirect_to #short_url.url
end
def result
end
private
def url_params
params.require(:url).permit(:url)
end
end
And the routes.rb:
Rails.application.routes.draw do
resources :urls, :only => [:show, :new, :create, :result]
get 'urls/result' => 'urls#result'
root to: redirect('/urls/new')
end
When I submit the link, however, rails returns the following error:
Couldn't find Url with 'id'=result
Extracted source (around line #17):
def show
Url.find(params[:id])
##short_url_yield =
redirect_to #short_url.url
end
It seems I don't understand the logic behind it. What's going wrong? Isn't the show bit supposed to be a redirect that happens when I click the shortified link?
Rails routes have priority in the order they are defined. Since your SHOW route declaration is before get 'urls/result' => 'urls#result' the url gets matched as /urls/id=result.
Simply move your custom route above the resources block or use a collection block.
resources :urls, :only => [:show, :new, :create, :result] do
collection do
get 'result'
end
end
Using the collection and member blocks tells Rails to give priority to the routes inside over the normal CRUD actions such as show.
I have an account/settings page people can visit to update their account. It's a singular resource, so they can (or should) only be able to update their own account settings. I'm running into a weird URL format when there are form errors displayed.
If they are on /account/settings/edit and try to submit the form with errors (not a valid email address, for example) they are redirected to /account/settings.1 where it shows them what went wrong (in our example, not a valid email address).
Everything "works" but I was wondering why there is a .1 being appended to the URL. I figured they would be sent back to account/settings or account/settings/edit where they can correct the error. Am I doing something wrong?
routes.rb
namespace :account do
resource :settings, :only => [:show, :edit, :update]
end
settings_controller.rb
def edit
#account = Account.find(session[:account][:id])
end
def update
#account = Account.find(session[:account][:id])
if #account.update_attributes(params[:account])
redirect_to account_settings_path
else
render 'edit'
end
end
rake routes
edit_account_settings GET /account/settings/edit(.:format) account/settings#edit
account_settings GET /account/settings(.:format) account/settings#show
account_settings PUT /account/settings(.:format) account/settings#update
Make sure you generate your paths using edit_account_settings_path, NOT edit_account_settings_path(#user). For singular resources you shouldn't pass in a resource, because, as you say, there is only one of them.
I have a really hard time understanding routes and I hope someone can help me.
Here's my custom controller
class SettingsController < ApplicationController
before_filter :authenticate_user!
def edit
#user = current_user
end
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
end
and I have the file settings/edit.html.erb and my link
<li><%= link_to('Settings', edit_settings_path) %></li>
The route
get "settings/edit"
doesn't work for this, because then I get
undefined local variable or method `edit_settings_path' for #<#<Class:0x00000001814ad8>:0x00000002b40a80>
What route do I have to give this? I can't figure it out. If I put "/settings/edit" instead of a path it messes up as soon as I'm on a other resource page because the resource name is put BEFORE settings/edit
Thx
Following should do:
get 'settings/edit' => 'settings#edit', :as => :edit_settings
# you can change put to post as you see fit
put 'settings/edit' => 'settings#update'
If you use /settings/edit directly in link, you shouldn't have problem with other resource name being prepended in path. However, without the leading slash, i.e. settings/edit it might have that issue.
Reason why edit_settings_path is not working might be because you didn't declare a named route. You have to use :as option to define by which method you will be generating this path/url.
If you want to explicitly define the route, you would use something like
get 'settings/edit' => 'settings#edit', :as => edit_settings
This statement defines that when a GET request is received for setting/edit, call the SettingsController#edit method, and that views can reference this link using 'edit_settings_path'.
Take some time to read the Rails guide on routing. It explains routing better than any other reference out there.
Also keep in mind the rake routes task, that lists the details of all the routes defined in your application.
I am making a login route and added this to the routes.rb resources :sign_in
I made a controller like this:
class Mobile::Sign_inController < ApplicationController
layout "mobile/application"
def get
respond_to do |format|
format.html
end
end
def index
respond_to do |format|
format.html
end
end
end
and it seems to get routed correctly, but my view file which is located here:
/app/views/mobile/sign_in.html.haml
which just has 1 line for test purposes:
%strong{:class => "code", :id => "message"} Hello Signin!
But when I go to the url: http://m.cmply.local:8800/signin in the browser, the screen is totally white with nothing rendered in the browser.
Any idea why this happens and how to fix it?
Thanks!
A few problems here:
Your controller name should be SignInsController, not Sign_inController. Consider changing your name to UserSessionsController or similar, since that better reflects the resource it represents. You can still specify an alternate name for the URL (such as sign_in).
Why is your controller namespaced under Mobile? Your routes given don't reflect that, but you don't seem to have provided them all. The route should probably be under a scope:
scope :module => "mobile" do
resource :sign_in
end
Since there is only "one" sign in, it should have its route declared resource :sign_in, and probably even resource :sign_in, :only => [:new, :create, :destroy], depending on what you want. This means that the index action no longer exists, and you probably want to replace it with the new action`.
There is no get action by default for RESTful resources, I'm not sure what you meant it to be, but it should be something else.