Module: Praxis::EndpointDefinition::ClassMethods

Defined in:
lib/praxis/endpoint_definition.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#actionsObject (readonly)

Returns the value of attribute actions.



69
70
71
# File 'lib/praxis/endpoint_definition.rb', line 69

def actions
  @actions
end

#controllerObject

Returns the value of attribute controller.



75
76
77
# File 'lib/praxis/endpoint_definition.rb', line 75

def controller
  @controller
end

#metadataObject (readonly)

opaque hash of user-defined medata, used to decorate the definition, and also available in the generated JSON documents



73
74
75
# File 'lib/praxis/endpoint_definition.rb', line 73

def 
  @metadata
end

#parent_prefixObject (readonly)

Returns the value of attribute parent_prefix.



69
70
71
# File 'lib/praxis/endpoint_definition.rb', line 69

def parent_prefix
  @parent_prefix
end

#responsesObject (readonly)

Returns the value of attribute responses.



69
70
71
# File 'lib/praxis/endpoint_definition.rb', line 69

def responses
  @responses
end

#routing_prefixObject



162
163
164
165
166
# File 'lib/praxis/endpoint_definition.rb', line 162

def routing_prefix
  return @routing_prefix if @routing_prefix

  @routing_prefix = parent_prefix + prefix
end

#traitsObject (readonly)

Returns the value of attribute traits.



69
70
71
# File 'lib/praxis/endpoint_definition.rb', line 69

def traits
  @traits
end

#version_optionsObject (readonly)

Returns the value of attribute version_options.



69
70
71
# File 'lib/praxis/endpoint_definition.rb', line 69

def version_options
  @version_options
end

#version_prefixObject (readonly)

Returns the value of attribute version_prefix.



69
70
71
# File 'lib/praxis/endpoint_definition.rb', line 69

def version_prefix
  @version_prefix
end

Instance Method Details

#action(name, &block) ⇒ Object

Raises:

  • (ArgumentError)


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
# File 'lib/praxis/endpoint_definition.rb', line 222

def action(name, &block)
  raise ArgumentError, 'can not create ActionDefinition without block' unless block_given?
  raise ArgumentError, "Action names must be defined using symbols (Got: #{name} (of type #{name.class}))" unless name.is_a? Symbol

  action = ActionDefinition.new(name, self, &block)
  if action.sister_post_action
    post_path = \
      if action.sister_post_action == true
        "#{action.route.prefixed_path}/actions/#{action.name}"
      elsif action.sister_post_action.start_with?('//')
        action.sister_post_action # Avoid appending prefix
      else
        # Make sure to cleanup the leading '/' if any, as we're always adding it below
        cleaned_path = action.sister_post_action.start_with?('/') ? action.sister_post_action[1..] : action.sister_post_action
        "#{action.route.prefixed_path}/#{cleaned_path}"
      end

    # Save the finalization of the twin POST actions once we've loaded the endpoint definition
    on_finalize do
      # Create the sister POST action with a payload matching the original params
      post_action = action.clone_action_as_post(at: post_path)
      @actions[post_action.name] = post_action
    end
  end
  @actions[name] = action
end

#action_defaults(&block) ⇒ Object



216
217
218
219
220
# File 'lib/praxis/endpoint_definition.rb', line 216

def action_defaults(&block)
  @action_defaults.instance_eval(&block) if block_given?

  @action_defaults
end

#canonical_path(action_name = nil) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/praxis/endpoint_definition.rb', line 175

def canonical_path(action_name = nil)
  if action_name
    raise "Canonical path for #{name} is already defined as: '#{@canonical_action_name}'. 'canonical_path' can only be defined once." if @canonical_action_name

    @canonical_action_name = action_name
  else
    # Resolution of the actual action definition needs to be done lazily, since we can use the `canonical_path` stanza
    # at the top of the resource, well before the actual action is defined.
    unless @canonical_action
      href_action = @canonical_action_name || DEFAULT_RESOURCE_HREF_ACTION
      @canonical_action = actions.fetch(href_action) do
        raise "Error: trying to set canonical_href of #{name}. Action '#{href_action}' does not exist"
      end
    end
    @canonical_action
  end
end

#describe(context: nil) ⇒ Object



258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/praxis/endpoint_definition.rb', line 258

def describe(context: nil)
  {}.tap do |hash|
    hash[:description] = description
    hash[:media_type] = media_type.describe(true) if media_type
    hash[:actions] = actions.values.collect { |action| action.describe(context: context) }
    hash[:name] = name
    hash[:parent] = parent.id if parent
    hash[:display_name] = display_name
    hash[:metadata] = 
    hash[:traits] = traits
  end
end

#description(text = nil) ⇒ Object



249
250
251
252
# File 'lib/praxis/endpoint_definition.rb', line 249

def description(text = nil)
  @description = text if text
  @description
