Invoking Rails Partials - ruby-on-rails

So I understand how partial works and how useful they can be. I'm currently working my way through a screencast on building a web app and even though he says not to worry if you don't understand, I must understand regardless.
This is the controller and the action for index
class BooksController < ApplicationController
before_action :authenticate_user!, only: [:new, :edit, :create, :update, :destroy]
before_action :set_book, only: [:show, :edit, :update, :destroy]
# GET /books
# GET /books.json
def index
#books = Book.where(availability: true)
end
and this is the corresponding index.html.erb
<p id="notice"><%= notice %></p>
<div class='row'>
<%= render #books %>
</div>
<%= link_to 'New Book', new_book_path %>
I have a partial in the books view called _books.html.erb
Now the thing I'm unsure about is the #books = Book.where(availability: true)
I understand that #books is the instance variable for the index action, but what exactly is Book.where(availability: true)? I the parameter being passed, but I'm still lost on Book. Is that referencing the model? I've read through the Action Controller overview over at guides.rubyonrails repeatedly and I'm still lost on this one singular point and it's driving me crazy and slightly frustrated that something that simple isn't clicking.
How exactly is the partial _books.html.erb being called? If I understand it correctly, def index tells rails to search my books view for index.html.erb correct?
Hmmm typing this out (and still trying to find the answer for myself) has me thinking I have it all wrong.
The partial is used for styling purposes in this instance correct? This is my _books.html.erb partial
<div class='col-md-3'>
<div class='thumbnail'>
<%= link_to book_path(book) do %>
<%= image_tag 'book.jpg', class: 'img-responsive', alt: 'Pretty Bird' %>
<% end %>
<div class='caption'>
<h3><%=link_to book.name, book_path(book) %></h3>
<h4><%= book.author %></h4>
<p><%= book.description %></p>
</div>
</div>
#books = Books.xxxx is like attr_reader correct? So the render #book in the index.html.erb is basically saying, fetch all stored book data from the user and display them via the _book partial, but only the ones whose availability equal true. Is this accurate? And I assume the stored data is sitting in development.sqlite3 within my db folder and that I can view this data via the rails console right? God I hope I'm on the right track at least.
If so, I guess my question ultimately is how does rails know to use the partial?
Any direction would be greatly appreciated!

When you call
#books = Book.where(availability: true)
So here books table in database which has been mapped with the help of ActiveRecord model Book.
books table has records with unique id's and fields you defined on Model are mapped with columns in database table. availability is the column in table.
So above query will call books table and fetch all the books where availability is 1 or true
This is Object relational mapping Book is the model which represent a table in database named as books,
ORM is Object Relational Mapper. It means you don't have to manually call the database yourself, the ORM handles it for you.
Ruby on Rails uses one called ActiveRecord
<%= render #books %>
which is like
<%= render partial: 'books', collection: #books, locals: {name: "ruby"} %>
which means that render the partial which should be named as _books which should be in books views else you can also specify the path render partial: 'users/books'

Related

How to minimize/refactor my events controller

Context: this is the 1st rails app I've built and it is fully deployed with fly.io
In the spirit of DRY, I feel like I am definitely repeating myself alot with my current configuration, but due to my lack of Rails knowledge, I'm not sure of a better way.
I have an app where I'm listing dance events around the world and I created individal pages for individual cities.
The first thing I did was create a scope in the event.rb:
scope :in_austin, -> { where(city: "Austin").or(where(city: "Pflugerville")).where(state: "Texas") }
Next, I created a page for the city in the events folder atx_index.html.erb and in that file I applied the scope:
<%= render 'events/local_search_form', { url: atx_index_path } %>
<h3>Upcoming Events</h3>
<div class="event-list-wrapper">
<% #events.upcoming_events.in_austin.each do |event| %>
<%= render 'event', event: event %>
<% end %>
</div>
<h3>Past Events</h3>
<div class="event-list-wrapper past-event">
<% #events.past_events.in_austin.each do |event| %>
<%= render 'event', event: event %>
<% end %>
</div>
On the local city page I'm also pulling in a separate partial for a search form for local pages modified from the main seach form partial I'm using on my main event index page:
<% if local_assigns.has_key? :url %>
<%= search_form_for #q, { url: url } do |f| %>
[Local Search Form]
<% end %>
<% end %>
I added the route to the local page:
get '/atx', to: 'events#atx_index', as: 'atx_index'
I also found out I needed to add an exception to my before_action in the events controller to not require a user:
before_action :require_user, except: %i[show index atx_index ]
as well as duplicate my index method for the local city since I'm using Ransack to search on my main index page as well as the individual city page:
def atx_index
if params[:q]
params[:q][:combinator] = "and"
params[:q][:groupings] = []
[ Bunch of Ransack parameters ]
end
#q = Event.ransack(params[:q])
#events = #q.result(distinct: true).order(event_start_date: :asc)
end
So once I added the above I was able to get my local city view to display and function correctly with Ransack.
The issue I have now is that I have about 20 more cities (and scaling wise this list of cities could grow 100+) I've followed this process with and now my controller is 500+ lines long as I'm duplicating the index method for each city as well as the before_action array getting longer and longer.
My app is working, but I'm pretty sure there's a way to refactor this, especially my controller.
Any insights or resources I could look into would be appreciated!

