I'm working with Ruby on Rails, trying to get my search bar to display the results. I've tried searching for similar issues, but most already had search working and weren't getting it to work right.
in my html I have:
<%= form_tag users_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<div class="scrollBox">
<%= will_paginate%>
<ul>
<% #users.each do |user| %>
<li>
<%= link_to user.name, user, {:class=>"signout-style"} %>
</li>
<% end %>
</ul>
<%= will_paginate %>
</div>
In my users controller I have:
def index
#users = User.paginate(page: params[:page])
#searches = User.search(params[:search])
end
In my users model I have:
def self.search(search)
if search
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
I was using the railscast episode #37
Any help is appreciated!
Update the index action as below:
def index
#users = User.search(params[:search]).paginate(page: params[:page])
end
Currently, you are storing the search results in instance variable #searches with this line:
#searches = User.search(params[:search])
BUT in your view you are accessing #users which is set as #users = User.paginate(page: params[:page]). So, you always see all the users in the view and not the searched users.
UPDATE
As you are using Rails 4.0.2, I would suggest you to refactor your search method as below:
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
all
end
end
Related
I’m completely new to ruby on rails, I’m creating a simple article search application that would use the Guardian API and just display the news titles. It just needs to work like this: a user enters the page, fills in the search form and views the news titles.
I want to simply select the request result’s ‘webTitle’ keys and display their values as list items, but I get a big chunk of data and I’m not sure how can I do that.
Here is the request result:
{"response"=>{"status"=>"ok", "userTier"=>"developer", "total"=>2153270, "startIndex"=>1, "pageSize"=>10, "currentPage"=>1, "pages"=>215327, "orderBy"=>"relevance", "results"=>[{"id"=>"books/2017/jul/16/fall-down-7-times-get-up-8-naoki-higashida-review-autism", "type"=>"article", "sectionId"=>"books", "sectionName"=>"Books", "webPublicationDate"=>"2017-07-16T06:00:13Z", "webTitle"=>"Fall Down 7 Times Get Up 8 review – a window on the world of autism", "webUrl"=>"https://www.theguardian.com/books/2017/jul/16/fall-down-7-times-get-up-8-naoki-higashida-review-autism", "apiUrl"=>"https://content.guardianapis.com/books/2017/jul/16/fall-down-7-times-get-up-8-naoki-higashida-review-autism", "isHosted"=>false, "pillarId"=>"pillar/arts", "pillarName"=>"Arts"}, {"id"=>"football/2017/jul/07/gold-cup-2017-predictions-usa-mexico-costa-rica-football", "type"=>"article", "sectionId"=>"football", "sectionName"=>"Football", "webPublicationDate"=>"2017-07-07T09:00:08Z", "webTitle"=>"Gold Cup picks: USA to tip under-strength Mexico and in-form Costa Rica", "webUrl"=>"https://www.theguardian.com/football/2017/jul/07/gold-cup-2017-predictions-usa-mexico-costa-rica-football", "apiUrl"=>"https://content.guardianapis.com/football/2017/jul/07/gold-cup-2017-predictions-usa-mexico-costa-rica-football", "isHosted"=>false, "pillarId"=>"pillar/sport", "pillarName"=>"Sport"}, {"id"=>"world/2017/jul/15/stream-of-floating-bodies-near-mosul-raises-fears-of-reprisals-by-iraqi-militias", "type"=>"article", "sectionId"=>"world", "sectionName"=>"World news", "webPublicationDate"=>"2017-07-15T08:00:01Z", "webTitle"=>"Stream of floating bodies near Mosul raises fears of reprisals by Iraqi militias", "webUrl"=>"https://www.theguardian.com/world/2017/jul/15/stream-of-floating-bodies-near-mosul-raises-fears-of-reprisals-by-iraqi-militias", "apiUrl"=>"https://content.guardianapis.com/world/2017/jul/15/stream-of-floating-bodies-near-mosul-raises-fears-of-reprisals-by-iraqi-militias", "isHosted"=>false, "pillarId"=>"pillar/news", "pillarName"=>"News"}]}}
API consumer class:
#app/clients/guardian_api_client.rb
class GuardianApiClient
include HTTParty
API_KEY = ENV['GUARDIAN_CONTENT_API_KEY']
BASE_URL ="https://content.guardianapis.com/search?"
API_PARTIAL_URL = "api-key=#{API_KEY}"
def query(q)
request = HTTParty.get(BASE_URL+"q=#{q}&""api-key=#{API_KEY}")
puts request
request
end
end
Controller:
class SearchController < ApplicationController
def search
#app = GuardianApiClient.new
#results = #app.query(params[:q])
end
end
View:
<%= form_with(url: '/search') do |f| %>
<%= f.text_field :q %>
<%= f.submit 'search' %>
<% end %>
<% if #results != nil %>
<ul>
<%= #results.each do |r| %>
<li><%= r["webTitle"] %></li>
<% end %>
</ul>
<% else %>
<p>No results yet</p>
<% end %>
Routes:
Rails.application.routes.draw do
get '/search' => 'search#search'
post '/search' => 'search#search'
end
The response is some JSON, so you need to learn how to map through it and get the results that you want.
To see the data more clearly try printing it with:
puts JSON.pretty_generate(#results)
in your controller, then see the output in your rails console.
Anyway, you have a few options:
Option 1: Likely you just need to drill down further into #results in your view. In the JSON that is returned, the webTitles are nested, so changing the third line below should work. Also note on that line that I removed the = sign to prevent the return value from being displayed.
<% if #results != nil %>
<ul>
<% #results["response"]["results"].each do |r| %>
<li><%= r["webTitle"] %></li>
<% end %>
</ul>
<% else %>
<p>No results yet</p>
<% end %>
Option 2: You may consider getting the list of articles in your controller, which I think was your original intent and also is probably more "rails" like:
class SearchController < ApplicationController
def search
#app = GuardianApiClient.new
#results = #app.query(params[:q])
#articles = #results["response"]["results"].map do |article|
article
end
end
end
In your view, then call render to a partial:
<%= render 'articles' %>
Then create a partial view called _articles.html.erb in whatever directory your other view is in, and then add some code to display each article:
<ul>
<% #articles.each do |article| %>
<li><%= article["webTitle"] %> <% link_to 'Link', article["webUrl"] %></li>
<% end %>
<ul>
By separating out each article that was returned in the #articles array, it will probably be easier for you to get other attributes as well in a more readable way. As you can see, above I included a link to the actual article.
My problem is similar to this question: Ruby on Rails - Data not saved. Index showing blank values
However the strong params match the answers and no data seems to come through.
If I use params.require(:banktransaction).permit(...) I get an error: param is missing or the value is empty
If i remove the require part, a row is added but with no values.
I went through the view and controller and checked my spelling, for the life of me I can't see what I have missed, can anyone point me in the right direction?
controller:
class BankAccountsController < ApplicationController
def delete
end
def destroy
end
def edit
end
def update
end
def index
#bankaccount = BankAccount.all
end
def show
end
def new
#banktransaction = BankAccount.new(:transactionDate => Time.now, :description => params[:description], :credit => params[:credit], :debit => params[:debit])
end
def create
#banktransaction = BankAccount.new(bank_account_params)
if #banktransaction.save
flash[:notice] = "transaction added successfully."
redirect_to(bank_accounts_path)
else
render('new')
end
end
private
def bank_account_params
params.require(:banktransaction).permit(:transactionDate,:description,:credit,:debit)
end
end
View:
<h1>BankAccounts#new</h1>
<p>Find me in app/views/bank_accounts/new.html.erb</p>
<div class="new transaction">
<h2>Create Transaction</h2>
<%= form_for(#banktransaction, :html => {:multipart =>true }) do |f| %>
<%= render(:partial =>'form', :locals=> {:f => f}) %>
<div class="form-buttons">
<%= f.submit("Create Transaction") %>
</div>
<% end %>
</div>
Form partial:
<%= f.label(:transactionDate) %>: <%= f.date_field(:transactionDate) %><br>
<%= f.label(:description) %>: <%= f.text_field(:description) %><br>
<%= f.label(:credit) %>: <%= f.number_field(:credit) %><br>
<%= f.label(:debit) %>: <%= f.number_field(:debit) %><br>
routes:
Rails.application.routes.draw do
resources :bank_accounts do
member do
get :delete
end
end
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
In short, you just permit wrong params key. It's bank_account and your bank_account_params should be
def bank_account_params
params.require(:bank_account).permit(:transactionDate,:description,:credit,:debit)
end
The Rails's form builder will build your params base on model name, not variable name.
Your new action assign a BankAccount instance
def new
#banktransaction = BankAccount.new(:transactionDate => Time.now, :description => params[:description], :credit => params[:credit], :debit => params[:debit])
end
so the form builder will use bank_account as the param key instead of variable name banktransaction
Im trying to get this loop to only render the reviews form for services which doesnt already have a review. I can't get it to function properly. Any ideas?
<% #services.each do |service| %>
<% if service == #booked && !#hasReview %>
<%= form_for(service, service.reviews.new) do |f| %>
<label>Create review for</label> <%= label_tag service.title %>
<div id="user_stars"></div>
<div class form-group>
<%= f.text_area :comment, rows: 3, class: "form-control" %>
</div>
<%= f.hidden_field :service_id, value: service.id %>
<div class="actions">
<%= f.submit "Create", class: "btn btn-primary" %>
</div>
<% end %>
<% end %>
<% end %>
The #booked and #hasReview actions are working correctly by themselves. So I guess Im setting it up wrongly with the IF
EDIT:
services_controller.rb
def show
#user = User.find(params[:id])
#services = #user.services
#booked = Booking.where("service_id = ? AND user_id = ?", #service.id, current_user_id).present? if current_user
#reviews = #service.reviews
#hasReview = #reviews.find_by(user_id: current_user_id) if current_user
end
reviews_controller.rb
def create
#review = current_user.reviews.create(review_params)
redirect_to request.referer
end
Your logic looks good, so I'm guessing your problem is with this part of your if statement:
if service == #booked
Judging from this line in your services_controller:
#booked = Booking.where("service_id = ? AND user_id = ?", #service.id, current_user_id).present? if current_user
it looks like the #booked instance variable is a boolean. So you're comparing a boolean to service, but it doesn't look like service is a boolean.
So you'll want to change the if statement so that you're comparing two equivalent types, and in your case you probably want to compare two boolean values.
I am using rails 4 and have a subject and comment models. Subject is a one to many relationship with comments. I want a simple page that can add comments to many subjects on the same page. So in my form I know how to submit a comment to create but I dont know how to find the right subject in my controller to add it to. Any advice?
class CommentsController < ApplicationController
def create
comment = Comment.create(comment_params)
if comment.save
# The line below is incorrect, I dont know what to do
Subject.find(params[:subject_id]).comments << comment
redirect_to(:controller => 'static_pages', action: 'home')
end
end
def new
end
private
def comment_params
params.require(:comment).permit(:text, :user_name)
end
end
StaticPages#home Find me in
app/views/static_pages/home.html.erb
<% #subjects.each do |subject| %>
<div class="subjects <%= cycle('odd', 'even') %>">
<h1><%= subject.name %></h1>
<h3><%= subject.description %></h3>
<% subject.comments.each do |comment|%>
<div class="comment">
<h4><%= comment.user_name%></h4>
<%= comment.text %>
</div>
<% end %>
<%= form_for(#comment) do |f| %>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
<%= f.label :text %>
<%= f.text_field :text %>
<%= f.submit('Create comment', subject_id: subject.id) %>
<% end %>
</div>
<% end %>
The simplest way would be to populate the subject_id attribute of your #comment form, like this:
<%= form_for(#comment) do |f| %>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
<%= f.label :text %>
<%= f.text_field :text %>
<%= f.hidden_field :subject_id, value: subject.id %>
<%= f.submit('Create comment', subject_id: subject.id) %>
<% end %>
This will populate the subject_id attribute of your new Comment object, which will essentially associate it through Rails' backend:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
def create
#comment = Comment.new comment_params
#comment.save
end
private
def comment_params
params.require(:comment).permit(:subject_id, :text, :user_name)
end
end
--
foreign_keys
This works because of the Rails / relational database foreign_keys structure
Every time you associate two objects with Rails, or another relational database system, you basically have a database column which links the two. This is called a foreign_key, and in your case, every Comment will have the subject_id foreign_key column, associating it with the relevant subject
So you may have many different forms using the same #comment variable - the trick is to populate the foreign_key for each one
inside one of my view pages I'm using old fashion way of presenting data but I have problem in converting a string like "User.country.name" to a query statement.
I'm using a loop like below :
#columns = ['id','email','name','country.name']
table = User.all
<% table.each do |row| %>
<% #columns.each do |field| %>
<%= row[field] %>
<% end %>
<% end %>
The table shows almost all data except for column "country.name". I can't use constantize for country.name it gives me error. Any solution ? Thanks
Your User doesn't have an attribute country.name, it has an association country and that association has an attribute name.
You could set up a delegate:
class User < ActiveRecord::Base
delegate :name, to: :country, prefix: true
# ...
end
This creates a method User#country_name, returning country.name.
Now you can adapt your loop, using public_send to call the methods: (I've changed the variable names to make it clearer)
#methods = [:id, :email, :name, :country_name]
records = User.all
<% records.each do |record| %>
<% #methods.each do |method| %>
<%= record.public_send(method) %>
<% end %>
<% end %>
#users = User.select(:id, :email).joins(:countries)
<% #users.each do |user| %>
<%= user.id %>
<%= user.email %>
<%= user.countries %>
<% end %>
http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-joins
Wish it helps