My clever co-worker Shane Harvie just blogged about making Ruby constructors protected in a nice way.

Shane, here’s something slightly more terse which might be the new content of your make_constructor_protect.rb file:

class Class
	def protect_constructor
		class_eval(%Q[
			class < < self
				protected :new

				def inherited(klass)
					klass.class_eval do
						def self.new(*args); super; end
					end
				end
			end
		])
	end
end

Seems to pass all the tests. So, your party.rb might look like this now (I’ve left the other stuff commented out, and added access to annual_cost just to be sure).


With a little more effort messing around with the Module.protected, I reckon you could make “protected :new” work as a more natural idiomatic approach – the way you originally hoped it might.

Update: not really as some kernel behaviour appears to be suppressed. See comments.

require 'make_constructor_protected'
require 'test/unit'

class Party
#  include MakeConstructorProtected
  attr_reader :name

  protect_constructor
  def initialize(name)
    @name = name
  end
end

class Employee < Party
  attr_reader :annual_cost

  def initialize(name, id, annual_cost)
    super(name)
    @id, @annual_cost = id, annual_cost
  end
end

class Department < Party
  def initialize(name)
    super
  end
end

class PartyTest < Test::Unit::TestCase
	def test_new_raises_no_method_error
	 assert_raise(NoMethodError) { Party.new("some name") }
	end

	def test_department_initialize
	 d = Department.new("name")
	 assert_equal "name", d.name
	end

	def test_employee_initialize
	 e = Employee.new("name", 5, 12)
	 assert_equal "name", e.name
	 assert_equal 12, e.annual_cost
	end
end