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