Module: IntrospectiveGrape::Filters

Included in:
API
Defined in:
lib/introspective_grape/filters.rb

Instance Method Summary collapse

Instance Method Details

#apply_filter_params(klass, model, api_params, params, records) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/introspective_grape/filters.rb', line 102

def apply_filter_params(klass, model, api_params, params, records)
  records = records.order(default_sort) if default_sort.present?

  simple_filters(klass, model, api_params).each do |field|
    records = apply_simple_filter(klass, model, params, records, field)
  end

  klass.custom_filters.each do |filter, _details|
    records = records.send(filter, params[filter])
  end

  records = apply_filters(records, params[:filter])
  records.where( JSON.parse(params[:query]) ) if params[:query].present?
  records
end

#apply_filters(records, filters) ⇒ Object



118
119
120
121
122
123
124
125
126
# File 'lib/introspective_grape/filters.rb', line 118

def apply_filters(records, filters)
  if filters.present?
    filters = JSON.parse( filters.delete('\\') )
    filters.each do |key, value|
      records = records.where(key => value) if value.present?
    end
  end
  records
end

#apply_simple_filter(klass, model, params, records, field) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/introspective_grape/filters.rb', line 89

def apply_simple_filter(klass, model, params, records, field)
  return records if params[field].blank?

  if timestamp_filter(klass, model, field)
    op      = field.ends_with?('_start') ? '>=' : '<='
    records.where(ActiveRecord::Base.sanitize_sql_array(["#{timestamp_filter(klass, model, field)} #{op} ?", Time.zone.parse(params[field])]))
  elsif model.respond_to?("#{field}=")
    records.send("#{field}=", params[field])
  else
    records.where(field => params[field])
  end
end

#custom_filter(*args) ⇒ Object



10
11
12
# File 'lib/introspective_grape/filters.rb', line 10

def custom_filter(*args)
  custom_filters( *args )
end

#custom_filters(*args) ⇒ Object



14
15
16
17
18
# File 'lib/introspective_grape/filters.rb', line 14

def custom_filters(*args)
  @custom_filters ||= {}
  @custom_filters   = Hash[*args].merge(@custom_filters) if args.present?
  @custom_filters
end

#declare_filter_params(dsl, klass, model, api_params) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/introspective_grape/filters.rb', line 47

def declare_filter_params(dsl, klass, model, api_params)
  # Declare optional parameters for filtering parameters, create two parameters per
  # timestamp, a Start and an End, to apply a date range.
  simple_filters(klass, model, api_params).each do |field|
    declare_simple_filter(dsl, klass, model, field)
  end

  custom_filters.each do |filter, details|
    dsl.optional filter, details
  end

  dsl.optional :filter, type: String, description: filter_doc if special_filter_enabled?(filters)
end

#declare_simple_filter(dsl, klass, model, field) ⇒ Object



61
62
63
64
65
66
67
68
69
# File 'lib/introspective_grape/filters.rb', line 61

def declare_simple_filter(dsl, klass, model, field)
  if timestamp_filter(klass, model, field)
    dsl.optional field, type: klass.param_type(model, field), description: "Constrain #{field} by #{humanize_date_range(field)} date."
  elsif identifier_filter?(model, field)
    dsl.optional field, type: Array[String], coerce_with: ->(val) { val.split(',') }, description: 'Filter by a comma separated list of unique identifiers.'
  else
    dsl.optional field, type: klass.param_type(model, field), description: "Filter on #{field} by value."
  end
end

#default_sort(*args) ⇒ Object

Allow filters on all whitelisted model attributes (from api_params) and declare customer filters for the index in a method.



6
7
8
# File 'lib/introspective_grape/filters.rb', line 6

def default_sort(*args)
  @default_sort ||= args
end

#filter_docObject



83
84
85
86
87
# File 'lib/introspective_grape/filters.rb', line 83

def filter_doc
  <<-STR
    JSON of conditions for query.  If you're familiar with ActiveRecord's query conventions you can build more complex filters, i.e. against included child associations, e.g.: {\"&lt;association_name&gt;_&lt;parent&gt;\":{\"field\":\"value\"}}
  STR
end

#filter_on(*args) ⇒ Object



20
21
22
# File 'lib/introspective_grape/filters.rb', line 20

def filter_on(*args)
  filters( *args )
end

#filters(*args) ⇒ Object



24
25
26
27
28
# File 'lib/introspective_grape/filters.rb', line 24

def filters(*args)
  @filters ||= []
  @filters  += args if args.present?
  @filters
end

#humanize_date_range(field) ⇒ Object



71
72
73
# File 'lib/introspective_grape/filters.rb', line 71

def humanize_date_range(field)
  field.ends_with?('_start') ? 'initial' : 'terminal'
end

#identifier_filter?(model, field) ⇒ Boolean

Returns:

  • (Boolean)


75
76
77
# File 'lib/introspective_grape/filters.rb', line 75

def identifier_filter?(model, field)
  true if field.ends_with?('id') && %i(integer uuid).include?(model.columns_hash[field]&.type)
end

#simple_filters(klass, model, api_params) ⇒ Object



30
31
32
33
34
35
36
# File 'lib/introspective_grape/filters.rb', line 30

def simple_filters(klass, model, api_params)
  @simple_filters ||= api_params.select {|p| p.is_a? Symbol }.select {|field|
    filters.include?(:all) || filters.include?(field)
  }.map {|field|
    (klass.param_type(model, field) == DateTime ? ["#{field}_start", "#{field}_end"] : field.to_s)
  }.flatten
end

#special_filter_enabled?(filters) ⇒ Boolean

Returns:

  • (Boolean)


79
80
81
# File 'lib/introspective_grape/filters.rb', line 79

def special_filter_enabled?(filters)
  filters.include?(:all) || filters.include?(:filter)
end

#timestamp_filter(klass, model, field) ⇒ Object



38
39
40
41
42
43
44
45
# File 'lib/introspective_grape/filters.rb', line 38

def timestamp_filter(klass, model, field)
  filter = field.sub(/_(end|start)\z/, '')
  if field =~ /_(end|start)\z/ && klass.param_type(model, filter) == DateTime
    filter
  else
    false
  end
end