Reputation: 1664
I want to create a factory for a class, which will call a private setter. Is this possible?
Doing this:
class Foo
def self.from_bar(bar)
f = new bar.foo_id # bar has the id of the foo object I want to make
f.bar = bar # but I also wanna store the bar object in foo
f
end
def initialize(id)
end
private
attr_writer :bar
# lazily load and memoize bar if needed and construction was not
# made from factory method
def bar
@bar ||= ...
end
end
results in a NoMethodError: private method
error. I can send
the method, but I'm looking for a non hacky way to do it.
EDIT: Changed attr_accessor to attr_writer and added memoized reader to reveal more of my intent here.
Upvotes: 2
Views: 735
Reputation: 2450
Having re-read the question, I believe this may be the answer.
I've come to the conclusion that a factory is not needed, and this can all be done in the initialize method.
class Foo
def initialize(id, bar)
@id = id
@bar = bar
end
end
Note: You could then make bar accessible with an attr_reader. Which will define bar
but not bar=
.
attr_reader :bar
Upvotes: 1
Reputation: 2450
Yes, this is possible.
Ruby allows you to call private methods from anywhere using the send
method.
Your factory would look something like this:
class FooFactory
def FooFactory.build(bar)
foo = Foo.new(generate_id)
foo.send('bar=', bar)
foo
end
private
def generate_id
rand(999999)
end
end
Then simply call:
FooFactory.build('bar')
Note, this is a standard pattern for factories, the send line would also work in your code snippet.
Upvotes: 1
Reputation: 121000
This is possible only with a hacky approach:
class Foo
def self.from_bar(bar)
new(bar.foo_id).tap { |f| f.send(:bar=, bar) }
# or, better:
new(bar.foo_id).tap { |f| f.instance_variable_set(:@bar, bar) }
end
private
attr_accessor :bar
end
Honestly, making attr_accessor
private makes a little sense, though.
Upvotes: 1