module Pureapi::Controller

Implement methods use for handle with table records like

  1. Pagination

  2. Sort

  3. Search

  4. Include

Constants

AFTER_REGEX
BEFORE_REGEX
FIELD_NAME

Define constants of regex table and column name

FULLBEFORE_REGEX
NAME_REGEX

Public Instance Methods

advanced_search_params() click to toggle source

Strong parameters for default advanced search query

# File lib/pureapi/controller.rb, line 622
def advanced_search_params
  params.permit()
end
core_cond_filter(objects) click to toggle source
Begin setup new version of feature comparison conditions

Filter conditions of object criteria

# File lib/pureapi/controller.rb, line 334
def core_cond_filter(objects)
  # Default +compcond_params+ for query
  compcond_params = {}

  # Collaborate params, operators, model to get +compcond_params+
  if params[:compconds].is_a?(ActionController::Parameters)
    compcond_params = parse_compcond_fields(params[:compconds], objects)
  end

  # Assign params[:compconds] after parse
  params[:compconds] = deparse_compcond_fields(compcond_params)

  # Add conditions of +compcond_params+ to objects
  objects.compcond_search(compcond_params)
end
core_index_filter(objects) click to toggle source
Begin 3 main API for GET data

Filter list objects contain search, sort, paginate

# File lib/pureapi/controller.rb, line 20
def core_index_filter(objects)
  # 1. search
  objects = default_search_filter(objects)
  # 2. sort and default sort is id.desc
  objects = default_sort_filter(objects)
  # 3. conditions (replace for +default_cond|incond_filter+)
  objects = core_cond_filter(objects)
  # 4. paginate
  objects = paging_standard(objects)
end
default_cond_filter(objects) click to toggle source
Begin setup of feature comparison conditions

Filter conditions of object criteria Need upgrade calculate compcond_params

# File lib/pureapi/controller.rb, line 247
def default_cond_filter(objects)
  compcond_params = []

  objects.compcond_columns.each do |column_name|
    Pureapi::Model::COMPARISON_OPERATORS.each do |key, value|
      compcond_params << [column_name, key].join('.')
    end
  end

  compconds = parse_compconds(params.permit(compconds: compcond_params)[:compconds].to_h, objects.column_names)
  params[:compconds] = {}

  compconds.each do |compcond|
    params[:compconds]["#{compcond[:f]}.#{Pureapi::Model::COMPARISON_OPERATORS_INVERT[compcond[:o]]}"] = compcond[:v]
  end

  objects.compconds(compconds)
end
default_incond_filter(objects) click to toggle source
Begin setup of feature in conditions (logical)

Filter conditions of object criteria Need upgrade calculate compcond_params

# File lib/pureapi/controller.rb, line 296
def default_incond_filter(objects)
  logic_params = []

  objects.incond_columns.each do |column_name|
    logic_params << {[column_name, :in].join('.') => []}
  end

  logics = parse_logicconds(params.permit(logics: logic_params)[:logics].to_h, objects.column_names)
  params[:logics] = {}

  logics.each do |logic|
    params[:logics]["#{logic[:f]}.#{Pureapi::Model::LOGICAL_OPERATORS_INVERT[logic[:o]]}"] = logic[:v]
  end

  objects.logicconds(logics)
end
default_index_filter(objects) click to toggle source

Filter list objects contain search, sort, paginate

# File lib/pureapi/controller.rb, line 32
def default_index_filter(objects)
  # 1. search
  # 2. sort and default sort is id.desc
  objects = default_sort_filter(objects)
  # 3. conditions
  objects = default_cond_filter(objects)
  objects = default_incond_filter(objects)
  # 4. paginate
  objects = paging_standard(objects)
end
default_report_filter(objects) click to toggle source

Filter list objects contain search, sort, limit

# File lib/pureapi/controller.rb, line 44
def default_report_filter(objects)
  # 1. search
  # 2. sort and default sort is id.desc
  # 3. conditions
  objects = default_cond_filter(objects)
  objects = default_incond_filter(objects)
  # 4. limit similar paginate
end
default_search_filter(objects) click to toggle source
Begin setup of default_search_filter

Filter searchs of object criteria

# File lib/pureapi/controller.rb, line 212
def default_search_filter(objects)
  params[:searches] = search_params
  advanced_search_params.each{|k, v| params[:searches][k] = v}

  objects = objects.full_search(search_params, advanced_search_params)
