require 'spec_helper'

describe Petroglyph::Scope do

  it "takes a simple string value" do
    scope = Petroglyph::Scope.new
    scope.node :whatever => "nevermind"
    scope.value.should eq(:whatever => "nevermind")
  end

  it "merges in a hash" do
    tea = {:tea => {:temperature => 'hot', :type => 'wulong'}}

    scope = Petroglyph::Scope.new
    scope.merge tea
    scope.value.should eq({:tea => {:temperature => "hot", :type => 'wulong'}})
  end

  it "merges within a block" do
    scope = Petroglyph::Scope.new
    scope.node :whatever do
      merge(:stuff => {:no => :way})
    end

    scope.value.should eq({:whatever => {:stuff => {:no => :way}}})
  end

  it "lets you process what you merge in a block" do
    scope = Petroglyph::Scope.new
    scope.node :whatever do
      merge(10) do
        attributes :to_s
      end
    end

    scope.value.should eq({:whatever => {:to_s => '10'}})
  end

  it "handles sibling nodes" do
    scope = Petroglyph::Scope.new
    scope.node :whatever => "nevermind"
    scope.node :stuff => "awesome"

    scope.value.should eq({:whatever => "nevermind", :stuff => "awesome"})
  end

  it "handles sibling nodes as blocks" do
    scope = Petroglyph::Scope.new
    scope.node :whatever => "nevermind"
    scope.node :stuff do
      merge(:too => :cool)
    end

    scope.value.should eq({:whatever => "nevermind", :stuff => {:too => :cool}})
  end

  it "nests nodes" do
    scope = Petroglyph::Scope.new
    scope.node :whatever do
      node :stuff => "awesome"
    end

    scope.value.should eq({:whatever => {:stuff => 'awesome'}})
  end

  it "nests stuff arbitrarily deeply with complex values" do
    scope = Petroglyph::Scope.new
    scope.node :drink do
      node :tea do
        node :temperature do
          merge(:really => :hot)
        end
      end
    end

    scope.value.should eq({:drink => {:tea => {:temperature => {:really => :hot}}}})
  end

  it "uses regular ruby" do
    scope = Petroglyph::Scope.new
    scope.node :drink do
      if false
        "cold"
      else
        node(:tea) do
          merge(:temperature => "hot".upcase)
        end
      end
    end

    scope.value.should eq({:drink => {:tea => {:temperature => "HOT"}}})
  end

  context "with locals" do
    it "resolves values" do
      scope = Petroglyph::Scope.new(nil, {:thing => 'stuff'})

      scope.instance_eval do
        node :thing => thing
      end

      scope.value.should eq({:thing => 'stuff'})
    end
  end

  describe "within a context" do
    it "has access to methods" do
      context = Object.new
      def context.thing
        'stuff'
      end

      scope = Petroglyph::Scope.new(context)
      scope.instance_eval do
        node :thing => thing
      end
      scope.value.should eq({:thing => 'stuff'})
    end

    it "lets local variables take precedence over methods" do
      context = Object.new
      def context.thing
        'junk'
      end

      scope = Petroglyph::Scope.new(context, {:thing => 'stuff'})
      scope.instance_eval do
        node :thing => thing
      end
      scope.value.should eq({:thing => 'stuff'})
    end
  end

  describe "attributes" do
    it "evaluates on an object" do
      hal = OpenStruct.new(:name => 'HAL 9000', :temperament => 'psychotic', :garbage => 'junk')

      scope = Petroglyph::Scope.new
      scope.node :hal => hal do
        attributes :name, :temperament
      end

      scope.value.should eq({:hal => {:name => 'HAL 9000', :temperament => 'psychotic'}})
    end

    it "evaluates on a hash" do
      hal = {:name => 'HAL 9000', :temperament => 'psychotic', :garbage => 'junk'}

      scope = Petroglyph::Scope.new
      scope.node :hal => hal do
        attributes :name, :temperament
      end

      scope.value.should eq({:hal => {:name => 'HAL 9000', :temperament => 'psychotic'}})
    end
  end

  context "with a collection" do
    let(:tea) { OpenStruct.new(:type => 'tea', :temperature => 'hot') }
    let(:coffee) { OpenStruct.new(:type => 'coffee', :temperature => 'lukewarm') }
    let(:drinks) { [tea, coffee] }

    it "evaluates attributes" do
      scope = Petroglyph::Scope.new
      scope.collection :drinks => drinks do
        attributes :type, :temperature
      end

      scope.value.should eq({:drinks => [{:type => 'tea', :temperature => 'hot'}, {:type => 'coffee', :temperature => 'lukewarm'}]})
    end

    it "evaluates attributes on explicitly named items" do
      scope = Petroglyph::Scope.new
      scope.collection :drinks => drinks do |drink|
        node :drink do
          node :type => drink.type
        end
        node :prep => "Make water #{drink.temperature}."
      end

      scope.value.should eq({:drinks => [{:drink => {:type => 'tea'}, :prep => "Make water hot."}, {:drink => {:type => 'coffee'}, :prep => "Make water lukewarm."}]})
    end

    it "evaluates object attributes within a sub node" do
      scope = Petroglyph::Scope.new
      scope.collection :drinks => drinks do |drink|
        node :drink => drink do
          attributes :type
        end
        node :prep => "Make water #{drink.temperature}."
      end

      scope.value.should eq({:drinks => [{:drink => {:type => 'tea'}, :prep => "Make water hot."}, {:drink => {:type => 'coffee'}, :prep => "Make water lukewarm."}]})
    end

    it "evaluates an empty collection to an empty array" do
      scope = Petroglyph::Scope.new
      scope.collection :drinks => [] do |drink|
        node :drink => drink
      end

      scope.value.should eq({:drinks => []})
    end
  end

  context "with partials" do
    it "renders a partial" do
      Petroglyph.stub(:partial) { 'node :drink => "tea"' }

      scope = Petroglyph::Scope.new
      scope.node :partial do
        partial :the_partial
      end

      scope.value.should eq({:partial => {:drink => 'tea'}})
    end

    it "renders a partial with local variables" do
      Petroglyph.stub(:partial) { 'node :drink => drink' }

      scope = Petroglyph::Scope.new
      scope.node :partial do
        partial :the_partial, :drink => 'tea'
      end

      scope.value.should eq({:partial => {:drink => 'tea'}})
    end

    it "finds the partial" do
      scope = Petroglyph::Scope.new
      scope.file = "spec/fixtures/views/some_template.pg"
      scope.node :partial do
        partial :the_partial, :thing => 'stuff'
      end

      scope.value.should eq({:partial => {:thing => 'stuff'}})
    end

    it "finds the partial in a subdirectory" do
      scope = Petroglyph::Scope.new
      scope.file = "spec/fixtures/views/some_template.pg"
      scope.node :partial do
        partial :sub_partial, :thing => 'stuff'
      end

      scope.value.should eq({:partial => {:thing => 'stuff'}})
    end

  end
end