module Motor::ApiQuery::BuildJson

Public Instance Methods

assign_fields_params!(json_params, model, params, current_ability, models_index) click to toggle source

@param json_params [Hash] @param model [Class<ActiveRecord::Base>] @param params [Hash] @param current_ability [CanCan::Ability] @param models_index [Hash] @return [void]

# File lib/motor/api_query/build_json.rb, line 46
def assign_fields_params!(json_params, model, params, current_ability, models_index)
  return if params[:fields].blank?

  params[:fields].each do |key, fields|
    fields_model = models_index[key]

    next unless model

    fields = fields.split(',') if fields.is_a?(String)

    fields_hash = fields_model == model ? json_params : find_key_in_params(json_params, key)

    fields_hash.merge!(build_fields_hash(fields_model, fields, current_ability))
  end
end
build_fields_hash(model, fields, current_ability) click to toggle source

@param model [Class<ActiveRecord::Base>] @param fields [Hash] @param current_ability [CanCan::Ability] @return [Hash]

# File lib/motor/api_query/build_json.rb, line 66
def build_fields_hash(model, fields, current_ability)
  return { 'methods' => fields } unless model

  column_names = model.column_names.map(&:to_sym)
  instance_methods = model.instance_methods
  permitted_attributes = current_ability.permitted_attributes(:read, model)
  is_permitted_all = column_names == permitted_attributes

  fields_hash = { 'only' => [], 'methods' => [] }

  fields.each_with_object(fields_hash) do |field, acc|
    field_symbol = field.to_sym

    next if !is_permitted_all && permitted_attributes.exclude?(field_symbol)

    if model.columns_hash[field]
      acc['only'] << field
    elsif instance_methods.include?(field_symbol)
      acc['methods'] << field
    end
  end
end
build_hash_from_string_path(string_path) click to toggle source

@param string_path [String] @return [Hash]

# File lib/motor/api_query/build_json.rb, line 148
def build_hash_from_string_path(string_path)
  string_path.split(',').reduce({}) do |accumulator, path|
    hash = {}

    path.split('.').reduce(hash) do |acc, part|
      acc_hash = {}

      acc[part] = acc_hash

      acc_hash
    end

    accumulator.deep_merge(hash)
  end
end
build_include_hash(include_params) click to toggle source

@param include_params [Hash] @return [Hash]

# File lib/motor/api_query/build_json.rb, line 30
def build_include_hash(include_params)
  return {} if include_params.blank?

  if include_params.is_a?(String)
    build_hash_from_string_path(include_params)
  else
    include_params
  end
end
build_models_index(model, includes_hash) click to toggle source

@param model [Class<ActiveRecord::Base>] @param includes_hash [Hash] @return [Hash]

# File lib/motor/api_query/build_json.rb, line 128
def build_models_index(model, includes_hash)
  default_index = {
    model.name.underscore => model,
    model.name.underscore.split('/').last => model
  }

  includes_hash.reduce(default_index) do |acc, (key, value)|
    reflection = model.reflections[key]

    next acc unless reflection
    next acc if reflection.polymorphic?

    acc[key] = reflection.klass

    acc.merge(build_models_index(reflection.klass, value))
  end
end
call(rel, params, current_ability = Motor::CancanUtils::CanManageAll.new) click to toggle source

@param rel [ActiveRecord::Base, ActiveRecord::Relation] @param params [Hash] @param current_ability [CanCan::Ability] @return [Hash]

# File lib/motor/api_query/build_json.rb, line 12
def call(rel, params, current_ability = Motor::CancanUtils::CanManageAll.new)
  rel = rel.none if limit_zero_params?(params)
  rel = rel.preload_associations_lazily if rel.is_a?(ActiveRecord::Relation)

  model = rel.is_a?(ActiveRecord::Relation) ? rel.klass : rel.class

  include_hash = build_include_hash(params['include'])
  models_index = build_models_index(model, include_hash)

  json_params = normalize_include_params(include_hash)

  assign_fields_params!(json_params, model, params, current_ability, models_index)

  rel.as_json(json_params.with_indifferent_access)
end
find_key_in_params(params, key) click to toggle source

@param params [Hash] @param key [String] @return [Hash]

# File lib/motor/api_query/build_json.rb, line 92
def find_key_in_params(params, key)
  params = params['include']

  return {} if params.blank?
  return params[key] if params[key]

  params.keys.reduce(nil) do |acc, k|
    acc || find_key_in_params(params[k], key)
  end
end
limit_zero_params?(params) click to toggle source

@param params [Hash] @return [Boolean]

# File lib/motor/api_query/build_json.rb, line 166
def limit_zero_params?(params)
  params.dig(:page, :limit).yield_self { |limit| limit.present? && limit.to_i.zero? }
end
normalize_include_params(params) click to toggle source

@param params [Hash] @return [Hash]

# File lib/motor/api_query/build_json.rb, line 105
def normalize_include_params(params)
  case params
  when Array
    params.index_with do |_|
      { 'include' => {} }
    end
  when String
    { params => { 'include' => {} } }
  when Hash
    include_hash =
      params.transform_values do |value|
        normalize_include_params(value)
      end

    { 'include' => include_hash }
  else
    raise ArgumentError, "Wrong include param type #{params.class}"
  end
end