Undefined method error in rails? - ruby-on-rails

I have a model where students can enter information about themselves and others can view them. Since there are a lot of profiles, I have added a search section too. If they want only CS majors, they can select from a drop down. I had things like major, college... today I added "level", which is level of education and I get an error (Error shown at the end). I know this is long, but any help would be really really useful. Thanks!
(Userinfo is the student model)
My controller looks like this:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy, :log_impression]
def index
#userinfors = Userinfo.search(params[:search])
#search = Search.new
#college = Userinfo.uniq.pluck(:college)
#major = Userinfo.uniq.pluck(:major)
#level = Userinfo.uniq.pluck(:level)
end
def show
end
def new
#userinformation = current_user.build_userinfo
end
def create
#userinformation = current_user.build_userinfo(userinfo_params)
if #userinformation.save
redirect_to userinfo_path(#userinformation)
else
render 'new'
end
end
def edit
end
def update
if #userinformation.update(userinfo_params)
redirect_to userinfo_path(#userinformation)
else
render 'edit'
end
end
def destroy
#userinformation.destroy
redirect_to root_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major, :token, :skills, :level)
end
def find_userinfo
#userinformation = Userinfo.friendly.find(params[:id])
end
end
My information fill out form (please note how the way of input is different in major and level of edu, maybe thats the problem?):
<%= simple_form_for #userinformation, :html => { :multipart => true } do |f| %>
<%= f.input :name, required: true, label: 'Full name' %>
<%= f.input :college, required: true, label: 'Name of college' %>
<%= f.input :gpa, required: true, label: 'Enter GPA' %>
<%= f.input :email, required: true, label: 'Enter email address' %>
<%= f.input :major, required: true, label: 'Major' %>
<%= f.input :level, collection: ["College undergrad", "College grad"], required: true, label: 'Level of education' %>
<%= f.input :skills, required: true, label: 'Skills' %>
<%= f.button :submit, 'Create account' %>
<% end %>
Search controller:
class SearchesController < ApplicationController
def new
#search = Search.new
#college = Userinfo.uniq.pluck(:college)
#major = Userinfo.uniq.pluck(:major)
#level = Userinfo.uniq.pluck(:level)
end
def create
#search = Search.create(search_params)
redirect_to #search
end
def show
#search = Search.find(params[:id])
end
private
def search_params
params.require(:search).permit(:keyword, :college, :min_gpa, :major, :token, :level )
end
end
Search model:
class Search < ActiveRecord::Base
def search_userinfos
userinfos = Userinfo.all
userinfos = userinfos.where(["name LIKE ?","%#{keyword}%"]) if keyword.present?
userinfos = userinfos.where(["college LIKE ?", college]) if college.present?
userinfos = userinfos.where(["cast(gpa as numeric) >= ?", min_gpa]) if min_gpa.present?
userinfos = userinfos.where(["major LIKE ?", major]) if major.present?
userinfos = userinfos.where(["level LIKE ?", level]) if level.present?
userinfos = userinfos.order("gpa")
return userinfos
end
end
This is where the search section is displayed (index view):
<%= simple_form_for #search do |s| %>
<%= s.input :keyword, label: 'Name' %>
<label>College</label>
<%= s.select :college, options_for_select(#college), :include_blank => true %>
<%= s.input :min_gpa, label: "Minimum GPA" %>
<label>Major</label>
<%= s.select :major, options_for_select(#major), :include_blank => true %>
<label>Job type</label>
<%= s.select :level, options_for_select(#level), :include_blank => true %>
<%= s.button :submit, "Search" %>
<% end %>
The error I get when I try to visit the index view:
NoMethodError in Userinfos#index
Showing app-path/index.html.erb where line #25 raised:
undefined method `level' for #<Search:0x000000139d6c38>
Line 25 is:
<%= s.select :level, options_for_select(#level), :include_blank => true %>
Everything works if I remove that line. When I show the education level in the user profile page, it works perfectly.
Schema for searches:
create_table "searches", force: :cascade do |t|
t.string "keyword"
t.string "college"
t.decimal "min_gpa"
t.string "major"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

As you commented, you do not have a level attribute in your Search model.
rails g migration add_level_to_searches level:string
And of course
rails db:migrate
Happy to help!

Related

Rails 7 stimulus Hide dropdown if empty

I have a 3 category/subcategory/subsubcategory dependant dropdowns
everything works fine but I have a small issue. the second dropdown (subcategories) does not always have subsubcategories so I want to hide the last dropdown and only show it when there is a subsubcategory so it only shows based on the user selection of the subcategory
I would appreciate if someone can help.
my categories table
create_table :categories do |t|
t.string "name", limit: 255
t.text "description", limit: 65535
t.integer "parent_id", limit: 4
t.boolean "important", default: false
t.integer "position", limit: 4, default: 0
t.timestamps
end
in modules category.rb
class Category < ApplicationRecord
validates :name, presence: true
has_many :jobs
belongs_to :parent, foreign_key: :parent_id, class_name: 'Category' , :optional => true
has_many :subcategories, foreign_key: :parent_id, class_name: 'Category'
has_many :subsubcategories, foreign_key: :parent_id, class_name: 'Category'
end
in modules job.rb
class Job < ApplicationRecord
validates :title,:category_id, :description, presence: true
validates :category, :presence => true
belongs_to :user
belongs_to :category , -> { order("name") }
belongs_to :subcategories, class_name: "Category"
belongs_to :subsubcategories, class_name: "Category"
end
dropdown_controller.js
import { Controller } from "#hotwired/stimulus";
// connect to data-controller="dropdown"
export default class extends Controller {
submit () {
this.element.requestSubmit();
}
}
in my jobs_controller.rb
class JobsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
before_action :set_categories
def index
#jobs = Job.all
end
def new
#category = Category.find_by(id: params[:category])
#categories = Category.where(parent_id: nil)
#subcategories = #category.subcategories if #category
#subsubcategories = Category.where(:parent_id => params[:subcategories])
#job = Job.new
end
def create
#job = Job.new(job_params.merge({ user: current_user }))
if #job.save
format.html { redirect_to root_path, notice: "Job was successfully created." }
else
render :new, status: :unprocessable_entity
end
end
private
def job_params
params.require(:job).permit(:title, :description,:category_id).merge(user: current_user)
end
def set_categories
#category = Category.find_by(id: params[:category].presence)
#subcategory = Category.find_by(id: params[:subcategories])
#subsubcategory = Category.find_by(id: params[:subsubcategories])
end
end
views/jobs/new.html.erb
<%= turbo_frame_tag "form" do %>
<%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<%= select_tag :subcategories, options_from_collection_for_select(#subcategories || [], "id" , "name", #subcategory&.id), prompt: "Select a subcategories category" %>
<%= select_tag :subsubcategories, options_from_collection_for_select(#subsubcategories || [], "id" , "name", #subsubcategory&.id), prompt: "Select a subsubcategories category" %>
<% end %>
<% end %>
Why don't you hide it definitely. Like changing views/jobs/new.html.erb to something like this :
<%= turbo_frame_tag "form" do %>
<%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<%= select_tag :subcategories, options_from_collection_for_select(#subcategories || [], "id" , "name", #subcategory&.id), prompt: "Select a subcategories category" %>
<% if #subsubcategories.any? %>
<%= select_tag :subsubcategories, options_from_collection_for_select(#subsubcategories || [], "id" , "name", #subsubcategory&.id), prompt: "Select a subsubcategories category" %>
<% end %>
<% end %>
<% end %>
I actually got it working exactly as I want without Javascript:
<% if !#subsubcategories.blank? %>
<%= select_tag :subsubcategories , options_from_collection_for_select(#subsubcategories || [], "id" , "name", #subsubcategory&.id), prompt: "Select a subsubcategories category" %>
<% end %>

has_many build AssociationTypeMismatch Error

Another newb question. A Band has_many Albums. I'm getting the error:
ActiveRecord::AssociationTypeMismatch (Band(#70076964285020) expected,
got "1" which is an instance of String(#12787800)):
app/controllers/albums_controller.rb:20:in `create'
... which is the build line in #create
albums controller
def new
binding.pry
#band = Band.find(params[:band])
authorize #band, :admin?
#album = Album.new
respond_to do |format|
format.js
end
end
def create
binding.pry
#band = Band.find(params[:album][:band].to_i)
authorize #band, :admin?
#album = #band.albums.build(album_params)
if #album.save
#albums = #band.albums
##eps = #band.eps
#songs = #band.songs
respond_to do |format|
format.js
end
else
#fail = "fail"
respond_to do |format|
format.js
end
end
end
def album_params
params.require(:album).permit(:band, :album_name, :album_release_date, :etc)
end
the form:
<%=simple_form_for(#album, remote: true, :authenticity_token => true, format: :js) do |f| %>
<%= f.hidden_field :band, :value => #band.id %>
<%= f.input :album_name %>
<%= f.input :album_release_date %>
<%= f.input :etc %>
<div id="albumsubmit">
<div class="form-actions">
<%= f.button :submit, "Create Album", class: "btn btn-primary" %>
</div>
</div>
schema
create_table "albums", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "band_id"
t.string "album_name"
t.string "album_release_date"
t.index ["band_id"], name: "index_albums_on_band_id"
end
when you passing to save method param with that have has_many Association he expect the instance of "band", just set the param name to band_id
here:
<%= f.hidden_field :band, :value => #band.id %>
to:
<%= f.hidden_field :band_id, :value => #band.id %>
and here:
params.require(:album).permit(:band, :album_name, :album_release_date, :etc)
to:
params.require(:album).permit(:band_id, :album_name, :album_release_date, :etc)

Rails 4 multiple image uploading using carrierwave

How can I upload multiple images using CarrierWave?
I followed "drjorgepolanco" answer from
Rails 4 multiple image or file upload using carrierwave
but I keep getting a error
undefined method `[]' for #<AssignmentpicUploader:0x007eff7831a4d8>
Extracted source (around line #25):
ActionView::Template::Error (undefined method `[]' for #<AssignmentpicUploader:0x007eff7831a4d8>):
22:
23:
24: <div id="img">
25: <%= image_tag #assignment.picture[1].url(:large) %>
26: </div>
27:
28: </br>
app/views/assignments/show.html.erb:25
assignment.rb
class Assignment < ActiveRecord::Base
validates :name, length: { minimum: 1, maximum:120 }
validates :description, length: { minimum: 1 }
mount_uploader :picture, AssignmentpicUploader
end
assignments_controller.rb
class AssignmentsController < ApplicationController
before_action :authenticate_user!
before_filter :admin_access, only: [:new, :create, :edit, :update, :destroy]
def index
#assignments = Assignment.all.order("created_at DESC")
end
def show
#assignment = Assignment.find(params[:id])
end
def new
#assignment = Assignment.new
end
def create
#assignment = current_user.assignments.build
#assignment.name = params[:assignment][:name]
#assignment.description = params[:assignment][:description]
#assignment.picture = params[:assignment][:picture]
if #assignment.save
flash[:notice] = "Assignment was saved successfully."
redirect_to #assignment
else
flash.now[:alert] = "Error creating assignment. Please make sure there is a name and description."
render :new
end
end
def edit
#assignment = Assignment.find(params[:id])
end
def update
#assignment = Assignment.find(params[:id])
#assignment.name = params[:assignment][:name]
#assignment.description = params[:assignment][:description]
#assignment.picture = params[:assignment][:picture]
if #assignment.save
flash[:notice] = "Assignment was updated successfully."
redirect_to #assignment
else
flash.now[:alert] = "Error saving assignment. Please try again."
render :edit
end
end
def destroy
#assignment = Assignment.find(params[:id])
if #assignment.destroy
flash[:notice] = "\"#{#assignment.name}\" was deleted successfully."
redirect_to action: :index
else
flash.now[:alert] = "There was an error deleting the assignment."
render :show
end
end
private
def assignment_params
params.require(:assignment).permit(:name, :description, picture: [])
end
end
assignments/new.html.erb
...
<div class="col-md-8">
<%= form_for #assignment, :html => { :multipart => true } do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control', placeholder: "Enter assignment name" %>
</div>
<div class="form-group">
<%= f.label :description %>
<%= f.text_area :description, rows: 8, class: 'form-control', placeholder: "Enter assignment description" %>
</div>
<div class="form-group">
<%= f.label :picture %>
<%= f.file_field :picture, :multiple => true %>
</div>
<br />
<%= f.submit "Save", class: 'button' %>
<% end %>
</div>
</div>
assignments/show.html.erb
<%= image_tag #assignment.picture[1].url(:large) %>
schema.rb
create_table "assignments", force: :cascade do |t|
t.string "name"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "picture"
end
add_index "assignments", ["user_id"], name: "index_assignments_on_user_id"
Carrierwave documentation says:
Add a column which can store an array. This could be an array column or a JSON column for example. Your choice depends on what your database supports.
Your "picture" is just a string. Add serialize :picture, JSON to your model.

Rails nested routes for create method smart_listing

I have two models: apartment and room. Apartment has_many rooms, and rooms belongs_to apartment. I use smart_listing gem as ajax form. I show my table in edit_apartment_path
= render 'rooms/index' # index is partial
And I add this to my apartment_controller
def edit
#rooms = smart_listing_create :rooms,
Room.where(apartment_id: params[:apartment_id]),
partial: "rooms/list"
end
Now I must set paths for my form
= simple_form_for object, url: object.new_record? ? apartment_rooms_path : apartment_room_path(id: object),
remote: true, html: {class: "form-horizontal"} do |f|
= f.input :title
= f.button :submit
I can edit my created room, but I can't create new room in apartment. My error:
ActionController::UrlGenerationError - No route matches {:action=>"edit", :apartment_id=>nil, :controller=>"rooms", :id=>#<Room id: 83, title: "dawawd">, created_at: "2016-02-11 10:36:30", updated_at: "2016-02-11 10:36:30", apartment_id: 4>} missing required keys: [:apartment_id]:
My routes
resources :apartments do
resources :rooms
end
Propably smart_listing not support nested routes. Anyone have idea? :)
here's simple example of nested routes with smart_listing. I think that should cover the subject.
I used Rails 4.2, ruby 2.2.0, smart_listing 1.1.2
config/routes.rb
resources :users do
resources :bios
end
root 'users#index'
models/user.rb
class User < ActiveRecord::Base
has_one :bio
accepts_nested_attributes_for :bio, allow_destroy: true
scope :like, ->(args) { where("email like '%#{args}%' OR name like '%#{args}%' OR surname like '%#{args}%'")}
end
models/bio.rb
class Bio < ActiveRecord::Base
belongs_to :user
end
controllers/users_controller.rb
class UsersController < ApplicationController
include SmartListing::Helper::ControllerExtensions
helper SmartListing::Helper
before_action :set_user, only: [:update, :destroy]
def index
users_scope = User.all.includes(:bio)
users_scope = users_scope.like(params[:filter]) if params[:filter]
# #users = smart_listing_create :users, users_scope, partial: "users/list", page_sizes: [5, 7, 13, 26]
#users = smart_listing_create(:users, users_scope, partial: 'users/list',
sort_attributes: [
[:name, 'name'],
[:surname, 'surname'],
[:email, 'email'],
[:city, 'bio.city'],
[:birthday, 'bio.birthday']
],
default_sort: { start_at: 'desc' }
)
end
def new
#user = User.new
#user.build_bio
end
def create
#user = User.new(user_params)
#user.save
end
def edit
#user = User.includes(:bio).find(params[:id])
#user.bio.build if #user.bio.nil?
end
def update
#user.update(user_params)
end
def delete
end
def destroy
#user.destroy
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :surname, :email, :bio_attributes => [:birthday, :city])
end
end
views/users/index.html.haml
= smart_listing_controls_for(:users, {class: "form-inline text-right"}) do
.form-group.filter.input-append
= text_field_tag :filter, '', class: "search form-control",
placeholder: "Search...", autocomplete: :off
= smart_listing_render :users
views/users/_list.html.haml
- unless smart_listing.empty?
%table.table.table-striped
%thead
%th= smart_listing.sortable "Name", :name
%th= smart_listing.sortable "Surname", :surname
%th= smart_listing.sortable "Email", :email
%th= smart_listing.sortable "City", :city
%th= smart_listing.sortable "Birthday", :birthday
%tbody
- smart_listing.collection.each do |o|
%tr.editable{data: {id: o.id}}
= smart_listing.render object: o, partial: "users/user", locals: {object: o}
= smart_listing.item_new colspan: 6, link: new_user_path
= smart_listing.paginate
= smart_listing.pagination_per_page_links
- else
%p.warning No users
views/users/_user.html.haml
%td= object.name
%td= object.surname
%td= object.email
%td= object.bio.city
%td= object.bio.birthday
%td.actions= smart_listing_item_actions [ {name: :edit, url: edit_user_path(object)}, {name: :destroy, url: user_path(object), confirmation: "Are you sure you want to delete this?"}]
views/users/_form.html.haml
%td{colspan: 6}
= form_for object, url: object.new_record? ? users_path : user_path(object),
remote: true, html: {class: "form-horizontal"} do |f|
%p
Name:
= f.text_field :name
%p
Surname:
= f.text_field :surname
%p
Email:
= f.text_field :email
= f.fields_for :bio do |ff|
%p
Birthday
= ff.date_field :birthday
%p
City
= ff.text_field :city
= f.submit "Save", class: "btn btn-primary"
%button.btn.btn-link.cancel Cancel
views/users/create.js.erb
<%= smart_listing_item :users, :create, #user, #user.persisted? ? "users/user" : "users/form" %>
views/users/edit.js.erb
<%= smart_listing_item :users, :edit, #user, "users/form" %>
views/users/destroy.js.erb
<%= smart_listing_item :users, :destroy, #user %>
views/users/index.js.erb
<%= smart_listing_update(:users) %>
views/users/new.js.erb
<%= smart_listing_item :users, :new, #user, "users/form" %>
views/users/update.js.erb
<%= smart_listing_item :users, :update, #user, #user.valid? ? "users/user" : "users/form" %>
you should receive to form #apartment also, and in this case if your #room.persisted? form recive request to edit, else to create:
= simple_form_for [#apartment, #room], remote: true, html: {class: "form-horizontal"} do |f|
= f.input :title
= f.button :submit

rails faux active record

I am trying to use faux active record (a model that has no persistence in db).
I followed this example:
https://quickleft.com/blog/using-faux-activerecord-models-in-rails-3/
And I have this error:
ArgumentError in SearchController#new
wrong number of arguments (2 for 1)
app/models/search.rb:32:in assign_attributes'
app/models/search.rb:27:ininitialize'
app/controllers/search_controller.rb:4:in new'
app/controllers/search_controller.rb:4:innew'
It seems that when you call Search.new it needs the parametters but my goal is to make it an empty Search object.
Here are my model, controller, view, route:
Model
class Search
include ActiveModel::MassAssignmentSecurity
TRUE_VALUES = ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES
attr_accessor :query
attr_reader :safe_search, :results_per_page
alias_method :safe_search?, :safe_search
attr_accessible :query, :safe_search, :results_per_page
RESULTS_PER_PAGE = [10, 25, 50, 100].freeze
include ActiveModel::Conversion
include ActiveModel::Validations
validates :query, presence: true
validates :results_per_page, presence: true, inclusion: { in: RESULTS_PER_PAGE }
def persisted?
false
end
def initialize(attributes = {})
assign_attributes(attributes)
yield(self) if block_given?
end
def assign_attributes(values, options = {})
sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
send("#{k}=", v)
end
end
def results_per_page=(value)
#results_per_page = value.to_i
end
def safe_search=(value)
#safe_search = TRUE_VALUES.include?(value)
end
end
Controller
class SearchController < ApplicationController
def new
#search = Search.new
render "search/new.html.erb"
end
def create
#search = Search.new(params[:search])
# TODO: use the #search object to perform a search. Adding
# a `results` method on the search object might be a good starting point.
end
end
View: search/new.html.erb
<%= form_for #search do |f| %>
<%= f.label :query %>
<%= f.text_field :query %>
<%= f.label :safe_search %>
<%= f.check_box :safe_search %>
<%= f.label :results_per_page %>
<%= f.select_box :results_per_page %>
<%= end %>
Here is the solution that I found:
In the model I am using this:
def initialize(attributes = {})
assign_attributes(attributes)
end
def assign_attributes(attributes)
sanitize_for_mass_assignment(attributes).each do |k, v|
send("#{k}=", v)
end
end
Here are my new and create methods in the controller:
def new
#search = Search.new
render "search/new.html.erb"
end
def create
#search = Search.new(params[:search])
respond_to do |format|
format.html { render :template => "search/create.html.erb" }
end
end
By the way i had to rename my controller to SearchesController because controllers must be pluralized.
And here is my new.html.erb view:
<%= form_for #search, url: { action: "create" }, :method => :post do |f| %>
<%= f.label :query %>
<%= f.text_field :query %>
<%= f.label :safe_search %>
<%= f.check_box :safe_search %>
<%= f.label :results_per_page %>
<%= f.select(:results_per_page, [10, 25, 50, 100]) %>
<%= f.submit %>
<% end %>
I hope this will be useful to somebody.

Resources