I am working on a search with Rails 4 - learning from a Rails cast (which is about version prior 3, apparently).
However, I get the following error:
NameError in GamesController#index undefined local variable or method
`search' for #
This is my code
view:
<%= form_tag games_path, :method => 'get', :id => "games_search" do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<% end %>
controller:
def search
#games = Game.search(params[:search])
end
model:
self.search(search)
if search
where('title LIKE ?', "%#{search}")
else
scoped
end
end
migration:
def change
create_table :games do |t|
t.string :title
t.date :releaseDate
t.text :description
t.integer :rating
t.timestamps
end
end
routes:
resources :games
What am I doing wrong?
Class Method
I remember using that Railscast to help me create a search facility
What you're looking at is what's known as a class method - a method which initiates a class, and creates a response for you.
Here's how a class method should work in Rails (also consider each "model" is a class):
#app/models/game.rb
Class Game < ActiveRecord::Base
def self.search(query)
... logic here
end
end
I believe the problem you have is that you haven't declared def for your method, although I don't know for sure as you haven't posted your entire model code
The model missed 'def'
**def** self.search(search)
if search
where('title LIKE ?', "%#{search}")
else
scoped
end
end
I should be awarded the n00b badge. Sorry!
Related
I have a Photo Share web app and I am trying to add comments in photos. I can't spot any mistakes. Maybe in the controller class in index function is the problem. There is an undefined method error when I try to show-post comments below the photo. Error in HAML code.
Error: - if #photo_comments.any?
Controller:
class CommentsController < ApplicationController
def index
#photo_comments = Comment.where(photo_id: => photo_id)
end
def create
#comment = Comment.create(user_id: params[:user_id], photo_id: params[:photo_id], text: params[:comment][:text])
flash[:notice] = "Successfully added a comment"
redirect_to :back
end
private
def comment_params
params.require(:comment).permit(:user_id, :photo_id, :text)
end
end
Model:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :photo
end
Database:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.integer :user_id
t.integer :photo_id
t.string :text
t.timestamps
end
end
end
View:
%p Comments
- if #photo_comments.any?
- #photo_comments.each do |comment|
.bold-text= "#{comment.user.email}: "
.normal-text= comment.text
%br
- else
.text No comments for this photo yet!
%br
%br
%p
= form_for Comment.new(), :url => user_photo_comments_path do |form|
= form.label :text, 'Add a Comment'
%br
= form.text_area :text
%br
= form.submit 'Post'
Routes:
Rails.application.routes.draw do
get '/' => 'home#index'
resources :users do
resources :photos do
resources :comments
end
resources :follows
end
resources :tags, only: [:create, :destroy]
get '/log-in' => "sessions#new"
post '/log-in' => "sessions#create"
get '/log-out' => "sessions#destroy", as: :log_out
end
This line seems a bit problematic:
#photo_comments = Comment.where(photo_id: => photo_id)
I can spot a couple of potential errors here:
hash syntax: you are mixing both styles, you should use photo_id: photo_id or (Ruby pre 1.9) :photo_id => photo_id
the method or variable photo_id seems not defined in that controller, maybe you would mean params[:photo_id]?
There is definitely a syntax error on this line:
#photo_comments = Comment.where(photo_id: => photo_id)
also photo_id is not defined anywhere in the controller so maybe it should instead look like:
#photo_comments = Comment.where(photo_id: params[:photo_id])
?
The error undefined_method often comes when calling a method on a nil value. In your case instance variable #photo_comments is nil thus giving you undefined_method error in views.
These two line make no sense:
- if #photo_comments.nil?
- #photo_comments.each do |comment|
If the instance variable #photo_comments is nil then iterate of it? Of course, you will get an undefined method 'each' for nil:NilClass in that case.
I guess you mean something like this:
- unless #photo_comments.nil?
- #photo_comments.each do |comment|
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])
I'm creating an app where I have a 'Shop' model, which is associated with a 'Service' model (For example, a shop might offer different services which can be added by the shop owner only - such as an oil change, new tires, window tinting etc.)
I believe I have the relationship sorted:
class Service < ActiveRecord::Base
belongs_to :shop
end
and
class Shop < ActiveRecord::Base
belongs_to :user
has_many :services
end
However I'm not sure how to add this association in the 'Service' create method so that 'service' belongs only to 'shop'
I'd also like to display all related services on the Shop 'show' page.
I'm in the 'Show' view of Shop, and I want to add a service, how can I pass the shop_id into the service and what files would I have to change?
The create method is just the bog standard scaffold one:
def create
#service = Service.new(service_params)
#service.save
respond_with(#service)
end
And the database looks like this:
Shops:
class CreateShops < ActiveRecord::Migration
def change
create_table :shops do |t|
t.string :name
t.text :description
t.string :phone
t.string :email
t.string :website
t.integer :user_id
t.timestamps
end
end
end
and Services:
class CreateServices < ActiveRecord::Migration
def change
create_table :services do |t|
t.string :category
t.integer :shop_id
t.timestamps
end
end
end
Any help would be very appreciated. I guess i'm just not getting how to pass the relationship information correctly.
Cheers!
First you will need a set of nested routes for shops & services:
# config/routes.rb
resources :shops do
resources :services
end
This creates routes for services nested under shop:
Prefix Verb URI Pattern Controller#Action
shop_services GET /shops/:shop_id/services(.:format) services#index
POST /shops/:shop_id/services(.:format) services#create
new_shop_service GET /shops/:shop_id/services/new(.:format) services#new
edit_shop_service GET /shops/:shop_id/services/:id/edit(.:format) services#edit
shop_service GET /shops/:shop_id/services/:id(.:format) services#show
PATCH /shops/:shop_id/services/:id(.:format) services#update
PUT /shops/:shop_id/services/:id(.:format) services#update
DELETE /shops/:shop_id/services/:id(.:format) services#destroy
shops GET /shops(.:format) shops#index
POST /shops(.:format) shops#create
new_shop GET /shops/new(.:format) shops#new
edit_shop GET /shops/:id/edit(.:format) shops#edit
shop GET /shops/:id(.:format) shops#show
PATCH /shops/:id(.:format) shops#update
PUT /shops/:id(.:format) shops#update
DELETE /shops/:id(.:format) shops#destroy
If look at the parameters for the services routes you can see that there is a :shop_id parameter.
We can use this in our ServicesController to set the shop before each action:
class ServicesController < ApplicationController
before_action :set_shop
# GET /shops/:shop_id/services/new
def new
#service = #shop.services.new
end
# POST /shops/:shop_id/services
def create
#service = #shop.services.new(service_params)
# ... #todo validate and save
end
private
def service_params
params.require(:service).permit(:category)
end
def set_shop
#shop = Shop.find(params[:shop_id])
end
end
In your views you need to change your forms somewhat so that form_for uses the correct path.
<!-- views/services/new.html.erb -->
<h1>Add a service to <%= #service.shop.name %></h1>
<%= form_for([#service.shop, #service]) do |f| %>
<div>
<%= f.label :category %>
<%= f.text_field :category %>
</div>
<%= f.submit %>
<% end %>
I'm trying to set up a page where there are 4 dropdown boxes, each of which have a full list of Products. A user can select any combination of 4 products, and 'create' a new print page, which has the product information list
I only have one box right now, but when I try to create a new row for Print from this page, it doesn't return anything to :p1
new.html.erb:
<%= f.collection_select :p1, Product.all, :id, :name, :prompt => 'Select One' %>
<%= f.submit "Create" %>
class PrintsController < ApplicationController
def new
#print = Print.new
end
def create
#print = Print.new(print_params)
if #print.save
redirect_to #print, alert: "Created successfully."
else
redirect_to new_print_path, alert: "Error creating print page."
end
end
def show
#print = Print.find(params[:id])
end
private
def print_params
params.require(:p1).permit(:p2, :p3, :p4)
end
end
Model
class Print < ActiveRecord::Base
belongs_to :product
end
Migrate
class CreatePrints < ActiveRecord::Migration
def change
create_table :prints do |t|
t.integer :p1
t.integer :p2
t.integer :p3
t.integer :p4
t.timestamps
end
end
end
Routes:
Rails.application.routes.draw do
resources :categories, :products, :prints
I'm a total rails newbie, so I know I'm probably making a stupid mistake somewhere, but I've been fiddling with code for hours and still haven't figured out what I did wrong.
Your print_params method is wrong :
def print_params
params.require(:print).permit(:p1, :p2, :p3, :p4)
end
This is the right format.
I have two models in my Rails app which form a one_to_many relationship. The first model, store, represents a brick and mortar store and has latitude and longitude columns which are being geocoded correctly using the 'geocoder' gem and an address column. The store has_many products. The product has_one store. I would like to return the index of products based on proximity to an address which the user inputs in the search form. Here are the relevant parts of my code as well as my attempt at implementing the search:
in schema.rb:
create_table "products", force: true do |t|
t.string "title"
...
t.integer "store_id"
end
create_table "stores", force: true do |t|
...
t.string "business_address"
t.float "latitude"
t.float "longitude"
end
in store.rb
class Store < ActiveRecord::Base
has_many :products
geocoded_by :business_address
after_validation :geocode, :if => :business_address_changed?
end
in product.rb
class Offer < ActiveRecord::Base
belongs_to :store
end
in views/products/search.html.erb
...
<%= form_tag products_path, :method => 'get' do %>
<p>
Find products near<br />
<%= text_field_tag :custom_address, params[:custom_address] %><br />
</p>
<p>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
in products_controller.rb
def index
#products = Store.near(params[:custom_address], 100, order: :distance, :select => "products.*")
end
The above index method generates a
ActiveRecord::StatementInvalid in Products#index
error
I am not sure how to continue. Obviously there is a problem with the way I am using the near method and :select but I can't wrap my head around it. How can I return the products sorted by distance?
I am using MySQL as the database adapter; I have heard of issues due to a lack of trig functions with SQLite.
I got my code working properly by using the following:
I added an attr_accessor in the Product model:
class Product < ActiveRecord::Base
belongs_to :store
attr_accessor :distance_to_user
end
And I changed the index method:
def index
#products = []
if params[:custom_address]
geocoded_stores = (Stores.near(params[:custom_address], 100,))
geocoded_stores.each do |s|
s.products.each do |product|
product.distance_to_user = s.distance
#products << product
end
end
else
#products = Product.all
end
end