Skip to main content

Recurring events with the ice_cube gem Discussion

General • Asked by Chris Oliver

On my app I use a Event and a EventTrans model so for each time a event occurs we can have a record with info and other stuff. So for the Calendar we display the First event with the recurrent on the fly and each time the event pass we save it as a record or if we need to move only on recurrent we create it as a event transaction.

My only problem is the time it takes to check if a recurrent is create as event_trans or not for calendar view.


For some reason the simple_calendar gem messed with a bunch of the CSS on my site (e.g. all my links now have a black background on hover, etc.). Any hints on how to avoid this?

simple_calendar doesn't include any CSS for the links, so it must come from somewhere else. It only has some optional CSS for styling the calendar, but not links.

Sounds like you may have created a scaffold and the scaffold.css file is styling your links.

Gaaahhh! Thank you! That was driving me crazy.


Thanks for a good demo for IceCube! I've been looking for this functionality to use it in one of my pet projects, but ended up with implementing something like this using cron format to represent recurrent event rules, and parse-cron gem to parse it. But now I'll try to switch to IceCube. I like the hash format it uses. It look less cryptic than crontab. Thanks for a good tip :)


Waiting for the next episode!!!
You are great!


Really enjoying this series. Curious to when the next episode will be for exceptions etc?

Hey Josh, looks like I forgot to put the video in the series. You can find the episode here: https://gorails.com/episode...

Fantastic! Thanks @excid3:disqus much appreciated. Are you looking to extend on this gem any further or do any further Vlog's?
Keen to have another view for possible listing and also if we needed to associate another model to a event recurrence?

Cheers
Josh

Um possibly. I just made a tiny tweak today that improved performance by like 4x or something. Thought that might be useful to talk about, but I think I've covered memoization before.

What do you mean by "view for possible listing"? And if you want to do two models, you could combine the query results into what you pass in for the events array and as long as they have the "start_time" interface, it doesn't care what type of objects they are.

Hi @excid3:disqus looking to try and implement views like this https://github.com/Serhioro...
That's what I mean by "view for possible listing?"

Also when you click on Week, Month etc it provide Event summary like number of events in that month/week.

Would be a good for extending on this series also :)
https://uploads.disquscdn.c...

Ah yeah, that's what I figured. I'll consider it, one of the reason I haven't is that I didn't want any JS to be required. You could easily just make a route for each of those and them add your own buttons to each of the views and turbolinks would make navigating between those pretty snappy.

It does probably make sense to add day and year views to the calendar and leave it up to you to link them up on the page.


Hi, thanks for the tutorial I found it very helpful. I wondered if you had any thoughts on the best way to query recurring events that have been serialised using the recurring gem? The issue I'm running into is if an individual who has not created the original calendar events wanted to search several calendars (created by other users) for free slots at given times the serialiser method then seems to become more complex than if we'd created individual discrete events. For example if you wanted to share calendars between different users. Would be interested to know your thoughts on this. Thanks

Depends on what you're trying to accomplish (like everything right? lol). What kind of search are you trying to pull off?

