require File.dirname(__FILE__) + '/../../spec_helper'

describe Merb::Router::Route, "initially" do
  predicate_matchers[:allow_fixation] = :allow_fixation?

  before :each do
    Merb::Router.prepare do |r|
      r.resources :continents do |c|
        c.resources :countries
      end
    end

    @r1 = Merb::Router.routes.first
    @r5 = Merb::Router.routes[4]
  end

  after :each do
    Merb::Router.reset!
  end

  it "has reader for conditions" do
    @r1.conditions
  end

  it "has reader for params" do
    @r1.params
  end

  it "has reader for behavior" do
    @r5.behavior
  end

  it "has reader for conditional block" do
    @r5.conditional_block
  end

  it "has reader for segments" do
    @r5.segments
  end

  it "has reader for index" do
    @r1.index.should == 0
  end

  it "has reader for symbol" do
    @r1.symbol
  end

  it "does not allow fixation" do
    @r1.should_not allow_fixation
  end
end

describe Merb::Router::Route, "#fixatable" do
  predicate_matchers[:allow_fixation] = :allow_fixation?

  before :each do
    Merb::Router.prepare do |r|
      r.resources :continents do |c|
        c.resources :countries
      end
    end

    @route = Merb::Router.routes[4]
  end
  
  after :each do
    Merb::Router.reset!
  end

  it "allows fixation when called with true" do
    @route.fixatable(true)

    @route.should allow_fixation
  end

  it "allows fixation when called with true" do
    @route.fixatable(true)
    @route.should allow_fixation

    @route.fixatable(false)
    @route.should_not allow_fixation
  end

  it "allows fixation when called with default argument" do
    @route.fixatable

    @route.should allow_fixation
  end
end



describe Merb::Router::Route, "#to_s" do
  before :each do
    Merb::Router.prepare do |r|
      r.resources :continents do |c|
        c.resources :countries
      end
    end

    @route = Merb::Router.routes[4]
  end
  
  after :each do
    Merb::Router.reset!
  end

  it "concatenates route segments" do
    @route.stub!(:segments).and_return(["continents/", "new"])
    @route.to_s.should == "continents/new"
  end

  it "prefixes symbol segments with colon" do
    @route.stub!(:segments).and_return(["continents/", :id])
    @route.to_s.should == "continents/:id"
  end
end



describe Merb::Router::Route, "#register" do
  before :each do
    Merb::Router.prepare do |r|
      r.resources :continents do |c|
        c.resources :countries
      end
    end

    @route = Merb::Router.routes[4]
  end
  
  after :each do
    Merb::Router.reset!
  end

  it "adds route to Merb routes set" do
    Merb::Router.routes = []
    @route.register

    Merb::Router.routes.should include(@route)
  end

  it "sets index on route" do
    Merb::Router.routes = []
    @route.register

    @route.index.should == 0
  end

  it "returns route itself" do
    Merb::Router.routes = []
    @route.register.should == @route
  end
end



describe Merb::Router::Route, "#symbol_segments" do
  before :each do
    Merb::Router.prepare do |r|
      r.resources :continents do |c|
        c.resources :countries
      end
    end

    @route = Merb::Router.routes[4]
  end

  after :each do
    Merb::Router.reset!
  end

  it "cherrypicks segments that are Symbols" do
    @route.stub!(:segments).and_return(["prefix/", :controller, "ping"])
    @route.symbol_segments.should == [:controller]
  end
end



describe Merb::Router::Route, "#segments_from_path" do
  before :each do
    Merb::Router.prepare do |r|
      r.resources :continents do |c|
        c.resources :countries
      end
    end

    @route = Merb::Router.routes[4]
  end

  after :each do
    Merb::Router.reset!
  end

  it "turns path into string and symbol segments" do
    @route.segments_from_path("prefix/:controller/:action/:id").should == ["prefix/", :controller, "/", :action, "/", :id]
  end

  it "handles slash edge case just fine" do
    @route.segments_from_path("/").should == ["/"]
  end
end



describe Merb::Router::Route, "#name" do
  before :each do
    Merb::Router.prepare do |r|
      r.match("/").to(:controller => "home")
    end

    @route = Merb::Router.routes.first
  end

  after :each do
    Merb::Router.reset!
  end

  it "places the route into named routes collection" do
    @route.name(:home)

    Merb::Router.named_routes[:home].should == @route
  end

  it "only accepts Symbols" do
    lambda { @route.name("home") }.should raise_error(ArgumentError)
  end
  
end


describe Merb::Router::Route, "#regexp?" do
  before :each do
    Merb::Router.prepare do |r|
      r.match(/api\/(.*)/).to(:controller => "api", :token => "[1]").name(:regexpy)

      r.match("/what-is-regexps-dude").to(:controller => "plain_strings").name(:non_regexpy)
    end

    @regexp_route   = Merb::Router.named_routes[:regexpy]
    @non_regexp_route = Merb::Router.named_routes[:non_regexpy]
  end

  it "is true for routes that use regular expressions" do
    @regexp_route.should be_regexp
  end

  it "is false for routes that do not explicitly use regular expressions" do
    @non_regexp_route.should_not be_regexp
  end

  it "is true if behavior returns true when sent regexp?" do
    @non_regexp_route.stub!(:behavior).and_return(stub("regexp_behavior", :regexp? => true))
    @non_regexp_route.should be_regexp
  end

  it "is true if any of behavior's anscestors return true when sent regexp?" do
    @regexp_ancestor = stub("regexp_ancestor", :regexp? => true)
    @not_regexp_ancestor = stub("not_regexp_ancestor", :regexp? => false)
    @behavior_with_ancestors = stub("regexp_behavior", :regexp? => false, :ancestors => [@regexp_ancestor, @not_regexp_ancestor])

    @non_regexp_route.stub!(:behavior).and_return(@behavior_with_ancestors)
    @non_regexp_route.should be_regexp
  end

  after :each do
    Merb::Router.reset!
  end
