Ruby: Struct VS Initialize

Ruby: Struct vs Initialize

The class (non-struct) has a simpler ancestry tree:

>> Fruit.ancestors
=> [Fruit, Object, Kernel, BasicObject]

As compared to the struct version:

>> Fruit.ancestors
=> [Fruit, #<Class:0x1101c9038>, Struct, Enumerable, Object, Kernel, BasicObject]

So, the Struct class could be mistaken for an array (rare, but absolutely could happen)

fruit = Fruit.new("yo")
# .. later
fruit.each do |k|
puts k
end
# outputs: yo

So... I use Structs as throw-away data objects. I use "real" classes in my domain and application.

How to define a Ruby Struct which accepts its initialization arguments as a hash?

Cant you just do:

def initialize(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end

UPDATE:

To specify default values you can do:

def initialize(hash)
default_values = {
first_name: ''
}
default_values.merge(hash).each do |key, value|
send("#{key}=", value)
end
end

If you want to specify that given attribute is required, but has no default value you can do:

def initialize(hash)
requried_keys = [:id, :username]
default_values = {
first_name: ''
}
raise 'Required param missing' unless (required_keys - hash.keys).empty?
default_values.merge(hash).each do |key, value|
send("#{key}=", value)
end
end

When and when not to use the initialize method in ruby

The initialize method is necessary for objects which require something to be done during the object's initialization, that's all. Some objects do not require initialization, for example:

class Dog
def woof
:arf
end
end

However if you wanted to be able to customize how this dog could bark, you could give it an initializer:

class Dog
WOOF_DEFAULT = :arf

attr_reader :woof

def initialize(woof)
@woof = woof || WOOF_DEFAULT
end
end

Now you can configure it during the initialization:

dog = Dog.new(:bork)
dog.woof
# => :bork

You don't always need initialize, but it is a useful tool for when you do.

In the context of a board, presumably you need to create the board:

class Board
def initialize
# Create 9 cells, each of which is nil by default
@cells = Array.new(9, nil)
end

def move(x, y, side)
# Make a move in a cell if the cell is not occupied (||=)
@cells[x + y * 3] ||= side
end

def to_s
# Produces a string representation of the board state
@cells.map do |cell|
cell || ' '
end.each_slice(3).map do |row|
row.join(' | ')
end.join("\n---------\n")
end
end

Where now you have a board which you can use like this:

b = Board.new

puts b
# | |
# ---------
# | |
# ---------
# | |

puts

b.move(0,1, 'X')
b.move(1,0, 'O')

puts b
# | O |
# ---------
# X | |
# ---------
# | |

Defining a Struct during initialize

You don't have to use a constant. Use an instance variable.

class SomeClass
def initialize(csvfile)
@csv = CSV.open(csvfile, options...)
...
headers = @csv.headers
@record = Struct.new(headers)
load_data
end
def load_data
@records = []
@csv.each do |r|
@records << @record.new(r.fields)
end
end
end

why would you need an initialize in ruby class?

Struct exists for exactly this:

Book = Struct.new(:title, :author)

book = Book.new('Fear & Trembling', 'Søren Kierkegaard')
book.title #=> "Fear & Trembling"
book.author #=> "Søren Kierkegaard"

You can add new methods to Book by instead passing it a block:

Book = Struct.new(:title, :author) do
def info
[title, author]
end
end

book = Book.new('Zur Genealogie der Moral', 'Friedrich Nietzsche')
book.info #=> ["Zur Genealogie der Moral", "Friedrich Nietzsche"]

or by subclassing:

class Book < Struct.new(:title, :author)
def info
[title, author]
end
end

For even more functionality in building classes with attributes, have a look at Virtus.

Initialize Struct from array keys

Use the splat operator:

arrs = [[:key1, :key2, :key3, :key4],[:key6, :key7]]
arrs.map { |a| Struct.new(*a) }
=> [#<Class:0x007fa833e25738>, #<Class:0x007fa833e1fa18>]

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

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


Related Topics



Leave a reply



Submit