I need to let the user select a root path (or a get) to be one of many pages he has created. That means, i want to be able to created the page "fantastic page" and set it to be the front page (http://example.com/).
Is this at all possible? I have tried sending the user to a fixed action and redirecting the user to the selected action but that leaves me with a route like this: http://example.com/page/8
My desired result is: http://example.com/
(while the page is the one selected by the user).
Any help is appreciated :)
Edit: I need to clarify. I want the user to be able to select a page or a photo. That means I need the user to be able so select both the controller and the action.
Something like this:
constraints(Subdomain) do
get "/" => '#{user_selected_controller}#{user_selected_action}/#{:id}'
end
The code above is completely wrong. But i think it illustrates what i want to accomplish.
You should explicitly render the page view from the controller.
config/routes.rb
root to: 'home#index'
controllers/home_controller.rb:
def index
#page = #get fantastic page
render 'pages/page', page: #page
end
See http://guides.rubyonrails.org/layouts_and_rendering.html#rendering-an-action-s-template-from-another-controller.
I Solved this problem not through routing but by creating a polymorphic association to both Pages and Galleries with the users table. Like this:
First create polymorphic association
# In my page model
has_one :user, :as => :home
# In my Gallery model
has_one :user, :as => :home
# In my User model
belongs_to :home, polymorphic: true
Then i used this as my controller in the users_controller:
def home
# Multitenant rails app. This refers to user who owns the subdomain visited.
#user = current_tenant
# This is not optimal, results in two queries.
if #user.home_type.classify.constantize.exists?(#user.home_id)
# This is the same as #page = Page.find(params[:id])
instance_variable_set("##{#user.home_type.downcase}", #user.home_type.classify.constantize.find(#user.home_id))
# this is the same as render 'pages/show', layout: false
render "#{#user.home_type.downcase.pluralize}/show", layout: false
else
redirect_to photos_path
end
end
My routes look like this:
# Set a page as the home screen
post 'pages/set_home/:id' => 'pages#set_home', as: :set_home_page
# User selected Home screen
get "/" => 'users#home'
Setting a home page:
# In pages_controller
def set_home
#user = current_user
#user.home = #page
if #user.save
redirect_to pages_path, notice: "Home screen set to #{#page.title}"
else
redirect_to pages_path, alert: "An error occured. Home not set."
end
end
This way i could load the relevant resource through the users.home variable, while using a normal route to the users controller home action.
This might be an inefficiently solution, but its the only one i could think of. Please feel free to suggest improvements :)
Related
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
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.
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.
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
I'd like to create a rails route for editing a user's profile.
Instead of having to use /users/:id/edit, I'd like to have a url like /edit_profile
Is it possible to create a dynamic route that turns /edit_profile into /users/{user's id}/edit, or should I do thing in a controller or?
You might want to create a separate controller for this task but you could also continue using users_controller and just check whether there is a params[:id] set:
def edit
if params[:id]
#user = User.find(params[:id])
else
#user = current_user
end
end
But you should note that /users normally routes to the index action and not show if you still have the map.resources :users route. But you could set up a differently called singular route for that:
map.resources :users
map.resource :profile, :controller => "users"
This way /users would list all the users, /users/:id would show any user and /profile would show the show the currently logged in users page. To edit you own profile you would call '/profile/edit'.
Since a route and controller serve two different purposes, you will need both.
For the controller, assuming you're storing the user id in a session, you could just have your edit method do something like:
def edit
#user = User.find(session[:user_id])
end
Then have a route that looks something like:
map.edit_profile "edit_profile", :controller => "users", :action => "edit"
This route would give you a named route called edit_profile_path
Tomas Markauskas's answer could work, but here's the answer to your question from the Rails Guide:
get 'edit_profile', to: 'users#edit'
So, when someone goes to www.yoursite.com/edit_profile, it will route to www.yoursite.com/users/edit.
Then, in your controller you can access the user with
#user = User.find(session[:current_user_id])
Assuming you set that session variable when someone logs in. Also, don't forget to check if they're logged in. This will work if your using Resourceful Routing (the Rails default) or not.
Source: http://guides.rubyonrails.org/routing.html
make the route as
get '/users/:id/edit', to: 'users#edit', as: 'edit_profile'
As explained in this link section 'The hard way' :
http://augustl.com/blog/2009/styling_rails_urls/
The url will be
/users/edit_profile
Because the ID is no longer in the URL, we have to change the code a bit.
class User < ActiveRecord::Base
before_create :create_slug
def to_param
slug
end
def create_slug
self.slug = self.title.parameterize
end
end
When a user is created, the URL friendly version of the title is stored in the database, in the slug column.
For better understanding read the link below
http://blog.teamtreehouse.com/creating-vanity-urls-in-rails
write it in any home controler.
def set_roots
if current_user
redirect_to dashboard_home_index_path
else
redirect_to home_index_path
end
end
in routes.rb file
root :to => 'home#set_roots'
match "/find_roots" => "home#set_roots"