Class: Refine::Conditions::OptionCondition

Inherits:
Condition
  • Object
show all
Includes:
ActiveModel::Validations, HasClauses, UsesAttributes
Defined in:
app/models/refine/conditions/option_condition.rb

Constant Summary collapse

CLAUSE_EQUALS =
Clauses::EQUALS
CLAUSE_DOESNT_EQUAL =
Clauses::DOESNT_EQUAL
CLAUSE_IN =
Clauses::IN
CLAUSE_NOT_IN =
Clauses::NOT_IN
CLAUSE_SET =
Clauses::SET
CLAUSE_NOT_SET =
Clauses::NOT_SET
I18N_PREFIX =
"refine.refine_blueprints.option_condition."

Constants included from SupportsFlatQueries

SupportsFlatQueries::LEFT_JOIN_CLAUSES

Instance Attribute Summary collapse

Attributes inherited from Condition

#attribute, #before_validations, #clause, #display, #ensurances, #filter, #id, #is_flat_query, #is_refinement

Instance Method Summary collapse

Methods included from UsesAttributes

#apply_relationship_attribute, #can_use_where_in_relationship_subquery?, #create_pending_has_many_through_subquery, #create_pending_wherein_subquery, #get_reflection_object, #group, #is_relationship_attribute?, #key_1, #key_2, #raw_attribute, #should_inverse_clause?, #with_attribute

Methods included from HasClauses

#before_clause_validation, #boot_has_clauses, #clause_display_map, #custom_clauses, #ensure_clause, #ensure_clauses, #get_clause_by_id, #get_clauses, included, #only_clauses, #remap_clause_displays, #update_show_clauses, #with_clauses, #without_clauses

Methods inherited from Condition

#add_ensurance, #add_messages, #add_rules, #apply, #approved_clauses, #before_validate, #boot_traits, #call_proc_if_callable, #clause_display, #clause_exists?, #clause_in_approved_list?, #ensure_attribute_configured, #ensure_id, #evaluated_meta, #initialize, #messages, #recursively_evaluate_lazy_enumerable, #run_before_validate_validations, #run_ensurance_validations, #set_filter, #to_array, #update_value, #validate_user_input, #with_display

Methods included from SupportsFlatQueries

#add_pending_join, #add_pending_joins_if_needed, #apply_flat, #apply_flat_relational_condition, #condition_uses_different_database?, #get_foreign_key_from_relation, #get_through_reflection, #handle_flat_cross_database_condition, #handle_flat_relational_condition

Methods included from HasIcon

#category_icon_class, #category_icon_template, #has_icon?, #icon_class, #icon_container_class, #icon_template, #with_category_icon_class, #with_category_icon_template, #with_icon, #with_icon_class, #with_icon_container_class, #with_icon_template

Methods included from WithForcedIndex

#forced_index, #with_forced_index

Methods included from HasThroughIdRelationship

#through_id_relationship, #with_through_id_relationship

Methods included from HasRefinements

#apply_count_refinement, #apply_date_refinement, #apply_refinements, #define_count_condition, #define_date_condition, #get_count_refinement_condition, #get_date_refinement_condition, #has_any_refinements?, #has_count_refinement?, #has_date_refinement?, #refine_by_count, #refine_by_date, #refine_by_filter, #refinements_allowed?, #refinements_to_array

Methods included from HasMeta

#meta, #with_meta

Constructor Details

This class inherits a constructor from Refine::Conditions::Condition

Instance Attribute Details

#nil_option_id ⇒ Object (readonly)

Returns the value of attribute nil_option_id.



10
11
12
# File 'app/models/refine/conditions/option_condition.rb', line 10

def nil_option_id
  @nil_option_id
end

#options ⇒ Object (readonly)

Returns the value of attribute options.



10
11
12
# File 'app/models/refine/conditions/option_condition.rb', line 10

def options
  @options
end

#selected ⇒ Object (readonly)

Returns the value of attribute selected.



10
11
12
# File 'app/models/refine/conditions/option_condition.rb', line 10

def selected
  @selected
end

Instance Method Details

#apply_clause_doesnt_equal(value, table) ⇒ Object



240
241
242
243
244
245
246
# File 'app/models/refine/conditions/option_condition.rb', line 240

def apply_clause_doesnt_equal(value, table)
  if nil_option_selected?(value)
    apply_not_nil_query(value, table)
  else
    apply_not_equals(values_for_application(value, true), table)
  end
end

#apply_clause_equals(value, table) ⇒ Object



232
233
234
235
236
237
238
# File 'app/models/refine/conditions/option_condition.rb', line 232

def apply_clause_equals(value, table)
  if nil_option_selected?(value)
    apply_nil_query(value, table)
  else
    apply_equals(values_for_application(value, true), table)
  end
end

#apply_clause_in(value, table) ⇒ Object



210
211
212
213
214
215
216
217
218
# File 'app/models/refine/conditions/option_condition.rb', line 210

def apply_clause_in(value, table)
  normalized_values = values_for_application(value)

  if nil_option_selected?(value)
    table.grouping(table[:"#{attribute}"].in(normalized_values).or(table[:"#{attribute}"].eq(nil)))
  else
    table.grouping(table[:"#{attribute}"].in(normalized_values))
  end
