DRY controller .valid? for create and update using simple_form - ruby-on-rails

So I've got these views:
new.html.erb
<div class="booyah-box col-xs-10 col-xs-offset-1">
<h1>Expose Your Hidden Gem</h1>
<%= simple_form_for #place do |f| %>
<%= f.input :name, error: "Name is mandatory" %>
<%= f.input :address %>
<%= f.input :description %>
<br />
<%= f.submit 'Create', class: 'btn btn-primary' %>
<% end %>
</div>
edit.html.erb
<div class="booyah-box col-xs-10 col-xs-offset-1">
<h1>Edit Your Place</h1>
<%= simple_form_for #place do |f| %>
<%= f.input :name %>
<%= f.input :address %>
<%= f.input :description %>
<br />
<%= f.submit 'Update', class: 'btn btn-primary' %>
<% end %>
</div>
this model:
Place.rb
class Place < ActiveRecord::Base
belongs_to :user
has_many :comments, dependent: :destroy
has_many :photos
geocoded_by :address
after_validation :geocode
validates :name, presence: true
validates :address, presence: true
validates :description, presence: true
end
And finally, places_controller.rb (only showing create and update)
def create
#place = current_user.places.create(place_params)
if #place.save
redirect_to root_path
else
render :new
end
end
def update
#place = Place.find(params[:id])
if #place.user != current_user
return render text: 'Not Allowed', status: :forbidden
end
#place.update_attributes(place_params)
if #place.save
redirect_to root_path
else
render :edit
end
end
But, I'm trying to think DRY and want to know if there is a better way to do a validation for name address and description presence without having the same identical code in both the create and update portions of my controller? I feel like I should just be writing it once...

First, you can refactor your views to use the following structure:
# new.html.erb
<div class="booyah-box col-xs-10 col-xs-offset-1">
<h1>Expose Your Hidden Gem</h1>
<%= render 'form' %>
</div>
# edit.html.erb
<div class="booyah-box col-xs-10 col-xs-offset-1">
<h1>Edit Your Place</h1>
<%= render 'form' %>
</div>
# _form.html.erb
<%= simple_form_for #place do |f| %>
<%= f.input :name %>
<%= f.input :address %>
<%= f.input :description %>
<br />
<% submit_label = #place.new_record? ? 'Create' : 'Update' %>
<%= f.submit submit_label, class: 'btn btn-primary' %>
<% end %>
And then in your controllers you could refactor to:
def create
#place = current_user.places.new(place_params)
if #place.save
redirect_to root_path
else
render :new
end
end
def update
#place = current_user.places.find(params[:id])
#place.attributes = place_params
if #place.save
redirect_to root_path
else
render :edit
end
end

Related

unknown attribute 'development_id' for Lot with cocoon triple nested forms

