Home > Enterprise >  Rails - how to query has_many from model
Rails - how to query has_many from model

Time:12-25

I have a scheduleevent model. The model uses ice_cube to create recurrence rules. A scheduleevent has_many schedtimeranges. A schedtimnerange has a start_time, end_time and booleans for the days of the week Sunday - Saturday. Whatever days are checked are true.

What I need to do is get those days in an array to pass them into ice_cube. How would I join scheduleevent to schedtimeranges, and get Sunday - Saturday where the value is true in a rails model? Do I query the records in the controller, then loop them in the model, or if I need to loop in the model, query them from the model?

Trying to do something like this, but getting a no method schedtimeranges error:

  def self.timeranges
    Schedtimeranges.where(scheduleevent_id: self.id).columns.select{ |c| c.type == :boolean }.map(&:name)
  end
  weekdays = self.timeranges

Scheduleevent Model:

  has_many :schedtimeranges, inverse_of: :scheduleevent
  accepts_nested_attributes_for :schedtimeranges, allow_destroy: true

EDIT:

weekdays = schedtimeranges.where(scheduleevent_id: id).columns.select{ |c| c.type == :boolean }.map(&:name).to_a

This works, but I just need to only get them where the value is true. How do I add that to this? So if a scheduleevent has say 3 schedtimeranges, I want to get the days of week, and start_time/end_time where they are true.

Expected output would be 07:00 AM, 14:00 PM, Monday, Thursday, Friday. (If those are the 3 days that are true.

  create_table "schedtimeranges", force: :cascade do |t|
    t.bigint "scheduleevent_id", null: false
    t.string "start_time"
    t.string "end_time"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "monday"
    t.boolean "tuesday"
    t.boolean "wednesday"
    t.boolean "thursday"
    t.boolean "friday"
    t.boolean "saturday"
    t.boolean "sunday"
    t.index ["scheduleevent_id"], name: "index_schedtimeranges_on_scheduleevent_id"
  end

EDIT 2:

This is how I am trying to create the recurrence rules. How would I loop the to_ice_cube and pass the scheduleevent_id through?

schedule = IceCube::Schedule.new(start)
case orule  
when 'daily'
  schedule.add_recurrence_rule IceCube::Rule.daily(1).until(end_date.to_time)    
when 'weekly'
  self.map(&:to_ice_cube).where(scheduleevent_id: self.id).each do |days|
    schedule.add_recurrence_rule IceCube::Rule.weekly(1).day(days.map{|s| s.to_sym}).until(end_date.to_time) 
  end
end

The idea being that if it is a daily repeat type I just have a start time and end time between all days froms tart date to end date. If it is a weekly recurrence I want to create the rules for the times on those particular days.

I am getting an undefined method map the scheduleevent record.

CodePudding user response:

I think you made it sound way more complicated than it is. Expected output helped a lot. I've added a couple of methods to Schedtimerange to make it simpler:

# why not ScheduleTimeRange and ScheduleEvent

class Schedtimerange < ApplicationRecord
  # sometimes you just have to type all the attributes that you need
  def days
    {
      monday:    monday,
      tuesday:   tuesday,
      wednesday: wednesday,
      thursday:  thursday,
      friday:    friday,
      saturday:  saturday,
      sunday:    sunday
    }
  end

  # # or like this
  # DAYS = %w[monday tuesday wednesday thursday friday saturday sunday].freeze
  # def days
  #   attributes.slice(*DAYS)
  # end

  # desired format goes here
  def to_ice_cube
    [start_time, end_time, *days.compact_blank.keys]
  end
end

https://api.rubyonrails.org/classes/Hash.html#method-i-compact_blank-21

>> Schedtimerange.first.to_ice_cube
=> ["07:00 AM", "08:00 AM", :monday, :friday]

>> Schedtimerange.where(scheduleevent_id: 1).map(&:to_ice_cube)
=> [["07:00 AM", "08:00 AM", :monday, :friday], ["11:00 AM", "04:00 PM", :monday, :tuesday]]

Do the same for Scheduleevent if you want:

class Scheduleevent < ApplicationRecord
  has_many :schedtimeranges

  def to_ice_cube
    [id, schedtimeranges.map(&:to_ice_cube)]
  end
end
>> Scheduleevent.limit(2).map(&:to_ice_cube)
=> [[1, [["07:00 AM", "08:00 AM", :monday, :friday], ["11:00 AM", "04:00 PM", :monday, :tuesday]]],
    [2, [[nil, nil, :monday, :tuesday], [nil, nil, :tuesday, :wednesday]]]]

If you're calling it from Scheduleevent, just use association to get relevant ranges:

schedtimeranges.map(&:to_ice_cube).each do |days|
  schedule.add_recurrence_rule IceCube::Rule.weekly(1).day(days).until(end_date.to_time) 
end
  • Related