end

#apply_clause_not_in(value, table) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
# File 'app/models/refine/conditions/option_condition.rb', line 220

def apply_clause_not_in(value, table)
  normalized_values = values_for_application(value)
  # Must check for only nil option selected here
  if nil_option_selected?(value) && value.one?
    table.grouping(table[:"#{attribute}"].not_eq(nil))
  elsif nil_option_selected?(value)
    table.grouping(table[:"#{attribute}"].not_in(normalized_values).or(table[:"#{attribute}"].not_eq(nil)))
  else
    table.grouping(table[:"#{attribute}"].not_in(normalized_values).or(table[:"#{attribute}"].eq(nil)))
  end
end

#apply_clause_not_set(table) ⇒ Object



256
257
258
# File 'app/models/refine/conditions/option_condition.rb', line 256

def apply_clause_not_set(table)
  table.grouping(table[:"#{attribute}"].eq_any([nil, ""]))
end

#apply_clause_set(table) ⇒ Object



252
253
254
# File 'app/models/refine/conditions/option_condition.rb', line 252

def apply_clause_set(table)
  table.grouping(table[:"#{attribute}"].not_eq_any([nil, ""]))
end

#apply_condition(input, table, inverse_clause) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'app/models/refine/conditions/option_condition.rb', line 147

def apply_condition(input, table, inverse_clause)
  value = input[:selected]
  # TODO: Triggers on "through" relationship. Other relationships?
  @clause = CLAUSE_IN if inverse_clause

  case clause
  when CLAUSE_SET
    apply_clause_set(table)

  when CLAUSE_NOT_SET
    apply_clause_not_set(table)

  when CLAUSE_EQUALS
    apply_clause_equals(value, table)

  when CLAUSE_DOESNT_EQUAL
    apply_clause_doesnt_equal(value, table)

  when CLAUSE_IN
    apply_clause_in(value, table)

  when CLAUSE_NOT_IN
    apply_clause_not_in(value, table)
  end
end

#apply_equals(value, table) ⇒ Object



206
207
208
# File 'app/models/refine/conditions/option_condition.rb', line 206

def apply_equals(value, table)
  table.grouping(table[:"#{attribute}"].eq(value))
end

#apply_nil_query(value, table) ⇒ Object



198
199
200
# File 'app/models/refine/conditions/option_condition.rb', line 198

def apply_nil_query(value, table)
  table.grouping(table[:"#{attribute}"].eq(nil))
end

#apply_not_equals(value, table) ⇒ Object



248
249
250
# File 'app/models/refine/conditions/option_condition.rb', line 248

def apply_not_equals(value, table)
  table.grouping(table[:"#{attribute}"].not_eq(value).or(table[:"#{attribute}"].eq(nil)))
end

#apply_not_nil_query(value, table) ⇒ Object



202
203
204
# File 'app/models/refine/conditions/option_condition.rb', line 202

def apply_not_nil_query(value, table)
  table.grouping(table[:"#{attribute}"].not_eq(nil))
end

#boot ⇒ Object



63
64
65
66
67
68
69
# File 'app/models/refine/conditions/option_condition.rb', line 63

def boot
  @nil_option_id = nil
  @options = nil
  # TODO @validate_selections = true
  with_meta({options: get_options})
  add_ensurance(ensure_options)
end

#clauses ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'app/models/refine/conditions/option_condition.rb', line 123

def clauses
  [
    Clause.new(CLAUSE_EQUALS, I18n.t("#{I18N_PREFIX}is"))
      .requires_inputs(["selected"])
      .with_meta({multiple: false}),

    Clause.new(CLAUSE_DOESNT_EQUAL, I18n.t("#{I18N_PREFIX}is_not"))
      .requires_inputs(["selected"])
      .with_meta({multiple: false}),

    Clause.new(CLAUSE_IN, I18n.t("#{I18N_PREFIX}is_one_of"))
      .requires_inputs(["selected"])
      .with_meta({multiple: true}),

    Clause.new(CLAUSE_NOT_IN, I18n.t("#{I18N_PREFIX}is_not_one_of"))
      .requires_inputs(["selected"])
      .with_meta({multiple: true}),

    Clause.new(CLAUSE_SET, I18n.t("#{I18N_PREFIX}is_set")),

    Clause.new(CLAUSE_NOT_SET, I18n.t("#{I18N_PREFIX}is_not_set"))
  ]
end

#component ⇒ Object



23
24
25
# File 'app/models/refine/conditions/option_condition.rb', line 23

def component
  "option-condition"
end

#ensure_no_duplicates(developer_options) ⇒ Object



115
116
117
118
119
120
121
# File 'app/models/refine/conditions/option_condition.rb', line 115

def ensure_no_duplicates(developer_options)
  id_array = developer_options.map { |option| option[:id] }
  duplicates = id_array.select { |id| id_array.count(id) > 1 }.uniq
  if duplicates.present?
    raise Refine::Conditions::Errors::OptionError.new(I18n.t("#{I18N_PREFIX}must_be_unique", duplicates: duplicates))
  end
