rails 4 : use of form_for , no route, POST - ruby-on-rails

started learning Ruby on Rails via Lynda - very excited and am trying my best to practice as much as possible. I am following the exercises but training is based on Rails 3 - through now some of the uses are not accepted.
Here is the situation:
I am reaching the create form at subjects/new
Filling out the form
get the following error in return
No route matches [POST] "/subjects/create"
Rails.root: /Users/alpozenalp/Sites/simple_cms
I spent the last 2 hours wandering around stackoverflow, rail guide and all other sources - tried many variations but can't get past this stage.
your help will be greatly appreciated.
routes.rb
SimpleCms::Application.routes.draw do
root :to => "demo#index"
get ':controller(/:action(/:id(.:format)))'
end
subjects_controller.rb
class SubjectsController < ApplicationController
def index
list
render('list')
end
def list
#subjects = Subject.order("subjects.position ASC")
end
def show
#subject = Subject.find(params[:id])
end
def new
#subject = Subject.new
end
def create
# Instantiate a new object using form parameters
#subject = Subject.new(params[:subject])
# Save the object
if #subject.save
# If save succeeds, redirect to the list action
redirect_to(:action => 'list')
else
# If save fails, redisplay the form so user can fix problems
render('new')
end
end
end
new.html.erb
<%= link_to("<< Back to List", {:action => 'list'}, :class => 'back-link') %>
<div class="subject new">
<h2>Create Subject</h2>
<%= form_for(:subject, :url => {:action => 'create'}, :method => :post) do |f| %>
<table summary="Subject form fields">
<tr>
<th>Name</th>
<td><%= f.text_field(:name) %></td>
</tr>
<tr>
<th>Position</th>
<td><%= f.text_field(:position) %></td>
</tr>
<tr>
<th>Visible</th>
<td><%= f.text_field(:visible) %></td>
</tr>
</table>
<div class="form-buttons">
<%= submit_tag("Create Subject") %>
</div>
<% end %>
</div>

naomik's answer will definitely help the form be cleaner, but it sounds like you just need to add a route for subjects in your config/routes.rb file:
SimpleCms::Application.routes.draw do
resources :subjects
root :to => "demo#index"
end
More info in the Rails routing guide.
Edit: Removed the default fallback route, per naomik's suggestion.

I have completed the course and my route.rb looks like below:
Cms2::Application.routes.draw do
root to: "public#index"
get 'admin', :to => 'access#menu'
get 'show/:id', :to => 'sections#show'
get ':controller(/:action(/:id(.:format)))'
post "admin_users/update"
post "subjects/update"
post "pages/update"
post "sections/update"
post "subjects/destroy"
post "subjects/create"
post "pages/destroy"
post "pages/create"
post "sections/destroy"
post "sections/create"
post "admin_users/destroy"
post "admin_users/create"
post "access/attempt_login"
get "access/logout"
end
My controller for def create is as follows:
def create
#new_position = params[:subject].delete(:position)
# Instantiate a new object using form parameters
#subject = Subject.new(params.require(:subject).permit(:name, :position, :visible, :created_at, :updated_at))
# Save the object
if #subject.save
##subject.move_to_position(new_position)
# If save succeeds, redirect to the list action
flash[:notice] = "Subject Created."
redirect_to(:action => 'list')
else
# If save fails, redisplay the form so user can fix problems
#subject_count = Subject.count +1
render('new')
end
end
Hope that helped!

You shouldn't have any problem if you just do
<%= form_for #subject do |f| %>
The form_for helper will automatically choose the correct (idiomatic) action and method based on the state of the model.
If #subject is a new record, you will get
<form action="/subjects" method="post">
...
If #subject is an existing record (with id: 1), you will get
<form action="/subjects/1" method="post">
<input type="hidden" name="_method" value="put">
...
Extra: Your list action seems completely pointless. Just use the index as intended.
Then this piece of code
<%= link_to("<< Back to List", {:action => 'list'}, :class => 'back-link') %>
Becomes this
<%= link_to '« Back to List'.html_safe, subjects_path, class: 'back-link' %>

