when search features works it causes error on nesting routes - ruby-on-rails

I'm trying to implement a search with on an index page.
However it's affecting the nested route of a single entry when I add the code. [I tried gems, using elsif, and anything else I could think of].
For context, I'm using books belongs to authors scenario.
I'd like to implement this:
#comicbooks = Comicbook.search(params[:search])
into here:
def index
#comicbooks = #comicbooks.filter_by_name(params[:name])
if params[:person_id]
person = Person.find_by(id: params[:person_id])
#comicbooks = person.comicbooks
else
#comicbooks = Comicbook.all
end
end
Here is my model:
class Comicbook < ApplicationRecord
belongs_to :person
belongs_to :squad
validates :title, presence: true, uniqueness: { scope: :person }
def self.search(search)
if search
find(:all, :conditions => ['name Like ?', "%#{:search}%"])
else
find(:all)
end
end
def person_attributes=(args)
self.person = Person.find_or_create_by(args)
end
def squad_attributes=(args)
self.squad = Squad.find_or_create_by(args)
end
end
Here is the search form on the view
<%= form_tag comicbooks_path, :method => :get do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search" , :name => nil %>
<% end %>
Whats the best way to implement search without throwing off the other routes? I've tried many gems, but they've complicated things more than I can understand.
Thanks in advance.

You can use scope in your model and chain in your controller
Model:
scope :search, -> (search) do
return if search.blank?
where('name Like ?', "%#{:search}%")
end
Controller:
#comicbooks = Comicbook.filter_by_name(params[:name]).search(params[:search])

Related

Use all strings in an array to search through a has_many association

Im building a search field for my profile model, the search field takes in a list of skills separated by comas and find the profiles that have those skills. The code splits up the string by comas into multiple strings in an array. Now I am trying to find profiles that have all the skills in that array but I can't seem to wrap my head around it. I've tried playing around with PSQL code in where() but I can only seem to get it to work with 1 of the skills.
I am following the advanced search guide by ryan bates.
Profile.rb
class Profile < ApplicationRecord
belongs_to :user, dependent: :destroy
has_many :educations
has_many :experiences
has_many :skills
has_many :awards
has_many :publications
def owner? user
if self.user == user
return true
else
return false
end
end
end
business_search.rb
class BusinessSearch < ApplicationRecord
def profiles
#profiles ||= find_profiles
end
private
def find_profiles
profiles = Profile.all
if self.skills.present?
profiles = profiles.joins(:skills)
skills_array(self.skills).each do |skill|
profiles = profiles.where('skills.name_en = :q or skills.name_ar = :q', q: skill)
end
end
profiles
end
def skills_array(skills)
return skills.split(/,/)
end
end
business_search_controller.rb
class BusinessSearchesController < ApplicationController
def new
#search = BusinessSearch.new
end
def create
#search = BusinessSearch.create!(search_params)
redirect_to #search
end
def show
search = BusinessSearch.find(params[:id])
#profiles = search.profiles
end
private
def search_params
params.require(:business_search).permit(:first_name, :education_field, :skills)
end
end
Adavanced search view
<h1>Advanced Search</h1>
<%= form_for #search do |f| %>
<div class="field">
<%= f.label :skills %><br />
<%= f.text_field :skills %>
</div>
<div class="actions"><%= f.submit "Search" %></div>
<% end %>
Try this query:
def find_profiles
return Profile.all if skills.blank?
Profile.joins(:skills)
.where('skills.name_en IN (:skills) OR skills.name_ar IN (:skills)', skills: skills_array(skills))
.group('profiles.id')
.having('COUNT(skills.id) = ?', skills_array(skills).size)
end
Try this one:
def find_profiles
return Profile.all if skills.blank?
Profile.joins(:skills).where('skills.name_en IN (:skills) OR skills.name_ar IN (:skills)', skills: skills_array(skills))
end

ruby on rails - Ordering two models

I have two models image.rb and story.rb
I am trying to order them together.
stories_controller.rb looks like this:
def index
#stories = Story.all.order(:cached_votes_total => :desc)
#images = Image.all.order(:cached_votes_total => :desc)
#combined = (#stories + #images).sort_by {|record| record.created_at}
end
private
def story_params
params.require(:story).permit(:title, :content, :category)
end
images_controller.rb looks like this:
private
def image_params
params.require(:image).permit(:title, :image, :image_file_name, :category)
end
In my index.html.erb im tryign to order them both but i run into undefined method errors because they have different parameters.
<% #combined.each do |s| %>
...
<% end %>
is there a way to fix this?
This is an wrong approach, combining two models is not recommended, you should use model association for this.. For example
# Image Model
class Image < ActiveRecord::Base
belongs_to :story
end
# Image Model
class Story < ActiveRecord::Base
has_one :image
end
In the controller..
def index
#stories = Story.find(:all, :include => :image).order(:cached_votes_total => :desc)
end
and finally in the view
<% #stories.each do |story| %>
# here you can access story and story.image
...
<% end %>

Wrong posts showing up in other board

