Rails 4: Routing and sharing actions between controllers? What's the proper convention? - ruby

I'm building a Todo list in rails trying to learn how it all works. I'm having an issue with calling the action of a controller from the view of another controller.
I have a TodoList has_many TodoItem and TodoItem belongs_to TodoList.
TodoList#Model
has_many :todo_items
def has_completed_items?
todo_items.complete.size > 0
end
def has_incompleted_items?
todo_items.incomplete.size > 0
end
TodoItem#Model
belongs_to :todo_list
scope :complete, -> { where("completed_at is not null") }
scope :incomplete, -> { where(completed_at: nil) }
def completed?
!completed_at.blank?
end
Routes
resources :todo_lists do
resources :todo_items do
member do
patch :complete
end
end
end
Patch generates this route:
complete_todo_list_todo_item PATCH
/todo_lists/:todo_list_id/todo_items/:id/complete(.:format)
todo_items#complete
Now I can call this path from my TodoItem views without a problem like this: <%= link_to "Mark", complete_todo_list_todo_item_path(todo_item), method: :patch %>
After adding complete/incomplete scope to my TodoItem I added the following to my TodoList show view:
TodoList#Show
<% #todo_list.has_incompleted_items? %>
<% #todo_list.todo_items.incomplete.each do |item| %>
<li><%= item.content %></li>
<% end %>
This is properly displaying the incomplete items, so I tried to put a link to mark the item complete from within this view like this:
<% #todo_list.has_incompleted_items? %>
<% #todo_list.todo_items.incomplete.each do |item| %>
<li><%= item.content %>
<%= link_to "Mark", complete_todo_list_todo_item_path(item), method: :patch %></li>
<% end %>
Now this is where I get the following error:
NoMethodError in TodoLists#show undefined method
`complete_todo_list_todo_items_path' for #<#:0x000001071cf540>
Why is this complete#action undefined from my TodoList#show view?
What is the proper convention to share actions between controllers?
Thank you for your help.

I was able to solve this problem by removing the member block from the routes file:
resources :todo_lists do
resources :todo_items do
patch :complete
end
end
Now my action route was updated to: todo_list_todo_item_complete
So from my view I had to call the new route with it's nested resource:
link_to "Mark", todo_list_todo_item_complete_path(#todo_list, item), method: :patch
Lastly I had to update my controller to locate the proper todo_item#id in the complete block:
#todo_item = #todo_list.todo_items.find(params[:todo_item_id])

Related

Missing Params on Post in Rails?

I'm learning Ruby on Rails at the moment and we're making a blog app to learn about crud actions and such and I'm stuck on this create method in my controller not working as it does in the course. I'm having trouble the create method in this controller:
class ArticlesController < ApplicationController
def show
#article = Article.find(params[:id])
end
def index
#articles = Article.all
end
def new
end
def create
#article = Article.new(params.require(:article).permit(:title, :description))
#article.save
redirect_to #article
end
end
I get this error when trying to create an article:
ActionController::ParameterMissing in ArticlesController#create
param is missing or the value is empty: article
It seems to be getting hung up on the first line of the create method but I'm not sure why it doesn't think there's an article... Here's my new article view as well for further reference:
<h1>Create a new Article</h1>
<%= form_with scope: #article, url: articles_path, local: true do |f| %>
<p>
<%= f.label :title %><br/>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br/>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Check whether your parameters coming under hash with key article like this -
{"article"=>{params}}
Also initiate Article object in new action.
You can redefine the params to permit like this:
in controller new method create object
#article = Article.new
and in form HTML add one more option use method: "post"

Not showing created blog entries

I'm fairly new to Rails and learning to create a blog using this tutorial. On step 10, once I define create and show, after creating a new post in browser I don't see any entries on show with id page. All I see is heading and and blank title and post header.
Following is my controller -
class PostController < ApplicationController
def index
end
def new
end
def create
#post = Post.new(params[:posts])
#post.save
redirect_to #post
end
def show
#post = Post.find(params[:id])
end
end
Show view ---
<h1>Show a post</h1>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Text:</strong>
<%= #post.text %>
</p>
Route ---
RailsBlog::Application.routes.draw do
resources :post
root :to => "post#index"
end
Form ---
<%= form_for :post, url: {action: 'create'} do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
May be this is just a spelling mistake, but since I've recently started learning Rails, I'm unable to resolve this.
Update: I can go to particular id using
http://localhost:3000/post/1
but am only seeing blank page with view headers
The problem is here:
#post = Post.new(params[:posts])
It should be params[:post] - singular, not plural.
Also note that the best practice with form_for is to pass an object instead of symbol:
form_for #post do |f|
Then:
You don't need to specify url
You can reuse the same form for an edit action or create action (if object creation failed due to failing validation)
This however requires to initialize new object in your new action:
def new
#post = Post.new
end
UPDATE:
Also your routes are incorrect. When defining plural resources, you need to use plural form (it's more the convention than requirement, but since you're learning stick with it). So change your routes to:
resources :posts
And rename your controller to PostsController (remember to rename file name as well). restart the server and all should work.
ANOTHER UPDATE:
You also need to rename folder app/views/post to app/view/posts.
AND YET ANOTHER UPDATE:
In rails 4, you are not allowed to mass assign any params which has not been whitelisted using strong parameters. You need to tell rails which fields you allow to be assigned first - this is a security thing. You need to make some changes to your controller:
class PostsController < ApplicationController
...
def create
#post = Post.new(post_params)
...
end
...
private
def post_params
params.require(:post).permit(:title, :text)
end
end
This is the way to tell your controller that you are expecting those attributes from your form and they can be safely assigned.
I had just similar problem on the same tutorial.
The code spelling was correct and clearly accorded to examples in tutorial and BroiSatse's answer above.
The mistake was in order of private method definition.
How it was:
...
private
def post_params
params.require(:post).permit(:title, :text)
end
def show
#post = Post.find(params[:id])
end
...
The working order:
def show
#post = Post.find(params[:id])
end
private
...
Anyway, this topic was rather helpful. Thak you for your answers!

Clarification on accessing methods

I am having trouble accessing a method i have written in a class, the problem is im trying to access it within an instance of that class, but would appreciate someone explain in a bit more details as to why this doesn't work and things to look at to get a solution.
I have created a simple helper method to use in my view to join two attributes
class TeamMember < ActiveRecord::Base
def fullname
"#{self.forename} #{self.surname}"
end
end
Within my view (show action) I want to be able to use this method
def show
#team_member = TeamMember.find(params[:id])
end
So doing this for example gives me an undefined method
<%= link_to fullname(#team_member) %>
OR
<% #team_member.each do |t| %>
<%= link_to fullname, t %>
<% end %>
Whereas this works
<% #team_member.each do |t| %>
<%= link_to "#{t.forename} #{t.surname}", t %>
<% end %>
Could someone help to clarify this for me so that i can learn from it please
You defined fullname method in your TeamMember class, but you try to call this method with implicit receiver in view, which is ActionView::Base instance. Instead, you should use explicit receiver, which must be TeamMember instance:
<%= link_to #team_member.fullname, #team_member %>
and:
<%= link_to t.fullname, t %>

Rails 3.1 Ajax forms don't show validation

I'm using updated pre Ruby on Rails on Ruby 1.9.2 (with rvm), and made a new test application with
$ rails generate scaffold Project name:string
and
class Project < ActiveRecord::Base
validates_presence_of :name
end
I change
<%= form_for #project do |f| %>
to
<%= form_for #project, :remote => true do |f| %>
I can now still (without any changes in the controller) add new items to the project. If I try to add with empty in the name field, it will not add anything (validates_presence_of :name stops this), but I don't get any validation error messages. I have tried the same on an application converted from 3.0 I'm working on with same results. Here I had:
class KursController < ApplicationController
# GET /kurs
# GET /kurs.xml
respond_to :js, :html
and:
def update
#kur = Kur.find(params[:id])
#kur.update_attributes(params[:kur])
flash[:notice] = "Lagret" if #kur.save
respond_with( #kur, :layout => !request.xhr? )
end
In 3.1 I get no validation error messages. Is this because of a bug in Ruby on Rails 3.1 or something I should do different?
You are missing to call these errors. Do something like this:
<%= form_for #project do |f| %>
<%= render :partial => 'my_error_messages_for', :locals => {:collection => #project} %>
In "_my_error_messages_for.html.erb":
<% if collection.errors.any? %>
<ul>
<% collection.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
You can create a helper for this work.
I don't believe that Ruby on Rails will automatically include the partial that you've requested. You'll need to add some JavaScript code to get it to work correctly. Something like this I imagine:
// Application.js
$("form").bind("ajax:success", function (event, data, status, xhr) {
$(event.target).replaceWith(data);
});

Why form_for use incorrect REST url_helpers for namespaced controller?

In my rails app have a partial that contains a form shared between the new and edit action:
<%= form_for #customer do |f| %>
....
<% end %>
These action are of a controller (called customers) namespaced (called admin), if try to run the code show the error when execute form_for:
undefined method `customer_path'
Have resolved this using:
<%= form_for :customer, #customer do
|f| %>
....
<% end %>
Now the form is generated with correct url when is called by new action but when generated by edit the form url is "/admin/customers/1/edit" instead of update. If submit the form show the error:
No route matches "/admin/customers/1/edit"
but in routes.rb have:
namespace :admin do
resources :customers
end
and rake:routes show all the REST urls:
admin_customers GET /admin/customers(.:format) {:action=>"index", :controller=>"admin/customers"}
POST /admin/customers(.:format) {:action=>"create", :controller=>"admin/customers"}
new_admin_customer GET /admin/customers/new(.:format) {:action=>"new", :controller=>"admin/customers"}
edit_admin_customer GET/admin/customers/:id/edit(.:format){:action=>"edit",:controller=>"admin/customers"}
admin_customer GET /admin/customers/:id(.:format) {:action=>"show",:controller=>"admin/customers"}
PUT /admin/customers/:id(.:format) {:action=>"update", :controller=>"admin/customers"}
DELETE /admin/customers/:id(.:format) {:action=>"destroy",:controller=>"admin/customers"}
Any idea?
Try this
<%= form_for [:admin, #customer] do |f| %>

Resources