Implement a Search Bar for Your Rails App

Search bars are a great way to query through an API. Here’s how you can add one to your project:

1) Add the search form somewhere in your view.

This will pull the search term from your API into a Controller.

<%= form_tag :search, method: :get, class: "navbar-form" do %>
<div class="form-group">
<%= text_field_tag "search", nil, placeholder: "Search...", class: "form-control" %>
</div>
<%= submit_tag "Search", class: "btn btn-info" %>
<% end %>

2) Don’t forget about your test! You need these.

require 'rails_helper'
RSpec.describe ApiService do
context "returns a product" do
it "gets the product details" do
VCR.use_cassette("item_details") do
result = ApiService.new.data("camera")
expect(result["calories"]).to eq(115)
expect(result["totalWeight"]).to eq(223.0)
expect(result["dietLabels"]).to eq(["LOW_FAT", "LOW_SODIUM"])
end
end
end
end

2) In app/controllers/search_controller.rb

Notice the text_field_tag is “search”. This correlates with the params[:search] below.

class SearchController < ApplicationController
def index
@object = Object.new(params[:search])
end
end

Where is this string headed? It appends to your APIService.rb located in app/services/api_service.rb

2.5) Don’t forget about your routes.rb!

get ‘/search’, to: “search#index”

3) Add the search form somewhere in your view.

class ApiService
def initialize
@conn = Faraday.new("https://api.foobar.com/v1")
end
def data(search)
response = @conn.get do |req|
req.url 'items', :app_key => ENV["YOUR_APP_KEY"]
req.params['callback'] = "JSON_CALLBACK"
req.params['format'] = "json"
end
parse(response.body)
end
private
def parse(response)
JSON.parse(response)
end
end
# GET http://sushi.com/search?page=2&limit=100
conn.get do |req|                           
req.url '/search', :page => 2
req.params['limit'] = 100
end

2) In app/models/item.rb

Now you will instantiate the model with the params from the ApiService. Don’t forget about associations!

class Foobar < ActiveRecord::Base
def initialize(search)
data = ApiService.new.data(search)
@foo = data["bar"]
@foo2 = data["bar2"]
end
end

Don’t forget about your model tests.

require 'rails_helper'
describe Item do
context "item details" do
it "returns all details" do
VCR.use_cassette("item model") do
food = Item.new("camera")
expect(item.name).to eq(115)
expect(item.cost).to eq(223.0)
end
end
end
end

Now you can add some fancy queries to your Model, and then call them in your view. If it’s a list of objects, use the Ruby enumerable .map. Then you can easily call .each in your view:

def search_term
search = @items.map do |hash|
hash['text']
end
search.first
end
def cool_thing
labels = @items.each do |attr|
attr.gsub!('_', ' ')
end
labels
end
def list_of_things
list = @items.map do |k,v|
"#{v["label"]}: #{v["quantity"].round(2)} #{v["unit"]}"
end
list
end
def name
name = @items.last["foo"].map do |k,v|
k["fooBar"]
end
name.first
end
def things
nutrients = @items.last["foo"].map do |k,v|
k["bar"]
end
nutrients
end
def pretty_nested
nutrients = ingr_nutrients.map do |k,v|
k.values.map do |k,v|
"#{k["label"]} #{k["quantity"].round(2)} #{k["unit"]}"
end
end
nutrients.first
end
end

//SERVICE

class EdamamService

def initialize
 @conn = Faraday.new(“https://api.edamam.com/api")
 end

def data(search)
 response = @conn.get do |req|
 req.url ‘nutrition-data’, :app_key => ENV[“EDAMAM_APP_KEY”], :app_id => ENV[“EDAMAM_APP_ID”]
 req.params[‘ingr’] = “#{search}”
 end
 parse(response.body)
 end

private
 def parse(response)
 JSON.parse(response)
 end
end

//MODEL

class Food < OpenStruct
 attr_reader :calories,
 :total_weight,
 :diet_labels,
 :health_labels,
 :total_nutrients,
 :total_daily,
 :ingredients

def initialize(search)
 data = EdamamService.new.data(search)
 @calories = data[‘calories’]
 @total_weight = data[‘totalWeight’]
 @diet_labels = data[‘dietLabels’]
 @health_labels = data[‘healthLabels’]
 @total_nutrients = data[‘totalNutrients’]
 @total_daily = data[‘totalDaily’]
 @ingredients = data[‘ingredients’]
 end

def search_term
 search = @ingredients.map do |hash|
 hash[‘text’]
 end
 search.first
 end

def diet_labels
 labels = @diet_labels.each do |label|
 label.gsub!(‘_’, ‘ ‘)
 end
 labels
 end

def health_labels
 labels = @health_labels.each do |label|
 label.gsub!(‘_’, ‘ ‘)
 end
 labels.take(5)
 end

def total_nutrients
 list = @total_nutrients.map do |k,v|
 “#{v[“label”]}: #{v[“quantity”].round(2)} #{v[“unit”]}”
 end
 list
 end

def total_daily
 list = @total_daily.map do |k,v|
 “#{v[“label”]}: #{v[“quantity”].round(2)} #{v[“unit”]}”
 end
 list
 end
end

//CONTROLLER

class SearchController < ApplicationController
 def index
 @food = Food.new(params[:search])
 end
end

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.