module Flapjack::Gateways::JSONAPI::Helpers::Resources

Constants

ISO8601_PAT

Public Instance Methods

filter_params(klass, filter) click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/resources.rb, line 67
def filter_params(klass, filter)
  attributes = if klass.respond_to?(:jsonapi_methods)
    (klass.jsonapi_methods[:get] || {}).attributes || []
  else
    []
  end

  attribute_types = klass.attribute_types

  rangeable_attrs = []
  attrs_by_type = {}

  klass.send(:with_index_data) do |idx_data|
    idx_data.each do |k, d|
      next unless d.index_klass == ::Zermelo::Associations::RangeIndex
      rangeable_attrs << k
    end
  end

  Zermelo::ATTRIBUTE_TYPES.keys.each do |at|
    attrs_by_type[at] = attributes.select do |a|
      at.eql?(attribute_types[a])
    end
  end

  filter.each_with_object({}) do |filter_str, memo|
    k, v = filter_str.split(':', 2)
    halt(err(403, "Single filter parameters must be 'key:value'")) if k.nil? || v.nil?

    value = if attrs_by_type[:string].include?(k.to_sym) || :id.eql?(k.to_sym)
      if v =~ %r{^/(.+)/$}
        Regexp.new(Regexp.last_match(1))
      elsif v =~ /\|/
        v.split('|')
      else
        v
      end
    elsif attrs_by_type[:boolean].include?(k.to_sym)
      case v.downcase
      when '0', 'f', 'false', 'n', 'no'
        false
      when '1', 't', 'true', 'y', 'yes'
        true
      end

    elsif attrs_by_type[:timestamp].include?(k.to_sym)
      parse_range_or_value('timestamp', ISO8601_PAT, v, :rangeable => rangeable_attrs.include?(k.to_sym)) {|s|
        begin; DateTime.parse(s); rescue ArgumentError; nil; end
      }
    elsif attrs_by_type[:integer].include?(k.to_sym)
      parse_range_or_value('integer', '\d+', v, :rangeable => rangeable_attrs.include?(k.to_sym)) {|s|
        s.to_i
      }
    elsif attrs_by_type[:float].include?(k.to_sym)
      parse_range_or_value('float', '\d+(\.\d+)?', v, :rangeable => rangeable_attrs.include?(k.to_sym)) {|s|
        s.to_f
      }
    end

    halt(err(403, "Invalid filter key '#{k}'")) if value.nil?
    memo[k.to_sym] = value
  end
end
normalise_json_data(attribute_types, data) click to toggle source

TODO refactor some of these methods into a module, included in data/ classes

# File lib/flapjack/gateways/jsonapi/helpers/resources.rb, line 20
def normalise_json_data(attribute_types, data)
  record_data = data.dup
  attribute_types.each_pair do |name, type|
    t = record_data[name.to_s]
    next unless t.is_a?(String) && :timestamp.eql?(type)
    begin
      record_data[name.to_s] = DateTime.parse(t)
    rescue ArgumentError
      record_data[name.to_s] = nil
    end
  end
  symbolize(record_data)
end
paginate_get(dataset, options = {}) click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/resources.rb, line 164
def paginate_get(dataset, options = {})
  return([[], {}, {}]) if dataset.nil?

  total = dataset.count
  return([[], {}, {}]) if total == 0

  page = options[:page].to_i
  page = (page > 0) ? page : 1

  per_page = options[:per_page].to_i
  per_page = (per_page > 0) ? per_page : 20

  total_pages = (total.to_f / per_page).ceil

  pages = set_page_numbers(page, total_pages)
  links = create_links(pages)
  headers['Link']        = create_link_header(links) unless links.empty?
  headers['Total-Count'] = total_pages.to_s

  [dataset.page(page, :per_page => per_page),
    links,
    {
      :pagination => {
        :page        => page,
        :per_page    => per_page,
        :total_pages => total_pages,
        :total_count => total
      }
    }
  ]
end
parse_range_or_value(name, pattern, v, opts = {}) { |start_s| ... } click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/resources.rb, line 34
def parse_range_or_value(name, pattern, v, opts = {}, &block)
  rangeable = opts[:rangeable]

  if rangeable && (v =~ /\A(?:(#{pattern})\.\.(#{pattern})|(#{pattern})\.\.|\.\.(#{pattern}))\z/)
    start = nil
    start_s = Regexp.last_match(1) || Regexp.last_match(3)
    unless start_s.nil?
      start = yield(start_s)
      halt(err(403, "Couldn't parse #{name} '#{start_s}'")) if start.nil?
    end

    finish = nil
    finish_s = Regexp.last_match(2) || Regexp.last_match(4)
    unless finish_s.nil?
      finish = yield(finish_s)
      halt(err(403, "Couldn't parse #{name} '#{finish_s}'")) if finish.nil?
    end

    if !start.nil? && !finish.nil? && (start > finish)
      halt(err(403, "Range order cannot be inversed"))
    else
      Zermelo::Filters::IndexRange.new(start, finish, :by_score => true)
    end

  elsif v =~ /\A#{pattern}\z/
    t = yield(v)
    halt(err(403, "Couldn't parse #{name} '#{v}'")) if t.nil?
    t
  else
    halt(err(403, "Invalid #{name} parameter '#{v}'"))
  end
end
resource_filter_sort(klass, options = {}) click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/resources.rb, line 131
def resource_filter_sort(klass, options = {})
  options[:sort] ||= 'id'
  scope = klass

  unless params[:filter].nil?
    unless params[:filter].is_a?(Array)
      halt(err(403, "Filter parameters must be passed as an Array"))
    end
    filter_ops = filter_params(klass, params[:filter])
    scope = scope.intersect(filter_ops)
  end

  sort_opts = if params[:sort].nil?
    options[:sort].to_sym
  else
    sort_params = params[:sort].split(',')

    if sort_params.any? {|sp| sp !~ /^(?:\+|-)/ }
      halt(err(403, "Sort parameters must start with +/-"))
    end

    sort_params.each_with_object({}) do |sort_param, memo|
      sort_param =~ /^(\+|\-)([a-z_]+)$/i
      rev  = Regexp.last_match(1)
      term = Regexp.last_match(2)
      memo[term.to_sym] = (rev == '-') ? :desc : :asc
    end
  end

  scope = scope.sort(sort_opts)
  scope
end
set_page_numbers(page, total_pages) click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/resources.rb, line 196
def set_page_numbers(page, total_pages)
  pages = {}
  pages[:first] = 1 # if (total_pages > 1) && (page > 1)
  pages[:prev]  = page - 1 if (page > 1)
  pages[:next]  = page + 1 if page < total_pages
  pages[:last]  = total_pages # if (total_pages > 1) && (page < total_pages)
  pages
end