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
Sorting a Hash in Ruby by Its Value First Then Its Key
Rails Active Record: Find in Conjunction with :Order and :Group
Rails Routes: Get Without Param :Id
Rails:Runtimeerror - Can't Modify Frozen Array When Running Rspec in Rails
Rspec 'Eq' VS 'Eql' in 'Expect' Tests
In Ruby, How to Find Out If a String Is Not in an Array
Is There a Bug in Ruby Lookbehind Assertions (1.9/2.0)
Pg::Invalidparametervalue: Error: Invalid Value for Parameter "Client_Min_Messages": "Panic"
Ruby Mailer Is Coming Up with an Eoferror
Is There a Method to Limit/Clamp a Number
Ruby -V Dyld: Library Not Loaded: /Usr/Local/Lib/Libgmp.10.Dylib
Making a Module Inherit from Another Module in Ruby
Is It a Good Idea to Purge Old Rails Migration Files
Devise Raises Error with Rails 4.2 Upgrade
How to Specify a Read Timeout for a Net::Http::Post.New Request in Ruby 2