Pranav
Pranav

Reputation: 3

How to concatenate string fields to one field with a delimiter in rails 4 application?

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

Answers (3)

Vapire
Vapire

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

Ruby Racer
Ruby Racer

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

Jon
Jon

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

Related Questions