I'm trying to make an app in Rails 5 for post answer to the task.
The Task content and the answer form in the same page.
When the user come into the task page, he can see the task content.
And he can post(create) an answer or edit the answer.
I get an error:
NoMethodError in Tasks#show
undefined method `to_key' for #
Did you mean? to_query
to_set
to_ary
What am I doing wrong?
What else information do you need that can help debug this issue?
I'd really apprieciate any help!
Models
```
# task.rb
class Task < ApplicationRecord
belongs_to :post
has_many :answers, dependent: :destroy
end
# answer.rb
class Answer < ApplicationRecord
belongs_to :task
belongs_to :user
end
```
routes.rb
```
resources :tasks, only: [:show] do
resources :answers #, only: [:new, :create, :edit, :update]
end
```
Controllers
```
# tasks_controller.rb
class TasksController < ApplicationController
before_action :authenticate_user!, only: [:show]
def show
#task = Task.find(params[:id])
#post = #task.post
if #task.answers.present?
#answer = Answer.where("task_id = ? and user_id = ?", #task.id, current_user.id)
else
#answer = Answer.new
end
end
end
# answers_controller.rb
def new
#task = Task.find(params[:task_id])
#answer = Answer.new
end
def create
#task = Task.find(params[:task_id])
#answer = Answer.new(answer_params)
#answer.task_id = #task.id
#answer.user_id = current_user.id
if #answer.save
redirect_to post_path(#task.post), notice: "Answer Added."
else
render :new
end
end
```
Views
```
<div class="answer-form">
<%= simple_form_for [#task, #answer], :url => task_answer_path(#task, #answer), method: :put do |f| %>
<% if #task.answers.present? %>
<%= f.input :content, id: "x", value: #task.answers.first.content,
input_html: {class: "hidden"}, name: "content", label: false %>
<trix-editor input="x" class="formatted_content trix-content"></trix-editor>
<div class="form-actions">
<%= f.submit "Submitting", class: "assignment-btn", data: {disable_with: "Submitting..."} %>
</div>
<% end %>
<% else %>
<%= simple_form_for [#task, #answer], :url => task_answers_path(#task), method: :post do |f| %>
<%= f.input :content, id: "x", value: "content",
input_html: {class: "hidden"}, name: "content", label: false %>
<trix-editor input="x" class="formatted_content trix-content"></trix-editor>
<div class="file-upload-block">
<input name="fileToUpload[]" id="fileToUpload" type="file">
</div>
<div class="form-actions">
<%= f.submit "提交", class: "assignment-btn", data: {disable_with: "Submitting..."} %>
</div>
<% end %>
<% end %>
</div>
```
I know what may be wrong, the line below was returning an ActiveRecord::Relation, not the instance itself, you need to append 'first' in the end of the answer query, like such:
#answer = Answer.where(task: #task, user: current_user).first
Related
I am trying to allow reply to comments and for some reason when I create a comment it works uneventfully but when I reply the "body" on params comes back empty. The weird part is that I am using the same form. Please take a look:
Note: I am using Action Text rich_text_area, if I use a simple text_area it works just fine.
models/comment.rb
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
belongs_to :parent, class_name: "Comment", optional: true
has_many :comments, class_name: "Comment", foreign_key: :parent_id
has_rich_text :body
validates_presence_of :body
end
routes.rb
resources :posts do
resources :comments, only: [:create, :update, :destroy]
end
comments_controller.rb
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
respond_to do |format|
format.html do
if #comment.save
flash[:success] = "Comment was successfully created."
else
flash[:danger] = #comment.errors.full_messages.to_sentence
end
redirect_to post_url(#post)
end
end
end
private
def comment_params
params.require(:comment).permit(:body, :parent_id)
end
_comment.rb
// this works (creating a comment without parent)
= render "comments/form", post: #post, comment: #post.comments.build, submit_label: "Comment"
// this won't work (creating a comment with a parent being the current comment)
= render "comments/form", post: #post, comment: #post.comments.build, parent: comment, submit_label: "Reply"
- _form.rb
<%= form_with model: [post, comment], class: "form" do |f| %>
<% if !parent.nil? %>
<%= f.hidden_field :parent_id, value: parent.id %>
<% end %>
<div class="field">
<%= f.rich_text_area :body, placeholder: "What do you think?" %>
</div>
<div class="actions">
<%= f.submit submit_label, class: "btn btn-primary" %>
</div>
<% end %>
What puzzles me the most is that the problem is that the body comes back empty. I cant't see the relation between the body and everything else.
Parameters: {"authenticity_token"=>"[FILTERED]", "comment"=>{"body"=>"", "parent_id"=>"14"}, "commit"=>"Reply", "post_id"=>"4"}
Thanks in advance!
In my views I have a form and trying to update quantity for an order line:
<div class="quantity">Quantity</br>
<%= form_tag(order_line_path(line.id), method: "patch") do %>
<%= number_field_tag 'qty', '1', within: 1...line.book.stock %>
<%= submit_tag "Update", class: "btn btn-primary" %>
<% end %>
</div>
The instance variable in the rest of my view is a collection of order lines, so I cannot use it.
Then I have in my controller the update method:
def update
#order = current_order
#order_line = #order.order_lines.find(params[:id])
#order_line.update_attributes(order_line_params)
end
And my strong params definition:
def order_line_params
params.require(:order_line).permit(:qty)
end
I get this error :
param is missing or the value is empty: order_line
Could someone please have a look?
Thanks!
The reason you are getting param is missing or the value is empty: order_line is that you are using form_tag which gives a "flat" params hash.
However this is easily avoidable if you just use form_with/form_for.
# routes.rb
resources :orders do
resources :line_items, shallow: true
end
# app/views/order_items/edit.html.erb
# use `form_with(model: #order_item)` in Rails 5
<%= form_for(#order_item) do |f| %>
<%= f.label :qty, within: 1...f.object.book.stock %>
<%= f.number_field :qty, %>
<%= f.submit %>
<% end %>
class OrderItemsController < ApplicationController
before_action :set_order_item, only: [:show, :edit, :update, :destroy]
# ...
# GET /order_items/:id/edit
def edit
end
# PATCH /order_items/:id
def update
if #order_item.update(order_item_params)
redirect_to #order_item, notice: 'Line updated'
else
render :edit
end
end
private
def set_order_item
#order_item = OrderItem.find(params[:id])
end
def order_item_params
params.require(:order_item).permit(:qty)
end
end
But what you're really looking for unless you are doing the update/creation of nested items with AJAX is most likely a combination of accepts_nested_attributes and fields_for which will let the user mass edit the line items:
class Order < ApplicationRecord
accepts_nested_attributes_for :order_items
end
<%= form_for(#order) do |f| %>
<%= fields_for(:order_items) do |oif| %>
<%= f.label :qty, within: 1...f.object.book.stock %>
<%= f.number_field :qty, %>
<% end %>
<%= f.submit %>
<% end %>
class OrdersController < ApplicationController
# PATCH /orders/:id
def update
if #order.update(order_params)
redirect_to #order, notice: 'Order updated.'
else
render :new
end
end
private
def order_params
params.require(:order).permit(order_items_attributes: [:qty])
end
end
I am working on a project for which I have to analyse data inside a file, which the user should be able to upload. I have a model InputFile and chose to implement an analysis_facade for my Analysis model which bundles most of the functionality.
Here are the models
#app/models/analysis.rb
class Analysis < ApplicationRecord
has_one :input_file, dependent: :destroy
end
#app/models/input_file.rb
class InputFile < ApplicationRecord
belongs_to :analysis
has_many :access_data, dependent: :destroy
def uploaded_file=(file_field)
self.name = File.basename(file_field.original_filename).gsub(/[^\w._-]/,'')
self.content_type = file_field.content_type.chomp
self.data = file_field.read
end
end
Here comes my AnalysisController and the corresponding facade pattern
#app/controllers/analyses_controller.rb
class AnalysesController < ApplicationController
before_action :set_analysis, only: :show
def show
#analysis_facade = AnalysisFacade.new(#analysis)
end
private
def set_analysis
#analysis ||= Analysis.create
end
end
#app/facades/analysis_facade.rb
class AnalysisFacade
attr_reader :analysis
def initialize(analysis)
#analysis = analysis
end
def new_input_file
#input_file = analysis.build_input_file
end
def input_file
#input_file ||= new_input_file
end
def access_data
#access_data ||= analysis.input_file.access_data
end
end
My controller for InputFiles
#app/controllers/input_files_controller.rb
class InputFilesController < ApplicationController
include LogFileScanner
before_action :set_analysis, only: [:new, :create, :update]
def new
#input_file = Input_file.new
end
def create
#input_file = #analysis.build_input_file(input_file_params)
if #input_file.save
access_data = scan(#input_file.data)
#input_file.access_data.create(access_data)
end
end
def update
#input_file = #analysis.input_file
if #input_file.update
access_data = scan(#input_file.data)
#input_file.access_data.update(access_data)
end
end
private
def input_file_params
params.require(:input_file).permit(:uploaded_file, :analysis_id)
end
def set_analysis
#analysis = Analysis.find(params[:input_file][:analysis_id])
end
end
For the file upload I have these views
#app/views/analyses/show.html.erb
<%= render template: 'input_files/get' %>
#app/views/input_files/get.html.erb
<%= form_with model: [#analysis_facade.analysis, #analysis_facade.input_file], action: :update, html: {multipart: true, class: 'form-horizontal center'} do |form| %>
<div class='form-group.new'>
<% if false %>
<%= form.hidden_field :analysis_id, value: #analysis_facade.analysis[:id] %>
<% end %>
<%= form.file_field 'uploaded_file' %>
<%= form.submit "Upload", class: 'btn btn-default btn-primary' %>
</div>
<% end %>
Executing this results in error
undefined method `analysis_input_files_path' for #<#<Class:0x00007f1208040288>:0x00007f11f0d71d48>
Did you mean? analysis_path
for this loc
<%= form_with model: [#analysis_facade.analysis, #analysis_facade.input_file], action: :update, html: {multipart: true,
class: 'form-horizontal center'} do |form| %>
Why is that? I need to pass the analysis_id to params in order to create the new InputFile. But I want it to be cleaner than manually adding a hidden_field, since model_with should provide this feature. What do I miss here?
Also: Why does rails search for `analysis_input_files_path'? Its clearly a has_one relation, so shouldn't it look for analysis_input_file_path then?
EDIT:
#bin/rails routes
Prefix Verb URI Pattern Controller#Action
analyses GET /analyses(.:format) analyses#index
POST /analyses(.:format) analyses#create
new_analysis GET /analyses/new(.:format) analyses#new
edit_analysis GET /analyses/:id/edit(.:format) analyses#edit
analysis GET /analyses/:id(.:format) analyses#show
PATCH /analyses/:id(.:format) analyses#update
PUT /analyses/:id(.:format) analyses#update
DELETE /analyses/:id(.:format) analyses#destroy
input_file_access_data GET /input_files/:input_file_id/access_data(.:format) access_data#index
POST /input_files/:input_file_id/access_data(.:format) access_data#create
new_input_file_access_datum GET /input_files/:input_file_id/access_data/new(.:format) access_data#new
edit_access_datum GET /access_data/:id/edit(.:format) access_data#edit
access_datum GET /access_data/:id(.:format) access_data#show
PATCH /access_data/:id(.:format) access_data#update
PUT /access_data/:id(.:format) access_data#update
DELETE /access_data/:id(.:format) access_data#destroy
input_files GET /input_files(.:format) input_files#index
POST /input_files(.:format) input_files#create
new_input_file GET /input_files/new(.:format) input_files#new
edit_input_file GET /input_files/:id/edit(.:format) input_files#edit
input_file GET /input_files/:id(.:format) input_files#show
PATCH /input_files/:id(.:format) input_files#update
PUT /input_files/:id(.:format) input_files#update
DELETE /input_files/:id(.:format) input_files#destroy
analyses_show GET /
I was a conceptional mistake. I changed the facade model to
class AnalysisFacade
attr_reader :analysis
def initialize(analysis)
#analysis = analysis
end
def new_input_file
#analysis.build_input_file
end
def input_file
#analysis.input_file ||= new_input_file
end
def access_data
#analysis.input_file.access_data
end
end
And the view works now like this
<%= form_with model: #analysis_facade.input_file, html: {multipart: true, class: 'form-horizontal center'} do |form| %>
<div class='form-group.new'>
<%= form.file_field 'uploaded_file' %>
<%= form.submit "Upload", class: 'btn btn-default btn-primary' %>
</div>
<% end %>
I'm getting a curious error after submitting my form. Been trying to solve this for several hours..
No route matches {:action=>"show", :controller=>"items", :item_id=>"141", :matter_id=>"3"} missing required keys: [:id]
The parameters are:
{"utf8"=>"✓",
"authenticity_token"=>"w0D7XmX2X2/ZMU19T6RlMvWCEClXnCFFOR+4EdIFvWg=",
"comment_item"=>{"item_id"=>"",
"name"=>"kaljdf",
"body"=>"yet another comment test"},
"commit"=>"Post Comment",
"matter_id"=>"3",
"item_id"=>"141"}
I have the following models:
class Matter < ActiveRecord::Base
has many :discoveries
delegate :items, to: :discoveries
end
class Discovery < ActiveRecord::Base
belongs_to :matter
scope :items, -> { where(type: 'Item') }
end
class Item < Discovery
has_many :comment_items
end
class CommentItem < ActiveRecord::Base
belongs_to :item
end
Controllers:
class ItemsController < DiscoveriesController
def show
#item = Item.find(params[:id])
#comment_item = CommentItem.new
end
def edit
#item = Item.find(params[:id])
end
def new
#item = Item.new
end
end
class CommentItemsController < ApplicationController
before_action :set_comment_item, only: [:show, :edit, :update, :destroy]
def new
#item = Item.find(params[:item_id])
#comment_item = #item.comment_item.new
end
def create
#item = Item.find(params[:item_id])
#comment_item = #item.comment_items.new(comment_item_params)
if #comment_item.save
flash[:notice] = 'Comment was successfully created'
redirect_to matter_item_url(matter_id: params[:matter_id])
else
flash[:notice] = "Error creating comment: #{#comment.errors}"
redirect_to matter_item_url(#matter, #item)
end
end
def destroy
#comment_item = CommentItem.find(params[:id])
#comment_item.destroy
redirect_to(#comment_item.item)
end
private
def set_comment_item
#comment_item = CommentItem.find(params[:id])
end
def comment_item_params
params.require(:comment_item).permit(:name, :body, :item_id, :matter_id)
end
end
The show action for the item resource:
<p>
<strong>Matter:</strong>
<%= #item.matter_id %>
</p>
<p>
<strong>Content:</strong>
<%= #item.content %>
</p>
<hr />
<%= form_for #comment_item, url: matter_item_comment_items_path(matter_id: #item.matter, item_id: #item.id) do |f| %>
<% if #comment_item.errors.any? %>
<ul>
<% #comment_item.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= f.hidden_field :item_id %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit "Post Comment" %>
</p>
<% end %>
<%= render :partial => 'comment_item', :collection => #item.comment_items %>
<%= link_to 'Edit', edit_matter_item_path(id: #item.id) %> |
<%= link_to 'Back', matter_items_path %>
Routes
resources :items do
resources :comment_items
end
resources :matters do
resources :items do
resources :comment_items
end
end
When looking at CommentItems in the console, I see that the comments are in fact being added to the model with their correct ID's, but they don't seem to be passed as parameters.. What am I missing?
I've reviewed Rails 4 form_for double nested comments and Rails 3.2 - Nested Resource Passing ID but I didn't have much luck..
I'd really appreciate your help!
No route matches {:action=>"show", :controller=>"items", :item_id=>"141", :matter_id=>"3"} missing required keys: [:id]
your request is going to ItemsController instead of CommentItemsController
see :controller => "items"
For my application, I have Projects. I have used Polymorphism to build a model called "Newcomment" for comments made on these Projects. I followed this railscast. This works great.
But now, I want to build comments on top of comments. I tried following this tutorial (http://kconrails.com/2010/10/23/nested-comments-in-ruby-on-rails-1-models/) and (http://kconrails.com/2011/01/26/nested-comments-in-ruby-on-rails-controllers-and-views/). I put a form for comments in each comment that I render. I also adjusted the newcomment.rb model, so that newcomment has_many newcomments and the routes.rb file.
Question: Right now, when I make a comment in the form of each comment, it posts as a comment to the project and not as a response to a specific comment. How would I adjust my code so that I can have comments for comments?
newcomment.rb
class Newcomment < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :commentable, polymorphic: true
has_many :newcomments, :as => :commentable
belongs_to :user
scope :newest, order("created_at desc")
validates :content, presence: true
end
newcomments_controller.rb
class NewcommentsController < ApplicationController
before_filter :load_commentable
before_filter :authenticate_user!
def create
#newcomment = #commentable.newcomments.new(params[:newcomment])
if #newcomment.save
redirect_to comments_project_path(#commentable), notice: "Comment created."
else
render :new
end
end
def destroy
if current_user.try(:admin?)
#newcomment = Newcomment.find(params[:id])
#commentable = #newcomment.commentable
#newcomment.destroy
if #newcomment.destroy
redirect_to comments_url, notice: "Comment deleted."
end
else
#newcomment = Newcomment.find(params[:id])
#commentable = #newcomment.commentable
#newcomment.destroy
if #newcomment.destroy
redirect_to comments_project_path(#commentable), notice: "Comment deleted."
end
end
end
private
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
end
routes.rb
resources :projects do
resources :newcomments do
resources :newcomments
end
end
view/projects/_comments.html.erb
<%= render #newcomments %>
projects_controller.rb
def comments
#commentable = #project
#newcomments = #commentable.newcomments.newest.page(params[:comments_page]).per_page(10)
#newcomment = Newcomment.new
end
view/newcomments/_newcomment.html.erb
<div class="comments">
<%= link_to newcomment.user.name %></strong>
Posted <%= time_ago_in_words(newcomment.created_at) %> ago
<%= newcomment.content %>
</div>
<span class="comment">
<%= form_for [#commentable, #newcomment] do |f| %>
<div class="field">
<%= f.text_area :content, rows: 3, :class => "span8" %>
</div>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="actions">
<%= f.submit "Add Comment", :class => "btn btn-header" %>
</div>
<% end %>
<% unless newcomment.newcomments.empty? %>
<%= render #newcomments %>
<% end %>
</span>
All you need, instead of working on it from a scrap, is: https://github.com/elight/acts_as_commentable_with_threading
You should not have the routes like this
resources :newcomments do
resources :newcomments
end
which is a bad smell and we need to fix. In some of our projects, we are also using https://github.com/elight/acts_as_commentable_with_threading and it's good.