Skip to content

Instantly share code, notes, and snippets.

@nathancolgate
Last active January 31, 2023 01:44
Show Gist options
  • Star 37 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save nathancolgate/4998196 to your computer and use it in GitHub Desktop.
Save nathancolgate/4998196 to your computer and use it in GitHub Desktop.
How I built a rails interface on top of the amazing IceCube ruby gem. Video of final product: http://youtu.be/F6t-USuWPag
= bootstrap_form_for @event, html: {class: 'form-horizontal', novalidate: 'novalidate'} do |f|
.row
.span6
= f.text_field :name, class: 'span4'
= f.text_area :description, class: 'span4', rows: 5
= f.select :location_id, options_from_collection_for_select(Place.all, 'id', 'name', f.object.location_id), include_blank: true
= f.select :host_id, options_from_collection_for_select(Place.all, 'id', 'name', f.object.host_id), include_blank: true
.span6
= f.check_box :is_all_day
= f.date_select :from_date, {}, {style: 'width:auto;'}
.event_time
= f.time_select :from_time, {:minute_step => 15}, {style: 'width:auto;'}
= f.date_select :to_date, {}, {style: 'width:auto;'}
.event_time
= f.time_select :to_time, {:minute_step => 15}, {style: 'width:auto;'}
.event_time
= f.time_zone_select :time_zone, ActiveSupport::TimeZone.us_zones, :default => "Central Time (US & Canada)"
= f.select :repeats, Event::RepeatsOptions
= render partial: 'repeats_daily_options', locals: {f: f}
= render partial: 'repeats_weekly_options', locals: {f: f}
= render partial: 'repeats_monthly_options', locals: {f: f}
= render partial: 'repeats_yearly_options', locals: {f: f}
#repeats_options.event_option
= f.select :repeat_ends, Event::RepeatEndsOptions
#event_repeat_ends_on
= f.date_select :repeat_ends_on, {}, {style: 'width:auto;'}
.form-actions
= f.submit 'Save'
- content_for :last_javascripts do
:javascript
$(function() {
var toggle_repeats_yearly_on = function(){
if($('#event_repeats_yearly_on').is(':checked')){
$('#event_repeats_yearly_on_options').show();
} else {
$('#event_repeats_yearly_on_options').hide();
}
}
toggle_repeats_yearly_on();
$('#event_repeats_yearly_on').on('change',function(){
toggle_repeats_yearly_on();
});
var toggle_event_times = function(){
if($('#event_is_all_day').is(':checked')){
$('.event_time').hide();
} else {
$('.event_time').show();
}
}
toggle_event_times();
$('#event_is_all_day').on('change',function(){
toggle_event_times();
});
var toggle_event_options = function(){
$('.event_option').hide();
switch ($('#event_repeats').val())
{
case 'never':
// Nothing
break;
case 'daily':
$('#repeats_options').show();
$('#repeats_daily_options').show();
break;
case 'weekly':
$('#repeats_options').show();
$('#repeats_weekly_options').show();
break;
case 'monthly':
$('#repeats_options').show();
$('#repeats_monthly_options').show();
break;
case 'yearly':
$('#repeats_options').show();
$('#repeats_yearly_options').show();
break;
}
}
toggle_event_options();
$('#event_repeats').on('change',function(){
toggle_event_options();
});
var toggle_repeat_ends_on = function(){
switch ($('#event_repeat_ends').val())
{
case 'never':
$('#event_repeat_ends_on').hide();
break;
case 'on':
$('#event_repeat_ends_on').show();
break;
}
}
toggle_repeat_ends_on();
$('#event_repeat_ends').on('change',function(){
toggle_repeat_ends_on();
});
var toggle_repeats_monthly = function(){
switch ($('#event_repeats_monthly').val())
{
case 'each':
$('#event_repeats_monthly_each').show();
$('#event_repeats_monthly_on').hide();
break;
case 'on':
$('#event_repeats_monthly_each').hide();
$('#event_repeats_monthly_on').show();
break;
}
}
toggle_repeats_monthly();
$('#event_repeats_monthly').on('change',function(){
toggle_repeats_monthly();
});
});
- content_for :first_styles do
:css
#repeats_options {display:none;}
#repeats_daily_options {display:none;}
#repeats_weekly_options {display:none;}
#repeats_monthly_options {display:none;}
#repeats_yearly_options {display:none;}
%fieldset{id: 'repeats_daily_options', class: 'event_option'}
= f.number_field :repeats_every_n_days
%fieldset{ id: 'repeats_monthly_options', class: 'event_option'}
= f.number_field :repeats_every_n_months
= f.select :repeats_monthly, Event::RepeatMonthlyOptions
#event_repeats_monthly_each.control-group
%label.control-label Each
.controls
- Event::DaysOfTheMonth.each do |day_of_the_month|
= label_tag "event_repeats_monthly_each_days_of_the_month_#{day_of_the_month}", day_of_the_month, :class => 'checkbox inline' do
= check_box_tag "event[repeats_monthly_each_days_of_the_month][#{day_of_the_month}]", day_of_the_month, f.object.repeats_monthly_each_days_of_the_month.include?(day_of_the_month), {:name => "event[repeats_monthly_each_days_of_the_month][]"}
= day_of_the_month
= hidden_field_tag "event[repeats_monthly_each_days_of_the_month][]", ""
#event_repeats_monthly_on.control-group
%label.control-label On the
.controls
- Event::Ordinals.each_with_index do |ordinal,index|
= label_tag "event_repeats_monthly_on_ordinals_#{ordinal}", ordinal, :class => 'checkbox inline' do
= check_box_tag "event[repeats_monthly_on_ordinals][#{ordinal}]", ordinal, f.object.repeats_monthly_on_ordinals.include?(ordinal), {:name => "event[repeats_monthly_on_ordinals][]"}
= Event::HumanOrdinals[index]
= hidden_field_tag "event[repeats_monthly_on_ordinals][]", ""
%hr
- Event::DaysOfTheWeek.each do |day_of_the_week|
= label_tag "event_repeats_monthly_on_days_of_the_week_#{day_of_the_week}", day_of_the_week.humanize, :class => 'checkbox inline' do
= check_box_tag "event[repeats_monthly_on_days_of_the_week][#{day_of_the_week}]", day_of_the_week, f.object.repeats_monthly_on_days_of_the_week.include?(day_of_the_week), {:name => "event[repeats_monthly_on_days_of_the_week][]"}
= day_of_the_week.humanize
= hidden_field_tag "event[repeats_monthly_on_days_of_the_week][]", ""
%fieldset{ id: 'repeats_weekly_options', class: 'event_option'}
= f.number_field :repeats_every_n_weeks
.control-group
%label.control-label Each
.controls
- Event::DaysOfTheWeek.each do |day_of_the_week|
= label_tag "event_repeats_weekly_each_days_of_the_week_#{day_of_the_week}", day_of_the_week.humanize, :class => 'checkbox inline' do
= check_box_tag "event[repeats_weekly_each_days_of_the_week][#{day_of_the_week}]", day_of_the_week, f.object.repeats_weekly_each_days_of_the_week.include?(day_of_the_week), {:name => "event[repeats_weekly_each_days_of_the_week][]"}
= day_of_the_week.humanize
= hidden_field_tag "event[repeats_weekly_each_days_of_the_week][]", ""
%fieldset{ id: 'repeats_yearly_options', class: 'event_option'}
= f.number_field :repeats_every_n_years
.control-group
%label.control-label Each
.controls
- Event::MonthsOfTheYear.each do |month_of_the_year|
= label_tag "event_repeats_yearly_each_months_of_the_year_#{month_of_the_year}", month_of_the_year.humanize, :class => 'checkbox inline' do
= check_box_tag "event[repeats_yearly_each_months_of_the_year][#{month_of_the_year}]", month_of_the_year, f.object.repeats_yearly_each_months_of_the_year.include?(month_of_the_year), {:name => "event[repeats_yearly_each_months_of_the_year][]"}
= month_of_the_year.humanize
= hidden_field_tag "event[repeats_yearly_each_months_of_the_year][]", ""
= f.check_box :repeats_yearly_on
#event_repeats_yearly_on_options.control-group
%label.control-label On the
.controls
- Event::Ordinals.each_with_index do |ordinal,index|
= label_tag "event_repeats_yearly_on_ordinals_#{ordinal}", ordinal, :class => 'checkbox inline' do
= check_box_tag "event[repeats_yearly_on_ordinals][#{ordinal}]", ordinal, f.object.repeats_yearly_on_ordinals.include?(ordinal), {:name => "event[repeats_yearly_on_ordinals][]"}
= Event::HumanOrdinals[index]
= hidden_field_tag "event[repeats_yearly_on_ordinals][]", ""
%hr
- Event::DaysOfTheWeek.each do |day_of_the_week|
= label_tag "event_repeats_yearly_on_days_of_the_week_#{day_of_the_week}", day_of_the_week.humanize, :class => 'checkbox inline' do
= check_box_tag "event[repeats_yearly_on_days_of_the_week][#{day_of_the_week}]", day_of_the_week, f.object.repeats_yearly_on_days_of_the_week.include?(day_of_the_week), {:name => "event[repeats_yearly_on_days_of_the_week][]"}
= day_of_the_week.humanize
= hidden_field_tag "event[repeats_yearly_on_days_of_the_week][]", ""
#calendar{:style => 'margin:1em 0;'}
- content_for :last_javascripts do
:javascript
$('#calendar').fullCalendar({
eventSources: [
// your event source
{
url: '/event_instances.json',
data: {
custom_param1: 'something',
custom_param2: 'somethingelse'
},
error: function() {
alert('there was an error while fetching events!');
}
}
// any other sources...
]
});
class Event < ActiveRecord::Base
include IceCubeMethods
# My custom attributes. Modify to suite your needs.
# attr_accessible :description, :name, :host_id, :location_id
# validates :name, :presence => true
# validates :host_id, :presence => true
# validates :location_id, :presence => true
# belongs_to :host, :class_name => 'Place'
# belongs_to :location, :class_name => 'Place'
end
class EventInstance
include ActiveModel::AttributeMethods
attr_accessor :title, :start, :end, :allDay, :event_id
def self.occurrences_between(begin_date,end_date)
# Using Squeel
# line 1 = Event doesn't repeat, but ends in window
# line 2 = Event doesn't repeat, but starts in window
# line 2 = Event doesn't repeat, but starts before and ends after
# line 4 = Event starts before our end date and repeats until a certain point of time, and that point of time after our begin date
# line 5 = Event repeats indefinitely, then all we care about is that it has started at somepoint in the last
results = Event.where{
(
(repeats == 'never') &
(from_date >= begin_date) &
(from_date <= end_date)
) | (
(repeats == 'never') &
(to_date >= begin_date) &
(to_date <= end_date)
) | (
(repeats == 'never') &
(from_date <= begin_date) &
(to_date >= end_date)
) | (
(repeats != 'never') &
(from_date <= end_date) &
(repeat_ends == 'on') &
(repeat_ends_on >= begin_date)
) | (
(repeats != 'never') &
(repeat_ends == 'never') &
(from_date <= end_date)
)
}
results.map { |event|
event.schedule.occurrences_between(begin_date,end_date).map { |date|
i = EventInstance.new()
i.title = event.name
i.start = date
i.end = date + event.duration
i.allDay = event.is_all_day
i.event_id = event.id
i
}
}.flatten.sort! {|x,y| x.start <=> y.start }
end
end
class EventInstancesController < ApplicationController
# GET /events
# GET /events.json
def index
@event_instances = EventInstance.occurrences_between(Date.parse('2013-01-01'),Date.parse('2013-04-30'))
respond_to do |format|
format.html # index.html.erb
format.json { render json: @event_instances }
end
end
end
# Add these two gems
gem 'ice_cube', '0.9.3'
gem 'squeel', '1.0.16'
module IceCubeMethods
def self.included(base)
base.send :attr_accessible, :from_date
base.send :attr_accessible, :from_time
base.send :attr_accessible, :to_date
base.send :attr_accessible, :to_time
base.send :attr_accessible, :is_all_day
base.send :attr_accessible, :repeat_ends
base.send :attr_accessible, :repeat_ends_on
base.send :attr_accessible, :repeats
base.send :attr_accessible, :repeats_every_n_days
base.send :attr_accessible, :repeats_every_n_weeks
base.send :attr_accessible, :repeats_weekly_each_days_of_the_week
base.send :attr_accessible, :repeats_every_n_months
base.send :attr_accessible, :repeats_monthly
base.send :attr_accessible, :repeats_monthly_each_days_of_the_month
base.send :attr_accessible, :repeats_monthly_on_days_of_the_week
base.send :attr_accessible, :repeats_monthly_on_ordinals
base.send :attr_accessible, :repeats_every_n_years
base.send :attr_accessible, :repeats_yearly_each_months_of_the_year
base.send :attr_accessible, :repeats_yearly_on
base.send :attr_accessible, :repeats_yearly_on_days_of_the_week
base.send :attr_accessible, :repeats_yearly_on_ordinals
base.send :attr_accessible, :time_zone
base.send :validates, :repeats, :presence => true
base.send :validates, :repeats_every_n_days, :presence => true, :if => lambda { |e| e.repeats == "daily" }
base.send :validates, :repeats_every_n_weeks, :presence => true, :if => lambda { |e| e.repeats == "weekly" }
base.send :validate, :must_have_at_least_one_repeats_weekly_each_days_of_the_week, :if => lambda { |e| e.repeats == "weekly" }
base.send :validates, :repeats_every_n_months, :presence => true, :if => lambda { |e| e.repeats == "monthly" }
base.send :validates, :repeats_monthly, :presence => true, :if => lambda { |e| e.repeats == "monthly" }
base.send :validate, :must_have_at_least_one_repeats_monthly_each_days_of_the_month, :if => lambda { |e| e.repeats == "monthly" && e.repeats_monthly == 'each' }
base.send :validate, :must_have_at_least_one_repeats_monthly_on_ordinals, :if => lambda { |e| e.repeats == "monthly" && e.repeats_monthly == 'on' }
base.send :validate, :must_have_at_least_one_repeats_monthly_on_days_of_the_week, :if => lambda { |e| e.repeats == "monthly" && e.repeats_monthly == 'on' }
base.send :validates, :repeats_every_n_years, :presence => true, :if => lambda { |e| e.repeats == "yearly" }
base.send :validate, :must_have_at_least_one_repeats_yearly_each_months_of_the_year, :if => lambda { |e| e.repeats == "yearly" }
base.send :validate, :must_have_at_least_one_repeats_yearly_on_ordinals, :if => lambda { |e| e.repeats == "yearly" && e.repeats_yearly_on }
base.send :validate, :must_have_at_least_one_repeats_yearly_on_days_of_the_week, :if => lambda { |e| e.repeats == "yearly" && e.repeats_yearly_on }
base.send :validate, :from_must_come_before_to
end
RepeatsOptions = ['never','daily','weekly','monthly','yearly']
RepeatEndsOptions = ['never','on']
RepeatMonthlyOptions = ['each','on']
DaysOfTheWeek = %w[sunday monday tuesday wednesday thursday friday saturday]
DaysOfTheMonth = (1..31).to_a
Ordinals = [1,2,3,4,-1]
HumanOrdinals = ['first','second','third','fourth','last']
MonthsOfTheYear = %w[january february march april may june july august september october november december]
def repeats_weekly_each_days_of_the_week=(repeats_weekly_each_days_of_the_week)
self.repeats_weekly_each_days_of_the_week_mask = (repeats_weekly_each_days_of_the_week & DaysOfTheWeek).map { |r| 2**DaysOfTheWeek.index(r) }.inject(0, :+)
end
def repeats_weekly_each_days_of_the_week
DaysOfTheWeek.reject do |r|
((repeats_weekly_each_days_of_the_week_mask || 0) & 2**DaysOfTheWeek.index(r)).zero?
end
end
def repeats_monthly_each_days_of_the_month=(repeats_monthly_each_days_of_the_month)
repeats_monthly_each_days_of_the_month.map!{|d| d.to_i} # They come in as strings, but our array is full of integers
self.repeats_monthly_each_days_of_the_month_mask = (repeats_monthly_each_days_of_the_month & DaysOfTheMonth).map { |r| 2**DaysOfTheMonth.index(r) }.inject(0, :+)
end
def repeats_monthly_each_days_of_the_month
DaysOfTheMonth.reject do |r|
((repeats_monthly_each_days_of_the_month_mask || 0) & 2**DaysOfTheMonth.index(r)).zero?
end
end
def repeats_monthly_on_ordinals=(repeats_monthly_on_ordinals)
repeats_monthly_on_ordinals.map!{|o| o.to_i} # They come in as strings, but our array is full of integers
self.repeats_monthly_on_ordinals_mask = (repeats_monthly_on_ordinals & Ordinals).map { |r| 2**Ordinals.index(r) }.inject(0, :+)
end
def repeats_monthly_on_ordinals
Ordinals.reject do |r|
((repeats_monthly_on_ordinals_mask || 0) & 2**Ordinals.index(r)).zero?
end
end
def repeats_monthly_on_days_of_the_week=(repeats_monthly_on_days_of_the_week)
self.repeats_monthly_on_days_of_the_week_mask = (repeats_monthly_on_days_of_the_week & DaysOfTheWeek).map { |r| 2**DaysOfTheWeek.index(r) }.inject(0, :+)
end
def repeats_monthly_on_days_of_the_week
DaysOfTheWeek.reject do |r|
((repeats_monthly_on_days_of_the_week_mask || 0) & 2**DaysOfTheWeek.index(r)).zero?
end
end
def repeats_yearly_each_months_of_the_year=(repeats_yearly_each_months_of_the_year)
self.repeats_yearly_each_months_of_the_year_mask = (repeats_yearly_each_months_of_the_year & MonthsOfTheYear).map { |r| 2**MonthsOfTheYear.index(r) }.inject(0, :+)
end
def repeats_yearly_each_months_of_the_year
MonthsOfTheYear.reject do |r|
((repeats_yearly_each_months_of_the_year_mask || 0) & 2**MonthsOfTheYear.index(r)).zero?
end
end
def repeats_yearly_on_ordinals=(repeats_yearly_on_ordinals)
repeats_yearly_on_ordinals.map!{|o| o.to_i} # They come in as strings, but our array is full of integers
self.repeats_yearly_on_ordinals_mask = (repeats_yearly_on_ordinals & Ordinals).map { |r| 2**Ordinals.index(r) }.inject(0, :+)
end
def repeats_yearly_on_ordinals
Ordinals.reject do |r|
((repeats_yearly_on_ordinals_mask || 0) & 2**Ordinals.index(r)).zero?
end
end
def repeats_yearly_on_days_of_the_week=(repeats_yearly_on_days_of_the_week)
self.repeats_yearly_on_days_of_the_week_mask = (repeats_yearly_on_days_of_the_week & DaysOfTheWeek).map { |r| 2**DaysOfTheWeek.index(r) }.inject(0, :+)
end
def repeats_yearly_on_days_of_the_week
DaysOfTheWeek.reject do |r|
((repeats_yearly_on_days_of_the_week_mask || 0) & 2**DaysOfTheWeek.index(r)).zero?
end
end
def from
if is_all_day
ActiveSupport::TimeZone[time_zone].parse(from_date.to_datetime.strftime('%Y-%m-%d')).beginning_of_day
else
ActiveSupport::TimeZone[time_zone].parse(from_date.to_datetime.strftime('%Y-%m-%d')).beginning_of_day + from_time.seconds_since_midnight
end
end
def to
if is_all_day
ActiveSupport::TimeZone[time_zone].parse(to_date.to_datetime.strftime('%Y-%m-%d')).end_of_day
else
ActiveSupport::TimeZone[time_zone].parse(to_date.to_datetime.strftime('%Y-%m-%d')).beginning_of_day + to_time.seconds_since_midnight
end
end
def duration
d = to - from - 1
end
def schedule(starts_at = nil, ends_at = nil)
starts_at ||= from
ends_at ||= to
s = IceCube::Schedule.new(starts_at, :ends_time => ends_at)
case repeats
when 'never'
s.add_recurrence_time(starts_at)
when 'daily'
s.add_recurrence_rule IceCube::Rule.daily(repeats_every_n_days)
when 'weekly'
days = repeats_weekly_each_days_of_the_week.map {|d| d.to_sym }
s.add_recurrence_rule IceCube::Rule.weekly(repeats_every_n_weeks).day(*days)
when 'monthly'
case repeats_monthly
when 'each'
s.add_recurrence_rule IceCube::Rule.monthly(repeats_every_n_months).day_of_month(*repeats_monthly_each_days_of_the_month)
when 'on'
h = {}
repeats_monthly_on_days_of_the_week.each do |day_of_the_week|
h[day_of_the_week.to_sym] = repeats_monthly_on_ordinals
end
s.add_recurrence_rule IceCube::Rule.monthly(repeats_every_n_months).day_of_week(h)
end
when 'yearly'
if repeats_yearly_on
h = {}
repeats_yearly_on_days_of_the_week.each do |day_of_the_week|
h[day_of_the_week.to_sym] = repeats_yearly_on_ordinals
end
months = repeats_yearly_each_months_of_the_year.map {|m| m.to_sym }
s.add_recurrence_rule IceCube::Rule.yearly(repeats_every_n_years).month_of_year(*months).day_of_week(h)
else
months = repeats_yearly_each_months_of_the_year.map {|m| m.to_sym }
s.add_recurrence_rule IceCube::Rule.yearly(repeats_every_n_years).month_of_year(*months)
end
end
return s
end
private
def must_have_at_least_one_repeats_weekly_each_days_of_the_week
if repeats_weekly_each_days_of_the_week.empty?
errors.add(:base, "You must have at least one repeats weekly each days of the week")
end
end
def must_have_at_least_one_repeats_monthly_each_days_of_the_month
if repeats_monthly_each_days_of_the_month.empty?
errors.add(:base, "You must have at least one repeats monthly each days of the month")
end
end
def must_have_at_least_one_repeats_monthly_on_ordinals
if repeats_monthly_on_ordinals.empty?
errors.add(:base, "You must have at least one repeats monthly on ordinals")
end
end
def must_have_at_least_one_repeats_monthly_on_days_of_the_week
if repeats_monthly_on_days_of_the_week.empty?
errors.add(:base, "You must have at least one repeats monthly on days of the week")
end
end
def must_have_at_least_one_repeats_yearly_each_months_of_the_year
if repeats_yearly_each_months_of_the_year.empty?
errors.add(:base, "You must have at least one repeats yearly each months of the year")
end
end
def must_have_at_least_one_repeats_yearly_on_ordinals
if repeats_yearly_on_ordinals.empty?
errors.add(:base, "You must have at least one repeats yearly on ordinals")
end
end
def must_have_at_least_one_repeats_yearly_on_days_of_the_week
if repeats_yearly_on_days_of_the_week.empty?
errors.add(:base, "You must have at least one repeats yearly on days of the week")
end
end
def from_must_come_before_to
if from > to
errors.add(:to_date, "must come after the from date")
end
end
end
class CreateEvents < ActiveRecord::Migration
def change
create_table :events do |t|
# My custom attributes. Modify to suite your needs.
# t.string :name
# t.text :description
# t.integer :location_id
# t.integer :host_id
# The core migration
t.boolean :is_all_day
t.date :from_date
t.time :from_time
t.date :to_date
t.time :to_time
t.string :repeats
t.integer :repeats_every_n_days
t.integer :repeats_every_n_weeks
t.integer :repeats_weekly_each_days_of_the_week_mask
t.integer :repeats_every_n_months
t.string :repeats_monthly
t.integer :repeats_monthly_each_days_of_the_month_mask
t.integer :repeats_monthly_on_ordinals_mask
t.integer :repeats_monthly_on_days_of_the_week_mask
t.integer :repeats_every_n_years
t.integer :repeats_yearly_each_months_of_the_year_mask
t.boolean :repeats_yearly_on
t.integer :repeats_yearly_on_ordinals_mask
t.integer :repeats_yearly_on_days_of_the_week_mask
t.string :repeat_ends
t.date :repeat_ends_on
t.string :time_zone
t.timestamps
end
end
end
@dgilperez
Copy link

