Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a Rails 4 Concern that takes an argument

I have an ActiveRecord class called User. I'm trying to create a concern called Restrictable which takes in some arguments like this:

class User < ActiveRecord::Base
  include Restrictable # Would be nice to not need this line
  restrictable except: [:id, :name, :email]
end

I want to then provide an instance method called restricted_data which can perform some operation on those arguments and return some data. Example:

user = User.find(1)
user.restricted_data # Returns all columns except :id, :name, :email

How would I go about doing that?

like image 642
jesal Avatar asked Nov 13 '14 02:11

jesal


People also ask

How do I create a concern in Rails?

A Rails Concern is a module that extends the ActiveSupport::Concern module. Concerns allow us to include modules with methods (both instance and class) and constants into a class so that the including class can use them. The code inside the included block is evaluated in the context of the including class.

What is the reason for using concerns in Rails?

People use Rails concern for mainly for two things: First, to reduce the size of a model that could be separated into several models, and therefore reducing the size. Second, to avoid declaring the same class over and over again.

What is ActiveSupport concern?

ActiveSupport's Concern module allows us to mix in callbacks, class and instance methods, and create associations on target objects. This module has an included method, which takes a block, as well as an append_features method and class_methods block, which you can read about in the source code.


2 Answers

If I understand your question correctly this is about how to write such a concern, and not about the actual return value of restricted_data. I would implement the concern skeleton as such:

require "active_support/concern"

module Restrictable
  extend ActiveSupport::Concern

  module ClassMethods
    attr_reader :restricted

    private

    def restrictable(except: []) # Alternatively `options = {}`
      @restricted = except       # Alternatively `options[:except] || []`
    end
  end

  def restricted_data
    "This is forbidden: #{self.class.restricted}"
  end
end

Then you can:

class C
  include Restrictable
  restrictable except: [:this, :that, :the_other]
end

c = C.new
c.restricted_data  #=> "This is forbidden: [:this, :that, :the_other]"

That would comply with the interface you designed, but the except key is a bit strange because it's actually restricting those values instead of allowing them.

like image 97
Gabriel de Oliveira Avatar answered Oct 14 '22 04:10

Gabriel de Oliveira


I'd suggest starting with this blog post: https://signalvnoise.com/posts/3372-put-chubby-models-on-a-diet-with-concerns Checkout the second example.

Think of concerns as a module you are mixing in. Not too complicated.

module Restrictable
  extend ActiveSupport::Concern

  module ClassMethods
    def restricted_data(user)
      # Do your stuff
    end
  end
end
like image 23
Matt Walston Avatar answered Oct 14 '22 06:10

Matt Walston