lib/credentials/rule.rb in credentials-2.2.3 vs lib/credentials/rule.rb in credentials-2.3.0

- old
+ new

@@ -31,11 +31,11 @@ # There are two exceptions to this behaviour. Firstly, # if the rule specifies an array, then the argument will # match any element of that array: # class User # credentials do |user| - # user.can :fight, [ :shatner, :gandhi ] + # user.can [ :punch, :fight ], [ :shatner, :gandhi ] # end # end # # user.can? :fight, :gandhi # => true # @@ -45,20 +45,24 @@ # credentials do |user| # user.can :fight, :self # SPOILER ALERT # end # end def match?(*args) - return false unless arity == args.length + values = args.last.is_a?(Hash) ? args.pop : {} + return false unless arity == args.length + parameters.zip(args).each do |expected, actual| case expected when :self then return false unless actual == args.first when Array then return false unless expected.any? { |item| (item === actual) || (item == :self && actual == args.first) } else return false unless expected === actual end end + result = true + result = result && (options.keys & Credentials::Prepositions).inject(true) { |memo, key| memo && evaluate_preposition(args.first, options[key], values[key]) } result = result && evaluate_condition(options[:if], :|, *args) unless options[:if].nil? result = result && !evaluate_condition(options[:unless], :&, *args) unless options[:unless].nil? result end @@ -81,8 +85,35 @@ !!condition.call(receiver, *args) else raise ArgumentError, "invalid :if or :unless option (expected Symbol or Proc, or array thereof; got #{condition.class})" end end + end + + def evaluate_preposition(object, expected, actual) + return true if expected.nil? + return false if actual.nil? + return true if expected === actual + + single = expected.to_s + plural = single.respond_to?(:pluralize) ? single.pluralize : single + "s" + + lclass, rclass = [ object.class.name, actual.class.name ].map do |s| + s.gsub(/^.*::/, ''). + gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). + gsub(/([a-z\d])([A-Z])/,'\1_\2'). + tr("-", "_"). + downcase + end + + if object.respond_to?(:id) && actual.respond_to?(:"#{lclass}_id") + return true if actual.send(:"#{lclass}_id") == object.id + end + + if actual.respond_to?(:id) && object.respond_to?(:"#{rclass}_id") + return true if object.send(:"#{rclass}_id") == actual.id + end + + (object.respond_to?(single) && (object.send(single) == actual)) || (object.respond_to?(plural) && (object.send(plural).include?(actual))) end end end \ No newline at end of file