Man, thanks for sharing this! Awesome work!

@rpearce
Copy link

rpearce commented Apr 24, 2013

well done and thanks for sharing

@mzaragoza
Copy link

Any chance of adding a license for this... I am adding it to a sample project I want to share on heroku and here

@mzaragoza
Copy link

Created a Demo of this
http://rails-fullcalendar-icecube.herokuapp.com/

Github
https://github.com/mzararagoza/rails-fullcalendar-icecube

There are some problems with repeat-ends feature not adding an exception to the schedule
Can you foward me an updated module IceCubeMethods

@jbat
Copy link

jbat commented Dec 3, 2013

This is totally awesome. Great work!
+1 to the repeat-ends feature not adding an exception

Has anyone made any improvements to this?

@kleinjm
Copy link

kleinjm commented Jul 3, 2014

Really really great work. You saved me hours of time here. Thanks a bunch!

@findchris
Copy link

I'd recommend you all check out this gem, which I've used. It hides most of this complexity and adds a slick UI: https://github.com/GetJobber/recurring_select

@lulalala
Copy link

lulalala commented Sep 5, 2014

Nice interface! Will you ever package this as a gem?

@kcollignon
Copy link

@jbat or @mzaragoza did you ever by chance get updated code to handle adding an exception to schedules? thanks

