Rails Form Helper not reading question mark for object attribute - ruby-on-rails

Ruby Version: 2.0
Rails Version: 4.0
I have a controller Question, which has an embedded form for a model Answer.
question.rb
class Question < ActiveRecord::Base
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :allow_destroy => true
end
answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
end
answers migration
class CreateAnswers < ActiveRecord::Migration
def change
create_table :answers do |t|
t.string :text
t.integer :question_id
t.boolean :correct?
t.timestamps
end
end
end
in the form, when editing or creating a new question - the user may enter up to 4 possible answers, and mark a check box for the "correct" answer(s).
/views/questions/_form.html.erb
<%= form_for(#question) do |f| %>
<div class="field">
<%= f.label :text %><br>
<%= f.text_area :text %>
</div>
<p>Enter up to 4 posisble answer choices.</p>
<%= f.fields_for :answers do |answer| %>
<div class="field">
<%= answer.text_field :text %>
<%= answer.check_box :correct? %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
relevant snippets from questions_controller.rb
def new
#question = Question.new
4.times { #question.answers.build }
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:text, :quiz_id, answers_attributes: [:id, :text, :correct?])
end
Finally - on to my problem
Everything listed above worked perfectly until I added the checkboxes for answer.correct?. When I submit the form as is I get this message in my logs:
Unpermitted parameters: correct
Unpermitted parameters: correct
Unpermitted parameters: correct
Unpermitted parameters: correct
Weird... there is definitely a question mark at the end of that parameter. Allowing this to pass through without the question mark be editing the allowed parameters in the controller gets me this error message:
unknown attribute: correct (this one actually throws an error message, I don't have to go digging in the logs to find this.)
How do I get the form helper to read the question mark?

? is not a valid character for inclusion within a column name. First, create a new database migration:
# from command line
rails generate migration ChangeCorrectInAnswers
Rename your column from correct? to correct:
# in the resulting migration
class ChangeCorrectInAnswers < ActiveRecord::Migration
def up
rename_column :answers, :correct?, :correct
end
end
Run the migration:
# from command line
rake db:migrate
Finally, remove the ? from your field in the view:
# app/views/questions/_form.html.erb
<%= answer.check_box :correct %>

Related

Ruby on rails Validation does not work for Form objects that contain Action text, etc

Error Overview
There is a Github URL of a simple app that reproduces the problem in the bottom paragraph.
I'm creating an application that allows article submission. To make it easier to submit articles, I introduced Rails Action text. I also decided to add a tagging feature, so I have a tags table, an article table, and a user table in the database. The article form looks like the following image.
The article form
I was able to submit an article, but I wanted to check the validation of the condition that the article could not be submitted with a blank field, so I submitted it with a blank field, but I got the following error.
NoMethodError
I don't know how to solve this problem and I need your help.
The relevant source code
This is the view file of the corresponding section.↓
<%= render "shared/header" %>
<%= form_with model: #article, url: articles_path, class:'form-wrap', local: true do |f| %>
<%= render 'shared/error_messages', model: f.object %>
<div class='article-form'>
<div class="title-field">
<%= f.label :title, "題名" %>
<%= f.text_area :title, class:"input-title" %>
</div>
<div class="tag-field", id='tag-field'>
<%= f.label :name, "タグ" %>
<%= f.text_field :name, class:"input-tag" %>
</div>
<div class="content-field">
<%= f.label :content, "記事本文" %>
<%= f.rich_text_area :content %>
</div>
<div id="search-result">
</div>
</div>
<div class="submit-post">
<%= f.submit "Send", class: "submit-btn" %>
</div>
<% end %>
This is the controller code.↓
class ArticlesController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
def index
#article = Article.all.order(created_at: :desc)
end
def new
#article = ArticlesTag.new
end
def create
#article = ArticlesTag.new(article_params)
if #article.valid?
#article.save
return redirect_to root_path
else
render :new
end
end
def search
return nil if params[:keyword] == ""
tag = Tag.where(['name LIKE ?', "%#{params[:keyword]}%"] )
render json:{ keyword: tag }
end
private
def article_params
params.require(:articles_tag).permit(:title, :content, :name).merge(user_id: current_user.id)
end
end
This is the code of the Article model.↓
class Article < ApplicationRecord
has_rich_text :content
belongs_to :user
has_one_attached :image
has_many :article_tags
has_many :tags, through: :article_tag_relations
end
This is the code of The Tag model. ↓
class Tag < ApplicationRecord
has_many :article_tag_relations
has_many :articles, through: :article_tag_relations
validates :name, uniqueness: true
end
This is the intermediate model between The Tag model and The Article model.↓
class ArticleTagRelation < ApplicationRecord
belongs_to :article
belongs_to :tag
end
This is the Form object class that collects the tags and articles tables.
class ArticlesTag
include ActiveModel::Model
attr_accessor :title, :name, :content, :user_id
with_options presence: true do
validates :title
validates :name
validates :content
validates :user_id
end
def save
article = Article.create(title: title, content: content, user_id: user_id)
tag = Tag.where(name: name).first_or_initialize
tag.save
ArticleTagRelation.create(article_id: article.id, tag_id: tag.id)
end
end
Database Status
Action text table
Article Tag Relation table
Article table
Tag table
Please help me.
A simple application that reproduces the error.
github URL
A simple app that reproduce the error
This is the error you are getting
undefined method `body' for "":String
That says that it is trying to call the method body on an empty string.
Why is it trying to call that method? Because you wrote this:
<%= f.rich_text_area :content %>
So the form helper is expecting that content contains an instance of a RichText (https://api.rubyonrails.org/classes/ActionText/RichText.html)
However, content actually just contains an instance of String, because your ArticlesTag model does not declare that it has_rich_text (like your Articles Model does)
I note that your ArticlesTag model is not persisted. I am not sure how to use rich text with a model that is not persisted - I suspect it might not be possible.

Unable to Destroy record. No association found

I am a new student learning about Ruby on Rails. For my current assignment I have to build a Question feature. Everything works but the deletion of a "Question".
I am trying to follow the Form Helper One to Many. Thanks for looking.
Instructions
Complete QuestionsController and its corresponding views. Accept input for resolved in the Question form using a checkbox.
Test your changes in the browser. Confirm that you can:
see an index of all questions,
view an individual question,
create new questions,
edit and update questions, and
delete questions.
When I go to edit a question and then proceed to delete it with the checkbox this error is thrown:
No association found for name `question'. Has it been defined yet?
Here is my console output:
Started GET "/questions/1/edit" for ::1 at 2015-04-23 15:42:11 -0400
Processing by QuestionsController#edit as HTML
Parameters: {"id"=>"1"}
Completed 500 Internal Server Error in 2ms (ActiveRecord: 0.0ms)
ArgumentError (No association found for name `question'. Has it been defined yet?):
app/models/question.rb:3:in `<class:Question>'
app/models/question.rb:1:in `<top (required)>'
app/controllers/questions_controller.rb:27:in `edit'
questions_controller.rb
class QuestionsController < ApplicationController
def index
#questions = Question.all
end
def show
#question = Question.find(params[:id])
end
def create
#question = Question.new(params.require(:question).permit(:title, :body, :resolved))
if #question.save
flash[:notice] = "Question was saved."
redirect_to #question
else
flash[:error] = "There was an error saving the question. Please try again."
render :new
end
end
def new
#question = Question.new
end
def edit
#question = Question.find(params[:id])
end
def update
#question = Question.find(params[:id])
if #question.update_attributes(params.require(:question).permit(:title, :body, :resolved, :_destroy))
flash[:noteice] = "Question was updated."
redirect_to #question
else
flash[:error] = "There was an error saving the question. Please try again."
render :edit
end
end
end
question.rb
class Question < ActiveRecord::Base
has_many :answers
accepts_nested_attributes_for :question, allow_destroy: true
end
edit.html.erb
<div class="col-md-8">
<%= form_for #question do |f| %>
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control', placeholder: "Enter question title" %>
</div>
<div class="form-group">
<%= f.label :body %>
<%= f.text_area :body, rows: 8, class: 'form-control', placeholder: "Enter question body" %>
</div>
<div class="form-group">
<%= f.label :resolved %>
<%= f.check_box :resolved %>
</div>
<div class="form-group">
<%= f.label :destroy %>
<%= f.check_box :_destroy %>
</div>
<div class="form-group">
<%= f.submit "Save", class: 'btn btn-success' %>
</div>
<% end %>
Edit: added my answers.rb.
answers.rb
class Answer < ActiveRecord::Base
belongs_to :question
end
Nested attributes are for the relation. Your Question has many Answers. The point of accepts_nested_attributes_for is that it lets you update "nested" associations from the parent model. The argument is the name of the association to accept attributes for. In this case, your questions should accept nested attributes for their answers:
class Question < ActiveRecord::Base
has_many :answers
accepts_nested_attributes_for :answers
end
See the documentation, where the example class Book accepts nested attributes for author and pages. A Book does not accept nested attributes for a book, similarly your question should not accept nested attributes for a question.
As far as deleting the question, you cannot use accepts_nested_attributes to point a model back to itself so that _destroy will destroy the parent model.
You'll have to check for the presence of that attribute and destroy the record in your controller.
The problem is the accepts_nested_attributes_for line in the Question class.
You wrote
accepts_nested_attributes_for :question
but it likely should be
accepts_nested_attributes_for :answers
since that is the name of the has_many relationship. Rails is barking because there is no such relationship called :question inside the Question class. Your class should look like this:
class Question < ActiveRecord::Base
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end
You need in answers.rb
belongs_to :question
in order to complete the association

Rails 4 - problems with nested attributes

I'm having an inordinate amount of trouble using a nested model with fields_for in a form. Specifically, not all of the nested fields save. A User has many Experiences, but when I submit the form, an Experience with the correct user_id but NO CONTENT is inserted into the database.
Looking at the logs, I also get an error:
unpermitted parameters: experience.
Rails 4 nested attributes not saving doesn't help, unfortunately.
Here's the code:
SCHEMA
create_table "experiences", force: true do |t|
t.string "content", null: false, default: ""
t.integer "user_id"
end
MODEL
#user.rb
class User < ActiveRecord::Base
has_many :experiences
accepts_nested_attributes_for :experiences
#experience.rb
class Experience < ActiveRecord::Base
belongs_to :user
end
CONTROLLER
class UsersController < ApplicationController
def new
#user = User.new
#user.experiences.build
end
def update
#user = current_user
#user.experiences.build
#user.update!(user_params)
redirect_to root_path
end
def user_params
params.require(:user).permit(:username, :email, :password,
:password_confirmation, :title, :blurb, :city, :state,
:style_list, :experience_attributes => [:id, :content])
end
VIEW
<%= form_for #user do |f| %>
<!-- (Omitted) user fields -->
<%= f.fields_for :experience do |experience_fields| %>
<%= experience_fields.text_field :content, placeholder: 'Content' %>
<% end %>
<%= f.submit 'Edit profile' %>
<% end %>
Any help would be greatly appreciated!
Here's your problem:
#user.experiences.build # -> note "experience**s**"
This means when you use fields_for, you have to reference :experiences (you're currently referencing the singular):
<%= f.fields_for :experiences do |experience_fields| %>
<%= experience_fields.text_field :content, placeholder: 'Content' %>
<% end %>
This also goes for your strong_params:
params.require(:user).permit(experiences_attributes: [:id, :content])

Simple comments with voting and karma

I've tried every commenting gem out there and they pretty much all suck.
Take a look at this question I previously asked:Finding user total votes recieved on posts by other users
Per recommendation, I've decided to build my own commenting system from scratch.
Here is the goal:
To have a post model, user model(using devise), comment model.
The users can create comments. These comments are votable. The amount sum of votes users receive on the comments they made is their score or karma.
How do I implement something like this?
So far this is what I have:
I ran
rails generate model Comment commenter:string body:text post:references
The migration
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :commenter
t.text :body
t.references :post
t.timestamps
end
add_index :comments, :post_id
end
end
In my comment model
class Comment < ActiveRecord::Base
belongs_to :post
attr_accessible :commenter, :body
end
In my post model
class Post < ActiveRecord::Base
has_many :comments
end
My routes file
resources :posts do
resources :comments
end
My comments controller
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:commenter, :body))
redirect_to post_path(#post)
end
end
Within my posts show view
<h2>Comments</h2>
<% #post.comments.each do |comment| %>
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<% end %>
<%= form_for([#post, #post.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Edit Post', edit_post_path(#post) %> |
<%= link_to 'Back to Posts', posts_path %>
I really need help here: Instead of picking a commenter name, I need the controller to require the user to be logged in, and pass the current user as the commentor of the comment. How do I implement in place editing of a comment?
I then need to use either one of these gems to make the comments votable:
https://github.com/bouchard/thumbs_up
https://github.com/ryanto/acts_as_votable
Lastly I need to be able to calculate the total votes a given user has received on all of their posted comments. Something like #user.comments.votes.size
To handle assigning the current_user to the comment, first you'll need to change the commenter column to an id that references Users (I would also rename it to commenter_id). So, you'll want to generate the model like so:
rails generate migration ChangeCommentsCommenterToCommenterId
# db/migrate/<timestamp>_change_comments_commenter_to_commenter_id.rb
class ChangeCommentsCommenterToCommenterId < ActiveRecord::Migration
def change
remove_column :comments, :commenter
add_column :comments, :commenter_id, :integer, null: false
add_index :comments, :commenter_id
end
end
Or, regenerate the model from scratch:
rails generate model Comment commenter_id:integer:index body:text post:references
Note that I've added an index to the column. In your Comment model:
# app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :commenter, class_name: 'User'
attr_accessible :body
end
Note that, since we're using belongs_to here, when you send the commenter message to an instance of Comment, you'll get back an instance of User.
Next you'll need to update your controller to make the proper user assignment. I would also recommend a bit of refactoring to private methods to make the implementation more expressive of the domain:
# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
post.comments.create(new_comment_params) do |comment|
comment.commenter = current_user
end
redirect_to post_path(post)
end
private
def new_comment_params
params.require(:comment).permit(:body)
end
def post
#post ||= Post.find(params[:post_id])
end
end
Since you're redirecting to post_path, I've assumed that you don't need to keep the #comment instance variable.
Finally, you'll want to remove the commenter field from the form in the view. Does that do the trick?
[EDIT: Adding this section to address the question about voting...]
I'm not completely clear on exactly what parts of the voting you're looking for help with, but at least from a high-level perspective, I'd guess you'd probably want a VotesController that's accessible from a nested route:
# config/routes.rb
...
resources :comments do
resources :votes, except: :index
end
Hope this helps!

Create comments for two models from comments controller ruby on rails

Ruby on rails newbie here, I have previously been using CakePHP and wanted to allow my create comments controller to create events for two models, any help please?
My comments controller:
def create
#event = Event.find(params[:event_id])
#comment = #event.comments.create(params[:comment].permit(:commenter, :body))
redirect_to event_path(#event)
end
def create
#venue = Venue.find(params[:venue_id])
#comment = #venue.comments.create(params[:comment].permit(:commenter, :body))
redirect_to venue_path(#venue)
end
My create comments view:
<h2>Add a comment:</h2>
<%= form_for([#event, #event.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
This is a classic example for polymorphic association.
There would be a bit of tweaking to get it to work right.
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
...
end
class Venue < ActiveRecord::Base
has_many :comments, as: :commentable
...
end
class Event < ActiveRecord::Base
has_many :comments, as: :commentable
...
end
This will add to your comments Model another attribute called commentable_type so you could differ the types of comments for each Model (Venue, Event)
You would have to run a migration that looks mostly like this
def change
create_table :comments do |t|
t.integer :commenter
t.text :body
t.integer :commentable_id
t.string :commentable_type
t.timestamps
end
end
Now when you migrate you can go to your rails console and see that if you try
Venue.first.comments << Comment.create!(:body => "Body", :commenter => "Guy") # or commenter => 1 depending on your schema
It will be saved to the database as a comment and you can also do the same thing for Event
Now as for your comments controller, I would advise against creating a global comments controller and rather have follow the RESTful approach and have each controller handle his comments.
I.E
# routes.rb
resources :venues
resources :comments
end
resources :events do
resources :comments
end
This way you can both tweak your views according to each controller (venue / events), you follow the RESTful approach as you can use this with either HTML/JSON/XML, you get nicer routes
/events/1/comments # index for all the comments for event 1
/events/1/comments/new # your add a comment form
and same goes for venue.
You can find more info on associations in here http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Good luck!

Resources