I am trying to learn how to use namespaced models in my Rails 5 app so as to better organise my content.
I have an address model. It is polymorphic. Each of Settings and Organisation have many addresses. Settings is a model which is namespaced under user.
The associations are
User
has_one :setting, dependent: :destroy
Setting
belongs_to :user
has_many :addresses, as: :addressable#, class_name: Address
accepts_nested_attributes_for :addresses, reject_if: :all_blank, allow_destroy: true
Organisation
has_many :addresses, as: :addressable#, class_name: Address
accepts_nested_attributes_for :addresses, reject_if: :all_blank, allow_destroy: true
Address
belongs_to :addressable, :polymorphic => true, optional: true
Routes - organisation
resources :organisations do
namespace :contacts do
resources :addresses
resources :phones
end
end
Routes - setting
resources :users, shallow: true do
scope module: :users do
resources :identities
resources :settings do
namespace :contacts do
resources :addresses
resources :phones
end
end
end
end
Organisation form
<%= f.simple_fields_for :addresses do |f| %>
<%= f.error_notification %>
<%= render 'contacts/addresses/address_fields', f: f %>
<% end %>
<%= link_to_add_association 'Add another address', f, :addresses, partial: 'contacts/addresses/address_fields' %>
user / setting form
<%= simple_form_for [#user, #setting] do |f| %>
<%= f.error_notification %>
<%= simple_fields_for :addresses do |f| %>
<%= f.error_notification %>
<%= render 'contacts/addresses/address_fields', f: f %>
<% end %>
<%= link_to_add_association 'Manage address', f, :addresses, partial: 'contacts/addresses/address_fields' %>
</div>
<% end %>
Everything about my address functionality works fine for Organisation, but I have a problem in getting it to work for my settings.
The problem I have when I try to use the user/setting form to add an address is that I get an error that says:
ActionController::RoutingError at /contacts/addresses/1/edit
uninitialized constant Users::Contacts
There isn't a direct association between user and contacts. Contacts is the name of the namespaced folder I use to store my address views and controller.
Can anyone see what I need to do in order to be able to access the address form functionality from my user settings form?
When I rake routes for setting, I can see the path format to get settings address, but I can't figure out how to use it.
rake routes | grep setting
setting_contacts_addresses GET /settings/:setting_id/contacts/addresses(.:format) users/contacts/addresses#index
POST /settings/:setting_id/contacts/addresses(.:format) users/contacts/addresses#create
new_setting_contacts_address GET /settings/:setting_id/contacts/addresses/new(.:format) users/contacts/addresses#new
SETTINGS CONTROLLER
class Users::SettingsController < ApplicationController
before_action :set_setting, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
after_action :verify_authorized
def index
#settings = Setting.all
authorize #settings
end
def show
# authorize #setting
#addresses = #setting.addresses.all
#phones = #setting.phones
end
def new
#setting = Setting.new
#setting.addresses_build
#setting.phones_build
authorize #setting
end
def edit
#setting.addresses_build unless #setting.addresses
#setting.phones_build unless #setting.phones
end
def create
#setting = Setting.new(setting_params)
authorize #setting
respond_to do |format|
if #setting.save
format.html { redirect_to #setting }
format.json { render :show, status: :created, location: #setting }
else
format.html { render :new }
format.json { render json: #setting.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #setting.update(setting_params)
format.html { redirect_to #setting }
format.json { render :show, status: :ok, location: #setting }
else
format.html { render :edit }
format.json { render json: #setting.errors, status: :unprocessable_entity }
end
end
end
def destroy
#setting.destroy
respond_to do |format|
format.html { redirect_to settings_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_setting
#setting = Setting.find(params[:id])
authorize #setting
end
# Never trust parameters from the scary internet, only allow the white list through.
def setting_params
params.require(:setting).permit( :newsletter,
addresses_attributes: [:id, :description, :unit, :building, :street_number, :street, :city, :region, :zip, :country, :time_zone, :latitude, :longitude, :_destroy],
phones_attributes: [:phone_number, :country, :phone_type],
)
end
end
ORGANISATIONS CONTROLLER
class OrganisationsController < ApplicationController
before_action :set_organisation, only: [:show, :edit, :update, :destroy]
def index
#organisations = Organisation.all
authorize #organisations
end
def show
#addresses = #organisation.addresses.all
# #hash = Gmaps4rails.build_markers(#addresses) do |address, marker|
# marker.lat address.latitude
# marker.lng address.longitude
# marker.infowindow address.full_address
# end
#bips = #organisation.bips
#proposals = #organisation.proposals#.in_state(:publish_openly)
end
def new
#organisation = Organisation.new
#organisation.addresses#_build
end
def edit
#organisation.addresses_build unless #organisation.addresses
end
def create
#organisation = Organisation.new(organisation_params)
respond_to do |format|
if #organisation.save
format.html { redirect_to #organisation, notice: 'Organisation was successfully created.' }
format.json { render :show, status: :created, location: #organisation }
else
format.html { render :new }
format.json { render json: #organisation.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #organisation.update(organisation_params)
format.html { redirect_to #organisation, notice: 'Organisation was successfully updated.' }
format.json { render :show, status: :ok, location: #organisation }
else
format.html { render :edit }
format.json { render json: #organisation.errors, status: :unprocessable_entity }
end
end
end
def destroy
#organisation.destroy
respond_to do |format|
format.html { redirect_to organisations_url, notice: 'Organisation was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_organisation
#organisation = Organisation.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def organisation_params
params.fetch(:organisation, {}).permit(:title, :comment,
addresses_attributes: [:id, :description, :unit, :building, :street_number, :street, :city, :region, :zip, :country, :time_zone, :latitude, :longitude, :_destroy],
phones_attributes: [:id, :phone_number, :country, :phone_type, :_destroy]
)
end
end
I don't quite understand what you're trying to accomplish. These codes below allow you to create a new setting for an user. You can add more addresses to that setting. If you want to edit an address'name, you have to go to the edit page of the setting that address belongs to.
routes.rb
resources :users, shallow: true do
scope module: :users do
resources :settings
end
end
users/settings_controller.rb
class Users::SettingsController < ApplicationController
before_action :prepare_user, only: [:index, :new, :create]
before_action :prepare_setting, only: [:show, :edit, :update]
def new
#setting = Setting.new
end
def create
#setting = #user.build_setting(setting_params)
if #setting.save
redirect_to #setting
else
render 'new'
end
end
def show
end
def edit
end
def update
if #setting.update(setting_params)
redirect_to #setting
else
render 'edit'
end
end
private
def prepare_user
#user = User.find(params[:user_id])
end
def prepare_setting
#setting = Setting.find(params[:id])
end
def setting_params
params.require(:setting).permit(:name, addresses_attributes: [:name, :id])
end
end
users/settings/new.html.erb
<h1>New Setting</h1>
<%= render 'form' %>
users/settings/_form.html.erb
<%= simple_form_for [#user, #setting] do |f| %>
<%= f.input :name %>
<div>
<%= f.simple_fields_for :addresses do |f| %>
<%= f.error_notification %>
<%= render 'contacts/addresses/address_fields', f: f %>
<% end %>
<%= link_to_add_association 'Add another address', f, :addresses, partial: 'contacts/addresses/address_fields' %>
</div>
<%= f.submit %>
<% end %>
contacts/addresses/address_fields.html.erb
<%= f.input :name, label: 'Address name' %>
<%= f.input :id, as: :hidden %>
Related
I want that when the user creates a project s/he could also add new tasks into it. I have figured out this "solution" and I have encountred this error that I can't solve.
I dont even know of it is "ruby correct" that I mix tasks actions with the projects controller. The reason I took this approach is that I couldn't figure out for a solution for doing so in the views and make the project be saved..
This is my projects_controler.rb
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def create
#project = Project.new(project_params)
#task = Task.new(task_params)
respond_to do |format|
if #project.save and #task.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /projects/1
# PATCH/PUT /projects/1.json
def update
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { render :show, status: :ok, location: #project }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# DELETE /projects/1
# DELETE /projects/1.json
def destroy
# #list = List.find(params[:id])
#project.destroy
respond_to do |format|
format.html { redirect_to projects_url, notice: 'Project was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def project_params
params.require(:project).permit(:title, :description, :done, :user_id)
end
def set_task
#task = Task.find(params[:id])
end
def task_params
params.require(:task).permit(:title, :done, :project_id)
end
end
project.rb
class Project < ApplicationRecord
belongs_to :user, foreign_key: "user_id"
has_many :tasks , dependent: :destroy
validates_presence_of :user
validates_uniqueness_of :title, on: :create, message: "must be unique"
validates :title, presence: true, length: { minimum: 2 }
def done_tasks
tasks.where(done: true).order("updated_at DESC")
end
end
task.rb
class Task < ApplicationRecord
belongs_to :project, foreign_key: "project_id"#, class_name: "List"
validates_presence_of :title
def completed?
!completed_at.blank?
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
resources :projects do
resources :tasks
end
authenticated :user do
root to: 'projects#index'
end
unauthenticated :user do
root 'welcome#home'
end
end
new.html.erb
<h1>New Project</h1>
<%= render 'form', project: #project, task: #task %>
<%# render 'form', project: #project, task: #task %>
<!-- custom -->
<!-- shall I ADD ID TO THE projrct t be trackable by the task????? and describe that in task.rb -->
<!-- <h1>Tasks</h1> -->
<%# f.button %>
<!-- button to add a new task -->
<%# f.button "add a new task to the current project" %>
<!-- if else mechanism for adding a new task via a button -->
<!-- when button pressed, tasks/_form will appear down here -->
<!-- button to save the task while at the same moment ..... override "create task" and "create project" to save "save project to task" -->
<%# render '../tasks/_form' %>
<%= link_to 'Back', projects_path %>
_form.html.erb
<%= simple_form_for(#project, #task) do |f| %>
<%# form_for([#post, #comment]) do |f| %>
<% f.error_notification %>
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :description %>
<%= f.input :done %>
<%= f.association :user %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
In task_params
params.require(:task).permit(:title, :done)
and create method should be like
def create
#project = Project.new(project_params)
#project.tasks.build(task_params)
respond_to do |format|
if #project.save and #task.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
I have an error when I try create a product: "Cannot build association 'type_data'. Are you trying to build a polymorphic one-to-one association?"
I don't know what is happend. I saw a lot examples with the same code but mine doesn't work.
Here my product.rb
class Product < ApplicationRecord
belongs_to :type_data, polymorphic: true
accepts_nested_attributes_for :type_data, allow_destroy: true
end
Here my hotel_room.rb
class HotelRoom < ApplicationRecord
has_one :product, as: :type_data, dependent: :destroy
has_many :rates
accepts_nested_attributes_for :rates, allow_destroy: true
end
My products/_form.html.erb
<%= f.fields_for :type_data do |builder| %>
<%= render "edit_hotel_account_fields", f: builder %>
<% end %>
My products/_edit_account_fields.heml.erb
<%= f.fields_for :rates do |builder| %>
<%= render 'rate_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add rate", f, :rates %>
My products/_rate_fields.html.erb
<div class="form-group">
<label><%= t('products.new.rates.double_price') %></label>
<%= f.number_field :double_price, step: 0.1, class: 'form-control' %>
</div>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
And this is all my products_controller.rb
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to products_path, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
# AJAX cargas más fechas
def date_range
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:status, :provider_id, :commission, :title, :commercial_title, :title_seo, :description, :seo_description, :slug_web, :slug_redirection, :services, :info, :province, :state, :city, :country, :cancel_conditions, :limit_children_age, :special_alert, :notes, :stock, :iva, :minimum_advance, type_data_attributes: [:room_stock, :room_service, :_destroy, rates_attributes: [:title, :start_date, :end_date, :hotel_room_id, :individual_price, :double_price, :child_bed_price, :adult_bed_price, :adult_breakfast_price, :adult_medium_price, :adult_complete_price, :child_breakfast_price, :child_medium_price, :child_complete_price, :_destroy]], dates: {}, map: {}, address_autocomplete: {})
end
end
Do anyone know what is happend?
Thanks!
As Hotel model has one Product via polymorphic,
class HotelRoom < ApplicationRecord
has_one :product, as: :type_data, dependent: :destroy
accepts_nested_attributes_for :product, allow_destroy: true
accepts_nested_attributes_for :rates, allow_destroy: true
....
end
In hotel_rooms controller new action
def new
#hotel_room = HotelRoom.new
#hotel_room.build_product
end
In Hotel view form
<%= form_for(#hotel_room) do |f| %>
....
<%= f.fields_for(:product) do |p| %>
....
<% end %>
<% end %>
Note:
If you have common data between HotelRoom and Product, then you need to introduce another model like TypeData and set it polymorphic.
I'm making a invoice application where i have the #invoice form and inside it i have nested forms for customers, products, and company information. The products form is working fine in all views but the customer form isn't. When i fill in the customer information and create a new invoice it works. But when i try to edit that invoice the entire form is gone.
invoice/_form
<%= form_for #invoice do |f| %>
<% if #invoice.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#invoice.errors.count, "error") %> prohibited this invoice from being saved:</h2>
<ul>
<% #invoice.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :customer do |customer| %>
<div class="field">
<%= customer.label 'Bedrijfsnaam ontvanger' %><br/>
<%= customer.text_field :company_name, placeholder: 'bedrijfsnaam', class: 'form-control' %>
</div>
<div class="field">
<%= customer.label 'Adres ontvanger' %><br>
<%= customer.text_field :address_line_1, placeholder: 'adres ontvanger', class: 'form-control' %>
</div>
<div class="field">
<%= customer.label 'Postcode & stad' %><br>
<%= customer.text_field :zip_code, placeholder: '1234AB Rotterdam', class: 'form-control' %>
</div>
<% end %>
Invoices_controller.rb
class InvoicesController < ApplicationController
before_action :set_invoice, only: [:show, :edit, :update, :destroy]
# GET /invoices
# GET /invoices.json
def index
#invoices = Invoice.all
end
# GET /invoices/1
# GET /invoices/1.json
def show
end
# GET /invoices/new
def new
#invoice = Invoice.new
#invoice.products.build
#invoice.build_customer
end
# GET /invoices/1/edit
def edit
end
# POST /invoices
# POST /invoices.json
def create
#invoice = Invoice.new(invoice_params)
respond_to do |format|
if #invoice.save
format.html { redirect_to #invoice, notice: 'Invoice was successfully created.' }
format.json { render :show, status: :created, location: #invoice }
else
format.html { render :new }
format.json { render json: #invoice.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /invoices/1
# PATCH/PUT /invoices/1.json
def update
respond_to do |format|
if #invoice.update(invoice_params)
format.html { redirect_to #invoice, notice: 'Invoice was successfully updated.' }
format.json { render :show, status: :ok, location: #invoice }
else
format.html { render :edit }
format.json { render json: #invoice.errors, status: :unprocessable_entity }
end
end
end
# DELETE /invoices/1
# DELETE /invoices/1.json
def destroy
#invoice.destroy
respond_to do |format|
format.html { redirect_to invoices_url, notice: 'Invoice was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_invoice
#invoice = Invoice.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def invoice_params
params.require(:invoice).permit(:number, :currency, :date, :duedate, :btwtotal,
:subtotal, :total, :footer, customers_attributes: [:id, :company_name, :address_line_1, :zip_code, :_destroy],
companies_attributes: [:id, :btw_number, :iban_number, :kvk_number, :company_name, :_destroy],
products_attributes: [:id, :quantity, :description, :unitprice, :btw, :total])
end
end
Invoice.rb - (model)
class Invoice < ActiveRecord::Base
has_one :company
has_one :customer
has_many :products
accepts_nested_attributes_for :customer, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :products, reject_if: :all_blank, allow_destroy: true
validates :number, :currency, :date, :duedate, :btwtotal, :subtotal, :total, presence: true
end
In the invoices controller try changing customers_attributes to customer_attributes in your strong params:
customer_attributes: [:id, :company_name, :address_line_1, :zip_code, :_destroy]
I suspect this is an issue where your customer nested attributes are not being properly saved, so when you go to the edit view that part of the form isn't being rendered because there isn't any customer saved for your invoice
I am building an app that allows a user to create a contest. Each contest has many questions and each contests has many entries. Each entry has many answers and each question has many answers. Here are my models:
class Answer < ActiveRecord::Base
belongs_to :entry
belongs_to :question
end
class Contest < ActiveRecord::Base
has_many :entries
has_many :questions
end
class Entry < ActiveRecord::Base
belongs_to :contest
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end
class Question < ActiveRecord::Base
has_many :answers
belongs_to :contest
end
Everything works except for when I try to create an entry. I get a "param is missing or the value is empty: entry" error. Here is my controller:
class EntriesController < ApplicationController
before_action :set_entry, only: [:show, :edit, :update, :destroy]
before_action :set_contest
# GET /entries
# GET /entries.json
def index
#entries = Entry.all
end
# GET /entries/1
# GET /entries/1.json
def show
end
# GET /entries/new
def new
#entry = Entry.new
end
# GET /entries/1/edit
def edit
end
# POST /entries
# POST /entries.json
def create
#entry = Entry.new(entry_params)
#entry.contest = #contest
respond_to do |format|
if #entry.save
format.html { redirect_to #entry, notice: 'Entry was successfully created.' }
format.json { render :show, status: :created, location: #entry }
else
format.html { render :new }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /entries/1
# PATCH/PUT /entries/1.json
def update
respond_to do |format|
if #entry.update(entry_params)
format.html { redirect_to #entry, notice: 'Entry was successfully updated.' }
format.json { render :show, status: :ok, location: #entry }
else
format.html { render :edit }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
# DELETE /entries/1
# DELETE /entries/1.json
def destroy
#entry.destroy
respond_to do |format|
format.html { redirect_to entries_url, notice: 'Entry was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_entry
#entry = Entry.find(params[:id])
end
def set_contest
#contest = Contest.find(params[:contest_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def entry_params
params.require(:entry).permit(:contest_id, answers_attributes: [:id, :content, :entry_id, :question_id, :_destroy])
end
end
And here is my entry form:
<%= simple_form_for([#contest, #entry]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<h3>Questions</h3>
<%= simple_fields_for :answers do |ff| %>
<% #contest.questions.each do |question| %>
<h4><%= question.content %></h4>
<%= ff.input :content, input_html: {class: 'form-control'} %>
<% end %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
I am still working on the logic but am perplexed as to why the entry form is giving me this error. Any help would be appreciated!
UPDATE
In the Rails Guide example they show the new action as:
def new
#person = Person.new
2.times { #person.addresses.build}
end
Do I need to build the answer objects in my new action? I'm not sure... I tried it but it didn't work. I feel like that can't be the problem though as the error is coming from the entry_params method
You should be adding this line to your new action.
#entry.answers.build
And change this line
<%= simple_fields_for :answers do |ff| %>
to
<%= f.simple_fields_for :answers do |ff| %>
I'm getting an error raised of "undefined method 'comments_path' for..."
At this code in app/views/comments/_form.html.erb (line 1).
I recently tried to implement nestable comments via polymorphic associations on a website I'm building; however, I'm running into a problem that's not allowing me to move forward.
I'm trying to implement nested comments on a 'commentable' model (ie the blog in this case) and then when show is clicked, all the nested comments are shown and the individual can comment on the blog or reply to a comment (and hence result in nested comments) without leaving the page.
I've included a few other files to show the setup, but if I've missed one that's necessary, please let me know and I'll promptly add it. Any help is much appreciated. I've been stumped for several hours and I'm sure its something simple.
<%= form_for [#commentable, #comment] do |f| %>
<%= f.hidden_field :parent_id %></br>
<%= f.label :content, "New Comment" %></br>
<%= f.text_area :body, :rows => 4 %></br>
<%= f.submit "Submit Comment" %>
<% end %>
app/views/blogs/show.html.erb
<div class="content">
<div class="large-9 columns" role="content">
<h2>Comments</h2>
<div id="comments">
<%= nested_comments #comments %>
<%= render "comments/form" %>
</div>
</div>
</div>
app/controllers/comments_controller
class CommentsController < ApplicationController
def new
#parent_id = params.delete(:parent_id)
#commentable = find_commentable
#comment = Comment.new( :parent_id => #parent_id,
:commentable_id => #commentable.id,
:commentable_type => #commentable.class.to_s)
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
if #comment.save
flash[:notice] = "Successfully created comment."
redirect_to #commentable
else
flash[:error] = "Error adding comment."
end
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def comment_params
params.require(:comment).permit(:parent_id, :body, :commentable_type, :commentable_id)
end
end
app/controller/blogs_controller.rb
class BlogsController < ApplicationController
before_filter :authenticate, :except => [ :index, :show ]
before_action :set_blog, only: [:show, :edit, :update, :destroy]
include MyModules::Commentable
# GET /blogs
# GET /blogs.json
def index
#blogs = Blog.all.order('created_at DESC')
respond_to do |format|
format.html
format.json
format.atom
end
end
# GET /blogs/1
# GET /blogs/1.json
def show
#blog = Blog.find(params[:id])
end
# GET /blogs/new
def new
#blog = Blog.new
end
# GET /blogs/1/edit
def edit
#blog = Blog.find(params[:id])
end
# POST /blogs
# POST /blogs.json
def create
#blog = Blog.new(blog_params)
respond_to do |format|
if #blog.save
flash[:success] = "Blog was sucessfuly created"
format.html { redirect_to #blog }
format.json { render :show, status: :created, location: #blog }
else
format.html { render :new }
format.json { render json: #blog.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /blogs/1
# PATCH/PUT /blogs/1.json
def update
respond_to do |format|
if #blog.update(blog_params)
flash[:success] = "Blog was successfully updated."
format.html { redirect_to #blog }
format.json { render :show, status: :ok, location: #blog }
else
format.html { render :edit }
format.json { render json: #blog.errors, status: :unprocessable_entity }
end
end
end
# DELETE /blogs/1
# DELETE /blogs/1.json
def destroy
#blog.destroy
respond_to do |format|
flash[:success] = "Blog was successfully deleted."
format.html { redirect_to blogs_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_blog
#blog = Blog.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def blog_params
params.require(:blog).permit(:title, :body, :image, :image_cache, :remote_image_url)
end
private
def authenticate
authenticate_or_request_with_http_basic do |name, password|
name == "admin" && password == "runfast"
end
end
end
my routes file looks like this...
Rails.application.routes.draw do
resources :blogs do
resources :comments
end
resources :applications
resources :reviews
resources :properties
root to: 'blogs#index'
end
the blog and comment models...
class Blog < ActiveRecord::Base
validates_presence_of :body, :title
has_many :comments, :as => :commentable, :dependent => :destroy
mount_uploader :image, ImageUploader
end
class Comment < ActiveRecord::Base
has_ancestry
belongs_to :commentable, :polymorphic => true
validates_presence_of :body
end
and finally the commentable module in lib/my_modules/commentable.rb
require 'active_support/concern'
module MyModules::Commentable
extend ActiveSupport::Concern
included do
before_filter :comments, :only => [:show]
end
def comments
#Commentable = find_commentable
#comments = #Commentable.comments.arrange(:order => :created_at)
#comment = Comment.new
end
private
def find_commentable
return params[:controller].singularize.classify.constantize.find(params[:id])
end
end
#blog
#<Blog id: 8, title: "New Blog Post about Databases.... Again", body: "RDM, the database management system, was designed ...", created_at: "2015-03-01 22:28:07", updated_at: "2015-03-03 00:11:07", image: "IMG_2210.JPG">
#commentable
#<Blog id: 8, title: "New Blog Post about Databases.... Again", body: "RDM, the database management system, was designed ...", created_at: "2015-03-01 22:28:07", updated_at: "2015-03-03 00:11:07", image: "IMG_2210.JPG">
app/helpers/comments_helper.rb
module CommentsHelper
def nested_comments(comments)
comments.map do |comment, sub_comments|
content_tag(:div, render(comment), :class => "media")
end.join.html_safe
end
end
#_params instance variable in better errors
{"utf8"=>"✓", "authenticity_token"=>"OH2tDdI5Kp54hf5J78wXHe//Zsu+0jyeXuG27v1REqjdAec7yBdlrVPLTZKEbLZxgR2L7rGwUwz5BlGTnPcLWg==", "comment"=>{"parent_id"=>"", "body"=>"Hello!\r\n"}, "commit"=>"Submit Comment", "controller"=>"comments", "action"=>"create", "blog_id"=>"8"}
You're getting the error in this view: app/views/blogs/show.html.erb.
The data for this view has been prepared in blogs#show. So in your view, you should have <%= form_for [#blog, #comment] do |f| %>, since you set #blog, not #commentable.
You should also do #comment = Comment.new. Not sure where you set this one...
Do <% raise #commentable.inspect %> in your view (app/views/blogs/show.html.erb). If it's nil, then that's why you're getting the error.