Basic Search in Ruby Sinatra ActiveRecord - ruby

I'm trying to make a basic search function for a simple product inventory app on Sinatra, but don't know how to make the controller and view to properly output all the products which have similar names to a results page.
SearchPage.erb:
<form action="/search", method="post">
<input type="text" name="product[name]">
Controller:
post '/search' do
#Products = Product.find_by(name: params[:product][:name])
#Products = Product.all(:name.like => "%#{params[:name]}%") #found this on another question
erb :"result"
end
Result.erb
<% #Products.each do |product| %>
<%=product.name %>
<%=product.details %>
EDIT: I was able to make search work based on the suggestion with the following code. Thanks!:
Search.erb View
<form action="/search", method="get">
<input type="text" name="search">
Controller
get '/search' do
#products = Product.all
if params[:search]
#products = Product.search(params[:search])
else
#products = Product.all
end
erb :'results'
end
Model
class Product < ActiveRecord::Base
def self.search(search)
where("name like ?", "%#{search}%")
end
Results.erb View
<% if #products.present? %>
<table>
<td>Product Name</td><td>Company</td>
<% #products.each do |product| %>
<tr><td><%=h product.name %> </td>
<td><%=h product.company.name %></td>
<% end %>
<% else %>
<p>There are no Products containing the term(s) <%= params[:search] %>.</p>
<% end %>
</table>