I current have my project set up like this:
resources :boards, :path => '' do
resources :posts, :path => 'thread' do
resources :replies
On /board1/ only posts from board1 show, same for board2. In /board1/thread/1/ it shows post 1 and the replies to it.
However in /board2/thread/1/ the post that is showing is from board1/thread/1/, and in the reverse board1/thread/2/ shows the post from board2/thread/2/.
Each post has a related board_id in the db, and each reply has the related post_id in the db.
How can I keep these separate?
class Board < ActiveRecord::Base
has_many :posts
has_many :replies, through: :posts
include FriendlyId
friendly_id :name, use: :slugged
accepts_nested_attributes_for :posts, :replies
end
class Post < ActiveRecord::Base
belongs_to :board
has_many :replies, dependent: :destroy
accepts_nested_attributes_for :replies
include FriendlyId
friendly_id :pid, use: :slugged
after_create :set_pid
def set_pid
post_max = self.board.posts.maximum(:pid)
reply_max = self.board.replies.maximum(:pid)
if post_max.to_i < reply_max.to_i
self.update_attributes(:pid => reply_max.to_i + 1)
else
self.update_attributes(:pid => post_max.to_i + 1)
end
end
end
Code to display post in /:board_id/show:
<% #board.posts.find_each do |post| %>
<%= post.subject %>
<%= post.name %>
<%= post.email %>
<%= post.created_at %>
No.<%= post.pid %>
<%= link_to "[reply]", board_posts_path(#board, #post)%>
<br>
<%= post.comment %><br><br>
<%= render "replies/replies" %>
<% end %>
Code to display post in /:board_id/thread/:id:
<p>
<%= #post.subject %>
<%= #post.name %>
<%= #post.email %>
<%= #post.created_at %>
No.<%= #post.pid %>
<br>
<%= #post.comment %>
</p>
Edit:
class RepliesController < ApplicationController
def create
#board = Board.friendly.find(params[:board_id])
#post = Post.friendly.find(params[:post_id])
#reply = #post.replies.create(reply_params)
redirect_to #board
end
private
def reply_params
params.require(:reply).permit(:name, :email, :subject, :comment, :pid)
end
end
class PostsController < ApplicationController
def show
#boards = Board.all
#replies = Reply.all
#post = Post.friendly.find(params[:id])
end
def create
#board = Board.friendly.find(params[:board_id])
#post = #board.posts.create(post_params)
if #post.save
redirect_to #board
else render #board
end
end
private
def post_params
params.require(:post).permit(:name, :email, :subject, :comment, :pid)
end
end
The missing part here is the RepliesController which is the source of the problem if I got the question correctly.
Most probably you have there something like #replies = current_post.replies which fetch all replies of the given post regardless of the current board. Scoping post by board will solve the problem:
current_post = Post.find_by(board_id: params[:board_id], id: params[:post_id])
if current_post
#replies = current_post.replies
end
On your friendly_id declaration in the Post model, you don't have the pid as globally unique. Use this form of friendly_id, instead:
friendly_id :pid, use: :scoped, scope: :board
In this way, duplicate friendly_id values for pid are kept separate by the board that they belong to. This is necessary for slugging nested resources properly. The :scoped value says that it's for nested (scoped) models, and the scope: key indicates that posts is nested within boards. Note that you may have to do this with replies, as well.
You'll also want to make sure that your indexes for your :slug are correct. Typically when the :scope is incorrect, you'll find it when you try to save the record. In this case, it looks like the indexes might not be set correctly to ensure the uniqueness of the board name/post pid combination. Check out Friendly ID 4 Using scoped module for more information.
When you have the indexes sorted out, you'll find that inserting new records will require you to have the friendly_id (based on your pid) already assigned. You may also want to look into using slug candidates to dynamically generate the proper slug at creation time. Also check out slug candidates rails 4 for some usage information.

I want to acts_as_taggable_on :topics and :places in Ruby on Rails

I have a question model in my rails app. A user can add questions. When adding a question a user can assign it topics and a place.
I followed the railscast on how to set up acts_as_taggable_on for tags, in my case topics.
However, I now want to add the place feature. I had a go myself and had a google around but couldn't find much on having 2 acts_as_taggable items.
If someone could advise me on how to setup a second acts_as_taggable feature on the same acts_as_taggable that would be great
My code so far:
Question.rb
acts_as_taggable
acts_as_taggable_on :topics, :places
questions_controller.rb
def topics
if params[:topic].present?
#questions = Question.tagged_with(params[:topic])
else
#questions = Question.postall
end
end
questions/show.html.erb
<p>Topics: <%= raw #question.tag_list.map { |t| link_to t, topic_path(t) }.join(', ') %></p>
form.html.erb
<%= f.input :tag_list, placeholder: "Topics" %>
you have done it right in model..
just add some code to controller and view
def topics
if params[:topic].present?
#questions = Question.tagged_with([params[:topic]], :on => :topics, :any => true)
elsif params[:place].present?
#questions = Question.tagged_with([params[:place]], :on => :places, :any => true)
else
#questions = Question.postall
end

Tire - sort by number of related object

I have something like this in my model:
class User < ActiveRecord::Model
has_many :followers, :through => :as_followed_followings, :class_name => "User", :foreign_key => "follower_id", :uniq => true
...
def self.search(params)
tire.search(load: true, page: params[:page], per_page: params[:per]) do
end
end
end
I would like the return an array of users ordered by the count of followers that user has.
So what is the correct way to define mappings and indexes to search through nested object with count property ?
Thanks for reading.
I have solved this problem by creating another field on users table and index that value instead of performing nested search in runtime.
This may not be the best solution but I have deadline. Any better solution is welcome :)
Trying with ...
AsFollowedFollowing.count(:group => :follower_id, :order => "count_all DESC")
This is Ordered Hash that contains user_id => count
If you want to loop this hash, try following
# in controller
#counts = AsFollowedFollowing.count(:group => :follower_id, :order => "count_all DESC")
#users = User.where(:id => #counts.map{|k,_| k})
#in view
<% #counts.each do |k, v| %>
<% user = #users.find{|u| u.id == k} %>
<div><%= user.name %> | <%= v %></div>
<% end %>

Resources