@Frank004
Copy link

Frank004 commented Oct 1, 2015

this great thank you

@rramsden
Copy link

@nathancolgate Thanks, this really saved me a lot of time.

@kleinjm
Copy link

kleinjm commented Jun 5, 2016

Has anyone ported this to Rails 4? Not sure how send :attr_accessible is supposed to work in IceCubeMethods in Rails 4

@jonathan-nye
Copy link

@kcollignon, @jbat, @mzaragoza: the documentation for IceCube::Schedule#new shows that the param should be :end_time and not :ends_time. However, when I changed to that, it still did not fix the repeat-ends problem. So, I worked around it by adding the following change in EventInstance#occurrences_between:

    results.map { |event|
      schedule_end_date = end_date
      if event.repeat_ends == 'on' && event.repeat_ends_on < end_date
        schedule_end_date = event.repeat_ends_on
      end
      event.schedule.occurrences_between(begin_date,schedule_end_date).map { |date|
        ...

@jonathan-nye
Copy link

@kleinjm, I removed the :attr_accessible lines from IceCubeMethods since ActiveRecord will create those by default based on the DB columns. Also, I'm using this on Rails 5, but I had to use baby_squeel since squeel doesn't support Rails 5 (baby_squeel supports Rails 4/5). To port to baby_squeel, just use Event.where.has{ in EventInstance instead of Event.where{.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment