How to retrieve the searched result with Pg gem multisearch - ruby-on-rails

I am currently using Pg gem search in my application. Now, I want to use the pg search to search for the data across two models which are school and teacher. I created a welcome page let the audience use the search functionality there. However, at the moment I am wondering how can I retrieve the result after the audience enter the query, for example, if they type a specific name of a teacher or a school, I want to return that teacher/school with a link so that the user can click on and find out more information about that school/teacher.
Here are my files:
search.haml:
.row
%h1.text-center Tìm Trường Học và Giáo Viên
= form_tag search_welcome_index_url, :id => 'custom-search-input', method: :get, class: "input-group col-md-12", role: "search" do
.input-group
= text_field_tag :search,
params[:search], class: "search-query form-control", placeholder: "Tên trường học, ví dụ: Đại Học Bách Khoa Hồ Chí Minh...."
%span.input-group-btn
= button_tag( :class => "btn btn-danger") do
.search-size
%span.glyphicon.glyphicon-search
= #results
welcome_controller.rb:
class WelcomeController < ApplicationController
def welcome
end
def search
#results = PgSearch.multisearch(params[:search])
end
end
teacher.rb:
class Teacher < ActiveRecord::Base
include PgSearch
multisearchable against: [:full_name]
belongs_to :school
has_many :ratings
end
school.rb:
class School < ActiveRecord::Base
include PgSearch
multisearchable :against => [:name]
has_many :teachers, dependent: :destroy
end
routes.rb:
Rails.application.routes.draw do
devise_for :users
resources :welcome do
collection do
get 'search'
end
end
resources :schools do
collection do
get 'search'
end
resources :teachers do
collection do
get 'search'
end
end
end
resources :teachers do
resources :ratings
end
root 'welcome#welcome'
end

Related

Rails 4 Error: ActiveRecord::RecordNotFound Couldn't find id=

