diff options
| author | cedricnjanga | 2018-02-14 06:26:04 -0800 |
|---|---|---|
| committer | GitHub | 2018-02-14 06:26:04 -0800 |
| commit | 3916476c64dc690195a1273dab7188bb48f69152 (patch) | |
| tree | 4b0c02015b4e41ffb8bad34d3694266eae25ca20 | |
| parent | 2b37ca3b2f57d4c8cb00e18ce39371e7aefc1db3 (diff) | |
| parent | 385a9e1b68f3eea51f71386a8f4856477ba6a273 (diff) | |
| download | chouette-core-3562_ccset_affectation.tar.bz2 | |
Merge branch 'master' into 3562_ccset_affectation3562_ccset_affectation
51 files changed, 397 insertions, 122 deletions
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb new file mode 100644 index 000000000..e38a92982 --- /dev/null +++ b/app/controllers/statuses_controller.rb @@ -0,0 +1,20 @@ +class StatusesController < ChouetteController + respond_to :json + + def index + + status = { + referentials_blocked: Referential.blocked.count, + imports_blocked: Import.blocked.count, + compliance_check_sets_blocked: ComplianceCheckSet.blocked.count + } + status[:status] = global_status status + render json: status.to_json + end + + private + + def global_status status + status.values.all?(&:zero?) ? 'ok' : 'ko' + end +end diff --git a/app/decorators/time_table_decorator.rb b/app/decorators/time_table_decorator.rb index 95b1fd959..e4f9d7dbc 100644 --- a/app/decorators/time_table_decorator.rb +++ b/app/decorators/time_table_decorator.rb @@ -14,7 +14,7 @@ class TimeTableDecorator < AF83::Decorator l.href { [:edit, context[:referential], object] } end - instance_decorator.action_link if: ->{ object.calendar }, secondary: true do |l| + instance_decorator.action_link policy: :actualize, if: ->{ object.calendar }, secondary: true do |l| l.content t('actions.actualize') l.href do h.actualize_referential_time_table_path( diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb index 279995741..2068dd23c 100644 --- a/app/helpers/table_builder_helper.rb +++ b/app/helpers/table_builder_helper.rb @@ -330,7 +330,7 @@ module TableBuilderHelper else menu = content_tag :ul, class: 'dropdown-menu' do ( - CustomLinks.new(item, pundit_user, links, referential).links + + CustomLinks.new(item, pundit_user, links, referential, workgroup).links + action_links.select { |link| link.is_a?(Link) } ).map do |link| gear_menu_link(link) diff --git a/app/javascript/routes/components/BSelect2.js b/app/javascript/routes/components/BSelect2.js index 158deaa17..035bce155 100644 --- a/app/javascript/routes/components/BSelect2.js +++ b/app/javascript/routes/components/BSelect2.js @@ -85,7 +85,7 @@ class BSelect2 extends Component{ onSelect={ this.props.onSelect } ref='newSelect' options={{ - placeholder: this.context.I18n.routes.edit.select2.placeholder, + placeholder: this.context.I18n.t("routes.edit.select2.placeholder"), allowClear: true, language: 'fr', /* Doesn't seem to work... :( */ theme: 'bootstrap', diff --git a/app/javascript/routes/components/OlMap.js b/app/javascript/routes/components/OlMap.js index 056bddbcb..4beb02872 100644 --- a/app/javascript/routes/components/OlMap.js +++ b/app/javascript/routes/components/OlMap.js @@ -115,40 +115,40 @@ export default class OlMap extends Component{ <strong>{this.props.value.olMap.json.name}</strong> </p> <p> - <strong>{this.context.I18n.routes.edit.stop_point_type} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.stop_point_type')} : </strong> {this.props.value.olMap.json.area_type} </p> <p> - <strong>{this.context.I18n.routes.edit.short_name} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.short_name')} : </strong> {this.props.value.olMap.json.short_name} </p> <p> - <strong>{this.context.I18n.id_reflex} : </strong> + <strong>{this.context.I18n.t('id_reflex')} : </strong> {this.props.value.olMap.json.user_objectid} </p> - <p><strong>{this.context.I18n.routes.edit.map.coordinates} : </strong></p> + <p><strong>{this.context.I18n.t('routes.edit.map.coordinates')} : </strong></p> <p style={{paddingLeft: 10, marginTop: 0}}> - <em>{this.context.I18n.routes.edit.map.proj}.: </em>WSG84<br/> - <em>{this.context.I18n.routes.edit.map.lat}.: </em>{this.props.value.olMap.json.latitude} <br/> - <em>{this.context.I18n.routes.edit.map.lon}.: </em>{this.props.value.olMap.json.longitude} + <em>{this.context.I18n.t('routes.edit.map.proj')}.: </em>WSG84<br/> + <em>{this.context.I18n.t('routes.edit.map.lat')}.: </em>{this.props.value.olMap.json.latitude} <br/> + <em>{this.context.I18n.t('routes.edit.map.lon')}.: </em>{this.props.value.olMap.json.longitude} </p> <p> - <strong>{this.context.I18n.routes.edit.map.postal_code} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.postal_code')} : </strong> {this.props.value.olMap.json.zip_code} </p> <p> - <strong>{this.context.I18n.routes.edit.map.city} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.city')} : </strong> {this.props.value.olMap.json.city_name} </p> <p> - <strong>{this.context.I18n.routes.edit.map.comment} : </strong> + <strong>{this.context.I18n.t('routes.edit.map.comment')} : </strong> {this.props.value.olMap.json.comment} </p> {(this.props.value.stoparea_id != this.props.value.olMap.json.stoparea_id) &&( <div className='btn btn-outline-primary btn-sm' onClick= {() => {this.props.onUpdateViaOlMap(this.props.index, this.props.value.olMap.json)}} - >{this.context.I18n.actions.select}</div> + >{this.context.I18n.t('actions.select')}</div> )} </div> <div className='map_content'> @@ -162,7 +162,7 @@ export default class OlMap extends Component{ } } -OlMap.PropTypes = { +OlMap.propTypes = { } OlMap.contextTypes = { diff --git a/app/javascript/routes/components/StopPoint.js b/app/javascript/routes/components/StopPoint.js index 2d47e802b..af51a6bb4 100644 --- a/app/javascript/routes/components/StopPoint.js +++ b/app/javascript/routes/components/StopPoint.js @@ -18,15 +18,15 @@ export default function StopPoint(props, {I18n}) { <div> <select className='form-control' value={props.value.for_boarding} id="for_boarding" onChange={props.onSelectChange}> - <option value="normal">{I18n.routes.edit.stop_point.boarding.normal}</option> - <option value="forbidden">{I18n.routes.edit.stop_point.boarding.forbidden}</option> + <option value="normal">{I18n.t('routes.edit.stop_point.boarding.normal')}</option> + <option value="forbidden">{I18n.t('routes.edit.stop_point.boarding.forbidden')}</option> </select> </div> <div> <select className='form-control' value={props.value.for_alighting} id="for_alighting" onChange={props.onSelectChange}> - <option value="normal">{I18n.routes.edit.stop_point.alighting.normal}</option> - <option value="forbidden">{I18n.routes.edit.stop_point.alighting.forbidden}</option> + <option value="normal">{I18n.t('routes.edit.stop_point.alighting.normal')}</option> + <option value="forbidden">{I18n.t('routes.edit.stop_point.alighting.forbidden')}</option> </select> </div> @@ -77,7 +77,7 @@ export default function StopPoint(props, {I18n}) { ) } -StopPoint.PropTypes = { +StopPoint.propTypes = { onToggleMap: PropTypes.func.isRequired, onToggleEdit: PropTypes.func.isRequired, onDeleteClick: PropTypes.func.isRequired, @@ -93,4 +93,4 @@ StopPoint.PropTypes = { StopPoint.contextTypes = { I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/routes/components/StopPointList.js b/app/javascript/routes/components/StopPointList.js index b39fa0c9c..b227abdea 100644 --- a/app/javascript/routes/components/StopPointList.js +++ b/app/javascript/routes/components/StopPointList.js @@ -10,22 +10,22 @@ export default function StopPointList({ stopPoints, onDeleteClick, onMoveUpClick <div className="wrapper"> <div style={{width: 100}}> <div className="form-group"> - <label className="control-label">{I18n.reflex_id}</label> + <label className="control-label">{I18n.t('simple_form.labels.stop_point.reflex_id')}</label> </div> </div> <div> <div className="form-group"> - <label className="control-label">{I18n.simple_form.labels.stop_point.name}</label> + <label className="control-label">{I18n.t('simple_form.labels.stop_point.name')}</label> </div> </div> <div> <div className="form-group"> - <label className="control-label">{I18n.simple_form.labels.stop_point.for_boarding}</label> + <label className="control-label">{I18n.t('simple_form.labels.stop_point.for_boarding')}</label> </div> </div> <div> <div className="form-group"> - <label className="control-label">{I18n.simple_form.labels.stop_point.for_alighting}</label> + <label className="control-label">{I18n.t('simple_form.labels.stop_point.for_alighting')}</label> </div> </div> <div className='actions-5'></div> diff --git a/app/javascript/time_tables/actions/index.js b/app/javascript/time_tables/actions/index.js index 4a36ec4e1..98b9eab4b 100644 --- a/app/javascript/time_tables/actions/index.js +++ b/app/javascript/time_tables/actions/index.js @@ -8,7 +8,7 @@ const I18n = clone(window, "I18n") const actions = { weekDays: (index) => { - return range(1, 8).map(n => I18n.time_tables.edit.metas.days[n]) + return range(1, 8).map(n => I18n.t('time_tables.edit.metas.days')[n]) }, strToArrayDayTypes: (str) =>{ return actions.weekDays().map(day => str.indexOf(day) !== -1) @@ -155,7 +155,7 @@ const actions = { type : 'CLOSE_MODAL' }), monthName(strDate) { - let monthList = range(1,13).map(n => I18n.calendars.months[n]) + let monthList = range(1,13).map(n => I18n.t('calendars.months.'+ n )) let date = new Date(strDate) return monthList[date.getUTCMonth()] }, @@ -225,7 +225,7 @@ const actions = { let period = periods[i] if (index !== i && !period.deleted) { if (new Date(period.period_start) <= end && new Date(period.period_end) >= start) { - error = I18n.time_tables.edit.error_submit.periods_overlaps + error = I18n.t('time_tables.edit.error_submit.periods_overlaps') break } } @@ -239,7 +239,7 @@ const actions = { for (let day of in_days) { if (start <= new Date(day.date) && end >= new Date(day.date)) { - error = I18n.time_tables.edit.error_submit.dates_overlaps + error = I18n.t('time_tables.edit.error_submit.dates_overlaps') break } } @@ -316,9 +316,9 @@ const actions = { errorModalMessage: (errorKey) => { switch (errorKey) { case "withoutPeriodsWithDaysTypes": - return I18n.time_tables.edit.error_modal.withoutPeriodsWithDaysTypes + return I18n.t('time_tables.edit.error_modal.withoutPeriodsWithDaysTypes') case "withPeriodsWithoutDayTypes": - return I18n.time_tables.edit.error_modal.withPeriodsWithoutDayTypes + return I18n.t('time_tables.edit.error_modal.withPeriodsWithoutDayTypes') default: return errorKey diff --git a/app/javascript/time_tables/components/ConfirmModal.js b/app/javascript/time_tables/components/ConfirmModal.js index 845e7ed1b..4e8583bc0 100644 --- a/app/javascript/time_tables/components/ConfirmModal.js +++ b/app/javascript/time_tables/components/ConfirmModal.js @@ -9,11 +9,11 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>{I18n.time_tables.edit.confirm_modal.title}</h4> + <h4 className='modal-title'>{I18n.t('time_tables.edit.confirm_modal.title')}</h4> </div> <div className='modal-body'> <div className='mt-md mb-md'> - <p>{I18n.time_tables.edit.confirm_modal.message}</p> + <p>{I18n.t('time_tables.edit.confirm_modal.message')}</p> </div> </div> <div className='modal-footer'> @@ -23,7 +23,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan type='button' onClick={() => { onModalCancel(modal.confirmModal.callback) }} > - {I18n.cancel} + {I18n.t('cancel')} </button> <button className='btn btn-primary' @@ -31,7 +31,7 @@ export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCan type='button' onClick={() => { onModalAccept(modal.confirmModal.callback, timetable, metas) }} > - {I18n.actions.submit} + {I18n.t('actions.submit')} </button> </div> </div> @@ -49,4 +49,4 @@ ConfirmModal.propTypes = { ConfirmModal.contextTypes = { I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/time_tables/components/ErrorModal.js b/app/javascript/time_tables/components/ErrorModal.js index 543177e54..8af12f1d1 100644 --- a/app/javascript/time_tables/components/ErrorModal.js +++ b/app/javascript/time_tables/components/ErrorModal.js @@ -10,7 +10,7 @@ export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { <div className='modal-dialog'> <div className='modal-content'> <div className='modal-header'> - <h4 className='modal-title'>{I18n.time_tables.edit.error_modal.title}</h4> + <h4 className='modal-title'>{I18n.t('time_tables.edit.error_modal.title')}</h4> </div> <div className='modal-body'> <div className='mt-md mb-md'> @@ -24,7 +24,7 @@ export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { type='button' onClick={() => { onModalClose() }} > - {I18n.back} + {I18n.t('back')} </button> </div> </div> @@ -41,4 +41,4 @@ ErrorModal.propTypes = { ErrorModal.contextTypes = { I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/time_tables/components/Metas.js b/app/javascript/time_tables/components/Metas.js index 3c6848d27..08a6e26fe 100644 --- a/app/javascript/time_tables/components/Metas.js +++ b/app/javascript/time_tables/components/Metas.js @@ -13,7 +13,7 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat {/* comment (name) */} <div className="form-group"> <label htmlFor="" className="control-label col-sm-4 required"> - {I18n.time_tables.edit.metas.name} <abbr title="">*</abbr> + {I18n.t('time_tables.edit.metas.name')} <abbr title="">*</abbr> </label> <div className="col-sm-8"> <input @@ -28,7 +28,7 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat {/* color */} {metas.color !== undefined && <div className="form-group"> - <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.color}</label> + <label htmlFor="" className="control-label col-sm-4">{I18n.attribute_name('time_table', 'color')}</label> <div className="col-sm-8"> <div className="dropdown color_selector"> <button @@ -73,7 +73,7 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat {/* tags */} {metas.tags !== undefined && <div className="form-group"> - <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.tag_list}</label> + <label htmlFor="" className="control-label col-sm-4">{I18n.attribute_name('time_table', 'tag_list')}</label> <div className="col-sm-8"> <TagsSelect2 initialTags={metas.initial_tags} @@ -86,16 +86,16 @@ export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdat {/* calendar */} {metas.calendar !== null && <div className="form-group"> - <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.calendar}</label> + <label htmlFor="" className="control-label col-sm-4">{I18n.attribute_name('time_table', 'calendar')}</label> <div className="col-sm-8"> - <span>{metas.calendar ? metas.calendar.name : I18n.time_tables.edit.metas.no_calendar}</span> + <span>{metas.calendar ? metas.calendar.name : I18n.t('time_tables.edit.metas.no_calendar')}</span> </div> </div>} {/* day_types */} <div className="form-group"> <label htmlFor="" className="control-label col-sm-4"> - {I18n.time_tables.edit.metas.day_types} + {I18n.t('time_tables.edit.metas.day_types')} </label> <div className="col-sm-8"> <div className="form-group labelled-checkbox-group"> diff --git a/app/javascript/time_tables/components/PeriodForm.js b/app/javascript/time_tables/components/PeriodForm.js index 085654a88..d17a246f7 100644 --- a/app/javascript/time_tables/components/PeriodForm.js +++ b/app/javascript/time_tables/components/PeriodForm.js @@ -46,7 +46,7 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm <div> <div className="form-group"> <label htmlFor="" className="control-label required"> - {I18n.time_tables.edit.period_form.begin} + {I18n.t('time_tables.edit.period_form.begin')} <abbr title="requis">*</abbr> </label> </div> @@ -54,7 +54,7 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm <div> <div className="form-group"> <label htmlFor="" className="control-label required"> - {I18n.time_tables.edit.period_form.end} + {I18n.t('time_tables.edit.period_form.end')} <abbr title="requis">*</abbr> </label> </div> @@ -105,14 +105,14 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm className='btn btn-link' onClick={onClosePeriodForm} > - {I18n.cancel} + {I18n.t('cancel')} </button> <button type='button' className='btn btn-outline-primary mr-sm' onClick={() => onValidatePeriodForm(modal.modalProps, timetable.time_table_periods, metas, filter(timetable.time_table_dates, ['in_out', true]))} > - {I18n.actions.submit} + {I18n.t('actions.submit')} </button> </div> </div> @@ -124,7 +124,7 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm className='btn btn-outline-primary' onClick={onOpenAddPeriodForm} > - {I18n.time_tables.actions.add_period} + {I18n.t('time_tables.actions.add_period')} </button> </div> } @@ -132,7 +132,7 @@ export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm </div> </div> </div> - ) + ) } PeriodForm.propTypes = { @@ -147,4 +147,4 @@ PeriodForm.propTypes = { PeriodForm.contextTypes = { I18n: PropTypes.object -}
\ No newline at end of file +} diff --git a/app/javascript/time_tables/components/TagsSelect2.js b/app/javascript/time_tables/components/TagsSelect2.js index dc3739d58..43cf59fdf 100644 --- a/app/javascript/time_tables/components/TagsSelect2.js +++ b/app/javascript/time_tables/components/TagsSelect2.js @@ -40,7 +40,7 @@ export default class TagsSelect2 extends Component { allowClear: true, theme: 'bootstrap', width: '100%', - placeholder: this.context.I18n.time_tables.edit.select2.tag.placeholder, + placeholder: this.context.I18n.t('time_tables.edit.select2.tag.placeholder'), ajax: { url: origin + path + '/tags.json', dataType: 'json', diff --git a/app/javascript/time_tables/components/Timetable.js b/app/javascript/time_tables/components/Timetable.js index c44f2a134..991f31435 100644 --- a/app/javascript/time_tables/components/Timetable.js +++ b/app/javascript/time_tables/components/Timetable.js @@ -31,11 +31,11 @@ export default class Timetable extends Component { <div className="table table-2entries mb-sm"> <div className="t2e-head w20"> <div className="th"> - <div className="strong">{this.context.I18n.time_tables.synthesis}</div> + <div className="strong">{this.context.I18n.t('time_tables.edit.synthesis')}</div> </div> - <div className="td"><span>{this.context.I18n.time_tables.edit.day_types}</span></div> - <div className="td"><span>{this.context.I18n.time_tables.edit.periods}</span></div> - <div className="td"><span>{this.context.I18n.time_tables.edit.exceptions}</span></div> + <div className="td"><span>{this.context.I18n.t('time_tables.edit.day_types')}</span></div> + <div className="td"><span>{this.context.I18n.t('time_tables.edit.periods')}</span></div> + <div className="td"><span>{this.context.I18n.t('time_tables.edit.exceptions')}</span></div> </div> <div className="t2e-item-list w80"> <div> diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb index 47c18af09..5cc5d8b0d 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -133,7 +133,7 @@ module Chouette def checksum_attributes values = self.slice(*['name', 'published_name', 'wayback']).values values.tap do |attrs| - attrs << self.stop_points.sort_by(&:position).map{|sp| "#{sp.stop_area.user_objectid}#{sp.for_boarding}#{sp.for_alighting}" }.join + attrs << self.stop_points.sort_by(&:position).map{|sp| [sp.stop_area.user_objectid, sp.for_boarding, sp.for_alighting]} attrs << self.routing_constraint_zones.map(&:checksum) end end diff --git a/app/models/chouette/routing_constraint_zone.rb b/app/models/chouette/routing_constraint_zone.rb index 903922241..58703598e 100644 --- a/app/models/chouette/routing_constraint_zone.rb +++ b/app/models/chouette/routing_constraint_zone.rb @@ -25,7 +25,9 @@ module Chouette end def checksum_attributes - self.stop_points.map(&:stop_area).map(&:user_objectid) + [ + self.stop_points.map(&:stop_area).map(&:user_objectid) + ] end def stop_points_belong_to_route diff --git a/app/models/compliance_check_set.rb b/app/models/compliance_check_set.rb index f4c44d26d..289fc134f 100644 --- a/app/models/compliance_check_set.rb +++ b/app/models/compliance_check_set.rb @@ -19,6 +19,20 @@ class ComplianceCheckSet < ActiveRecord::Base where('created_at BETWEEN :begin AND :end', begin: period_range.begin, end: period_range.end) end + scope :blocked, -> { where('created_at < ? AND status = ?', 4.hours.ago, 'running') } + + def self.finished_statuses + %w(successful failed warning aborted canceled) + end + + def self.abort_old + where( + 'created_at < ? AND status NOT IN (?)', + 4.hours.ago, + finished_statuses + ).update_all(status: 'aborted') + end + def notify_parent if parent # parent.child_change diff --git a/app/models/concerns/checksum_support.rb b/app/models/concerns/checksum_support.rb index a76995b0f..92103798e 100644 --- a/app/models/concerns/checksum_support.rb +++ b/app/models/concerns/checksum_support.rb @@ -24,10 +24,29 @@ module ChecksumSupport self.attributes.values end + def checksum_replace_nil_or_empty_values values + # Replace empty array by nil & nil by VALUE_FOR_NIL_ATTRIBUTE + values + .map { |x| x.present? && x || VALUE_FOR_NIL_ATTRIBUTE } + .map do |item| + item = + if item.kind_of?(Array) + checksum_replace_nil_or_empty_values(item) + else + item + end + end + end + def current_checksum_source - source = self.checksum_attributes.map{ |x| x unless x.try(:empty?) } - source = source.map{ |x| x || VALUE_FOR_NIL_ATTRIBUTE } - source.map(&:to_s).join(SEPARATOR) + source = checksum_replace_nil_or_empty_values(self.checksum_attributes) + source.map{ |item| + if item.kind_of?(Array) + item.map{ |x| x.kind_of?(Array) ? "(#{x.join(',')})" : x }.join(',') + else + item + end + }.join(SEPARATOR) end def set_current_checksum_source diff --git a/app/models/concerns/min_max_values_validation.rb b/app/models/concerns/min_max_values_validation.rb index 9b2e0d548..eff779d81 100644 --- a/app/models/concerns/min_max_values_validation.rb +++ b/app/models/concerns/min_max_values_validation.rb @@ -2,6 +2,7 @@ module MinMaxValuesValidation extend ActiveSupport::Concern included do + validates_presence_of :minimum, :maximum validate :min_max_values_validation end diff --git a/app/models/concerns/timetable_support.rb b/app/models/concerns/timetable_support.rb index 8c49723fe..5242abc33 100644 --- a/app/models/concerns/timetable_support.rb +++ b/app/models/concerns/timetable_support.rb @@ -101,7 +101,7 @@ module TimetableSupport period.period_start = Date.parse(item['period_start']) period.period_end = Date.parse(item['period_end']) - period.save if period === ActiveRecord::Base && period.changed? + period.save if period.is_a?(ActiveRecord::Base) && period.changed? item['id'] = period.id end diff --git a/app/models/import.rb b/app/models/import.rb index 049a65f40..29aadcd56 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -13,6 +13,8 @@ class Import < ActiveRecord::Base where('started_at BETWEEN :begin AND :end', begin: period_range.begin, end: period_range.end) end + scope :blocked, -> { where('created_at < ? AND status = ?', 4.hours.ago, 'running') } + extend Enumerize enumerize :status, in: %w(new pending successful warning failed running aborted canceled), scope: true, default: :new @@ -42,6 +44,14 @@ class Import < ActiveRecord::Base %w(successful failed warning aborted canceled) end + def self.abort_old + where( + 'created_at < ? AND status NOT IN (?)', + 4.hours.ago, + finished_statuses + ).update_all(status: 'aborted') + end + def notify_parent parent.child_change update(notified_parent_at: DateTime.now) diff --git a/app/models/referential.rb b/app/models/referential.rb index f64db4ebf..509e0412f 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -62,6 +62,7 @@ class Referential < ActiveRecord::Base scope :order_by_validity_period, ->(dir) { joins(:metadatas).order("unnest(periodes) #{dir}") } scope :order_by_lines, ->(dir) { joins(:metadatas).group("referentials.id").order("sum(array_length(referential_metadata.line_ids,1)) #{dir}") } scope :not_in_referential_suite, -> { where referential_suite_id: nil } + scope :blocked, -> { where('ready = ? AND created_at < ?', false, 4.hours.ago) } def save_with_table_lock_timeout(options = {}) save_without_table_lock_timeout(options) diff --git a/app/models/vehicle_journey_control/delta.rb b/app/models/vehicle_journey_control/delta.rb index f061b9fdd..737b7d78c 100644 --- a/app/models/vehicle_journey_control/delta.rb +++ b/app/models/vehicle_journey_control/delta.rb @@ -4,6 +4,7 @@ module VehicleJourneyControl store_accessor :control_attributes, :maximum validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0 + validates_presence_of :maximum def self.default_code; "3-VehicleJourney-3" end end diff --git a/app/models/vehicle_journey_control/waiting_time.rb b/app/models/vehicle_journey_control/waiting_time.rb index f2666cb72..89a18a5d9 100644 --- a/app/models/vehicle_journey_control/waiting_time.rb +++ b/app/models/vehicle_journey_control/waiting_time.rb @@ -3,6 +3,7 @@ module VehicleJourneyControl store_accessor :control_attributes, :maximum validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0 + validates_presence_of :maximum def self.default_code; "3-VehicleJourney-1" end end diff --git a/app/services/parent_import_notifier.rb b/app/services/parent_import_notifier.rb deleted file mode 100644 index 47e6755e4..000000000 --- a/app/services/parent_import_notifier.rb +++ /dev/null @@ -1,15 +0,0 @@ -class ParentImportNotifier - def self.notify_when_finished(imports = nil) - imports ||= imports_pending_notification - imports.each(&:notify_parent) - end - - def self.imports_pending_notification - Import - .where( - notified_parent_at: nil, - status: Import.finished_statuses - ) - .where.not(parent: nil) - end -end diff --git a/app/services/parent_notifier.rb b/app/services/parent_notifier.rb new file mode 100644 index 000000000..653c98aff --- /dev/null +++ b/app/services/parent_notifier.rb @@ -0,0 +1,19 @@ +class ParentNotifier + def initialize(klass) + @klass = klass + end + + def notify_when_finished(collection = nil) + collection ||= objects_pending_notification + collection.each(&:notify_parent) + end + + def objects_pending_notification + @klass + .where( + notified_parent_at: nil, + status: @klass.finished_statuses + ) + .where.not(parent: nil) + end +end diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim index f17b5d4d4..0b58c0c72 100644 --- a/app/views/calendars/index.html.slim +++ b/app/views/calendars/index.html.slim @@ -16,7 +16,7 @@ key: :name, \ attribute: 'name', \ link_to: lambda do |calendar| \ - workgroup_calendar_path(current_workgroup, calendar) \ + workgroup_calendar_path(workgroup, calendar) \ end \ ), \ TableBuilderHelper::Column.new( \ diff --git a/app/views/time_tables/_form.html.slim b/app/views/time_tables/_form.html.slim index d06fdf444..007044e65 100644 --- a/app/views/time_tables/_form.html.slim +++ b/app/views/time_tables/_form.html.slim @@ -5,7 +5,7 @@ = form.input :comment, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.time_table.comment")} - if @time_table.new_record? && !@time_table.created_from - = form.input :calendar_id, as: :select, input_html: { class: 'tt_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un modèle de calendrier...', term: 'name_cont', url: autocomplete_calendars_path}} + = form.input :calendar_id, as: :select, input_html: { class: 'tt_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un modèle de calendrier...', term: 'name_cont', url: autocomplete_workgroup_calendars_path(current_workgroup)}} - if @time_table.created_from = form.input :created_from, disabled: true, input_html: { value: @time_table.created_from.comment } diff --git a/config/deploy.rb b/config/deploy.rb index 3353b4186..833ecfbbe 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -104,6 +104,6 @@ namespace :deploy do desc "Run db:seed" task :seed do - run "cd #{release_path} && RAILS_ENV=#{rails_env} #{rake} db:seed" + run "cd #{current_path} && RAILS_ENV=#{rails_env} #{rake} db:seed" end end diff --git a/config/locales/journey_patterns.en.yml b/config/locales/journey_patterns.en.yml index e5248c29c..9d9bc21e2 100644 --- a/config/locales/journey_patterns.en.yml +++ b/config/locales/journey_patterns.en.yml @@ -6,6 +6,7 @@ en: vehicle_journeys_count: "Vehicle journeys: %{count}" vehicle_journey_at_stops: "Vehicle journey at stops" actions: + index: "Journey patterns" new: "Add a new journey_pattern" edit: "Edit this journey pattern" destroy: "Remove this journey pattern" diff --git a/config/locales/stop_points.en.yml b/config/locales/stop_points.en.yml index d22d85731..72e138270 100644 --- a/config/locales/stop_points.en.yml +++ b/config/locales/stop_points.en.yml @@ -55,3 +55,4 @@ en: name: Stop Point for_boarding: "Pickup" for_alighting: "Drop off" + reflex_id: ID diff --git a/config/locales/stop_points.fr.yml b/config/locales/stop_points.fr.yml index d3c873442..71be684f6 100644 --- a/config/locales/stop_points.fr.yml +++ b/config/locales/stop_points.fr.yml @@ -52,6 +52,7 @@ fr: simple_form: labels: stop_point: - name: Arrêt + name: Arrêt for_boarding: "Montée" for_alighting: "Descente" + reflex_id: ID diff --git a/config/routes.rb b/config/routes.rb index 681b36c59..b01bba18d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -256,4 +256,6 @@ ChouetteIhm::Application.routes.draw do match '/422', to: 'errors#server_error', via: :all, as: 'unprocessable_entity' match '/500', to: 'errors#server_error', via: :all, as: 'server_error' + match '/status', to: 'statuses#index', via: :get + end diff --git a/config/schedule.rb b/config/schedule.rb index 08488c255..0d2a24f31 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -40,9 +40,15 @@ every :day, :at => '4:00 am' do end every 5.minutes do + rake "import:netex_abort_old" rake "import:notify_parent" end +every 5.minutes do + rake "compliance_check_sets:abort_old" + rake "compliance_check_sets:notify_parent" +end + every 1.minute do command "/bin/echo HeartBeat" end diff --git a/lib/tasks/compliance_check_sets.rb b/lib/tasks/compliance_check_sets.rb new file mode 100644 index 000000000..c53c7f9ed --- /dev/null +++ b/lib/tasks/compliance_check_sets.rb @@ -0,0 +1,11 @@ +namespace :compliance_check_sets do + desc "Notify parent check sets when children finish" + task notify_parent: :environment do + ParentNotifier.new(ComplianceCheckSet).notify_when_finished + end + + desc "Mark old unfinished check sets as 'aborted'" + task abort_old: :environment do + ComplianceCheckSet.abort_old + end +end diff --git a/lib/tasks/imports.rake b/lib/tasks/imports.rake index 6bc84acc8..02e32fd3d 100644 --- a/lib/tasks/imports.rake +++ b/lib/tasks/imports.rake @@ -1,6 +1,11 @@ namespace :import do desc "Notify parent imports when children finish" task notify_parent: :environment do - ParentImportNotifier.notify_when_finished + ParentNotifier.new(Import).notify_when_finished + end + + desc "Mark old unfinished Netex imports as 'aborted'" + task netex_abort_old: :environment do + NetexImport.abort_old end end diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb new file mode 100644 index 000000000..8a6db8e28 --- /dev/null +++ b/spec/controllers/statuses_controller_spec.rb @@ -0,0 +1,50 @@ +RSpec.describe StatusesController, :type => :controller do + + describe "GET index" do + login_user + render_views + + + let(:request){ get :index} + let(:parsed_response){ JSON.parse response.body } + it "should be ok" do + request + expect(response).to have_http_status 200 + expect(parsed_response["status"]).to eq "ok" + end + context "without blocked object" do + before do + create :referential + create :import + create :compliance_check_set + request + end + + it "should be ok" do + expect(response).to have_http_status 200 + expect(parsed_response["status"]).to eq "ok" + expect(parsed_response["referentials_blocked"]).to eq 0 + expect(parsed_response["imports_blocked"]).to eq 0 + expect(parsed_response["imports_blocked"]).to eq 0 + end + end + + context "with a blocked object" do + before do + create :referential, created_at: 5.hours.ago, ready: false + create :import + create :compliance_check_set + request + end + + it "should be ko" do + expect(Referential.blocked.count).to eq 1 + expect(response).to have_http_status 200 + expect(parsed_response["status"]).to eq "ko" + expect(parsed_response["referentials_blocked"]).to eq 1 + expect(parsed_response["imports_blocked"]).to eq 0 + expect(parsed_response["imports_blocked"]).to eq 0 + end + end + end +end diff --git a/spec/factories/chouette_routes.rb b/spec/factories/chouette_routes.rb index 7443d08bc..92a50b924 100644 --- a/spec/factories/chouette_routes.rb +++ b/spec/factories/chouette_routes.rb @@ -19,6 +19,7 @@ FactoryGirl.define do after(:create) do |route, evaluator| create_list(:stop_point, evaluator.stop_points_count, route: route) route.reload + route.update_checksum! end factory :route_with_journey_patterns do diff --git a/spec/factories/compliance_controls/vehicle_journey_control_factories.rb b/spec/factories/compliance_controls/vehicle_journey_control_factories.rb index e8f68cbdf..86a335aba 100644 --- a/spec/factories/compliance_controls/vehicle_journey_control_factories.rb +++ b/spec/factories/compliance_controls/vehicle_journey_control_factories.rb @@ -1,10 +1,12 @@ FactoryGirl.define do factory :vehicle_journey_control_wating_time, class: 'VehicleJourneyControl::WaitingTime' do + maximum 10 association :compliance_control_set end factory :vehicle_journey_control_delta, class: 'VehicleJourneyControl::Delta' do + maximum 10 association :compliance_control_set end diff --git a/spec/models/chouette/footnote_spec.rb b/spec/models/chouette/footnote_spec.rb index fc5e5f306..05f55c2f0 100644 --- a/spec/models/chouette/footnote_spec.rb +++ b/spec/models/chouette/footnote_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' describe Chouette::Footnote, type: :model do - let(:footnote) { create(:footnote) } + subject { create(:footnote) } it { should validate_presence_of :line } describe 'data_source_ref' do it 'should set default if omitted' do - expect(footnote.data_source_ref).to eq "DATASOURCEREF_EDITION_BOIV" + expect(subject.data_source_ref).to eq "DATASOURCEREF_EDITION_BOIV" end it 'should not set default if not omitted' do @@ -18,16 +18,16 @@ describe Chouette::Footnote, type: :model do end describe 'checksum' do - it_behaves_like 'checksum support', :footnote + it_behaves_like 'checksum support' context '#checksum_attributes' do it 'should return code and label' do - expected = [footnote.code, footnote.label] - expect(footnote.checksum_attributes).to include(*expected) + expected = [subject.code, subject.label] + expect(subject.checksum_attributes).to include(*expected) end it 'should not return other atrributes' do - expect(footnote.checksum_attributes).to_not include(footnote.updated_at) + expect(subject.checksum_attributes).to_not include(subject.updated_at) end end end diff --git a/spec/models/chouette/journey_pattern_spec.rb b/spec/models/chouette/journey_pattern_spec.rb index b5eb9004c..19a74a0e7 100644 --- a/spec/models/chouette/journey_pattern_spec.rb +++ b/spec/models/chouette/journey_pattern_spec.rb @@ -2,9 +2,10 @@ require 'spec_helper' describe Chouette::JourneyPattern, :type => :model do it { is_expected.to be_versioned } + subject { create(:journey_pattern) } describe 'checksum' do - it_behaves_like 'checksum support', :journey_pattern + it_behaves_like 'checksum support' end # context 'validate minimum stop_points size' do diff --git a/spec/models/chouette/route/route_base_spec.rb b/spec/models/chouette/route/route_base_spec.rb index 26f57eae5..98cb3e358 100644 --- a/spec/models/chouette/route/route_base_spec.rb +++ b/spec/models/chouette/route/route_base_spec.rb @@ -1,8 +1,8 @@ RSpec.describe Chouette::Route, :type => :model do - subject { create(:route) } + describe 'checksum' do - it_behaves_like 'checksum support', :route + it_behaves_like 'checksum support' end it { is_expected.to enumerize(:direction).in(:straight_forward, :backward, :clockwise, :counter_clockwise, :north, :north_west, :west, :south_west, :south, :south_east, :east, :north_east) } diff --git a/spec/models/chouette/routing_constraint_zone_spec.rb b/spec/models/chouette/routing_constraint_zone_spec.rb index 0282cb8b1..bda6bb04a 100644 --- a/spec/models/chouette/routing_constraint_zone_spec.rb +++ b/spec/models/chouette/routing_constraint_zone_spec.rb @@ -11,7 +11,7 @@ describe Chouette::RoutingConstraintZone, type: :model do it { is_expected.to be_versioned } describe 'checksum' do - it_behaves_like 'checksum support', :routing_constraint_zone + it_behaves_like 'checksum support' end describe 'validations' do diff --git a/spec/models/chouette/time_table_period_spec.rb b/spec/models/chouette/time_table_period_spec.rb index cc1a3ae09..e14d38ade 100644 --- a/spec/models/chouette/time_table_period_spec.rb +++ b/spec/models/chouette/time_table_period_spec.rb @@ -10,7 +10,7 @@ describe Chouette::TimeTablePeriod, :type => :model do it { is_expected.to validate_presence_of :period_end } describe 'checksum' do - it_behaves_like 'checksum support', :time_table_period + it_behaves_like 'checksum support' end describe "#overlap" do diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb index 6e5da2c67..fe4bd3cfa 100644 --- a/spec/models/chouette/time_table_spec.rb +++ b/spec/models/chouette/time_table_spec.rb @@ -1048,7 +1048,7 @@ end # it { is_expected.to validate_uniqueness_of :objectid } describe 'checksum' do - it_behaves_like 'checksum support', :time_table + it_behaves_like 'checksum support' it "handles newly built dates and periods" do time_table = build(:time_table) diff --git a/spec/models/chouette/vehicle_journey_at_stop_spec.rb b/spec/models/chouette/vehicle_journey_at_stop_spec.rb index 02306883c..a97559a0c 100644 --- a/spec/models/chouette/vehicle_journey_at_stop_spec.rb +++ b/spec/models/chouette/vehicle_journey_at_stop_spec.rb @@ -1,10 +1,12 @@ require 'spec_helper' RSpec.describe Chouette::VehicleJourneyAtStop, type: :model do + subject { create(:vehicle_journey_at_stop) } + describe 'checksum' do let(:at_stop) { build_stubbed(:vehicle_journey_at_stop) } - it_behaves_like 'checksum support', :vehicle_journey_at_stop + it_behaves_like 'checksum support' context '#checksum_attributes' do it 'should return attributes' do diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index 7279980a3..76e73d9cf 100644 --- a/spec/models/chouette/vehicle_journey_spec.rb +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Chouette::VehicleJourney, :type => :model do + subject { create(:vehicle_journey) } + it { is_expected.to be_versioned } it { should have_and_belong_to_many(:purchase_windows) } @@ -21,7 +23,7 @@ describe Chouette::VehicleJourney, :type => :model do end describe 'checksum' do - it_behaves_like 'checksum support', :vehicle_journey + it_behaves_like 'checksum support' it "changes when a vjas is updated" do vehicle_journey = create(:vehicle_journey) expect{vehicle_journey.vehicle_journey_at_stops.last.update_attribute(:departure_time, Time.now)}.to change{vehicle_journey.reload.checksum} diff --git a/spec/models/compliance_check_spec.rb b/spec/models/compliance_check_spec.rb index f83d78c29..ffa59245c 100644 --- a/spec/models/compliance_check_spec.rb +++ b/spec/models/compliance_check_spec.rb @@ -15,4 +15,36 @@ RSpec.describe ComplianceCheck, type: :model do it { should validate_presence_of :name } it { should validate_presence_of :code } it { should validate_presence_of :origin_code } + + describe ".abort_old" do + it "changes check sets older than 4 hours to aborted" do + Timecop.freeze(Time.now) do + old_check_set = create( + :compliance_check_set, + status: 'pending', + created_at: 4.hours.ago - 1.minute + ) + current_check_set = create(:compliance_check_set, status: 'pending') + + ComplianceCheckSet.abort_old + + expect(current_check_set.reload.status).to eq('pending') + expect(old_check_set.reload.status).to eq('aborted') + end + end + + it "doesn't work on check sets with a `finished_status`" do + Timecop.freeze(Time.now) do + check_set = create( + :compliance_check_set, + status: 'successful', + created_at: 4.hours.ago - 1.minute + ) + + ComplianceCheckSet.abort_old + + expect(check_set.reload.status).to eq('successful') + end + end + end end diff --git a/spec/models/import_spec.rb b/spec/models/import_spec.rb index ffb2360c2..8b85f151b 100644 --- a/spec/models/import_spec.rb +++ b/spec/models/import_spec.rb @@ -29,6 +29,58 @@ RSpec.describe Import, type: :model do ) end + describe ".abort_old" do + it "changes imports older than 4 hours to aborted" do + Timecop.freeze(Time.now) do + old_import = create( + :workbench_import, + status: 'pending', + created_at: 4.hours.ago - 1.minute + ) + current_import = create(:workbench_import, status: 'pending') + + Import.abort_old + + expect(current_import.reload.status).to eq('pending') + expect(old_import.reload.status).to eq('aborted') + end + end + + it "doesn't work on imports with a `finished_status`" do + Timecop.freeze(Time.now) do + import = create( + :workbench_import, + status: 'successful', + created_at: 4.hours.ago - 1.minute + ) + + Import.abort_old + + expect(import.reload.status).to eq('successful') + end + end + + it "only works on the caller type" do + Timecop.freeze(Time.now) do + workbench_import = create( + :workbench_import, + status: 'pending', + created_at: 4.hours.ago - 1.minute + ) + netex_import = create( + :netex_import, + status: 'pending', + created_at: 4.hours.ago - 1.minute + ) + + NetexImport.abort_old + + expect(workbench_import.reload.status).to eq('pending') + expect(netex_import.reload.status).to eq('aborted') + end + end + end + describe "#destroy" do it "must destroy all child imports" do netex_import = create(:netex_import) diff --git a/spec/services/parent_import_notifier_spec.rb b/spec/services/parent_notifier_spec.rb index 3ab505f88..ecf508fcd 100644 --- a/spec/services/parent_import_notifier_spec.rb +++ b/spec/services/parent_notifier_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe ParentImportNotifier do +RSpec.describe ParentNotifier do let(:workbench_import) { create(:workbench_import) } describe ".notify_when_finished" do @@ -20,7 +20,7 @@ RSpec.describe ParentImportNotifier do expect(netex_import).to receive(:notify_parent) end - ParentImportNotifier.notify_when_finished(netex_imports) + ParentNotifier.new(Import).notify_when_finished(netex_imports) end it "doesn't call #notify_parent if its `notified_parent_at` is set" do @@ -33,11 +33,11 @@ RSpec.describe ParentImportNotifier do expect(netex_import).not_to receive(:notify_parent) - ParentImportNotifier.notify_when_finished + ParentNotifier.new(Import).notify_when_finished end end - describe ".imports_pending_notification" do + describe ".objects_pending_notification" do it "includes imports with a parent and `notified_parent_at` unset" do netex_import = create( :netex_import, @@ -47,7 +47,7 @@ RSpec.describe ParentImportNotifier do ) expect( - ParentImportNotifier.imports_pending_notification + ParentNotifier.new(Import).objects_pending_notification ).to eq([netex_import]) end @@ -55,7 +55,7 @@ RSpec.describe ParentImportNotifier do create(:import, parent: nil) expect( - ParentImportNotifier.imports_pending_notification + ParentNotifier.new(Import).objects_pending_notification ).to be_empty end @@ -70,7 +70,7 @@ RSpec.describe ParentImportNotifier do end expect( - ParentImportNotifier.imports_pending_notification + ParentNotifier.new(Import).objects_pending_notification ).to be_empty end @@ -83,7 +83,7 @@ RSpec.describe ParentImportNotifier do ) expect( - ParentImportNotifier.imports_pending_notification + ParentNotifier.new(Import).objects_pending_notification ).to be_empty end end diff --git a/spec/support/checksum_support.rb b/spec/support/checksum_support.rb index e02d9f9f3..f8dffb1b7 100644 --- a/spec/support/checksum_support.rb +++ b/spec/support/checksum_support.rb @@ -1,25 +1,23 @@ -shared_examples 'checksum support' do |factory_name| - let(:instance) { create(factory_name) } - +shared_examples 'checksum support' do describe '#current_checksum_source' do let(:attributes) { ['code_value', 'label_value'] } - let(:seperator) { ChecksumSupport::SEPARATOR } + let(:separator) { ChecksumSupport::SEPARATOR } let(:nil_value) { ChecksumSupport::VALUE_FOR_NIL_ATTRIBUTE } before do - allow_any_instance_of(instance.class).to receive(:checksum_attributes).and_return(attributes) + allow_any_instance_of(subject.class).to receive(:checksum_attributes).and_return(attributes) end - it 'should separate attribute by seperator' do - expect(instance.current_checksum_source).to eq("code_value#{seperator}label_value") + it 'should separate attribute by separator' do + expect(subject.current_checksum_source).to eq("code_value#{separator}label_value") end context 'nil value' do let(:attributes) { ['code_value', nil] } it 'should replace nil attributes by default value' do - source = "code_value#{seperator}#{nil_value}" - expect(instance.current_checksum_source).to eq(source) + source = "code_value#{separator}#{nil_value}" + expect(subject.current_checksum_source).to eq(source) end end @@ -27,27 +25,62 @@ shared_examples 'checksum support' do |factory_name| let(:attributes) { ['code_value', []] } it 'should convert to nil' do - source = "code_value#{seperator}#{nil_value}" - expect(instance.current_checksum_source).to eq(source) + source = "code_value#{separator}#{nil_value}" + expect(subject.current_checksum_source).to eq(source) + end + end + + context 'array value' do + let(:attributes) { [['v1', 'v2', 'v3'], 'code_value'] } + + it 'should convert to list' do + source = "v1,v2,v3#{separator}code_value" + expect(subject.current_checksum_source).to eq(source) + end + end + + context 'array of array value' do + let(:attributes) { [[['a1', 'a2', 'a3'], ['b1', 'b2', 'b3']], 'code_value'] } + + it 'should convert to list' do + source = "(a1,a2,a3),(b1,b2,b3)#{separator}code_value" + expect(subject.current_checksum_source).to eq(source) + end + end + + context 'array of array value, with empty array' do + let(:attributes) { [[['a1', 'a2', 'a3'], []], 'code_value'] } + + it 'should convert to list' do + source = "(a1,a2,a3),-#{separator}code_value" + expect(subject.current_checksum_source).to eq(source) end end end it 'should save checksum on create' do - expect(instance.checksum).to_not be_nil + expect(subject.checksum).to_not be_nil end it 'should save checksum_source' do - expect(instance.checksum_source).to_not be_nil + expect(subject.checksum_source).to_not be_nil end it 'should trigger set_current_checksum_source on save' do - expect(instance).to receive(:set_current_checksum_source).at_least(:once) - instance.save + expect(subject).to receive(:set_current_checksum_source).at_least(:once) + subject.save end it 'should trigger update_checksum on save' do - expect(instance).to receive(:update_checksum).at_least(:once) - instance.save + expect(subject).to receive(:update_checksum).at_least(:once) + subject.save + end + + it "doesn't change the checksum on save if the source hasn't been changed" do + checksum = subject.checksum + + subject.save + + expect(subject.checksum).to eq(checksum) end end |
