Ruby protected constructors
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





Well, I tried to get Module.protected to work the way you’d want, but it breaks the usual usage of protected as a keyword where subsequent methods are protected.
So, this works:
Because protected is passed all the symbols you want to protect. Unfortunately, this doesn’t work:
protected def hello "hello" endBecause it appears that kernel magic is suppressed (can anyone enlighten?)
Therefore, recommend you stay with the solution (in the blog post).
Anyway, for posterity, here is an almost-but-not-quite-solution:
Comment by Josh — August 24, 2007 @ 2:29 pm
Hey Josh!
Nice work!
Minor bug report.. If you want to put ‘protect_constructor’ in ‘Class’ you need to drop the self. Ie,
def protect_constructor
class_eval…
Alternatively, you can put ‘protect_constructor’ in ‘Object’ with the explicit self reference.
Cheers,
Lawrence & James
Comment by James Crisp & Lawrence Song — August 28, 2007 @ 4:31 pm
Done - thanks
Comment by Josh — August 28, 2007 @ 4:48 pm