I'm fairly new to rails and into coding my first app. Just can't figure out how to target a user the current_user favorited (Three models: User, Tool, FavoriteUser).
Controller (Tools)
def index
#favorites = current_user.favorites.order("created_at DESC")
#userfavorites = current_user.userfavorites.order("created_at DESC")
#tools = Tool.where(user_id: current_user).order("created_at DESC")
#user = current_user.favorite_user # problematic!
#cuser = current_user
end
Index View (Tools)
%h2 My Favorite Users
- #userfavorites.each do |user|
= image_tag gravatar_for #user if #user.use_gravatar == true
= image_tag #user.avatar_filename.url if #user.use_gravatar == false
%h2= link_to #user.username, #user
%p= link_to "Favorite", userfavorite_user_path(user, type: "favorite"), method: :get
%p= link_to "Unfavorite", userfavorite_user_path(user, type: "unfavorite"), method: :get
If I run this in my browser it appears following error:
NoMethodError in ToolsController#index
undefined method `favorite_user' for #<User:0x39df638>
FavoriteUser Database:
user_id:integer #favorited
c_user_id:integer #active user
I simply can't figure it out.
Thanks in advance for your help!
RELATIONSHIPS:
user.rb
has_many :favorite_users # just the 'relationships'
# Favorite users of user
has_many :favorite_relationships, class_name: "FavoriteUser", foreign_key: "c_user_id"
has_many :userfavorites, through: :favorite_relationships, source: :user
# Favorited by a user
has_many :favorited_relationships, class_name: "FavoriteUser", foreign_key: "user_id"
has_many :userfavorited_by, through: :favorited_relationships, source: :c_user
favorite_user.rb
class FavoriteUser < ActiveRecord::Base
belongs_to :c_user, class_name: "User"
belongs_to :user, class_name: "User"
end
ROUTES
Rails.application.routes.draw do
devise_for :users
get 'welcome/index'
resources :tools do
member do
get "like", to: "tools#upvote"
get "dislike", to: "tools#downvote"
end
get :favorite, on: :member
end
resources :users, only: [:index, :show] do
get :userfavorite, on: :member
end
authenticated :user do
root 'tools#index', as: "authenticated_root"
end
root 'welcome#index'
get '/search' => 'tools#search'
get '/users' => 'users#index'
end
change this:
#user = current_user.favorite_user
to:
#favorite_users = current_user.favorite_users
user has_many favorite_users
don't forget to rename the instance variable wherever #user was being called.
That's the solution!
#userfavorites = current_user.userfavorites

Deleting a 'friend' that added you using self-referential association

I can implement reverse relationships, so if UserA adds UserB, then it shows UserA in B's profile, and visa versa.
But I cannot figure out how to let UserB remove UserA as a friend, if UserA added UserB.
I've tried so many different ways, but everytime I change something it moves the problem elsewhere! I can't tell if the fundamental issue is:
a. how the FriendshipsController destroy method is defined
b. whether I need another controller specifically just to handle
InverseFriendships destroy
c. if I need to customize the routes
d. if all the above are ok, but the code I have in my views (specifically
the _suggested_connections partial) is calling the wrong controller
and/or route
e. or none of the above.
Code snippets below:
class FriendshipsController < ApplicationController
def destroy
#friendship = current_user.friendships.find(params[:id])
#friendship.destroy
flash[:notice] = "Removed friendship."
redirect_to current_user
end
In the view
<% #user.inverse_friends.each do |inverse_friendship| %>
<li>
<%= inverse_friendship.name %>
<%= link_to "remove", #user.inverse_friendships, :method => :delete, :class => "btn-small btn-danger" %><br />
<%= image_tag inverse_friendship.avatar(:thumb) %>
My models:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: 'User'
attr_accessible :friend_id, :user_id
end
class User < ActiveRecord::Base
has_many :friendships, dependent: :destroy
has_many :friends, through: :friendships
has_many :inverse_friendships, dependent: :destroy, class_name: "Friendship", foreign_key: "friend_id"
has_many :inverse_friends, through: :inverse_friendships, source: :user
And routes:
resources :friendships
authenticated :user do
root :to => 'home#index'
end
root :to => "home#index"
devise_for :users, :controllers => { :registrations => :registrations }
resources :users
Your main problem is a:
a. how the FriendshipsController destroy method is defined
You're looking for the friendship in the current_user.friendships, but it's not there. It's in inverse_friendships.
You'd need to either check both associations, or let the controller know which one you're looking for. The latter is probably preferable since although they are the same class, they are different resources. Something like this maybe:
# In routes, route inverse friendships to the same controller, but with a
# different path (I'm routing everything here, you may not need that.)
resources :friendships
resources :inverse_friendships, :controller => 'friendships'
# Then in your friendships controller, use the path to determine which
# collection you're working with:
#
def destroy
#friendship = collection.find(params[:id])
# ...
end
# the other collection methods would use the same collection, if you needed them,
# for example:
def create
#friendship = collection.build(params[:friendship])
# ..
end
protected
# simple case statement here, but you get the idea
def collection
case request.path
when /\/inverse_friendships/ then current_user.inverse_friendships
else current_user.friendships
end
end
Finally in your view you'd route to an inverse friendship like:
<%= link_to "remove", inverse_friendship_path(friendship), :method => :delete %>
A normal friendship could use the shorter form, or the full named route:
<%= link_to "remove", friendship, :method => :delete %>
OR
<%= link_to "remove", friendship_path(friendship), :method => :delete %>
EDIT: Searching both associations.
Of course if you wanted to keep it simple, and had no other use for inverse_friends being a separate resource, you could always just...
def destroy
id, cid = params[:id], current_user.id
# search both associations (two queries)
#friendship = current_user.friendships.find_by_id(id) ||
current_user.inverse_friendships.find(id)
# or query friendship looking for both types
#friendship = Friendship.
where("user_id = ? OR friend_id = ?", cid, cid).find(id)
# ...
end

Has_many through and path helper - accessing resources through the application

I have an app in which users can follow law firms
I have 3 models
- User
- Firm
- Follow
class Firm < ActiveRecord::Base
has_many :follows, :dependent => :destroy
has_many :users, :through => :follows
class User < ActiveRecord::Base
has_many :follows, :dependent => :destroy
has_many :firms, :through => :follows
class Follow < ActiveRecord::Base
belongs_to :firm
belongs_to :user
In a table in my firms index view, I would like to take the current signed and create an association between that user and the law firm - through the follow table.
In effect doing this -
firm.users << User(current)
This is the code that I have at present, how would you suggest that I structure the path, and the corresponding controller?
<% #firms.each do |firm| %>
<tr id = "firm_<%= firm.id %>">
<td><%= link_to image_tag(firm.logo_url, :size => "80x120"), firm.url %></td>
<td><%= link_to firm.name, firm_path(firm) %></td>
<% if user_signed_in? %><td>
<%= button_to 'Follow', ? , method: :post %>
</td>
<% end %>
I am using devise for the User authentication and have put the following helpers into application helper to allow my login partial to function in a different models view.
def resource_name
:user
end
def resource_id
:user_id
end
def resource
#resource ||= User.new
end
The simplest way would be to have a follow action on a FirmsController.
In config/routes.rb:
resources :firms do
post :follow, on: :member
end
In your FirmsController:
def follow
#firm.users << current_user
end
In your view:
<%= link_to "Follow", follow_firm_path(#firm), method: :post %>
Another way would be to represent a follow relationship as a singular resource. You'd follow a firm by POSTing to /firms/1234/follow and you'd unfollow a firm by sending a DELETE request to /firms/1234/follow.
If you wanted to take that approach, you'd stick this in your config/routes.rb:
resources :firms do
resource :follow, on: :member
end
And you'd create a FollowsController like this:
class FollowsController < ApplicationController
def create
#firm = Firm.find params[:firm_id]
#firm.users << current_user
# respond with success
end
def destroy
#firm = Firm.find params[:firm_id]
#firm.users.delete current_user
# respond with success
end
end

Not showing user during iteration if user already belongs to "group" relationship

I have this relationship where User can create a document(trip) and invite other users to a group that belongs to that document. My relationship indicates that "Group" has a user_id and trip_id column, so for every user I invite, a new Group record will be created in the database.
When I am inviting other users, I only want users who are NOT in the group to appear. Users who are already in the group should not show up, but my view still shows the users.
I've been playing around with <% if !friend.trips.include?(#trip)%>, but I can't seem to get the correct view. The record is being created in the database correctly.
Also, when I am viewing groups/new.html.erb, this is the url http://localhost:3000/groups/new?id=2, where the id is the trip_id.
My question:
Am I using restful convention? That is, should I be using the new method here (as is) or should I be using the index method instead?
How do I iterate through each friend's groups to make sure that none of the group's trip_id is equivalent to #trip.id?
Thanks!
view (/groups/new.html.erb)
<% if !#friends.blank? %>
<% #friends.each do |friend| %>
<% if !friend.trips.include?(#trip)%>
<%= link_to groups_path(:user_id => friend.id, :trip_id => #trip.id),
:method => :post, :action => 'create' do %>
<div id="addfriend_totrip_button_groupsnew">add friend to trip</div>
<% end %>
<% end %>
<% end %>
<% end %>
groups_controller.rb
class GroupsController < ApplicationController
before_filter :authenticate, :only => [:update, :create, :destroy]
def new
#trip = Trip.find(params[:id])
#user = User.find(current_user)
#group = Group.new
#friends = #user.friends.all
end
def create
#trip = Trip.find(params[:trip_id])
#user = User.find(params[:user_id])
#group = Group.create(:user_id => #user.id, :trip_id => #trip.id)
if #group.save
flash[:success] = "Friend added to group."
redirect_to groups_path(:id => #trip.id)
else
flash[:error] = "Could not add friend."
redirect_to root_path
end
end
end
user.rb
class User < ActiveRecord::Base
has_many :trips, :through => :groups
has_many :trips, :dependent => :destroy
has_many :groups
end
trip.rb
class Trip < ActiveRecord::Base
belongs_to :user
belongs_to :traveldeal
has_many :groups
has_many :users, :through => :groups
end
group.rb
class Group < ActiveRecord::Base
belongs_to :trip
belongs_to :user
end
First of all, you have has_many :trips called twice in your User model. I understand you have two different types of User-Trip relationships (one directly, and one through Group), but you can't give both the same name, otherwise one will hide the other. Try defining your User model like this:
class User < ActiveRecord::Base
has_many :group_trips, :through => :groups,
:class_name => "Trip"
has_many :trips, :dependent => :destroy
has_many :groups
def all_trips
Trip.joins(:groups).where({:user_id => self.id} | {:groups => {:user_id => self.id}})
end
end
There's also the problem that you're searching the friend's list of groups for a Trip object. Try changing that line to:
<% if !friend.all_trips.include?(#trip) %>
Or without the new method, something like this should work:
<% if !friend.groups.where(:trip_id => #trip.id).first %>
I don't see anything un-RESTful about your approach. RESTful in general means stateless. I.e. the only thing a response depends on is the HTTP method and the address. So as long as your not keeping state information in, say, the session, you should be following REST.

route or applogic

trying to get rails routing to "click" and just not getting it
have a project and a task model:
class Task
include Mongoid::Document
field :title, :type => String
has_many :projects
belongs_to :user
end
class Project
include Mongoid::Document
field :title, :type => String
has_and_belongs_to_many :tasks
belongs_to :user
end
I want to "associate" a task with a project
so I have this in the project controller:
def connect
#project = Project.find(params[:id])
#project.tasks_ids.push(params[:task_id])
#project.save
redirect_to project
end
with this route:
resources :projects do
match 'connect/:id' => 'projects#connect', :as => :connect, :via => :put
resources :tasks
end
I cant seem to get this to work in the view:
= link_to 'Associate Task', project_connect_path(#task)
fails with:
No route matches {:controller=>"projects", :action=>"connect"}
resources :projects do
member do
put :connect
end
resources :tasks
end
Your path should look like this:
= link_to 'Associate Task', project_connect_path(#project, :task_id => #task.id), :method => :put
Try to do this:
resources :projects do
member do
put 'connect'
end
resources :tasks
end
or you can write your route above the resources :projects do ...
and here is the link, you can read more about routes there: Rails routes

Resources