Creating a dynamic root route in rails - ruby-on-rails

I have a rails app where I am wanting to make the landing page dynamic so that every time the user goes to it it changes.
I have cafe's in my app, and each cafe has its own show page. I want each show page to be dynamic. The url's are based off of the cafe's id, so I figured I'd have to use those IDs as the focal point for creating the dynamic work i'm looking for.
In my cafe's controller I have
class CafesController < ApplicationController
def root
array = Cafe.pluck(:id)
array.sample
end
end
in my routes file I have
root 'cafes#root'
The error I'm getting is
`CafesController#root is missing a template for this request format and variant. `
Would anyone know what I am missing with this one? Much appreciated.

I image you could do something like:
class CafeController < ApplicationController
def root
redirect_to Cafe.all.sample
end
end
By the way, this approach has the benefits of letting you keep and use all your normal routes in the conventional manner.
Also, you might consider calling this action something a little more descriptive. Perhaps something like random_cafe. IMO, root 'cafes#random_cafe' is a bit more understandable.

missing a template error because you have to specify a template to render, I'm imagining that array.sample will return something like /cafes/firstsampe.html.erb where firstsampe.html.erb persists inside cafes views folder so you can use it like this redirect_to :template => array.sample.
Hope it helps.

I don't have any idea about what your data looks like, but there are two approaches you can take here:
Show a random cafe when the user hits your home page (root)
Redirect a user to a cafe's page randomly when they hit the home page
In scenario 1:
Controller:
class CafesController < ApplicationController
def index
#cafe = Cafe.order("RANDOM()").first
end
end
Routing:
root 'cafes#index'
Views:
app/views/cafes/index.html.erb
<p><%= #cafe.name %></p>
In scenario 2:
Controller:
class CafesController < ApplicationController
def index
redirect_to cafe_path(Cafe.order("RANDOM()").first)
end
def show
#cafe = Cafe.find(params[:id])
end
end
Routing:
root 'cafes#index'
resources :cafes, only: [:show]
Views:
app/views/cafes/show.html.erb
<p><%= #cafe.name %></p>

Related

Rails 4 - Couldn't find User without an ID

I'm new to rails, so any explanation & advise would much appreciated.
i have a webpage in which i would like any user to view that page not just the current_user, but i am unsure how to correctly define the instance variable #user in my controller
in my static_pages_controller.rb i have the below action recruiterpg
static_pages_controller.rb
def recruiterpg
#user = User.find(params[:user_id])
#adverts = #user.adverts
#applications = #user.forms
end
in my controller, i have defined user as #user = User.find(params[:user_id]) but this breaks my code in the views; views/static_pages/recruiterpg.html.erb
when i define user as #user = current_user my code in the views works perfectly fine
what am trying to do is: for my views, the recruiterpg.html.erb, i would like
any user to be able to view the page not only the current_user to
view the page. Could one kindly advise me and explain to me how to
define #user correctly in my status_pages_controller.rb. i also
tried #user = User.find(params[:id]) but my code still breaks in the
views - i get the error message
Couldn't find User without an ID
You need to make sure you are passing a user_id to the recruiterpg action. For example, try this url in your browser (set user_id to a known id in the users table):
http://localhost:3000/dashboard?user_id=1
A suggested modification to your action:
def recruiterpg
#user = User.find params.require(:user_id)
#adverts = #user.adverts
#applications = #user.forms
end
If params[:user_id] isn't defined, you want to find a way to make visible what is being defined.
If you throw the following statements into your controller...
def recruiterpg
...
puts params
...
end
...you should see something like the following get spit out in your console when you load the page...
{"controller"=>"static_pages", "action"=>"recruiterpg", "id"=>"49"}
Take a look at the Rails guide for parameters. They can get defined in one of three ways.
One: As a query string similar to Sean's answer above.
Two: Routing parameters. See section 4.3 in the Rails guide. In your case, that would mean you should have something like the following in routes.rb:
get '/dashboard/:user_id' => 'staticpages#recruiterpg'
Note that there's nothing magic about :user_id in that string.
Three: From a form which it doesn't seem like applies here, since a user isn't submitting data.
Since you're new, here is some information for you:
User Story
Firstly, the best way to resolve errors is to identify your user story.
A "user story" is a software principle in which you put the "user's perspective" first -- explaining how the software should work in conditions defined from how the user engages with it.
One of the main issues you have with your question is your user story is very weak; it's hard to decifer what you're trying to achieve.
Next time you ask a question, you should try your hardest to describe how the user should see your app, before providing code snippets :)
Controller
Your main issue is an antipattern.
An antipattern is basically a "hack" which will likely break another part of your app in future. Kind of like duct tape for software):
#app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def recruiterpg
#user = User.find(params[:user_id])
#adverts = #user.adverts
#applications = #user.forms
end
end
So you're showing a static page but yet you want to populate it with data?
Hmm...
What you should be doing is something like the following:
#config/routes.rb
resources :users do
resources :recruiters, only: :index #-> url.com/users/:user_id/recruiters
end
#app/controllers/recruiters_controller.rb
class RecruitersController < ApplicationController
def index
#user = User.find params[:user_id]
#adverts = #user.adverts
#applications = #user.forms
end
end
This will allow you to populate the following view:
#app/views/recruiters/index.html.erb
<%= #adverts %>
--
It's important to note the structure of the controller / routes here.
The issue you have is that you're calling a "static page" and expecting to have params available to find a User. This can only happen if you have params available...
Params
Rails is not magic, and as such if you want to look up a user, you have to provide the parameters to do so.
This is why you're able to look up current_user -- the params are already set for this user.
As such, you'll need to use something called nested routes in order to attain a user ID other than that of current_user:
#config/routes.rb
resources :users do
resources :recruiters #-> url.com/users/:user_id/recruiters
end