I notice off the bat you're using a POST method. There is an easier way to do create search functionality for your products. Try this:
Posts Controller:
#products = Product.all
if params[:search]
#products = Product.search(params[:search]).order("created_at DESC")
else
#products = Product.all.order('created_at DESC')
end
end
Posts Model (note: If you are using SQLite keep it as LIKE. If you are using Postgres, change LIKE to ILIKE)
def self.search(search)
where('name like :pat or content like :pat', :pat => "%#{search}%")
end
Search Form (Put into your Result.erb and edit as needed but keep as get method. I personally like using form helpers but you can create a normal form if you'd like)
<%= form_tag(products_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Posts" %>
<%= submit_tag "Search" %>
<% end %>
Render Results
<% if #products.present? %>
<%= render #products %>
<% else %>
<p>There are no posts containing the term(s) <%= params[:search] %>.</p>
<% end %>
Let me know if this works for you. If not, i'll try help some more.

Related

Using carmen gem with rails

What I am trying to do is show the 2 letter abbreviation code of the countries, i.e 'US'.
After the country is selected I need to show its state or province.
but I am having a problem.
My code looks like
<%= f.select :country_code, region_options_for_select(only_us_and_france) %>
and define this in helper:
def only_us_and_france
Carmen::Country.all.select{|c| %w{US FR}.include?(c.code)}
end
I am using Rails 4.1.0.
I have solved this by :
Step1: Generated migration
rails g migration addStateCodeFieldToAccounts state_code:string
Step2: Define a method inside controller
def subregion_options
render partial: 'subregion_select'
end
Step3: Declare in routes
resources :accounts do
collection do
get 'subregion_options'
end
end
Step4: In view
<div class="input-control select state_input" data-role="input-control">
<%= f.select :country, region_options_for_select(only_us_and_france) %>
</div>
<div class="input-control select state_input" data-role="input-control">
<%= render partial: 'subregion_select', locals: {parent_region: f.object.country} %>
</div>
Step5: Make partial subregion_select
<div id="account_state_code_wrapper">
<% parent_region ||= params[:parent_region] %>
<% country = Carmen::Country.coded(parent_region) %>
<% if country.nil? %>
<em>Please select a country above</em>
<% elsif country.subregions? %>
<%= subregion_select(:order, :state_code, parent_region) %>
<% else %>
<%= text_field(:order, :state_code) %>
<% end %>
</div>
Step6: In my js file written this
$('select#account_detail_country').change(function(){
selectWrapper = $('#account_state_code_wrapper')
countryCode = $(this).val()
url = "/account_details/subregion_options?parent_region="+countryCode
console.log(url)
selectWrapper.load(url)
});
yeah and it works :) Hope this will help you.

How to save many items on one form rails?

I need to save many items to Cart on form, user enter quantity one form, and selected items goes to db, but now save only first entered quantity of item. Why?
my form
<%= form_for #cart_item do |f| %>
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, :value => item.id %>
<%= f.text_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
And controller
cart_items_controller.rb
class CartItemsController < ApplicationController
before_action :set_cart, only: [:create]
def create
#cart_items = CartItem.create(cart_items_params)
#cart_items.cart_id = #cart.id
if #cart_items.save
redirect_to :back
else
render root_path
end
end
private
def cart_items_params
params.require(:cart_item).permit(:id, :qty, :item_id, :cart_id)
end
def set_cart
#cart = Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
#cart = Cart.create
session[:cart_id] = #cart.id
end
end
There are a few problems here. I'll give you a little bump:
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, :value => item.id %>
<%= f.text_field :qty %>
<% end %>
For each CartItem, this is going to create an input like this
<input name="qty">
This is problematic because only one (the last one in the DOM) will be submitted. You need to research fields_for and incorporate that into your loop in order to get unique names for each Item in the form.
This same issue follows through into your controller
def cart_items_params
params.require(:cart_item).permit(:id, :qty, :item_id, :cart_id)
end
This is going to look for a single :id, :qty, :item_id, and :cart_id, when in reality you're looking to accept multiple :item_id and :qty fields. You need to research Strong Parameters with nested has_many associations.
Finally you have this
#cart_items = CartItem.create(cart_items_params)
which is going to attempt to create a single CartItem when you're really trying to create multiple items and associate them back to the Cart. You need to research accepts_nested_attributes_for as well as more generally "rails form save has_many association". It's a widely covered topic here on SO and elsewhere.
I do this:
def create
#cart_items = params[:cart_items]
#cart_items.each do |c|
#cart_item = CartItem.new(c)
if #cart_item.qty.present?
#cart_item.cart_id = #cart.id
#cart_item.save
end
end
and form
<%= form_tag cart_items_path do %>
<% #cart_items.each do |cart_item| %>
<%= fields_for "cart_items[]", cart_item do |f| %>
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, value: item.id %>
<%= f.text_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
<% end %>
<% end %>

Can I work around ActiveRecord?

Trying get a handle on oAuth by recreating twitter using the omniauth-twitter and twitter gems, and I'm stuck on viewing the tweets of all the users I'm following in my app together in one place. I can view my own tweets and other users' in their own profile fine.
This is the error trace
NoMethodError in PagesController#home
undefined method `where' for Twitter::Timeline:Class
app/models/twitter/timeline.rb:47:in `from_users_followed_by'
app/models/user.rb:24:in `hub'
app/controllers/pages_controller.rb:5:in `home'
pages_controller.rb
def home
if signed_in?
#hub_items = current_user.hub.paginate(page: params[:page])
end
end
user.rb
def hub
Twitter::Timeline.from_users_followed_by(self)
end
timeline.rb
module Twitter
class Timeline
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
Twitter::Timeline.where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
end
** Controllers
hub_controller.rb
def show
#hub_items = []
render 'pages/home'
end
** Views
_hub.html.erb
<% if #hub_items.any? %>
<ol class="microposts">
<%= render partial: 'shared/hub_item', collection: #hub_items %>
</ol>
<%= will_paginate #hub_items %>
<% end %>
_hub_items.html.erb
<li id="<%= hub_item.id %>">
<% if #hub_item.any? %>
<% #hub_item.each do |post| %>
<% if post.provider == "twitter" %>
<%= render partial: "users/twitter_post", locals: {post: post} %>
<% end %>
</li>
So, Is there a way to write my self.from_users_followed_by method so that it doesn't have to inherit from ActiveRecord::Base? I can give anything else that's needed to figure this out.

Bring code from view into a popup in Rails

I want to bring this code from my view html into a popup when a link is clicked
Is that possible in Ruby on Rails? I already have the pop up working but I'm wondering about the code to show just the comments:
<div class = "comments"><% if post.comments.exists? %>
<% post.comments.each do |comment| %>
<%= image_tag("http://www.gravatar.com/someavatarlink %) <!-- Retrieves Gravatar -->
<%= link_to comment.user.name, comment.user %>
<span class="timestamp"><%= time_ago_in_words(comment.created_at) %> ago</span>
<span class="content2"><%= comment.comment_content %></span>
<% end %>
<% end %></div>
Added Ajax call to _comment_form.html.erb
<%= link_to "Link", comment, :remote => true %>
Comments
<% end %></div></div>
<div id ="modal" class = "comments"><% if post.comments.exists? %>
<% post.comments.each do |comment| %>
<%= link_to comment.user.name, comment.user %>
<span class="timestamp"><%= time_ago_in_words(comment.created_at) %> ago</span>
<span class="content2"><%= comment.comment_content %></span>
<% end %>
<% end %></div>
Added def show into comments controller
class CommentsController < ApplicationController
def new
#post = post.new(params[:post])
end
def show
#comment = Comment.find(params[:id])
respond_to do |format|
format.js
end
def create
#post = post.find(params[:micropost_id])
#comment = Comment.new(params[:comment])
#comment.post = #post
#comment.user = current_user
if #comment.save
redirect_to(:back)
else
render 'shared/_comment_form'
end
end
end
Created show.erb.js and put it into 'comments' and 'shared' folders
$("#popup").html('<%= escape_javascript(render "comments") %>');
Then finally wrote my partial which is in comments/_comment.html.erb
<% if post.comments.exists? %>
<% post.comments.each do |comment| %>
<%= link_to comment.user.name, comment.user %>
<span class="timestamp"><%= time_ago_in_words(comment.created_at) %> ago</span>
<span class="content2"><%= comment.comment_content %></span>
<% end %>
<% end %>
1. Ajax call
To retrieve the data you use an Ajax call.
<%= link_to "Link", comment, :remote => true %>
Rails will evaluate these requests and will look for a .js view first (it will use .html if it does not exist).
Make also sure that the controller accepts requests to .js like
def show
#comment = Comment.find(params[:id])
respond_to do |format|
format.js
end
end
2. write js view
Add a show.erb.js view to your Comments . This is a JavaScript file with ERB evaluation.
In this template use your js popup code and tell it to fill a div with your html code like so:
$("#popup").html('<%= escape_javascript(render #comment) %>');
This will render the comment. The only thing we need then is a partial to render the html of the comment.
3. write partial for html
Write a partial for the view part you want to have in the popup. This can then be used in a normal html view or the js view. To make it work with the code above call it _comment.html.erb
To know more about partials you can check the guides here:
http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

RoR: How can I get my microposts to show up?

Here is the users show view where they are supposed to show up. ..
<section>
<div id= "purchases">
<%= render 'shared/micropost_form_purchase' %>
</div>
<div id="sales">
<%= render 'shared/micropost_form_sale' %>
</div>
</section>
<%= #sales %> <%# This is just to see if it outputs anything. It doesn't :( %>
<div id="purchases list">
<ol class="microposts">
<%= render #purchases unless #purchases.nil? %>
</ol>
</div>
<div id="sales list">
<ol class="microposts">
<%= render #sales unless #sales.nil? %>
</ol>
</div>
so the forms (partials) are loading fine, but then when I make a post, in either one, neither the purchases list nor the sales list shows up. I checked the database and they are being created along with an entry in the column indicating kind (either sale or purchase).
Here are the forms:
<%= form_for (#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= hidden_field_tag 'micropost[kind]', "purchase" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
and
<%= form_for (#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= hidden_field_tag 'micropost[kind]', "sale" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
also, here is the show part of the users_controller.rb
def show
#user = User.find(params[:id])
#micropost=Micropost.new
#microposts = #user.microposts.paginate(page: params[:page])
end
and here is the show part of the microposts_controller.rb
def show
#micropost = Micropost.find(params[:id])
#microposts = Micropost.where(:user_id => #user.id)
#purchases= #microposts.collect{ |m| m if m.kind == "purchase"}.compact
#sales = #microposts.collect{ |m| m if m.kind == "sale"}.compact
end
additionally, with the help of this post (http://stackoverflow.com/questions/12505845/ruby-error-wrong-number-of-arguments-0-for-1#12505865) the variables #microposts, #purchases, and #sales are all outputting correctly in the console.
can anyone help me out?
edit: using scopes as suggested by the answer given works in the console (it outputs everything correctly, but they still don't show up in the view. Does this mean it is something wrong with my syntax for the users show page?
edit 2:
Here is the view/microposts/_micropost.html.erb code
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
confirm: "You sure?",
title: micropost.content %>
<% end %>
</li>
I'm making some assumptions without seeing more of your code, but it looks like you could
write what you've shown a little differently. I'm assuming your databases are migrating
and have the required columns, e.g., Micropost#kind, Micropost#user_id, etc.
You can use scopes to refine a collection of microposts more expressively. It might be helpful to read
up about ActiveRecord scopes: http://guides.rubyonrails.org/active_record_querying.html#scopes.
class Micropost < ActiveRecord::Base
belongs_to :user
scope :purchases, where(:kind => "purchase")
scope :sales, where(:kind => "sale")
# your code
end
I'm also assuming your user has many microposts:
class User < ActiveRecord::Base
has_many :microposts
# your code
end
For your forms, I'd suggest attaching your hidden field to the form object (f.hidden_field) so
you don't have to specify the name as 'micropost[kind]'.
<%= form_for(#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= f.hidden_field :kind, :value => "sale" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
In MicropostsController#show, you can use your new scopes:
def show
#micropost = Micropost.find(params[:id])
#microposts = #user.microposts
#purchases = #microposts.purchases
#sales = #microposts.sales
end
You should also confirm that your MicropostsController#create action is actually adding
the microposts to the user sending the form (I'm assuming a current user method).
def create
#micropost = current_user.microposts.create(params[:micropost])
# yada
end
You can also confirm expected results on rails console after creating purchases or sales micropost with:
Micropost.purchases
Micropost.sales
Again, I could be missing something without seeing more of the code base.
Check Micropost.count, #purchases.count, #sales.count (by printing them in the controller, or some part of the view) to see if the records actually exist.
Also, if you want to render collections likes #sales and #purchases, you need to make sure that the model partial exists (_micropost.html.erb in your case). That is probably where you need to look for the view errors. For all you know, that file could be empty, thus no errors will show up at all.
The problem might also lie in your microposts#create (or whichever action that you are saving the micropost in), the micropost should be associated with the current_user:
#micropost = current_user.microposts.build(params[:micropost])
Taking this and your previous question into account, I suggest you go through the original code for the RoR tutorial again (and verify that all tests are passing) before taking it apart. You can always add new tests to it for your experiments and they will help in figuring out where you went wrong.

Resources