How to minimize/refactor my events controller - ruby-on-rails

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!

Related

Show page failing to pass seed data along (rails)

I have a seed database that renders Admin user information and links to individual member profile pages. The database renders images and data fine in the main page, but comes up nil <p>, with id </p> *== $0* when I try to call the individual objects on the show page.
routes.rb:
get '/team', to: 'pages#team'
get '/team/:id', to: 'pages#show', as: 'agent'
pages_controller.rb:
def team
#admins = Admin.where(role: :staff)
end
def show
#admin = Admin.find(params[:id])
end
views/pages/team.html.erb:
<%= link_to agent_path(agent.id), class:"team-link" do %>
.....
<% end %> (all working, routes stable)
views/pages/show.html.erb:
<p><% #admin.name %>, with id <% #admin.id %></p>
#rendering == $0
Where's the data connection breaking down? I've been working at this for a day or so now and this is a fairly solid wall for me.
Ah, your ERB statements are a bit off. You need to use the <%= %> format for the output of your ruby code to be shown (note the =).
So <% #admin.name %> should be <%= #admin.name %>, etc.

Creating site-wide search in a Rails app

I'm trying to create a search form that retrieves results based on a user's query for a restaurant's name. So far I've setup its route, controller, and index view.
routes.rb
resources :search, :only => [:index]
search_controller.rb
class SearchController < ApplicationController
def index
if params[:query].present?
#restaurants = Restaurant.search(params[:query])
else
#restaurants = Restaurant.all
end
end
end
search/index.html.erb
<% #restaurants.each do |restaurant| %>
<%= restaurant.name %>
<% end %>
Here is how the search for is setup:
layouts/_header.html.erb
<%= form_for search_index_path, method: :get do |f| %>
<%= text_field_tag :query, params[:query] %>
<%= submit_tag "Search", name: nil %>
<% end %>
Right now I'm running into two problems. The first being that if I enter a query and submit, the page doesn't go to the index page. All it does is append the query to the current page I'm on:
localhot:3000/restaurant?utf8=✓&query=pizza
Second is that I'm getting every restaurant in my db on the index page (as expected). Is there a way that I can make it so the page is blank for anything other than on search requests?
Question 1
Use form_tag instead of form_for, since the latter is used to handle specific model objects and this is not the case.
Question 2
You can achieve that by:
if params[:query].present?
#restaurants = Restaurant.search(params[:query])
else
#restaurants = [] # or Restaurant.none if you need a chainable scope
end

Invoking Rails Partials

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'

Working with Third party yummly api in rails 4

I've searched all over the web for decent explanations of how to do what I want to do, but cannot find any.
What I want to do is have the user be able to search through the yummly api and return some results back...
Here is some code.
index.html.erb
<% #results.each do |r| %>
<%= r.name %>
<% end %>
home_controller.rb
class HomeController < ApplicationController
def index
#results = Yummly.search('Onion')
#recipe = #results.map(&:to_s)
end
end
I've installed the Yummly gem which allows me to call Yummly.search
How can I allow the user to search for the term instead of hard coding it? It returns just fine hard coded but I cannot figure out how to allow the user to search.
Thank you!
Ideally, you would have a form that allows the user to enter a search term, and then pass that term to the Yummly API.
Something like (substitute your own route name):
<%= form_tag({controller: 'home', action: 'index'}, {method: :get}) do %>
<%= text_field_tag 'search' %>
<% end %>
And in your controller:
Yummly.search(params[:search])

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