This strikes me as pretty simple, which is why it's bugging me that I can't figure it out. I am creating a basic news app in rails. I have a "Posts" controller that controls the content for my basic pages. I want to have a page with local news. I went into the posts controller and made a new action called "local"
def local
end
I then went into my routes.rb file and made a route for the page.
get "posts/local" => "posts#local"
I then created a local.html.erb file and placed it in my post views.
When I try to click a link with the posts_local_path, I get this:
ActiveRecord::RecordNotFound
Extracted source (around line #74):
72
73
74
75
76
77
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
I don't understand. This is not involved in any way with the page I am trying to open. I'm not using that part of the controller. I'm stuck. Can someone help me understand what I'm doing wrong? Thank you!
I'm assuming you have before_action in your controller
You want to ensure that's not called for the local action
before_action :set_post, except: [:local]
or if it's only used in show
before_action :set_post, only: [:show]
You can pass either except or only arrays of the action names as symbols.
The problem originates from the fact that you're not passing any id in the params to the controller. Hence your method set_post cannot retrieve any post and returns the ActiveRecord error.
So you've got 2 options:
Either you want to use set_post and need to specify an :id param in your routes
Or you avoid calling set_post by adapting your before_action filter (as per the answer of j-dexx)
Related
I looked at the following questions:
Are multiple before_action calls bad code style?
Api Dock - before_filter
And I think there was something about creating a controller superclass, but I am not familiar with such.
The reason I need to do this is because, we have a bunch of setup views, and those views are renering a seperate layout. setup_screen
So instead of doing
def setup_step_1
render: layout => 'setup_screen'
end
def setup_step_2
render: layout => 'setup_screen'
end
def setup_step_3
render: layout => 'setup_screen'
end
We created the following:
before_action :setup_layout, only: %i[ setup_step_1 setup_step_2
setup_step_3]
The problem is, we want to run some extra logic in the setup_screen's. But we can't add another before_filter. It's also not possible to add the logic in the def setup_screen because of the case that not ALL setup_screen's will need it.
As stated in those answers, there is no objection to using multiple before-filters. So you can easily add multiple before_action. E.g. a typical example
before_action :authenticate_user!
before_action :get_post, only: [:show, :edit, ...]
In your case for the render I would use a after_action, since the render is the last action. Then you could add your conditionial code in each separate action, or an extra before_action.
An alternative method, which I would prefer in your case is something like the following:
def setup_step_1
setup_step(1)
end
def setup_step_2
setup_step(2)
end
def setup_step_3
setup_step(3)
end
protected
def setup_step(step)
if step == 1
# .. do something for step 1
elsif step == 2
else
end
render: layout => 'setup_screen'
end
I would prefer this approach since it is a little more expressive/explicit. Looking at the methods it is clear they share the same core. Personally I prefer to use before_action for setting up pre-conditions: e.g. authentication, authorization, fetching the data if very simple.
But as usual in programming, there are a lot of roads leading to a working application and sometimes it is just a matter of taste which you prefer.
If I use the Rails scaffold generator for the Article resource it will create a before filter and a private method like this
# app/controllers/articles_controller.rb
before_action :set_article, only: [:show, :edit, :update, :destroy]
...
private
def set_article
#article = Article.find(params[:id])
end
Now I understand this makes your controller DRY because instead of adding #article = Article.find(params[:id]) 4 times which is 4 lines of code, you can use the above and you save yourself... well it's still 4 lines and a similar number of keystrokes. But if you want to modify it you only have to do it in one place. But you sacrifice clarity because to see that #article is defined in those 4 actions you have to look at the before filter at the top, then at the private method at the bottom, then back at the action. For me personally this is no benefit so I always just put this in the four actions. My question is, is this merely a personal preference issue or is there a compelling reason to always use the DRY method that I am not seeing?
When the logic of retrieving gets a bit more complicated than just one line it is quite helpful, but not by any means necessary. You could also leave the set_article method as it is, remove the before_action and call it explicitly in the actions that need it, a bit of a trade off between before_action and repeating it in all actions. That's quite handy when the logic is a few lines of code instead of just one.
In my app, after a user logs in, he is redirected to the AccountsSelection controller. In there, I have an index action that basically will get all the potential accounts a user can use, and display them in a index view.
def index
#accounts = current_user.eligible_accounts
end
In that page the user can click in one of the accounts, and that should go to one of the actions in the controller, that will set this in the session:
def show
session[:selected_account] = params[:account_id]
redirect_to account_path
end
Right now I have made that action to be show (so the selection of the account is doing a GET request, but I am not sure if this is a RESTful way of approaching this). From my point of view this is not a PUT/POST because I am not changing any resource or creating any resource, but it seems weird to be using the show action to just set a session.
What would be a proper way of handling this? It is correct to use show?
It is not recommended to use session in this case. So the standard approach is to create a before_action to set the account_id.
Use like this:
before_action :set_account, only: [:show]
and create the function in private as:
private
def set_account
account_id = params[:account_id]
end
Hope this helps.
i'd like to create settings page via activeadmin (edit/update).
https://github.com/huacnlee/rails-settings-cached.
But i faced that there is no way to register resource (not resources) in routes for particular page, eg have routes like /admin/settings, but not admin/settings/:id
inherit_resource has
defaults singleton: true
for this case, but this doesnt work for activeadmin.
Please help.
Otherwise, i can go with register_pagse way and create form myself and update action,but another problem appeared: how can i render errors messages on form from that update action.
The singleton way is way preferred.
You can always force the index action to redirect to the singleton resource that you want. While this is not a perfect solution, I have used it in the past. Something like this:
ActiveAdmin.register Setting, as: 'Setting' do
actions :all, only: [:show, :edit, :update, :index]
controller do
def index
redirect_to resource_path(Setting.first)
end
end
end
I'm new to Rails, and am trying to create a page that is largely a copy of my users#show page, with slightly different content and formatting. Ideally, this would work something like this:
Normal route: http://myUrl.com/users/2
New route: http://myUrl.com/users/2/lightbox <-this is the new route with the formatting. It should have access to user #2's info.
I did some research on stack overflow, and added the following to routes.rb
resources :users do
member do
get 'lightbox'
end
end
and then raked the routes. This allows me to type in the url http://myUrl.com/users/2/lightbox . However, it doesn't seem to have access to any of the user class's resources, and seems to have no idea who User #2 is.
I may completely have gone about this the wrong way - all I really want to do is create a custom page to display an individual user's information that's different from the show page. I'd really appreciate any help!
You need to add an action to your app/controllers/users_controller.rb:
def lightbox
#user = User.find(params[:id]
# any other logic, look at your show method
end
Routing only maps a url to a controller action. It is up to the controller action, each individually, to set variables and render the view.
Before filters and helper methods are used make sure you don't have to write code a bunch of times. For example:
before_filter :find_user, only: [ :show, :lightbox ]
def show
end
def lightbox
end
protected
def find_user
#user = User.find(params.fetch :id)
end