File: decorated_route.rb

package info (click to toggle)
ruby-grape-path-helpers 1.7.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 192 kB
  • sloc: ruby: 814; makefile: 3
file content (181 lines) | stat: -rw-r--r-- 4,557 bytes parent folder | download
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
module GrapePathHelpers
  # wrapper around Grape::Route that adds a helper method
  class DecoratedRoute
    attr_reader :route, :helper_names, :helper_arguments,
                :extension, :route_options

    PATH_SEGMENTS_REGEXP = %r{\(/?\.:?\w+\)|/(?!\))|(?<=\))|\??\*}
    PATH_SEGMENTS_WITH_WILDCARDS_REGEXP = %r{\(/?\.:?\w+\)|/(?!\))|(?<=\))|\?}

    def self.sanitize_method_name(string)
      string.gsub(/\W|^[0-9]/, '_')
    end

    def initialize(route)
      @route = route
      @route_options = route.options
      @helper_names = []
      @helper_arguments = required_helper_segments
      @extension = default_extension
      define_path_helpers
    end

    def default_extension
      pattern = /\((\.\:?\w+)\)$/
      match = route_path.match(pattern)
      return '' unless match
      ext = match.captures.first
      if ext == '.:format'
        ''
      else
        ext
      end
    end

    def define_path_helpers
      route_versions.each do |version|
        route_attributes = { version: version, format: extension }
        method_name = path_helper_name(route_attributes)
        @helper_names << method_name
        define_path_helper(method_name, route_attributes)
      end
    end

    def define_path_helper(method_name, route_attributes)
      method_body = <<-RUBY
        def #{method_name}(attributes = {}, include_wildcard_segments = false)
          attrs = #{route_attributes}.merge(attributes)

          query_params = attrs.delete(:params)
          content_type = attrs.delete(:format)
          path = '/' + path_segments_with_values(attrs, include_wildcard_segments).join('/')

          path + content_type + query_string(query_params)
        end
      RUBY
      instance_eval method_body
    end

    def query_string(params)
      if params.nil?
        ''
      else
        '?' + params.to_param
      end
    end

    def route_versions
      return [nil] if route_version.nil? || route_version.empty?

      if route_version.is_a?(String)
        version_pattern = /[^\[",\]\s]+/
        route_version.scan(version_pattern)
      else
        route_version
      end
    end

    def path_helper_name(opts = {})
      if route_options[:as]
        name = route_options[:as].to_s
      else
        segments = path_segments_with_values(opts)

        name = if segments.empty?
                 'root'
               else
                 segments.join('_')
               end
      end

      sanitized_name = self.class.sanitize_method_name(name)
      sanitized_name + '_path'
    end

    def segment_to_value(segment, opts = {})
      if dynamic_segment?(segment)
        options = route.options.merge(stringify_keys(opts))
        key = segment.slice(1..-1).to_sym
        options[key]
      else
        segment
      end
    end

    def path_segments_with_values(opts, include_wildcard_segments = false)
      segments = path_segments(include_wildcard_segments).map do |s|
        segment_to_value(s, opts)
      end
      segments.reject(&:blank?)
    end

    def path_segments(include_wildcard_segments = false)
      pattern = if include_wildcard_segments
                  PATH_SEGMENTS_WITH_WILDCARDS_REGEXP
                else
                  PATH_SEGMENTS_REGEXP
                end
      route_path.split(pattern).reject(&:blank?)
    end

    def dynamic_path_segments
      segments = path_segments.select do |segment|
        dynamic_segment?(segment)
      end
      segments.map { |s| s.slice(1..-1) }
    end

    def dynamic_segment?(segment)
      segment.start_with?(':', '*')
    end

    def optional_segment?(segment)
      segment.start_with?('(')
    end

    def required_helper_segments
      segments_in_options = dynamic_path_segments.select do |segment|
        route.options[segment.to_sym]
      end
      dynamic_path_segments - segments_in_options
    end

    def special_keys
      %w[format params]
    end

    def uses_segments_in_path_helper?(segments)
      segments = segments.reject { |x| special_keys.include?(x) }

      if helper_arguments.empty? && segments.any?
        false
      else
        helper_arguments.all? { |x| segments.include?(x) }
      end
    end

    def route_path
      route.path
    end

    def route_version
      route.version
    end

    def route_namespace
      route.namespace
    end

    def route_method
      route.request_method
    end

    private

    def stringify_keys(original)
      original.each_with_object({}) do |(key, value), hash|
        hash[key.to_sym] = value
      end
    end
  end
end