shicholas
shicholas

Reputation: 6153

Conditional Keyword Arguments (Ruby 2.0)

let's say I have this object:

class Post
  def initialize(title: 'title', content: 'content')
    @title = title
    @content = content
  end
end

but I would like to add logic like:

class Post
  def initialize(title: 'title', content: 'content')
    @title = title unless title.empty? # if it's empty I'd like to use the default value
    @content = content unless content.empty?
  end
end

In the above example, how do I assign a keyword argument conditionally?

Upvotes: 1

Views: 2566

Answers (2)

aceofbassgreg
aceofbassgreg

Reputation: 3937

Revisiting this with a better answer...this is exactly what fetch is used for:

class Post
  def initialize(options)
    @title = options.fetch(:title, 'title')
    @content = options.fetch(:content, 'content')
  end
end

Here's how one would use this logic:

irb(main):007:0> post = Post.new(title: 'A Very Short Story', content: 'Hello World!')
=> #<Post:0x00007fadc6829878 @title="A Very Short Story", @content="Hello World!">
irb(main):008:0> post = Post.new(title: 'A Nonexistent Story')
=> #<Post:0x00007fadc6820138 @title="A Nonexistent Story", @content="content">  # Notice how 'content' was set using the default option

Old (Valid But Uses More Code) Answer

Why not set defaults in a separate method and then merge in the arguments passed at initialization? If arguments aren't passed, then the defaults kick in. Otherwise, the defaults are overwritten by the arguments passed in at initialization.

For example:

class Post
  def initialize(options)
    options = defaults.merge(options)
    @title   = options[:title]
    @content = options[:content]
  end

## Code omitted ##

  private def defaults
    {
      title:  "Your Default Title"
      content: "Your Default Content"
    }
  end
    ...
end

Upvotes: 2

sawa
sawa

Reputation: 168071

I feel a code smell here. You are trying to assign the default value to the variables under two separated conditions: when the argument is not given, and when the argument is empty. That is not a good design. It is a potential cause of bugs, and makes maintenance difficult. I suggest you should take one of the two ways:

(i) Make the arguments obilgatory (i.e., pass nil or an empty value rather than not passing an argument), and do the validation in the method body:

class Post
  def initialize(title, content)
    @title = title.nil? || title.empty? ? "title" : title
    @content = content.nil? || content.empty? ? "content" : content
  end
end

(ii) Rather than passing an empty value as an argument, do not pass it:

class Post
  def initialize(title: "title", content: "content")
    @title, @content = title, content
  end
end

Upvotes: 1

Related Questions