ActiveAdmine register_page and controller - ruby-on-rails

What will be the link to an action defined like this:
ActiveAdmin.register_page "such_page" do
content title: 'A page' do
columns do
column do
render partial: 'index'
end
end
end # content
action_item do
link_to('Perform', 'such_page/much_action')
end
controller do
def much_action
puts 'Wow, actually doing!'
redirect_to 'http://stackoverflow.com'
end
end
I supposed that link will be just like mentioned in action_item, but is leads to 404 error page. Did I forgot to add some routes or am I wrong about how ActiveAdmin register_page and controller cooperate?

In Active Admin pages you'll have to define a custom action like so:
ActiveAdmin.register_page "such_page" do
#...
action_item do
# please refer to rake routes for the exact route name
link_to('Perform', admin_much_action_path)
end
page_action :much_action do
puts 'Wow, actually doing!'
redirect_to 'http://stackoverflow.com'
end
#...
end
by using page_action a route will be configured automatically. You can make sure the action is available by calling rake routes.
Reference: http://activeadmin.info/docs/10-custom-pages.html

It seems ActiveAdmin have some problems with underscores as you can see here.
Try rename your page like this
ActiveAdmin.register_page "Such Page" do
...
end
and you will get this in your routes
admin_such_page GET /admin/such_page(.:format) admin/such_page#index

Related

Link to another view using ruby in windows

I am doing a simple CR and trying to put some link/button(add,edit etc), when the user will click the add it will direct to that view..
Question: How should I indicate which view will be load when I click the specific link/button?
Index.html.erb
<h1>My First CRUD!</h1>
<%= link_to "Add Page", posts_path %>
<%= link_to "Edit Page", posts_path %>
<%= link_to "Showx Page", posts_path %>
Posts Controller
class PostsController < ApplicationController
def index
end
def addItem
end
def create
#post = Post.new(post_params)
#post.save
redirect_to #post
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:item, :description)
end
end
Routes
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :posts
root "posts#index"
resources :posts
root "posts#addItem"
You can declare all the root routes that you want in your routes.rb, but it'll take just the one that was declared first, it doesn't matter if the definition has some mistake, it won't tell you.
And the route where the link_to will redirect is what you declare as second argument (in this case).
In your example you have three link_totags, which are pointing to the same path. If you'd like to make the "Edit" link_toredirect to the posts#edit method you firstly must create the edit method in your posts_controller and then add it to your route as:
get '/posts/edit/:id', to: 'posts#edit'
But in your case as you have resources :posts it makes the whole work for you and creates all the routes defined in the posts#controller, what resources do is to map a number of related requests to actions in a single controller, that's to say;
When Rails application receives a request for:
GET /posts/edit/1
That's what happen when you go to edit the a certain post by passing the id of 1, then it asks the router to map it to a controller action. If the first matching route is:
resources :posts
Then Rails dispatchs that request to the edit action on the posts controller with { id: '1' } in params.
Short answer, to create a common <a> tag create a link_to, add a text to specify to where will this redirect, then the route that you've defined, you can check them by running rails routes if you're using Rails 5 or bin/rake routes if you're with Rails < 5.

Rails: How to review content before submit/save?

