• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gregschmit / rails-rest-framework / 324

pending completion
324

push

travis-ci-com

gregschmit
Add option to allow all nested attributes by default.

11 of 11 new or added lines in 1 file covered. (100.0%)

805 of 887 relevant lines covered (90.76%)

74.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.77
/lib/rest_framework/filters.rb
1
class RESTFramework::BaseFilter
1✔
2
  def initialize(controller:)
1✔
3
    @controller = controller
236✔
4
  end
5

6
  def get_filtered_data(data)
1✔
7
    raise NotImplementedError
1✔
8
  end
9
end
10

11
# A simple filtering backend that supports filtering a recordset based on fields defined on the
12
# controller class.
13
class RESTFramework::ModelFilter < RESTFramework::BaseFilter
1✔
14
  # Get a list of filterset fields for the current action. Fallback to columns because we don't want
15
  # to try filtering by any query parameter because that could clash with other query parameters.
16
  def _get_fields
1✔
17
    return @_get_fields ||= (
111✔
18
      @controller.class.filterset_fields || @controller.get_fields(fallback: true)
111✔
19
    ).map(&:to_s)
20
  end
21

22
  # Filter params for keys allowed by the current action's filterset_fields/fields config.
23
  def _get_filter_params
1✔
24
    # Map filterset fields to strings because query parameter keys are strings.
25
    fields = self._get_fields
111✔
26

27
    return @controller.request.query_parameters.select { |p, _|
111✔
28
      # Remove any trailing `__in` from the field name.
29
      field = p.chomp("__in")
23✔
30

31
      # Remove any associations whose sub-fields are not filterable.
32
      if match = /(.*)\.(.*)/.match(field)
23✔
33
        field, sub_field = match[1..2]
2✔
34
        next false unless field.in?(fields)
2✔
35

36
        sub_fields = @controller.class.get_field_config(field)[:sub_fields] || []
2✔
37
        next sub_field.in?(sub_fields)
2✔
38
      end
39

40
      next field.in?(fields)
21✔
41
    }.map { |p, v|
42
      # Convert fields ending in `__in` to array values.
43
      if p.end_with?("__in")
8✔
44
        p = p.chomp("__in")
×
45
        v = v.split(",")
×
46
      end
47

48
      # Convert "nil" and "null" to nil.
49
      if v == "nil" || v == "null"
8✔
50
        v = nil
×
51
      end
52

53
      [p, v]
8✔
54
    }.to_h.symbolize_keys
55
  end
56

57
  # Filter data according to the request query parameters.
58
  def get_filtered_data(data)
1✔
59
    if filter_params = self._get_filter_params.presence
111✔
60
      return data.where(**filter_params)
8✔
61
    end
62

63
    return data
103✔
64
  end
65
end
66

67
# A filter backend which handles ordering of the recordset.
68
class RESTFramework::ModelOrderingFilter < RESTFramework::BaseFilter
1✔
69
  # Get a list of ordering fields for the current action. Do not fallback to columns in case the
70
  # user wants to order by a virtual column.
71
  def _get_fields
1✔
72
    return @_get_fields ||= (
111✔
73
      @controller.class.ordering_fields || @controller.get_fields
111✔
74
    )&.map(&:to_s)
75
  end
76

77
  # Convert ordering string to an ordering configuration.
78
  def _get_ordering
1✔
79
    return nil if @controller.class.ordering_query_param.blank?
111✔
80

81
    # Ensure ordering_fields are strings since the split param will be strings.
82
    fields = self._get_fields
111✔
83
    order_string = @controller.params[@controller.class.ordering_query_param]
111✔
84

85
    if order_string.present?
111✔
86
      ordering = {}.with_indifferent_access
6✔
87
      order_string.split(",").each do |field|
6✔
88
        if field[0] == "-"
6✔
89
          column = field[1..-1]
3✔
90
          direction = :desc
3✔
91
        else
92
          column = field
3✔
93
          direction = :asc
3✔
94
        end
95
        next unless !fields || column.in?(fields)
6✔
96

97
        ordering[column] = direction
6✔
98
      end
99
      return ordering
6✔
100
    end
101

102
    return nil
103
  end
104

105
  # Order data according to the request query parameters.
106
  def get_filtered_data(data)
1✔
107
    ordering = self._get_ordering
111✔
108
    reorder = !@controller.class.ordering_no_reorder
111✔
109

110
    if ordering && !ordering.empty?
111✔
111
      return data.send(reorder ? :reorder : :order, ordering)
6✔
112
    end
113

114
    return data
105✔
115
  end
116
end
117

118
# Multi-field text searching on models.
119
class RESTFramework::ModelSearchFilter < RESTFramework::BaseFilter
1✔
120
  # Get a list of search fields for the current action. Fallback to columns because we need an
121
  # explicit list of columns to search on, so `nil` is useless in this context.
122
  def _get_fields
1✔
123
    return @controller.class.search_fields || @controller.get_fields(fallback: true)
13✔
124
  end
125

126
  # Filter data according to the request query parameters.
127
  def get_filtered_data(data)
1✔
128
    fields = self._get_fields
13✔
129
    search = @controller.request.query_parameters[@controller.class.search_query_param]
13✔
130

131
    # Ensure we use array conditions to prevent SQL injection.
132
    if search.present? && !fields.empty?
13✔
133
      return data.where(
×
134
        fields.map { |f|
135
          "CAST(#{f} AS VARCHAR) #{@controller.class.search_ilike ? "ILIKE" : "LIKE"} ?"
×
136
        }.join(" OR "),
137
        *(["%#{search}%"] * fields.length),
×
138
      )
139
    end
140

141
    return data
13✔
142
  end
143
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc