Осмо предизвикателство

  1. До колкото разбирам условието ето малко по-подробен пример какво трябва да прави Spy:

    s = Spy.new 'Spy'
    s.length         # => 3
    s.swapcase       # => 'sPY'
    s.length         # => 3
    s.calls          # => [:length, :swapcase, :length]
    s.unknown_method # хвърля Spy::Error: Spy::Error
    s[1]             # => 'p' 
    s.calls          # => [:length, :swapcase, :length, :[]]
    
    # s = Spy.new [1, 2, 3, 4]
    # s.map { |item| item + 1 }       # => [2, 3, 4, 5]
    

    само че имам няколко въпроса: 1. В масива върнат от Spy#calls има ли повтарящи се методи и съответно пази ли се реда на извикване на методите(както по-горе)? 2. Ако може примерен тест за въпросното изключение което трябва да се хвърли. 3. Ако подадения обект на Spy има вече метод calls какво се случва?

  2. @Иван:

    1. В масива Spy#calls се записва всяко извикване на метод, тъй че да, има повтарящи се методи и да, редът трябва да се пази.
    2. Не може. :-) След 2 задачи и 7 предизвикателства се предполага, че можете да сами да си напишете някакви прости тестове с RSpec.
    3. Ако подаденият обект вече има метод calls, то няма да можем да го достъпим, понеже Spy#calls ще го „скрие“. Тъпо или не, стори ни се по-уместно от това да измислим някакво „уникално“ име за метода, което да не застъпва евентуално съществуващи имена на методи.

    @Никола:

    s = Spy.new(Spy.new 'Call the pyro'); s.length # => 13 За другото виж този слайд, прочети малко тук и ако си отговориш сам на въпросите (и споделиш отговорите тук), ще получиш звездичка. :-)

  3. Ако предположим, че Spy.new(TestClass.new) трябва да се държи по същия начин, по който TestClass.new що се отнася до викане на method_missing :unknown_method , и TestClass има публичен method_missing ще се вика той с параметър :unknown_method и *args = []. Иначе ще се вика с параметър :method_missing и *args = [:unknown_method] (ако го няма предефиниран и private / protected съответно ще вика method_missing на суперкласа, който хвърля NoMethodError). Ако се вика със send ще е все едно е бил дефиниран public . Въпросът ми беше действително ли трябва да се държи така?

  4. @Никола, не съм сигурен, че те разбирам какво точно питаш и имам смътното усещане, че търсиш под вола теле. Не ми стана ясно кога кой дефиниран къде method_missing с какви аргументи ще се вика... :) Предлагам ти да не дълбаеш толкова в тази посока. Все пак, ако държиш, може да ни пишеш с повече детайли и примерен код на имейла.

  5. Съжалявам, не съм спал достатъчно от известно време и съзнанието ми е на спагети. Накратко имаме клас Test , който дефинира публичен method_missing, принтиращ "Some text". Ще се очаква ли Spy.new(Test.new).method_missing :ignore_me да принтира също "Some text". Това беше един от случаите, за които питах, надявам се да е по-ясно.

  6. Перфектно, мерси. Бях приел прекалено буквално, че викането на който и да е метод върху обект и същия обект, обгърнат от Spy, трябва да дава еднакви резултати (с изключение на calls).

  7. @Мария, "Разликата е, че инстанциите на Spy имат метод Spy#calls, който връща масив от имената на всички извикани върху него методи, на които съответства метод в подадената инстанция."

  8. @Георги, първоначално имах по-mindfuck тестове, това е опростена версия. Ще спра със спама и ще изчакам решенията да станат публични, после ако имам още въпроси ще говоря с колектива довечера, накрая ако остане нещо интересно ще го напиша.

  9. При писане на тестове, къде трябва да се дефинира инстанция като spied_thing? Така както е в момента се повтаря твърде мого. Ако я дефинирам извън it "" do не се вижда в техния скоуп, а ако я направя константа, не би следвало да я променям от String на Array. Как е най-добре да се структурира?

    describe "Spy" do
      STRING = 'Why so shy, Spy?'
      LIST = [-5, 5, 1, 0, 99]
      LAMBDA = lambda { |n| n }
      it "uses the correct methods" do
        spied_thing = Spy.new STRING
        spied_thing.length.should eq STRING.length
      end
      it "passes method argument" do
        spied_thing = Spy.new STRING
        spied_thing.count('y').should eq STRING.count('y')
      end
      it "uses subscription" do
        spied_thing = Spy.new STRING
        spied_thing[2].should eq STRING[2]
      end
      it "tracks calls" do
        spied_thing = Spy.new STRING
        spied_thing.length
        spied_thing.count('y')
        spied_thing[2]
        spied_thing.calls.should eq [:length, :count, :[]]
      end
      it "takes multiple arguments" do
        spied_thing = Spy.new LIST
        spied_thing.insert(2,"3").should eq LIST.insert(2,"3")
      end
      it "takes keyword argument" do
        spied_thing = Spy.new LIST
        spied_thing.join(separator="x").should eq LIST.join(separator="x")
      end
      it "takes block" do
        class Lazy
          def foo(arg, &block)
            block.call("!#{arg}!")
          end
        end
        LAZINESS = Lazy.new
        spied_thing = Spy.new LAZINESS
        spied_thing.foo(STRING, &LAMBDA).should eq LAZINESS.foo(STRING, &LAMBDA)
      end
      it "gives Spy::Error when method is missing" do
        spied_thing = Spy.new LIST
        expect { spied_thing.no_such_thing }.to raise_error Spy::Error
      end
      it "tracks only successful calls" do
        spied_thing = Spy.new LIST
        spied_thing.insert(2,"3")
        spied_thing.join(separator="x")
        begin
          spied_thing.no_such_thing
        rescue Spy::Error => e
        end
        spied_thing.calls.should eq [:insert, :join]
      end
    end
    
  10. @Илиян, в let. Както каза и Мислав на лекцията във ФМИ, четенето на код помага много – може да прочетеш spec-овете на сайта на курса, например. Другият вариант е да прочетеш малко за RSpec, RTFM, както се казва :)

Трябва да сте влезли в системата, за да може да отговаряте на теми.