end
default_sort_filter(objects) click to toggle source
Begin setup of default_sort_filter (.order asc|desc)

Filter orders of object criteria

# File lib/pureapi/controller.rb, line 222
def default_sort_filter(objects)
  orders = parse_orders(params.permit(orders: [])[:orders], objects.column_names)
  orders[:id] = :desc if orders.blank?
  params[:orders] = orders.map{|field, order_type| "#{field}.#{order_type}"}.uniq
  objects.order(orders)
end
deparse_compcond_fields(compcond_params) click to toggle source
# File lib/pureapi/controller.rb, line 375
def deparse_compcond_fields compcond_params
  # Default results
  _results = {}

  # Assign from +compcond_params[:only]+
  compcond_params[:only] && compcond_params[:only].each do |item|
    _results["#{item[:f]}.#{item[:o]}"] = item[:v]
  end

  # Assign from +compcond_params[:include]+
  compcond_params[:include] && compcond_params[:include].each do |key, item|
    _results[key] = deparse_compcond_fields(item)
  end

  _results
end
deparse_fields(fields) click to toggle source

Hash fields return String is params query

# File lib/pureapi/controller.rb, line 521
def deparse_fields(fields)
  return '' unless fields.is_a? Hash

  _arr = []

  if fields[:only].is_a? Array
    _arr += fields[:only]
  end

  if fields[:methods].is_a? Array
    _arr += fields[:methods]
  end

  if fields[:include].is_a?(Hash) && !fields[:include].blank?
    fields[:include].each do |k, v|
      _arr << "#{k}{#{deparse_fields(v)}}"
    end
  end

  _arr.join(',')
end
filter_jsons() click to toggle source

Define json filters for params in render json api

# File lib/pureapi/controller.rb, line 157
def filter_jsons
  {
    paging: {
      page: params[:page],
      per_page: params[:per_page],
      page_total: params[:page_total],
      record_total: params[:record_total],
      next: params[:page] < params[:page_total] ? (params[:page] + 1) : nil,
      prev: params[:page] > 1 ? (params[:page] - 1) : nil,
    },
    orders: params[:orders] || [],
    searches: params[:searches] || {},
    compconds: params[:compconds] || {},
    logics: params[:logics] || {},
    fields: params[:fields] || ''
  }
end
include_relations(fields) click to toggle source

return all relations be included by fields

# File lib/pureapi/controller.rb, line 574
def include_relations(fields)
  result = []

  if fields[:include].is_a? Hash
    fields[:include].each do |k, v|
      if (v.is_a? Hash) && (v[:include].is_a? Hash)
        result << { k => include_relations(v)}
      else
        result << k
      end
    end
  end

  result
end
merge_as_json(target ,source) click to toggle source
Begin feature records_json and record_json which render format json

Merge target from request params, source from model

Arguments:

target: (Hash)
source: (Hash)
# File lib/pureapi/controller.rb, line 433
def merge_as_json(target ,source)
  result = {}

  if source[:only].is_a?(Array)
    result[:only] = target[:only].is_a?(Array) ? (target[:only] & source[:only]) : source[:only]
  end

  if source[:methods].is_a?(Array) && target[:only].is_a?(Array)
    result[:methods] = target[:only] & source[:methods]
  end

  if source[:include].is_a?(Hash) && target[:include].is_a?(Hash)
    result[:include] = {}

    (source[:include].keys & target[:include].keys).each do |k|
      result[:include][k] = merge_as_json(target[:include][k], source[:include][k])
    end
  end

  result.delete_if { |k, v| v.blank? }
end
merge_includes(target ,source) click to toggle source
# File lib/pureapi/controller.rb, line 590
def merge_includes(target ,source)
  result = {}

  target.each do |k, v|
    result[k] = {}

    if v.is_a?(Hash) && source[k].is_a?(Hash)
      if source[k][:only].is_a?(Array)
        result[k][:only] = v[:only].is_a?(Array) ? (v[:only] & source[k][:only]) : source[k][:only]
      end

      if v[:include].is_a?(Hash) && source[k][:include].is_a?(Hash)
        result[k][:include] = merge_includes(v[:include], source[k][:include])
      end
    end
  end

  result.delete_if { |k, v| v.blank? }
end
openandclose(before, after) click to toggle source

Require length of before and after is equal +before.length == after.length > 1+

