class Protector::DSL::Meta::Box

Single DSL evaluation result

Attributes

access[RW]
adapter[RW]
destroyable[RW]

Public Class Methods

new(adapter, model, fields, subject, entry, blocks) click to toggle source

@param model [Class] The class of protected entity @param fields [Array<String>] All the fields the model has @param subject [Object] Restriction subject @param entry [Object] An instance of the model @param blocks [Array<Proc>] An array of ‘protect` blocks

# File lib/protector/dsl.rb, line 15
def initialize(adapter, model, fields, subject, entry, blocks)
  @adapter     = adapter
  @model       = model
  @fields      = fields
  @access      = {}
  @scope_procs = []
  @destroyable = false

  Protector.insecurely do
    blocks.each do |b|
      case b.arity
      when 2
        instance_exec(subject, entry, &b)
      when 1
        instance_exec(subject, &b)
      else
        instance_exec(&b)
      end
    end
  end
end

Public Instance Methods

can(action, *fields) click to toggle source

Enables action for given fields.

Built-in possible actions are: ‘:read`, `:update`, `:create`. You can pass any other actions you want to use with {#can?} afterwards.

**The method enables action for every field if ‘fields` splat is empty.** Use {#cannot} to exclude some of them afterwards.

The list of fields can be given as a Hash. In this form you can pass ‘Range` or `Proc` as a value. First will make Protector check against value inclusion. The latter will make it evaluate given lambda (which is supposed to return true or false determining if the value should validate or not).

@param action [Symbol] Action to allow @param fields [String, Hash, Array] Splat of fields to allow action with

@see can?

@example

protect do
  can :read               # Can read any field
  can :read, 'f1'         # Can read `f1` field
  can :read, %w(f2 f3)    # Can read `f2`, `f3` fields
  can :update, f1: 1..2   # Can update f1 field with values between 1 and 2

  # Can create f1 field with value equal to 'olo'
  can :create, f1: lambda{|x| x == 'olo'}
end
# File lib/protector/dsl.rb, line 105
def can(action, *fields)
  action = deprecate_actions(action)

  return @destroyable = true if action == :destroy

  @access[action] = {} unless @access[action]

  if fields.length == 0
    @fields.each { |f| @access[action][f.to_s] = nil }
  else
    fields.each do |a|
      if a.is_a?(Array)
        a.each { |f| @access[action][f.to_s] = nil }
      elsif a.is_a?(Hash)
        @access[action].merge!(a.stringify_keys)
      else
        @access[action][a.to_s] = nil
      end
    end
  end
end
can?(action, field=false) click to toggle source

Check whether you can perform custom action for given fields (or generally if no ‘field` given)

@param [Symbol] action Action to check against @param [String] field Field to check against

# File lib/protector/dsl.rb, line 192
def can?(action, field=false)
  return destroyable? if action == :destroy

  return false unless @access[action]
  return !@access[action].empty? unless field

  @access[action].key?(field.to_s)
end
cannot(action, *fields) click to toggle source

Disables action for given fields.

Works similar (but oppositely) to {#can}.

@param action [Symbol] Action to disallow @param fields [String, Hash, Array] Splat of fields to disallow action with

@see can @see can?

# File lib/protector/dsl.rb, line 136
def cannot(action, *fields)
  action = deprecate_actions(action)

  return @destroyable = false if action == :destroy

  return unless @access[action]

  if fields.length == 0
    @access.delete(action)
  else
    fields.each do |a|
      if a.is_a?(Array)
        a.each { |f| @access[action].delete(f.to_s) }
      else
        @access[action].delete(a.to_s)
      end
    end

    @access.delete(action) if @access[action].empty?
  end
end
cannot?(*args) click to toggle source
# File lib/protector/dsl.rb, line 201
def cannot?(*args)
  !can?(*args)
end
creatable?(fields=false) click to toggle source

Checks whether you can create a model with given field in context of current subject

# File lib/protector/dsl.rb, line 166
def creatable?(fields=false)
  modifiable? :create, fields
end
destroyable?() click to toggle source

Checks whether you can destroy a model in context of current subject

# File lib/protector/dsl.rb, line 184
def destroyable?
  @destroyable
end
eval_scope_procs(instance) click to toggle source
# File lib/protector/dsl.rb, line 71
def eval_scope_procs(instance)
  scope_procs.reduce(instance) do |relation, scope_proc|
    relation.instance_eval(&scope_proc)
  end
end
first_uncreatable_field(fields) click to toggle source
# File lib/protector/dsl.rb, line 170
def first_uncreatable_field(fields)
  first_unmodifiable_field :create, fields
end
first_unupdatable_field(fields) click to toggle source
# File lib/protector/dsl.rb, line 179
def first_unupdatable_field(fields)
  first_unmodifiable_field :update, fields
end
readable?(field) click to toggle source

Checks whether given field of a model is readable in context of current subject

# File lib/protector/dsl.rb, line 161
def readable?(field)
  @access[:read] && @access[:read].key?(field.to_s)
end
relation() click to toggle source
# File lib/protector/dsl.rb, line 65
def relation
  return false unless scoped?

  @relation ||= eval_scope_procs @model
end
scope(&block) click to toggle source

Activates the scope that selections will be filtered with

@yield Calls given model methods before the selection

@example

protect do
  # You can select nothing!
  scope { none }
end
# File lib/protector/dsl.rb, line 55
def scope(&block)
  @scope_procs << block
  @relation = false
end
scope_procs() click to toggle source
# File lib/protector/dsl.rb, line 60
def scope_procs
  return [@adapter.null_proc] if @scope_procs.empty? && Protector.config.paranoid?
  @scope_procs
end
scoped?() click to toggle source

Checks whether protection with given subject has the selection scope defined

# File lib/protector/dsl.rb, line 39
def scoped?
  Protector.config.paranoid? || @scope_procs.length > 0
end
updatable?(fields=false) click to toggle source

Checks whether you can update a model with given field in context of current subject

# File lib/protector/dsl.rb, line 175
def updatable?(fields=false)
  modifiable? :update, fields
end

Private Instance Methods

deprecate_actions(action) click to toggle source
# File lib/protector/dsl.rb, line 233
def deprecate_actions(action)
  if action == :view
    ActiveSupport::Deprecation.warn ":view rule has been deprecated and replaced with :read! "+
      "Starting from version 1.0 :view will be treated as a custom rule."

    :read
  else
    action
  end
end
first_unmodifiable_field(part, fields) click to toggle source
# File lib/protector/dsl.rb, line 207
def first_unmodifiable_field(part, fields)
  return (fields.keys.first || '-') unless @access[part]

  diff = fields.keys - @access[part].keys
  return diff.first if diff.length > 0

  fields.each do |k, v|
    case x = @access[part][k]
    when Enumerable
      return k unless x.include?(v)
    when Proc
      return k unless Protector.insecurely{ x.call(v) }
    else
      return k if !x.nil? && x != v
    end
  end

  false
end
modifiable?(part, fields=false) click to toggle source
# File lib/protector/dsl.rb, line 227
def modifiable?(part, fields=false)
  return false unless @access[part]
  return false if fields && first_unmodifiable_field(part, fields)
  true
end