7

I can't figure out the proper block initialize

class Foo 
  attr_accessor :bar
end

obj = Foo.new do |a|
  a.bar = "baz"
end

puts obj.bar

Expect "baz" instead get nil

What is the proper incantation for block class initializers in ruby?

5
  • The attr_accessor can't work in that form and the block is never called. Commented Mar 22, 2012 at 16:13
  • Thanks, you are right about that, I fixed it so it is "functional" code now Commented Mar 22, 2012 at 16:24
  • Right, I get that it doesn't get called (which explains why it doesn't work). I like the "tap" idea below, as it lets me use tools like FactoryGirl (which require default initializers) without having to override new. Commented Mar 22, 2012 at 16:28
  • I don't get it. Can you give an example usage of FactoryGirl where this would be of advantage? Commented Mar 22, 2012 at 16:34
  • Here's some links on the problem wrong # of args github discussion Commented Mar 22, 2012 at 16:40

4 Answers 4

30

Another way to make a block initializer would be writing it yourself one:

class Foo
  attr_accessor :bar

  def initialize
    yield self if block_given?
  end
end

And later use it:

foo = Foo.new do |f|
  f.bar = true
end

My two cents.

Sign up to request clarification or add additional context in comments.

1 Comment

no doubt this is the better way of initializing a class with a block.
9

Try again:

class Foo 
  attr_accessor :bar
end

obj = Foo.new.tap do |a|
  a.bar = "baz"
end

puts obj.bar

3 Comments

That's not the same. Also, it should raise a NameError.
It's the best way without adding new ways your users have to read the documentation for.
This way is better than putting yield self if block_given? in the Foo's initialize method, because often you can't or don't want to modify the Foo class (think of the Open Closed Principle). Plus, why clutter up the initialize method of the Foo class, when a simple method chaining of tap would do?
2

I don't think new can take a block. Never saw it anywhere anyway. Why do you want to initialize in a block ? You can always do obj = foo.new.tap do |a| ... If you really want a block

4 Comments

It's not that it can't take a block, it's just ugly. Despite this, it's a commonly seen "idiom".
Why is it ugly? It's a common idiom in other languages that I've used such as c#.
@Doug: It's equally ugly in other languages, the main reason for it being that it doesn't serve any particular purpose and just increases complexity.
It seems clearer and less error-prone to me. There's no order dependency as in a normal constructor, and you get named parameters which are easier to read and write. I'd probably only resort to a constructor if there was some initialization beyond copying in values (which is not always the case)
0

actually you have a constructor for these purposes:

class Foo
  attr_accessor :bar

  def initialize(bar = "baz")
    @bar = bar
  end

end

3 Comments

Maybe you should actually make that a parameter.
Niklas, yeah, that would be better
It's not common pass them as constrcuter. Just look at some ruby code in github

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.