Class: Jets::Router::Route

Inherits:
Object
  • Object
show all
Includes:
Authorizer, Util
Defined in:
lib/jets/router/route.rb,
lib/jets/router/route/authorizer.rb

Defined Under Namespace

Modules: Authorizer

Constant Summary collapse

CAPTURE_REGEX =

as string

"([^/]*)"

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Authorizer

#api_key_required, #authorization_scopes, #authorization_type, #authorizer, #authorizer_id, #authorizer_metadata

Methods included from Authorizer::ModuleMethods

#logical_id, #metadata

Methods included from Util

#get_controller_action, #handle_on!, #join, #underscore

Constructor Details

#initialize(options, scope = Scope.new) ⇒ Route

Returns a new instance of Route.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/jets/router/route.rb', line 14

def initialize(options, scope=Scope.new)
  @options, @scope = options, scope
  @path = compute_path
  @to = compute_to
  @as = compute_as
  # Pretty tricky. The @options[:mount_class] is a class that is mounted.
  # For Grape apps, calling ActiveSupport to_json on a Grape class causes an infinite loop.
  # Can reproduce with `GrapeApp.to_json`
  # There's some type of collision between Grape and ActiveSupport to_json.
  # Coerce mount_class option into a string so that when the route is serialized to JSON
  # it is a string it won't cause an infinite loop. This allows the apigw routes to be
  # saved to s3 and loaded back up at the end of a deploy.
  # Related PR: smarter apigw routes paging calculation #635
  # https://github.com/boltops-tools/jets/pull/635
  # Debugging notes: https://gist.github.com/tongueroo/c9baa7e98d5ad68bbdd770fde4651963
  @options[:mount_class] = @options[:mount_class].to_s if @options[:mount_class]
end

Instance Attribute Details

#asObject (readonly)

Returns the value of attribute as.



13
14
15
# File 'lib/jets/router/route.rb', line 13

def as
  @as
end

#toObject (readonly)

Returns the value of attribute to.



13
14
15
# File 'lib/jets/router/route.rb', line 13

def to
  @to
end

Instance Method Details

#account_on(prefix) ⇒ Object



65
66
67
68
69
70
71
# File 'lib/jets/router/route.rb', line 65

def (prefix)
  # Tricky @scope.from == :resources since the account_scope already has accounted for it
  if @options[:on] == :collection && @scope.from == :resources
    prefix = prefix.split('/')[0..-2].join('/')
  end
  prefix == '' ? nil : prefix
end

#account_scope(prefix) ⇒ Object



54
55
56
57
58
59
60
61
62
63
# File 'lib/jets/router/route.rb', line 54

def (prefix)
  return unless prefix
  return prefix unless @options[:from_scope]

  if @options[:singular_resource]
    prefix.split('/')[0..-2].join('/')
  else
    prefix.split('/')[0..-3].join('/')
  end
end

#action_nameObject

IE: index



128
129
130
# File 'lib/jets/router/route.rb', line 128

