Module: ActiveRestrictor

Defined in:
lib/active_restrictors/active_restrictor.rb

Defined Under Namespace

Modules: ClassMethods, InstanceMethods

Class Method Summary collapse

Class Method Details

.included(klass) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/active_restrictors/active_restrictor.rb', line 223

def self.included(klass)
  # Patch up the model we have been called on
  ([klass] + klass.descendants).compact.each do |base|
    base.class_eval do
      cattr_accessor :restrictors
      
      extend ClassMethods
      include InstanceMethods

      unless(base.singleton_methods.map(&:to_sym).include?(:_restrictor_custom_user_class))
        class << self
          # This can be overridden for customization
          def _restrictor_custom_user_class
            User
          end
        end
      end
      scope :allowed_for, lambda{|*args|
        user = args.detect{|item|item.is_a?(User)}
        if(user.present?)
          r_scope = user.send("allowed_#{base.name.tableize}").select("#{base.table_name}.id")
          r_scope.arel.ast.cores.first.projections.delete_if{|item| item != "#{base.table_name}.id"}
          where("#{table_name}.id IN (#{r_scope.to_sql})")
        else
          where('null')
        end
      }
    end
  end
  # Patch up the user to provide restricted methods
  ([klass._restrictor_custom_user_class] + klass._restrictor_custom_user_class.descendants).compact.each do |user_klass|
    user_klass.class_eval do
      # This patches a method onto the User instance to
      # provide access to the allowed instance of the model
      # in use. For example, if the restrictor module is
      # included into the Fubar model, it will
      # provide User#allowed_fubars
      define_method("allowed_#{klass.name.tableize}") do
        # First we perform a basic check against the User to see
        # if this user instance is even allowed by default
        user_scope = klass.restrictor_user_scoping.where(:id => self.id)
        if(user_scope.count > 0)
          scope = klass.restrictor_klass_scoping
          klass.full_restrictors.each do |restrictor|
            scope = scope.includes(restrictor[:name])
            if(restrictor[:scope].present?)
              scope = scope.merge(restrictor[:scope].respond_to?(:call) ? restrictor[:scope].call : restrictor[:scope])
            end
            unless(restrictor[:model_custom].present?)
              rtable_name = restrictor[:table_name] || restrictor[:class].table_name
              r_scope = self.send(restrictor[:user_association] || restrictor[:name]).scoped.select("#{rtable_name}.id") # This gives us valid joiners!
              # this next bit gets rid of the association_name.* rails insists upon and any extra cruft
              r_scope.arel.ast.cores.first.projections.delete_if{|item| item != "#{rtable_name}.id"}
              scope = scope.where(
                "#{rtable_name}.id IN (#{r_scope.to_sql})#{
                  " OR #{rtable_name}.id IS NULL" if restrictor[:default_allowed_all]
                }"
              )
            else
              scope = restrictor[:model_custom].call(scope, self)
            end
          end
          scope
        else
          klass.where('null')
        end
      end
    end
  end
end