I encountered the same problem at this point in the Lynda.com course. It was solved by adding resources :subjects above get ':controller(/:action(/:id(.:format)))', then changing the subjects_controller create action to
def create
#subject = Subject.new(params.require(:subject).permit(:name, :position, :visible))
if #subject.save
redirect_to(:action => 'list')
else
render('new')
end
end
This circumvented the Forbidden Attributes Error that occurred with the action as it was previously written.
Because I'd added resources :subjects, this then meant the redirect_to(:action => 'list') above would create an error along the lines of 'Couldn't find Subject with id=list'. To fix this, I added get 'subjects/list' => 'subjects#list' above the resources :subjects route (I've no idea if this was the 'right' thing to do, but it works for now).

Related

NoMethodError in UserCrud#new while trying to create new record

Hi I am running into problems while trying to new record its showing an error message which error is not present in my code-
THe error message-
undefined method `user_cruds_path' for #<#<Class:0x00007f403e3162f0>:0x00007f403e3527a0>
Did you mean? user_crud_path
Extracted source (around line #8):
<h2>Create User</h2>
<%= form_for(#users) do |f| %>
<%= render(:partial => 'form', :locals => {:f => f}) %>
Rails.root: /home/ayan/Desktop/ror/q
Application Trace | Framework Trace | Full Trace
app/views/user_crud/new.html.erb:8:in `_app_views_user_crud_new_html_erb___1837862909000615994_69956949154020'
Request
Parameters:
None
Toggle session dump
_csrf_token: "DChk63/c8Jl1+JamtZiB03rFYMC/kybhyZr0/JixXDo="
flash: {"discard"=>[], "flashes"=>{"notice"=>"UserCrud updated successfully."}}
session_id: "aa128a12236d1d8d94061ed5de21d7f1"
Which is surprisingly some other request message , but the intended method call with url path helper "user_cruds_path" is not present-Its already corrected.
I have already restarted the server but same problem
Here is my controller class-
class UserCrudController < ApplicationController
def create
# Instantiate a new object using form parameters
#users = UserCrud.new(users_params)
##users=UserCrud.new(:name=>"Ayan",:email=>"ac#gmail.com")
# Save the object
if #users.save
# If save succeeds, redirect to the index action
flash[:notice] = "UserCrud created successfully."
redirect_to("/user_crud/index/")
else
# If save fails, redisplay the form so user can fix problems
render('new')
end
end
def new
#users = UserCrud.new({:name => 'Default',:email=>'deault#domain.ext'})
end
My view for new Record creation:(new.html.erb)
<% #page_title = "New User" %>
<%= link_to("<< Back to List", "/user_crud/", :class => 'back-link') %>
<div class="users new">
<h2>Create User</h2>
<%= form_for(#users) do |f| %>
<%= render(:partial => 'form', :locals => {:f => f}) %>
<div class="form-buttons">
<%= f.submit("Create User") %>
</div>
<% end %>
</div>
routees.rb
resources :user_crud do
member do
get :delete
end
end
Its aweird to see the error message referring to a path name that is not present at all-
Any help is very much appreciated.
The error is on the form, you are not specifying the correct path. When not setting this option, Rails will look for the "default path", which in this case is user_cruds_path
form_for(#users, url: "/user_crud/") works , but it would be great if anyone suggests what is the suitable url helper "/user_crud/"
Edit:
I got it from Marcelo's Comment and found the routes by command rails routes
And found the url helper to be user_crud_index_url for "/user_crud/"
So the final answer is:
form_for(#users,url: user_crud_index_url)

Missing required keys: [:id] - Routing to a view for each instance of my model with its unique ID

I'm working on a job board to learn/practice rails. I have a separate view for each job category, and I want the user to be able to click on a particular job posting to access another view with all of the listing's details. I've partially figured out the routing, as I'm able to go to localhost:3000/jobs/listing/7 for example to see the view for the listing of id:7. I'm having a problem linking to the /listing/:id from my job category view, and can only access a view for a specific id by typing it into the address bar. I've been working on this for days and checked out many related questions on here, but can't seem to figure it out. I believe I'm overlooking a simple, but critical detail, since i'm not very experienced with rails.
Here is my job category view:
<div class="cat-banner" id="developer">
<h1>Human Resources Jobs</h1>
<h4 class="listing-count"><%= Job.where(:category => 'Human Resources').count.to_s + ' Listings' %></h4>
</div>
<div class="category-page">
<!-- Full-Stack Developer Jobs -->
<div class="jobs-posted">
<hr class="other-line">
<% if Job.where(:category => 'Human Resources').count > 0 %>
<% #jobs.each do |job| %>
<div class="job">
<% if job.category == "Human Resources" %>
<span class="job-logo"><%= image_tag('salesforce.png', :size => "60x40") %></span>
<span id="line-align"><p class="title"><%= link_to job.title, listing_path(#job) %> </p>
<p class="name"><%= job.name %></p></span>
<% if job.location == "Remote" %>
<span class="remote-tag"><%= job.location.upcase %></span>
<% else %>
<p class="location"><%= job.location %></p>
<% end %>
<p class="kind"><%= job.kind %></p>
<p class="time-stamp"><%= job.created_at.strftime("%A, %B %d %Y") %></p>
<hr class="line">
<% end %>
</div>
<% end %>
<% else %>
<p>There are no human resources jobs at the moment...</p>
<p style="text-decoration: underline;"><%= link_to 'Post a Human Resources Job Here', new_job_path %></p>
<% end %>
</div>
<!-- End of Human Resources Jobs -->
</div>
And my routes.rb file:
Rails.application.routes.draw do
get 'subscribers' => 'subscribers#index'
get 'jobs/remote' => 'jobs#remote'
get 'jobs/listing/:id' => 'jobs#listing', as: 'listing'
get 'jobs/company' => 'jobs#company'
get 'jobs/dev' => 'jobs#dev'
get 'jobs/design' => 'jobs#design'
get 'jobs/marketing' => 'jobs#marketing'
get 'jobs/ai' => 'jobs#ai'
get 'jobs/fullstack' => 'jobs#fullstack'
get 'jobs/frontend' => 'jobs#frontend'
get 'jobs/backend' => 'jobs#backend'
get 'jobs/it' => 'jobs#it'
get 'jobs/software' => 'jobs#software'
get 'jobs/product' => 'jobs#product'
get 'jobs/cloud' => 'jobs#cloud'
get 'jobs/mobile' => 'jobs#mobile'
get 'jobs/database' => 'jobs#database'
get 'jobs/datascience' => 'jobs#datascience'
get 'jobs/blockchain' => 'jobs#blockchain'
get 'jobs/security' => 'jobs#security'
get 'jobs/support' => 'jobs#support'
get 'jobs/network' => 'jobs#network'
get 'jobs/finance' => 'jobs#finance'
get 'jobs/sales' => 'jobs#sales'
get 'jobs/hr' => 'jobs#hr'
get 'jobs/businessdev' => 'jobs#businessdev'
resources :jobs
root 'jobs#index'
get 'jobs/index'
get 'jobs/new'
And part of the Jobs Controller:
class JobsController < ApplicationController
def index
#jobs = Job.all
#subscribers = Subscriber.all
end
def new
#job = Job.new
end
def listing
#job = Job.find(params[:id])
end
def show
#job = Job.find(params[:id])
end
def create
#job = Job.new(params.require(:job).permit(:title, :url, :category, :kind, :location, :description, :name, :summary, :website, :email, :companyDescription, :headquarters ))
if #job.save
redirect_to root_path
else
render "new"
end
respond_to :html, :json
end
I've tried:
<%= link_to job.title, listing_path(#job) %>
<%= link_to job.title, listing_path(#job.id) %>
<%= link_to job.title, listing_path(#job[:id]) %>
<%= link_to job.title, listing_path %>
and other variations that I thought would work from reading StackOverflow
I believe my problem comes from this link_to statement (wrong syntax?), as everything looks alright in my jobs controller and routes file.
I either get:
No route matches {:action=>"listing", :controller=>"jobs", :id=>nil}, missing required keys: [:id],
or
undefined method `id' for nil:NilClass,
or
undefined method `[]' for nil:NilClass
The "missing required keys: [:id]" error is confusing me. I've checked out many resources trying to figure out how to pass the :id but can't solve it.
This is my first post on here and I REALLY appreciate any help and look forward to being knowledgeable enough in the future so I can answer questions too.
rake routes
The view that you are putting as example is using the instance variable #jobs. According with your controller that would be your index action where you don't have #job defined
Note: Notice on that view you are using #jobs and job on your iterator
So I think if you change #job for job that would fix the problem on that view
<%= link_to job.title, listing_path(job) %>
As the error clearly states, your path is expecting a job id but it is not getting one, so inspecting your call for the path, in line:
<span id="line-align"><p class="title"><%= link_to job.title, listing_path(#job) %> </p>
you are passing #job as param to the listing path.
change it to job so it would be:
<span id="line-align"><p class="title"><%= link_to job.title, listing_path(job) %> </p>
For the missing id, you should do:
<%= link_to job.title, listing_path(id: #job) %>
since your id is stored in the variable #job
It seems that the reason this isn't working is because you are not sending the correct argument to your link_to here:
<%= link_to job.title, listing_path(#job) %>
You have to remember that you are iterating through all your #jobs and calling each individual job with the job variable between the | |. You are trying to create a link to each individual job, so the correct link_to should be:
<%= link_to job.title, listing_path(job) %>
Where job is the individual job that the link_to is looking for.

GET errors in ruby on rails. Controllers and actions. CRUD

I'm working through the "Ruby on rails essential 3 training" on lynda.com. I'm at the point of creating forms and implementing CRUD. So far whenever I try to have rails initiate an action it can never find the definition.
To solve this problem I define the action within the controller and then create a GET within my routes.rb file. My instructor doesn't have to do this and his server starts just fine. I have a feeling I'm doing something wrong because my routes.rb file has far to many GET commands.
Please look at the following folders and let me know if you have an idea of why I have to make a GET every time I try a new action. I heard that when creating a Model there is supposed to be a GET created at the same time but that isn't happening and I'm not sure if I'm understanding that correctly. My subjects controller:
class SubjectsController < ApplicationController
def index
list
render('list')
end
# def index
# show
# render('show')
# end
def list
#subjects = Subject.order("subjects.position ASC")
end
def show
#subject = Subject.find(params[:id])
end
def new
#subject = Subject.new
end
def create
end
end
My new.html.erb file:
<%= link_to("<< Back to List", {:action => 'list'}, :class => 'back- link') %>
<div class="subject new">
<h2>Create Subject</h2>
<%= form_for(:subject, :url => {:action => 'create'}) do |f| %>
<table summary="Subject form fields">
<tr>
<th>Name</th>
<td><%= f.text_field(:name) %></td>
</tr>
<tr>
<th>Position</th>
<td><%= f.text_field(:position) %></td>
</tr>
<tr>
<th>Visible</th>
<td><%= f.text_field(:visible) %></td>
</tr>
</table>
<div class="form-buttons">
<%= submit_tag("Create Subject") %>
</div>
<% end %>
My config/routes.rb
Rails.application.routes.draw do
root :to=>"demo#index"
get 'demo/index'
get 'demo/hello'
get 'demo/other_hello'
get 'subjects/list'
get 'subjects/show'
get 'subjects/new'
get 'subjects/create'
Now having an issue with the Create portion of Crud when trying to create a test subject. Here is my SubjectsController.rb
class SubjectsController < ApplicationController
def index
list
render('list')
end
# def index
# show
# render('show')
# end
def list
#subject = Subject.order("subjects.position ASC")
end
def show
#subject = Subject.find(params[:id])
end
def new
#subject = Subject.new
end
def create
#subject = Subject.new(params[:subject])
if #subject.save
redirect_to(:action => 'list')
else
render('new')
end
end
end
Yes, you do not need to specify GET for CRUD actions. You can use Rails Resource Routing. For example, your subjects controller actions can be called in routes.rb like so:
resources: subjects
This will automatically route all the generic CRUD actions, and look for them in your controller. You can see whether rails "picked up" your routing by typing rake routes in the command line. You can also limit the number of actions you want by passing resources: subjects, only:[:destroy]. This tells Rails to only use the subjects#destroy action. There is more to it. I suggest reading through this RailsGuide for more information
Here's what your config/routes.rb should look like:
root 'subjects#index'
resources :subjects
get 'subjects/list' => 'subjects#list'
Normally, you have to create routes by yourself. They are not created with a model. Also, they are rather related to the controller than to the model.
I recommend to have a look at the official Rails routing guide here. Usually something like subjects/create is not a GET but a POST route. Your instructor might also have used a resources :subjects route instead of multiple GET/POST/etc. routes.
Slight variant of #OhHendrie's answer using only the resources dsl
root 'subjects#index'
resources :subjects do
get 'list', on: :collection
end

Search using like query in Ruby on Rails

I am a beginner in ROR development. I have navigation and I want search box on every page and when I enter some keyword, it should do like query across table's field. I tried using some online tutorials but could not do it.
My table name : tutorials
here is my search form on navigation bar
<li><%= link_to 'Login', :controller => 'access', :action => 'login' %></li>
<li><%= link_to 'Sign Up', :controller => 'users', :action => 'new' %></li>
<li><%= link_to 'Logout', :controller => 'access', :action => 'logout' %></li>
<div align="right">
<%= form_tag("/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
</div>
Here is my controller
class SearchController < ApplicationController
def show
#tutorial = Tutorial.find(params[:q])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #tutorial }
end
end
end
Here is my model
class Search < ActiveRecord::Base
def #tutorial.search(search)
if search
find(:all, :conditions => ['tutorial_name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
end
I am not sure how to do this. Please help
It's often true that a bad name indicates wrong thinking. I believe your name Search for the model is in this category. It should probably be called Tutorial, no? Search is something you do to a model, not the model itself.
If this guesswork is correct and the model is now called Tutorial and it has a field called name that is a string, then your model will be
class Tutorial < ActiveRecord::Base
def self.search(pattern)
if pattern.blank? # blank? covers both nil and empty string
all
else
where('name LIKE ?', "%#{pattern}%")
end
end
end
This makes the model "smart" on how to search through tutorial names: Tutorial.search('foo') will now return all tutorial records that have foo in their names.
So we can create a controller that uses this new functionality:
class SearchController < ApplicationController
def show
#tutorials = Tutorial.search(params[:q])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #tutorial }
end
end
end
The corresponding view must display the tutorials. Yours doesn't. The simplest way to do this is write a partial that renders exactly one tutorial. Say it's called _tutorial.html.erb.
Then in the view for Search, you need to add
<%= render :partial => #tutorials %>
to actually display the search results.
Addition
I'll build a little example.
# Make a new rails app called learning_system
rails new learning_system
# Make a new scaffold for a Tutorial model.
rails g scaffold Tutorial name:string description:text
# Now edit app/models/tutorial.rb to add the def above.
# Build tables for the model.
rake db:migrate
rails s # start the web server
# Now hit http://0.0.0.0:3000/tutorials with a browser to create some records.
<cntrl-C> to kill the web server
mkdir app/views/shared
gedit app/views/shared/_search_box.html.erb
# Edit this file to contain just the <%= form_tag you have above.
# Now add a header at the top of any view you like, e.g.
# at the top of app/views/tutorials/index.html.erb as below
# (or you could use the layout to put it on all pages):
<h1>Listing tutorials</h1>
<%= render :partial => 'shared/search_box' %>
# Make a controller and view template for searches
rails g controller search show
# Edit config/routes.rb to the route you want: get "search" => 'search#show'
# Verify routes:
rake routes
search GET /search/:id(.:format) search#show
tutorials GET /tutorials(.:format) tutorials#index
POST /tutorials(.:format) tutorials#create
new_tutorial GET /tutorials/new(.:format) tutorials#new
edit_tutorial GET /tutorials/:id/edit(.:format) tutorials#edit
tutorial GET /tutorials/:id(.:format) tutorials#show
PUT /tutorials/:id(.:format) tutorials#update
DELETE /tutorials/:id(.:format) tutorials#destroy
# Edit app/controllers/search_controller.rb as above.
# Create app/views/tutorial/_tutorial.html.erb with following content:
<tr>
<td><%= tutorial.name %></td>
<td><%= tutorial.description %></td>
</tr>
# Edit app/views/search/show.html.erb to have following content:
<h1>Show Search Results</h1>
<table>
<%= render :partial => #tutorials %>
</table>
Now try a little test. Fill in a search criterion and press the Search button.
On Rails 6 you can use for search action implementation in the controller.rb:
def search
keyword = params[:q] #you can get this params from the value of the search form input
#posts = Post.where("title LIKE ?", "%#{keyword}%")
end

AJAX Form Issues with Ruby on Rails

I'm sure this question has been asked before in a different context but I'm still so stuck with figuring out AJAX Rails and, I guess, Rails in general (kinda makes me wonder if I should just go back to PHP...). Well anyways I have this form that I want to AJAXify.
This is the "list" view which is part of the "subject" controller
<h1>Listing Subjects</h1>
<ul id="subject_list">
<% #subjects.each do |c| %>
<li><%= link_to c.name, :action => 'show', :id => c.id %></li>
<% end %>
</ul>
<p id="add_link"><%= link_to_function("Add a Subject",
"Element.remove('add_link'); Element.show('add_subject')")%></p>
<div id="add_subject" style="display:none;">
<%= form_tag(:action => 'create') do%>
Name: <%= text_field "subject", "name" %>
<%= submit_tag 'Add' %>
<% end %>
</div>
Code for my "subject" controller
class SubjectController < ApplicationController
def list
#subjects = User.find(:all)
end
def show
#subject = User.find(params[:id])
end
def create
#subject = User.new(params[:subject])
if #subject.save
render :partial => 'subject', :object => #subject
end
end
end
My "subject" partial
<li id="subject_<%= subject.id %>">
<%= link_to subject.name, :action => 'show', :id => subject.id %>
</li>
And the User is just a simple model I made that contains two columns "name" and "email".
How this code currently works is that when you click "Add", the textfield input is revealed. When you type something in the input and submit it, the "_show" partial is rendered in the create link. I was following a Rails 2.0 tutorial but I have 3.0 and I've read through some tutorials and they all mention ":remote => true" and jquery_ujs.js but I have no idea how to apply them to a "form_tag" rather than "form_for" Rails helper.
Basically I want to asynchronously add the element to the bottom of the list without a page load. I've really tried to understand absolutely all of the tutorials I could find but I just can't figure it out.
I believe that you'd better use some Unobtrusive JavaScript to tell to your app
and browser what exactly you want to render and how.
You want too much from simple render :partial => 'subject', :object => #subject line of code.
Here's my snippet that may be helpful to you.
# in the view (:remote => true for form_tag is not problem at all)
<%= form_tag({:controller => :products, :action => :empty_cart }, {:id => 'empty_cart', :remote => true}) do %>
<%= submit_tag 'Clear' %>
<% end %>
# in the controller (note that format.js section in the respond_to block)
def empty_cart
...
respond_to do |format|
format.html { redirect_to :root, :notice => 'Your cart is empty now' } # in the case of disabled JS support
format.js { render :js => "$('#empty_cart').fadeOut()" } # or you can place js code in the empty_cart.js.erb file and specify format.js here without the block
end
end
Check this article if I'm not clear enough.

Resources