May 2, 2015 by Daniel P. Clark

Private Module Methods in Ruby

So this is something I’ve looked into many times over the years and have never found an answer to.  But great news is that I’ve found a way!

It’s as simple as defining module methods as private within the singleton class.  Here’s how I did it in my gem PolyBelongsTo

module PolyBelongsTo::Core
  extend ActiveSupport::Concern

  included do

    def self._pbt_polymorphic_orphans
      # some code here
    end

    def self._pbt_nonpolymorphic_orphans
      # some code here
    end

    class << self
      private :_pbt_polymorphic_orphans
      private :_pbt_nonpolymorphic_orphans
    end

  end
end

My gem gets included into ActiveRecord::Base and these private methods are available for my other methods to use and my tests show that they are indeed private.  One sample ActiveRecord table I’ve named Squishy and this is the Minitest test I wrote to prove it’s private.

it "keeps helper methods private" do
  Squishy.singleton_class.private_method_defined?(:_pbt_polymorphic_orphans).must_be_same_as true
  Squishy.singleton_class.method_defined?(:_pbt_polymorphic_orphans).must_be_same_as false 
  Squishy.method_defined?(:_pbt_polymorphic_orphans).must_be_same_as false 
  Squishy.singleton_class.private_method_defined?(:_pbt_nonpolymorphic_orphans).must_be_same_as true
  Squishy.singleton_class.method_defined?(:_pbt_nonpolymorphic_orphans).must_be_same_as false 
  Squishy.method_defined?(:_pbt_nonpolymorphic_orphans).must_be_same_as false 
end

And it passes all green!  My other methods that call these pass as well! Yay!

If you were to do this without the Rails “Concern” way of doing it your code would look more like.

module PolyBelongsTo::Core

  def self.included(base)

    def base._pbt_polymorphic_orphans
      # some code here
    end

    def base._pbt_nonpolymorphic_orphans
      # some code here
    end

    class << base
      private :_pbt_polymorphic_orphans
      private :_pbt_nonpolymorphic_orphans
    end

  end
end

And anything you include it into will now have these private methods defined!  Hope you enjoyed this!  I’m very happy to have discovered it myself and hope it finds you well.

If you’re not planning on including the module you can do it like this.

module Example
  def self.x
    "hello"
  end

  def self.y
    "hello"
  end

  class << self
    private :x
  end
end

Example.x
# NoMethodError: private method `x' called for B:Module
Example.y
# => "hello"

Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!
-Daniel P. Clark

Image by Richard Scott via the Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Generic License.

#base#include#included#method#methods#module#private#rails#ruby

Comments

    • Daniel P. Clark
      May 2, 2015 - 5:36 pm

      Yes thanks! They’ve added several ways of defining a private method in more recent Ruby versions. The approach I’ve shared here works with Ruby 1.8

  1. Piotr Szotkowski
    May 25, 2015 - 2:44 pm

    In all of those cases presented in the blog my choice would be `module_function`, which makes the given `Module`’s methods callable on the `Module` itself and locally anywhere it’s included, but not from outside the places it’s included:

    module Foo
      module_function
      def foo
        :foo
      end
    end
    
    class Bar
      include Foo
      def bar
        foo
      end
    end
    
    Foo.foo     #=> :foo
    Bar.new.bar #=> :foo
    Bar.new.foo #=> NoMethodError: private method `foo' called
    
    • Daniel P. Clark
      May 25, 2015 - 10:31 pm

      Awesome! That’s definitely handy if you’d like to be able to include the behavior. In the last example I’ve shown of a non-inclusive module the method access is exclusive. Which is something I rather like myself.

Leave a Reply

Your email address will not be published / Required fields are marked *