caller
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