# File lib/pureapi/controller.rb, line 545
def openandclose(before, after)
  results = {}

  index = 0
  results[before[index]] = after[index]
  before.each_with_index do |value, i|
    if results[before[index]] > value
      results[before[index]] = after[i]
    else
      index = i
      results[before[index]] = after[index]
    end
  end

  return results
end
paging_params() click to toggle source

Begin declare strong parameters

# File lib/pureapi/controller.rb, line 612
def paging_params
  params.permit(:page, :per_page)
end
paging_standard(criteria_model) click to toggle source

Paging standard Criteria model that will be pagination Return criteria_model.paginate and params[:page, :per_page, :page_total]

# File lib/pureapi/controller.rb, line 406
def paging_standard(criteria_model)
  per_page = [paging_params[:per_page].try(:to_i) || Pureapi::Model::Pagination::DEFAULT_PER_PAGE, 1].max

  # Paging standard
  record_total = criteria_model.count
  per_page = record_total if paging_params[:per_page] == Pureapi::Model::Pagination::INFINITE_PER_PAGE\
    && record_total > 0
  page_total = (record_total.to_f / per_page).ceil
  page = validate_page(paging_params[:page].try(:to_i) || 1, page_total)
  page = [page, 1].max

  # Assign paging params to params helper
  params[:page] = page
  params[:per_page] = per_page
  params[:page_total] = page_total
  params[:record_total] = record_total

  criteria_model.paginate(page: page, per_page: per_page)
end
parse_compcond_fields(params, model, limit = 10) click to toggle source
# File lib/pureapi/controller.rb, line 350
def parse_compcond_fields params, model, limit = 10
  # Default results
  _results = {only: [], include: {}}

  params.keys.each do |key|
    # Get field and operator from +key+ of model
    matchs = model.compcond_match(key)

    if matchs
      matchs[:v] = params.permit(permit_operator(key, matchs[:o]))[key]

      _results[:only] << matchs
    end

    # Process for belongs_to relations
    entity = model.of_entities[key.to_sym]

    if limit > 1 && entity && params[key].is_a?(ActionController::Parameters)
      _results[:include][key.to_sym] = parse_compcond_fields(params[key], entity, limit - 1)
    end
  end

  _results
end
parse_compconds(compconds = {}, column_names = []) click to toggle source

