1

I have two models:

  • Project.
  • Item.

A project has many items, and an item belong to a project.

class Project < ActiveRecord::Base

  attr_accessor :main_color, :secondary_color, :tertiary_color
  has_many :items
  accepts_nested_attributes_for :items

  validates :main_color, :presence => true
  validates :secondary_color, :presence => true
  validates :tertiary_color, :presence => true
end



class Item < ActiveRecord::Base
  belongs_to :project

  validates :title, :presence => true,
            :length => {:maximum => 300}

  validates_presence_of :project
end

Every project has main, secondary, and tertiary colors.

I want to store them in my database (PostgreSQL) as an array of strings (and I already setup a migration for that), but I have to display them separately in a form using color input fields, as well as validate them.

I've used virtual attributes to achieve that so the colors aren't saved separately, but I still want to save them properly as an array, how should I do that?

I also have another issue happening in my Item model related to the colors.

Each Item validates the presence of a project. I have a single page where a user can add multiple items to a project, and since the project requires these three colors and those are messing up, I keep getting validation errors : main_color can't be blank, secondary_color can't be blank, tertiary_color can't be blank.

This is my create item method in my ItemsController

 def create

   @project = Project.find(params[:project_id])
   return if !@project

   items = @project.items.build( \
   project_params.to_h['items_attributes'].hash_with_index_to_a)

   @project.items.append(items)

   if @project.save
     redirect_to "/projects/#{@project.id}/items"
   else
     render 'items/new'
   end

 end

And this is the related strong params:

 def project_params
   params.require(:project).permit(:items_attributes =>
                                   [
                                     :id,
                                     :title
                                   ]
                                  )
 end

How should I go about implementing this?

Ehsan
  • 123
  • 1
  • 9
  • I would suggest using `hstore` datatype instead of array, this way you don't have to worry about the order of writing to colors to db. To not validate your virtual attributes on update, just edit the validation, `validates :main_color, presence: true, on: :create` – Andrey Deineko May 26 '15 at 10:04
  • 1
    @AndreyDeineko Can you please give me an example of using Hstore since I'm still going to map 3 different values to a single array? don't you think that JSON would be a better choice? also .. I still need to validate their presence on update. – Ehsan May 26 '15 at 10:35

2 Answers2

1

well, you will need an attribute setter and getter to achieve the trick, i expect that you will save the array as following: [main_color, secondary_color, tertiary_color] and that your column in database called color:

def main_color= m_color
  color ||=[]
  color[0] = m_color
end

def main_color
  color[0]
end

def secondary_color= s_color
  color ||=[]
  color[1] = s_color
end

def secondary_color
  color[1]
end

def tertiary_color= t_color
  color ||=[]
  color[2] = t_color
end

def tertiary_color
  color[2]
end
mohamed-ibrahim
  • 10,837
  • 4
  • 39
  • 51
0

Building on mohamed-ibrahim's answer. How about doing it like this?

class Project < ActiveRecord::Base

  attr_accessor :main_color, :secondary_color, :tertiary_color
  has_many :items
  accepts_nested_attributes_for :items

  validates :main_color, :presence => true
  validates :secondary_color, :presence => true
  validates :tertiary_color, :presence => true

  def main_color=(m_color)
    self.colors ||= []
    self.colors[0] = m_color
  end

  def main_color
    self.colors[0]
  end

  def secondary_color=(s_color)
    self.colors ||= []
    self.colors[1] = s_color
  end

  def secondary_color
    self.colors[1]
  end

  def tertiary_color=(t_color)
    self.colors ||= []
    self.colors[2] = t_color
  end

  def tertiary_color
    self.colors[2]
  end
end
Community
  • 1
  • 1
Omar Bahareth
  • 875
  • 6
  • 22