I know it is simple but I can't get my head around a solution.
It is a job board site. Lets say it's functionality similar to this site. When a user fill all required information and click "To next step" or "Preview", another page loads with all filled data. That page is similar to the final page when data is saved.
When user on preview page, it can go forward and submit the page (in this case it will be saved to DB). Or, click back to Edit the job.
I tried the following::
Within _form.html.erb I added a preview button
<%= f.submit "Preview", :name => 'preview' %>
Within JobControllers I altered create method
def create
if params[:preview]
#job = Job.new(jobs_params)
render 'jobs/preview'
else
#job.save
end
end
Created a Preview view /jobs/preview.html.erb
Now I have 2 problems.
1- Within my preview page, I have an edit button like so: <%= link_to "Edit Job", edit_job_path(#job) %>. But I have an error because I can't find #job. Error says: No route matches {:action=>"edit", :controller=>"jobs", :id=>nil} missing required keys: [:id]
SOLUTION Changed like to <%= link_to 'Back to edit', 'javascript:history.go(-1);' %>
2- How I would submit and add to my DB all information on preview page?
Thank you.
Once I've given a similar task. What I've done is to save records, but not to publish. In my index (resource listing) action of relevant controller, I only fetch published records. Also show action prechecks if that record's published attribute is set to true.
What was my model/controllers looked like before
#model
class Book < ActiveRecord::Base
...
scope :active, -> { where(published: true).some_other_queries }
self.active?
(published && some_other_requirements)
end
...
end
#controller
def index
#books = Book.active
...
end
def show
if #book.active?
render 'show'
...
else
...
end
end
First added a secret key for previews.
#model
def secret
#some custom random key generation
# e.g. Digest::MD5.hexdigest("#{id}_#{ENV['RAILS_SECRET']}")
end
Then added preview action to controller
def preview
# i don't check if the record is active.
# also added a security layer, to prevent irrelevant guys to view
# that record
if #book.secret == params[:secret]
render 'show'
else
...
end
end
In dashboard
...
= link_to "Preview", preview_book_path(book, secret: book.secret)
...
then added a member route
#routes
resources :books do
get :preview, on: :member
end
When I have to do something like this what I normally do is create a review table in my app. This table looks just like the table that is going to saving to.
When they press the "Approved" or "Save" button just populate the new table with the proper data.
I like to create a routes to handle this
resources :something do
match 'move_to_something_else' => 'somethings#move_to_something_else', as: :move_to_something_else, via: :all
end
Now on the controller we can do the following:
def move_to_something_else
#something = Something.find(params[:id])
#something_else = SomethingElse.new
#something_else.name = #something.name
....
#something_else.save
redirect_to something_else_path(#something_else)
end
Alternative you could add a state to your table with the default value of 'draft'
# config/routes.rb
resources :something do
match 'published' => 'somethings#published', as: :published, via: :all
end
# Controller
def published
#something = Something.find(params[:id])
#something.state = 'published'
#something.save
redirect_to something_path(#something)
end

How to display Devise account details on a profile page

Using Devise I would like to display User account information such as profile name, first & last name etc on another page called profile page within my rails application.
I have created a controller called profiles with a view called profile/show
In the controller have added the below code
def show
#user = User.find_by_profile_name(params[:id])
if #user
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
end
In the view profiles/show I have the following code
<%= #user.profile_name %>
and the route is get 'profiles/show'.
My issue is when I do all of the above the profile name of the user still does not display on the profile page? There are no errors that come up it just doesn't display. I am not sure what code I am missing. I have checked the console and the user does have a profile name save to that ID and this is also in the devise account settings. So I am not sure how to get this information to display?
In Rails you would usually set it up like follow to take leverage of convention over configuration:
# config/routes.rb
resources :users, only: [:show, :index]
# app/models/user.rb
class User < ActiveRecord::Base
# ...
def self.find_by_uid!(uid)
User.find_by!("profile_name = :p OR id = :p", p: uid)
end
end
# app/controllers/users_controller.rb
class UsersController
# GET /users/:id
def show
#user = User.find_by_uid!(params[:id])
# Rails does the magic.
end
# GET /users
def index
#users = User.all
end
end
<%- # app/views/users/show.html.erb -%>
<h1><%= #user.profile_name %></h1>
The only special part here is that in the user model we create a class method which will query by id or profile_name. The reason that this is important is that it lets you use link_to(#user) and redirect_to(#user) as expected.
Which is also why we use resources :users. When the route name and the model line up the Rails polymorphic route handlers are able to do their job. If you want to use /profiles thats fine but never /profiles/show - including the action in the route defeats the whole purpose of REST.
The show action will render users/show.html.erb by default. So you rarely need to explicitly render in your controller.
render action: :foo
is only used when you want to render a template with the same name as another action, its usually used as follows:
def create
#something = Something.new
if #something.save
redirect_to(#something)
else
render action: :new # renders views/something/new.html.erb
end
end
If you want to explicitly render a template you would do render :foo or render "foo/bar".
And when you use find or find_by! it will raise an exception if the record is not found which by default will render the static 404 template. Reproducing this error handling in your actions is not very desirable since it violates the DRY pinciple.

How to add routes for a new template?

I am new in Ruby and Rails and little bit confused about rendering and adding routes for a new template.
I have following link_to tag
<td colspan="3">
<%= link_to 'Show Current State', simulation, :action => :current_state, :class => 'btn btn-primary'%>
</td>
Where simulation is the name of controller and action is name of the method in SimulationController.
I added this in my routes.rb
resources :simulations, except: [:edit]
resources :simulations do
collection do
get 'current_state'
post 'current_state'
end
end
In my SimulationController class I added a new method i.e.
def current_state
byebug
end
My problem? routes is not re-directing to current_state method. Instead, it is redirecting to http://localhost:3000/simulations/{someID}
This redirection is calling show action.
def show
...
end
How can I make this work out and make <%= #simulation.dat %> line accessible in new.html.erb. Location of new.html.erb is in following path
views/simulations/index.html.js
views/similations/show.html.js
views/simulations/new.html.erb
This could be a basic question but I am new to rails 4. Thanks in advance.
Edit-1
Def of get_state method in controller
def get_state
#simulation = current_user.simulations.find(params[:id])
return not_found if #simulation.nil?
.....
/// How to send `#simulation` into `state.html.erb` formally as `new.html.erb`
end
You have too many misses in your code.
First, You don't need 2 resources :simulations, just merge them into one:
resources :simulations, except: :edit do
member do
get 'current_state', action: 'get_state'
post 'current_state', action: 'change_state'
end
end
Note that the original collection block is changed to a member block.
The difference between a collection block and a member block is that you need to provide an resource id for each routes in the member block, while no resource id is required for those in the collection block.
Also note that I added action: 'xxx' in each route, so you have to add these 2 actions in your SimulationsController, one for GET requests, and the other for POST requests.
UPDATE
In both of these actions, add render 'new' at the end.
END OF UPDATE
Run rake routes in your console (or bundle exec rake routes if you have multiple versions of rails installed), and you will see all the routes along with there url helper methods listed, like this:
Prefix Verb URI Pattern Controller#Action
current_state_simulations GET /simulations/:id/current_state simulations#get_state
current_state_simulations POST /simulations/:id/current_state simulations#change_state
...
According to the Prefix column, the link in the view should be
<%= link_to 'Show Current State', current_state_simulations_path(simulation), :class => 'btn btn-primary'%>
Or in short
<%= link_to 'Show Current State', [:current_state, simulation], :class => 'btn btn-primary'%>
UPDATE FOR Edit-1
Don't return in actions, because return doesn't stop rendering.
Instead, use raise ActionController::RoutingError.new('Not Found') to redirect users to the 404 page.
You can define an instance method in ApplicationController:
class ApplicationController < ActionController::Base
private
def not_found!
raise ActionController::RoutingError.new('Not Found')
end
end
And modify your SimulationsController:
def get_state
#simulation = current_user.simulations.find(params[:id])
not_found! unless #simulation
# ...
render 'new'
end
Best Practice
For dynamic page web applications, don't render views for non-GET requests!
Why? Because if a user POSTs some data to your web app, and then refreshes his/her browser, that request gets POSTed again, and your database got tainted. Same for PATCH, PUT and DELETE requests.
You can redirect the user to a GET path if the non-GET request succeeds, or to a 400 page if the non-GET request fails.

preview page signup redirecting to another static page error

I have a preview page up with a form that takes in emails(#premails). I've created a model & migration for this.
I have a pages controller with a Home, About & Contact actions and corresponding views.
After they submit their email on the Home page, I want to redirect them to a static About page. I have not been able to achieve this.
this is my pages controller:
class PagesController < ApplicationController
def home
#premail = Premail.new
if #premail.save
redirect_to about_path
else
render home_path
end
end
def about
end
end
But when I open my localhost with this code I get:
NameError in PagesController#home
undefined local variable or method `about_path' for #<PagesController:0x337ac40>
How can I make this happen?
For your case, use:
if #premail.save
redirect_to :action => :about
end
else is not needed here, since by default Rails would render app/views/pages/home.html.erb, be sure you have this file.
Also when you redirect to about, you will need app/views/pages/about.html.erb file to be present.
Update
Seems you don't have this route in config/routes.rb, for Rails 3.x:
match ':controller(/:action(/:id))'
In Rails 4:
match ':controller(/:action(/:id))', :via => [:get , :post]
If you are planning to just answer to get, i.e. there are nor forms posting to controllers:
get ':controller(/:action(/:id))'
This will detect routes like localhost:3000/asd/qwe/1 and:
Use asd as controller AsdController
Use qwe as action:
class AsdController
def qwe; end
params[:id] would be equal to 1.
() means optional, for example if you go in your browser to localhost:3000/asd, Rails would call Asd#index, i.e.:
class AsdController
def index
# whatever you have here
end

Resources