This file is indexed.

/usr/lib/ruby/vendor_ruby/innate/node.rb is in ruby-innate 2013.02.21-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
module Innate

  # The nervous system of {Innate}, so you can relax.
  #
  # Node may be included into any class to make it a valid responder to
  # requests.
  #
  # The major difference between this and the old Ramaze controller is that
  # every Node acts as a standalone application with its own dispatcher.
  #
  # What's also an important difference is the fact that {Node} is a module, so
  # we don't have to spend a lot of time designing the perfect subclassing
  # scheme.
  #
  # This makes dispatching more fun, avoids a lot of processing that is done by
  # Rack anyway and lets you tailor your application down to the last action
  # exactly the way you want without worrying about side-effects to other
  # {Node}s.
  #
  # Upon inclusion, it will also include {Innate::Trinity} and {Innate::Helper}
  # to provide you with {Innate::Request}, {Innate::Response},
  # {Innate::Session} instances, and all the standard helper methods as well as
  # the ability to simply add other helpers.
  #
  # Please note that method_missing will _not_ be considered when building an
  # {Action}. There might be future demand for this, but for now you can simply
  # use `def index(*args); end` to make a catch-all action.
  module Node
    include Traited

    attr_reader :method_arities, :layout_templates, :view_templates

    NODE_LIST = Set.new

    # These traits are inherited into ancestors, changing a trait in an
    # ancestor doesn't affect the higher ones.
    #
    #   class Foo; include Innate::Node; end
    #   class Bar < Foo; end
    #
    #   Foo.trait[:wrap] == Bar.trait[:wrap] # => true
    #   Bar.trait(:wrap => [:cache_wrap])
    #   Foo.trait[:wrap] == Bar.trait[:wrap] # => false

    trait :views          => []
    trait :layouts        => []
    trait :layout         => nil
    trait :alias_view     => {}
    trait :provide        => {}
    trait :fast_mappings  => false

    # Caching related traits.
    trait :cache_provides => false
    trait :provides_cache => LRUHash.new(:max_count => 100)

    trait :cache_method_arities => false
    trait :method_arity_cache   => LRUHash.new(:max_count => 100)

    # @see wrap_action_call
    trait :wrap           => SortedSet.new
    trait :provide_set    => false
    trait :needs_method   => false
    trait :skip_node_map  => false

    # @see patterns_for
    trait :separate_default_action => false
    trait :default_action_name => 'index'

    # Upon inclusion we make ourselves comfortable.
    def self.included(into)
      into.__send__(:include, Helper)
      into.extend(Trinity, self)

      NODE_LIST << into

      return if into.provide_set?
      into.provide(:html, :engine => :Etanni)
      into.trait(:provide_set => false)
    end

    # node mapping procedure
    #
    # when Node is included into an object, it's added to NODE_LIST
    # when object::map(location) is sent, it maps the object into DynaMap
    # when Innate.start is issued, it calls Node::setup
    # Node::setup iterates NODE_LIST and maps all objects not in DynaMap by
    # using Node::generate_mapping(object.name) as location
    #
    # when object::map(nil) is sent, the object will be skipped in Node::setup

    def self.setup
      NODE_LIST.each{|node|
        node.map(generate_mapping(node.name)) unless node.trait[:skip_node_map]
      }
    end

    def self.generate_mapping(object_name = self.name)
      return '/' if NODE_LIST.size == 1
      parts = object_name.split('::').map{|part|
        part.gsub(/^[A-Z]+/){|sub| sub.downcase }.gsub(/[A-Z]+[^A-Z]/, '_\&')
      }
      '/' << parts.join('/').downcase
    end

    # Tries to find the relative url that this {Node} is mapped to.
    # If it cannot find one it will instead generate one based on the
    # snake_cased name of itself.
    #
    # @example Usage:
    #
    #   class FooBar
    #     include Innate::Node
    #   end
    #   FooBar.mapping # => '/foo_bar'
    #
    # @return [String] the relative path to the node
    #
    # @api external
    # @see Innate::SingletonMethods#to
    # @author manveru
    def mapping
      Innate.to(self)
    end

    # Shortcut to map or remap this Node.
    #
    # @example Usage for explicit mapping:
    #
    #   class FooBar
    #     include Innate::Node
    #     map '/foo_bar'
    #   end
    #
    #   Innate.to(FooBar) # => '/foo_bar'
    #
    # @example Usage for automatic mapping:
    #
    #   class FooBar
    #     include Innate::Node
    #     map mapping
    #   end
    #
    #   Innate.to(FooBar) # => '/foo_bar'
    #
    # @param [#to_s] location
    #
    # @api external
    # @see Innate::SingletonMethods::map
    # @author manveru
    def map(location)
      trait :skip_node_map => true
      Innate.map(location, self)
    end

    # Specify which way contents are provided and processed.
    #
    # Use this to set a templating engine, custom Content-Type, or pass a block
    # to take over the processing of the {Action} and template yourself.
    #
    # Provides set via this method will be inherited into subclasses.
    #
    # The +format+ is extracted from the PATH_INFO, it simply represents the
    # last extension name in the path.
    #
    # The provide also has influence on the chosen templates for the {Action}.
    #
    # @example providing RSS with ERB templating
    #
    #   provide :rss, :engine => :ERB
    #
    # Given a request to `/list.rss` the template lookup first tries to find
    # `list.rss.erb`, if that fails it falls back to `list.erb`.
    # If neither of these are available it will try to use the return value of
    # the method in the {Action} as template.
    #
    # A request to `/list.yaml` would match the format 'yaml'
    #
    # @example providing a yaml version of actions
    #
    #   class Articles
    #     include Innate::Node
    #     map '/article'
    #
    #     provide(:yaml, :type => 'text/yaml'){|action, value| value.to_yaml }
    #
    #     def list
    #       @articles = Article.list
    #     end
    #   end
    #
    # @example providing plain text inspect version
    #
    #   class Articles
    #     include Innate::Node
    #     map '/article'
    #
    #     provide(:txt, :type => 'text/plain'){|action, value| value.inspect }
    #
    #     def list
    #       @articles = Article.list
    #     end
    #   end
    #
    # @param [Proc] block
    #   upon calling the action, [action, value] will be passed to it and its
    #   return value becomes the response body.
    #
    # @option param :engine [Symbol String]
    #   Name of an engine for View::get
    # @option param :type [String]
    #   default Content-Type if none was set in Response
    #
    # @raise [ArgumentError] if neither a block nor an engine was given
    #
    # @api external
    # @see View::get Node#provides
    # @author manveru
    #
    # @note
    #  If you specify a block when calling this method you'll have to take care
    #  of rendering views and the like yourself. If you merely want to set a
    #  extension and content type you can omit the block.
    #
    # @todo
    #   The comment of this method may be too short for the effects it has on
    #   the rest of Innate, if you feel something is missing please let me
    #   know.

    def provide(format, param = {}, &block)
      if param.respond_to?(:to_hash)
        param = param.to_hash
        handler = block || View.get(param[:engine])
        content_type = param[:type]
      else
        handler = View.get(param)
      end

      raise(ArgumentError, "Need an engine or block") unless handler

      trait("#{format}_handler"      => handler, :provide_set => true)
      trait("#{format}_content_type" => content_type) if content_type
    end

    ##
    # Returns the list of provide handlers. This list is cached after the first
    # call to this method.
    #
    # @return [Hash]
    #
    def provides
      if ancestral_trait[:cache_provides]
        return ancestral_trait[:provides_cache][self] ||= provide_handlers
      else
        return provide_handlers
      end
    end

    ##
    # @see Innate::Node#provides
    # @return [Hash]
    #
    def provide_handlers
      ancestral_trait.reject { |key, value| key !~ /_handler$/ }
    end

    # This makes the Node a valid application for Rack.
    # +env+ is the environment hash passed from the Rack::Handler
    #
    # We rely on correct PATH_INFO.
    #
    # As defined by the Rack spec, PATH_INFO may be empty if it wants the root
    # of the application, so we insert '/' to make our dispatcher simple.
    #
    # Innate will not rescue any errors for you or do any error handling, this
    # should be done by an underlying middleware.
    #
    # We do however log errors at some vital points in order to provide you
    # with feedback in your logs.
    #
    # A lot of functionality in here relies on the fact that call is executed
    # within Current#call which populates the variables used by Trinity.
    # So if you use the Node directly as a middleware make sure that you #use
    # Innate::Current as a middleware before it.
    #
    # @param [Hash] env
    #
    # @return [Array]
    #
    # @api external
    # @see Response#reset Node#try_resolve Session#flush
    # @author manveru

    def call(env)
      path = env['PATH_INFO']
      path << '/' if path.empty?

      response.reset
      try_resolve(path).finish
    end

    # Let's try to find some valid action for given +path+.
    # Otherwise we dispatch to {Innate::Node#action_missing}.
    #
    # @param [String] path from env['PATH_INFO']
    #
    # @return [Response]
    #
    # @api external
    # @see Node#resolve Node#action_found Node#action_missing
    # @author manveru
    def try_resolve(path)
      action = resolve(path)
      action ? action_found(action) : action_missing(path)
    end

    # Executed once an {Action} has been found.
    #
    # Reset the {Innate::Response} instance, catch :respond and :redirect.
    # {Action#call} has to return a String.
    #
    # @param [Action] action
    #
    # @return [Innate::Response]
    #
    # @api external
    # @see Action#call Innate::Response
    # @author manveru
    def action_found(action)
      response = catch(:respond){ catch(:redirect){ action.call }}

      unless response.respond_to?(:finish)
        self.response.write(response)
        response = self.response
      end

      response['Content-Type'] ||= action.options[:content_type]
      response
    end

    # The default handler in case no action was found, kind of method_missing.
    # Must modify the response in order to have any lasting effect.
    #
    # Reasoning:
    # * We are doing this is in order to avoid tons of special error handling
    #   code that would impact runtime and make the overall API more
    #   complicated.
    # * This cannot be a normal action is that methods defined in
    #   {Innate::Node} will never be considered for actions.
    #
    # To use a normal action with template do following:
    #
    # @example
    #
    #   class Hi
    #     include Innate::Node
    #     map '/'
    #
    #     def self.action_missing(path)
    #       return if path == '/not_found'
    #       # No normal action, runs on bare metal
    #       try_resolve('/not_found')
    #     end
    #
    #     def not_found
    #       # Normal action
    #       "Sorry, I do not exist"
    #     end
    #   end
    #
    # @param [String] path
    #
    # @api external
    # @see Innate::Response Node#try_resolve
    # @author manveru
    def action_missing(path)
      response = Current.response
      response.status = 404
      response['Content-Type'] = 'text/plain'
      response.write("No action found at: %p" % path)

      response
    end

    # Let's get down to business, first check if we got any wishes regarding
    # the representation from the client, otherwise we will assume he wants
    # html.
    #
    # @param [String] path
    # @param [Hash] options
    #
    # @return [nil, Action]
    #
    # @api external
    # @see Node::find_provide Node::update_method_arities Node::find_action
    # @author manveru
    def resolve(path, options = {})
      name, wish, engine = find_provide(path)
      node = (respond_to?(:ancestors) && respond_to?(:new)) ? self : self.class

      action = Action.create(
        :node    => node,
        :wish    => wish,
        :engine  => engine,
        :path    => path,
        :options => options
      )

      if !action.options.key?(:needs_method)
        action.options[:needs_method] = node.needs_method?
      end

      if content_type = node.ancestral_trait["#{wish}_content_type"]
        action.options[:content_type] = content_type
      end

      node.update_method_arities
      node.update_template_mappings
      node.fill_action(action, name)
    end

    # Resolve possible provides for the given +path+ from
    # {Innate::Node#provides}.
    #
    # @param [String] path
    #
    # @return [Array] with name, wish, engine
    #
    # @api internal
    # @see Node::provide Node::provides
    # @author manveru
    def find_provide(path)
      pr = provides

      name, wish, engine = path, 'html', pr['html_handler']

      pr.find do |key, value|
        key = key[/(.*)_handler$/, 1]
        next unless path =~ /^(.+)\.#{key}$/i
        name, wish, engine = $1, key, value
      end

      return name, wish, engine
    end

    # Now we're talking {Action}, we try to find a matching template and
    # method, if we can't find either we go to the next pattern, otherwise we
    # answer with an {Action} with everything we know so far about the demands
    # of the client.
    #
    # @param [String] given_name the name extracted from REQUEST_PATH
    # @return [Action, nil]
    #
    # @api internal
    # @see Node#find_method Node#find_view Node#find_layout Node#patterns_for
    #      Action#wish Action#merge!
    # @author manveru
    def fill_action(action, given_name)
      needs_method = action.options[:needs_method]
      wish = action.wish

      patterns_for(given_name) do |name, params|
        method = find_method(name, params)

        next unless method if needs_method
        next unless method if params.any?
        next unless (view = find_view(name, wish)) || method

        params.map!{|param| Rack::Utils.unescape(param) }

        action.merge!(:method => method, :view => view, :params => params,
                      :layout => find_layout(name, wish))
      end
    end

    # Try to find a suitable value for the layout. This may be a template or
    # the name of a method.
    #
    # If a layout could be found, an Array with two elements is returned, the
    # first indicating the kind of layout (:layout|:view|:method), the second
    # the found value, which may be a String or Symbol.
    #
    # @param [String] name
    # @param [String] wish
    #
    # @return [Array, nil]
    #
    # @api external
    # @see Node#to_layout Node#find_method Node#find_view
    # @author manveru
    #
    # @todo allow layouts combined of method and view... hairy :)
    def find_layout(name, wish)
      return unless layout = ancestral_trait[:layout]
      return unless layout = layout.call(name, wish) if layout.respond_to?(:call)

      if found = to_layout(layout, wish)
        [:layout, found]
      elsif found = find_view(layout, wish)
        [:view, found]
      elsif found = find_method(layout, [])
        [:method, found]
      end
    end

    # We check arity if possible, but will happily dispatch to any method that
    # has default parameters.
    # If you don't want your method to be responsible for messing up a request
    # you should think twice about the arguments you specify due to limitations
    # in Ruby.
    #
    # So if you want your method to take only one parameter which may have a
    # default value following will work fine:
    #
    #   def index(foo = "bar", *rest)
    #
    # But following will respond to /arg1/arg2 and then fail due to ArgumentError:
    #
    #   def index(foo = "bar")
    #
    # Here a glance at how parameters are expressed in arity:
    #
    #   def index(a)                  # => 1
    #   def index(a = :a)             # => -1
    #   def index(a, *r)              # => -2
    #   def index(a = :a, *r)         # => -1
    #
    #   def index(a, b)               # => 2
    #   def index(a, b, *r)           # => -3
    #   def index(a, b = :b)          # => -2
    #   def index(a, b = :b, *r)      # => -2
    #
    #   def index(a = :a, b = :b)     # => -1
    #   def index(a = :a, b = :b, *r) # => -1
    #
    # @param [String, Symbol] name
    # @param [Array]         params
    #
    # @return [String, Symbol]
    #
    # @api external
    # @see Node#fill_action Node#find_layout
    # @author manveru
    #
    # @todo Once 1.9 is mainstream we can use Method#parameters to do accurate
    #       prediction
    def find_method(name, params)
      return unless arity = method_arities[name.to_s]
      name if arity == params.size || arity < 0
    end

    # Answer with a hash, keys are method names, values are method arities.
    #
    # Note that this will be executed once for every request, once we have
    # settled things down a bit more we can switch to update based on Reloader
    # hooks and update once on startup.
    # However, that may cause problems with dynamically created methods, so
    # let's play it safe for now.
    #
    # @example
    #
    #   Hi.update_method_arities
    #   # => {'index' => 0, 'foo' => -1, 'bar' => 2}
    #
    # @api internal
    # @see Node#resolve
    # @return [Hash] mapping the name of the methods to their arity
    def update_method_arities
      if ancestral_trait[:cache_method_arities] \
      and ancestral_trait[:method_arity_cache][self]
        return ancestral_trait[:method_arity_cache][self]
      end

      @method_arities = {}

      exposed = ancestors & Helper::EXPOSE.to_a
      higher = ancestors.select{|ancestor| ancestor < Innate::Node }

      (higher + exposed).reverse_each do |ancestor|
        ancestor.public_instance_methods(false).each do |im|
          @method_arities[im.to_s] = ancestor.instance_method(im).arity
        end
      end

      if ancestral_trait[:cache_method_arities]
        ancestral_trait[:method_arity_cache][self] = @method_arities
      end

      @method_arities
    end

    # Try to find the best template for the given basename and wish and respect
    # aliased views.
    #
    # @param [#to_s] action_name
    # @param [#to_s] wish
    #
    # @return [String, nil] depending whether a template could be found
    #
    # @api external
    # @see Node#to_template Node#find_aliased_view
    # @author manveru
    def find_view(action_name, wish)
      aliased = find_aliased_view(action_name, wish)
      return aliased if aliased

      to_view(action_name, wish)
    end

    # Try to find the best template for the given basename and wish.
    #
    # This method is mostly here for symetry with {Innate::Node#to_layout} and
    # to allow you overriding the template lookup easily.
    #
    # @param [#to_s] action_name
    # @param [#to_s] wish
    #
    # @return [String, nil] depending whether a template could be found
    #
    # @api external
    # @see {Node#find_view} {Node#to_template} {Node#root_mappings}
    #      {Node#view_mappings} {Node#to_template}
    # @author manveru
    def to_view(action_name, wish)
      return unless files = view_templates[wish.to_s]
      files[action_name.to_s]
    end

    # Aliasing one view from another.
    # The aliases are inherited, and the optional third +node+ parameter
    # indicates the Node to take the view from.
    #
    # The argument order is identical with `alias` and `alias_method`, which
    # quite honestly confuses me, but at least we stay consistent.
    #
    # @example
    #   class Foo
    #     include Innate::Node
    #
    #     # Use the 'foo' view when calling 'bar'
    #     alias_view 'bar', 'foo'
    #
    #     # Use the 'foo' view from FooBar node when calling 'bar'
    #     alias_view 'bar', 'foo', FooBar
    #   end
    #
    # Note that the parameters have been simplified in comparision with
    # Ramaze::Controller::template where the second parameter may be a
    # Controller or the name of the template.  We take that now as an optional
    # third parameter.
    #
    # @param [#to_s]      to   view that should be replaced
    # @param [#to_s]      from view to use or Node.
    # @param [#nil?, Node] node optionally obtain view from this Node
    #
    # @api external
    # @see Node::find_aliased_view
    # @author manveru
    def alias_view(to, from, node = nil)
      trait[:alias_view] || trait(:alias_view => {})
      trait[:alias_view][to.to_s] = node ? [from.to_s, node] : from.to_s
    end

    # Resolve one level of aliasing for the given +action_name+ and +wish+.
    #
    # @param [String] action_name
    # @param [String] wish
    #
    # @return [nil, String] the absolute path to the aliased template or nil
    #
    # @api internal
    # @see Node::alias_view Node::find_view
    # @author manveru
    def find_aliased_view(action_name, wish)
      aliased_name, aliased_node = ancestral_trait[:alias_view][action_name]
      return unless aliased_name

      aliased_node ||= self
      aliased_node.update_view_mappings
      aliased_node.find_view(aliased_name, wish)
    end

    # Find the best matching action_name for the layout, if any.
    #
    # This is mostly an abstract method that you might find handy if you want
    # to do vastly different layout lookup.
    #
    # @param [String] action_name
    # @param [String] wish
    #
    # @return [nil, String] the absolute path to the template or nil
    #
    # @api external
    # @see {Node#to_template} {Node#root_mappings} {Node#layout_mappings}
    # @author manveru
    def to_layout(action_name, wish)
      return unless files = layout_templates[wish.to_s]
      files[action_name.to_s]
    end

    # Define a layout to use on this Node.
    #
    # A Node can only have one layout, although the template being chosen can
    # depend on {Innate::Node#provides}.
    #
    # @example
    #   layout :foo
    # @example
    #   layout do |name, wish|
    #     name == 'foo' ? 'dark' : 'bright'
    #   end
    # @example
    #   layout :foo do |name, wish|
    #     wish == 'html'
    #   end
    #
    # @param [String, #to_s] layout_name basename without extension of the
    #  layout to use
    # @param [Proc, #call] block called on every dispatch if no name given
    #
    # @return [Proc, String] The assigned name or block
    #
    # @api external
    # @see Node#find_layout Node#layout_paths Node#to_layout Node#app_layout
    # @author manveru
    def layout(layout_name = nil, &block)
      if layout_name and block
        # default name, but still check with block
        trait(:layout => lambda{|name, wish| layout_name.to_s if block.call(name, wish) })
      elsif layout_name
        # name of a method or template
        trait(:layout => layout_name.to_s)
      elsif block
        # call block every request with name and wish, returned value is name
        # of layout template or method
        trait(:layout => block)
      else
        # remove layout for this node
        trait(:layout => nil)
      end

      return ancestral_trait[:layout]
    end

    # The innate beauty in Nitro, Ramaze, and {Innate}.
    #
    # Will yield the name of the action and parameter for the action method in
    # order of significance.
    #
    #   def foo__bar # responds to /foo/bar
    #   def foo(bar) # also responds to /foo/bar
    #
    # But foo__bar takes precedence because it's more explicit.
    #
    # The last fallback will always be the index action with all of the path
    # turned into parameters.
    #
    # @example yielding possible combinations of action names and params
    #
    #   class Foo; include Innate::Node; map '/'; end
    #
    #   Foo.patterns_for('/'){|action, params| p action => params }
    #   # => {"index"=>[]}
    #
    #   Foo.patterns_for('/foo/bar'){|action, params| p action => params }
    #   # => {"foo__bar"=>[]}
    #   # => {"foo"=>["bar"]}
    #   # => {"index"=>["foo", "bar"]}
    #
    #   Foo.patterns_for('/foo/bar/baz'){|action, params| p action => params }
    #   # => {"foo__bar__baz"=>[]}
    #   # => {"foo__bar"=>["baz"]}
    #   # => {"foo"=>["bar", "baz"]}
    #   # => {"index"=>["foo", "bar", "baz"]}
    #
    # @param [String, #split] path usually the PATH_INFO
    #
    # @return [Action] it actually returns the first non-nil/false result of yield
    #
    # @api internal
    # @see Node#fill_action
    # @author manveru
    def patterns_for(path)
      default_action_name = ancestral_trait[:default_action_name]
      separate_default_action = ancestral_trait[:separate_default_action]

      atoms = path.split('/')
      atoms.delete('')
      result = nil
      atoms.size.downto(0) do |len|
        action_name = atoms[0...len].join('__')

        next if separate_default_action && action_name == default_action_name

        params = atoms[len..-1]

        action_name = default_action_name if action_name.empty? &&
          (separate_default_action || params != [default_action_name])

        return result if result = yield(action_name, params)
      end

      return nil
    end

    # Try to find a template at the given +path+ for +wish+.
    #
    # Since Innate supports multiple paths to templates the +path+ has to be an
    # Array that may be nested one level.
    #
    # @example Usage to find available templates
    #
    #   # This assumes following files:
    #   # view/foo.erb
    #   # view/bar.erb
    #   # view/bar.rss.erb
    #   # view/bar.yaml.erb
    #
    #   class FooBar
    #     Innate.node('/')
    #   end
    #
    #   FooBar.to_template(['.', 'view', '/', 'foo'], 'html')
    #   # => "./view/foo.erb"
    #   FooBar.to_template(['.', 'view', '/', 'foo'], 'yaml')
    #   # => "./view/foo.erb"
    #   FooBar.to_template(['.', 'view', '/', 'foo'], 'rss')
    #   # => "./view/foo.erb"
    #
    #   FooBar.to_template(['.', 'view', '/', 'bar'], 'html')
    #   # => "./view/bar.erb"
    #   FooBar.to_template(['.', 'view', '/', 'bar'], 'yaml')
    #   # => "./view/bar.yaml.erb"
    #   FooBar.to_template(['.', 'view', '/', 'bar'], 'rss')
    #   # => "./view/bar.rss.erb"
    #
    # @param [Array<Array<String>>, Array<String>] path
    #   array containing strings and nested (1 level) arrays containing strings
    # @param [String] wish
    #
    # @return [nil, String] relative path to the first template found
    #
    # @api external
    # @see Node#find_view Node#to_layout Node#find_aliased_view
    # @author manveru
    def to_template(path, wish)
      to_view(path, wish) || to_layout(path, wish)
    end

    def update_template_mappings
      update_view_mappings
      update_layout_mappings
    end

    def update_view_mappings
      if ancestral_trait[:fast_mappings]
        return @view_templates if @view_templates
      end

      paths = possible_paths_for(view_mappings)
      @view_templates = update_mapping_shared(paths)
    end

    def update_layout_mappings
      if ancestral_trait[:fast_mappings]
        return @layout_templates if @layout_templates
      end

      paths = possible_paths_for(layout_mappings)
      @layout_templates = update_mapping_shared(paths)
    end

    def update_mapping_shared(paths)
      mapping = {}
      paths.reject!{|path| !File.directory?(path) }

      provides.each do |wish_key, engine|
        wish = wish_key[/(.*)_handler/, 1]
        exts = possible_exts_for(wish)

        paths.reverse_each do |path|
          Find.find(path) do |file|
            exts.each do |ext|
              next unless file =~ ext

              case file.sub(path, '').gsub('/', '__')
              when /^(.*)\.(.*)\.(.*)$/
                action_name, wish_ext, engine_ext = $1, $2, $3
              when /^(.*)\.(.*)$/
                action_name, wish_ext, engine_ext = $1, wish, $2
              end

              mapping[wish_ext] ||= {}
              mapping[wish_ext][action_name] = file
            end
          end
        end
      end

      return mapping
    end

    # Answer with an array of possible paths in order of significance for
    # template lookup of the given +mappings+.
    #
    # @param [#map] mappings An array two Arrays of inner and outer directories.
    #
    # @return [Array]
    # @see update_view_mappings update_layout_mappings update_template_mappings
    # @author manveru
    def possible_paths_for(mappings)
      root_mappings.map{|root|
        mappings.first.map{|inner|
          mappings.last.map{|outer|
            ::File.join(root, inner, outer, '/') }}}.flatten
    end

    # Answer with an array of possible extensions in order of significance for
    # the given +wish+.
    #
    # @param [#to_s] wish the extension (no leading '.')
    #
    # @return [Array] list of exts valid for this +wish+
    #
    # @api internal
    # @see Node#to_template View::exts_of Node#provides
    # @author manveru
    def possible_exts_for(wish)
      pr = provides
      return unless engine = pr["#{wish}_handler"]
      View.exts_of(engine).map{|e_ext|
        [[*wish].map{|w_ext| /#{w_ext}\.#{e_ext}$/ }, /#{e_ext}$/]
      }.flatten
    end

    # For compatibility with new Kernel#binding behaviour in 1.9
    #
    # @return [Binding] binding of the instance being rendered.
    # @see Action#binding
    # @author manveru
    def binding; super end

    # make sure this is an Array and a new instance so modification on the
    # wrapping array doesn't affect the original option.
    # [*arr].object_id == arr.object_id if arr is an Array
    #
    # @return [Array] list of root directories
    #
    # @api external
    # @author manveru
    def root_mappings
      [*options.roots].flatten
    end

    # Set the paths for lookup below the Innate.options.views paths.
    #
    # @param [String, Array<String>] locations
    #   Any number of strings indicating the paths where view templates may be
    #   located, relative to Innate.options.roots/Innate.options.views
    #
    # @return [Node] self
    #
    # @api external
    # @see {Node#view_mappings}
    # @author manveru
    def map_views(*locations)
      trait :views => locations.flatten.uniq
      self
    end

    # Combine Innate.options.views with either the `ancestral_trait[:views]`
    # or the {Node#mapping} if the trait yields an empty Array.
    #
    # @return [Array<String>, Array<Array<String>>]
    #
    # @api external
    # @see {Node#map_views}
    # @author manveru
    def view_mappings
      paths = [*ancestral_trait[:views]]
      paths = [mapping] if paths.empty?

      [[*options.views].flatten, [*paths].flatten]
    end

    # Set the paths for lookup below the Innate.options.layouts paths.
    #
    # @param [String, Array<String>] locations
    #   Any number of strings indicating the paths where layout templates may
    #   be located, relative to Innate.options.roots/Innate.options.layouts
    #
    # @return [Node] self
    #
    # @api external
    # @see {Node#layout_mappings}
    # @author manveru
    def map_layouts(*locations)
      trait :layouts => locations.flatten.uniq
      self
    end

    # Combine Innate.options.layouts with either the `ancestral_trait[:layouts]`
    # or the {Node#mapping} if the trait yields an empty Array.
    #
    # @return [Array<String>, Array<Array<String>>]
    #
    # @api external
    # @see {Node#map_layouts}
    # @author manveru
    def layout_mappings
      paths = [*ancestral_trait[:layouts]]
      paths = ['/'] if paths.empty?

      [[*options.layouts].flatten, [*paths].flatten]
    end

    def options
      Innate.options
    end

    # Whether an {Action} can be built without a method.
    #
    # The default is to allow actions that use only a view template, but you
    # might want to turn this on, for example if you have partials in your view
    # directories.
    #
    # @example turning needs_method? on
    #
    #   class Foo
    #     Innate.node('/')
    #   end
    #
    #   Foo.needs_method? # => true
    #   Foo.trait :needs_method => false
    #   Foo.needs_method? # => false
    #
    # @return [true, false] (false)
    #
    # @api external
    # @see {Node#fill_action}
    # @author manveru
    def needs_method?
      ancestral_trait[:needs_method]
    end

    # This will return true if the only provides set are by
    # {Innate::Node.included}.
    #
    # The reasoning behind this is to determine whether the user has touched
    # the provides at all, in which case we will not override the provides in
    # subclasses.
    #
    # @return [true, false] (false)
    #
    # @api internal
    # @see {Node::included}
    # @author manveru
    def provide_set?
      ancestral_trait[:provide_set]
    end
  end

  module SingletonMethods
    # Convenience method to include the Node module into +node+ and map to a
    # +location+.
    #
    # @param [#to_s]    location where the node is mapped to
    # @param [Node, nil] node     the class that will be a node, will try to
    #                            look it up if not given
    #
    # @return [Class, Module]    the node argument or detected class will be
    #                            returned
    #
    # @api external
    # @see SingletonMethods::node_from_backtrace
    # @author manveru
    def node(location, node = nil)
      node ||= node_from_backtrace(caller)
      node.__send__(:include, Node)
      node.map(location)
      node
    end

    # Cheap hack that works reasonably well to avoid passing self all the time
    # to Innate::node
    # We simply search the file that Innate::node was called in for the first
    # class definition above the line that Innate::node was called and look up
    # the constant.
    # If there are any problems with this (filenames containing ':' or
    # metaprogramming) just pass the node parameter explicitly to Innate::node
    #
    # @param [Array<String>, #[]] backtrace
    #
    # @return [Class, Module]
    #
    # @api internal
    # @see SingletonMethods::node
    # @author manveru
    def node_from_backtrace(backtrace)
      filename, lineno = backtrace[0].split(':', 2)
      regexp = /^\s*class\s+(\S+)/
      File.readlines(filename)[0..lineno.to_i].reverse.find{|ln| ln =~ regexp }
      const_get($1)
    end
  end
end