Show top posts in a proper way Rails - ruby-on-rails

I have a method in my posts_controller to display most viewed posts.
def top
#posts = Post.all.order("post.views DESC").page(params[:page]).per(10)
end
In routes I have
resources :posts do
collection do
get :top
end
end
The problem is: when i go to /posts/top i have an error: Missing template posts/top, application/top Do I need to write view files for my every method (top isn't the only one) or I can somehow display them in my index file without duplication of code?

Just render the index template at the end of your method:
def top
#posts = Post.all.order("post.views DESC").page(params[:page]).per(10)
render :index
end

I would suggest you to have a close look to rails layouts and rendering documentation. You will get your answer as well concept behind them. Below is the snippet of doc.
In most cases, the ActionController::Base#render method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behavior of render. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.

Related

How does ActionView::Template :render decide where to load from?

Here's the problem, let's say I have a few resources that I want index all on the same page separated by tabs, in this case it's different types of content: videos, pdfs, etc. I also have a partial that gets included into several different views, in this case a search functionality. The partial does something like this to let you customize various parts of it:
in _search.html.slim:
.toolbar
.left
= render 'left_toolbar', f: f
The project tree looks something like:
app
--views
----media
------index.html.slim
------videos_list.html.slim
------pdfs_list.html.slim
------videos
--------_left_toolbar.html.slim
------pdfs
--------_left_toolbar.html.slim
And I have a controller that manages requests coming in from the page with the various content-resources:
in media_library_controller.rb:
def index
end
def videos
<sets everything needed to render a videos list, #videos etc.>
render :index
end
def pdfs
<sets everything needed to render a pdf list, #pdfs etc.>
render :index
end
And in the index.html.slim
= render 'application/search'
ul.nav.nav-tabs
li.active
a href='#videosListTab' data-toggle='tab' Videos
li
a href='#pdfsListTab' data-toggle='tab' PDFs
ul.tab-content
li.tab-pane.active#videosListTab
.js-video-collection
= render 'videos_list' if #videos
li.tab-pane.active#pdfsListTab
.js-pdf-collection
= render 'pdfs_list' if #pdfs
What I want is to be able to define in the controller which folder left_toolbar.html.slim is looked for when search.html.slim goes to look for it
I have tried overriding controller_path which doesn't seem to work. prepend_view_path seems promising but it appends the controller name to the end of the view path so that I have something like app/views/media_library/videos/media_library when I set it. Any advice? I don't want to had a local to the search partial.
What I want is to be able to define in the controller which folder left_toolbar.html.slim is looked for when search.html.slim goes to look for it
Perhaps you're getting ahead of yourself:
<%= render "shared/menu" %>
That code will pull in the partial from
app/views/shared/_menu.html.erb.
From what I understand of your question, you want to know how Rails can specify a specific path for your partial? There's no need - partials can be referenced from anywhere in your app.
So when you mention that search.html goes looking for it, you just have to prepend the folder for left_toolbar to the path:
render 'your_path/left_toolbar', f: f
Again, with your media tabs... they're all partials - just use the folder name in the partial path:
= render 'app/views/media_library/pdfs_list' if #pdfs

Rails controller action duplication

I have a controller show action which does some stuff and renders a view but due to some custom routing, I need a totally different action in a totally different controller to perform the same stuff and render the same view.
I don't really wish to duplicate the code. Is there somewhere I can put it and call it from both locations?
Edit:
I basically need to run the following from Collection#Show AND also from Gallery#SplitUrl:
#collection = Collection.find_by_id(params[:id])
if #collection.has_children?
#collections = #collection.children
else
redirect_to [#collection, :albums]
end
I cannot just redirect_to Collection#Show at the end of Gallery#SplitUrl as the redirect causes my custom URL's to be lost as it's a new request.
You could put the view content into a partial (/app/views/home/_example.html.erb) and then use the render "example" command in the HomeController.
The "some stuff" code you talk about could be put into a helper file /app/helpers/... to save you from duplicating code in the two separate controllers although it could depend on what the code is trying to do in the first place.
http://guides.rubyonrails.org/layouts_and_rendering.html
This might provide more information on the subject in general.
I think the simplest approach would be to add a route definition for your new url and map that to your existing controller's action.
Something like follows:
# config/routes.rb
# Existing resource
resources :foos
# The resource in question
resources :bars do
get :show, controller: 'foos', action: 'show', as: bar_foo_common_show
end
Note that this will give you /bar/:id, where id represents a bar resource. So in your foo#show action, your finder needs to be executed on appropriate class. This is when a few lines of hacky codes get added, for e.g. checking for request referer.
I think moving the common code to a function in possibly application_controller and calling that function from both show action would be cleaner approach, but based on my understanding you already have a similar scenario except for the common code in application_controller, and would like to try out a different approach!

Rendering on specific pages through application.html.erb file?

I have a search box that I only want to render on particular pages through the navigation section in my application.html.erb file.
How do I set exceptions? Is this done through the main application controller?
There are several ways to do this.
Most obvious way is to use an instance variable for flagging.
In your application.html.erb
<%= render 'search' if #search_box %>
And wherever you want to show the search, set the flag instance variable to true.
def show
#search_box = true
...
end
Edit
You might also want to utilize Rails' filters in your controllers if you want multiple actions to show search.
before_action :flag_search_box, :only => [:show, :new, :all_kinds_of_controller_actions_where_i_wanna_show_search]
...
private
def flag_search_box
#search_box = true
end
I might suggest to put the search box not in the application.html page but maybe in the separate html pages that you want it to render on. You could make the search a partial so that you could access it from the other pages with just one line of code.

Add RESTful Action

The source of my information is section 2.9 here:
[http://guides.rubyonrails.org/routing.html#connecting-urls-to-code][1]
What I'm trying to do is add a custom action "search" and corresponding view.
So, as it says to do in the documentation, I've added this code in my config/routes.rb file:
resources :dimensions do
collection do
get "search"
end
end
I've also defined in the dimensions_controller file:
def search
#dimensions = Dimension.all
respond_to do |format|
format.html # search.html.erb
format.json { render json: #dimensions }
end
end
I then stopped and restarted the rails server, but when I navigate to /dimensions/home, I'm still getting this error message:
Couldn't find Dimension with id=search
Also showing that my parameter is:
{"id"=>"search"}
So am I just missing another bit of code that gives the instruction to interpret /dimension/search as a collection action as opposed to the show action?
I've already confirmed that search_dimensions_path exists, so I know that the resource block in the routes.rb file is actually adding paths. It's just interpreting them as a separate search action that's giving me trouble.
Thanks for your time.
This code should work fine. Can you show us your routes.rb file?
On a side note, you probably don't want to have a separate action for searching, using the index action is the preferred way.
Found the issue:
I had to make the resource declaration in my config/routes.db file for dimensions after creating the collection action, like so:
resources :dimensions do
collection do
get "search"
end
end
resources :dimensions
Then everything worked as expected.

Identical Files behave differently due to link with controller

I am building my first app with ROR and stumbled upon a couple of problems due to my understanding of the MVC
I have a page to add a new item, and this works fine, rails magically hooks it up to the items controller and somehow by magic it knows to look in the method 'new' as the page is called new.
But this layer is confusing me, as i need to now create a different version of new, same functionality but with a different look so to use a different layout to application.html.erb
So i attempt to create a copy of new.html.erb and create bookmarklet.html.erb - both contain exactly the same code: a link to a form. but of course bookmarklet will error on me because it does not have that link in the controller - how do i 'wire' up bookmarklet so that i can call the new method and so that it can behave in a similar way to the identical new.html.erb
Also, how can i tell the new bookmarklet.html.erb to ignore the application.html.erb and get its layout from another file?
thanks in advance
The magic happens in the routes. Rails uses something called RESTful routes, which is taking HTTP verbs and assigning standard actions to it. the new action is a GET request in HTTP speak, and if you are using scaffolding or following REST, will have the ruby call to build a new object in the controller, as an instance variable so you can use it in your view.
You have to tell rails routes that you want to BREAK this arrangement and to let /items/bookmarklet be used in the controller.
In your routes.rb file replace resources :items with
resources items do
member do
get 'bookmarklet'
end
end
In your controller put:
def bookmarklet
#item = Item.new
render :template => "bookmarklet", :layout => "different_layout" # or you can put this on the top of the controller, which is my style, but whatevs.
end
You should look into partials, if you are doing this as they clean up your code immensely.
A better way to think of things is to start with the controller instead of the view html.erb files. So each public method in your controller is effectively a page or action in the site. When you need a new action or page, add the method to the controller first. Then create the relevant view files.
So in your controller you'll need something like:
def bookmarklet
#item = Item.new(params[:item])
#item.save
render :template => "items/bookmarklet.html.erb", :layout => "different_layout.html.erb"
end
Normally you don't need to call render manually in the controller, but since you want a different layout than the default you need to specify it using render.

Resources