def action_name
  to.sub(/.*#/,'')
end

#compute_asObject



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/jets/router/route.rb', line 80

def compute_as
  return nil if @options[:as] == :disabled
  return unless @options[:method] == :get || @options[:root]

  controller, action = get_controller_action(@options)
  klass = if @options[:root]
    Jets::Router::MethodCreator::Root
  elsif %w[index edit show new].include?(action.to_s)
    class_name = "Jets::Router::MethodCreator::#{action.camelize}"
    class_name.constantize # Index, Show, Edit, New
  else
    Jets::Router::MethodCreator::Generic
  end

  klass.new(@options, @scope, controller).full_meth_name(nil)
end

#compute_pathObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/jets/router/route.rb', line 37

def compute_path
  # Note: The @options[:prefix] is missing prefix and is not support via direct create_route.
  # This is because it can be added directly to the path. IE:
  #
  #     get "myprefix/posts", to: "posts#index"
  #
  # Also, this helps to keep the method creator logic more simple.
  #
  prefix = @scope.full_prefix
  prefix = (prefix)
  prefix = (prefix)

  path = [prefix, @options[:path]].compact.join('/')
  path = path[1..-1] if path.starts_with?('/') # be more forgiving if / accidentally included
  path
end

#compute_toObject



73
74
75
76
77
78
# File 'lib/jets/router/route.rb', line 73

def compute_to
  controller, action = get_controller_action(@options)
  mod = @options[:module] || @scope.full_module
  controller = [mod, controller].compact.join('/') # add module
  "#{controller}##{action}"
end

#controller_nameObject

IE: PostsController



123
124
125
# File 'lib/jets/router/route.rb', line 123

def controller_name
  to.sub(/#.*/,'').camelize + "Controller"
end

#extract_parameters(actual_path) ⇒ Object

Extracts the path parameters from the actual path Only supports extracting 1 parameter. So:

actual_path: posts/tung/edit
route.path: posts/:id/edit

Returns:

{ id: "tung" }


151
152
153
154
155
156
157
158
159
160
# File 'lib/jets/router/route.rb', line 151

def extract_parameters(actual_path)
  if path.include?(':')
    extract_parameters_capture(actual_path)
  elsif path.include?('*')
    extract_parameters_proxy(actual_path)
  else
    # Lambda AWS_PROXY sets null to the input request when there are no path parmeters
    nil
  end
end

#extract_parameters_capture(actual_path) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/jets/router/route.rb', line 184

def extract_parameters_capture(actual_path)
  # changes path to a string used for a regexp
  # posts/:id/edit => posts\/(.*)\/edit
  labels = []
  regexp_string = path.split('/').map do |s|
                    if s.start_with?(':')
                      labels << s.delete_prefix(':')
                      CAPTURE_REGEX
                    else
                      s
                    end
                  end.join('\/')
  # make sure beginning and end of the string matches
  regexp_string = "^#{regexp_string}$"
  regexp = Regexp.new(regexp_string)

  values = regexp.match(actual_path).captures
  labels.map do |next_label|
    [next_label, values.delete_at(0)]
  end.to_h
end

#extract_parameters_proxy(actual_path) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/jets/router/route.rb', line 162

def extract_parameters_proxy(actual_path)
  # changes path to a string used for a regexp
  # others/*proxy => others\/(.*)
  # nested/others/*proxy => nested/others\/(.*)
  if path.include?('/')
    leading_path = path.split('/')[0..-2].join('/') # drop last segment
    # leading_path: nested/others
    # capture everything after the leading_path as the value
    regexp = Regexp.new("#{leading_path}/(.*)")
    value = actual_path.match(regexp)[1]
  else
    value = actual_path
  end

  # the last segment without the '*' is the key
  proxy_segment = path.split('/').last # last segment is the proxy segment
  # proxy_segment: *proxy
  key = proxy_segment.sub('*','')

  { key => value }
end

#homepage?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/jets/router/route.rb', line 118

def homepage?
  path == ''
end

#internal?Boolean

Returns:

  • (Boolean)


114
115
116
# File 'lib/jets/router/route.rb', line 114

def internal?
  !!@options[:internal]
end

#methodObject



110
111
112
# File 'lib/jets/router/route.rb', line 110

def method
  @options[:method].to_s.upcase
end

#mount_classObject

Constantize back to the original class



33
34
35
# File 'lib/jets/router/route.rb', line 33

def mount_class
  @options[:mount_class].constantize
end

#path(format = :jets) ⇒ Object

IE: standard: posts/:id/edit

api_gateway: posts/{id}/edit


99
100
101
102
103
104
105
106
107
108
# File 'lib/jets/router/route.rb', line 99

def path(format=:jets)
  case format
  when :api_gateway
    api_gateway_format(@path)
  when :raw
    @path
  else # jets format
    ensure_jets_format(@path)
  end
end

#to_hObject



206
207
208
# File 'lib/jets/router/route.rb', line 206

def to_h
  JSON.load(to_json)
end

#valid?Boolean

Checks to see if the corresponding controller exists. Useful to validate routes before deploying to CloudFormation and then rolling back.

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
# File 'lib/jets/router/route.rb', line 134

def valid?
  controller_class = begin
    controller_name.constantize
  rescue NameError
    return false
  end
  controller_class.lambda_functions.include?(action_name.to_sym)
end