Generate method dynamically using strong params in Rails

Is it possible to generate a dynamic method using the strong parameter I get from my view?
In my view I will generate a different path according to the card the user clicks on. Then, in the controller I want to generate a dynamic method using the parameter obtained from the view, but I'm not sure how to write that. Thanks!
show.html.erb
<div class="branch-names">
<% #branches.each do |branch| %>
<div>
<%= image_tag "cat.jpeg" %>
<%= link_to "#{branch.name} Posts", send("#{branch.branch}_posts_path") %>
</div>
<% end %>
</div>
posts_controller.rb
def self.define_category(name)
define_method(name) do |params[:id]|
#posts = Post.where(category_id = params[:id])
end
end
define_category("#{params[:id]}")
You shouldn't define method based on user input. It may cause security issue, and for sure it causes performance penalty related to method cache invalidation.
Instead you can create one method that have an alternarive on params[:id] and then decide what to show to the user:
class MyController
def branches
case params[:id]
when('cat')
do_cat_stuff
when('dog')
do_dog_stuff
end
end
end
For having routes like /posts/cats you do not have to add dynamic methods. Think of branch like of an id of category:
routes:
resources :post_categories, only:[:index, :show]
view:
...
<%= link_to "#{branch.name} Posts", post_category_path(branch.branch) %>
PostCategories controller:
def show
#posts = Post.where(category_id: params[:id])
end
Also you can make posts a nested resource under categories and use a more RESTful structure with /post_categories/some_branch/posts mapping to posts#index

How to render the index view, from inside a view in ruby-on-rails

