diff options
| author | Xinhui | 2017-05-29 16:29:32 +0200 |
|---|---|---|
| committer | Xinhui | 2017-05-29 16:29:32 +0200 |
| commit | 6886441ce86bcd720b27cdd089567def5b9d771a (patch) | |
| tree | bda614b5fce39f51a794304615a7252ef86b012e | |
| parent | 4f5cc7d35777f3b4bfa1c63c1223c679f713424e (diff) | |
| parent | 54bf18da9a74295c327e39c659ef3a28719a2631 (diff) | |
| download | chouette-core-6886441ce86bcd720b27cdd089567def5b9d771a.tar.bz2 | |
Merge branch 'master' into staging
61 files changed, 564 insertions, 227 deletions
diff --git a/app/assets/javascripts/es6_browserified/itineraries/containers/VisibleStopPoints.js b/app/assets/javascripts/es6_browserified/itineraries/containers/VisibleStopPoints.js index 11c58d9c2..8b08a1e5f 100644 --- a/app/assets/javascripts/es6_browserified/itineraries/containers/VisibleStopPoints.js +++ b/app/assets/javascripts/es6_browserified/itineraries/containers/VisibleStopPoints.js @@ -25,6 +25,7 @@ const mapDispatchToProps = (dispatch) => { onChange: (index, text) =>{ dispatch(actions.updateInputValue(index, text)) dispatch(actions.closeMaps()) + dispatch(actions.toggleEdit(index)) }, onSelectChange: (e, index) =>{ dispatch(actions.updateSelectValue(e, index)) diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js b/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js index 502320c27..a0fac84f3 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Metas.js @@ -87,7 +87,7 @@ const Metas = ({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelec <div className="form-group"> <label htmlFor="" className="control-label col-sm-4">Modèle de calendrier associé</label> <div className="col-sm-8"> - <span>{metas.calendar.name}</span> + <span>{metas.calendar ? metas.calendar.name : 'Aucun'}</span> </div> </div> diff --git a/app/assets/javascripts/es6_browserified/time_tables/index.js b/app/assets/javascripts/es6_browserified/time_tables/index.js index 1fe6ee84b..01f8c428e 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/index.js +++ b/app/assets/javascripts/es6_browserified/time_tables/index.js @@ -30,7 +30,7 @@ var initialState = { tags: [], initial_tags: [], color: '', - calendar: {} + calendar: null }, pagination: { stateChanged: false, diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js index 97de90225..4f1e7a528 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js +++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js @@ -10,15 +10,15 @@ const metas = (state = {}, action) => { tags: action.json.tags, initial_tags: action.json.tags, color: action.json.color, - calendar: action.json.calendar ? action.json.calendar : {name : 'Aucun'} + calendar: action.json.calendar ? action.json.calendar : null }) case 'INCLUDE_DATE_IN_PERIOD': case 'EXCLUDE_DATE_FROM_PERIOD': case 'DELETE_PERIOD': case 'VALIDATE_PERIOD_FORM': - return _.assign({}, state, {calendar: {name: 'Aucun'}}) + return _.assign({}, state, {calendar: null}) case 'UPDATE_DAY_TYPES': - return _.assign({}, state, {day_types: action.dayTypes, calendar : {name: 'Aucun'}}) + return _.assign({}, state, {day_types: action.dayTypes, calendar : null}) case 'UPDATE_COMMENT': return _.assign({}, state, {comment: action.comment}) case 'UPDATE_COLOR': diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js index 314d481d4..c1e40b3bc 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js @@ -77,6 +77,17 @@ class CreateModal extends Component { /> </div> </div> + <div className='col-lg-6 col-md-6 col-sm-6 col-xs-12'> + <div className='form-group'> + <label className='control-label'>Numéro de train</label> + <input + type='text' + ref='published_journey_identifier' + className='form-control' + onKeyDown={(e) => actions.resetValidation(e.currentTarget)} + /> + </div> + </div> </div> </div> <div className='modal-footer'> diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js index d6c1179ba..82fed23d9 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js @@ -84,17 +84,6 @@ class TimetablesEditVehicleJourney extends Component { isFilter={false} /> </div> - <div> - <a - href='#' - title='Ajouter' - className='fa fa-plus' - onClick={(e) => { - e.preventDefault() - this.props.onAddSelectedTimetable() - }} - ></a> - </div> </div> </div> </div> @@ -140,7 +129,6 @@ TimetablesEditVehicleJourney.propTypes = { onTimetablesEditVehicleJourney: PropTypes.func.isRequired, onDeleteCalendarModal: PropTypes.func.isRequired, onSelect2Timetable: PropTypes.func.isRequired, - onAddSelectedTimetable: PropTypes.func.isRequired, filters: PropTypes.object.isRequired } diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/TimetablesEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/TimetablesEditVehicleJourney.js index 6d0096019..1cfff407c 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/TimetablesEditVehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/containers/tools/TimetablesEditVehicleJourney.js @@ -27,8 +27,6 @@ const mapDispatchToProps = (dispatch) => { }, onSelect2Timetable: (e) =>{ dispatch(actions.selectTTCalendarsModal(e.params.data)) - }, - onAddSelectedTimetable: () => { dispatch(actions.addSelectedTimetable()) } } diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js index 489446ab9..2a76ae43a 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js @@ -8,10 +8,10 @@ var actions = require("./actions") var enableBatching = require('./batch').enableBatching // logger, DO NOT REMOVE -var applyMiddleware = require('redux').applyMiddleware -var createLogger = require('redux-logger') -var thunkMiddleware = require('redux-thunk').default -var promise = require('redux-promise') +// var applyMiddleware = require('redux').applyMiddleware +// var createLogger = require('redux-logger') +// var thunkMiddleware = require('redux-thunk').default +// var promise = require('redux-promise') var selectedJP = [] @@ -85,12 +85,12 @@ if (window.jpOrigin){ initialState.filters.queryString = actions.encodeParams(params) } -const loggerMiddleware = createLogger() +// const loggerMiddleware = createLogger() let store = createStore( enableBatching(vehicleJourneysApp), - initialState, - applyMiddleware(thunkMiddleware, promise, loggerMiddleware) + initialState + // applyMiddleware(thunkMiddleware, promise, loggerMiddleware) ) render( diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js index e504c2531..9f071069d 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js @@ -88,9 +88,18 @@ const modal = (state = {}, action) => { newModalProps.timetables = timetablesModal return _.assign({}, state, {modalProps: newModalProps}) case 'CREATE_VEHICLEJOURNEY_MODAL': + let selectedJP = {} + if (window.jpOrigin){ + selectedJP = { + id: window.jpOrigin.id, + name: window.jpOrigin.name, + published_name: window.jpOrigin.published_name, + objectid: window.jpOrigin.objectid + } + } return { type: 'create', - modalProps: {}, + modalProps: window.jpOrigin ? {selectedJPModal: selectedJP} : {}, confirmModal: {} } case 'SELECT_JP_CREATE_MODAL': diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js index d153739ce..9dea63e07 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js @@ -36,6 +36,7 @@ const vehicleJourney= (state = {}, action, keep) => { company: action.selectedCompany, journey_pattern: action.selectedJourneyPattern, published_journey_name: action.data.published_journey_name.value, + published_journey_identifier: action.data.published_journey_identifier.value, objectid: '', footnotes: [], time_tables: [], diff --git a/app/controllers/autocomplete_calendars_controller.rb b/app/controllers/autocomplete_calendars_controller.rb index dbdd1a9fc..f85f529fc 100644 --- a/app/controllers/autocomplete_calendars_controller.rb +++ b/app/controllers/autocomplete_calendars_controller.rb @@ -2,6 +2,6 @@ class AutocompleteCalendarsController < ApplicationController respond_to :json, :only => [:autocomplete] def autocomplete - @calendars = Calendar.search(params[:q]).result.paginate(page: params[:page]) + @calendars = current_organisation.referentials.search(params[:q]).result.paginate(page: params[:page]) end end diff --git a/app/controllers/time_table_combinations_controller.rb b/app/controllers/time_table_combinations_controller.rb index bbb262247..32f1818b0 100644 --- a/app/controllers/time_table_combinations_controller.rb +++ b/app/controllers/time_table_combinations_controller.rb @@ -5,6 +5,7 @@ class TimeTableCombinationsController < ChouetteController def new @combination = TimeTableCombination.new(source_id: parent.id) + @combination.combined_type = 'time_table' end def create diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb index dcf8f3e5e..5c4552afb 100644 --- a/app/controllers/time_tables_controller.rb +++ b/app/controllers/time_tables_controller.rb @@ -43,7 +43,7 @@ class TimeTablesController < ChouetteController if calendar calendar.dates.each_with_index do |date, i| - @time_table.dates << Chouette::TimeTableDate.new(date: date, position: i) + @time_table.dates << Chouette::TimeTableDate.new(date: date, position: i, in_out: true) end calendar.date_ranges.each_with_index do |date_range, i| @time_table.periods << Chouette::TimeTablePeriod.new(period_start: date_range.begin, period_end: date_range.end, position: i) diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index eacc5c827..42879c6d5 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -64,7 +64,8 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord def state_update state update_attributes(self.class.state_permited_attributes(state)) - self.tag_list = state['tags'].collect{|t| t['name']}.join(', ') + self.tag_list = state['tags'].collect{|t| t['name']}.join(', ') + self.calendar_id = nil unless state['calendar'] days = state['day_types'].split(',') Date::DAYNAMES.map(&:underscore).each do |name| diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index d20a3d315..72d554e96 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -28,7 +28,8 @@ module Chouette has_and_belongs_to_many :time_tables, :class_name => 'Chouette::TimeTable', :foreign_key => "vehicle_journey_id", :association_foreign_key => "time_table_id" has_many :stop_points, -> { order("stop_points.position") }, :through => :vehicle_journey_at_stops - validate :increasing_times + validates :vehicle_journey_at_stops, + vehicle_journey_at_stops_are_in_increasing_time_order: true validates_presence_of :number before_validation :set_default_values @@ -156,14 +157,6 @@ module Chouette attrs end - def increasing_times - previous = nil - vehicle_journey_at_stops.select{|vjas| vjas.departure_time && vjas.arrival_time}.each do |vjas| - errors.add( :vehicle_journey_at_stops, 'time gap overflow') unless vjas.increasing_times_validate( previous) - previous = vjas - end - end - def missing_stops_in_relation_to_a_journey_pattern(selected_journey_pattern) selected_journey_pattern.stop_points - self.stop_points end diff --git a/app/models/chouette/vehicle_journey_at_stop.rb b/app/models/chouette/vehicle_journey_at_stop.rb index 7d6414f55..553531422 100644 --- a/app/models/chouette/vehicle_journey_at_stop.rb +++ b/app/models/chouette/vehicle_journey_at_stop.rb @@ -16,8 +16,11 @@ module Chouette # security against nil values return unless arrival_time && departure_time - if exceeds_gap?( arrival_time, departure_time) - errors.add(:arrival_time,I18n.t("activerecord.errors.models.vehicle_journey_at_stop.arrival_must_be_before_departure")) + if TimeDuration.exceeds_gap?(4.hours, arrival_time, departure_time) + errors.add( + :arrival_time, + I18n.t("activerecord.errors.models.vehicle_journey_at_stop.arrival_must_be_before_departure") + ) end end @@ -26,23 +29,5 @@ module Chouette @_destroy = false end - def increasing_times_validate( previous) - result = true - return result unless previous - - if exceeds_gap?( previous.departure_time, departure_time) - result = false - errors.add( :departure_time, 'departure time gap overflow') - end - if exceeds_gap?( previous.arrival_time, arrival_time) - result = false - errors.add( :arrival_time, 'arrival time gap overflow') - end - result - end - def exceeds_gap?(first, second) - (4 * 3600) < ((second - first) % (3600 * 24)) - end - end end diff --git a/app/models/chouette/vehicle_journey_at_stops_are_in_increasing_time_order_validator.rb b/app/models/chouette/vehicle_journey_at_stops_are_in_increasing_time_order_validator.rb new file mode 100644 index 000000000..95f0cdc3e --- /dev/null +++ b/app/models/chouette/vehicle_journey_at_stops_are_in_increasing_time_order_validator.rb @@ -0,0 +1,50 @@ +module Chouette + class VehicleJourneyAtStopsAreInIncreasingTimeOrderValidator < + ActiveModel::EachValidator + def validate_each(vehicle_journey, attribute, value) + previous_at_stop = nil + + vehicle_journey + .vehicle_journey_at_stops + .select { |vjas| vjas.departure_time && vjas.arrival_time } + .each do |vjas| + unless self.class.validate_at_stop_times_must_increase( + vjas, + previous_at_stop + ) + vehicle_journey.errors.add( + :vehicle_journey_at_stops, + 'time gap overflow' + ) + end + + previous_at_stop = vjas + end + end + + def self.validate_at_stop_times_must_increase(at_stop, previous_at_stop) + valid = true + return valid unless previous_at_stop + + if TimeDuration.exceeds_gap?( + 4.hours, + previous_at_stop.departure_time, + at_stop.departure_time + ) + valid = false + at_stop.errors.add(:departure_time, 'departure time gap overflow') + end + + if TimeDuration.exceeds_gap?( + 4.hours, + previous_at_stop.arrival_time, + at_stop.arrival_time + ) + valid = false + at_stop.errors.add(:arrival_time, 'arrival time gap overflow') + end + + valid + end + end +end diff --git a/app/models/time_table_combination.rb b/app/models/time_table_combination.rb index 9b5111014..0ca9e9253 100644 --- a/app/models/time_table_combination.rb +++ b/app/models/time_table_combination.rb @@ -31,7 +31,6 @@ class TimeTableCombination attributes.each do |name, value| send("#{name}=", value) end - self.combined_type = "time_table" end def persisted? @@ -40,7 +39,7 @@ class TimeTableCombination def target id = self.send("#{combined_type}_id") - klass = combined_type == 'calendar' ? Calendar : Chouette::TimeTable + klass = combined_type == "calendar" ? Calendar : Chouette::TimeTable target = klass.find id target = target.convert_to_time_table unless target.is_a? Chouette::TimeTable target diff --git a/app/models/user.rb b/app/models/user.rb index 1230a64a1..14dbeb4d7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,3 @@ -# coding: utf-8 class User < ActiveRecord::Base # Include default devise modules. Others available are: # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable, :database_authenticatable @@ -52,7 +51,7 @@ class User < ActiveRecord::Base raise 'Rails.application.config.stif_portail_api settings is not defined' unless conf conn = Faraday.new(:url => conf[:url]) do |c| - c.headers['Authorization'] = "Token token=\"#{conf[:key]}\"" + c.headers['Authorization'] = %{Token token="#{conf[:key]}"} c.adapter Faraday.default_adapter end @@ -73,6 +72,7 @@ class User < ActiveRecord::Base user.organisation = Organisation.sync_update el['organization_code'], el['organization_name'], el['functional_scope'] user.synced_at = Time.now user.permissions = el['permissions'].include?('boiv:edit-offer') ? @@edit_offer_permissions : [] + user.permissions += el['permissions'].grep( %r{^\Aboiv:read-offer\z} ) user.save puts "✓ user #{user.username} has been updated" unless Rails.env.test? end diff --git a/app/policies/acces_point_policy.rb b/app/policies/acces_point_policy.rb index 904b7a242..08af5981a 100644 --- a/app/policies/acces_point_policy.rb +++ b/app/policies/acces_point_policy.rb @@ -1,4 +1,4 @@ -class AccessPointPolicy < ApplicationPolicy +class AccessPointPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/access_link_policy.rb b/app/policies/access_link_policy.rb index 73b2d1baa..654739d06 100644 --- a/app/policies/access_link_policy.rb +++ b/app/policies/access_link_policy.rb @@ -1,4 +1,4 @@ -class AccessLinkPolicy < ApplicationPolicy +class AccessLinkPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb index 4a2d760fb..a863404ae 100644 --- a/app/policies/application_policy.rb +++ b/app/policies/application_policy.rb @@ -7,7 +7,10 @@ class ApplicationPolicy @record = record end - attr_accessor :referential + def archived? + !!referential.try(:archived_at) + end + def referential @referential ||= record_referential end @@ -48,6 +51,10 @@ class ApplicationPolicy Pundit.policy_scope!(user, record.class) end + def boiv_read_offer? + organisation_match? && user.has_permission?('boiv:read-offer') + end + def organisation_match? user.organisation == organisation end diff --git a/app/policies/boiv_policy.rb b/app/policies/boiv_policy.rb new file mode 100644 index 000000000..4270dc686 --- /dev/null +++ b/app/policies/boiv_policy.rb @@ -0,0 +1,15 @@ +require_relative 'chain' +class BoivPolicy < ApplicationPolicy + + def boiv_read_offer? + organisation_match? && user.has_permission?('boiv:read-offer') + end + + def index? + boiv_read_offer? + end + + def show? + boiv_read_offer? + end +end diff --git a/app/policies/calendar_policy.rb b/app/policies/calendar_policy.rb index 4248bccc7..9d6b09a9b 100644 --- a/app/policies/calendar_policy.rb +++ b/app/policies/calendar_policy.rb @@ -1,4 +1,4 @@ -class CalendarPolicy < ApplicationPolicy +class CalendarPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/chain.rb b/app/policies/chain.rb new file mode 100644 index 000000000..4bf96bd84 --- /dev/null +++ b/app/policies/chain.rb @@ -0,0 +1,57 @@ +module Policies + # Implements the `chain_policies` macro as follows + # + # chain_policies <method_chain>, policies: + # + # e.g. + # + # chain_policies [:archived?, :!], policies: %i{create? edit?} + # + # which would establish a precondition `not archived` for the `create?` and `edit?` + # method, it is semantically identical to instrumenting both methods + # as follows: + # + # def create? # or edit? + # archived?.! && <original code of method> + # end + module Chain + + # A local chain store implemented to avoid any possible side effect on client policies. + defined_chains = {} + + # Using `define_method` in order to close over `defined_chains` + # We need to store the chains because the methods they will apply to + # are not defined yet. + define_method :chain_policies do |*conditions, policies:| + policies.each do | meth_name | + # self represents the client Policy + defined_chains[[self, meth_name]] = conditions + end + end + # Intercept method definition and check if a policy_chain has been registered for it‥. + define_method :method_added do |meth_name, *args, &blk| + # Delete potentially registered criteria conditions to‥. + # (i) protect against endless recursion via (:merthod_added → :define_method → :method_added → ‥. + # (ii) get the condition + conditions = defined_chains.delete([self, meth_name]) + return unless conditions + + instrument_method(meth_name, conditions) + end + + private + + # Access to the closure is not necessary anymore, normal metaprogramming can take place :) + def instrument_method(meth_name, conditions) + orig_method = instance_method(meth_name) + # In case of warnings remove original method here, depends on Ruby Version, ok in 2.3.1 + define_method meth_name do |*a, &b| + # Method chain describing the chained policy precondition. + conditions.inject(self) do | result, msg | + result.send msg + end && + orig_method.bind(self).(*a, &b) + end + end + end +end diff --git a/app/policies/company_policy.rb b/app/policies/company_policy.rb index d28e9b515..95d607f3d 100644 --- a/app/policies/company_policy.rb +++ b/app/policies/company_policy.rb @@ -1,4 +1,4 @@ -class CompanyPolicy < ApplicationPolicy +class CompanyPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/connection_link_policy.rb b/app/policies/connection_link_policy.rb index abefd741c..21414efb9 100644 --- a/app/policies/connection_link_policy.rb +++ b/app/policies/connection_link_policy.rb @@ -1,4 +1,4 @@ -class ConnectionLinkPolicy < ApplicationPolicy +class ConnectionLinkPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/group_of_line_policy.rb b/app/policies/group_of_line_policy.rb index 5d42a23bd..86d522545 100644 --- a/app/policies/group_of_line_policy.rb +++ b/app/policies/group_of_line_policy.rb @@ -1,4 +1,4 @@ -class GroupOfLinePolicy < ApplicationPolicy +class GroupOfLinePolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/journey_pattern_policy.rb b/app/policies/journey_pattern_policy.rb index 56f32613c..9d13624c5 100644 --- a/app/policies/journey_pattern_policy.rb +++ b/app/policies/journey_pattern_policy.rb @@ -1,4 +1,4 @@ -class JourneyPatternPolicy < ApplicationPolicy +class JourneyPatternPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/line_policy.rb b/app/policies/line_policy.rb index 2ea1ecda9..c3e0051c8 100644 --- a/app/policies/line_policy.rb +++ b/app/policies/line_policy.rb @@ -1,4 +1,9 @@ -class LinePolicy < ApplicationPolicy +require_relative 'chain' +class LinePolicy < BoivPolicy + extend Policies::Chain + + chain_policies :archived?, :!, policies: %i{create_footnote? destroy_footnote? edit_footnote?} + class Scope < Scope def resolve scope @@ -22,7 +27,7 @@ class LinePolicy < ApplicationPolicy end def destroy_footnote? - user.has_permission?('routes.destroy') + user.has_permission?('footnotes.destroy') end def update_footnote? ; edit_footnote? end diff --git a/app/policies/network_policy.rb b/app/policies/network_policy.rb index 427eace93..4c1ea1090 100644 --- a/app/policies/network_policy.rb +++ b/app/policies/network_policy.rb @@ -1,4 +1,4 @@ -class NetworkPolicy < ApplicationPolicy +class NetworkPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/referential_policy.rb b/app/policies/referential_policy.rb index 8d366aff7..4a5e85ead 100644 --- a/app/policies/referential_policy.rb +++ b/app/policies/referential_policy.rb @@ -1,4 +1,4 @@ -class ReferentialPolicy < ApplicationPolicy +class ReferentialPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/route_policy.rb b/app/policies/route_policy.rb index c4d048f2a..dba3a27da 100644 --- a/app/policies/route_policy.rb +++ b/app/policies/route_policy.rb @@ -1,10 +1,13 @@ -class RoutePolicy < ApplicationPolicy +class RoutePolicy < BoivPolicy + extend Policies::Chain class Scope < Scope def resolve scope end end + chain_policies :archived?, :!, policies: %i{create? destroy? edit?} + def create? user.has_permission?('routes.create') # organisation match via referential is checked in the view end diff --git a/app/policies/routing_constraint_zone_policy.rb b/app/policies/routing_constraint_zone_policy.rb index 3126241f0..abba5639c 100644 --- a/app/policies/routing_constraint_zone_policy.rb +++ b/app/policies/routing_constraint_zone_policy.rb @@ -1,10 +1,13 @@ -class RoutingConstraintZonePolicy < ApplicationPolicy +class RoutingConstraintZonePolicy < BoivPolicy + extend Policies::Chain class Scope < Scope def resolve scope end end + chain_policies :archived?, :!, policies: %i{create? destroy? edit?} + def create? user.has_permission?('routing_constraint_zones.create') # organisation match via referential is checked in the view end diff --git a/app/policies/stop_area_policy.rb b/app/policies/stop_area_policy.rb index 4fa426ff6..79b7178ce 100644 --- a/app/policies/stop_area_policy.rb +++ b/app/policies/stop_area_policy.rb @@ -1,4 +1,4 @@ -class StopAreaPolicy < ApplicationPolicy +class StopAreaPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/policies/time_table_policy.rb b/app/policies/time_table_policy.rb index 82e4ca194..efab6ac00 100644 --- a/app/policies/time_table_policy.rb +++ b/app/policies/time_table_policy.rb @@ -1,24 +1,28 @@ -class TimeTablePolicy < ApplicationPolicy +class TimeTablePolicy < BoivPolicy + extend Policies::Chain + class Scope < Scope def resolve scope end end + chain_policies :archived?, :!, policies: %i{create? destroy? duplicate? edit?} + def create? - user.has_permission?('time_tables.create') # organisation match via referential is checked in the view + user.has_permission?('time_tables.create') # organisation match via referential is checked in the view end def edit? - organisation_match? && user.has_permission?('time_tables.edit') + organisation_match? && user.has_permission?('time_tables.edit') end def destroy? - organisation_match? && user.has_permission?('time_tables.destroy') + organisation_match? && user.has_permission?('time_tables.destroy') end def duplicate? - organisation_match? && create? + organisation_match? && create? end def update? ; edit? end diff --git a/app/policies/vehicle_journey_policy.rb b/app/policies/vehicle_journey_policy.rb index ae3680adf..de6dd7088 100644 --- a/app/policies/vehicle_journey_policy.rb +++ b/app/policies/vehicle_journey_policy.rb @@ -1,4 +1,4 @@ -class VehicleJourneyPolicy < ApplicationPolicy +class VehicleJourneyPolicy < BoivPolicy class Scope < Scope def resolve scope diff --git a/app/views/referential_lines/_filters.html.slim b/app/views/referential_lines/_filters.html.slim index aa355884b..93d449507 100644 --- a/app/views/referential_lines/_filters.html.slim +++ b/app/views/referential_lines/_filters.html.slim @@ -9,7 +9,7 @@ .ffg-row .form-group.togglable = f.label Chouette::Route.human_attribute_name(:wayback), required: false, class: 'control-label' - = f.input :wayback_eq_any, as: :checkboxes, class: 'form-control', collection: Chouette::Route.wayback.values, as: :check_boxes, label: false, required: false, wrapper_html: { class: 'checkbox_list'}, label_method: lambda{|l| ("<span>" + t("enumerize.route.wayback.#{l}") + "</span>").html_safe} + = f.input :wayback_eq_any, class: 'form-control', collection: Chouette::Route.wayback.values, as: :check_boxes, label: false, required: false, wrapper_html: { class: 'checkbox_list'}, label_method: lambda{|l| ("<span>" + t("enumerize.route.wayback.#{l}") + "</span>").html_safe} .actions = link_to 'Effacer', referential_line_path(@referential, @line), class: 'btn btn-link' diff --git a/app/views/time_table_combinations/_form.html.slim b/app/views/time_table_combinations/_form.html.slim index 3716f6713..d8bebf0c4 100644 --- a/app/views/time_table_combinations/_form.html.slim +++ b/app/views/time_table_combinations/_form.html.slim @@ -8,7 +8,7 @@ = f.input :combined_type, as: :boolean, checked_value: 'time_table', unchecked_value: 'calendar', required: false, label: content_tag(:span, t("time_table_combinations.combined_type.#{@combination.combined_type}"), class: 'switch-label', data: { checkedValue: 'Calendriers', uncheckedValue: 'Modèles de calendriers' }), wrapper_html: { class: 'col-sm-8' } = f.input :time_table_id, as: :select, input_html: {class: 'tt_combination_target', style: "width: 100%", data: { 'select2-ajax': 'true', term: 'comment_or_objectid_cont', url: referential_autocomplete_time_tables_path(@referential, format: :json)}}, wrapper_html: {class: @combination.combined_type != 'time_table' ? 'hidden' : ''} - + = f.input :calendar_id, as: :select, input_html: { class: 'tt_combination_target', style: "width: 100%", data: { 'select2-ajax': 'true', term: 'name_cont', url: autocomplete_calendars_path}}, wrapper_html: {class: @combination.combined_type != 'calendar' ? 'hidden' : ''} .separator diff --git a/app/views/time_tables/_form.html.slim b/app/views/time_tables/_form.html.slim index 196682823..d4e1d838e 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, as: :select, collection: current_organisation.calendars + = form.input :calendar_id, as: :select, collection: current_organisation.calendars - if @time_table.created_from = form.input :created_from, disabled: true, input_html: { value: @time_table.created_from.comment } diff --git a/lib/time_duration.rb b/lib/time_duration.rb new file mode 100644 index 000000000..12419fdc8 --- /dev/null +++ b/lib/time_duration.rb @@ -0,0 +1,20 @@ +module TimeDuration + # `earlier` and `later` are times. Get the duration between those times and + # check whether it's longer than the given `duration`. + # + # Example: + # TimeDuration.exceeds_gap?( + # 4.hours, + # Time.now, + # Time.now + 2.hours + # ) + def self.exceeds_gap?(duration, earlier, later) + duration < self.duration_without_24_hour_cycles(later - earlier) + end + + private + + def self.duration_without_24_hour_cycles(duration) + duration % 24.hours + end +end diff --git a/spec/features/line_footnotes_spec.rb b/spec/features/line_footnotes_spec.rb index 4d77cba41..6a359ad50 100644 --- a/spec/features/line_footnotes_spec.rb +++ b/spec/features/line_footnotes_spec.rb @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -require 'spec_helper' - describe 'Line Footnotes', type: :feature do login_user diff --git a/spec/features/time_tables_spec.rb b/spec/features/time_tables_spec.rb index 06ae9bac3..58a1dc98f 100644 --- a/spec/features/time_tables_spec.rb +++ b/spec/features/time_tables_spec.rb @@ -67,6 +67,10 @@ describe "TimeTables", :type => :feature do expect(page).to have_content(time_tables.first.comment) end + it 'should not show actualize link on time_tabl without calendar' do + expect(page).not_to have_content(I18n.t('time_tables.actions.actualize')) + end + # context 'user has permission to create time tables' do # it 'shows a create link for time tables' do # expect(page).to have_content(I18n.t('time_tables.actions.new')) diff --git a/spec/javascripts/time_table/reducers/metas_spec.js b/spec/javascripts/time_table/reducers/metas_spec.js index 79dbe1ea3..5ec7a0034 100644 --- a/spec/javascripts/time_table/reducers/metas_spec.js +++ b/spec/javascripts/time_table/reducers/metas_spec.js @@ -30,7 +30,7 @@ describe('metas reducer', () => { type: 'UPDATE_DAY_TYPES', dayTypes: arr }) - ).toEqual(Object.assign({}, state, {day_types: arr, calendar: {name: 'Aucun'}})) + ).toEqual(Object.assign({}, state, {day_types: arr, calendar: null})) }) it('should handle UPDATE_COMMENT', () => { diff --git a/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js b/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js index f805852c7..662c3d82f 100644 --- a/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js +++ b/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js @@ -88,7 +88,8 @@ describe('vehicleJourneys reducer', () => { dummy: true }] let fakeData = { - published_journey_name: {value: 'test'} + published_journey_name: {value: 'test'}, + published_journey_identifier: {value : ''} } let fakeSelectedJourneyPattern = {id: "1"} let fakeSelectedCompany = {name: "ALBATRANS"} @@ -104,6 +105,7 @@ describe('vehicleJourneys reducer', () => { journey_pattern: fakeSelectedJourneyPattern, company: fakeSelectedCompany, published_journey_name: 'test', + published_journey_identifier: '', objectid: '', footnotes: [], time_tables: [], diff --git a/spec/lib/time_duration_spec.rb b/spec/lib/time_duration_spec.rb new file mode 100644 index 000000000..1cba1f6d5 --- /dev/null +++ b/spec/lib/time_duration_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe TimeDuration do + describe ".exceeds_gap?" do + context "when duration is 4.hours" do + it "should return false if gap < 1.hour" do + t1 = Time.now + t2 = Time.now + 3.minutes + expect(TimeDuration.exceeds_gap?(4.hours, t1, t2)).to be_falsey + end + + it "should return true if gap > 4.hour" do + t1 = Time.now + t2 = Time.now + (4.hours + 1.minutes) + expect(TimeDuration.exceeds_gap?(4.hours, t1, t2)).to be_truthy + end + + it "returns true when `earlier` is later than `later`" do + earlier = Time.new(2000, 1, 1, 11, 0, 0, 0) + later = Time.new(2000, 1, 1, 12, 0, 0, 0) + + expect(TimeDuration.exceeds_gap?(4.hours, later, earlier)).to be true + end + + it "returns false when `earlier` == `later`" do + earlier = Time.new(2000, 1, 1, 1, 0, 0, 0) + later = earlier + + expect(TimeDuration.exceeds_gap?(4.hours, later, earlier)).to be false + end + end + end +end diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb index 505ca12be..3d45bd346 100644 --- a/spec/models/chouette/time_table_spec.rb +++ b/spec/models/chouette/time_table_spec.rb @@ -51,6 +51,15 @@ describe Chouette::TimeTable, :type => :model do }.to change {subject.periods.count}.by(-1) end + it 'should update caldendar association' do + subject.calendar = create(:calendar) + subject.save + state['calendar'] = nil + + subject.state_update state + expect(subject.reload.calendar).to eq(nil) + end + it 'should update color' do state['color'] = '#FFA070' subject.state_update state diff --git a/spec/models/chouette/vehicle_journey_at_stop_spec.rb b/spec/models/chouette/vehicle_journey_at_stop_spec.rb deleted file mode 100644 index fbd544a28..000000000 --- a/spec/models/chouette/vehicle_journey_at_stop_spec.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'spec_helper' -require 'pp' - -describe Chouette::VehicleJourneyAtStop, :type => :model do - let!(:vehicle_journey) { create(:vehicle_journey_odd)} - subject { vehicle_journey.vehicle_journey_at_stops.first } - - describe "#exceeds_gap?" do - it "should return false if gap < 1.hour" do - t1 = Time.now - t2 = Time.now + 3.minutes - expect(subject.exceeds_gap?(t1, t2)).to be_falsey - end - it "should return true if gap > 4.hour" do - t1 = Time.now - t2 = Time.now + (4.hours + 1.minutes) - expect(subject.exceeds_gap?(t1, t2)).to be_truthy - end - end - - describe "#increasing_times_validate" do - let(:vjas1){ vehicle_journey.vehicle_journey_at_stops[0]} - let(:vjas2){ vehicle_journey.vehicle_journey_at_stops[1]} - context "when vjas#arrival_time exceeds gap" do - it "should add errors on arrival_time" do - vjas1.arrival_time = vjas2.arrival_time - 5.hour - expect(vjas2.increasing_times_validate(vjas1)).to be_falsey - expect(vjas2.errors).not_to be_empty - expect(vjas2.errors[:arrival_time]).not_to be_blank - end - end - context "when vjas#departure_time exceeds gap" do - it "should add errors on departure_time" do - vjas1.departure_time = vjas2.departure_time - 5.hour - expect(vjas2.increasing_times_validate(vjas1)).to be_falsey - expect(vjas2.errors).not_to be_empty - expect(vjas2.errors[:departure_time]).not_to be_blank - end - end - context "when vjas does'nt exceed gap" do - it "should not add errors" do - expect(vjas2.increasing_times_validate(vjas1)).to be_truthy - expect(vjas2.errors).to be_empty - end - end - end -end diff --git a/spec/models/chouette/vehicle_journey_at_stops_are_in_increasing_time_order_validator_spec.rb b/spec/models/chouette/vehicle_journey_at_stops_are_in_increasing_time_order_validator_spec.rb new file mode 100644 index 000000000..c30e0dbd8 --- /dev/null +++ b/spec/models/chouette/vehicle_journey_at_stops_are_in_increasing_time_order_validator_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe Chouette::VehicleJourneyAtStopsAreInIncreasingTimeOrderValidator do + subject { create(:vehicle_journey_odd) } + + describe "#validate" do + before(:each) do + subject.vehicle_journey_at_stops[0].departure_time = + subject.vehicle_journey_at_stops[1].departure_time - 5.hour + subject.vehicle_journey_at_stops[0].arrival_time = + subject.vehicle_journey_at_stops[0].departure_time + subject.vehicle_journey_at_stops[1].arrival_time = + subject.vehicle_journey_at_stops[1].departure_time + end + + it "should make instance invalid if departure time exceeds gap" do + subject.validate + + expect( + subject.vehicle_journey_at_stops[1].errors[:departure_time] + ).not_to be_blank + expect(subject).not_to be_valid + end + end + + describe ".validate_at_stop_times_must_increase" do + let!(:vehicle_journey) { create(:vehicle_journey_odd) } + subject { vehicle_journey.vehicle_journey_at_stops.first } + + let(:vjas1) { vehicle_journey.vehicle_journey_at_stops[0] } + let(:vjas2) { vehicle_journey.vehicle_journey_at_stops[1] } + + context "when vjas#arrival_time exceeds gap" do + it "should add errors on arrival_time" do + vjas1.arrival_time = vjas2.arrival_time - 5.hour + expect( + Chouette::VehicleJourneyAtStopsAreInIncreasingTimeOrderValidator + .validate_at_stop_times_must_increase(vjas2, vjas1) + ).to be_falsey + expect(vjas2.errors).not_to be_empty + expect(vjas2.errors[:arrival_time]).not_to be_blank + end + end + + context "when vjas#departure_time exceeds gap" do + it "should add errors on departure_time" do + vjas1.departure_time = vjas2.departure_time - 5.hour + expect( + Chouette::VehicleJourneyAtStopsAreInIncreasingTimeOrderValidator + .validate_at_stop_times_must_increase(vjas2, vjas1) + ).to be_falsey + expect(vjas2.errors).not_to be_empty + expect(vjas2.errors[:departure_time]).not_to be_blank + end + end + + context "when vjas doesn't exceed gap" do + it "should not add errors" do + expect( + Chouette::VehicleJourneyAtStopsAreInIncreasingTimeOrderValidator + .validate_at_stop_times_must_increase(vjas2, vjas1) + ).to be_truthy + expect(vjas2.errors).to be_empty + end + end + end +end diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index ed76c278c..4a108d7c0 100644 --- a/spec/models/chouette/vehicle_journey_spec.rb +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -466,18 +466,6 @@ describe Chouette::VehicleJourney, :type => :model do end context "when following departure times exceeds gap" do - describe "#increasing_times" do - before(:each) do - subject.vehicle_journey_at_stops[0].departure_time = subject.vehicle_journey_at_stops[1].departure_time - 5.hour - subject.vehicle_journey_at_stops[0].arrival_time = subject.vehicle_journey_at_stops[0].departure_time - subject.vehicle_journey_at_stops[1].arrival_time = subject.vehicle_journey_at_stops[1].departure_time - end - it "should make instance invalid" do - subject.increasing_times - expect(subject.vehicle_journey_at_stops[1].errors[:departure_time]).not_to be_blank - expect(subject).not_to be_valid - end - end describe "#update_attributes" do let!(:params){ {"vehicle_journey_at_stops_attributes" => { "0"=>{"id" => subject.vehicle_journey_at_stops[0].id ,"arrival_time" => 1.minutes.ago,"departure_time" => 1.minutes.ago}, diff --git a/spec/policies/application_policy_spec.rb b/spec/policies/application_policy_spec.rb index d7e8e5e27..a7234461e 100644 --- a/spec/policies/application_policy_spec.rb +++ b/spec/policies/application_policy_spec.rb @@ -1,11 +1,6 @@ RSpec.describe ApplicationPolicy, type: :policy do - subject { described_class } - permissions :organisation_match? do - let( :user_context ) { create_user_context(user: user, referential: referential) } - let( :referentail ) { create :referential } - let( :user ) { create :user } it "denies a user with a different organisation" do expect_it.not_to permit(user_context, referential) @@ -16,4 +11,5 @@ RSpec.describe ApplicationPolicy, type: :policy do expect_it.to permit(user_context, referential) end end + end diff --git a/spec/policies/boiv_policy_spec.rb b/spec/policies/boiv_policy_spec.rb new file mode 100644 index 000000000..bf09cdcd9 --- /dev/null +++ b/spec/policies/boiv_policy_spec.rb @@ -0,0 +1,15 @@ +RSpec.describe BoivPolicy, type: :policy do + + permissions :index? do + it_behaves_like 'permitted policy and same organisation', 'boiv:read-offer' + end + + permissions :boiv_read_offer? do + it_behaves_like 'permitted policy and same organisation', 'boiv:read-offer' + end + + permissions :show? do + it_behaves_like 'permitted policy and same organisation', 'boiv:read-offer' + end + +end diff --git a/spec/policies/line_policy_spec.rb b/spec/policies/line_policy_spec.rb new file mode 100644 index 000000000..ead5918aa --- /dev/null +++ b/spec/policies/line_policy_spec.rb @@ -0,0 +1,18 @@ +RSpec.describe LinePolicy, type: :policy do + + %w{create destroy edit}.each do | permission | + footnote_permission = "#{permission}_footnote" + permissions "#{footnote_permission}?".to_sym do + it_behaves_like 'permitted policy', "footnotes.#{permission}", archived: true + end + end + + permissions :new_footnote? do + it_behaves_like 'permitted policy', 'footnotes.create', archived: true + end + + permissions :update_footnote? do + it_behaves_like 'permitted policy', 'footnotes.edit', archived: true + end + +end diff --git a/spec/policies/route_policy_spec.rb b/spec/policies/route_policy_spec.rb new file mode 100644 index 000000000..baf14c9fc --- /dev/null +++ b/spec/policies/route_policy_spec.rb @@ -0,0 +1,22 @@ +RSpec.describe RoutePolicy, type: :policy do + + permissions :create? do + it_behaves_like 'permitted policy', 'routes.create', archived: true + end + + permissions :destroy? do + it_behaves_like 'permitted policy and same organisation', 'routes.destroy', archived: true + end + + permissions :edit? do + it_behaves_like 'permitted policy and same organisation', 'routes.edit', archived: true + end + + permissions :new? do + it_behaves_like 'permitted policy', 'routes.create', archived: true + end + + permissions :update? do + it_behaves_like 'permitted policy and same organisation', 'routes.edit', archived: true + end +end diff --git a/spec/policies/routing_constraint_zone_policy_spec.rb b/spec/policies/routing_constraint_zone_policy_spec.rb new file mode 100644 index 000000000..4b0f2cafe --- /dev/null +++ b/spec/policies/routing_constraint_zone_policy_spec.rb @@ -0,0 +1,22 @@ +RSpec.describe RoutingConstraintZonePolicy, type: :policy do + + permissions :create? do + it_behaves_like 'permitted policy', 'routing_constraint_zones.create', archived: true + end + + permissions :destroy? do + it_behaves_like 'permitted policy and same organisation', 'routing_constraint_zones.destroy', archived: true + end + + permissions :edit? do + it_behaves_like 'permitted policy and same organisation', 'routing_constraint_zones.edit', archived: true + end + + permissions :new? do + it_behaves_like 'permitted policy', 'routing_constraint_zones.create', archived: true + end + + permissions :update? do + it_behaves_like 'permitted policy and same organisation', 'routing_constraint_zones.edit', archived: true + end +end diff --git a/spec/policies/time_table_policy_spec.rb b/spec/policies/time_table_policy_spec.rb index 63bd316e4..1283a9fcf 100644 --- a/spec/policies/time_table_policy_spec.rb +++ b/spec/policies/time_table_policy_spec.rb @@ -1,26 +1,18 @@ RSpec.describe TimeTablePolicy, type: :policy do permissions :duplicate? do - context "user of a different organisation" do - it "is denied" do - expect_it.not_to permit(user_context, referential) - end - it "even if she has the time_tables.create permission" do - add_permissions 'time_tables.create', for_user: user - expect_it.not_to permit(user_context, referential) - end - end - context "user of the same organisation" do - before do - user.update_attribute :organisation, referential.organisation - end - it "is denied" do - expect_it.not_to permit(user_context, referential) - end - it "unless she has the time_tables.create permission" do - add_permissions 'time_tables.create', for_user: user - expect_it.to permit(user_context, referential) - end + it_behaves_like 'permitted policy and same organisation', 'time_tables.create', archived: true + end + + %w{destroy edit}.each do | permission | + permissions "#{permission}?".to_sym do + it_behaves_like 'permitted policy and same organisation', "time_tables.#{permission}", archived: true end end + + permissions :create? do + it_behaves_like 'permitted policy', 'time_tables.create', archived: true + end + + end diff --git a/spec/support/pundit.rb b/spec/support/pundit.rb deleted file mode 100644 index d818ce754..000000000 --- a/spec/support/pundit.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'pundit/rspec' - -module Support - module ApplicationPolicy - def add_permissions(*permissions, for_user:) - for_user.permissions ||= [] - for_user.permissions += permissions.flatten - end - - def create_user_context(user:, referential:) - OpenStruct.new(user: user, context: {referential: referential}) - end - end - - module ApplicationPolicyMacros - def self.extended into - into.module_eval do - subject { described_class } - let( :user_context ) { create_user_context(user: user, referential: referential) } - let( :referentail ) { create :referential } - let( :user ) { create :user } - end - end - - end -end - -RSpec.configure do | c | - c.include Support::ApplicationPolicy, type: :policy - c.extend Support::ApplicationPolicyMacros, type: :policy -end diff --git a/spec/support/pundit/policies.rb b/spec/support/pundit/policies.rb new file mode 100644 index 000000000..637a2a528 --- /dev/null +++ b/spec/support/pundit/policies.rb @@ -0,0 +1,37 @@ +require 'pundit/rspec' + +module Support + module Pundit + module Policies + def add_permissions(*permissions, for_user:) + for_user.permissions ||= [] + for_user.permissions += permissions.flatten + end + + def create_user_context(user:, referential:) + OpenStruct.new(user: user, context: {referential: referential}) + end + + def add_permissions(*permissions, for_user:) + for_user.permissions ||= [] + for_user.permissions += permissions.flatten + end + end + + module PoliciesMacros + def self.extended into + into.module_eval do + subject { described_class } + let( :user_context ) { create_user_context(user: user, referential: referential) } + let( :referentail ) { create :referential } + let( :user ) { create :user } + end + end + end + end +end + +RSpec.configure do | c | + c.include Support::Pundit::Policies, type: :policy + c.extend Support::Pundit::PoliciesMacros, type: :policy +end diff --git a/spec/support/pundit/pundit_view_policy.rb b/spec/support/pundit/pundit_view_policy.rb new file mode 100644 index 000000000..b8434cac0 --- /dev/null +++ b/spec/support/pundit/pundit_view_policy.rb @@ -0,0 +1,22 @@ +module Pundit + module PunditViewPolicy + extend ActiveSupport::Concern + + included do + before do + controller.singleton_class.class_eval do + def policy(instance) + Class.new do + def method_missing(*args, &block); true; end + end.new + end + helper_method :policy + end + end + end + end +end + +RSpec.configure do |config| + config.include Pundit::PunditViewPolicy, type: :view +end diff --git a/spec/support/pundit/shared_examples.rb b/spec/support/pundit/shared_examples.rb new file mode 100644 index 000000000..4d14c46da --- /dev/null +++ b/spec/support/pundit/shared_examples.rb @@ -0,0 +1,60 @@ +RSpec.shared_examples 'permitted policy and same organisation' do + | permission, archived: false| + + context 'permission absent → ' do + it "denies a user with a different organisation" do + expect_it.not_to permit(user_context, referential) + end + it 'and also a user with the same organisation' do + user.update_attribute :organisation, referential.organisation + expect_it.not_to permit(user_context, referential) + end + end + + context 'permission present → ' do + before do + add_permissions(permission, for_user: user) + end + + it 'denies a user with a different organisation' do + expect_it.not_to permit(user_context, referential) + end + + it 'but allows it for a user with the same organisation' do + user.update_attribute :organisation, referential.organisation + expect_it.to permit(user_context, referential) + end + + if archived + it 'removes the permission for archived referentials' do + user.update_attribute :organisation, referential.organisation + referential.update_attribute :archived_at, 42.seconds.ago + expect_it.not_to permit(user_context, referential) + end + end + end +end + +RSpec.shared_examples 'permitted policy' do + | permission, archived: false| + context 'permission absent → ' do + it "denies a user with a different organisation" do + expect_it.not_to permit(user_context, referential) + end + end + context 'permission present → ' do + before do + add_permissions(permission, for_user: user) + end + it 'allows a user with a different organisation' do + expect_it.to permit(user_context, referential) + end + + if archived + it 'removes the permission for archived referentials' do + referential.update_attribute :archived_at, 42.seconds.ago + expect_it.not_to permit(user_context, referential) + end + end + end +end diff --git a/spec/support/pundit_view_policy.rb b/spec/support/pundit_view_policy.rb deleted file mode 100644 index 2945b9aac..000000000 --- a/spec/support/pundit_view_policy.rb +++ /dev/null @@ -1,20 +0,0 @@ -module PunditViewPolicy - extend ActiveSupport::Concern - - included do - before do - controller.singleton_class.class_eval do - def policy(instance) - Class.new do - def method_missing(*args, &block); true; end - end.new - end - helper_method :policy - end - end - end -end - -RSpec.configure do |config| - config.include PunditViewPolicy, type: :view -end |
