diff options
| author | Robert | 2017-05-23 23:23:34 +0200 |
|---|---|---|
| committer | Robert | 2017-05-24 07:13:21 +0200 |
| commit | a47fe543ca8213d4b32d51c209b42e8f1a762776 (patch) | |
| tree | 30a5006b516b0ed188d97d10b2eaa8f620dc9335 /app/policies | |
| parent | 97467efb3cfb8893f842988ece7f95e7f5b3b9b8 (diff) | |
| download | chouette-core-a47fe543ca8213d4b32d51c209b42e8f1a762776.tar.bz2 | |
Refs: #3446; metaprogrammed policy criteria
Diffstat (limited to 'app/policies')
| -rw-r--r-- | app/policies/boiv_policy.rb | 1 | ||||
| -rw-r--r-- | app/policies/chain.rb | 57 | ||||
| -rw-r--r-- | app/policies/time_table_policy.rb | 9 |
3 files changed, 62 insertions, 5 deletions
diff --git a/app/policies/boiv_policy.rb b/app/policies/boiv_policy.rb index e29a2e6de..7f7534813 100644 --- a/app/policies/boiv_policy.rb +++ b/app/policies/boiv_policy.rb @@ -11,5 +11,4 @@ class BoivPolicy < ApplicationPolicy def show? boiv_read_offer? end - end 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/time_table_policy.rb b/app/policies/time_table_policy.rb index 059edb8c6..4b2bf0cd9 100644 --- a/app/policies/time_table_policy.rb +++ b/app/policies/time_table_policy.rb @@ -1,27 +1,28 @@ +require_relative 'chain' 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? - !archived? && user.has_permission?('time_tables.create') # organisation match via referential is checked in the view end def edit? - !archived? && organisation_match? && user.has_permission?('time_tables.edit') end def destroy? - !archived? && organisation_match? && user.has_permission?('time_tables.destroy') end def duplicate? - !archived? && organisation_match? && create? end |
