require "reflexive/methods"

Rspec::Matchers.define :generate_methods do |expected|
  match do |actual|
    Reflexive::Methods.new(actual).all == eval(expected)
  end

  failure_message_for_should do |actual|
    require "pp"
    require "stringio"
    pp_out = StringIO.new
    PP.pp(Reflexive::Methods.new(actual).all, pp_out)
    "expected:\n#{ expected }\ngot:\n#{ pp_out.string }"
  end
end

describe Reflexive::Methods do
  describe "ModuleInclusionC" do
    module ModuleInclusionA
      def module_instance_from_A_WTF!
      end
    end

    module ModuleInclusionB
      include ModuleInclusionA

      def module_instance_from_B_WTF!
      end
    end

    module ModuleInclusionC
      include ModuleInclusionB
    end

    it "works" do
      ModuleInclusionC.should generate_methods(<<-METHODS_PP)
      [{"[M] ModuleInclusionB"=>
       {:instance=>{:public=>[:module_instance_from_B_WTF!]}}},
       {"[M] ModuleInclusionA"=>
       {:instance=>{:public=>[:module_instance_from_A_WTF!]}}}]
      METHODS_PP
    end
  end
  describe "Inheritance" do
    class InheritanceA
      def self.singleton_inherited_from_A_WTF!
      end
    end

    class InheritanceB < InheritanceA
    end

    class InheritanceC < InheritanceB
      def test
      end
    end
    
    it "works" do
      InheritanceC.should generate_methods(<<-METHODS_PP)
      [{"[C] InheritanceC"=>{:instance=>{:public=>[:test]}}},
       {"[C] InheritanceA"=>{:class=>{:public=>[:singleton_inherited_from_A_WTF!]}}}]
      METHODS_PP
    end
  end

  describe "SingletonAndInstance" do
    module M
      def instance_from_moduleWTF!
      end
    end

    module SM
      def singleton_from_moduleWTF!
      end
    end

    class SingletonAndInstanceTest
      include M
      extend SM

      def self.singletonWTF!
      end

      def instanceWTF!
      end
    end
    
    it "works" do
      SingletonAndInstanceTest.should generate_methods(<<-METHODS_PP)
      [{"[C] SingletonAndInstanceTest"=>
         {:class=>{:public=>[:singletonWTF!]},
          :instance=>{:public=>[:instanceWTF!]}}},
       {"S[M] SM"=>{:class=>{:public=>[:singleton_from_moduleWTF!]}}},
       {"[M] M"=>{:instance=>{:public=>[:instance_from_moduleWTF!]}}}]
      METHODS_PP
    end
  end

  describe "ExtendWithInstanceAndClass" do
    module ExtendWithInstanceAndClassM
      def self.singleton_WTF!
      end

      def instance_WTF!
      end
    end

    class ExtendWithInstanceAndClass
      extend ExtendWithInstanceAndClassM
    end

    it "works" do
      ExtendWithInstanceAndClass.should generate_methods(<<-METHODS_PP)
      [{"S[M] ExtendWithInstanceAndClassM"=>{:class=>{:public=>[:instance_WTF!]}}}]
      METHODS_PP
    end
  end

  describe "SingletonVisibility" do
    class SingletonVisibility
      def self.class_method
      end

      class << self

        def public_singleton_method
        end
        public :public_singleton_method

        def protected_singleton_method
        end
        protected :protected_singleton_method

        def private_singleton_method
        end
        private :private_singleton_method
      end
    end

    it "works" do
      SingletonVisibility.should generate_methods(<<-METHODS_PP)
      [{"[C] SingletonVisibility"=>
         {:class=>
           {:public=>[:class_method, :public_singleton_method],
            :protected=>[:protected_singleton_method],
            :private=>[:private_singleton_method]}}}]
      METHODS_PP
    end
  end

  describe "SingletonOverrides" do
    class SingletonOverridesA
      def self.overriden
        puts "A"
        # super
      end
    end

    module SingletonOverridesMB
      def overriden
        puts "  MB"
        super
      end
    end

    class SingletonOverridesB < SingletonOverridesA
      extend SingletonOverridesMB

      def self.overriden
      end
    end

    module SingletonOverridesMC
      def overriden
      end

      def singleton_WTF
      end
    end

    class SingletonOverridesC < SingletonOverridesB
      extend SingletonOverridesMC

      def self.class_WTF
      end

      def self.overriden
      end
    end
    
    it "works" do
      SingletonOverridesC.should generate_methods(<<-METHODS_PP)
        [{"[C] SingletonOverridesC"=>{:class=>{:public=>[:class_WTF, :overriden]}}},
         {"S[M] SingletonOverridesMC"=>
           {:class=>{:public=>[:overriden, :singleton_WTF]}}},
         {"[C] SingletonOverridesB"=>{:class=>{:public=>[:overriden]}}},
         {"S[M] SingletonOverridesMB"=>{:class=>{:public=>[:overriden]}}},
         {"[C] SingletonOverridesA"=>{:class=>{:public=>[:overriden]}}}]
      METHODS_PP
    end
  end
end