Search using like query in Ruby on Rails - ruby-on-rails

I am a beginner in ROR development. I have navigation and I want search box on every page and when I enter some keyword, it should do like query across table's field. I tried using some online tutorials but could not do it.
My table name : tutorials
here is my search form on navigation bar
<li><%= link_to 'Login', :controller => 'access', :action => 'login' %></li>
<li><%= link_to 'Sign Up', :controller => 'users', :action => 'new' %></li>
<li><%= link_to 'Logout', :controller => 'access', :action => 'logout' %></li>
<div align="right">
<%= form_tag("/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
</div>
Here is my controller
class SearchController < ApplicationController
def show
#tutorial = Tutorial.find(params[:q])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #tutorial }
end
end
end
Here is my model
class Search < ActiveRecord::Base
def #tutorial.search(search)
if search
find(:all, :conditions => ['tutorial_name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
end
I am not sure how to do this. Please help

It's often true that a bad name indicates wrong thinking. I believe your name Search for the model is in this category. It should probably be called Tutorial, no? Search is something you do to a model, not the model itself.
If this guesswork is correct and the model is now called Tutorial and it has a field called name that is a string, then your model will be
class Tutorial < ActiveRecord::Base
def self.search(pattern)
if pattern.blank? # blank? covers both nil and empty string
all
else
where('name LIKE ?', "%#{pattern}%")
end
end
end
This makes the model "smart" on how to search through tutorial names: Tutorial.search('foo') will now return all tutorial records that have foo in their names.
So we can create a controller that uses this new functionality:
class SearchController < ApplicationController
def show
#tutorials = Tutorial.search(params[:q])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #tutorial }
end
end
end
The corresponding view must display the tutorials. Yours doesn't. The simplest way to do this is write a partial that renders exactly one tutorial. Say it's called _tutorial.html.erb.
Then in the view for Search, you need to add
<%= render :partial => #tutorials %>
to actually display the search results.
Addition
I'll build a little example.
# Make a new rails app called learning_system
rails new learning_system
# Make a new scaffold for a Tutorial model.
rails g scaffold Tutorial name:string description:text
# Now edit app/models/tutorial.rb to add the def above.
# Build tables for the model.
rake db:migrate
rails s # start the web server
# Now hit http://0.0.0.0:3000/tutorials with a browser to create some records.
<cntrl-C> to kill the web server
mkdir app/views/shared
gedit app/views/shared/_search_box.html.erb
# Edit this file to contain just the <%= form_tag you have above.
# Now add a header at the top of any view you like, e.g.
# at the top of app/views/tutorials/index.html.erb as below
# (or you could use the layout to put it on all pages):
<h1>Listing tutorials</h1>
<%= render :partial => 'shared/search_box' %>
# Make a controller and view template for searches
rails g controller search show
# Edit config/routes.rb to the route you want: get "search" => 'search#show'
# Verify routes:
rake routes
search GET /search/:id(.:format) search#show
tutorials GET /tutorials(.:format) tutorials#index
POST /tutorials(.:format) tutorials#create
new_tutorial GET /tutorials/new(.:format) tutorials#new
edit_tutorial GET /tutorials/:id/edit(.:format) tutorials#edit
tutorial GET /tutorials/:id(.:format) tutorials#show
PUT /tutorials/:id(.:format) tutorials#update
DELETE /tutorials/:id(.:format) tutorials#destroy
# Edit app/controllers/search_controller.rb as above.
# Create app/views/tutorial/_tutorial.html.erb with following content:
<tr>
<td><%= tutorial.name %></td>
<td><%= tutorial.description %></td>
</tr>
# Edit app/views/search/show.html.erb to have following content:
<h1>Show Search Results</h1>
<table>
<%= render :partial => #tutorials %>
</table>
Now try a little test. Fill in a search criterion and press the Search button.

On Rails 6 you can use for search action implementation in the controller.rb:
def search
keyword = params[:q] #you can get this params from the value of the search form input
#posts = Post.where("title LIKE ?", "%#{keyword}%")
end

Related

Rails form_tag to show action

I have a Rails app where I have a partial that I want a dropdown in so when a user is selected it will do a get method to the urls /timecards/:user_id which will be the controller's show method passing in the User's id field. I'm having a hard time with my paths in the form_tag and could use some help.
Here is my partial in the view:
<%= form_tag timecard_path, :method => :get do %>
<%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username) %>
<%= submit_tag "View Employee", class: "btn btn-primary" %>
<% end %>
From rake routes I get the following output:
timecards GET /timecards(.:format) timecards#index
POST /timecards(.:format) timecards#create
new_timecard GET /timecards/new(.:format) timecards#new
edit_timecard GET /timecards/:id/edit(.:format) timecards#edit
timecard GET /timecards/:id(.:format) timecards#show
PUT /timecards/:id(.:format) timecards#update
DELETE /timecards/:id(.:format) timecards#destroy
Here is my controller: timecards_controller.rb
class TimecardsController < ApplicationController
before_filter :disallow_clients, :disallow_medics, :disallow_employee, :disallow_supervisor
def index
#clock_events = ClockEvent.includes(:user).search(params[:search])
respond_to do |format|
format.html do
#clock_events = #clock_events.paginate(:per_page => params[:per_page] || 20, :page => params[:page]).order('users.username asc').order('clock_in desc')
end
format.csv { send_data ClockEvent.to_csv(#clock_events.order('users.username asc').order('clock_in desc')) }
end
end
def new
#clock_event = ClockEvent.new
end
def create
parse_times!
#clock_event = ClockEvent.new(params[:clock_event])
if #clock_event.save
redirect_to timecard_path(#clock_event.user.id), notice: "Entry added for #{#clock_event.user.username}".html_safe
else
render :new, notice: "Time Card Entry failed to Save".html_safe
end
end
def show
#user = User.find(params[:id])
#clock_events = #user.clock_events.search(params[:search])
respond_to do |format|
format.html do
#clock_events = #clock_events.paginate(:per_page => params[:per_page] || 5, :page => params[:page]).order('clock_in DESC')
end
format.csv { send_data ClockEvent.to_csv(#clock_events.order('clock_in desc')) }
format.pdf do
pdf = TimeCardPdf.new(#clock_events, #user)
send_data pdf.render, filename: "timecard-#{#user.username}",
type: "application/pdf",
disposition: "inline"
end
end
end
def edit
#user = User.find(params[:id])
#clock_events = #user.clock_events.search(params[:search]).order("clock_in ASC").paginate(:per_page => 10, :page => params[:page])
end
def update
parse_times!
#clock_event = ClockEvent.find(params[:clock_event][:id])
if #clock_event.update_attributes(params[:clock_event])
redirect_to edit_timecard_path(#clock_event.user.id), notice: "Updated Successfully".html_safe
else
redirect_to :back, notice: "Woops.".html_safe
end
end
private
def parse_times!
params[:clock_event].parse_time_select! :clock_in if params[:clock_event].has_key? 'clock_in(5i)'
params[:clock_event].parse_time_select! :clock_out if params[:clock_event].has_key? 'clock_out(5i)'
end
end
So I believe I'm calling the path properly in the form_tag but when I load the page I get the error: No route matches {:action=>"show", :controller=>"timecards"} Even though there is a show action in the timecards_controller.
I think something I have to set the form_tag for an explicit url and somehow pass in the :id of the user in the params. But I'm a bit stuck on how to do this.
So to summarize. When I have the dropdown, I select a user, click "View Employee", and that should go to the show action in timecards_controller.rb with the url of /timecards/3 (as an example). I've never used form_tag in this way before so passing a path or explicit url is a bit foreign to me.
A simple hotfix:
The simplest possible fix would be to change the form to a bunch of links.
<%= User.employee.order("username ASC").each |u| %>
<%= link_to u.username, timecard_path %>
<% end %>
Otherwise you could use Javascript to simply make the form redirect:
<%= form_tag timecodes_path, :method => :get, :id => 'timecode_employee' do %>
<%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username) %>
<%= submit_tag "View Employee", class: "btn btn-primary" %>
<% end %>
$("#timecode_employee").submit(function(e){
var form = $(this);
// redirect to timecards/:id
window.location = form.attr('action') + form.find('select').val();
e.preventDefault();
});
Tips for redesign
Your design could be radically improved by adding an underlying TimeCard model.
Here is one very common case which tells you why:
The client decides that they want to have managers sign off on time
cards every month.
Oh, drat. Now we need get all the ClockEvents in that scope and update a 'clock_events.state' on each.
But then the client also wants to know who has signed off the card. So you add a clock_events.signed_off_by_id and update all the clockevents. And then they want three managers to sign off, etc.
An alternative design
Note that this is a opinionated generic example.
class ClockEvent < ActiveRecord::Base
enum status: [:clocked_in, :clocked_out]
has_many :users
belongs_to :time_card
end
class TimeCard < ActiveRecord::Base
belongs_to :user
has_many :clock_events
accepts_nested_attributes_for :clock_events
end
class User < ActiveRecord::Base
has_many :time_cards
has_many :clock_events, through: :time_cards
end
A TimeCard might be automatically issued per month or if you never want to change than just stick with one TimeCard per user.
Lets go with some traditional routes here:
resources :time_cards
end
resources :clock_events do
end
resources :users, shallow: true do
resources :clock_events do
end
resources :time_cards do
end
end
Now imagine we had a classical punch card time clock.
We would punch in with:
POST /clock_events { user_id: 1, time_card_id: 5 }
And punch out with:
PATCH /clock_events/1 { status: :clocked_out }
Thats REST for you.
Nesto Presto
We have nested routes for time cards and clock_events per user:
GET /users/1/time_cards
GET /users/1/clock_events
From the we can choose to either build a dedicated UserTimeCardController or we can scope by the user id param in TimeCardsController.
class TimeCardsController
def index
#time_cards = TimeCard.all
#time_cards = #time_cards.where(user: params[:user_id]) if params[:user_id]
#users = scope.all
end
end
Filtering
But imagine if we want managers be be able filter the number of employees he sees on the index - a good architecture would look something like this:
class TimeCardsController
def index
#time_cards = TimeCard.all
#time_cards = #time_cards.where(user: params[:user_id]) if params[:user_id]
if params[:filters]
#time_cards = #time_cards.search(params[:search])
end
end
end
And on our index page we would add a form like this:
<%= form_tag(time_cards_path, method: :get) %>
<%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username), multiple: true %>
<%= submit_tag "Filter", class: "btn btn-primary" %>
<% end %>
The route that you have defined:
timecard GET /timecards/:id(.:format) timecards#show
It requires an id to show the correct timecard. But when you are invoking it in form_tag, you are just sending, timecard_path with no id. So you do need to send an id or a timecard object, and Rails will automatically extract the id out of it.
So, that should be:
form_tag #timecard do
# other code
end
#timecard must be instantiated in the action that renders your partial, and it must be a valid TimeCard object.
I'm definitely going to take #maxcal's advice and rewrite this thing, but since I had to ship fast I came up with an ugly UI hack that "works". I don't like it but it does exactly what I need it to do.
<ul class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<button class="btn btn-medium btn-primary">View Employees</button>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<% #users.each do |u| %>
<li><%= link_to "#{u.username}", timecard_path(u) %>
<% end %>
</ul>
</ul>
I don't like all the loading and this is really dirty, but in this instance since I was breaking all sorts of Rails conventions and had to ship this afternoon it'll work until I can come back and make it better.
Let the laughs begin. :)

GET errors in ruby on rails. Controllers and actions. CRUD

I'm working through the "Ruby on rails essential 3 training" on lynda.com. I'm at the point of creating forms and implementing CRUD. So far whenever I try to have rails initiate an action it can never find the definition.
To solve this problem I define the action within the controller and then create a GET within my routes.rb file. My instructor doesn't have to do this and his server starts just fine. I have a feeling I'm doing something wrong because my routes.rb file has far to many GET commands.
Please look at the following folders and let me know if you have an idea of why I have to make a GET every time I try a new action. I heard that when creating a Model there is supposed to be a GET created at the same time but that isn't happening and I'm not sure if I'm understanding that correctly. My subjects controller:
class SubjectsController < ApplicationController
def index
list
render('list')
end
# def index
# show
# render('show')
# end
def list
#subjects = Subject.order("subjects.position ASC")
end
def show
#subject = Subject.find(params[:id])
end
def new
#subject = Subject.new
end
def create
end
end
My new.html.erb file:
<%= link_to("<< Back to List", {:action => 'list'}, :class => 'back- link') %>
<div class="subject new">
<h2>Create Subject</h2>
<%= form_for(:subject, :url => {:action => 'create'}) do |f| %>
<table summary="Subject form fields">
<tr>
<th>Name</th>
<td><%= f.text_field(:name) %></td>
</tr>
<tr>
<th>Position</th>
<td><%= f.text_field(:position) %></td>
</tr>
<tr>
<th>Visible</th>
<td><%= f.text_field(:visible) %></td>
</tr>
</table>
<div class="form-buttons">
<%= submit_tag("Create Subject") %>
</div>
<% end %>
My config/routes.rb
Rails.application.routes.draw do
root :to=>"demo#index"
get 'demo/index'
get 'demo/hello'
get 'demo/other_hello'
get 'subjects/list'
get 'subjects/show'
get 'subjects/new'
get 'subjects/create'
Now having an issue with the Create portion of Crud when trying to create a test subject. Here is my SubjectsController.rb
class SubjectsController < ApplicationController
def index
list
render('list')
end
# def index
# show
# render('show')
# end
def list
#subject = Subject.order("subjects.position ASC")
end
def show
#subject = Subject.find(params[:id])
end
def new
#subject = Subject.new
end
def create
#subject = Subject.new(params[:subject])
if #subject.save
redirect_to(:action => 'list')
else
render('new')
end
end
end
Yes, you do not need to specify GET for CRUD actions. You can use Rails Resource Routing. For example, your subjects controller actions can be called in routes.rb like so:
resources: subjects
This will automatically route all the generic CRUD actions, and look for them in your controller. You can see whether rails "picked up" your routing by typing rake routes in the command line. You can also limit the number of actions you want by passing resources: subjects, only:[:destroy]. This tells Rails to only use the subjects#destroy action. There is more to it. I suggest reading through this RailsGuide for more information
Here's what your config/routes.rb should look like:
root 'subjects#index'
resources :subjects
get 'subjects/list' => 'subjects#list'
Normally, you have to create routes by yourself. They are not created with a model. Also, they are rather related to the controller than to the model.
I recommend to have a look at the official Rails routing guide here. Usually something like subjects/create is not a GET but a POST route. Your instructor might also have used a resources :subjects route instead of multiple GET/POST/etc. routes.
Slight variant of #OhHendrie's answer using only the resources dsl
root 'subjects#index'
resources :subjects do
get 'list', on: :collection
end

Link or button to clear the database in Rails

I'm trying to figure out how to allow a user to click on a link or button on the index page to clear out all of the objects from the app's database, and then redirect to a newly cleared index page. So, with the example model Article, I expect it should have something to do with an Article.destroy_all method, and I'm expecting it would be a simple solution, but I've tried some variations and am just not sure of how to actually implement it.
So it would be another action in your controller. If we're dealing with Articles then the controller would be:
class ArticlesController < ApplicationController
def indef
#articles = Article.all
end
def destroy_them_all
Article.destroy_all
redirect_to articles_path
end
end
And in the view where you want the user to click on a button to destroy all articles:
<%= link_to 'Destroy them all', destroy_them_all_path, method: :delete, confirm: 'Are you crazy?' %>
Don't forget to add a named route in your routes file:
match '/articles/destroy_them_all', to: 'Articles#destroy_them_all', via: :delete
That should work. Though you might have to check rake routes to make sure I got the destroy_them_all_path correct.
try this:
Article controller:
def destroy_all
#Articles = Article.all
#Articles.each do |a|
a.destroy
end
redirect_to articless_path, notice: "Delted"
end
routes:
post "articles/destroy_all"
view:
<%= form_tag ({ :controller => 'articles', :action => 'destroy_all' }) do%>
<%= submit_tag 'destroy all'%>
<% end %>

undefined local variable or method 'gallery'

Okay, so I'm rebuilding an old photo gallery management app that I built a few years back in ROR 1 and running in to a few coding difficulties, since I'm rusty and apparently things have changed. My first question is I'm getting an undefined local variable or method 'gallery' error when trying to call a page. What I'm confused about is that I have the method defined in the 'gallery' controller but I'm wondering if I'm completely missing something. Here are some relevant code snippets, first is my index.html.erb page:
<% #photos.each do |photo| %>
<div>
<%= image_tag(photo.thumb_url) %>
<%= link_to 'Show', gallery %><br/>
</div>
<% end %>
<p><%= will_paginate #photos %></p>
my gallery controller:
class GalleryController < ApplicationController
skip_before_filter :authorize
# GET /gallery
# GET /gallery.xml
def index
#photos = Photo.all
#photos = Photo.paginate :page=>params[:page], :order=>'date desc',
:per_page => 2
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #gallery }
end
end
# GET /gallery/1
# GET /gallery/1.xml
def show
#photo = Photo.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #photo }
end
end
end
And my error:
undefined local variable or method `gallery' for #<#<Class:0x111127b38>:0x1112d5700>
I should clarify that "Photos" is the admin section which requires login and contains all of the fields/database/record data. I have no problem using the following line:
<%= link_to 'Show', photo %><br/>
Which brings up the correct record and viewing page, but in the admin section of the site (which requires login). Hopefully that makes sense.
Nowhere do I see you defining the variable gallery, and this is what your error message is telling you: gallery is undefined in your view.
Update RE your comments:
Just because you want the photo to go to the gallery controller doesn't mean you can just type "gallery" and expect results. This is a programming langauge, where words have meanings, and all you're doing is referencing an undefined variable. This, and nothing like this, has ever worked in any version of Rails.
If you want to route your photo to the Gallery controller, you can use the automagically generated _path helpers; specifically, gallery_path, which accepts an argument for the id of the "gallery" (really a photo) to show:
<%= link_to 'Show', gallery_path(photo.id) %><br/>
Try replacing your link_to method with something like: <%= link_to 'Show', :controller => "photos", :action => :your_method, :params1 => gallery %>.
Then in your PhotoController you can use: #my_gallery = params[:params1] to access your gallery item.
Some documentation on routes:
http://guides.rubyonrails.org/routing.html

Ruby on rails tags [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Adding tags to posts in Ruby on Rails
I have a simple yet tricky (for me at least) question... what would be the best way to create tags in my sample application blog?
I am adding tags using act-as-taggable, but can I make them clickable so that when people click on it, all posts with that tag would be shown?
I can't quite get it O___o
Any help is super appreciated!
Here is what i did so far:
in my posts controller
def tagged
#posts = Post.all(:order => 'created_at DESC')
#tags = Post.tag_counts_on(:tags)
#tagged_posts = Post.tagged_with(params[:tags])
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #posts }
end
end
then in my posts/show view
<% unless #post.tags.empty? %>
<div class="category">Category:
<% #post.tags.each do |t| %>
<%= link_to t.name, {:tag => t.name, :action => "tagged", :controller => 'posts'} %>
<% end %>
in my posts/tagged view
<% #tagged_posts.each do |post| %>
<div class="entry">
<h2><%= link_to post.title, post %></h2>
<div class="content"><%= sanitize blog_truncate(post.content, :words => 100),:tags => %w(strong, b, a) %><br /><%= link_to "[read more]", post %>
</div>
</div>
<% end %>
I kind of loosely followed this guide:
http://g-p.si/posts/tagging-with-acts-as-taggable-on
my issue is that the tag is clickable on my posts/show page, I get redirected to my tagged page and the url looks like mysite/tagged?tag=ruby
But my tagged page is blank...
Each link for the tag that the user clicks on should have a href of something like:
/posts?tag=my_tag_name
And then in the posts controller
class PostsController
def index
if params[:tag].present?
#posts = Post.where(tag: params[:tag])
else
#posts = Post.all
end
end
end
Note this code is not tested and I've never used acts as taggable so you should first make sure how to query for tagged posts.
It's almost right, thanks for the hint! you have to use
#posts = Post.tagged_with(params[:tag])
instead of
#posts = Post.where(tag: params[:tag])
and it works like magic! :)

Resources