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

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

Related

Paginate without a gem Next, Previous, buttons for Name.order(:id).limit(10).offset(0)

I've been searching for a solution to my problem for over a week now. I have an assignment that I lost 10 pts on due to no next/prev functionality and ran out of time. I still want to figure this out though.
I have Created a short single page site with rails generate scaffold Ripple name:string message:text url:string that shows an index of 10 most recent posts displays (name, message, created_on, link_to "show"). I still have to create a next, previous, newest, oldest links in view to show next 10, prev 10.... results. My code.
app\controllers\ripple_controller.rb
class RipplesController < ApplicationController
before_action :set_ripple, only: [:show, :update]
before_action :restrict_destroy_edit, only: [:edit, :destroy]
before_filter :set_page
helper_method :link_name_to_url, :next_button, :previous_button, :newest_button, :oldest_button, :is_next_page_available, :is_previous_page_available
RIPPLES_PER_PAGE = 10
def index
#ripples = Ripple.order(:id).limit(RIPPLES_PER_PAGE).offset(#page * RIPPLES_PER_PAGE)
end
#All my show, new, destroy, edit, create ....
def next_button
end
def previous_button
end
def newest_button
end
def oldest_button
end
def is_next_page_available?
end
def is_previous_page_available?
end
def set_page
#page = 5
end
private
...
\app\views\ripples.html.erb
<table>
<thead>
<tr>
<th>Name</th>
<th>Message</th>
<th>Posted</th>
<th>Show Ripple</th>
</tr>
</thead>
<tbody>
<% #ripples.each do |ripple| %>
<tr>
<td><%= link_name_to_url ripple %></td>
<td><%= truncate(ripple.message, length: 50) %></td>
<td><%= ripple.created_at.strftime("%B %d, %Y %l:%M %P") %></td>
<td><%= button_to 'Show', ripple, :method => "get" %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<div id = "nav">
<button><%= link_to 'Newest' %></button>
<button><%= link_to 'Previous 10 Ripples' %></button>
<button><%= link_to "Next 10 Ripples" %></button>
<button><%= link_to 'Oldest' %></button>
<button><%= link_to 'New Ripple', new_ripple_path, class: "button", method: :get %></button>
</div>
And I've tried calling methods in Model but keep getting undefined method "next" for #<Class:0xb4eabd0c> error on next and previous.
app\models\ripple.rb
class Ripple < ActiveRecord::Base
default_scope -> {order(created_at: :desc)}
validates :name, :message, presence: true
validates :url, allow_blank: true, format: {
with: URI::regexp(%w(http https)),
message: "Must be a url starting with http:// or https://"
}
def next
Ripple.order(:id).limit(10).offset((#page - 1) * 10)
end
def previous
Ripple.order(:id).limit(10).offset((#page + 1) * 10)
end
end
How would I implement next and previous using the order().limit().offset and maybe use #page to keep track of where I'm at in the ActiveRecord. Maybe something like
def next_button
#page -= 1
end
that I can call in index "<%= link_to Next 10" next_button %> either way I'm out of ideas that might work.
Thanks for any help.
A few things here. Firstly, controller methods should be used with matching routes. It's a request cycle where you click a button on your app, it makes a request to your server, then your server response with information.
Your next_button method is not going to work when you put it as a helper_method this way. In order to make your controller work, you should have routes that match to your controller method. Do a rake routes in your command line. You need to see something like this in your route.rb file
get 'ripple/next', 'ripple#next'
More about routes: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing
And in your controller, you could have
def next
page = params[:page]
params[:page] += 1
#ripples = Ripple.find_ripples_on_page(page, PAGE_SIZE)
render :index
end
Then in your erb view, your should be 'visiting' this particular route, not calling the method.
Secondly, you can not rely on class instance variable in your model. Instead, you should put it in your session or as part of a query string. As above controller code, I put it in session, which is a local storage. More about session: http://guides.rubyonrails.org/action_controller_overview.html#session
Finally, your model.
def find_ripples_on_page(page, PAGE_SIZE)
Ripple.where( ... ) # you can figure this out, depending on if you want to rank by updated_at or other filter
end
Side note: helper_method in controller is for retrieving information not giving command to the controller. All this macro does is use class_eval and 'copy' the method you have in controller into the renderer.
If you can use a gem, use will_paginate.
http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing
How about this?
class RipplesController < ApplicationController
before_filter :set_page, only: [:index]
RIPPLES_PER_PAGE = 10
def index
#ripples = Ripple.order(:id).limit(RIPPLES_PER_PAGE).offset(#page * RIPPLES_PER_PAGE)
end
private
def set_page
#page = params[:page].to_i || 0
end
end
Then in the view links will be
<div id = "nav">
<button><%= link_to 'Newest', ripples_path %></button>
<button><%= link_to 'Previous 10 Ripples', ripples_path(page: #page - 1) %></button>
<button><%= link_to "Next 10 Ripples", ripples_path(page: #page + 1) %></button>
</div>
All of this will be driven through the RipplesController#index method and will use URL's like /ripples?page=1, /ripples?page=2, etc.
These link_to calls will generate links with GET variables so that you can access them in the params hash and use them to drive the results returned.

Rails 4, params not received by controller

I'm trying to setup a post editing page, with links to edit and delete posts. I'm having troubles because my edit controller somehow doesn't seem to receive the parameters I'm sending. I've printed them out on the page to make sure they're there, and it displays correctly, but when I click the editlink I get ActiveRecord::RecordNotFound Couldn't find Article with 'id'= .
here's the view with the link: (article.id display correctly on this one)
<div id= "articles-index">
<div class = "row">
<% #article.each do |article| %>
<div class="row">
<div class = "container">
<p><%= link_to article.title, article %> | <%= article.updated_at.strftime("%e. %b %Y, %H:%M") %> | <%= link_to 'edit', articles_update_path(article.id) %> | delete</p>
<h2><%= article.id %></h2>
</div>
</div>
<% end %>
</div>
</div>
articles controller:(the ... is where I edited more code out as it's irrelevant to this question)
class ArticlesController < ApplicationController
...
def update
#article = Article.find(params[:id])
end
def manage
#article = current_user.articles
#article = current_user.articles.order('id DESC')
end
...
def article_params
params.require(:article).permit(:id, :title, :body, :user_id)
end
end
routes:
get 'articles/edit'
get 'articles/destroy'
get 'articles/manage'
get 'articles/show'
get 'articles/index'
get 'articles/new'
get 'articles/update'
get 'articles/search'
get 'articles/create'
get 'sessions/destroy'
get 'sessions/new'
get 'users/new'
resources :users
resources :sessions
resources :articles
GitHub Link
Clicking on a link instigates a GET request. I'm guessing the update action on your controller will be expecting the params to be a POST request.
The edit action normally handles GET.
The update action receives the POST request.
I was trying to find the DHH 2007 Keynote at Railsconf that covers this really well. If you can find it, watch it.
See CRUD Verbs and Actions at Rails Guides
UPDATE: Just seen your routes file. If it's a GET request then just put the URL directly into the browser. e.g.
articles/update?id=123
See if params[:id] == 123 in the controller. It should be.
UPDATE 2
articles_update_path(id: article.id)
Will generate a params has with an :id key but fundamentally you shouldn't be calling update actions with a GET route.

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

rails 4 : use of form_for , no route, POST

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).

Why is there a . instead of a / in my URL path in ROR [duplicate]

This question already has answers here:
Path helpers generate paths with dots instead of slashes
(3 answers)
Closed 8 years ago.
I have installed Devise (called members) and have set up some custom routes so that I can spit out all the members and allow them to have their own page (/memebers/ and /members/:id/)
However in my view file for the members index when passing the route members_path(member_id) it is outputting members.1 instead of members/1
Code below:
index view:
<% #members.each do |member| %>
<tr>
<td><%= link_to member.name, members_path(member.id) %></td>
<td><%= member.email %></td>
<td><%= member.id %></td>
<td><%= member.admin %></td>
</tr>
<% end %>
Routes:
devise_for :members
match 'members/' => 'members#index'
match 'members/:id' => 'members#show'
Members Controller:
class MembersController < ApplicationController
def index
#members = Member.all
respond_to do |format|
format.html # show.html.erb
format.json { render json: #student }
end
end
def show
#member = Member.find(params[:id])
end
end
Rake Routes:
members /members(.:format) members#index
/members/:id(.:format) members#show
Any help? Cheers :)
Had to add
, as: :show_member
so in my routes file I had to define a path for that
match 'members/:id/' => 'members#show', as: :show_member
and adding this in the index view file:
show_member_path(member)
and rake routes is now:
members GET /members(.:format) members#index
show_member GET /members/:id(.:format) members#show
Cheers :)
This is not the helper method you are looking for. members_path creates a path to members#index and takes format as its only argument.
The helper that links to members#show is member_path. Although, it's not automatically created by match:
# config/routes.rb
resources :members, only: [:index, :show]
# Remove `only` to get the remaining restful routes
# and in your view:
<%= link_to member.name, member_path(member.id) %>
You can also just pass in the member itself and link_to will automatically call to_param, which returns id by default:
<%= link_to member.name, member_path(member) %>
Alternatively, you can just pass in your member directly with no path helper:
<%= link_to member.name, member %>
Read more here: http://guides.rubyonrails.org/routing.html

Resources