end

#ensure_options ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'app/models/refine/conditions/option_condition.rb', line 98

def ensure_options
  proc do
    developer_options = get_options.call
    # Options must be sent in as an array
    if !developer_options.is_a? Array
      raise I18n.t("#{I18N_PREFIX}not_determined")
    end
    # Each option must be a hash of values that includes :id and :display
    developer_options.each do |option|
      if (!option.is_a? Hash) || option.keys.exclude?(:id) || option.keys.exclude?(:display)
        raise Refine::Conditions::Errors::OptionError.new(I18n.t("#{I18N_PREFIX}must_have_id_and_display"))
      end
    end
    ensure_no_duplicates(developer_options)
  end
end

#get_options ⇒ Object



90
91
92
93
94
95
96
# File 'app/models/refine/conditions/option_condition.rb', line 90

def get_options
  proc do
    @options = Refine::Rails.configuration.option_condition_ordering.call(
      call_proc_if_callable(options)
    )
  end
end

#human_readable(input) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'app/models/refine/conditions/option_condition.rb', line 27

def human_readable(input)
  current_clause = get_clause_by_id(input[:clause])
  display_values = input[:selected]&.map {|option_id| get_options.call.find{|option| option[:id] == option_id}[:display]}.to_a
  case input[:clause]
  when *[CLAUSE_EQUALS, CLAUSE_DOESNT_EQUAL]
    "#{display} #{current_clause.display} #{display_values.first}"
  when *[CLAUSE_IN, CLAUSE_NOT_IN]
    if display_values.length >= 3
      display_values = display_values.take(2) + ["..."]
    end
    "#{display} #{current_clause.display}: #{display_values.join(", ")}"
  when *[CLAUSE_SET, CLAUSE_NOT_SET]
    "#{display} #{current_clause.display}"
  else
    raise "#{input[:clause]} #{I18n.t("#{I18N_PREFIX}not_supported")}"
  end
end

#human_readable_value(input) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'app/models/refine/conditions/option_condition.rb', line 45

def human_readable_value(input)
  current_clause = get_clause_by_id(input[:clause])
  display_values = input[:selected]&.map {|option_id| get_options.call.find{|option| option[:id] == option_id}[:display]}.to_a
  case input[:clause]
  when *[CLAUSE_EQUALS, CLAUSE_DOESNT_EQUAL]
    display_values.first
  when *[CLAUSE_IN, CLAUSE_NOT_IN]
    if display_values.length >= 3
      display_values = display_values.take(2) + ["..."]
    end
    display_values.join(", ")
  when *[CLAUSE_SET, CLAUSE_NOT_SET]
    ""
  else
    raise "#{input[:clause]} #{I18n.t("#{I18N_PREFIX}not_supported")}"
  end
end

#nil_option_selected?(value) ⇒ Boolean

Returns:

  • (Boolean)


183
184
185
186
187
# File 'app/models/refine/conditions/option_condition.rb', line 183

def nil_option_selected?(value)
  # Return false if no nil option id
  return false unless nil_option_id
  value&.include? nil_option_id
end

#option_in_approved_list? ⇒ Boolean

Returns:

  • (Boolean)


79
80
81
82
83
84
85
86
87
88
# File 'app/models/refine/conditions/option_condition.rb', line 79

def option_in_approved_list?
  # TODO allow this to accept integers as well as strings. Right now must be a string.
  return if selected.nil?
  selected.each do |select|
    select.join if select.is_a? Array
    unless get_options.call.map { |option| option[:id] }.include? select
      errors.add(:base, I18n.t("#{I18N_PREFIX}not_approved", select: select))
    end
  end
end

#select_is_array ⇒ Object



75
76
77
# File 'app/models/refine/conditions/option_condition.rb', line 75

def select_is_array
  errors.add(:base, I18n.t("#{I18N_PREFIX}must_be_array")) unless selected.is_a?(Array)
end

#set_input_parameters(input) ⇒ Object



71
72
73
# File 'app/models/refine/conditions/option_condition.rb', line 71

def set_input_parameters(input)
  @selected = input[:selected]
end

#values_for_application(ids, single = false) ⇒ Object



189
190
191
192
193
194
195
196
# File 'app/models/refine/conditions/option_condition.rb', line 189

def values_for_application(ids, single = false)
  # Get developer configured options with nil_option_id removed and select only elements from requested ids
  # Extract values from either _value key or id key. _value can be a callable
  values = get_options.call.delete_if { |el| el[:id] == nil_option_id }
    .select { |value| ids.include? value[:id] }
    .map! { |value| (value.has_key? :_value) ? call_proc_if_callable(value[:_value]) : value[:id] }
  single ? values[0] : values
end

#with_nil_option(id) ⇒ Object



178
179
180
181
# File 'app/models/refine/conditions/option_condition.rb', line 178

def with_nil_option(id)
  @nil_option_id = id
  self
end

#with_options(developer_configured_options) ⇒ Object



173
174
175
176
# File 'app/models/refine/conditions/option_condition.rb', line 173

def with_options(developer_configured_options)
  @options = developer_configured_options
  self
end