Not use under code because lte => lt matchs = /([a-z0-9_]+).(#{Pureapi::Model::COMPARISON_OPERATORS.keys.join('|')})/.match(key) if matchs && column_names.include?(matchs)

results << {f: matchs[1], o: Pureapi::Model::COMPARISON_OPERATORS[matchs[2].to_sym], v: value}

end

# File lib/pureapi/controller.rb, line 271
def parse_compconds(compconds = {}, column_names = [])
  results = []

  compconds.map do |key, value|
    splits = key.split('.')

    if splits.length == 2 && column_names.include?(splits[0])
      if splits[1].to_sym == :eq && Pureapi::Model::NULL_OPERATOR_KEYS.include?(value)
        results << {
          f: splits[0],
          o: Pureapi::Model::COMPARISON_OPERATORS[splits[1].to_sym],
          v: Pureapi::Model::NULL_OPERATORS[value]}
      else
        results << {f: splits[0], o: Pureapi::Model::COMPARISON_OPERATORS[splits[1].to_sym], v: value}
      end
    end
  end unless compconds.blank?

  results
end
parse_fields(str, deep = 2) click to toggle source

Methods parse string fields user for as_json Example str = “a,b, c,d,e,f,g{k,l,h{b,i}},w{n, m},j,t{ww, gh, l{bb}}, f{k, mm}, vv” return result = {:only=>[“a”, “b”, “c”, “d”, “e”, “f”, “j”, “vv”], :include=>{“g”=>{:only=>[“k”, “l”], :include=>{“h”=>{:only=>[“b”, “i”]}}}, “w”=>{:only=>[“n”, “m”]}, “t”=>{:only=>[“ww”, “gh”], :include=>{“l”=>{:only=>}}}, “f”=>{:only=>[“k”, “mm”]}}}

# File lib/pureapi/controller.rb, line 481
def parse_fields(str, deep = 2)
  result = { only: [], include: {} }

  fullbefore = substr_index(str, FULLBEFORE_REGEX)
  after = substr_index(str, AFTER_REGEX)

  if fullbefore.length == after.length
    if fullbefore.length == 0
      result[:only] = str.scan(NAME_REGEX)
    else
      kvs = openandclose(fullbefore, after)
      _kvs = openandclose(substr_index(str, BEFORE_REGEX), after).invert
      s = 0

      kvs.each do |k, v|
        if s < k
          result[:only] += str[s..(k - 1)].scan(NAME_REGEX)
          s = v + 1
        elsif
          s = v + 1
        end

        if k < v && deep > 0
          result[:include][str[k..v][NAME_REGEX].to_sym] =\
            parse_fields(str[(_kvs[v] + 1)..(v - 1)], deep - 1)
        end
      end

      if s < (str.length - 1)
        result[:only] += str[s..-1].scan(NAME_REGEX)
      end
    end
  end

  result[:only] = result[:only].map(&:to_sym)
  return result.delete_if { |k, v| v.blank? }
end
parse_includes(fields, model, includes_deep_level = 2) click to toggle source

Methods return hash contain full params use for ActiveRecord includes() and ActiveModel::Serializers::JSON as_json()

# File lib/pureapi/controller.rb, line 457
def parse_includes(fields, model, includes_deep_level = 2)
  _fields = parse_fields(fields, includes_deep_level)

  if _fields[:only].is_a?(Array) && !_fields[:only].blank?
    _fields[:methods] = _fields[:only] & model.json_methods
    _fields[:only] = _fields[:only] & model.column_names.map(&:to_sym)
  end

  if _fields[:include].is_a?(Hash) && !_fields[:include].blank?
    _fields[:include] = merge_includes(_fields[:include], model.asjson_fields)
  end

  {
    includes: include_relations(_fields),
    as_json: _fields.delete_if { |k, v| v.blank? }
  }
end
parse_logicconds(compconds = {}, column_names = []) click to toggle source

References from parse_compconds methods Upgrade results.push(!value.blank?) >> parse_compconds

# File lib/pureapi/controller.rb, line 315
def parse_logicconds(compconds = {}, column_names = [])
  results = []

  compconds.map do |key, value|
    next if value.blank?

    splits = key.split('.')

    if splits.length == 2 && column_names.include?(splits[0])
      results << {f: splits[0], o: Pureapi::Model::LOGICAL_OPERATORS[splits[1].to_sym], v: value}
    end
  end unless compconds.blank?

  results
end
parse_orders(orders = [], column_names = []) click to toggle source

array orders contains field.asc or field array column_names contains valid fields return hash of field => sort type (asc, desc)

# File lib/pureapi/controller.rb, line 232
def parse_orders(orders = [], column_names = [])
  results = {}

  orders.map do |order|
    matchs = /(#{FIELD_NAME})\.(asc|desc)/.match(order) || /(#{FIELD_NAME})/i.match(order)
    results[matchs[1]] = matchs[2] || 'asc' if matchs && column_names.include?(matchs[1])
  end unless orders.blank?

  results
end
permit_operator(key, operator) click to toggle source
# File lib/pureapi/controller.rb, line 392
def permit_operator key, operator
  Pureapi::Model::PERMIT_OPERATORS[operator] ? { key => Pureapi::Model::PERMIT_OPERATORS[operator] } : key
end
record_as_json(model, includes_deep_level = 2) click to toggle source

New version of return json for show action

# File lib/pureapi/controller.rb, line 143
def record_as_json(model, includes_deep_level = 2)
  options = {}

  if params.permit(:fields)[:fields]
    # Get fields from params
    _fields = parse_fields(params.permit(:fields)[:fields], includes_deep_level)
    # Merge with as_json_fields of entity
    options = merge_as_json(_fields, model.class.as_json_fields)
  end

  model.as_json(options)
end
record_asjson(model) click to toggle source

Return json for show action

# File lib/pureapi/controller.rb, line 107
def record_asjson(model)
  options = {}

  if params.permit(:fields)[:fields]
    jsons = parse_includes(params.permit(:fields)[:fields], model.class)

    options = jsons[:as_json] if !jsons[:as_json].blank?
  end

  options[:only] = options[:only].blank? ? model.class.default_onlyasjsons : options[:only]
  model.as_json(options)
end
record_json(model, includes_deep_level = 2) click to toggle source

Return json for show action

# File lib/pureapi/controller.rb, line 73
def record_json(model, includes_deep_level = 2)
  options = {}

  if params.permit(:fields)[:fields]
    jsons = parse_includes(params.permit(:fields)[:fields], model.class, includes_deep_level)

    options = jsons[:as_json] if !jsons[:as_json].blank?
  end

  options[:only] = options[:only].blank? ? model.class.default_onlyasjsons : options[:only]
  options[:methods] = model.class.json_methods
  model.as_json(options)
end
records_as_json(criterias, includes_deep_level = 2) click to toggle source

New version of return json for index action

# File lib/pureapi/controller.rb, line 121
def records_as_json(criterias, includes_deep_level = 2)
  options = {}
  includes = []

  if params.permit(:fields)[:fields]
    # Get fields from params
    _fields = parse_fields(params.permit(:fields)[:fields], includes_deep_level)
    # Merge with as_json_fields of entity
    options = merge_as_json(_fields, criterias.as_json_fields)
    # Get relations for includes
    includes += include_relations(options)
  end

  # Reflect options to params
  params[:fields] = deparse_fields(options)
  # Get relations for includes from methods
  includes += criterias.json_method_includes.slice(*options[:methods]).values if !options[:methods].blank?

  criterias.includes(includes).as_json(options)
end
records_asjson(criterias) click to toggle source

Return json for index action

# File lib/pureapi/controller.rb, line 88
def records_asjson(criterias)
  options = {}
  includes = []

  if params.permit(:fields)[:fields]
    jsons = parse_includes(params.permit(:fields)[:fields], criterias)

    includes += jsons[:includes] if !jsons[:includes].blank?
    options = jsons[:as_json] if !jsons[:as_json].blank?
  end

  params[:fields] = deparse_fields(options)
  options[:only] = options[:only].blank? ? criterias.default_onlyasjsons : options[:only]
  includes += criterias.json_method_includes.slice(*options[:methods]).values if !options[:methods].blank?

  criterias.includes(includes.uniq).as_json(options)
end
records_json(criterias, includes_deep_level = 2) click to toggle source

Return json for index action

# File lib/pureapi/controller.rb, line 54
def records_json(criterias, includes_deep_level = 2)
  options = {}
  includes = criterias.default_includes

  if params.permit(:fields)[:fields]
    jsons = parse_includes(params.permit(:fields)[:fields], criterias, includes_deep_level)

    includes += jsons[:includes] if !jsons[:includes].blank?
    options = jsons[:as_json] if !jsons[:as_json].blank?
  end

  params[:fields] = deparse_fields(options)
  options[:methods] = criterias.json_methods
  options[:only] = options[:only].blank? ? criterias.default_onlyasjsons : options[:only]
  criterias = criterias.includes(includes) if !includes.blank?
  criterias.as_json(options)
end
report_filter_jsons() click to toggle source
# File lib/pureapi/controller.rb, line 175
def report_filter_jsons
  {
    searches: params[:searches] || {},
    compconds: params[:compconds] || {},
    logics: params[:logics] || {},
  }
end
search_params() click to toggle source

Strong parameters for default search query

# File lib/pureapi/controller.rb, line 617
def search_params
  params.permit()
end
standard_index_filter(objects) click to toggle source
Begin options API for GET data

Filter list objects contain search, sort, paginate

# File lib/pureapi/controller.rb, line 186
def standard_index_filter(objects)
  # 1. search
  objects = default_search_filter(objects)
  # 2. sort and default sort is id.desc
  objects = default_sort_filter(objects)
  # 3. conditions
  objects = default_cond_filter(objects)
  objects = default_incond_filter(objects)
  # 4. paginate
  objects = paging_standard(objects)
end
substr_index(str, sub_str) click to toggle source

return Array all index of sub_str in str

# File lib/pureapi/controller.rb, line 563
def substr_index(str, sub_str)
  i = 0
  all = []
  while (i = str.index(sub_str, i)).is_a? Integer
    all << i
    i += str[i..-1][sub_str].length
  end
  all
end
validate_page(page, page_total) click to toggle source
Begin setup of paging_standard. If not use will_paginate, write in mode class

Method validate page number (like page params)

# File lib/pureapi/controller.rb, line 399
def validate_page(page, page_total)
  page.blank? || page <= 0 ? 1 : (page > page_total ? page_total : page)
end
without_paging_index_filter(objects) click to toggle source

Filter list objects contain search, sort

# File lib/pureapi/controller.rb, line 199
def without_paging_index_filter(objects)
  # 1. search
  objects = default_search_filter(objects)
  # 2. sort and default sort is id.desc
  objects = default_sort_filter(objects)
  # 3. conditions
  objects = default_cond_filter(objects)
  objects = default_incond_filter(objects)
end