I am creating a blog with tags. Using Mongodb.
I got this error when submitting a new article with tags. And new artcile is not created. If I do not put any tags in the form, the article is created.
NoMethodError in ArticlesController#create
undefined method `first' for #
Rails.root: c:/Sites/blogK
Application Trace | Framework Trace | Full Trace
app/models/article.rb:18:in all_tags='
app/controllers/articles_controller.rb:22:innew'
app/controllers/articles_controller.rb:22:in `create'
article.rb
class Article
include Mongoid::Document
has_many :comments, dependent: :destroy
field :title, type: String
field :text, type: String
embeds_many :taggings
embeds_many :tags
def all_tags=(tags_string)
tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
new_or_found_tags = tag_names.collect { |name| Tag.find_or_create_by(name: name) }
self.tags = new_or_found_tags
end
def all_tags
self.tags.collect do |tag|
tag.name
end.join(", ")
end
validates :title, presence: true,
length: { minimum: 5 }
end
tag.rb
class Tag
include Mongoid::Document
field :name, type: String
embeds_many :taggings
embeds_many :articles
end
tagging.rb
class Tagging
include Mongoid::Document
embedded_in :article
embedded_in :tag
end
article_controller.rb
class ArticlesController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text, :all_tags)
end
end
_form.html.erb
<%= form_for #article do |f| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p><%= f.text_field :all_tags, placeholder: "Tags separated with comma" %></p>
<p>
<%= f.submit %>
</p>
<% end %>
Related
I have two models, Hotel and Room. I want to create a form that will allow me to add a new hotel and rooms, which indicates the name of the hotel and the quantity of rooms.
I know I should use "nested form", but it is hard for me to implement it properly.
Here is my code:
HotelsController
class HotelsController < ApplicationController
def index
#hotels = Hotel.all
end
def new
#hotel = Hotel.new
end
def create
#hotel = Hotel.new(hotel_params)
if #hotel.save
redirect_to #hotel
else
render 'new'
end
end
def show
#hotel = Hotel.find(params[:id])
end
def destroy
#hotel = Hotel.find(params[:id])
#hotel.destroy
redirect_to hotels_url, notice: 'Hotel was successfully destroyed.'
end
private
def hotel_params
params.require(:hotel).permit(:name, :rooms_count)
end
end
Hotel model
class Hotel < ApplicationRecord
has_many :rooms, dependent: :destroy
accepts_nested_attributes_for :rooms
end
Room model
class Room < ApplicationRecord
belongs_to :hotel, optional: true # avoiding rails 5.2 belongs_to error
end
form
<%= form_with scope: :hotel, url: hotels_path, local: true do |form| %>
<p>
<%= form.label :name %><br>
<%= form.text_field :name %>
</p>
<p>
<%= form.label :rooms_count %><br>
<%= form.number_field :rooms_count %>
</p>
<p>
<% form.fields_for :rooms do |f|%>
<p>
**THE CODE**
</p>
<% end %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
You forgot the "ERB echo sign" (=) on the fields_for helper.
Correct form:
<%= form.fields_for :rooms do |f|%>
I have found a solution to my question.
def create
#hotel = Hotel.new(hotel_params)
#hotel.rooms_count.times do
#hotel.rooms.build
end
if #hotel.save
redirect_to #hotel
else
render 'new'
end
end
It implement a #hotel.rooms.build as many times as #hotel.rooms_count number entered in Rooms Count field in form.
For a marketplace that I'm working on I'd like to add messaging-functionality between users.
I found this tutorial that matches my requirements and I'm trying to replicate it.
When trying to send a message to another user however, I get the following error:
ArgumentError in Messages#index
Showing /home/ubuntu/workspace/marketplace/app/views/messages/index.html.erb where line #9 raised:
First argument in form cannot contain nil or be empty
From the Message view file it's relating to:
<%= form_for [#conversation, #message] do |f| %>
<%= f.text_area :body %>
<%= f.text_field :user_id, value: current_user.id %>
<%= f.submit "Add Reply" %>
A conversation_id was made, but in Message view file it goes wrong.
Does anybody have a clue what I'm missing? I'm quite new to Rails and I'm blind-staring on this for 2 nights now. Your help is much appreciated!
Routes.rb
resources :conversations do
resources :messages
end
Conversation Model:
class Conversation < ApplicationRecord
belongs_to :sender, :foreign_key => :sender_id, class_name: "User"
belongs_to :recipient, :foreign_key => :recipient_id, class_name: "User"
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, :scope => :recipient_id
scope :between, -> (sender_id, recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id = ?) OR (conversations.sender_id = ? AND conversations.recipient_id = ?)", sender_id, recipient_id, recipient_id, sender_id)
end
end
Message Model:
class Message < ApplicationRecord
belongs_to :conversation
belongs_to :user
validates_presence_of :body, :conversation_id, :user_id
def message_time
created_at.strftime("%_d %b %Y at %k:%M")
end
end
Conversations Controller:
class ConversationsController < ApplicationController
before_action :authenticate_user!
def index
#users = User.all
#conversations = Conversation.all
end
def create
if Conversation.between(params[:sender_id],params[:recipient_id]).present?
#conversation = Conversation.between(params[:sender_id], params[:recipient_id]).first
else
#conversation = Conversation.create!(conversation_params)
end
redirect_to conversation_messages_path(#conversation)
end
private
def conversation_params
params.permit(:sender_id, :recipient_id)
end
end
Messages Controller:
class MessagesController < ApplicationController
before_action do
#conversation = Conversation.find(params[:conversation_id])
end
def index
#messages = #conversation.messages
end
def new
#message = #conversation.messages.new
end
def create
#message = #conversation.messages.new(message_params)
if #message.save
redirect_to conversation_messages_path(#conversation)
end
end
private
def message_params
params.require(:message).permit(:body, :user_id)
end
end
Conversation index page
<h3>Mailbox</h3>
<% #conversations.each do |conversation| %>
<% if conversation.sender_id == current_user.id || conversation.recipient_id == current_user.id %>
<% if conversation.sender_id == current_user.id %>
<% recipient = User.find(conversation.recipient_id) %>
<% else %>
<% recipient = User.find(conversation.sender_id) %>
<% end %>
<%= link_to recipient.firstname, conversation_messages_path(conversation)%>
<% end %>
<% end %>
<h3>All Users</h3>
<% #users.each do |user| %>
<% if user.id != current_user.id %>
<%= user.firstname %> <%= link_to "Message me!", conversations_path(sender_id: current_user.id, recipient_id: user.id), method: "post"%>
<% end %>
<% end %>
Messages index page
<% #messages.each do |message| %>
<% if message.body %>
<% user = User.find(message.user_id) %>
<p><%= user.firstname %> and <%= message.message_time %></p>
<p><%= message.body %></p>
<% end %>
<% end %>
<%= form_for [#conversation, #message] do |f| %>
<%= f.text_area :body %>
<%= f.text_field :user_id, value: current_user.id %>
<%= f.submit "Add Reply" %>
<% end %>
You did not define the #message variable in your MessagesController#index action. Please define it like this
def index
#messages = #conversation.messages
#message = #conversation.messages.new
end
I have the below form written for a movie review. Review is associated with movies. I am not able to display error message for this form. I have written similar code to display error message for validation on movies and that works well.
Code for the 'add new review' form -
<%= form_for([#movie, #movie.reviews.build]) do |f| %>
<% if #reviews.errors.any? %>
<div id="error_explanation">
<h3>
<%= pluralize(#reviews.errors.count, "error") %> prohibited
this review from being saved:
</h3>
<ul>
<% #reviews.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :reviewer %><br>
<%= f.text_field :reviewer %>
</p>
<p>
<%= f.label :comment %><br>
<%= f.text_area :comment %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Models -
MOVIE -
class Movies < ActiveRecord::Base
has_many :reviews, dependent: :destroy
validates_associated :reviews
validates :title, presence: true
validates :rating, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 10 }
end
REVIEW -
class Review < ActiveRecord::Base
belongs_to :movies
validates :comment, presence: true
end
Adding code for reviews_controller.erb
class ReviewsController < ApplicationController
def create
#movie = Movies.find(params[:movie_id])
#review = #movie.reviews.create(review_params)
redirect_to movies_path(#movie)
end
def destroy
#movie = Movies.find(params[:movie_id])
#review = #movie.reviews.find(params[:id])
#review.destroy
redirect_to movies_path(#movie)
end
private
def review_params
params.require(:review).permit(:reviewer, :comment)
end
end
Here is my code for movies_controller.erb
class MoviesController < ApplicationController
def new
#movie = Movies.new
end
def index
#movies = Movies.all
end
def show
#movie = Movies.find(params[:id])
end
def edit
#movie = Movies.find(params[:id])
end
def create
#movie = Movies.new(movie_params)
if #movie.save
redirect_to #movie
else
render 'new'
end
end
def update
#movie = Movies.find(params[:id])
if #movie.update(movie_params)
redirect_to #movie
else
render 'edit'
end
end
def destroy
#movie = Movies.find(params[:id])
#movie.destroy
redirect_to movies_path
end
private
def movie_params
params.require(:movie).permit(:title,:year,:rating,:description)
end
end
Please help me identify what is going wrong and how to be able to get the validation message working.
Thanks in advance.
You are trying to display error of #review with <% if #reviews.errors.any? %>
But You do not have #reviews in <%= form_for([#movie, #movie.reviews.build) do |f| %>
Better solution, through controller assigning #review = Review.new. In this case it should show you errors, if comment field is empty.
def new
#review = Review.new
end
def create
#movie = Movie.find(params[:movie_id])
#review = #movie.reviews.build(review_params)
if #review.save
else
render 'new'
end
end
In Form
<%= form_for ([#movie, #review) do |f| %>
I have these models:
class Review < ActiveRecord::Base
belongs_to :reviewable, polymorphic: true
end
class Article < ActiveRecord::Base
has_one :review, as: :reviewable, dependent: :destroy
accepts_nested_attributes_for :review
end
And a form like this:
<%= form_for #article do |f| %>
<%= f.fields_for(:review, Review.new) do |r| %>
<%= r.label :content %>
<%= r.text_field :content %>
<% end %>
<%= f.label :description %>
<%= f.text_field :description %>
<% end %>
Inside my ArticlesController I create article simple like this:
#article = Article.new(article_params)
#article.save
def article_params
params.require(:article).permit(:description, review_attributes: [:id, :content])
end
What am I doing wrong? Thank you.
Try adding accepts_nested_attributes_for
Updated:
app/models/article.rb
class Article < ActiveRecord::Base
has_one :review, as: :reviewable, dependent: :destroy
accepts_nested_attributes_for :review
end
app/controllers/articles_controller.rb
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article, notice: "Article created!"
else
render :new
end
end
private
def article_params
params.require(:article).permit(:description, review_attributes: [:content])
end
view
<%= form_for(#article) do |form| %>
<%= form.fields_for(:review_attributes, #article.build_review) do |review| %>
<%= review.label :content %>
<%= review.text_field :content %>
<% end %>
<%= form.label :description %>
<%= form.text_field :description %>
<% end %>
having a lot of trouble setting up polymorphic. I have Comments which works fine, but the Links which should work exactly the same has NameError in Links#index. If I try to forum_posts/1/comments all is good but not forum_posts/1/links.
I was following the railscast tutorial http://railscasts.com/episodes/154-polymorphic-association?view=asciicast
Appreciate the help.
Error
NameError in Links#index
uninitialized constant ForumPost::Link
Extracted source (around line #4):
1: <h1>Links</h1>
2:
3: <ul id="links">
4: <% #links.each do |link| %>
5: <li><%= link.display_name %></li>
6: <% end %>
7: </ul>
routes
resources :forum_posts do
resources :comments
resources :links
end
Models
*models/forum_posts.rb*
class ForumPost < ActiveRecord::Base
attr_accessible :content, :display_name, :section, :user_id
has_many :comments, :as => :commentable
has_many :links, :as => :linkable
end
models/comments.rb
class Comment < ActiveRecord::Base
attr_accessible :commentable_id, :commentable_type, :content, :user_id
belongs_to :commentable, :polymorphic => true
end
models/comments.rb
class Links < ActiveRecord::Base
attr_accessible :description, :display_name, :inamge, :linkable_id, :linkable_type, :user_id
belongs_to :linkable, :polymorphic => true
end
Controllers
*controllers/comments_controller.rb*
class CommentsController < ApplicationController
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def index
#commentable = find_commentable
#comments = #commentable.comments
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
if #comment.save
flash[:notice] = "Successfully saved comment."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
*controller/links_controller.rb*
class LinksController < ApplicationController
def find_linkable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def index
#linkable = find_linkable
#links = #linkable.links
end
def create
#linkable = find_linkable
#link = #linkable.links.build(params[:link])
if #link.save
flash[:notice] = "Successfully saved link."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
Views
views/comments/index.html.erb
<h1>Comments</h1>
<ul id="comments">
<% #comments.each do |comment| %>
<li><%= comment.content %></li>
<% end %>
</ul>
<h2>New Comment</h2>
<%= form_for [#commentable, Comment.new()] do |form| %>
<%= form.label :content %><br/>
<%= form.text_area :content, :rows => 5 %><br/>
<%= form.submit "Add comment" %>
<% end %>
views/links/index.html.erb
<h1>Links</h1>
<ul id="links">
<% #links.each do |link| %>
<li><%= link.display_name %></li>
<% end %>
</ul>
<h2>New Link</h2>
<%= form_for [#linkable, Link.new()] do |form| %>
<%= form.label :display_name %><br/>
<%= form.text_area :display_name %><br/>
<%= form.submit "Add link" %>
<% end %>
Your Links model should be placed at app/models/link.rb and should be called Link, not Links.