end

#display_name(string = nil) ⇒ Object



77
78
79
80
81
82
83
# File 'lib/praxis/endpoint_definition.rb', line 77

def display_name(string = nil)
  unless string
    return @display_name ||= name.split('::').last # Best guess at a display name?
  end

  @display_name = string
end

#idObject



254
255
256
# File 'lib/praxis/endpoint_definition.rb', line 254

def id
  name.gsub('::', '-')
end

#inherit_params_from_parent(parent_action, **mapping) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/praxis/endpoint_definition.rb', line 146

def inherit_params_from_parent(parent_action, **mapping)
  actions.each do |_name, action|
    action.params do
      mapping.each do |parent_name, name|
        next if action.params&.attributes&.key?(name)

        parent_attribute = parent_action.params.attributes[parent_name]

        attribute name, parent_attribute.type, **parent_attribute.options
      end
    end
  end
end

#media_type(media_type = nil) ⇒ Object



98
99
100
101
102
103
# File 'lib/praxis/endpoint_definition.rb', line 98

def media_type(media_type = nil)
  return @media_type if media_type.nil?

  media_type = SimpleMediaType.new(media_type) if media_type.is_a?(String)
  @media_type = media_type
end

#nodoc!Object



271
272
273
# File 'lib/praxis/endpoint_definition.rb', line 271

def nodoc!
  [:doc_visibility] = :none
end

#on_finalize(&block) ⇒ Object



85
86
87
88
89
# File 'lib/praxis/endpoint_definition.rb', line 85

def on_finalize(&block)
  @on_finalize << proc(&block) if block_given?

  @on_finalize
end

#parent(parent = nil, **mapping) ⇒ Object



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
# File 'lib/praxis/endpoint_definition.rb', line 105

def parent(parent = nil, **mapping)
  return @parent if parent.nil?

  @routing_prefix = nil # reset routing_prefix

  parent_action = parent.canonical_path
  parent_route = parent_action.route.path

  # if a mapping is passed, it *must* resolve any param name conflicts
  unless mapping.any?
    # assume last capture is the relevant one to replace
    # if not... then I quit.
    parent_param_name = parent_route.names.last

    # more assumptions about names
    parent_name = parent.name.demodulize.underscore.singularize

    # put it together to find what we should call this new param
    param = "#{parent_name}_#{parent_param_name}".to_sym
    mapping[parent_param_name.to_sym] = param
  end

  # complete the mapping and massage the route
  parent_route.names.collect(&:to_sym).each do |name|
    if mapping.key?(name)
      param = mapping[name]
      # FIXME: this won't handle URI Template type paths, ie '/{parent_id}'
      prefixed_path = parent_action.route.prefixed_path
      @parent_prefix = prefixed_path.gsub(/(:)(#{name})(\W+|$)/, "\\1#{param}\\3")
    else
      mapping[name] = name
    end
  end

  on_finalize do
    inherit_params_from_parent(parent_action, **mapping)
  end

  @parent = parent
end

#parse_href(path) ⇒ Object



197
198
199
200
201
202
203
204
205
206
# File 'lib/praxis/endpoint_definition.rb', line 197

def parse_href(path)
  path = path.path if path.is_a?(::URI::Generic)
  param_values = canonical_path.route.path.params(path)
  attrs = canonical_path.params.attributes
  param_values.each_with_object({}) do |(key, value), hash|
    hash[key.to_sym] = attrs[key.to_sym].load(value, [key])
  end
rescue StandardError => e
  raise Praxis::Exception, "Error parsing or coercing parameters from href: #{path}\n" + e.message
end

#prefix(prefix = nil) ⇒ Object



91
92
93
94
95
96
# File 'lib/praxis/endpoint_definition.rb', line 91

def prefix(prefix = nil)
  return @prefix if prefix.nil?

  @routing_prefix = nil # reset routing_prefix
  @prefix = prefix
end

#to_href(params) ⇒ Object



193
194
195
# File 'lib/praxis/endpoint_definition.rb', line 193

def to_href(params)
  canonical_path.route.path.expand(params.transform_values(&:to_s))
end

#trait(trait_name) ⇒ Object Also known as: use



208
209
210
211
212
213
# File 'lib/praxis/endpoint_definition.rb', line 208

def trait(trait_name)
  raise Exceptions::InvalidTrait, "Trait #{trait_name} not found in the system" unless ApiDefinition.instance.traits.key? trait_name

  # TODO: We're only storing the names here, should we store the actual traits in a hash?
  @traits << trait_name
end

#version(version = nil) ⇒ Object



168
169
170
171
172
173
# File 'lib/praxis/endpoint_definition.rb', line 168

def version(version = nil)
  return @version unless version

  @version = version
  @action_defaults.instance_eval(&EndpointDefinition.generate_defaults_block(version: version))
end