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

MushroomObserver / mushroom-observer / 13508521788

24 Feb 2025 09:48PM UTC coverage: 93.501% (+0.002%) from 93.499%
13508521788

push

github

web-flow
Merge pull request #2749 from MushroomObserver/nimmo-clean-up-recent-query-refactors

Script to update query records

67 of 69 new or added lines in 5 files covered. (97.1%)

1 existing line in 1 file now uncovered.

27336 of 29236 relevant lines covered (93.5%)

540.14 hits per line

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

96.92
/app/classes/query/modules/class_methods.rb
1
# frozen_string_literal: true
2

3
# Methods that are available to instances as class methods, and to ::Query.
4
# ::Query is a convenience delegator class so callers can access these methods.
5
module Query::Modules::ClassMethods
1✔
6
  def self.included(base)
1✔
7
    base.extend(ClassMethods)
2✔
8
  end
9

10
  module ClassMethods
1✔
11
    # Query::Modules::ActiveRecord
12
    def safe_find(id)
1✔
13
      find(id)
185✔
14
    rescue ::ActiveRecord::RecordNotFound
15
      nil
22✔
16
    end
17

18
    def find(id)
1✔
19
      record = QueryRecord.find(id)
185✔
20
      query = Query.rebuild_from_description(record.description)
163✔
21
      record.query = query
163✔
22
      query.record = record
163✔
23
      QueryRecord.cleanup
163✔
24
      query
163✔
25
    end
26

27
    def lookup_and_save(*)
1✔
28
      query = lookup(*)
123✔
29
      query.record.save!
123✔
30
      query
123✔
31
    end
32

33
    def lookup(*)
1✔
34
      query = Query.new(*)
3,147✔
35
      record = get_record(query)
3,128✔
36
      record.query = query
3,128✔
37
      query.record = record
3,128✔
38
      QueryRecord.cleanup
3,128✔
39
      query
3,128✔
40
    end
41

42
    def get_record(query)
1✔
43
      desc = query.serialize
3,128✔
44
      QueryRecord.find_by(description: desc) ||
3,128✔
45
        QueryRecord.new(
46
          description: desc,
47
          updated_at: Time.zone.now,
48
          access_count: 0
49
        )
50
    end
51

52
    # Query::Modules::Serialization
53
    #
54
    # Get the model from the serialized params and instantiate new Query.
55
    def rebuild_from_description(description)
1✔
56
      model, params = deserialize(description)
164✔
57
      ::Query.new(model, params)
164✔
58
    end
59

60
    def deserialize(description)
1✔
61
      params = JSON.parse(description).deep_symbolize_keys
164✔
62
      model = params.delete(:model)
164✔
63
      [model, params]
164✔
64
    end
65

66
    # Query::Modules::RelatedQueries
67
    #
68
    # Query needs to know which joins are necessary to make these conversions
69
    # work. Need to maintain RELATED_TYPES if the Query class is updated.
70
    # These could be derived by snooping through each Query subclass's
71
    # parameter_declarations, but that seems wasteful; there are not so many.
72
    #
73
    # target_model.name.to_sym: [:Association, :AnotherAssociation],
74
    RELATED_QUERIES = {
75
      Image: [:Image, :Observation],
1✔
76
      Location: [:Location, :LocationDescription, :Name, :Observation],
77
      LocationDescription: [:Location],
78
      Name: [:Name, :NameDescription, :Observation],
79
      NameDescription: [:Name],
80
      Observation: [:Image, :Location, :Name, :Observation, :Sequence]
81
    }.freeze
82

83
    def related?(target, filter)
1✔
84
      return false unless RELATED_QUERIES.key?(target)
399✔
85

86
      RELATED_QUERIES[target].include?(filter)
398✔
87
    end
88

89
    def current_or_related_query(target, filter, current_query)
1✔
90
      if target == filter
462✔
91
        current_query
17✔
92
      elsif (restored_query = restorable_query(target, current_query))
445✔
93
        restored_query
40✔
94
      elsif (new_query = new_query_with_subquery(target, filter, current_query))
405✔
95
        new_query
404✔
96
      end
97
    end
98

99
    # Check the query params for a relevant existing query nested within.
100
    # This only checks for the key name of the right subquery. It would be
101
    # more work to check for hash equality, because the nested hash has the
102
    # :model param too, to be easily deserialized and rebuilt.
103
    # NOTE: Our custom method `deep_find` returns an array of matches.
104
    def restorable_query(target, current_query)
1✔
105
      subquery_param = current_query.class.find_subquery_param_name(target)
445✔
106
      restorable_query_params = current_query.params.deep_find(subquery_param)
445✔
107
      return false if restorable_query_params.blank?
445✔
108

109
      lookup(target, restorable_query_params.first)
40✔
110
    end
111

112
    # Make a new query using the current_query as the subquery. Note that this
113
    # will continue nesting queries unless a restorable query is found above.
114
    def new_query_with_subquery(target, filter, current_query)
1✔
115
      query_class = "Query::#{target.to_s.pluralize}".constantize
405✔
116
      return unless (subquery = query_class.find_subquery_param_name(filter))
405✔
117

118
      params = current_query.params.compact
404✔
119
      subquery_params = add_default_subquery_conditions(target, filter, params)
404✔
120

121
      lookup(target, "#{subquery}": subquery_params)
404✔
122
    end
123

124
    def find_subquery_param_name(filter)
1✔
125
      parameter_declarations.key({ subquery: filter })
850✔
126
    end
127

128
    def add_default_subquery_conditions(target, filter, params)
1✔
129
      return params unless needs_is_collection_location(target, filter, params)
404✔
130

NEW
131
      params.merge(is_collection_location: true)
×
132
    end
133

134
    def needs_is_collection_location(target, filter, params)
1✔
135
      target == Location && filter == :Observation &&
404✔
NEW
136
        (params[:project] || params[:species_list]) &&
×
137
        params[:is_collection_location].blank?
138
    end
139
  end
140
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

© 2026 Coveralls, Inc