Redirect URL via User Input

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.

Rails controller view bug

Alright total noob, tried to find out what was happening but I haven't seen this come up with anyone else as far as I could find.
I'm going through a very basic blog tutorial with rails for the second time. made the home controller and the index view for it. all good. Next i made the 'posts' controller with basic classes and then views for index, edit, new, and show.
class PostsController < ApplicationController
def index
end
def new
end
def create
end
def edit
end
def update
end
def show
end
def destroy
end
end
The html for the views in their respective files
<h1>New Posts</h1>
<h1>Edit Posts</h1>
<h1>Show Posts</h1>
PROBLEM: In the browser when I go to localhost:3000/posts/edit it displays "Show Posts" instead of "Edit Posts". /show also displays "Show Posts". /new works fine and displays "New Posts".
I checked and it looks like localhost:3000/posts/literallyanything will display "Show posts".
I know this is potato level but I thought it was really odd and can't figure out why that would happen
Assuming you have a resource route for posts defined in your routing file like this:
resources :posts
you need to hit
localhost:3000/posts/<post id>/edit
More docs on routing here:
http://guides.rubyonrails.org/routing.html
You can always run rake:routes from your terminal to find out what route you need to hit for any given controller action.

Accessing specific pages in a controller/view on Rails App

I am trying to set individual Meta Descriptions and Titles to individual pages in a Ruby on Rails App. It was a previous developers App, that I have been given the task to edit. Also, I am new to Rails and Ruby.
The app has a controllers/pages_controller.rb where I was am able to set unique variables for #descriptionX and #title on some pages (mission and disclaimer), but not for others, such as pet_planning.
class PagesController < ApplicationController
def index
#title = params[:page].humanize
render params[:page]
end
def pet_planning
#descriptionX = 'pet planning'
#title = 'pet planning title'
render :pet_planning
end
def mission
#title = 'Our Mission Statement'
#descriptionX = 'Mission Description'
render :mission
end
def disclaimer
#title = 'Our Disclaimer'
render :disclaimer
end
end
I think that the render params[:page] is where I am getting lost. I'm not 100% sure of what this is doing, or how to use it.
I don't understand why I would be able to control the #title and #description of mission but not pet_planning when their views are both in the same /views/pages/ directory. And I can't seem to find any distinction between the two anywhere else in the app.
Also, tried to add = #title = 'Pet Planning' in the /views/pages/pet_planning.html.haml file. It does change the title, however it also displays at the top of the page content unexpectedly.
Any help would be appreciate. Thanks.
I'd recommend having a read of the ActionController guide, which explains how Rails turns a request from the user into a page to render.
Basically, when you send a request, for example
GET http://www.example.com/pages/?page=pet_planning
then Rails works out what to do with it using the router (the routing guide explains this in more detail). I would imagine that your app is set up so that the /pages route matches to the PagesController#index action. You can have a look in your config/routes.rb file and/or type rake routes at the terminal to see how your routes are set up.
The bit after the question mark in the request is the "query string", and Rails turns this into a params hash which, for my example above, would look like {:page => "pet_planning"}. Your index action looks at that to get the name of the page to render - that's what render params[:page] is doing.
I guess that the reason you can modify the variables for some of your pages and not others is that some of them have their own routes - /pages/mission uses the PagesController#mission action, for example - while certain pages are accessed via the index action using the page param - /pages/?page=pet_planning or possibly /pages/index.html?page=pet_planning.
Update: Your existing route
match 'practice_areas/:page' => 'pages#index', :as => :pages
could be broken up into
match 'practice_areas/pet_planning' => 'pages#pet_planning' :as => :pet_planning
# etc ...
which would correspond to a controller that looks like this
class PagesController < ApplicationController
def pet_planning
#title = "Pet planning!"
#description = "Whatever..."
end
end
Your suggestion is close, but because the route format is "controller_name#action_name", you would require multiple controllers that looked like this
class PetPlanningController < ApplicationController
def index
#title = "Pet planning!"
#description = "..."
end
end
and you would have to move your views from app/views/pages/pet_planning.html.haml to app/views/pet_planning/index.html.haml. So it's probably not quite what you want.
Note that there might be a better way to tackle the problem than splitting everything up into separate actions, if all you are doing differently in each one is customising the title and description. For example, you could use a hash that maps your page name to its corresponding information:
class PagesController < ApplicationController
PAGE_INFO = {
"pet_planning" => ["Pet planning!", "Whatever..."],
"other_page" => ["Title", "Description"],
# ...
}
def index
page_name = params[:page]
#title, #description = PAGE_INFO[page_name]
render page_name
end
end
The render calls in pet_planning, mission, and disclaimer do the same as default behavior, so those calls can be removed. They are telling rails to use the pages with the given file names. For the index method, this is rendering a page based on a parameter.
The title and description are likely set in the layout. Look in /views/layouts/application.html.haml or /views/layouts/pages.html.haml.

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