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



225
226
227
228
# File 'lib/active_restrictors/active_restrictor.rb', line 225

def self.included(klass)
  patch_base_restrictors(klass) unless klass.instance_methods.include?(:add_restrictor)
  patch_user_restrictors(klass)
end

.patch_base_restrictors(klass) ⇒ Object



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
# File 'lib/active_restrictors/active_restrictor.rb', line 230

def self.patch_base_restrictors(klass)
  # Patch up the model we have been called on
  klass.class_eval do
    cattr_accessor :restrictors
    
    extend ClassMethods
    include InstanceMethods

    unless(klass.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_#{klass.name.tableize}").select("#{klass.table_name}.id")
        r_scope.arel.ast.cores.first.projections.delete_if{|item| item != "#{klass.table_name}.id"}
        where("#{table_name}.id IN (#{r_scope.to_sql})")
      else
        where('null')
      end
    }

    class << self
      def inherited(klass)
        ActiveRestrictor.patch_user_restrictors(klass)
        super
      end
    end
  end
end

.patch_user_restrictors(klass) ⇒ Object



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
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/active_restrictors/active_restrictor.rb', line 266

def self.patch_user_restrictors(klass)
  # 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