travis-api/lib/travis/api/v3/query.rb
2016-08-23 01:36:10 -04:00

226 lines
6.8 KiB
Ruby

module Travis::API::V3
class Query
@@sidekiq_queue = {}
def self.sidekiq_queue(identifier)
@@sidekiq_queue[identifier] ||= [
"Travis::Sidekiq::#{identifier.to_s.camelcase}".freeze,
identifier.to_s.pluralize.freeze
]
end
def self.setup_sidekiq(identifier, queue: nil, class_name: nil)
sidekiq_queue(identifier)[0] = class_name if class_name
sidekiq_queue(identifier)[1] = queue if queue
end
# generate from eval to avoid additional string allocations on every params access
@@params_accessor = <<-RUBY
attr_writer :%<method_name>s
def %<method_name>s
return @%<method_name>s if defined? @%<method_name>s
return @%<method_name>s = @params['%<prefix>s.%<name>s'.freeze] if @params.include? '%<prefix>s.%<name>s'.freeze
return @%<method_name>s = @params['%<prefix>s'.freeze]['%<name>s'.freeze] if @params.include? '%<prefix>s'.freeze and @params['%<prefix>s'.freeze].is_a? Hash
return @%<method_name>s = @params['%<name>s'.freeze] if (@params['@type'.freeze] || @main_type) == '%<prefix>s'.freeze
return @%<method_name>s = @params['%<name>s'.freeze] if %<check_type>p and (@params['@type'.freeze] || @main_type) == '%<type>s'.freeze
@%<method_name>s = nil
end
def %<method_name>s!
%<method_name>s or raise WrongParams, 'missing %<prefix>s.%<name>s'.freeze
end
RUBY
@@prefixed_params_accessor = <<-RUBY
def %<prefix>s_params
@%<prefix>s ||= begin
params.select { |key, _| key.start_with?('%<prefix>s.'.freeze) }
Hash[params.map { |key, value| [key.split('.'.freeze).last, value] }]
end
end
RUBY
def self.type
name[/[^:]+$/].underscore
end
def self.params(*list, prefix: nil, method_name: nil)
prefix ||= type.to_s
check_type = method_name.nil? and type != prefix
list.each do |entry|
class_eval(@@params_accessor % {
name: entry,
prefix: prefix,
type: type,
method_name: method_name || entry,
check_type: check_type
})
end
class_eval(@@prefixed_params_accessor % { prefix: prefix })
end
def self.prefix(input)
return input if input.is_a? String
"#{type}.#{input}"
end
def self.sortable_by(*params, **mapping)
params.each { |param| sort_by[param.to_s] = prefix(param) }
mapping.each { |key, value| sort_by[key.to_s] = prefix(value) }
end
def self.prevent_sortable_join(*fields)
dont_join.push(*fields.map(&:to_s))
end
@dont_join = []
def self.dont_join
@dont_join ||= superclass.dont_join.dup
end
@experimental_sortable_by = []
def self.experimental_sortable_by(*fields)
@experimental_sortable_by ||= []
if fields.first
@experimental_sortable_by.push(*fields.map(&:to_s))
end
@experimental_sortable_by
end
def self.sort_condition(condition)
if condition.is_a? Hash
condition = condition.map { |e| e.map { |v| prefix(v) }.join(" = ".freeze) }.join(" and ".freeze)
end
"(case when #{prefix(condition)} then 1 else 2 end)"
end
def self.sortable?
!sort_by.empty?
end
@sort_by = {}
def self.sort_by
@sort_by ||= superclass.sort_by.dup
end
@default_sort = ""
def self.default_sort(value = nil)
@default_sort = value.to_s if value
@default_sort ||= superclass.default_sort
end
attr_reader :params, :main_type
def initialize(params, main_type, includes: nil, service: nil)
@params = params
@main_type = main_type.to_s
@includes = includes
@service = service
end
def warn(*args)
return unless @service
@service.warn(*args)
end
def ignored_value(param, value, reason: nil, **info)
message = reason ? "query value #{value} for #{param} #{reason}, ignored" : "query value #{value} for #{param} ignored"
warn(message, warning_type: :ignored_value, parameter: param, value: value, **info)
end
def perform_async(identifier, *args)
class_name, queue = Query.sidekiq_queue(identifier)
::Sidekiq::Client.push('queue'.freeze => queue, 'class'.freeze => class_name, 'args'.freeze => args)
end
def includes?(key)
@includes ||= @params['include'.freeze].to_s.split(?,.freeze)
key = key.to_s if key.is_a? Symbol
if key.is_a? String
key.include?(?.) ? @includes.include?(key) : @includes.any? { |k| k.start_with? key }
else
@includes.any? { |k| key === k }
end
end
def bool(value)
return false if value == 'false'.freeze
!!value
end
def list(value)
value.split(?,.freeze)
end
def sort(collection, **options)
return collection unless sort_by = params["sort_by".freeze] || self.class.default_sort and not sort_by.empty?
first = true
list(sort_by).each do |field_with_order|
field, order = field_with_order.split(?:.freeze, 2)
order ||= "asc".freeze
if sort_by? field, order
collection = sort_by(collection, field, order: order, first: first, **options)
first = false
else
ignored_value("sort_by".freeze, field_with_order, reason: "not a valid sort mode".freeze)
end
end
collection
end
def sort_by?(field, order)
return false unless order == "asc".freeze or order == "desc".freeze
self.class.sort_by.include?(field)
end
def sort_by(collection, field, order: nil, first: false, sql: nil, **)
raise ArgumentError, 'cannot sort by that' unless sort_by?(field, order)
actual = sql || self.class.sort_by.fetch(field)
line = add_order(actual, order)
if sort_join?(collection, actual)
collection = collection.joins(actual.to_sym)
elsif actual != field and sort_join?(collection, field)
collection = collection.joins(field.to_sym)
end
first ? collection.reorder(line) : collection.order(line)
end
def sort_join?(collection, field)
return if self.class.dont_join.include?(field)
!collection.reflect_on_association(field.to_sym).nil?
end
def sort_condition(*args)
self.class.sort_condition(*args)
end
def quote(value)
ActiveRecord::Base.connection.quote(value)
end
def user_condition(value)
case value
when String then { login: value }
when Integer then { id: value }
when Models::User then { id: value.id }
else raise WrongParams
end
end
def add_order(field, order)
order = order.upcase
if field =~ /%{order}/
field % { order: order }
else
"#{field} #{order}"
end
end
end
end