Nested resources create Couldn't find Topic without an ID - ruby-on-rails-3

I wanna make an application which User can create a topic and others can make posts after that. I nested my resources in my routes.rb:
MyPedia2::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :topics, only: [:show, :create, :destroy]
resources :posts
resources :topics do
resources :posts, only: [:create, :show, :new]
end
In my topic show page , I want to show topic.title and sended Posts and post.form.html.erb.
Everything works accept when i create a post , I get mistake
ActiveRecord::RecordNotFound in PostsController#create
Couldn't find Topic without an ID..
This is my posts_controller.rb:
class PostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
before_filter :correct_user, only: :destroy
def new
#topic= Topic.find_by_id(params[:id])
#post = #topic.posts.build(params[:post])
end
def show
#topic = Topic.find(params[:id])
#posts = #topic.posts.paginate(page: params[:page])
end
def create
#topic = Topic.find(params[:id])
#post = #topic.posts.build(params[:post])
#post.topic = #topic
if #post.save
flash[:success] = "Konu oluşturuldu!"
redirect_to :back
else
render 'static_pages/home'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def correct_user
#post = current_user.posts.find_by_id(params[:id])
redirect_to root_path if #post.nil?
end
end
and _post_form.html.erb:
<%= form_for #new_post do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "yorumunuzu girin..." %>
</div>
<%= f.submit "Gönder", class: "btn btn-large btn-primary" %>
<% end %>

