2

I'm new to ruby.

What I don't get is, if some patterns appear repetitively in my code, there should be something I could do to subtract that part and save the matter.

E.g., here is what it looks like now:

class Book
  def initialize(title, author)
    @title = title
    @author = author
  end
  def info
    puts [@title, @author]
  end
end

What I want the code to look like:

class Book(title, author)
  def info
    puts [title, author]
  end
end

What reasons I could come up with:

  1. initialize gets called every time an object has been created. so if there's something I want to do every time an object is created, initialize is the official way.

  2. the initialize indicates how many arguments a constructor would take. without which it would take 0.

But it still looks unnecessary to me. Is it possible that I could alter the syntax to look like the second one instead of having to do the initialization every time I create a Class, if I have nothing special to execute upon construction?

0

4 Answers 4

4

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.

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

Comments

2

A class is not a method therefore it does not accept parameters. The point of initialize is to do any kind of up front work when you newly create an object. For example:

book = Book.new('A Tale of Two Cities', 'Charles Dickens')
  #=> #<Book:0x007f9f5a8933d8 @title="A Tale of Two Cities", @author="Charles Dickens">

If you add an attr_accessor to the class you can get access to each of those instance variables like so:

class Book
  attr_accessor :title, :author
  def initialize(title, author)
    @title = title
    @author = author
  end
  def info
    puts [@title, @author]
  end
end  

book.title 
  #=> "A Tale of Two Cities" 
book.author
  #=> "Charles Dickens"

4 Comments

Is it possible that I could alter the syntax to look like the second one instead of having to do the initialization every time I create a Class, if I have nothing special to execute upon construction?
With a struct, yes: Book = Struct.new(:title, :author) do; def info; puts "#{title}"; end; end
Then why don't I see people using it this way? Is it just customs or are there any other special things about the original Class?
1

Disclaimer: if you just want a simple wrapper of attributes (i.e. no fancy methods) consider using something like an OpenStruct or a Hash.

That being said, here's some metaprogramming for creating the simple sort of class you described.

def simple_class *attrs
  klass = Class.new
  klass.class_eval do
    define_method('initialize') do |*args|
      if args.size != attrs.size
        raise ArgumentError.new("wrong number of arguments (#{args.size} for #{attrs.size})")
      end

      attrs.zip(args).each do |att, arg|
        instance_variable_set("@#{att}", arg)
      end
    end

    define_method('info') do
      attrs.map { |att| instance_variable_get("@#{att}") }
    end
  end

  klass
end

Book = simple_class(:title, :author)

b = Book.new("A BOOK", "ME")
puts b.info

Comments

0

No, you can't alter the inherent Ruby language to do what you're suggesting. However, since the language is so flexible, you could define a method which behaves essentially as you describe, e.g.

def new_class(class_name, *class_args)
  # insert Ruby metaprogramming code here
end

new_class(:Book, :title, :author) do
  def info
    puts [title, author]
    end
  end
end

2 Comments

So I could define a new_class like above (I assume it behaves exactly like the Class?). But why don't I see people using it this way?
No, new_class would be a method, not a class. However, if you called new_class as I showed, then as a result of executing that method, the class Book would be defined as you requested. That is, Book would be defined with an initialization method taking title and author as parameters. As to why people don't do this, I think it's because they prefer the language as it is and/or want to use the idiomatic approach.

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.