Thanks for your reply. Lol a little bit. I'm trying to make a basic scheduling app so that different users can search each others calendar schedules for available time slots and arrange meetings. From the user's standpoint; each user adds events to their calendar which take up a time slot. Their availability is then determined to be those time slots with no events (in practice slightly more complex than this but for now this is what I'm looking at). Then the user's search (of another user's calendar) would be something like:

"All time slots (for the another user) from datetime x to datetime y across one calendar with no events present".

The purpose would be to schedule a meeting for instance. The way I thought of the problem was to put all events matching given search date range criteria into an array that would appear as "search results". This would avoid the necessity of going into every user's calendar manually to check availability that way. It's quite simple to do that with discrete non-recurring events as you are querying discrete objects out of a database. But because the serialise methodology is not generating all the events in a series at once unless you load up the calendar for each user it seems a little more complex to do that.

Ah yeah, that's a pretty complex one. I think what I would probably do is this:

1. Query for all the individual events
2. Query for all the recurring events within that range. This would match recurring events where the start date is before the end of the query and end dates of null (infinitely recurring) or end date after the beginning of the query.

Then I'd load those recurring events into ice_cube and then run the generator to create all the events for the recurring ones within your query range.

3. Then you can combine those two arrays of events into one and your view will have an accurate set of event objects to use for that query range.

Obviously, it's not as simple as the alternative of making recurring events just insert regular event records into the database. That may be good enough for your solution, or you may want truly infinite recurring events which would require you to build a bit more of a complex query system like above.

Thanks, much appreciated that makes sense I'll give it a shot. I'm realising I have to weigh up the advantage of saving space by accessing serialised events versus the convenience of querying actual event records in DB. I really liked the way you used recurrence select with ice cube and wanted to keep that approach.

Yeah, I can see going both ones. On one hand, the developer effort is easier to just generate and save the recurring events but it makes the user experience a lot tougher (how do remove them all? what if you want to edit all of them? what about just one?). And then on the other hand, it's easier for the user, but tougher on you as a developer to build the ice_cube style recurring events and query for those.

You'll probably be fine either approach you take, just remember it can always be changed and improved later on so it all depends on what business needs are most important. :D

Sorry to bother you again but would really appreciate any tips of adapting this. I was trying to adapt your calendar_events method for use in saving all the instances of a recurring event from the events controller create method. Any idea how I could adapt this to generate and save all recurring events for a new instance of an event? My create method in event controller code is below. I think I just need to tweak it a little as it works for all the existing saved events but i specifically just want it to pass the new event through to the method.

My aim is this: user creates new event with recurrence rule, then on save it loops through all occurrences of this instance up until some predefined end time creating an array of the associated events which are subsequently saved after being generated. The difference here is that rather than fetching all the already saved events and looping through the occurrences, I want to do that only for the new instance that the user is creating when they open up the form.
/////events controller code

def create
## first testing for whether recurring is null or not
#room have one to many relationship to events

if params[:event][:recurring] == "null"

room = Room.find(params[:room_id])
@event = room.events.new(event_params)
@event.save
else

room = Room.find(params[:room_id])
#this loops through all already saved events and #then creates an array of occurances for each saved #event whereas I am trying to do this for only the #instance of the event being created not every event #already saved

@calendar_events = room.events.flat_map{ e|e.calendar_events(params.fetch(:start,Time.zone.now).to_date)}
@calendar_events.each(&:save)

end

end

///event modelcode

#I made a modification to the calendar_events method to account for one to many relationshi with rooms to events, and I am using start instead of start_date

def calendar_events(startit)
if recurring.empty?
[self]
else

end_date = startit.end_of_month.end_of_week

schedule(start).occurrences(end_date).map do |date|

@event = room.events.new(room_id: room_id, title: title, start: date, end: date, color: color, recurring: recurring)

end
end
end

This isn't too bad. You would probably be well served to move your code for creating the individual events to the model itself so that this logic doesn't clutter the controller.

It looks like you're on the right track. I think you can clean a lot of this up like so: https://gist.github.com/exc...

This is untested, but I think the only issue with this would be that you would need to probably skip that first event in the recurring loop as it probably would duplicate the initial event.

Thanks again, much appreciated I'll try that. Cheers


Shawn Nigel Rebello

Thanks a lot for this !


Has anyone gotten recurring_select to work on rails 5.2?? I keep getting a wrong number of attributes error no matter what I try to pass it.
Seems like it only occurs on Rails 5.2.rc2.. I'm not sure why though.

Looks like there was a change in the value method of the FormBuilder Base class. I changed it to not pass de object around anymore on a fork, but this would make the gem not work with previous versions... anyways, I'll leave the link here in case anyone is in need of a quick fix. https://github.com/gafemoyano/recurring_select
Good work! Looks like this probably changed somewhat recently then. The gem sure hasn't been updated for quite a while so it's surprising other things haven't broken!

Hello Chris, thank you so much for putting this leson together.
I followed it step by step and everything worked fine, that is until something got mistrerously broken and now I cannot seem to know what it is or how to put it back together.

Coould you please take a look to the issue I posted on github and let me know your thoughts?

https://github.com/GetJobber/recurring_select/issues/123

Best

OO


Thanks Chris, very helpful as always :-)

As RecurringSelect.is_valid_rule?('null') is true, I had to change the recurring=(value) method.

We did you change? I am getting the error "undefined method `to_hash' for nil:NilClass" And I was wondering if that could be the issue.

@Jeff:

Make sure you don't have old event data stored in the event table as you move through the excercise. Also, as Chris Seelus pointed out, latest recurring select does parse string null and makes it a valid rule.

Maybe put a guard to call super(nil) like so?

def recurring=(value)
    if value != "null" && RecurringSelect.is_valid_rule?(value)
      super(RecurringSelect.dirty_hash_to_rule(value).to_hash)
    else
      super(nil)
    end
end

Cool. Thanks for the tips!


Hi Chris!

Thank you for the episode. It was very educational. 8-) Ross Kaffenberger (@rossta) has a neat gem called Montrose (https://rossta.net/montrose/) similar to/inspired by ice_cube which models recurrence quite nicely as well.

I am stuck in the ice_cube world though trying to solve the following problem: My event model needs to be searchable/order-able. Since I did not want to reinvent the wheel, I ended up with datatables implementation. I created a nice JSON endpoint which produces events. I was cruising along with implementation, until I got to the actual search part. The JSON endpoint returns data which is ordered/searched via chained AR calls, something like:

def fetch_events
  search_string = []
  %w[name description city country state zipcode].each do |term|
    search_string << "#{ term } ILIKE :search"
  end

  events = Event.active.order("#{sort_column} #{sort_direction}")
  events = event.page(page).per(per_page)
  events = events.where(search_string.join(' OR '), search: "%#{params[:search][:value]}%")
end

Soooo, it seems that if I want to include recurring events in the JSON endpoint, I should inject Event#calendar_events in the code above. One could, do that at the end, however, that wouldn't jive with sorting part. Not sure if one can try to do it early in the chain, becase, well, AR.

Would you, or anybody elase, have a clever idea how to handle that?

Thank you,

Pawel


I am using ice-cube gem for recurring.
Condition:
I have a form which allows user to select datetime for recurring.
Now I want to schedule and run a background job based on user frequncy.
Like if the frequncy is daily hten that job should run daily. How can I achieve this using ice-cube gem.


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.