end



describe Merb::Router::Route, "#generate" do
  before :each do
    Merb::Router.prepare do |r|
      r.match(/api\/(.*)/).to(:controller => "api", :token => "[1]").name(:regexpy)
      r.match("/world/countries/:name").to(:controller => "countries").name(:non_regexpy)
    end

    @regexp_route   = Merb::Router.named_routes[:regexpy]
    @non_regexp_route = Merb::Router.named_routes[:non_regexpy]
  end
  
  after :each do
    Merb::Router.reset!
  end

  it "does not work for regexp routes" do
    lambda { @regexp_route.generate({ :token => "apitoken" }) }.should raise_error(RuntimeError, /regexp/)
  end

  it "replaces symbol segments in the URL with values from given Hash parameters" do
     @non_regexp_route.generate({ :name => "Italy" }).should == "/world/countries/Italy"
    @non_regexp_route.generate({ :name => "Brazil" }).should == "/world/countries/Brazil"
  end

  it "replaces symbol segments with Fixnum  values just fine" do
    @non_regexp_route.generate({ :name => 101 }).should == "/world/countries/101"
  end

  it "appends unknown symbol segments after ?" do
    @non_regexp_route.generate({ :name => 101, :area => 10101 }).should == "/world/countries/101?area=10101"
  end

  it "calls #to_param on segments that respond to it" do
    @non_regexp_route.generate({ :name => stub('US', :to_param => 'USA') }).should == "/world/countries/USA"
  end
end



describe Merb::Router::Route, "#if_conditions" do
  before :each do
    Merb::Router.prepare do |r|
      r.match(/api\/(.*)/).to(:controller => "api", :token => "[1]").name(:regexpy)
      r.match("/world/countries/:name").to(:controller => "countries").name(:non_regexpy)
      r.match("/world/continents/:continent/countries/:country").to(:controller => "world_map").name(:two_symbol_segments)
    end

    @regexp_route   = Merb::Router.named_routes[:regexpy]
    @non_regexp_route = Merb::Router.named_routes[:non_regexpy]
    @two_symbol_route = Merb::Router.named_routes[:two_symbol_segments]
  end
  
  after :each do
    Merb::Router.reset!
  end

  it "returns array with =~ statements" do
    # just to show you what it looks like
    @non_regexp_route.if_conditions({}).should == [" (/^\\/world\\/countries\\/([^\\/.,;?]+)$/ =~ cached_path)  && (path1 = $1)"]
  end

  it "returns array of syntactically valid Ruby code statements that can be evaluated" do
    cached_path = "/world/countries/Italy"
    eval(@non_regexp_route.if_conditions({}).first)
    # raises nothing and passes
  end

  it "works with regexp routes" do
    cached_path = "/api/signin"
    eval(@regexp_route.if_conditions({}).first)
    # raises nothing and passes
  end

  it "adds conditions and match group for each symbol segment of route" do
    # again, just to show you what it looks like
    @two_symbol_route.if_conditions({}).should == [" (/^\\/world\\/continents\\/([^\\/.,;?]+)\\/countries\\/([^\\/.,;?]+)$/ =~ cached_path)  && (path1, path2 = $1, $2)"]
  end
end

describe Merb::Router::Route, "#if_conditions" do
  before :each do
    Merb::Router.prepare do |r|
      r.match(/api\/(.*)/).to(:controller => "api", :token => "[1]").name(:regexpy)
      r.match("/world/countries/:name").to(:controller => "countries").name(:non_regexpy)
      r.match("/world/continents/:continent/countries/:country").to(:controller => "world_map").name(:two_symbol_segments)
    end

    @regexp_route   = Merb::Router.named_routes[:regexpy]
    @non_regexp_route = Merb::Router.named_routes[:non_regexpy]
    @two_symbol_route = Merb::Router.named_routes[:two_symbol_segments]
  end
  
  after :each do
    Merb::Router.reset!
  end

  it "uses if in compiled statement when argument is false" do
    @regexp_route.compile(false).should =~ /\s*elsif/
  end

  it "uses elsif in compiled statement when argument is true" do
    @non_regexp_route.compile(true).should =~ /^if/
  end

  it "uses if in compiled statement by default" do
    @regexp_route.compile.should =~ /\s*elsif/
  end
end

describe "Merb::Router::Route.capture" do
  
  after :each do
    Merb::Router.reset!
  end  
  
  it "should capture routes that have been added in the block context" do
    routes, named, route = [], {}, nil
    Merb::Router.prepare do |r|
      routes, named = Merb::Router.capture do
        route = r.match("/overview").to(:controller => "home", :action => 'overview')
      end
      r.match("/").to(:controller => "home").name(:home)
    end
    routes.should include(route)
    named.should_not include(route)
  end
  
  it "should capture named routes that have been added in the block context" do
    routes, named, route = [], {}, nil
    Merb::Router.prepare do |r|
      r.match("/overview").to(:controller => "home", :action => 'overview')
      routes, named = Merb::Router.capture do
        route = r.match("/").to(:controller => "home").name(:home)
      end
    end
    routes.should include(route)
    named[:home].should == route
  end
  
end