I'm trying to render a the index view inside my ruby-on-rails application. How do a I render the index view, from inside a view passing an array of things to display? Using the link_to.
I do not want to re-route to the controller, I just want to link_to a view passing it the variables it needs, how can I do this?
EDIT:
I am trying to create a page type functionality in the index of my article model. So I have around 400 articles for example, and when the index action in the article controller is called, it of course renders the index view which is calling a partial for each article in the '#articles array' passed on by article controller's index action.
So in the view, I'm trying to do something like:
<% count = 0 %>
<% #articles.each do |article| %>
<% if count <10 %>
<%= render partial: 'index_articles', locals: {article: article} %>
<% count = count + 1 %>
<% end %>
<% end %>
<% #articles = #articles.drop(10) %>
<% if #articles.any? %>
<%= link_to "Next", 'articles', locals: {#articles => #articles} %>
<% end %>
Thank you in advanced for all of your help.
You'll need to use the render command, probably with a partial:
<%= render "controller/index", collection: ["your", "array"], as: :object_name %>
You will have to call a controller action to generate this. You cannot simply load it on your screen, unless it was preloaded inside your javascript for something:
#View
<%= link_to "Index", controllers_path(ids: ["1","2"]), remote: true %>
#app/controllers/your_controller.rb
class YourController < ApplicationController
def index
#posts = request.xhr? Post.find(params[:ids]) : Post.all
respond_to do |format|
format.js #-> app/views/controller/index.js.erb
format.html
end
end
end
#app/views/controller/index.js.erb
$(".element").html("<%=j render 'index' %>");
There are several issues with this approach...
Flow
First of all, your flow of your app should be as structured as possible.
In short, if you're calling the index view inside another action, it's not the index view any more.
What you should look at is how to use a partial in your app:
#app/controller/views/_partial.html.erb
<%= post.title %>
This way, you can adapt your index view and your other page to use the partial in their respective action layouts:
#app/controller/views/index.html.erb
<%= render "partial", collection: #posts, as: :post %>
This will allow you to "reuse" code much in the way you want. This will be much more appropriate than trying to invoke other action/views.
-
Resources
Secondly, you'll want to look at how your app functions.
Your index view is meant to show all the items for a particular object. Whilst you're free to change this as you want, the fact remains that you have to keep some structure.
You should read up on the routes for your actions, and how they're meant to work in your application. This will give you some perspective on the resourceful nature of Rails routes, and how you'll have to call specific routes with specific actions.
Your problem is probably that the file needs to be named _index.html.erb. You can have another file named index.html.erb which just renders _index.html.erb.
If you need a full guide on using AJAX, look up the railscast. If you're not using AJAX and you just want to render it, then you don't use link_to. You just do <%= render :index %>.

Having problems with putting database to use

So I have this books database and a burrows database. In burrows, there is a field for book_id and also a field for user_id, so that I can see who burrowed which book.
Now, I am trying to create a controller and view for it but it is not going well really. Right now the view is looking like this:
<% provide(:title, "Burrow") %>
<b align="center">Choose the name of the book you want t burrow'</b>
<%= form_for(#book) do |f| %>
<div class="forms">
<%= f.name %>
<%= f.check_box(:book_id) %>
<%= f.submit 'Submit!' %>
</div>
<% end %>
But this puts me to the problem where it creates an error because I want to put all books into #books in burrows controller. But I dont really see any other way? \
The final idea would be so that I have all the books displayed and after them a checkbox, so I can select which books I want to burrow. And after that I also want a dropdown menu where all users are listed, I can choose to burrow the book for another user, but the default value would be the logged in user but theres time till that. Right now I am struggline to understand, why my solution for listing books does not work?
Listing my controller here also:
class BurrowsController < ApplicationController
before_action :signed_in_user, only: [:index,:edit,:update, :destroy]
before_action :admin_user, only: :destroy
def index
#burrows = Burrow.all
end
def show
#burrow = Burrow.find(params[:id])
end
def new
#burrow = Burrow.new
end
def create
#burrow = Burrow.new(burrow_params)
if #burrow.save
flash[:success] = "Burrowing a book was successful!"
redirect_to #burrow
else
render 'new'
end
end
def listing
#book_list = Book.all
end
# Private section, makes the page unable to be seen for non logged in users
private
def burrow_params
params.require(:burrow).permit(:user_id, :book_id)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
# Redirecting not logged in user etc.
def signed_in_user
unless signed_in?
store_location
redirect_to '/sessions/new', notice: "Please sign in!"
end
end
end
Right now I am struggline to understand, why my solution for listing books does not work?
I'm not sure what listing you mean. The view you pasted apparently corresponds to the controller action Burrows#new?
So I'm going to tell you what's wrong with your view:
<%= form_for(#book) do |f| %>
This prints a form for a Book, not a Burrow. You could create a new book with this form, but that's certainly not what you're trying to do here.
You'll want to have all these variables in your controller:
def new
#users = User.all
#books = Book.all
#burrow = Burrow.new
end
so you can use collection_select with #users, have a #burrow object to use with form_for, and do a each loop on #books, for instance, like this:
<%= form_for(#burrow) do |f| %>
<% #books.each do |book| %>
<%= f.check_box :book_ids, multiple: true, book.id, nil %>
<% end %>
<%= f.collection_select :user_id, #users, :id, :name, {}, selected: current_user.id %>
<%= f.submit %>
<% end %>
Click the links for documentation on these commands: collection_select, check_box
This is not ideal code, but it's as close to your example as I can get.
I understand if you don't get this right away. Your code is a bit of a mess right now and there's too much wrong with it to be explained in one post and fixed by just one line. You might want to start over, and make a single controller action work before you try to make everything at once.
Sometimes it's better to sit back and really think about what you're trying to achieve, and first lay out how to achieve it inside your head; and only then start programming!
It seems to me that you're using the RailsTutorial by Michael Hartl, so all I can recommend to you right now is, read what you've read again more thoroughly and try to stick to his examples first until you feel confident, before really starting to make your very own application.

How do I setup the views for nested resources that are displayed as tabs within their parent resource in Rails?

I have a resource Company that has many Projects and People. The index action for the companies displays each company in a table. The show action shows some additional information about it, and then what I'd like to show is another table, with tabs "Projects" and "People".
When I click on the "People" tab, I should go to URL "companies/:id/people", and likewise for the "Projects" tab. I'm not worried about AJAX or pre-loading all of this information into the #company variable during the show action. A simple nested resource is fine.
Now when I'm at "companies/:id/people", it will use PeopleController#index, but I want to show that view (which is JUST it's table, I suppose?) nested within the company's show view. So that when I switch between "companies/:id/people" and "companies/:id/projects", the only thing changing is the table, not the company information around the outside.
Is this sort of thing easily do-able? If Rails isn't build to handle this sort of thing easily, I don't mind using something else. I just don't have much experience with the view layer, so I don't know much about it since I primarily work with JSON.
Basic Example:
ProjectsController && PeopleController:
layout :current_layout
def current_layout
if #company && #company.persisted? && request.path_parameters[:action] == "index" # i prefer helper 'current_action'
"company"
else
"application"
end
end
Helper:
def parent_layout(layout)
#view_flow.set(:layout, self.output_buffer)
self.output_buffer = render(:file => "layouts/#{layout}")
end
Company layout:
#views/layouts/company.html.erb
<h1><%= #company %></h1>
<ul class="tabs">
<li>Info</li>
<li>Info</li>
<li>Info</li>
</ul>
<%= yield %>
<%= parent_layout(:application) %>
People template:
# views/people/index.html.erb
<% if current_layout == "company" %> # just table
<%= render "people_table" %>
<% else %>
<h1>People controller</h3>
<%= render #people %>
<% end %>
Projects template:
# views/projects/index.html.erb
<% if current_layout == "company" %> # just table
<%= render "projects_table" %>
<% else %>
<h1>Projects controller</h3>
<%= render #projects %>
<% end %>
I suggest you take a look at the rails guides, specifically routing from the outside in.
However rails can handle this, your route would be setup in the following way:
resources :company do
resource :projects
resource :people
end
I assume you already have all your CRUD actions setup, then this would work. However do note, it will change your named routes.
i.e
if you were calling
projects_path
in your views, they will now become:
company_projects_path
You can see a full list of routes with the command
rake routes

Resources