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
-
#calculate(*args) ⇒ Object
Merges current relation with restriction and calls real
calculate
. - #can?(action, field = false) ⇒ Boolean
- #count(*args) ⇒ Object
- #creatable? ⇒ Boolean
- #create_with_protector(*args, &block) ⇒ Object
- #create_with_protector!(*args, &block) ⇒ Object
- #except(*args) ⇒ Object
-
#exec_queries_with_protector(*args) ⇒ Object
Patches current relation to fulfill restriction and call real
exec_queries
. -
#exists?(*args) ⇒ Boolean
Merges current relation with restriction and calls real
exists?
. -
#new_with_protector(*args, &block) ⇒ Object
Forwards protection subject to the new instance.
- #only(*args) ⇒ Object
-
#protector_expand_inclusion(inclusion, results = [], base = [], klass = @klass) ⇒ Object
Indexes
includes
format by actual entity class. -
#protector_meta(subject = protector_subject) ⇒ Object
Gets DSL::Meta::Box of this relation.
-
#protector_mimic_base! ⇒ Object
Makes instance of Relation duck-type compatible to AR::Base to allow proper protection block execution with itself.
- #protector_relation ⇒ Object
-
#protector_substitute_includes(subject, relation) ⇒ Object
Swaps
includes
withpreload
if it's not referenced or merges security scope of proper class otherwise. - #sum(*args) ⇒ Object
- #unscoped ⇒ Object
Instance Method Details
#calculate(*args) ⇒ Object
Merges current relation with restriction and calls real calculate
77 78 79 80 |
# File 'lib/protector/adapters/active_record/relation.rb', line 77 def calculate(*args) return super unless protector_subject? protector_relation.unrestrict!.calculate(*args) end |
#can?(action, field = false) ⇒ Boolean
35 36 37 |
# File 'lib/protector/adapters/active_record/relation.rb', line 35 def can?(action, field=false) .can?(action, field) end |
#count(*args) ⇒ Object
This is here cause NullRelation
can return nil
from count
67 68 69 |
# File 'lib/protector/adapters/active_record/relation.rb', line 67 def count(*args) super || 0 end |
#creatable? ⇒ Boolean
31 32 33 |
# File 'lib/protector/adapters/active_record/relation.rb', line 31 def creatable? new.creatable? end |
#create_with_protector(*args, &block) ⇒ Object
103 104 105 106 107 108 109 110 111 112 |
# File 'lib/protector/adapters/active_record/relation.rb', line 103 def create_with_protector(*args, &block) return create_without_protector(*args, &block) unless protector_subject? protector_permit_strong_params(args) create_without_protector(*args) do |instance| instance.restrict!(protector_subject) block.call(instance) if block end end |
#create_with_protector!(*args, &block) ⇒ Object
114 115 116 117 118 119 120 121 122 123 |
# File 'lib/protector/adapters/active_record/relation.rb', line 114 def create_with_protector!(*args, &block) return create_without_protector!(*args, &block) unless protector_subject? protector_permit_strong_params(args) create_without_protector!(*args) do |instance| instance.restrict!(protector_subject) block.call(instance) if block end end |
#except(*args) ⇒ Object
56 57 58 59 |
# File 'lib/protector/adapters/active_record/relation.rb', line 56 def except(*args) return super unless protector_subject? super.restrict!(protector_subject) 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) intopreload
- delaying built-in preloading to the stage where selection is restricted
- merging current relation with restriction (of self and every eager association)
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/protector/adapters/active_record/relation.rb', line 132 def exec_queries_with_protector(*args) return @records if loaded? return exec_queries_without_protector unless protector_subject? subject = protector_subject relation = protector_relation.unrestrict! relation = protector_substitute_includes(subject, 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| if ::ActiveRecord::Associations::Preloader.method_defined? :preload ::ActiveRecord::Associations::Preloader.new.preload(@records, association) else ::ActiveRecord::Associations::Preloader.new(@records, association).run end end @loaded = true @records end |
#exists?(*args) ⇒ Boolean
Merges current relation with restriction and calls real exists?
83 84 85 86 |
# File 'lib/protector/adapters/active_record/relation.rb', line 83 def exists?(*args) return super unless protector_subject? protector_relation.unrestrict!.exists?(*args) end |
#new_with_protector(*args, &block) ⇒ Object
Forwards protection subject to the new instance
89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/protector/adapters/active_record/relation.rb', line 89 def new_with_protector(*args, &block) return new_without_protector(*args, &block) unless protector_subject? protector_permit_strong_params(args) unless block_given? new_without_protector(*args).restrict!(protector_subject) else new_without_protector(*args) do |instance| block.call instance.restrict!(protector_subject) end end end |
#only(*args) ⇒ Object
61 62 63 64 |
# File 'lib/protector/adapters/active_record/relation.rb', line 61 def only(*args) return super unless protector_subject? super.restrict!(protector_subject) 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}]
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/protector/adapters/active_record/relation.rb', line 211 def (inclusion, results=[], base=[], klass=@klass) if inclusion.is_a?(Hash) (inclusion, results, base, klass) else Array(inclusion).each do |i| if i.is_a?(Hash) (i, results, base, klass) else results << [ klass.reflect_on_association(i.to_sym).klass, i.to_sym ] end end end results end |
#protector_meta(subject = protector_subject) ⇒ Object
Gets DSL::Meta::Box of this relation
40 41 42 |
# File 'lib/protector/adapters/active_record/relation.rb', line 40 def (subject=protector_subject) @klass..evaluate(subject) end |
#protector_mimic_base! ⇒ Object
Makes instance of Relation duck-type compatible to AR::Base to allow proper protection block execution with itself
191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/protector/adapters/active_record/relation.rb', line 191 def protector_mimic_base! return unless Protector::Adapters::ActiveRecord.modern? class <<self # AR 4 has awfull inconsistency when it comes to method `all` # We have to mimic base class behaviour for relation we get from `unscoped` def all self end end end |
#protector_relation ⇒ Object
44 45 46 47 48 |
# File 'lib/protector/adapters/active_record/relation.rb', line 44 def protector_relation result = self.clone result = .eval_scope_procs(result) if .relation result end |
#protector_substitute_includes(subject, relation) ⇒ Object
Swaps includes
with preload
if it's not referenced or merges
security scope of proper class otherwise
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/protector/adapters/active_record/relation.rb', line 161 def protector_substitute_includes(subject, relation) if relation.eager_loading? (relation.includes_values + relation.eager_load_values).each do |klass, path| # AR drops default_scope for eagerly loadable associations # https://github.com/inossidabile/protector/issues/3 # and so should we = klass..evaluate(subject) if .scoped? unscoped = klass.unscoped # `unscoped` gets us a relation but Protector scope is supposed # to work with AR::Base. Some versions of AR have those uncompatible # so we have to workaround it :( unscoped.protector_mimic_base! # Finally we merge unscoped basic relation extended with protection scope relation = relation.merge .eval_scope_procs(unscoped) end end else relation.preload_values += includes_values relation.includes_values = [] end relation end |
#sum(*args) ⇒ Object
This is here cause NullRelation
can return nil
from sum
72 73 74 |
# File 'lib/protector/adapters/active_record/relation.rb', line 72 def sum(*args) super || 0 end |
#unscoped ⇒ Object
Unscoped relation drops properties and therefore should be re-restricted
51 52 53 54 |
# File 'lib/protector/adapters/active_record/relation.rb', line 51 def unscoped return super unless protector_subject? super.restrict!(protector_subject) end |