daremkd
daremkd

Reputation: 8424

Ruby - Modify Struct attribute immediately after it's being initialized

Suppose I have:

Book = Struct.new(:title, :content)
book = Book.new('harry potter', 'a bunch of content here')
p book.title #=> harry potter

What I want the last line to produce is "Harry Potter". I know I can do something like:

Book = Struct.new(:title, :content) do
  def capitalized_title
    self.title.gsub(/\S+/, &:capitalize)
  end
end

and then call capitalized_title, but what I want is to not have to create a separate method, instead, have some way of when you assign "title" to a new Book object, the title is immediately capitalized. Some kind of hook method, I would guess.

Upvotes: 2

Views: 1666

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110685

You could create an initialize method for the class Book (borrowing @falsetru's nice way of capitalizing the the words in title). First note that Book does not have its own initialize method;;

Book = Struct.new(:title, :content)
  #=> Book
Book.private_instance_methods(false)
  #=> []

Rather, it inherits from Struct:

Book.private_instance_methods.include?(:initialize)
  #=> true   
Book.instance_method(:initialize).owner
  #=> Struct
Struct.private_instance_methods(false)
  #=> [:initialize, :initialize_copy]

We add an initialize method in the usual way:

Book = Struct.new(:title, :content) do
  def initialize(title,content)
    self.title = title.gsub(/\S+/, &:capitalize)
    self.content = content
  end
end

confirm:

Book.private_instance_methods(false)
  #=> [:initialize]

and test:

book = Book.new('harry potter', 'a bunch of content here')
book.title
  #=> "Harry Potter"
book.content
  #=> "a bunch of content here"

If there were more than just two instance variables, we might do this:

Book = Struct.new(:title, :content) do
  def initialize(title,content)
    super
    self.title = title.gsub(/\S+/, &:capitalize)
  end
end

Upvotes: 2

falsetru
falsetru

Reputation: 369094

class Book < Struct.new(:title, :content)
  def title
    super.gsub(/\S+/, &:capitalize)
  end
end

book = Book.new('harry potter', 'a bunch of content here')
book.title # => "Harry Potter"

Book = Struct.new(:title, :content) do
  alias orig_title title
  def title
    orig_title.gsub(/\S+/, &:capitalize)
  end
end

To prevent title is called every time, override title=:

Book = Struct.new(:title, :content) do
  alias orig_title= title=
  def initialize(*args)
    super
    self.title = title
  end
  def title= value
    self.orig_title = value.gsub(/\S+/, &:capitalize)
  end
end

Upvotes: 5

Related Questions