-
-
Save nathancolgate/4998196 to your computer and use it in GitHub Desktop.
| = 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 |
well done and thanks for sharing
Any chance of adding a license for this... I am adding it to a sample project I want to share on heroku and here
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
This is totally awesome. Great work!
+1 to the repeat-ends feature not adding an exception
Has anyone made any improvements to this?
Really really great work. You saved me hours of time here. Thanks a bunch!
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
Nice interface! Will you ever package this as a gem?
@jbat or @mzaragoza did you ever by chance get updated code to handle adding an exception to schedules? thanks
this great thank you
@nathancolgate Thanks, this really saved me a lot of time.
Has anyone ported this to Rails 4? Not sure how send :attr_accessible is supposed to work in IceCubeMethods in Rails 4
@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|
...@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{.
Man, thanks for sharing this! Awesome work!