Reputation: 3
I have multiple checkboxes in form, in which I store the results of every field in their corresponding temporary fields which I specified in model with attr_accessor: attribute. I want to join the contents of these fields into one field with a comma delimiter and store that into database.
My complete form is something like this :
<%= form_for @company do |f| %>
<% if @company.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@company.errors.count, "error") %> prohibited
this company from being saved:</h2>
<ul>
<% @company.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<div class="row">
<div class='col-sm-6'>
<div class="form-group">
<p>
<%= f.label :name %><br>
<%= f.text_field :name,class:'form-control' %>
</p>
<p>
<%= f.label :grade %><br>
<%= f.select(:grade, options_for_select([['Dream', 'dream'], ['A++', 'a++'], ['A+', 'a+'],['A', 'a']])) %>
</p>
<p>
<%= f.label :beCutoff %><br>
<%= f.text_field :beCutoff,class:'form-control' %>
</p>
<%= f.label :branchesAllowed %><br>
<%= f.check_box :coe, {}, "COE", "" %><%= label_tag :COE %><br>
<%= f.check_box :ece, {}, "ECE", ""%><%= label_tag :ECE %><br>
<%= f.check_box :ice, {}, "ICE", ""%><%= label_tag :ICE %><br>
<%= f.check_box :it, {}, "IT", ""%><%= label_tag :IT %><br>
<%= f.check_box :mpae, {}, "MPAE", ""%><%= label_tag :MPAE %><br>
<%= f.check_box :bt, {}, "BT", ""%><%= label_tag :BT %><br>
<%= f.check_box :is, {}, "IS", ""%><%= label_tag :IS %><br>
<%= f.check_box :sp, {}, "SP", ""%><%= label_tag :SP %><br>
<%= f.check_box :pc, {}, "PC", ""%><%= label_tag :PC %><br>
<p>
<%= f.label :backsAllowed %><br>
<%= f.text_field :backsAllowed,class:'form-control' %>
</p>
<p>
<%= f.label :details %><br>
<%= f.text_field :details,class:'form-control' %>
</p>
<p>
<%= f.label :package %><br>
<%= f.text_field :package,class:'form-control' %>
</p>
<p>
<%= f.label :xiiCutoff %><br>
<%= f.text_field :xiiCutoff,class:'form-control' %>
</p>
<p>
<%= f.label :xCutoff %><br>
<%= f.text_field :xCutoff,class:'form-control' %>
</p>
<%= f.label :deadline %><br>
<div class='input-group date' id='datetimepicker1'>
<%= f.text_field :deadline ,class:'form-control',:readonly=>true%>
<span class="input-group-addon" ><span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
<script type="text/javascript">
$(function () {
$('#datetimepicker1').datetimepicker({format: 'YYYY-MM-DD HH:mm:ss '});
});
</script>
</div>
<p>
<%= f.submit %>
</p>
<% end %>
I specified fields :coe,:ece,:ice, etc in my model as attr_accessor:
class Company < ActiveRecord::Base
attr_accessor :coe,:ece,:ice,:it,:mpae,:bt,:is,:pc,:sp
validate :deadline_on_or_before_now
def deadline_on_or_before_now
errors.add(:deadline, "can't be in the past") if
!deadline.blank? and deadline < (Time.zone.now+19800).to_datetime
end
validates :name, presence: true,
length: { minimum: 2 };
validates :grade,:beCutoff,:details,:package, presence: true;
validates_inclusion_of :beCutoff,:xiiCutoff,:xCutoff, :in=> 0..100,:message=>"Out of Range";
end
My controller looks like this:
class CompaniesController < ApplicationController
def new
@company = Company.new
end
def edit
@company = Company.find(params[:id])
end
def update
@company = Company.find(params[:id])
if @company.update(company_params)
redirect_to @company
else
render 'edit'
end
end
def create
@company = Company.new(company_params)
if @company.save
redirect_to @company
else
render 'new'
end
end
def index
@companies = Company.all
end
def destroy
@company = Company.find(params[:id])
@company.destroy
redirect_to companies_path
end
def show
@company = Company.find(params[:id])
end
private
def company_params
params.require(:company).permit(:name, :beCutoff,:grade,:xiiCutoff,:xCutoff,:backsAllowed,:details,:package,:deadline,:branchesAllowed,:coe,:ece,:ice,:it,:mpae,:bt,:is,:sp,:pc) if params[:company]
end
end
I want to join :coe,:ece,:ice, etc fields and store it into :branchesAllowed(string field) field with a commma delimiter and able to split this field whenever I need use them separately. I am using postgresql with rails 4.1.
Upvotes: 0
Views: 599
Reputation: 4578
The quickest and dirtiest way would be to simply concatenate the fields in a method in your model and to call that method in a before_save
callback:
class Company < ActiveRecord::Base
# . . .
before_save :combine_branches # or choose other callback type respectively
def combine_branches
self.branchesAllowed = ""
self.branchesAllowed += "COE" if coe
self.branchesAllowed += "ECE" if eve
self.branchesAllowed += "ICE" if ice
# … and so on …
end
end
Now when you call save
or update
in your controller the callback triggers and sets the branchesAllowed
field before writing to the database.
This, however, is not really dry nor easily expandable. If you want to add another branch you'd have to edit at least 3 different places.
A dry(er) solution could be something like the following:
class Company < ActiveRecord::Base
# keep a constant of possible branches tied to the model
POSSIBLE_BRANCHES = [:coe, :ece, :ice, :it, :mpae, :bt, :is, :pc, :sp]
# add the branches as virtual attributes (only assign methods)
attr_writer *POSSIBLE_BRANCHES
# add virtual attributes reader methods
# --> define every branch as method, that looks if the value is currenly
# stored in the branchesAllowed string and return true or false
POSSIBLE_BRANCHES.each do |pb|
define_method(pb) do
self.banchesAllowed.split(',').include?(pb.to_s.upcase)
end
end
# add the callback to combine the branches
before_save :combine_branches
def combine_branches
self.banchesAllowed = POSSIBLE_BRANCHES.select { |pb| !!self.send(pb) }.map(&:upcase).join(',')
end
end
Then you can also use them in your view:
<%= f.label :branchesAllowed %><br>
<% Company::POSSIBLE_BRANCHES.each do |pb| %>
<%= f.check_box pb, {}, pb.to_s.upcase, "" %><%= label_tag pb.to_s.upcase %><br>
<% end %>
So, if you want to add another branch later on you'd simply add another symbol to the array constant and you're done. Hope that helps.
UPDATE
Since boolean
values in checkboxes use '1'
and '0'
(as strings) by default, I'm not entirely sure if the virtual attribute reader methods should return that instead of a boolean value.
Upvotes: 0
Reputation: 5740
If I wanted to do it this way, I'd go for a hash stored in a string field.
before_save :create_string
def create_string
fields = {}
fields[:coe]=coe
fields[:ece]=ece
# etcetera
branchesAllowed = fields.to_s # hash to string and then store the string
end
And then, to retrieve values (let's say in a controller or a view):
values = eval(@company.branchesAllowed) # reversely, evaluate string to hash
@coe = values[:coe]
@ece = values[:ece]
# etcetera
Upvotes: 1
Reputation: 10898
I would approach this slightly differently. Assuming these are all attributes of a Company
that you want to be able to set, you could use a single integer field to store this data by creating an array of the attribute types and using the index of each attribute in that array as a binary value, which you can use to identify whether an attribute is set or not.
Watch this Railscast for a complete explanation since Ryan does it much better than I would: http://railscasts.com/episodes/189-embedded-association
Upvotes: 0