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
nobody? hehe
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
Nice man thanks!
Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.