aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/calendars_controller.rb72
-rw-r--r--app/helpers/breadcrumb_helper.rb7
-rw-r--r--app/helpers/newfront_helper.rb16
-rw-r--r--app/models/calendar.rb152
-rw-r--r--app/models/organisation.rb1
-rw-r--r--app/policies/calendar_policy.rb25
-rw-r--r--app/views/calendars/_calendars.html.slim12
-rw-r--r--app/views/calendars/_date_fields.html.slim4
-rw-r--r--app/views/calendars/_form.html.slim20
-rw-r--r--app/views/calendars/_range_fields.html.slim6
-rw-r--r--app/views/calendars/edit.html.slim3
-rw-r--r--app/views/calendars/index.html.slim16
-rw-r--r--app/views/calendars/new.html.slim5
-rw-r--r--app/views/calendars/show.html.slim28
-rw-r--r--config/locales/actions.en.yml1
-rw-r--r--config/locales/actions.fr.yml1
-rw-r--r--config/locales/calendars.en.yml46
-rw-r--r--config/locales/calendars.fr.yml47
-rw-r--r--config/locales/users.fr.yml2
-rw-r--r--config/routes.rb2
-rw-r--r--db/migrate/20161228092957_create_calendars.rb15
-rw-r--r--db/schema.rb16
-rw-r--r--spec/factories/calendars.rb16
-rw-r--r--spec/models/calendar_spec.rb109
24 files changed, 614 insertions, 8 deletions
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
new file mode 100644
index 000000000..84b432bf3
--- /dev/null
+++ b/app/controllers/calendars_controller.rb
@@ -0,0 +1,72 @@
+class CalendarsController < BreadcrumbController
+ defaults resource_class: Calendar
+ before_action :check_policy, only: [:edit, :update, :destroy]
+
+ def new
+ new! do
+ @calendar.date_ranges = []
+ @calendar.dates = []
+ end
+ end
+
+ def create
+ @calendar = current_organisation.calendars.build(calendar_params)
+
+ if @calendar.valid?
+ respond_with @calendar
+ else
+ render action: 'new'
+ end
+ end
+
+ def update
+ update! do |success, failure|
+ success.html { redirect_to calendar_path(@calendar) }
+ end
+ end
+
+ def destroy
+ destroy! do |success, failure|
+ success.html { redirect_to calendars_path }
+ end
+ end
+
+ private
+ def calendar_params
+ params.require(:calendar).permit(:id, :name, :short_name, :shared, ranges_attributes: [:id, :begin, :end, :_destroy], dates: [])
+ end
+
+ def sort_column
+ current_organisation.calendars.column_names.include?(params[:sort]) ? params[:sort] : 'short_name'
+ end
+
+ def sort_direction
+ %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc'
+ end
+
+ protected
+ def collection
+ # if params[:q]
+ # if params[:q][:shared_eq]
+ # if params[:q][:shared_eq] == 'all'
+ # params[:q].delete(:shared_eq)
+ # else
+ # params[:q][:shared_eq] = params[:q][:shared_eq] == 'true'
+ # end
+ # end
+ # end
+
+ @q = current_organisation.calendars.search(params[:q])
+
+ if sort_column && sort_direction
+ @calendars ||= @q.result(distinct: true).order(sort_column + ' ' + sort_direction).paginate(page: params[:page])
+ else
+ @calendars ||= @q.result(distinct: true).paginate(page: params[:page])
+ end
+ end
+
+ def check_policy
+ authorize resource
+ end
+end
+
diff --git a/app/helpers/breadcrumb_helper.rb b/app/helpers/breadcrumb_helper.rb
index dc68bd897..d764ad60f 100644
--- a/app/helpers/breadcrumb_helper.rb
+++ b/app/helpers/breadcrumb_helper.rb
@@ -38,6 +38,8 @@ module BreadcrumbHelper
timeband_breadcrumb action
when 'Chouette::RoutingConstraintZone'
routing_constraint_zone_breadcrumb action
+ when 'Calendar'
+ calendar_breadcrumb action
when "StopAreaCopy"
stop_area_copy_breadcrumb action
when "Import"
@@ -70,6 +72,11 @@ module BreadcrumbHelper
end
end
+ def calendar_breadcrumb(action)
+ add_breadcrumb Calendar.model_name.human.pluralize
+ add_breadcrumb @calendar.name if %i(show edit).include? action
+ end
+
def workbench_breadcrumb(action)
add_breadcrumb I18n.t("breadcrumbs.referentials"), referentials_path
add_breadcrumb breadcrumb_label(@workbench), workbench_path(@workbench), :title => breadcrumb_tooltip(@workbench)
diff --git a/app/helpers/newfront_helper.rb b/app/helpers/newfront_helper.rb
index 2abfd589d..7bedbeea9 100644
--- a/app/helpers/newfront_helper.rb
+++ b/app/helpers/newfront_helper.rb
@@ -55,13 +55,15 @@ module NewfrontHelper
polymorph_url << action
end
- if current_referential
- polymorph_url << current_referential
- polymorph_url << item.line if item.respond_to? :line
- elsif item.respond_to? :referential
- polymorph_url << item.referential
- elsif item.respond_to? :line_referential
- polymorph_url << item.line_referential
+ unless item.class.to_s == 'Calendar'
+ if current_referential
+ polymorph_url << current_referential
+ polymorph_url << item.line if item.respond_to? :line
+ elsif item.respond_to? :referential
+ polymorph_url << item.referential
+ elsif item.respond_to? :line_referential
+ polymorph_url << item.line_referential
+ end
end
polymorph_url << item
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
new file mode 100644
index 000000000..a04aa6d0a
--- /dev/null
+++ b/app/models/calendar.rb
@@ -0,0 +1,152 @@
+class Calendar < ActiveRecord::Base
+ belongs_to :organisation
+
+ validates_presence_of :name, :short_name, :organisation
+ validates_uniqueness_of :short_name
+ validate :date_not_in_date_ranges
+
+ private
+ def date_not_in_date_ranges
+ errors.add(:dates, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')) if dates && date_ranges && dates_and_date_ranges_overlap?
+ end
+
+ def dates_and_date_ranges_overlap?
+ overlap = false
+ dates.each do |date|
+ date_ranges.each do |date_range|
+ overlap = true if date_range.cover? date
+ end
+ end
+ overlap
+ end
+
+ class DateRange
+ include ActiveAttr::Model
+
+ attribute :id, type: Integer
+ attribute :begin, type: Date
+ attribute :end, type: Date
+
+ validates_presence_of :begin, :end
+ validate :check_end_greather_than_begin
+
+ def check_end_greather_than_begin
+ if self.begin and self.end and self.begin > self.end
+ errors.add(:end, :invalid)
+ end
+ end
+
+ def self.from_range(index, range)
+ DateRange.new id: index, begin: range.begin, end: range.end
+ end
+
+ def range
+ if self.begin and self.end and self.begin <= self.end
+ Range.new self.begin, self.end
+ end
+ end
+
+ def intersect?(*other)
+ return false if range.nil?
+
+ other = other.flatten
+ other = other.delete_if { |o| o.id == id } if id
+
+ other.any? do |date_range|
+ if other_range = date_range.range
+ (range & other_range).present?
+ end
+ end
+ end
+
+ def cover? date
+ range.cover? date
+ end
+
+ # Stuff required for coocon
+ def new_record?
+ !persisted?
+ end
+ def persisted?
+ id.present?
+ end
+
+ def mark_for_destruction
+ self._destroy = true
+ end
+
+ attribute :_destroy, type: Boolean
+ alias_method :marked_for_destruction?, :_destroy
+ end
+
+ # Required by coocon
+ def build_date_range
+ DateRange.new
+ end
+
+ def ranges
+ @ranges ||= init_ranges
+ end
+
+ def init_ranges
+ if date_ranges
+ date_ranges.each_with_index.map { |r, index| DateRange.from_range(index, r) }
+ else
+ []
+ end
+ end
+ private :init_ranges
+
+ validate :validate_ranges
+
+ def validate_ranges
+ ranges_are_valid = true
+
+ unless ranges.all?(&:valid?)
+ ranges_are_valid = false
+ end
+
+ ranges.each do |range|
+ if range.intersect?(ranges)
+ range.errors.add(:base, I18n.t("referentials.errors.overlapped_period"))
+ ranges_are_valid = false
+ end
+ end
+
+ errors.add(:ranges, :invalid) unless ranges_are_valid
+ end
+
+ def ranges_attributes=(attributes = {})
+ @ranges = []
+ attributes.each do |index, range_attribute|
+ range = DateRange.new(range_attribute.merge(id: index))
+ @ranges << range unless range.marked_for_destruction?
+ end
+
+ date_ranges_will_change!
+ end
+
+ before_validation :fill_date_ranges
+
+ def fill_date_ranges
+ if @ranges
+ self.date_ranges = @ranges.map(&:range).compact.sort_by(&:begin)
+ end
+ end
+
+ after_save :clear_ranges
+
+ def clear_ranges
+ @ranges = nil
+ end
+ private :clear_ranges
+end
+
+class Range
+ def intersection(other)
+ return nil if (self.max < other.begin or other.max < self.begin)
+ [self.begin, other.begin].max..[self.max, other.max].min
+ end
+ alias_method :&, :intersection
+end
+
diff --git a/app/models/organisation.rb b/app/models/organisation.rb
index af1042081..8547ce5e1 100644
--- a/app/models/organisation.rb
+++ b/app/models/organisation.rb
@@ -13,6 +13,7 @@ class Organisation < ActiveRecord::Base
has_many :line_referentials, through: :line_referential_memberships
has_many :workbenches
+ has_many :calendars
validates_presence_of :name
validates_uniqueness_of :code
diff --git a/app/policies/calendar_policy.rb b/app/policies/calendar_policy.rb
new file mode 100644
index 000000000..4fbed2ccb
--- /dev/null
+++ b/app/policies/calendar_policy.rb
@@ -0,0 +1,25 @@
+class CalendarPolicy < ApplicationPolicy
+ class Scope < Scope
+ def resolve
+ scope
+ end
+ end
+
+ def show?
+ organisation_match? || share?
+ end
+
+ def create? ; true end
+ def update? ; true end
+ def new? ; true end
+ def edit? ; true end
+ def destroy? ; true end
+
+ def share?
+ record.shared
+ end
+
+ def organisation_match?
+ current_organisation == record.organisation
+ end
+end
diff --git a/app/views/calendars/_calendars.html.slim b/app/views/calendars/_calendars.html.slim
new file mode 100644
index 000000000..7813ff89e
--- /dev/null
+++ b/app/views/calendars/_calendars.html.slim
@@ -0,0 +1,12 @@
+- if @calendars.any?
+ = table_builder @calendars,
+ { @calendars.human_attribute_name(:short_name) => 'short_name', @calendars.human_attribute_name(:shared) => 'shared' },
+ [:show, :edit, :delete],
+ 'table table-bordered'
+
+ .text-center
+ = will_paginate @calendars, container: false, renderer: RemoteBootstrapPaginationLinkRenderer
+
+- else
+ = replacement_msg t('calendars.search_no_results')
+
diff --git a/app/views/calendars/_date_fields.html.slim b/app/views/calendars/_date_fields.html.slim
new file mode 100644
index 000000000..8fd1b5f14
--- /dev/null
+++ b/app/views/calendars/_date_fields.html.slim
@@ -0,0 +1,4 @@
+.nested-fields
+ = f.input :date, as: :date, html5: true
+ /= link_to_remove_association f, data: { confirm: t('are_you_sure') } do
+ span.fa.fa-trash
diff --git a/app/views/calendars/_form.html.slim b/app/views/calendars/_form.html.slim
new file mode 100644
index 000000000..d12473b3e
--- /dev/null
+++ b/app/views/calendars/_form.html.slim
@@ -0,0 +1,20 @@
+= simple_form_for @calendar, html: { class: 'form-horizontal' } do |f|
+ = f.input :name
+ = f.input :short_name
+ / = f.label :dates
+ / = f.simple_fields_for :dates do |date|
+ / = render 'date_fields', f: date
+ / .links
+ / = link_to_add_association '+', f, :dates
+ = f.label :date_ranges
+ = f.simple_fields_for :ranges do |range|
+ = render 'range_fields', f: range
+ .links
+ = link_to_add_association '+', f, :ranges
+
+ .form-actions
+ = f.button :submit, as: :button
+ = link_to t('cancel'), calendars_path
+
+
+/ TODO : cocoon
diff --git a/app/views/calendars/_range_fields.html.slim b/app/views/calendars/_range_fields.html.slim
new file mode 100644
index 000000000..92cadc4e9
--- /dev/null
+++ b/app/views/calendars/_range_fields.html.slim
@@ -0,0 +1,6 @@
+.nested-fields
+ = f.input :begin, as: :date, html5: true, label: t('simple_form.labels.calendar.ranges.begin')
+ = f.input :end, as: :date, html5: true, label: t('simple_form.labels.calendar.ranges.end')
+ /= link_to_remove_association f, data: { confirm: t('are_you_sure') } do
+ span.fa.fa-trash
+
diff --git a/app/views/calendars/edit.html.slim b/app/views/calendars/edit.html.slim
new file mode 100644
index 000000000..f827e2eb6
--- /dev/null
+++ b/app/views/calendars/edit.html.slim
@@ -0,0 +1,3 @@
+= title_tag t('.title')
+
+= render 'form'
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
new file mode 100644
index 000000000..3feca1b1c
--- /dev/null
+++ b/app/views/calendars/index.html.slim
@@ -0,0 +1,16 @@
+= title_tag t('.title')
+
+= search_form_for @q, url: calendars_path, remote: true, html: { method: :get, class: 'form', id: 'search', role: 'form' } do |f|
+
+ .panel.panel-default
+ .panel-heading
+ .input-group.col-md-12
+ = f.search_field :short_name_cont, placeholder: t('.short_name_cont'), class: 'form-control'
+
+ .input-group-btn
+ button.btn.btn-primary#search-btn type='submit'
+ span.fa.fa-search
+
+#calendars
+ = render 'calendars'
+
diff --git a/app/views/calendars/new.html.slim b/app/views/calendars/new.html.slim
new file mode 100644
index 000000000..ccae7ce89
--- /dev/null
+++ b/app/views/calendars/new.html.slim
@@ -0,0 +1,5 @@
+= title_tag t('.title')
+
+= @calendar.inspect
+
+= render 'form'
diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim
new file mode 100644
index 000000000..419cec78e
--- /dev/null
+++ b/app/views/calendars/show.html.slim
@@ -0,0 +1,28 @@
+= title_tag t('.title', calendar: @calendar.name)
+
+p
+ label => "#{Calendar.human_attribute_name('name')} : "
+ = @calendar.name
+p
+ label => "#{Calendar.human_attribute_name('short_name')} : "
+ = @calendar.short_name
+p
+ label => "#{Calendar.human_attribute_name('date_ranges')} : "
+ = @calendar.date_ranges
+p
+ label => "#{Calendar.human_attribute_name('dates')} : "
+ = @calendar.dates
+p
+ label => "#{Calendar.human_attribute_name('shared')} : "
+ = @calendar.shared
+p
+ label => "#{Organisation.model_name.human} : "
+ = @calendar.organisation.name
+
+
+- content_for(:sidebar) do
+ ul.actions
+ li
+ = link_to t('calendars.actions.edit'), edit_calendar_path(@calendar), class: 'edit'
+ li
+ = link_to t('calendars.actions.destroy'), calendar_path(@calendar), method: :delete, data: { confirm: t('calendars.actions.destroy_confirm') }, class: 'remove'
diff --git a/config/locales/actions.en.yml b/config/locales/actions.en.yml
index 1321e6761..ba5bc1506 100644
--- a/config/locales/actions.en.yml
+++ b/config/locales/actions.en.yml
@@ -10,4 +10,5 @@ en:
search_hint: "Type in a search term"
no_result_text: "No Results"
searching_term: "Searching..."
+ are_you_sure: Are you sure?
diff --git a/config/locales/actions.fr.yml b/config/locales/actions.fr.yml
index 6c3e22933..16c687458 100644
--- a/config/locales/actions.fr.yml
+++ b/config/locales/actions.fr.yml
@@ -10,3 +10,4 @@ fr:
search_hint: "Entrez un texte à rechercher"
no_result_text: "Aucun résultat"
searching_term: "Recherche en cours..."
+ are_you_sure: Etes vous sûr ?
diff --git a/config/locales/calendars.en.yml b/config/locales/calendars.en.yml
new file mode 100644
index 000000000..26aa3a7bd
--- /dev/null
+++ b/config/locales/calendars.en.yml
@@ -0,0 +1,46 @@
+en:
+ calendars:
+ search_no_results: No calendar matching your query
+ actions:
+ new: Add a new calendar
+ edit: Edit this calendar
+ destroy: Remove this calendar
+ destroy_confirm: Are you sure you want destroy this calendar?
+ activerecord:
+ models:
+ calendar: Calendar
+ attributes:
+ calendar:
+ name: Name
+ short_name: Short name
+ date_ranges: Date ranges
+ dates: Dates
+ shared: Shared
+ activerecord:
+ errors:
+ models:
+ calendar:
+ attributes:
+ dates:
+ date_in_date_ranges: A date can not be in Dates and in Date ranges.
+ index:
+ title: Calendars
+ short_name_cont: Search by short name
+ new:
+ title: "Add a new calendar"
+ edit:
+ title: "Update calendar %{calendar}"
+ show:
+ title: "Calendar %{calendar}"
+ simple_form:
+ labels:
+ calendar:
+ name: Name
+ short_name: Short name
+ dates: Dates
+ shared: Shared
+ date_ranges: Date ranges
+ ranges:
+ begin: Beginning
+ end: End
+
diff --git a/config/locales/calendars.fr.yml b/config/locales/calendars.fr.yml
new file mode 100644
index 000000000..292566532
--- /dev/null
+++ b/config/locales/calendars.fr.yml
@@ -0,0 +1,47 @@
+fr:
+ calendars:
+ search_no_results: "Aucun calendrier ne correspond à votre recherche"
+ actions:
+ new: Ajouter un calendrier
+ edit: Modifier cet calendrier
+ destroy: Supprimer cet calendrier
+ destroy_confirm: Etes vous sûr de supprimer cet calendrier ?
+ activerecord:
+ models:
+ calendar: Calendrier
+ attributes:
+ calendar:
+ name: Nom
+ short_name: Nom court
+ date_ranges: Intervalles de dates
+ dates: Dates
+ shared: Partagé
+ index:
+ title: Calendriers
+ short_name_cont: Recherche par nom court
+ new:
+ title: "Ajouter un calendrier"
+ edit:
+ title: "Modifier le calendrier %{calendar}"
+ show:
+ title: "Calendrier %{calendar}"
+ simple_form:
+ labels:
+ calendar:
+ name: Nom
+ short_name: Nom court
+ dates: Dates
+ shared: Partagé
+ date_ranges: Intervalles de dates
+ ranges:
+ begin: Début
+ end: Fin
+ activerecord:
+ errors:
+ models:
+ calendar:
+ attributes:
+ dates:
+ date_in_date_ranges: 'Une même date ne peut pas être à la fois dans Dates et dans Intervalles de dates.'
+
+
diff --git a/config/locales/users.fr.yml b/config/locales/users.fr.yml
index dcb81b808..f50218605 100644
--- a/config/locales/users.fr.yml
+++ b/config/locales/users.fr.yml
@@ -20,3 +20,5 @@ fr:
user:
name: "Nom complet"
username: "Nom d'utilisateur"
+
+
diff --git a/config/routes.rb b/config/routes.rb
index c49b28a07..b2d3922fa 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -67,6 +67,8 @@ ChouetteIhm::Application.routes.draw do
resources :networks
end
+ resources :calendars
+
resources :referentials do
resources :api_keys
resources :autocomplete_stop_areas
diff --git a/db/migrate/20161228092957_create_calendars.rb b/db/migrate/20161228092957_create_calendars.rb
new file mode 100644
index 000000000..d8bc89103
--- /dev/null
+++ b/db/migrate/20161228092957_create_calendars.rb
@@ -0,0 +1,15 @@
+class CreateCalendars < ActiveRecord::Migration
+ def change
+ create_table :calendars do |t|
+ t.string :name
+ t.string :short_name
+ t.daterange :date_ranges, array: true
+ t.date :dates, array: true
+ t.boolean :shared
+ t.belongs_to :organisation, index: true
+
+ t.timestamps
+ end
+ add_index :calendars, :short_name, unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 0277cae91..562bf2ab6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20161208120132) do
+ActiveRecord::Schema.define(version: 20161228092957) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -77,6 +77,20 @@ ActiveRecord::Schema.define(version: 20161208120132) do
t.datetime "updated_at"
end
+ create_table "calendars", force: true do |t|
+ t.string "name"
+ t.string "short_name"
+ t.daterange "date_ranges", array: true
+ t.date "dates", array: true
+ t.boolean "shared"
+ t.integer "organisation_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "calendars", ["organisation_id"], :name => "index_calendars_on_organisation_id"
+ add_index "calendars", ["short_name"], :name => "index_calendars_on_short_name", :unique => true
+
create_table "clean_up_results", force: true do |t|
t.string "message_key"
t.hstore "message_attributs"
diff --git a/spec/factories/calendars.rb b/spec/factories/calendars.rb
new file mode 100644
index 000000000..5f3188bee
--- /dev/null
+++ b/spec/factories/calendars.rb
@@ -0,0 +1,16 @@
+FactoryGirl.define do
+ factory :calendar do
+ sequence(:name) { |n| "Calendar #{n}" }
+ sequence(:short_name) { |n| "Cal #{n}" }
+ date_ranges { [generate(:date_range)] }
+ sequence(:dates) { |n| [ Date.yesterday - n, Date.yesterday - 2*n ] }
+ shared false
+ organisation
+ end
+
+ sequence :date_range do |n|
+ date = Date.today + 2*n
+ date..(date+1)
+ end
+end
+
diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb
new file mode 100644
index 000000000..1143c6615
--- /dev/null
+++ b/spec/models/calendar_spec.rb
@@ -0,0 +1,109 @@
+require 'rails_helper'
+
+RSpec.describe Calendar, :type => :model do
+
+ it { should belong_to(:organisation) }
+
+ it { is_expected.to validate_presence_of(:organisation) }
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_presence_of(:short_name) }
+ it { is_expected.to validate_uniqueness_of(:short_name) }
+
+ describe 'validations' do
+ it 'validates that dates and date_ranges do not overlap' do
+ calendar = build(:calendar, dates: [Date.today], date_ranges: [Date.today..Date.tomorrow])
+ expect {
+ calendar.save!
+ }.to raise_error(ActiveRecord::RecordInvalid)
+ expect(calendar.errors.messages[:dates]).to eq([I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')])
+ end
+ end
+
+ describe 'DateRange' do
+
+ subject { date_range }
+
+ def date_range(attributes = {})
+ return @date_range if attributes.empty? and @date_range
+ Calendar::DateRange.new(attributes).tap do |date_range|
+ @date_range = date_range if attributes.empty?
+ end
+ end
+
+ it 'should support mark_for_destruction (required by cocoon)' do
+ date_range.mark_for_destruction
+ expect(date_range).to be_marked_for_destruction
+ end
+
+ it 'should support _destroy attribute (required by coocon)' do
+ date_range._destroy = true
+ expect(date_range).to be_marked_for_destruction
+ end
+
+ it 'should support new_record? (required by cocoon)' do
+ expect(Calendar::DateRange.new).to be_new_record
+ expect(date_range(id: 42)).not_to be_new_record
+ end
+
+ it 'should cast begin as date attribute' do
+ expect(date_range(begin: '2016-11-22').begin).to eq(Date.new(2016,11,22))
+ end
+
+ it 'should cast end as date attribute' do
+ expect(date_range(end: '2016-11-22').end).to eq(Date.new(2016,11,22))
+ end
+
+ it { is_expected.to validate_presence_of(:begin) }
+ it { is_expected.to validate_presence_of(:end) }
+
+ it 'should validate that end is greather than or equlals to begin' do
+ expect(date_range(begin: '2016-11-21', end: '2016-11-22')).to be_valid
+ expect(date_range(begin: '2016-11-21', end: '2016-11-21')).to be_valid
+ expect(date_range(begin: '2016-11-22', end: '2016-11-21')).to_not be_valid
+ end
+
+ describe 'intersect?' do
+
+ it 'should detect date in common with other date_ranges' do
+ november = date_range(begin: '2016-11-01', end: '2016-11-30')
+ mid_november_mid_december = date_range(begin: '2016-11-15', end: '2016-12-15')
+ expect(november.intersect?(mid_november_mid_december)).to be(true)
+ end
+
+ it 'should not intersect when no date is in common' do
+ november = date_range(begin: '2016-11-01', end: '2016-11-30')
+ december = date_range(begin: '2016-12-01', end: '2016-12-31')
+
+ expect(november.intersect?(december)).to be(false)
+
+ january = date_range(begin: '2017-01-01', end: '2017-01-31')
+ expect(november.intersect?(december, january)).to be(false)
+ end
+
+ it 'should not intersect itself' do
+ date_range = date_range(id: 42, begin: '2016-11-01', end: '2016-11-30')
+ expect(date_range.intersect?(date_range)).to be(false)
+ end
+
+ end
+
+ end
+
+ describe 'before_validation' do
+ let(:calendar) { create(:calendar, date_ranges: []) }
+
+ it 'shoud fill date_ranges with date ranges' do
+ expected_ranges = [
+ Range.new(Date.today, Date.tomorrow)
+ ]
+ expected_ranges.each_with_index do |range, index|
+ calendar.date_ranges << Calendar::DateRange.from_range(index, range)
+ end
+ calendar.valid?
+
+ expect(calendar.date_ranges.map { |date_range| date_range.begin..date_range.end }).to eq(expected_ranges)
+ end
+ end
+
+end
+