Question

I found this article: Override a Mongoid model's setters and getters to help me, but behavior's still not what I am looking for.

My model is like so:

class Event
  include Mongoid::Document
  include IceCube

  validates_presence_of :title
  field :title, type: String
  field :description, type: String
  field :schedule, type: Hash

  attr_protected :schedule_hash

  def schedule=(new_schedule)
    super(new_schedule.to_hash)
  end

  def schedule
    Schedule.from_hash(super())
  end
end

This works more or less as I'd expect, in that it serializes and deserializes the IceCube object, but what I noticed is that while I can do this:

s = Schedule.new(Time.now)
s.add_recurrence_rule Rule.daily
e = Event.new
e.schedule = s

it seems to serialize and deseralize as I would expect, and I can call things like

e.schedule.occurs_at?(Date.today + 1.day) 

and get the expected response. However, if I try:

e.schedule.add_recurrence_rule Rule.daily

instead of calling it on the local variable s before setting the property on event, I can look at the hash and see the rule is not persisted.

Is there something I'm missing on the right way of doing this sort of thing in Ruby or Mongoid?

I tried using write_attribute and read_attribute, but that was likewise to no avail.

Was it helpful?

Solution

While not the most pretty, this strategy will work:



class Event
  include Mongoid::Document

  validates_presence_of :title
  field :title, type: String
  field :description, type: String
  field :schedule

  before_save do
    write_attribute(:schedule, @schedule.to_yaml)
  end

  def schedule=(s)
    @schedule = s
  end

  def schedule
    @schedule ||= begin
      yaml = read_attribute(:schedule)
      yaml ? IceCube::Schedule.from_yaml(yaml) : IceCube::Schedule.new
    end
  end
end

OTHER TIPS

Roman's solution requires an additional step to serialize the recurrence rules. You need to write the instance variable @schedule to the attributes hash once you've added the rule.

In my code it's used something like this:

before_save :serialize_schedule

def schedule=(s)
  super(s.to_yaml) if s.is_a?(IceCube::Schedule)
end

def schedule
  yaml = super()
  if yaml.nil?
    @schedule ||= IceCube::Schedule.new(Time.at(0))
  else
    @schedule ||= Schedule.from_yaml(yaml)
  end
end

def serialize_schedule
  self.schedule = schedule
end

When I send it the params hash from a form:

def repeats_weekly_days=(day_array)
  schedule.add_recurrence_rule IceCube::Rule.weekly.day(day_array.map(&:to_sym))
  serialize_schedule
end

The following test passes:

describe "#assign_attributes" do
  let(:params) {{start_time: "08:00", end_time: "12:00", repeats_weekly_days: ["monday", "thursday"]}}
  it "expect schedule to repeat on Mondays and Thursdays" do
    jc.assign_attributes(params)
    expect(jc.read_attribute(:schedule)).to include('WeeklyRule')
    expect(jc.schedule.recurrence_rules[0].to_s).to eq('Weekly on Mondays and Thursdays')
  end
end

It's not a particularly elegant solution but it works.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top