class HaveAPI::ModelAdapters::ActiveRecord::Output

Public Class Methods

used_by(action) click to toggle source
# File lib/haveapi/model_adapters/active_record.rb, line 156
      def self.used_by(action)
        action.meta(:object) do
          output do
            custom :path_params, label: 'URL parameters',
                                 desc: 'An array of parameters needed to resolve URL to this object'
            bool :resolved, label: 'Resolved', desc: 'True if the association is resolved'
          end
        end

        return unless %i[object object_list].include?(action.input.layout)

        clean = proc do |raw|
          if raw.is_a?(String)
            raw.strip.split(',')
          elsif raw.is_a?(Array)
            raw
          end
        end

        desc = <<~END
          A list of names of associated resources separated by a comma.
          Nested associations are declared with '__' between resource names.
          For example, 'user,node' will resolve the two associations.
          To resolve further associations of node, use e.g. 'user,node__location',
          to go even deeper, use e.g. 'user,node__location__environment'.
        END

        action.meta(:global) do
          input do
            custom :includes, label: 'Included associations',
                              desc:, &clean
          end
        end

        action.send(:include, Action::InstanceMethods)
      end

Public Instance Methods

[](name) click to toggle source
# File lib/haveapi/model_adapters/active_record.rb, line 198
def [](name)
  param = @context.action.output[name]
  v = @object.send(param.db_name)

  if v.is_a?(::ActiveRecord::Base)
    resourcify(param, v)
  else
    v
  end
end
has_param?(name) click to toggle source
# File lib/haveapi/model_adapters/active_record.rb, line 193
def has_param?(name)
  param = @context.action.output[name]
  param && @object.respond_to?(param.db_name)
end
meta() click to toggle source
# File lib/haveapi/model_adapters/active_record.rb, line 209
def meta
  res = @context.action.resource

  params = if @context.action.name.demodulize == 'Index' \
     && !@context.action.resolve \
     && res.const_defined?(:Show)
             res::Show.resolve_path_params(@object)

           else
             @context.action.resolve_path_params(@object)
           end

  {
    path_params: params.is_a?(Array) ? params : [params],
    resolved: true
  }
end

Protected Instance Methods

includes_include?(name) click to toggle source

Should an association with ‘name` be resolved?

# File lib/haveapi/model_adapters/active_record.rb, line 286
def includes_include?(name)
  includes = @context.action_instance.meta[:includes]
  return false unless includes

  name = name.to_sym

  if @context.action_instance.flags[:inner_assoc]
    # This action is called as an association of parent resource.
    # Meta includes are already parsed and can be accessed directly.
  else
    # This action is the one that was called by the user.
    # Meta includes contains an array of strings as was sent
    # by the user. The parsed includes must be fetched from
    # the action itself.
    includes = @context.action_instance.ar_parse_includes([])
  end

  includes.each do |v|
    if v.is_a?(::Hash)
      return true if v.has_key?(name)
    elsif v == name
      return true
    end
  end

  false
end
includes_pass_on_to(assoc) click to toggle source

Create an array of includes that is passed to child association.

# File lib/haveapi/model_adapters/active_record.rb, line 315
def includes_pass_on_to(assoc)
  parsed = if @context.action_instance.flags[:inner_assoc]
             @context.action_instance.meta[:includes]
           else
             @context.action_instance.ar_parse_includes([])
           end

  ret = []

  parsed.each do |v|
    if v.is_a?(::Hash)
      v.each { |k, v| ret << v if k == assoc }
    end
  end

  ret.flatten(1)
end
resourcify(param, val) click to toggle source

Return representation of an associated resource ‘param` with its instance in `val`.

By default, it returns an unresolved resource, which contains only object id and label. Resource will be resolved if it is set in meta includes field.

# File lib/haveapi/model_adapters/active_record.rb, line 235
def resourcify(param, val)
  res_show = param.show_action
  res_output = res_show.output

  args = res_show.resolve_path_params(val)

  if includes_include?(param.name)
    push_cls = @context.action
    push_ins = @context.action_instance

    pass_includes = includes_pass_on_to(param.name)

    show = res_show.new(
      push_ins.request,
      push_ins.version,
      {},
      nil,
      @context
    )
    show.meta[:includes] = pass_includes

    # This flag is used to tell the action that it is being used
    # as a nested association, that it wasn't called directly by the user.
    show.flags[:inner_assoc] = true

    show.authorized?(push_ins.current_user) # FIXME: handle false

    ret = show.safe_output(val)

    @context.action_instance = push_ins
    @context.action = push_cls

    raise "#{res_show} resolve failed" unless ret[0]

    ret[1][res_show.output.namespace].update({
        _meta: ret[1][:_meta].update(resolved: true)
    })

  else
    {
      param.value_id => val.send(res_output[param.value_id].db_name),
      param.value_label => val.send(res_output[param.value_label].db_name),
      _meta: {
        path_params: args.is_a?(Array) ? args : [args],
        resolved: false
      }
    }
  end
end