I'm making my first Rails application and mistakes abound. I've searched for "no route matches" questions, but I couldn't find one that fit my problem.
I have a User model. It has_many :tasks and it has_one :pet. Both Task and Pet models belong_to :user. Task and Pet have no pages of their own, they appear on User's page. With Task, this works fine. Here's the show action in users_controller.rb
def show
#user = User.find(params[:id])
#tasks = #user.tasks.paginate(page: params[:page])
#pet = #user.pet
end
I have a feed of #tasks on the #user show.html.erb page, as well as a form for creating tasks. I also have a form for creating a #pet. However, when I click the submit button on the #pet creation form, it goes to an error page.
Routing Error. No route matches [PATCH] "/pet/1"
Strangely, looking at the routes, I see this:
tasks POST /tasks(.:format) tasks#create
task DELETE /tasks/:id(.:format) tasks#destroy
pet_index POST /pet(.:format) pet#create
pet DELETE /pet/:id(.:format) pet#destroy
root GET / static_pages#home
pet#create has the address pet_index, even though I certainly did not do that on purpose. A user only has one pet, a pet index would make no sense. I created the Pets model in the same way I did the Tasks model, but somewhere, something went wrong. The differences between the two have to do with the fact that there are several #tasks that are created and destroyed, while the pet is just one.
Here is pets_controller.rb
class PetsController < ApplicationController
before_action :signed_in_user
def create
#pet = current_user.build_pet(pet_params)
if #pet.save
flash[:success] = "Your pet is ready!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
private
def pet_params
params.require(:pet).permit(:nome)
end
end
I also have a shared/_pet.html.erb partial which contains code that can't access the user.
<%= #user.pet.nil? ? "Pet name" : #user.pet.name %>
If I put
<%= render 'shared/pet' %>
in home.html.erb, it gives me this error:
NoMethodError in StaticPages#home. undefined method `pet' for
nil:NilClass
It's a mess. I appreciate any help.
EDIT: Here's the pet creation form, in shared/pet_form.html.erb
<%= form_for(#pet) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :name, placeholder: "Give your pet a name." %>
</div>
<%= f.submit "Meet pet", class: "btn btn-small btn-default" %>
<% end %>
EDIT 2: Here's config/routes.rb
Petrefa::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :tasks, only: [:create, :destroy]
resources :pet, only: [:create, :destroy]
root 'static_pages#home'
match '/register', to: 'users#new', via: 'get'
match '/help', to: 'static_pages#help', via: 'get'
match '/developers', to: 'static_pages#about', via: 'get'
match '/login', to: 'sessions#new', via: 'get'
match '/logout', to: 'sessions#destroy', via: 'delete'
end
EDIT 3: After having changed form_for(#pet) do into form_for(#pet, method: :create, action: '/pet') do this is the resulting HTML
<form accept-charset="UTF-8" action="/pet/1" class="edit_pet" id="edit_pet_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"><input name="_method" type="hidden" value="create"><input name="authenticity_token" type="hidden" value="fC/PjvsGPG1sKSGvzFSFfzSKAhUZv99w6fkmiRkLRU0="></div>
<div class="field">
<textarea id="pet_nome" name="pet[nome]" placeholder="Dê um nome para o seu petRefa.">fulano</textarea>
</div>
<input class="btn btn-small btn-default" name="commit" type="submit" value="Conhecer pet">
</form>
First, if you want to create a pet, you should use POST and not PATCH request. A POST request will be redirected to the create method, whereas a PATCH request will be redirected to the update method, which doesn't exist and has no route for it in your PetsController. You can specify which action (like POST or PATCH) to use in the HTML form.
Secondly, are you sure that you set #user in StaticPages#home? That could be the reason for the undefined method `pet' for nil:NilClass`.
Also, I think that to render a partial, you should use <%= render partial: 'shared/pet' %>
Related
I'm using the mailboxer gem to build a messaging system between users for my rails application. For some reason, I'm getting this error:
(undefined method `receipts_for' for nil:NilClass)
Maybe it's because I should define 'receipts_for' somewhere in my controller or in the mailboxer.rb? I tried a few things.. but unfortunately none of them were successful.
This is my routes.rb:
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
get 'search/index'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users do
member do
get :following, :followers
end
end
resources :conversations do
resources :messages
end
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
resources :searches
end
This is my conversations controller:
class ConversationsController < ApplicationController
def show
#conversation = current_user.mailbox.conversation.find(params[:id])
end
def new
#recipients = User.find(params[:user_id])
end
def create
recipient = User.find(params[:user_id])
receipt = current_user.send_message(recipient, params[:body])
redirect_to conversation_path(receipt.conversation)
end
end
This is my messages_controller:
class MessagesController < ApplicationController
before_action :set_conversation
def create
receipt = current_user.send_message(#conversation, body)
redirect_to receipt.conversation
end
private
def set_conversation
#conversation = current_user.mailbox.conversations.find(params[:conversation_id])
end
end
The messaging system I'm building precisely, is a messaging system where the 'current_user' can go to the profile page of any user on the application and message him/her via the profile page. So this means that I'm rendering show.html.erb of the conversation model inside the show.html.erb of my users model. Here is the code of both views:
show.html.erb - Users Model:
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
<section class="stats">
<%= render 'shared/stats' %>
</section>
<h6>
<%= #user.gender %>
</h6>
<%= render "conversations/conversation" %>
</aside>
<div class="col-md-8">
<%= render 'follow_form' if logged_in? %>
<% if #user.microposts.any? %>
<h3>Microposts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<%= render #microposts %>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
</div>
_conversation.html.erb - show view of the conversations model.
<% #conversation.receipts_for(current_user).each do |receipt| %>
<div>
<%= receipt.message.body %>
</div>
<% end %>
<%= form_tag conversation_messages_path(#conversation), method: post do %>
<div>
<%= text_area_tag :body %>
</div>
<%= submit_tag %>
<% end %>
So how exactly can I define the 'receipts_for' method for my mailboxer? Or is something else wrong with my code?
Any help would be much appreciated!
Thanks!
I’ll piggy-back on what #mu is too short said. It’s important to pay attention to the whole error message. “undefined method receipts_for for nil:NilClass” is telling you that the receiver of the receipts_for method is nil. #conversation.receipts_for(current_user) looks to be the only place where receipts_for is used, so I would start debugging by making sure #conversation is assigned the value of an existing ActiveRecord-backed conversation object.
There seems to be too a lot going on, so I don't know how to offer you a quick fix. The value assignment in your ConversationsController#show method, #conversation= current_user.mailbox.conversation.find(params[:id]) looks cumbersome. That suggests to me that you're looking for a conversation based on a mailbox belonging to the current_user, which might be what you want (in which case, you need to have the appropriate associations defined in your models).
However, since the chain ends with conversation.find(params[:id]), I'm guessing that current_user.mailbox is not needed. Alternatively, if your params don't actually have a conversation id, then maybe that's what you need to focus on.
The good news is you can probably figure out how to define #conversation if you stick byebug (or binding.pry, depending on what you have installed) at the top of your show method and in your view partial:
# In the controller:
def show
binding.pry
#conversation = current_user.mailbox.conversation.find(params[:id])
end
# in the view
<% binding.pry %>
So I have a Message model, and a ChatRoom model.
When I display a chat room, I use the show action on the ChatRoom controller. In the view for this action, there is a little form for the user to create a post, and submit that post to the chat room being shown.
When I run my tests, however, I get an error "no route matches [POST] /messages/an_id_of_some_sort". Specifically, in this little test:
post message_path, params: {message: {body: "yo ho ho and a bottle of rum!"}}
assert_redirected_to chat_room_path(#channel)
the error pops up in the post message_path.
The show method on the chat room controller looks like
def show
if(#user = current_user)
#chats = #user.chat_rooms
#chosen = ChatRoom.find_by(id: params[:id])
if(#chosen.messages.any?)
#messages = #chosen.messages
else
#messages = nil
end
#message = Message.new
end
end
Then the little form bit of the view is:
<div class="message-input">
<%= form_for(#message) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :body, placeholder: "Write Message..." %>
<%= f.hidden_field :room, :value => params[:room] %>
<%= button_tag(type: "submit", class: "message-submit-btn", name: "commit", value: "") do %>
<span class="glyphicon glyphicon-menu-right"></span>
<% end %>
<% end %>
</div>
I have a create action on the Messages Controller which does the saving to the database:
#message = current_user.messages.build(message_params);
#message.chat_room = params[:room]
if #message.save
redirect_to chat_room_path(#message.chat_room)
end
and routing-wise I have
Rails.application.routes.draw do
root 'welcome#welcome'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get 'users/signup_success'
delete '/chat_rooms/leave/:id', to: 'chat_rooms#leave', as: 'current'
get 'welcome/welcome'
resources :users
resources :account_activations, only: [:edit] #Only providing an Edit route for this resource.
resources :password_resets, only: [:new, :edit, :create, :update]
resources :chat_rooms, only: [:new, :create, :show, :index]
resources :messages, only: [:create, :edit, :destroy]
end
I have tried playing around with explicitly setting the :url on the form_for, but no dice. There is another question on this problem, but the solution there hasn't really helped.
I would greatly appreciate any help.
With this line you're running POST /messages/:id
post message_path, params: {message: {body: "yo ho ho and a bottle of rum!"}}
In your routes file you have this:
resources :messages, only: [:create, :edit, :destroy]
This will create the routes POST /messages, PUT/PATCH /messages/:id, and DELETE /messages/:id. You can verify this with rake routes.
None of these generated routes handle POST /messages/:id.
If you're trying to have the test create a new message, then you can use messages_path instead. message_path (with singular message) takes a message argument as a message, e.g. message_path(Message.first) and uses that to build the url.
References and External Links
Ruby on Rails - Settting up Reviews functionality
NoMethodError in Discussions#new
http://ruby.about.com/od/rubyonrails/ss/blogpart4_4.htm
Background
I'm implementing a feature in my application that allow users to rate and review pictures.
I am using a Posts/Comments relationship model for a Pictures/Reviews relationship.
Models
class Review < ActiveRecord::Base
belongs_to :picture
end
class Picture < ActiveRecord::Base
has_many :reviews
end
Above, I established a one-to-many relationship between pictures and reviews.
Reviews Migration
class CreateReviews < ActiveRecord::Migration
def change
create_table :reviews do |t|
t.string :username
t.text :body
t.references :picture, index: true
t.timestamps
end
end
end
Matched Routes
match '/pictures/:id/reviews', to: 'reviews#show', via: 'get', :as => 'picture_reviews'
match '/pictures/:id/reviews/edit', to: 'reviews#edit', via: 'get'
match '/pictures/:id/reviews/new', to: 'reviews#new', via: 'get', :as => 'new_reviews'
I will name the route for reviews#edit after I fix this issue with reviews#new.
Error Message
NoMethodError in Reviews#new
Undefined method 'reviews_path' for #<#<Class:0x45c1b00>:0x39ae810>
Extracted source (Around line #8):
5 <div class = 'edit-form'>
6 <div class = 'center'>
7
8 <% form_for #review do |f| %>
9
10 <p>
11 <%= f.label :username %><br />
I checked to see if any files contained 'review-path', but all routes were properly named.
Routes
favorite_picture_path PUT /pictures/:id/favorite(.:format) pictures#favorite
pictures_path GET /pictures(.:format) pictures#index
POST /pictures(.:format) pictures#create
new_picture_path GET /pictures/new(.:format) pictures#new
edit_picture_path GET /pictures/:id/edit(.:format) pictures#edit
picture_path GET /pictures/:id(.:format) pictures#show
PATCH /pictures/:id(.:format) pictures#update
PUT /pictures/:id(.:format) pictures#update
DELETE /pictures/:id(.:format) pictures#destroy
users_path GET /users(.:format) users#index
POST /users(.:format) users#create
new_user_path GET /users/new(.:format) users#new
edit_user_path GET /users/:id/edit(.:format) users#edit
user_path GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
sessions_path POST /sessions(.:format) sessions#create
new_session_path GET /sessions/new(.:format) sessions#new
session_path DELETE /sessions/:id(.:format) sessions#destroy
contacts_path POST /contacts(.:format) contacts#create
new_contact_path GET /contacts/new(.:format) contacts#new
root_path GET / pictures#welcome
users_new_path GET /users/new(.:format) users#new
about_path GET /about(.:format) pictures#about
GET /contacts(.:format) contacts#new
GET /users/:id/favorites(.:format) users#favorites
signup_path GET /signup(.:format) users#new
signin_path GET /signin(.:format) sessions#new
signout_path DELETE /signout(.:format) sessions#destroy
picture_reviews_path GET /pictures/:id/reviews(.:format) reviews#index
GET /pictures/:id/reviews/edit(.:format) reviews#edit
new_reviews_path GET /pictures/:id/reviews/new(.:format) reviews#new
updated_path GET /updated(.:format) pictures#new_updates
GET /top-rated(.:format) pictures#high_ratings
ReviewsController (Part 1)
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
def index
#picture = Picture.find(params[:id])
#review = Review.all
end
def show
#picture = Picture.find(params[:id])
#review = Review.find(params[:id])
end
def new
#review = Review.new
end
def edit
#picture = Picture.find(params[:picture_id])
#review = Review.find(params[:id])
end
def create
#picture = Picture.find(params[:picture_id])
#review = #picture.reviews.build(params[:review])
if #review.save
flash[:notice] = 'Review was successfully created.'
redirect_to #picture
else
flash[:notice] = "Error creating review: #{#review.errors}"
redirect_to #picture
end
end
Reviews Controller(Part 2)
def update
#picture = Picture.find(params[:picture_id])
#review = Review.find(params[:id])
if #review.update_attributes(params[:review])
flash[:notice] = "Review updated"
redirect_to #picture
else
flash[:error] = "There was an error updating your review"
redirect_to #picture
end
end
def destroy
#picture = Picture.find(params[:picture_id])
#review = Review.find(params[:id])
#review.destroy
redirect_to(#review.post)
end
private
def set_review
#review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:username, :body, :picture_id)
end
end
Reviews#Index Page
<h3>Reviews for <%= "#{#picture.title}" %></h3>
<table>
<thead>
</thead>
<tbody>
</tbody>
</table>
<div class = 'center'>
<p><%= link_to 'New Review', new_reviews_path(#review), :class => "btn btn-info" %></p>
<p><%= link_to 'Back', picture_path, :class => "btn btn-info" %></p>
</div>
Link to the Reviews#new page
<p><%= link_to 'New Review', new_reviews_path(#review), :class => "btn btn-info" %></p>
Reviews#New Page
<% #title = "New Review" %>
<h3>New Review</h3>
<div class = 'edit-form'>
<div class = 'center'>
<% form_for #review do |f| %>
<p>
<%= f.label :username %><br />
<%= f.text_field :username %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit "Submit Review" %>
</p>
<% end %>
</div>
</div>
<div class = 'center'>
<%= link_to 'Back', picture_reviews_path(#picture) %>
</div>
Pictures#Show Page
<% #title = "#{#picture.title}" %>
<h4 class = 'indent'>Picture Statistics</h4>
<ul id = 'view'>
<li><strong>Title:</strong> <%= #picture.title %></li>
<li><strong>Category:</strong> <%= #picture.category %></li>
<li><strong>Rating:</strong> <%= pluralize(#picture.rating, 'Star') %></li>
<li><strong>Favorited:</strong> By <%= pluralize(#picture.users.count, 'User') %></li></br>
</ul>
<% if #picture.rating > 4 %>
<button class = 'top-picture'>Top Rated</button>
<% end %>
<%= form_for #picture do |f| %>
<div class = 'indent'>
<p>
<%= f.label :stars, 'Rating' %>
<div class= "rating">
1 ☆<%= f.radio_button :stars, '1' %>
2 ☆<%= f.radio_button :stars, '2' %>
3 ☆<%= f.radio_button :stars, '3' %>
4 ☆<%= f.radio_button :stars, '4' %>
5 ☆<%= f.radio_button :stars, '5' %>
</div>
</p>
<p><input class="btn btn-info" type="submit" value="Rate"></p>
<p><%= link_to 'Reviews', picture_reviews_path(#picture), :class => "btn btn-info" %></p>
<% end %>
<p><%= link_to 'Index', pictures_path, :class => "btn btn-info" %></p>
</div>
I've tried using nested resources like so
resources :pictures do
put :favorite, on: :member
resources :reviews
end
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :contacts, only: [:new, :create]
That didn't work because It routed my pictures using :picture_id instead of the standard :id field. Since it routed to :picture_id it couldn't find any pictures.
picture_reviews_path GET /pictures/:picture_id/reviews(.:format) reviews#index
GET /pictures/:picture_id/reviews/edit/:id(.:format) reviews#edit
new_reviews_path GET /pictures/:picture_id/reviews/new(.:format) reviews#new
Picture Columns
Picture.column_names
=> ['id', 'title', 'category', 'stars', 'created_at', 'updated_at',
'ratings_count', 'ratings_total']
The problem with nesting routes, is that it calls a path using a column_name not found in the table. That is why I decided to go back to matching routes.
I believe the problem lies in my ReviewsController for which there may be duplicated code.
before_action :set_review, only: [:show, :edit, :update, :destroy]
#review = Review.find(params[:id])
def set_review
#review = Review.find(params[:id])
end
I think I could remove the #review = Review.find line from every method, but my main concern is that the set_review method was defined as a private method so that might not be possible.
Help is greatly appreciated and thanks in advanced.
Update
I think the problem lies in my new action in my ReviewsController.
This is just an extended version of #japed answer.
1. You have no route to the create or update action
Both actions works on POST request, hence url_helpers alone won't tell rails what to do with POST request when it gets it. What you need is to change your routes back to nested resources (it was good the way it was, your issue was caused by another bit of code). So, you need:
resources :pictures do
...
resources :reviews
end
Also remove all other routes for this controller as they may affect your final routes. Remeber to restart your server after changing your routes.
2. The controller:
Firstly, note that there are a lot of repetitions there - you are setting #picture in all the actions. Currently your problem is that it is using params[:id] in some actions and params[:picture_id] in others. It should always be picture_id, id should be reserved to be review's id, as you are inside reviews_controller.
The best way to do this is to create another before_filter which will set up the #picture variable:
class ReviewsContorller < ApplicationController
before_filter :set_picture
# This is perfectly fine, but needs to be executed after :set_picture
before_filter :set_review, only: [:show, :edit, :update, :destroy]
...
private
...
def set_picture
#picture = Picture.find(params[:picture_id])
end
def set_review
#review = picture.reviews.find(params[:id])
end
end
Note that the #review is pulled from #picture association - this is important security check, if you used Review.find instead, all the users are automatically able to view, edit and create new reviews for all the photos, without knowing which photo they are really commenting for. It should not be a great issue in your case, but it is good to get this into the habit.
3. The form:
<% form_for #review do |f| %>
This would seems all right, however imagine you are your application - how would you know what is the correct post url for this form? Rails is quite intelligent framework and it is trying to guess it by the resource supplied. In this case, you pass an instance of Review class, hence it will try to send the from to review_path(#review.id). The problem is, that this path does not exists in your routes, so you will get undefined_method 'review_path' here.
Also note, that the proper route you want is /picture/:picture_id/reviews for new reviews or /picture/:picture_id/review/:idfor existing reviews. Hence rails will need the parent picture object to be passed as well to figure out the rightpicture_id`. You can do this by passing an array of resources, with the one which the form is really for being the last so:
<% form_for [#picture, #review] do |f| %>
This will tell rails to look for picture_reviews_path(#picture.id) for new review or picture_review_path(#picture.id, #review.id) for existing reviews. If you have nested resources in your routes, both of those should exists.
4. Other links
Your current routes defines a named path new_reviews which will not longer exist after you use nested resources - it will be renamed to new_picture_review, so you need to change all the occurrences of new_reviews_path to new_picture_review(#picture)
As you're doing nested routes, you need to find by :picture_id as you've just found
class ReviewsController < ApplicationController
before_action { #picture = Picture.find(params[:picture_id] }
end
As your error says the issue is because reviews_path doesn't exist because you've nested it
So this
<% form_for #review do |f| %>
Wants to change to
<% form_for [#picture, #review] do |f| %>
So that it goes to the picture_reviews_path
Also this
<p><%= link_to 'New Review', new_reviews_path(#review), :class => "btn btn-info" %></p>
Wants to become
<p><%= link_to 'New Review', new_picture_reviews_path(#picture, #review), :class => "btn btn-info" %></p>
Can you use Shallow Nesting Routes? That is, you'll have a nested resource where needed, but when unambiguous you get a shorter path, with just one parameter for the review. You can still find your way back to the picture, using the picture_id in the review.
resources :pictures, shallow: true do
put :favorite, on: :member
resources :reviews, shallow: true
end
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :contacts, only: [:new, :create]
Then, improve the models to help the associations to bind well, with inverse_of:
class Review < ActiveRecord::Base
belongs_to :picture, inverse_of: :reviews
end
class Picture < ActiveRecord::Base
has_many :reviews, inverse_of: :picture
end
This should mean there's only one copy of a picture in memory. And then in the ReviewsController:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
def index
#picture = Picture.find(params[:id])
# reference #picture.reviews to get all reviews in the view
end
def show
#picture = Picture.find(params[:id])
# use #picture.reviews to get all reviews in the view
end
def new
# where will you get the picture this belongs to?
# Need to collect the picture_id param. and build the associated review
#picture = Picture.find(param[:picture_id])
#review = #picture.reviews.build()
end
def edit
#picture = Picture.find(params[:picture_id])
# use #picture.reviews in the view controller to get the associated reviews
end
def create
#picture = Picture.find(params[:picture_id])
#review = #picture.reviews.build(params[:review])
if #review.save
flash[:notice] = 'Review was successfully created.'
redirect_to #picture
else
flash[:notice] = "Error creating review: #{#review.errors}"
redirect_to #picture
end
end
I think there's one other significant issue. You keep using a piece of code like this:
#review = Picture.find(id)
But that returns zero or more elements. It will help you understand the code better if you reflect that this is, normally, an array:
#reviews = Picture.find(id)
But even better, don't do that. You have the associations. Use them in the view.
#picture.reviews
This will return an array. If zero length, there are no reviews. If non-zero, that's how many review elements there are.
Then you won't make the mistake of picking up an array variable called #review, which appears to be singular (meaning that link_to #review appears to make sense, but will fail), and instead use an array:
<%- #picture.reviews.each do |review| %>
<% link_to review ...%>
Hope that helps!
I've been trying to wrap my head around this single table inheritance example. I understand the concept behind it, but am not sure from reading other posts around the web & on this site how to make it work for my example. I'm using STI because i've got two types of projects - public and private. The logic behind them is the same and the data they store in the database will be identical whether public or private. I just intend to implement some authorization & roles for users in the future that differ slightly based on whether the project is public or private. So my question is, what steps are still needed to make this work...so far I've:
1) added a type_column to projects via a migration...my project model is
PROJECT.RB
class Project < ActiveRecord::Base
has_many :users
has_many :versions, dependent: :destroy
validates :title, presence: true, length: { maximum: 100 }
validates :background, presence: true
validates :user_id, presence: true
default_scope -> { order('created_at DESC') }
end
2) I created separate classes for PublicProjects & PrivateProjects...those models are:
PUBLIC_PROJECT.RB
class PublicProject < Project
end
PRIVATE_PROJECT.RB
class PrivateProject < Project
end
So now i'm wondering what needs to be re-factored in my controller (i'd like to keep the single Projects controller)...and then also what needs changing in my 'new' form view. Step by step help on the code that would make this work would be much appreciated. The controller/view files are:
PROJECTS_CONTROLLER.RB
class ProjectsController < ApplicationController
before_filter :signed_in_user, only: [:create, :new, :edit, :update]
def new
#project = Project.new
end
def show
#project = Project.find(params[:id])
#user = User.where(:id => #project.user_id).first
end
def index
#projects = Project.paginate(page: params[:page])
end
def create
#project = current_user.projects.build(project_params)
if #project.save
flash[:success] = "Welcome to your new project."
redirect_to #project
else
render 'new'
end
end
def edit
end
def update
#project = Project.find(params[:id])
if #project.update_attributes(params[:project])
flash[:success] = "Project Created"
redirect_to #project
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Project destroyed"
redirect_to users_path
end
private
def project_params
params.require(:project).permit(:title, :background)
end
end
NEW.HTML.ERB (NEW PROJECTS VIEW W/ FORM)
<% provide(:title, 'New Project') %>
<h1>Create a new project</h1>
<div class="row-fluid">
<div class="col-md-5 no-pad offset3">
<%= bootstrap_form_for #project do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_field :title %>
<%= f.text_area :background %>
<div class="row-fluid">
<div class="no-pad col-md-6">
<h5>Is this project public or private?</h5>
<div class="btn-group">
<button type="button" class="btn btn-default"><%= image_tag "globe.png" %> Public</button>
<button type="button" class="btn btn-default"><%= image_tag "lock.png" %> Private</button>
</div>
<script>
$(".btn-group > .btn.btn-default").click(function(){
$(".btn-group > .btn.btn-default").removeClass("active");
$(this).addClass("active");
});
</script>
</div>
<div class="col-md-6">
Some static graphics
</div>
</div>
<br clear="all"><br/>
<%= f.submit "Create your project", class: "btn btn-lg btn-primary" %>
<% end %>
</div>
</div>
Lastly, i've seen some STI examples that are getting into the routes file (although I've seen that this isn't recommended as often). but just in case:
ROUTES.RB
ProductionApp::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :projects do
resources :versions
end
# get "static_pages/home"
# get "static_pages/help"
# get "static_pages/about"
#The original routes above map to...
root 'static_pages#home'
match '/signup', to: 'users#new', via: 'get'
match '/signin', to: 'sessions#new', via: 'get'
match '/signout', to: 'sessions#destroy', via: 'delete'
match '/help', to: 'static_pages#help', via: 'get'
match '/about', to: 'static_pages#about', via: 'get'
match '/contact', to: 'static_pages#contact', via: 'get'
end
Thanks a lot for any help,
I am not sure about the authentication, but instead of using additional classes for public and private, you could just create a type field that tells you whether or not the project is public or private something like is_public.
The other option is to use a enum release in rails 4 - really simple and straight forward. This is my preferred option, but you need to be working on the latest version of rails.
I'm trying to get a user from his profile (user_path) can register his vehicles (new_user_car_path). but i got this error:
Routing Error
No route matches {:action=>"new", :controller=>"cars"}
for that i have the next routes.rb
Estaciones::Application.routes.draw do
root :to => "static_pages#home"
match '/contact', :to=>'static_pages#contact'
match '/about', :to=>'static_pages#about'
devise_for :users
resources :users do
resources :cars, :only => [:new, :create, :edit, :destroy]
end
here is part of the user_path
<div class="container">
<fieldset>
<h1><%= #user.email %></h1>
<br>
<h2>options</h2>
<p>
<strong>new car registration</strong>
<%= link_to "Sign up now!", new_user_car_path, :class => "btn btn-primary" %>
</p>
<p>
<strong>all cars view</strong>
</p>
</fieldset>
</div> <!-- /container -->
and my CarsController
class CarsController < ApplicationController
def new
#car = Car.new
end
def create
#car = current_user.cars.build(params[:car])
if #car.save
redirect_to current_user, :flash => { :success => "car created!" }
else
redirect_to new_user_car_path, :flash => { :success => "sorry try again" }
end
end
end
you need to pass information to route, so that it can determine where to go.
if you want to create a new car for a user, you need to pass the user. otherwise rails will not know where to get the user from... which is kind of logical if you think about it for more then a second...
new_user_car_path(user)