user672118
user672118

Reputation:

Ruby DSL Initialization of Variables

I'm trying to replicate the functionality of Sinatra. Specifically the DSL-like part where you can define routes in the definition of the class. When I try to run my version of a persons-DSL I get the error, undefined method '<<' for nil:NilClass on line 11.

class Persons
  class << self
    def reset!
      @persons = []
    end

    def add_person title, name
      @persons << {
        title: title,
        name: name
      }
    end
  end

  reset!
end

class MyPersons < Persons
  add_person 'Dr.', 'Bob'
  add_person 'Mr.', 'Jones'
end

Upvotes: 0

Views: 413

Answers (2)

user672118
user672118

Reputation:

After a good nights sleep and some more Googling, I have come up with the answer. There appears to be a #inherited method in Ruby; that is called when a class is inherited (duh).

Actually, this is how Sinatra implements instance variables in Sinatra::Base.

class Persons
  class << self
    def reset!
      @persons = []
    end

    def add_person title, name
      @persons << {
        title: title,
        name: name
      }
    end

    def inherited child
      child.reset!
    end
  end
end

class MyPersons < Persons
  add_person 'Dr.', 'Bob'
  add_person 'Mr.', 'Jones'
end

Upvotes: 0

Ryan Ahearn
Ryan Ahearn

Reputation: 7934

You are never initializing @persons to anything other than nil. A simple fix would be

class MyPersons < Persons
  reset!
  add_person 'Dr.', 'Bob'
  add_person 'Mr.', 'Jones'
end

The reason your call to reset! doesn't work is because MyPersons and Persons do not share the same @persons variable.

You can use @@persons instead to share the variable. Your example would look like this instead:

class Persons
  @@persons = []
  class << self
    def reset!
      @@persons = []
    end

    def add_person title, name
      @@persons << { title: title, name: name }
    end
  end
end

class MyPersons < Persons
  add_person 'Dr.', 'Bob'
  add_person 'Mr.', 'Jones'
end

Upvotes: 1

Related Questions