Ruby on Rails - possible error in naming convention throughout a route - ruby-on-rails

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.

Related

Rails Routing: How to have controllers in series to respond to same matching path

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.

Redirecting to another view in rails

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.

How to route a nested resource route to another?

I'm working on implementing boards.
Now I have BoardsController and PostsController.
By default, posts are nested by boards.
I want all board's post list have their special route using same PostsController
so I did this in route.rb
resources :notice, :controller => "posts", :board_id => 1
resources :faq, :controller => "posts", :board_id => 2
resources :qna, :controller => "posts", :board_id => 3
At first, it seems to work. But I realized a problem.
because i used same 'PostsController' in these resources.
Codes related to path are same when doing controller's action
like,
posts_controller
def create
#post = Board.find(params[:board_id]).posts.build(params[:post])
if #post.save
redirect_to board_posts_path(#post.board_id)
else
render 'new'
end
end
when I go to localhost:3000/notice/new, it works fine
but when I submitted the new post, controller redirects to localhost:3000/boards/1/posts/
because of redirect_to board_posts_path(#post.board_id)
and that's not what I want.
I could handle this using if statements, but it seems messy.
Is there any proper solution to this?
You can use the self.send on the controller to dynamically resolve the path by the post type. Assuming you have the type of the created post in a string ( I didn't understand from your question if Notice < Post and if you use Single Table Inheritance):
post_type = # Get the specific post type ( "notice, faq ...")
redirect_to self.send("#{post_type}_path", #post.board_id)

Rails: Route to custom controller action

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.

Best practices for static pages in rails app

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.

Resources