Skip to main content

caller

Ruby • Asked by roberto santelices

I have trouble solving the following excercise, i was wondering if there is any way in ruby where i can get which method called which i think caller but i havent figured out yet

require_relative 'game'
class Game::DSL
  MOVE_DISTANCE = 10
  WIN_DISTANCE  = 100

  def initialize(game)
    @game = game
  end

  # TODO
  # Moving during a redlight will cause the game to end and lose
  def redlight!
  end

  # TODO
  # Moving during a greenlight will allow the player to move
  def greenlight!
  end

  # TODO
  # If called outside a #greenlight! or #redlight! nothing happens
  # Moves the player MOVE_DISTANCE
  # If MOVE_DISTANCE >= WIN_DISTANCE game ends and wins
  def move
  end
end
class Game
  STATUS  = %i[standby playing finished]
  RESULTS = %i[won lost]

  attr_accessor :result, :distance

  def self.play(&block)
    new.play(&block)
  end

  def initialize
    @status   = :standby
    @distance = 0
    @dsl = Game::DSL.new(self)
  end

  # TODO Bonus!
  # Change this so the game truly acts like a DSL
  def play
    catch :finish! do
      start!
      yield @dsl
      finish!
    end
    self
  end

  def start!
    @status = :playing
  end

  # NOTE Call me from Game::DSL to end the game!
  def finish!
    @status = :finished
    freeze
    throw :finish!
  end

  # Defines #result=
  # Ensures you don't typo out of the valid results
  def result=(value)
    raise "'#{value}' is not a valid result. (Check for Typos)" unless RESULTS.include? value
    @result = value
  end

  # Defines #standby? #playing? #finished?
  STATUS.each do |status|
    define_method("#{status}?") { @status == status }
  end

  # Defines #won? #lost?
  RESULTS.each do |result|
    define_method "#{result}?" do
      raise "##{__method__} called before game finished!" unless finished?
      @result == result
    end
  end
end

I put comments in it



Hey Roberto,

Could you clarify exactly what it is you're trying to accomplish? I'm not sure what you mean by which method called which caller? What problem are you trying to solve by knowing this information?


Jacob thanks for taking a look, im trying to make that if i call the method move insight the method redligt then it makes the status lose


Hey Roberto,

From what I can tell (and from my extremely limited knowledge in the DSL area), I believe what you'll want to do is have redlight! and greenlight! set a variable when they're called. Then in your move method, you'll check the light status to determine if it can move or not. If it attempted to move and @redlight == true, then you can set the result to lost and call finish!

Do you by chance have more of this exercise completed that you could share?


i dont have more code but i do have the spec which needs to pass, i havent been able to resolve it yet

require_relative 'game'
require_relative 'dsl'
describe Game do
  context '#move' do
    it 'should not move unless called within a light' do
      game = Game.play do |g|
        g ||= self
        g.move
      end
      expect(game.distance).to eq 0a
    end
  end

  context '#redlight!' do
    it 'should do nothing if move was not called' do
      game = Game.play do |g|
        g ||= self
        g.redlight! {}
      end
      expect(game.result).to be_nil
    end

    it 'should finish and lose the game if move is called' do
      game = Game.play do |g|
        g ||= self
        g.redlight! do
          g.move
        end
      end
      expect(game.lost?).to be true
      expect(game.distance).to eq 0
    end
  end

  context '#greenlight!' do
    it 'should move if move is called' do
      game = Game.play do |g|
        g ||= self
        g.greenlight! do
          g.move
        end
      end
      expect(game.distance).to eq 10
    end

    it 'should finish and win the game if move is called enough times' do
      game = Game.play do |g|
        g ||= self
        g.greenlight! do
          11.times do
            g.move
          end
        end
      end

      expect(game.won?).to be true
      expect(game.distance).to eq 100
    end
  end

  context 'bonus' do
    it 'should be a true dsl' do
      begin
        game = Game.play do
          greenlight! do
            3.times { move }
          end

          redlight! {}

          greenlight! do
            2.times {move}
          end

          redlight! {}

          greenlight! do
            3.times {move}
          end

          greenlight! do
            2.times {move}
          end
        end
        expect(game.won?).to be true
      rescue NoMethodError
        # fail
      end
    end
  end
end

Well, here's what I came up with, althought it doesn't feel quite right but hey, all tests pass! :)

require_relative 'game'
class Game::DSL
  MOVE_DISTANCE = 10
  WIN_DISTANCE  = 100

  def initialize(game)
    @game = game
  end

  # TODO
  # Moving during a redlight will cause the game to end and lose
  def redlight!
    yield
  end

  # TODO
  # Moving during a greenlight will allow the player to move
  def greenlight!
    yield
  end

  # TODO
  # If called outside a #greenlight! or #redlight! nothing happens
  # Moves the player MOVE_DISTANCE
  # If MOVE_DISTANCE >= WIN_DISTANCE game ends and wins
  def move
    greenlight = caller_locations.map(&:label).include?("greenlight!")
    redlight   = caller_locations.map(&:label).include?("redlight!")

    if greenlight
      @game.distance += MOVE_DISTANCE
      @game.result = :won if @game.distance >= WIN_DISTANCE
      @game.finish! if @game.result == :won
    elsif redlight
      @game.result = :lost if redlight
      @game.finish!
    else
      # do nothing
    end
  end
end

Originally I had the redlight! and greenlight! methods defining a variable that I checked in the move method... then I came across caller_locations which you can check the labels, which basically equate into method names in the chain (I think).

Check: https://www.lucascaton.com.br/2016/11/04/ruby-how-to-get-the-name-of-the-calling-method



Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 22,346+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.