There are a few things that should solve things for you.
First and foremost, your create action in the posts controller is a bit wrong - it should look something like this:
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.build(params[:post])
# This is unnecessary as you're already adding
# the post to the topic with the build statement.
# #post.topic = #topic
if #post.save
flash[:success] = "Konu oluşturuldu!"
redirect_to :back
else
render 'static_pages/home'
end
end
This controller action assumes that you're going to use a put request to a post resource that is nested in topics, so you'll have to clean up your routes.
You have routes to posts#create both nested and unnested.
If posts are ALWAYS supposed to be nested within a topic, which your controller logic is stating, then you should add this to the unnested posts resource:
resources :posts, except: [:new, :create]
and then change that form_for tag to this:
<%= form_for [#topic, #post] do |f| %>
This tells the form builder that you're using a nested resource and will use the correct url for the http request.
Also - it looks like you're using loading all of your topics using Topic.find(params[:id]). This isn't going to work - you're in the posts controller, this is a post id. You should be loading posts with the id param like this: Post.find(params[:id]) and then the topic like this: topic = post.topic

Related

Nested routes and dot in url in Rails

I'm building a simple todo app for some practice. I have projects which has_many tasks and tasks belongs_to projects.
So that I can display url/projects/1/tasks I'm nesting the route:
routes.rb
resources :projects do
resources :tasks
end
In my project show view I have the following form:
Add a task:
<%= form_for [#project, #task] do |f| %>
<%= f.label :Task_name %>
<%= f.text_field :name, :placeholder => "Task Name" %>
<%= f.submit 'Create Task' %>
<% end %>
In my tasks controller I'm doing the following:
def create
#project = Project.find(params[:project_id])
#task = #project.tasks.new(params[:task])
if #task.save
redirect_to projects_path(#project)
else
redirect_to projects_path(#project)
end
end
It seems on task.save when I redirect and pass in the project instance variable it redirects me to http://todoapp.dev/projects.5 (5 being the id of the project) instead of http://todoapp.dev/projects/5.
Could the problem be in my controller with the redirect_to method or possibly the nested route?
I have a basic understanding of Rails routing but could use some advice.
Looks like in the redirect_to in my controller I was using the pluralized version of the path. projects_path(#project) instead of project_path(#project).
def create
#project = Project.find(params[:project_id])
#task = #project.tasks.new(params[:task])
if #task.save
redirect_to project_path(#project)
else
redirect_to project_path(#project)
end
end
This ended up working.

link_to post ID in Rails

In my feed I have this:
<span class="title"><strong><%= link_to feed_item.title, #micropost %></strong></span><br>
I cannot figure out how to make it link to the individual pages for posts.
Right now it links to: http://localhost:3000/microposts and I get an error: No route matches [GET] "/microposts"
if I manually type the URL: http://localhost:3000/microposts/25 I can see the indivual page for the post.
This works fin linking to a user profile, but I can't get the link working to a micropost's page.
<%= link_to feed_item.user.name, feed_item.user %>
I'm new to rails and I'm trying to figure this out. Any help would be appreciated.
microposts_controller.rb
class MicropostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
before_filter :correct_user, only: :destroy
def index
end
def show
#micropost = Micropost.find(params[:id])
end
def create
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#micropost.destroy
redirect_to root_url
end
private
def correct_user
#micropost = current_user.microposts.find_by_id(params[:id])
redirect_to root_url if #micropost.nil?
end
end
config/routes.rb
SampleApp::Application.routes.draw do
resources :users do
member do
get :following, :followers
end
end
resources :sessions, only: [:new, :create, :destroy]
resources :microposts, only: [:create, :destroy, :show]
resources :relationships, only: [:create, :destroy]
root to: 'static_pages#home'
match '/signup', to: 'users#new'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
match '/help', to: 'static_pages#help'
match '/about', to: 'static_pages#about'
match '/contact', to: 'static_pages#contact'
end
_feed_item.html.erb
<li id="<%= feed_item.id %>">
<%= link_to gravatar_for(feed_item.user), feed_item.user %>
<span class="title"><strong><%= link_to feed_item.title, micropost_path(#micropost) %></strong></span><br>
<span class="user">
<p><small>Created by: <%= link_to feed_item.user.name, feed_item.user %><br>
<%= feed_item.loc1T %><br>
<%= feed_item.startTime.strftime('%A, %B %d, %Y') %></small></p>
</span>
<span class="content"><%= feed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
</span>
<% if current_user?(feed_item.user) %>
<%= link_to "delete", feed_item, method: :delete,
data: { confirm: "Are you sure?" },
title: feed_item.content %>
<% end %>
</li>
feed.html.erb
<% if #feed_items.any? %>
<ol class="microposts">
<%= render partial: 'shared/feed_item', collection: #feed_items %>
</ol>
<%= will_paginate #feed_items %>
<% end %>`
static_pages_controller
class StaticPagesController < ApplicationController
def home
if signed_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
end
def about
end
def contact
end
end
Long answer
Rails generates a number of handy named routes for you when you add routes to your routes.rb file. Usually when in doubt for routes I take a look at my rake routes task which shows you a list of all available routes. Try running rake routes > routes.txt and open up the corresponding routes.txt file.
The resulting file will list out a series of requests for you, in your case you should see something similar to this for your microposts controller:
POST /microposts(.:format) microposts#create
micropost GET /microposts/:id(.:format) microposts#show
DELETE /microposts/:id(.:format) microposts#destroy
Rake routes produces the following information for each of your routes (where applicable):
The route name (if any)
The HTTP verb used (if the route doesn’t respond to all verbs)
The URL pattern to match
The routing parameters for the route
With that information in mind be can simply look at the urls provided in the routes.txt file for the url we're trying to get to (/microposts/25). You'll see that the listed /microposts/:id(.:format) url pattern handles that perfectly. Lo and behold it also maps to the microposts#show action that you want so now to get the named route just look at the first column to appear and you'll see the "microposts" keyword. Simply add _path` to this and you'll have your named route usable in views to generate link urls. Since this particular route requires an id parameter (as detailed in the url pattern) you have to pass the named route helper and id argument as well.
Also in your routes.rb file when you add resources :something it generates routes for each of the default seven RESTful routes (new, create, edit, update, delete, index, show). In your case you're explicitly telling rails to generate default routes for the actions create, destroy and show so you can erase the line at the bottom
match "/microposts/:id" => "microposts#show" because that's already being handled.
Short answer
Change this:
<%= link_to feed_item.title, #micropost %>
To this:
<%= link_to feed_item.title, micropost_path(feed_item) %>
See Ruby on Rails Guides: Rails routing from the Outside In for all you need to know about routes.

Rails Routing Redirects to wrong path

I have a view called, "clients" which shows a list of calls from the Call database. That works fine. However when I added a new button with a form behind it the call will be created but it redirects to calls_path instead of clients_path.
I have no idea why it's doing this, my only theory is that I'm working with actions that touch data outside of the clients_controller and somehow Rails is defaulting to the calls_path. The same thing happens on my delete action. Can someone help me make sense of this?
calls_controller
def new
#call = Call.new :call_status => "open"
respond_with #call
end
def create
#call = Call.new(params[:call])
if #call.save
redirect_to clients_path, notice: "Call was successfully created."
else
render :new
end
end
def destroy
#call = Call.find(params[:id])
#call.destroy
respond_to do |format|
format.html { redirect_to clients_index_path }
format.json { head :no_content }
end
end
_form.html.erb
<%= form_for(#call) do |f| %>
<%= f.label :caller_name %>
<%= f.text_field :caller_name %>
<%= f.label :caller_phone %>
<%= f.text_field :caller_phone, :placeholder => 'xxx-xxx-xxxx' %>
<%= f.label :caller_email %>
<%= f.text_field :caller_email %>
<%= f.button :submit %>
<% end %>
routes.rb
devise_for :users
match 'mdt' => 'mdt#index'
get "home/index"
resources :medics
resources :clients
resources :users
resources :units
resources :mdt do
collection do
put "in_service"
put "en_route"
put "to_hospital"
put "at_hospital"
put "on_scene"
put "out_of_service"
put "at_station"
put "staging"
put "at_post"
put "man_down"
end
end
resources :calls do
member do
post 'close'
end
end
root :to => 'home#index'
devise_scope :user do
get "/login" => "devise/sessions#new"
delete "/logout" => "devise/sessions#destroy"
end
The problem I had was with routes. I needed a post method for a new calls. After creating two actions (one for create and one for destroy) along with their routes everything started working. It looks like it was trying to use the default routes and actions which would re-route to the wrong URL for my purposes.
resources :clients do
collection do
post "calls"
end
member do
delete "cancel"
end
end
def calls
#call = Call.new(params[:call])
if #call.save
redirect_to clients_path, notice: "Call was successfully created."
else
render :new
end
end
def cancel
#call = Call.find(params[:id])
#call.destroy
respond_to do |format|
format.html { redirect_to clients_path }
format.json { head :no_content }
end
end

Nested model form not working properly - need to pinpoint POST/GET redirect

I have a nested model form that isn't functioning properly. The POST is to the proper place, but then the GET reroutes me. So I'm wondering if anyone can help explain what I'm doing wrong.
I have two models: User and Profile. Code for them below:
User:
class User < ActiveRecord::Base
attr_accessor :password, :email
has_one :profile, :dependent => :destroy
accepts_nested_attributes_for :profile
...
end
Profile:
class Profile < ActiveRecord::Base
attr_accessible :first_name, :last_name, etc.
belongs_to :user
accepts_nested_attributes_for :user
...
end
New/Create from both models:
class UsersController < ApplicationController
def new
#user = User.new
if logged_in?
redirect_to current_user.profile
end
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to signup_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
class ProfilesController < ApplicationController
def new
#profile = Profile.new
end
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to profile_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
def index
#profile = current_user.profile
end
My signup (two step process) mixes the models, so as I said I'm using a nested model form in my Users new.html.erb file. Code form_for and f.fields_for below:
<%= form_for(:user, :url => signup_path, :html => {:id => 'homepage'}) do |f| %>
<%= f.fields_for :profile do |f| %>
Now when I enter data into the form, my routes.rb file seems to POST to the proper place (/signup so profile can be filled out further), but GET routes me to /login.
Routes.rb:
match '/login' => "sessions#new", :as => "login"
match '/signup' => 'profiles#new', :as => "signup"
match 'skip/signup', :to => 'info#signupskip'
match 'skip/profiles/new', :to => 'profiles#newskip'
root :to => 'users#new'
resources :users
resources :profiles
In rails server:
Started POST "/signup" for 127.0.0.1 at Sun Aug 28 19:54:11 -0400 2011
Processing by ProfilesController#new as HTML
Started GET "/login" for 127.0.0.1 at Sun Aug 28 19:54:11 -0400 2011
Processing by SessionsController#new as HTML
Rendered sessions/new.html.erb within layouts/application (32.1ms)
I'm wondering if the problem is in my layouts/application file, specifically this code:
<% if logged_in? %>
<%= render 'layouts/header_in' %>
<% else %>
<%= render 'layouts/header_out' %>
<% end %>
Can anyone help explain to me what I'm doing wrong?
UPDATE:
I deleted the if/else argument in `layouts/application' and it was still redirected. So I'm back to wondering what's going on.
I believe your problem has to do with an inherent issue (though arguably not problem) with HTTP protocol. You cannot return a redirect to a POST request. Alternatives include calling the other method from within the first controller action, or rendering the correct page directly from that action, or some mix of both.

CanCan polymorphic resource access problem

i don't quite understand how to restrict access to links in this particular case with CanCan. I always get "Edit" link displayed.
So i believe the problem is in my incorrect definition of cancan methods(load_ and authorize_).
I have CommentsController like that:
class CommentsController < ApplicationController
before_filter :authenticate_user!
load_resource :instance_name => :commentable
authorize_resource :article
def index
#commentable = find_commentable #loading our generic object
end
......
private
def find_commentable
params.each { |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.includes(:comments => :karma).find(value)
end }
end
end
and i have in comments/index.html.erb following code that render file from other controller:
<%= render :file => "#{get_commentable_partial_name(#commentable)}/show.html.erb", :collection => #commentable %>
you can think about "#{get_commentable_partial_name(#commentable)}" like just "articles" in this case.
Content of "articles/show.html.erb":
<% if can? :update, #commentable %>
<%= link_to 'Edit', edit_article_path(#commentable) %> |
<% end %>
my ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.role? :admin
can :manage, :all
elsif user.role? :author
can :read, [Article, Comment, Profile]
can :update, Article, :user_id => user.id
end
end
end
i have tried debug this issue like that
user = User.first
article = Article.first
ability = Ability.new(user)
ability.can?(:update, article)
and i always get "=> true" in ability check
Note: user.role == author and article.user_id != user.id
if you need more information please write
thank's for your time && sorry for my english
okay i figure it out, redetermined rules in ability.rb so now order is like guest->author->moderator->admin and problem is solved. I believe root of problem was in cancan logic which assumes that i need to redefine rules or do it in order i've show before