I'm almost certain I know what the problem is, and if I'm correct, I just can't find where the problem is lying.
When triple-nesting with the cocoon gem, I'm having what seems to be an error with incorrect pluralisation. I have 3 resources, Developments > Lots > Listings, where Developments is the grandparent, Lots is the parent and Listings is the child.
The error I'm getting is unknown attribute 'development_id' for Lot. stemming from ActiveModel::UnknownAttributeError in Developments#new. I've checked my models and partials and have been playing around with them. Here is my code:
developments_controller.rb
class DevelopmentsController < ApplicationController
before_action :set_development, only: %i[ show edit update destroy ]
# GET /developments or /developments.json
def index
#developments = Development.all
end
# GET /developments/1 or /developments/1.json
def show
end
# GET /developments/new
def new
#development = Development.new
end
# GET /developments/1/edit
def edit
end
# POST /developments or /developments.json
def create
#development = Development.new(development_params)
respond_to do |format|
if #development.save
format.html { redirect_to #development, notice: "Development was successfully created." }
format.json { render :show, status: :created, location: #development }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #development.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /developments/1 or /developments/1.json
def update
respond_to do |format|
if #development.update(development_params)
format.html { redirect_to #development, notice: "Development was successfully updated." }
format.json { render :show, status: :ok, location: #development }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #development.errors, status: :unprocessable_entity }
end
end
end
# DELETE /developments/1 or /developments/1.json
def destroy
#development.destroy
respond_to do |format|
format.html { redirect_to developments_url, notice: "Development was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_development
#development = Development.find(params[:id])
end
# Only allow a list of trusted parameters through.
def development_params
params.require(:development).permit(:development_name, :development_type, :development_address, :description, :estimated_completion_date, :body_corp,
lots_attributes: [:id, :status, :stage, :land_size, :price, :eta, :_destroy,
listings_attributes: [:id, :lot_number, :price, :type, :bed, :bath, :car, :house_size, :rent, :_destroy]])
end
end
development.rb (model)
class Development < ApplicationRecord
has_many :lots
accepts_nested_attributes_for :lots, reject_if: :all_blank, allow_destroy: :true
end
Lot.rb (model)
class Lot < ApplicationRecord
has_many :listings
accepts_nested_attributes_for :listings, reject_if: :all_blank, allow_destroy: :true
belongs_to :development
end
Listing.rb (model)
class Listing < ApplicationRecord
belongs_to :lot
end
_form.html.erb (partial)
<%= form_for #development do |f| %>
<div class="field">
<%= f.label :development_name%>
<%= f.text_field :development_name%>
</div>
<div class="field">
<%= f.label :development_type %>
<%= f.text_field :development_type %>
</div>
<div class="field">
<%= f.label :development_address %>
<%= f.text_field :development_address %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :estimated_completion_date %>
<%= f.date_select :estimated_completion_date %>
</div>
<div class="field">
<%= f.label :body_corp %>
<%= f.number_field :body_corp %>
</div>
<div id="listings">
<%= f.fields_for :lots do |lot| %>
<%= render 'lot_fields', f: lot %>
<% end %>
<div class="links">
<%= link_to_add_association 'add lot', f, :lots %>
</div>
</div>
<%= f.submit %>
<% end %>
_lot_fields.html.erb (partial)
<div class="nested-fields">
<h3> New Lots </h3>
<div class="field">
<%= f.label :status %>
<br/>
<%= f.text_field :status %>
</div>
<div class="field">
<%= f.label :stage %>
<br/>
<%= f.text_field :stage %>
</div>
<div class="field">
<%= f.label :land_size %>
<br/>
<%= f.text_field :land_size %>
</div>
<div class="field">
<%= f.label :price %>
<br/>
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :eta %>
<br/>
<%= f.text_field :eta %>
</div>
<%= link_to_remove_association "remove lot", f %>
</div>
_listing_fields.html.erb (partial)
<div class="nested-fields">
<h3> New Listing </h3>
<div class="field">
<%= f.label :status %>
<br/>
<%= f.text_field :status %>
</div>
<div class="field">
<%= f.label :stage %>
<br/>
<%= f.text_field :stage %>
</div>
<div class="field">
<%= f.label :land_size %>
<br/>
<%= f.text_field :land_size %>
</div>
<div class="field">
<%= f.label :price %>
<br/>
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :eta %>
<br/>
<%= f.text_field :eta %>
</div>
<%= link_to_remove_association "remove listing", f %>
</div>
new.html.erb
<h1>New Development</h1>
<%= render 'form', development: #development %>
<%= link_to 'Back', developments_path %>
I have a clone project of this and I noticed in the schema.rb that there is a key difference:
t.index ["developments_id"], name: "index_lots_on_developments_id"
I believe it should be development_id instead of being pluralised, but I'm not sure where/how this needs to be changed. I'm under the impression you should never alter a schema file.
Any help is greatly appreciated!
Exactly, it should be :
t.index ["development_id"], name: "index_lots_on_development_id"
You have to rewrite a migration with
def change
remove_index :lots, name: "index_lots_on_developments_id"
add_reference :lots, :development, index: true
end

How do I create a two-step form in rails?

I have a question for rails. I'm creating a form for user to register. What I want to do is that after the user press "Submit" button I want to redirect the user to another page which shows all the information from the form filled by the user just now (read-only).
This is my controller
class PermitsController < ApplicationController
before_action :set_permit, only: [:show, :destroy]
def index
#permits = Permit.all
end
def new
#permits = Permit.new
end
def create
#permits = current_user.permits.build(permit_params)
if #permits.save
redirect_to invoice_path
else
render 'new'
end
end
def destroy
Permit.destroy_all(user_id: current_user)
respond_to do |format|
format.html { redirect_to root_path, notice: 'Permit was successfully canceled.' }
format.json { head :no_content }
end
end
def invoice
#permits = current_user.permits(permit_params)
end
def show
#user = User.find(params[:id])
#permits = #user.permits.paginate(permit_params)
end
def update
#permits = Permit.where(user_id: current_user).take
respond_to do |format|
if #permits.update(permit_params)
format.html { redirect_to root_path}
flash[:success] = "Permit successfully updated"
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_permit
#permits = Permit.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def permit_params
params.require(:permit).permit(:vehicle_type, :name, :studentid, :department, :carplate, :duration, :permitstart, :permitend)
end
end
This is the form filled by user
<% provide(:title, 'New Permit') %>
<h1>Permit Application</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#permits) do |f| %>
<%= f.label :"Vehicle" %>
<%= f.text_field :vehicle_type, class: 'form-control' %>
<%= f.label :"License Plate" %>
<%= f.text_field :carplate, class: 'form-control' %>
<%= f.label :"Student ID" %>
<%= f.text_field :studentid, class: 'form-control' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :"Department of applicant" %>
<%= f.text_field :department, class: 'form-control' %>
<%= f.label :permit_start %>
<%= f.date_select :permitstart, class: 'form-control' %>
<%= f.label :permit_end %>
<%= f.date_select :permitend, class: 'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
You could add an additional action between new and create.
# config/routes.rb
resources :permit do
collection do
post :confirm
end
end
The reason that we use POST even if the route does not create a resource is that we don't want to pass any user info in the request URL.
class PermitsController < ApplicationController
# POST /permits/confirm
def confirm
#fields = %i[vehicle_type, carplate, studentid, name, department, permitstart, permitend]
#permit = current_user.permits.build(permit_params)
render :new and return unless #permit.valid?
end
end
render :new and return unless #permit.valid? shortcuts the process and renders the :new form again if the input is not valid in the first place.
Since we are using POST we need a form for both the new.html.erb and confirm.html.erb all duplicating all those inputs would not be great so lets extract them to a partial:
<% # /views/permits/_inputs.html.erb %>
<%
input_options ||= {}
input_options[:class] ||= 'form-control'
%>
<%= f.label :"Vehicle" %>
<%= f.text_field :vehicle_type, input_options%>
<%= f.label :"License Plate" %>
<%= f.text_field :carplate, input_options %>
<%= f.label :"Student ID" %>
<%= f.text_field :studentid, input_options %>
<%= f.label :name %>
<%= f.text_field :name, input_options %>
<%= f.label :"Department of applicant" %>
<%= f.text_field :department, input_options %>
<%= f.label :permit_start %>
<%= f.date_select :permitstart, input_options %>
<%= f.label :permit_end %>
<%= f.date_select :permitend, input_options %>
So lets point the new.html.erb form so that it submits to /permits/confirm:
<% provide(:title, 'New Permit') %>
<h1>Permit Application</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#permits, url: '/permits/confirm_permits_path') do |f| %>
<% render partial: :inputs %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
And create a /views/permits/confirm.html.erb view:
<% provide(:title, 'Confirm Permit application') %>
<h1>Confirm Permit application</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#permits) do |f| %>
<% render partial: :inputs, input_options: { readonly: 'readonly' } %>
<% end %>
</div>
</div>

Ruby on Rails won't render edit page without id

For some reason the edit action won't render i get this error and is using show action instead of edit but the same form works for the render :new action
do not focus on the params[:preview], i am talking about the last render :edit
ActionController::UrlGenerationError in Admin::Blog::Posts#update
No route matches {:action=>"show", :controller=>"admin/blog/posts", :id=>""} missing required keys: [:id]
def edit
#post = Post.find_by_permalink(params[:id])
end
def update
#post = Post.find_by_permalink(params[:id])
if params[:publish]
#post.publish
elsif params[:draft]
#post.draft
end
if params[:preview]
if #post.published?
#post.draft
end
if #post.update(blog_post_params)
flash[:success] = "some text "
redirect_to blog_post_url(#post)
else
render :edit
end
end
if #post.update(blog_post_params)
flash[:success] = "Post was successfully updated."
redirect_to edit_admin_blog_post_url(#post)
else
render :edit
end
end
form
<%= form_for [:admin,:blog, #post] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="large-12 columns">
<div class="field panel">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field panel">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div class="actions panel text-right">
<% if #post.published? %>
<%= f.submit "Save Changes",name: "publish", class: "tiny button radius success" %>
<% else %>
<%= f.submit "Publish",name: "publish", class: "tiny button radius success" %>
<% end %>
<%= f.submit 'Mark as Draft', name: "draft", class: "tiny button radius " %>
<% if #post.created_at %>
<%= f.submit 'Preview', name: "preview", class: "tiny button radius warning" %>
<% end %>
<%= link_to 'Back', admin_blog_posts_path, class: "tiny button radius secondary" %>
</div>
<div class="field panel">
<%= f.label :body %><br>
<%= f.cktext_area :body, :height => '800px', :id => 'sometext' %>
</div>
</div>
<% end %>
relevant routes
namespace :admin do
namespace :blog do
get '', to: 'welcome#index', as: '/'
resources :posts
end
end
post model
class Post < ActiveRecord::Base
has_many :tags
has_many :comments
before_validation :generate_permalink
validates :permalink, uniqueness: true
validates :title, presence: true
validates :description, presence: true
def generate_permalink
self.permalink = title.parameterize
end
def to_param
permalink
end
end
I guess i know why you get this error.
In the edit action you use Post.find_by_permalink(params[:id]) to find your post, which returns nil if nothing was found. And since you may change the title attribute, your permalink is updated (i guess), and your post is not found. The controller still renders the action, with nil #post, and cannot generate the url for the form.
Try using Post.find_by_permalink!(params[:id]) instead, and you will get a 404.
I would actually suggest you to use regular find in the admin area, since the permalink might change.

Rails 4 - select_tag does not pass id for field

I cannot pass the lecture_id to the created object:
Basically it should create the project with the lecture_id based on the dropdown menu. It does not seem to pass the data.
If for instance I add <%= f.input :lecture_id %> it will pass the data
<%= simple_form_for(#project) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title %>
<%= select_tag(:lecture_id, options_for_select(#lecture_options)) %>
<%= f.input :company_name %>
<%= f.input :phone_number %>
<%= f.input :body %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
Controller:
def new
#lecture_options = Lecture.all.map{|u| [u.title, u.id]}
#project = Project.new
respond_to do |format|
format.html
format.js
end
authorize #project
end
def create
#project = current_user.projects.build(project_params)
if #project.save
flash[:success] = "You have successfully created a project."
redirect_to profile_path(current_user)
else
render action: 'new'
end
authorize #project
end
def project_params
params.require(:project).permit(:company_name, :phone_number, :body, :user_id, :title, :lecture_id)
end
Models:
Lecture.rb
has_many :projects
Project.rb
class Project < ActiveRecord::Base
belongs_to :user
belongs_to :lecture
end
You should change select_tag to :
<%= select_tag("project[lecture_id]", options_for_select(#lecture_options)) %>

Invalid association. Make sure that accepts_nested_attributes_for is used for :questions association

i am building nested form in rails 4.I keep getting this error
my _form.html.erb as
<%= nested_form_for (#project) do |f| %>
<% if #project.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#project.errors.count, "error") %> prohibited this project from being saved:</h2>
<ul>
<% #project.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= f.fields_for :questions do |builder| %>
<%= render "question_fields", :f => builder %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
_question.html.erb
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
<%= f.label :subject, "Question" %><br />
<%= f.text_field :subject %><br />
<%= f.link_to_remove "Remove this task" %>
<p><%= f.link_to_add "Add a questions",:questions %></p>
</p>
project.rb
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
Edit
question.rb
class Question < ActiveRecord::Base
belongs_to :project
end
projects controller
def new
#project = Project.new
3.times do
question = #project.questions.build
end
def project_params
params.require(:project).permit(:name)
end
def create
#project = Project.new(project_params)
respond_to do |format|
if #project.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 used "nested_form" gem Gives Error Invalid association. Make sure that accepts_nested_attributes_for is used for :questions association.
pls help me to get rid of this error
questions controller def:-
def question_params
params.require(:question).permit(:project_id, :subject, :content)
end
Might be your problem is with your strong parameters
Try changing your project_params in your projects_controller as
def project_params
params.require(:project).permit(:name,questions_attributes: [:project_id,:subject,:content])
end
And also,your controller code should look like this
def new
#project = Project.new
3.times do
#question = #project.questions.build
end
end
def create
#project = Project.new(project_params)
respond_to do |format|
if #project.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
private
def project_params
params.require(:project).permit(:name,questions_attributes: [:project_id,:subject,:content])
end
Also,you have to look for Strong Parameters with accepts_nested_attributes_for.
Update
Try changing your _form.html.erb as
<%= nested_form_for(#project) do |f| %>
<% if #project.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#project.errors.count, "error") %> prohibited this project from being saved:</h2>
<ul>
<% #project.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= f.fields_for :questions do |builder| %>
<%= render "question_fields", :ff => builder %> #changed to ff to avoid confusion
<% end %>
<p><%= f.link_to_add "Add a questions",:questions %></p> #this line it should be here.
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And your _question_fields.html.erb as
<p>
<%= ff.label :content, "Question" %><br />
<%= ff.text_area :content, :rows => 3 %><br />
<%= ff.label :subject, "Question" %><br />
<%= ff.text_field :subject %><br />
<%= ff.link_to_remove "Remove this task" %>
</p>

Resources