I have a Rails 3 App with Devise...
I want to create a Account controller that will allow the user to update their account, things like Profile, Account, Notices, Password, etc...
Here's what I've done so far.
I generated an account controller which gave me the following:
Routes:
get "account/profile"
get "account/password"
get "account/notices"
Views
now exists given the names above with /views/account/...
My Controller
class AccountController < ApplicationController
before_filter :authenticate_user!
def profile
#user = User.find(current_user.id)
end
def password
end
def notices
end
def privacy
end
def disable
end
end
View for Account Profile:
<% form_for #user, :html => { :multipart => true} do |f| %>
Problem is that makes the form like:
<form accept-charset="UTF-8" action="/users/13" class="edit_user" enctype="multipart/form-data" id="edit_user_13" method="post">
And I want it to be like /account/profile/update which is where the form should post?
This is the first time I've done something like this. Am I down the right track? How do I add the /account/profile/update part? Add something in the route and change the form tag??? Is there a cleaner way to do this in the route?
Thank you
You should stick to RESTful routes when possible. Instead of having URLs like /profile/123/update, the RESTful alternative would be /profile/123 using HTTP PUT
read this for more info on RESTful routes in Rails 3: http://edgeguides.rubyonrails.org/routing.html
Also, take a look at using nested resources in your routes for profiles, notices, etc (section 2.7 in the rails doc):
resources :accounts do
resources :profiles, :notices
end
For example, doing an edit using a nested resource will give you routes like this:
/accounts/123/notices/3/edit
Related
I would like to know how I would take the user's input and use it to redirect them to a new URL. For example if the user entered "556859" into the input field and clicked "Go" it would take them to www.website.com/556859.
I'm using ruby on rails but am not sure how to achieve my goal. Any input would be appreciated.
If you need examples for the view and the rest as well, then ask. But this controller method should be enough to point you in the right direction:
def redirect_action
redirect_to "/#{params[:redirect_path]}"
end
You can send user input to controller method
and in that method you can use redirect_to helper method
For example
in view create form with action="controller/redirect"
and in controller create method redirect
and with params[] use your input
Best regards
You'd do something like this:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def redirect
redirect_to root_path + param
end
end
#config/routes.rb
post "", to: "application#redirect", as: :redirect
#app/views/application/index.html.erb
<%= form_tag redirect_path do %>
<%= f.input :param %>
<%= f.submit "Go" %>
<% end %>
--
The next question becomes "why?"
Rails' routing infrastructure is such that it should give you the ability to access individual views for what you're trying to accomplish.
For example, if you're trying to show users in that redirect, you can do:
#config/routes.rb
resources :users
#app/controllers/users_controller.rb
class UsersController < ActionController::Base
def show
#user = User.find params[:id]
end
end
This will give you the ability to access http://yoururl.com/users/12312321
Alternatively, you could also (if you had nothing else on that part of the app), do the following:
#config/routes.rb
resources :users, path: "" #-> http://yoururl.com/12313123
If you know what type of data you're trying to show, it should give us more to work with in regards how you'd construct routing structure for it.
I have two actions in the controller:
def report
#user = User.find_by_slug(params[:slug])
end
def reportForm
#user = User.find_by_slug(params[:slug])
Thread.new do
mail = ...
end
#message = 'Thanks!'
end
and in routes:
# User report form
get "/user/:slug/report", to: "users#report"
# Catch report form and action
post "/user/:slug/report", to: 'users#reportForm'
And the view:
<form method="POST" action="/user/<%= #user.slug %>/reportForm">
...
But the problem is, that when I send the form, the action reportForm is not called and instead of that is only refresh the current page with the form.
What's wrong here?
Thank you guys.
Form Helpers
The first thing that's wrong is you're not using the form helpers that Rails provides - this is a problem because you'll end up with niggly little problems like the one you're receiving:
#config/routes.rb
resources :users do
get :report #-> domain.com/users/:id/report
post :reportForm #-> domain.com/users/:id/reportForm
end
#view
<%= form_tag user_reportForm_path(#user) do %>
...
<% end %>
Routes
The second issue you have is to do with your routes
You've set the following routes:
get "/user/:slug/report", to: "users#report"
post "/user/:slug/report", to: 'users#reportForm'
This means you've got to send the request to domain.com/user/user_slug/report. Your form sends the URL to reportForm...
You should see my routes above for the solution to this problem
But more importantly, you should read up on nested resources:
#config/routes.rb
resources :users do
match :report, action: "reportForm", via: [:get, :post] #-> domain.com/users/:id/report
end
Slug
Finally, you're trying to use params[:slug] in your controller
With the resourceful routes you should be using in Rails, you'll be passing params[:id] most of the time. This should not be an issue (what is contained in params[:id] can be anything).
I would highly recommend looking at a gem called friendly_id, which makes including slugs in your application a lot simpler:
#app/models/user.rb
Class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
end
This will allow you to call:
#app/controllers/users_controller.rb
Class UsersController < ApplicationController
def reportForm
User.find params[:id] #-> will use either `id` or `slug`
end
end
I have such method controller:
class Admin::CarManufacturersController < ApplicationController
def edit
#man = Manufacturer.find(params[:id])
render :layout => 'admin'
end
def update
#man = Manufacturer.find(params[:id])
if #man.update_attributes(params[:car_manufacturer])
****
else
render :action => :edit, :layout => 'admin'
end
end
end
and i have such route:
namespace :admin do
resources :car_manufacturers do
###
end
end
and such form partial:
= form_for [:admin, #man] do |f|
###
but when i call this form to edit my data i get:
undefined method `admin_manufacturer_path'
but i need admin_car_manufacturer_path i thing it's becouse i use other model name in controller, but i can't change it... how can i use right pass? i try to write admin_car_manufacturer_path in form, but i think this is bad idea. How to solve my problem?
I would think about renaming your controller/your model to match. Both should either be just manufacturer or car manufacturer. Having the same names for a resource's controller and model will spare you problems like the one you're having right now.
In any case, if you just need a quick fix, you can get around this by specifying the as option for your nested routes like this:
namespace :admin do
resources :manufacturers, as: :car_manufacturers do
###
end
end
Source: Rails Routing from the Outside In - Ruby on Rails Guides - 3.6: Naming Routes
That will turn your path names into admin_car_manufacturer_path etc and should allow you to use your form the way you you intended to. But I really recommend renaming your model and controller so that they match.
I am developing a app in ruby on rails for a local business. The pages are 'static', but changeable through a backend CMS I am building for them. Is there a best practice to creating a controller for static pages? Right now I have a sites controller with all static routes, like this.
routes.rb
get "site/home"
get "site/about_us"
get "site/faq"
get "site/discounts"
get "site/services"
get "site/contact_us"
get "site/admin"
get "site/posts"
or would I be better off creating member routes for the site controller like this without the crud, because a 'Site' will not need to have the CRUD.
resources :sites, :except => [:index, :new, :create, :update, :destroy]
member do
get :home
get :about_us
get :faq
get :discounts
get :services
get :contact_us
get :admin
get :posts
end
Or is there a best practice / better way? Any answers would be appreciated. Thanks
If the static pages list are not going to increase, then you can keep the list, but if you want a dynamic list like site/any_new_url , save the routes as
get 'site/:cms_page' => 'cms#show' # all requests matching site/any_page will go CmsController, show method
This will help reduce keep the routes from bloating, but the downside is you do not know what all routes are the valid ones. Your sample code can be
def show
#page_data = Page.find_by_page(:params[:cms_page])
end
show.html.erb
<%= #page_data.html_safe %>
Dunno yet if I consider this a best practice or an abomination but here is what I came up with when tackling the same problem.
My reasoning is that the site was providing some specified functionality (which doesn't really matter for this discussion) + a bunch of information about the organisation itself (about us, contact, FAQ, homepage blurb, whatever). Since all that data was really related to the organisation, an Organisation model seemed reasonable with each of those things as attributes. Here is the model:
class Organisation < ActiveRecord::Base
...validations stuff...
def self.attrs_regex
Regexp.new(self.attrs.join("|"))
end
def self.attrs
self.column_names.reject{|name| name =~ /id|created_at|updated_at/}
end
end
Then I use the attrs class method to generate routes based on the columns. This is in my routes.rb:
Organisation.attrs.each do |attr|
get "#{attr}" => "organisation##{attr}", :as => attr.to_sym
get "#{attr}/edit" => "organisation#edit", :as => "#{attr}_edit".to_sym, :defaults => { :attribute => attr }
post "#{attr}" => "organisation#update", :as => :organisation_update, :defaults => { :attribute => attr}, :constraints => Organisation.attrs_regex
end
The controller gets a little weird and I am not thrilled with the code here but here it is anyway. I need to make sure the attribute is set and available to the views so I can do the right thing there so I set it in the application controller:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_attribute
def set_attribute
#attribute = action_name.parameterize
end
end
For the organisation controller I just set the #organisation variable to be the first and only row in the database in the before_filter and then let Rails do its usual magic of calling the method, failing, and rendering a view of the same name. The edit action just uses one view file to edit all the different attributes:
class OrganisationController < ApplicationController
before_filter :set_organisation
def edit
authorize! :edit, #organisation
#attribute = params[:attribute].parameterize
end
def update
authorize! :update, #organisation
#attribute = params[:attribute]
respond_to do |format|
if #organisation.update_attributes(params[:organisation])
format.html do
redirect_to "/#{#attribute}", notice: t('successful_update')
end
format.json { head :ok }
else
format.html { render action: "edit" }
end
end
end
private
def set_organisation
#organisation = Organisation.first
end
end
So that is where I ended up. Like you I hit up SO to tap into the seething mass of genius here but ended up with disappointing results. If there is something better out there I am still hoping to find it.
What I like about what I did is that routes are automatically generated based on the structure of the organisation table.
What I don't like about what I did is that routes automatically generated based on the structure of the organisation table.
I know I will pay for that design decision when I have to deal with i18n routing and there are probably a thousand other reasons that this is a bad idea that I have yet to discover but for the moment I have a happy client.
In the end this is not a suggestion that you should do this, but I am hoping to give you more than I got so you can advance your thinking on this and hopefully end up a little closer to that best practice.
If you are going to construct a CMS, which likely connects to a database, and allow your customer to change the text on the pages of their site, I would not recommend using static pages. In Rails terms, a static page would refer to creating html files in your /views/pages directory. If you go this route, then you're walking outside of the way that Rails was designed.
I believe that what you want to do is create tables in the database that correspond to and store the data for your posts, etc. You can pull information into the controller from the model that it corresponds to and then user a view to display the data. You can create a layout for these pages and then create controllers for each of the pages that you add.
As far as routes, I would recommend using the following:
map.resource :controller_name
you then would add the code that displays the information from the CMS in the corresponding show controller action and view for each page.
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"