# Gemfile
source 'https://rails-assets.org' do
gem 'rails-assets-fullcalendar'
gem 'rails-assets-momentjs'
end
gem 'ice_cube'
# recurring_events_migration.rb
# rails g model recurring_event title anchor:date frequency:integer color
class CreateRecurringEvents < ActiveRecord::Migration[5.0]
def change
create_table :recurring_events do |t|
t.string :title
t.date :anchor
t.integer :frequency, limit: 1, default: 0
t.string :color
t.timestamps
end
end
end
# recurring_events_controller.rb
class RecurringEventsController < ApplicationController
before_action :set_recurring_event, only: [:show, :edit, :update, :destroy]
def index
@recurring_events = RecurringEvent.all
end
def show
end
def new
@recurring_event = RecurringEvent.new
end
def edit
end
def create
@recurring_event = RecurringEvent.new(recurring_event_params)
@recurring_event.save
end
def update
if params[:event]
@recurring_event.update(anchor: params[:event][:start])
else
@recurring_event.update(recurring_event_params)
end
end
def destroy
@recurring_event.destroy
end
private
def set_recurring_event
@recurring_event = RecurringEvent.find(params[:id])
end
def recurring_event_params
params.require(:recurring_event).permit(:title, :anchor, :frequency, :color)
end
end
# recurring_event.rb
class RecurringEvent < ApplicationRecord
enum frequency: { weekly: 0, biweekly: 1, monthly: 2, annually: 3 }
validates :anchor, presence: true
validates :frequency, presence: true
def schedule
@schedule ||= begin
schedule = IceCube::Schedule.new(now = anchor)
case frequency
when 'weekly'
schedule.add_recurrence_rule IceCube::Rule.weekly(1)
when 'biweekly'
schedule.add_recurrence_rule IceCube::Rule.weekly(2)
when 'monthly'
schedule.add_recurrence_rule IceCube::Rule.monthly(1)
when 'annually'
schedule.add_recurrence_rule IceCube::Rule.yearly(1)
end
schedule
end
end
def events(start_date, end_date)
start_frequency = start_date ? start_date.to_date : Date.today - 1.year
end_frequency = end_date ? end_date.to_date : Date.today + 1.year
schedule.occurrences_between(start_frequency, end_frequency)
end
end
# full_calendar.js
var initialize_calendar;
initialize_calendar = function() {
$('.calendar').each(function(){
var calendar = $(this);
calendar.fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
selectable: true,
selectHelper: true,
editable: true,
eventLimit: true,
eventSources: [
'/events.json',
'/recurring_events.json'
],
select: function(start, end) {
$.getScript('/events/new', function() {
$('#event_date_range').val(moment(start).format("MM/DD/YYYY HH:mm") + ' - ' + moment(end).format("MM/DD/YYYY HH:mm"))
date_range_picker();
$('.start_hidden').val(moment(start).format('YYYY-MM-DD HH:mm'));
$('.end_hidden').val(moment(end).format('YYYY-MM-DD HH:mm'));
});
calendar.fullCalendar('unselect');
},
eventDrop: function(event, delta, revertFunc) {
event_data = {
event: {
id: event.id,
start: event.start.format(),
end: event.end.format()
}
};
$.ajax({
url: event.update_url,
data: event_data,
type: 'PATCH'
});
},
eventClick: function(event, jsEvent, view) {
$.getScript(event.edit_url, function() {
$('#event_date_range').val(moment(event.start).format("MM/DD/YYYY HH:mm") + ' - ' + moment(event.end).format("MM/DD/YYYY HH:mm"))
date_range_picker();
$('.start_hidden').val(moment(event.start).format('YYYY-MM-DD HH:mm'));
$('.end_hidden').val(moment(event.end).format('YYYY-MM-DD HH:mm'));
});
}
});
})
};
$(document).on('turbolinks:load', initialize_calendar);
# index.json.jbuilder
json.partial! @recurring_events,
partial: 'recurring_events/recurring_event',
as: :recurring_event
# _recurring_event.json.jbuilder
events = recurring_event.events(params[:start], params[:end])
json.array! events do |event|
json.id "recurring_#{recurring_event.id}"
json.title recurring_event.title
json.start event.strftime('%Y-%m-%d')
json.end (event + 1.day).strftime('%Y-%m-%d')
json.color recurring_event.color unless recurring_event.color.blank?
json.allDay true
json.update_url recurring_event_path(recurring_event, method: :patch)
json.edit_url edit_recurring_event_path(recurring_event)
end
# update.js.erb
$('.calendar').fullCalendar('removeEvents', '<%= "recurring_#{@recurring_event.id}" %>');
$('.calendar').fullCalendar(
'renderEvents',
$.parseJSON("<%= j render(@recurring_event, format: :json).html_safe %>"),
true
);
$('.modal').modal('hide');