Module: Protector::Adapters::ActiveRecord::Relation

Extended by:
ActiveSupport::Concern
Defined in:
lib/protector/adapters/active_record/relation.rb

Overview

Patches ActiveRecord::Relation

Instance Method Summary collapse

Instance Method Details

#calculate(*args) ⇒ Object

Merges current relation with restriction and calls real calculate



51
52
53
54
# File 'lib/protector/adapters/active_record/relation.rb', line 51

def calculate(*args)
  return super unless @protector_subject
  merge(protector_meta.relation).unrestrict!.calculate *args
end

#count(*args) ⇒ Object

Note:

This is here cause NullRelation can return nil from count



41
42
43
# File 'lib/protector/adapters/active_record/relation.rb', line 41

def count(*args)
  super || 0
end

#exec_queries_with_protector(*args) ⇒ Object

Patches current relation to fulfill restriction and call real exec_queries

Patching includes:

  • turning includes (that are not referenced for eager loading) into preload
  • delaying built-in preloading to the stage where selection is restricted
  • merging current relation with restriction (of self and every eager association)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/protector/adapters/active_record/relation.rb', line 69

def exec_queries_with_protector(*args)
  return @records if loaded?
  return exec_queries_without_protector unless @protector_subject

  subject  = @protector_subject
  relation = merge(protector_meta.relation).unrestrict!
  relation = protector_substitute_includes(relation)

  # Preserve associations from internal loading. We are going to handle that
  # ourselves respecting security scopes FTW!
  associations, relation.preload_values = relation.preload_values, []

  @records = relation.send(:exec_queries).each{|record| record.restrict!(subject)}

  # Now we have @records restricted properly so let's preload associations!
  associations.each do |association|
    ::ActiveRecord::Associations::Preloader.new(@records, association).run
  end

  @loaded = true
  @records
end

#exists?(*args) ⇒ Boolean

Merges current relation with restriction and calls real exists?

Returns:

  • (Boolean)


57
58
59
60
# File 'lib/protector/adapters/active_record/relation.rb', line 57

def exists?(*args)
  return super unless @protector_subject
  merge(protector_meta.relation).unrestrict!.exists? *args
end

#protector_expand_inclusion(inclusion, results = [], base = [], klass = @klass) ⇒ Object

Indexes includes format by actual entity class

Turns {foo: :bar} into [[Foo, :foo], [Bar, {foo: :bar}]

Parameters:

  • inclusion (Symbol, Array, Hash)

    Inclusion description in the AR format

  • results (Array) (defaults to: [])

    Resulting set

  • base (Array) (defaults to: [])

    Association path ([:foo, :bar])

  • klass (Class) (defaults to: @klass)

    Base class



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/protector/adapters/active_record/relation.rb', line 117

def protector_expand_inclusion(inclusion, results=[], base=[], klass=@klass)
  if inclusion.is_a?(Hash)
    protector_expand_inclusion_hash(inclusion, results, base, klass)
  else
    Array(inclusion).each do |i|
      if i.is_a?(Hash)
        protector_expand_inclusion_hash(i, results, base, klass)
      else
        results << [
          klass.reflect_on_association(i.to_sym).klass,
          i.to_sym
        ]
      end
    end
  end

  results
end

#protector_metaObject

Gets DSL::Meta::Box of this relation



29
30
31
32
33
# File 'lib/protector/adapters/active_record/relation.rb', line 29

def protector_meta
  # We don't seem to require columns here as well
  # @klass.protector_meta.evaluate(@klass, @protector_subject, @klass.column_names)
  @klass.protector_meta.evaluate(@klass, @protector_subject)
end

#protector_substitute_includes(relation) ⇒ Object

Swaps includes with preload whether it's not referenced or merges security scope of proper class otherwise



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/protector/adapters/active_record/relation.rb', line 94

def protector_substitute_includes(relation)
  subject = @protector_subject

  if eager_loading?
    protector_expand_inclusion(includes_values + eager_load_values).each do |klass, path|
      relation = relation.merge(klass.protector_meta.evaluate(klass, subject).relation)
    end
  else
    relation.preload_values += includes_values
    relation.includes_values = []
  end

  relation
end

#sum(*args) ⇒ Object

Note:

This is here cause NullRelation can return nil from sum



46
47
48
# File 'lib/protector/adapters/active_record/relation.rb', line 46

def sum(*args)
  super || 0
end

#unscopedObject

Note:

Unscoped relation drops properties and therefore should be re-restricted



36
37
38
# File 'lib/protector/adapters/active_record/relation.rb', line 36

def unscoped
  super.restrict!(@protector_subject)
end