File: formatter.rb

package info (click to toggle)
ruby-journey 1.0.4-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid, trixie
  • size: 288 kB
  • sloc: ruby: 2,830; javascript: 113; yacc: 42; makefile: 2
file content (131 lines) | stat: -rw-r--r-- 3,086 bytes parent folder | download | duplicates (2)
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
module Journey
  ###
  # The Formatter class is used for formatting URLs.  For example, parameters
  # passed to +url_for+ in rails will eventually call Formatter#generate
  class Formatter
    attr_reader :routes

    def initialize routes
      @routes = routes
      @cache  = nil
    end

    def generate key, name, options, recall = {}, parameterize = nil
      constraints = recall.merge options

      match_route(name, constraints) do |route|
        data = constraints.dup

        keys_to_keep = route.parts.reverse.drop_while { |part|
          !options.key?(part) || (options[part] || recall[part]).nil?
        } | route.required_parts

        (data.keys - keys_to_keep).each do |bad_key|
          data.delete bad_key
        end

        parameterized_parts = data.dup

        if parameterize
          parameterized_parts.each do |k,v|
            parameterized_parts[k] = parameterize.call(k, v)
          end
        end

        parameterized_parts.keep_if { |_,v| v  }

        next if !name && route.requirements.empty? && route.parts.empty?

        next unless verify_required_parts!(route, parameterized_parts)

        z = Hash[options.to_a - data.to_a - route.defaults.to_a]

        return [route.format(parameterized_parts), z]
      end

      raise Router::RoutingError
    end

    def clear
      @cache = nil
    end

    private
    def named_routes
      routes.named_routes
    end

    def match_route name, options
      if named_routes.key? name
        yield named_routes[name]
      else
        #routes = possibles(@cache, options.to_a)
        routes = non_recursive(cache, options.to_a)

        hash = routes.group_by { |_, r|
          r.score options
        }

        hash.keys.sort.reverse_each do |score|
          next if score < 0

          hash[score].sort_by { |i,_| i }.each do |_,route|
            yield route
          end
        end
      end
    end

    def non_recursive cache, options
      routes = []
      stack  = [cache]

      while stack.any?
        c = stack.shift
        routes.concat c[:___routes] if c.key? :___routes

        options.each do |pair|
          stack << c[pair] if c.key? pair
        end
      end

      routes
    end

    def possibles cache, options, depth = 0
      cache.fetch(:___routes) { [] } + options.find_all { |pair|
        cache.key? pair
      }.map { |pair|
        possibles(cache[pair], options, depth + 1)
      }.flatten(1)
    end

    def verify_required_parts! route, parts
      tests = route.path.requirements
      route.required_parts.all? { |key|
        if tests.key? key
          /\A#{tests[key]}\Z/ === parts[key]
        else
          parts.fetch(key) { false }
        end
      }
    end

    def build_cache
      kash = {}
      routes.each_with_index do |route, i|
        money = kash
        route.required_defaults.each do |tuple|
          hash = (money[tuple] ||= {})
          money = hash
        end
        (money[:___routes] ||= []) << [i, route]
      end
      kash
    end

    def cache
      @cache ||= build_cache
    end
  end
end