Class: Fe::Element

Inherits:
ApplicationRecord show all
Defined in:
app/models/fe/element.rb

Direct Known Subclasses

Paragraph, Question, QuestionGrid, Section

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#old_idObject

Returns the value of attribute old_id.



6
7
8
# File 'app/models/fe/element.rb', line 6

def old_id
  @old_id
end

Class Method Details

.create_from_import(element_data, page, question_sheet) ⇒ Object



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'app/models/fe/element.rb', line 325

def self.create_from_import(element_data, page, question_sheet)
  element_data[:old_id] = element_data.delete('id')
  children = element_data.delete(:children)
  element = element_data['kind'].constantize.create!(element_data)
  question_sheet.element_id_mappings[element.old_id] = element.id
  children.each do |child|
    byebug unless child.class == Hash
    child_element = create_from_import(child, page, question_sheet)
    if child['choice_field_id'].present?
      child_element.choice_field_id = element.id
    end
    byebug if child_element.label == 'Your Name:'
    if child['question_grid_id'].present?
      child_element.question_grid_id = element.id
    end
    child_element.save!
  end
  element
end

.max_label_lengthObject



274
275
276
# File 'app/models/fe/element.rb', line 274

def self.max_label_length
  @@max_label_length ||= Fe::Element.columns.find{ |c| c.name == "label" }.limit
end

Instance Method Details

#all_elementsObject

include nested elements



247
248
249
250
251
252
253
254
255
256
257
# File 'app/models/fe/element.rb', line 247

def all_elements
  if respond_to?(:elements)
    elements.reload
    #(elements + elements.collect(&:all_elements)).flatten
    elements.collect{ |el|
      [el, el.all_elements]
    }.flatten
  else
    []
  end
end

#conditional_answersObject



264
265
266
# File 'app/models/fe/element.rb', line 264

def conditional_answers
  conditional_answer.split(';').collect(&:strip)
end

#conditional_match(answer_sheet) ⇒ Object



268
269
270
271
272
# File 'app/models/fe/element.rb', line 268

def conditional_match(answer_sheet)
  displayed_response = display_response(answer_sheet)
  return false unless displayed_response && conditional_answer
  conditional_answers.include?(displayed_response)
end

#content(locale = nil) ⇒ Object



71
72
73
# File 'app/models/fe/element.rb', line 71

def content(locale = nil)
  content_translations[locale].present? ? content_translations[locale] : self[:content]
end

#css_classesObject



321
322
323
# File 'app/models/fe/element.rb', line 321

def css_classes
  css_class.to_s.split(' ').collect(&:strip)
end

#duplicate(page, parent = nil) ⇒ Object

copy an item and all it’s children



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'app/models/fe/element.rb', line 226

def duplicate(page, parent = nil)
  new_element = self.class.new(self.attributes.except('id', 'created_at', 'updated_at'))
  case parent.class.to_s
    when "Fe::QuestionGrid", "Fe::QuestionGridWithTotal"
      new_element.question_grid_id = parent.id
    when "Fe::ChoiceField"
      new_element.choice_field_id = parent.id
  end
  new_element.position = parent.elements.maximum(:position).to_i + 1 if parent
  new_element.save!(validate: false)
  Fe::PageElement.create(element: new_element, page: page) unless parent

  # duplicate children
  if respond_to?(:elements) && elements.present?
    elements.each {|e| e.duplicate(page, new_element)}
  end

  new_element
end

#export_hashObject



345
346
347
348
# File 'app/models/fe/element.rb', line 345

def export_hash
  children = choice_field_children.collect(&:export_hash)
  self.attributes.to_hash.merge(children: children)
end

#export_to_yamlObject



350
351
352
# File 'app/models/fe/element.rb', line 350

def export_to_yaml
  export_hash.to_yaml
end

#has_response?(answer_sheet = nil) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
# File 'app/models/fe/element.rb', line 85

def has_response?(answer_sheet = nil)
  false
end

#hidden?(answer_sheet = nil, page = nil) ⇒ Boolean

use page if it’s passed in, otherwise it will revert to the first page in answer_sheet

Returns:

  • (Boolean)


174
175
176
177
178
# File 'app/models/fe/element.rb', line 174

def hidden?(answer_sheet = nil, page = nil)
  page ||= pages_on.detect{ |p| answer_sheet.question_sheets.include?(p.question_sheet) }
  return true if !page || page.hidden?(answer_sheet)
  return page.all_hidden_elements(answer_sheet).include?(self)
end

#hidden_by_choice_field?(answer_sheet) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
165
166
# File 'app/models/fe/element.rb', line 162

def hidden_by_choice_field?(answer_sheet)
  choice_field.present? &&
    choice_field.is_a?(Fe::ChoiceField) &&
    choice_field.is_response_false(answer_sheet)
end

#hidden_by_conditional?(answer_sheet, page) ⇒ Boolean

Returns:

  • (Boolean)


154
155
156
157
158
159
160
# File 'app/models/fe/element.rb', line 154

def hidden_by_conditional?(answer_sheet, page)
  return false unless answer_sheet.question_sheets.include?(page.question_sheet)
  prev_el = previous_element(page.question_sheet, page)
  prev_el.is_a?(Fe::Question) &&
    prev_el.conditional == self &&
    !prev_el.conditional_match(answer_sheet)
end

#label(locale = nil) ⇒ Object

HUMANIZED_ATTRIBUTES =

slug: "Variable"

changed.include?(‘address1’)

def self.human_attrib_name(attr)

HUMANIZED_ATTRIBUTES[attr.to_sym] || super

end



67
68
69
# File 'app/models/fe/element.rb', line 67

def label(locale = nil)
  label_translations[locale].present? ? label_translations[locale] : self[:label]
end

#limit(answer_sheet = nil) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/models/fe/element.rb', line 89

def limit(answer_sheet = nil)
  if answer_sheet && object_name.present? && attribute_name.present?
    begin
      unless eval("answer_sheet." + self.object_name + ".nil?")
        klass = eval("answer_sheet." + self.object_name + ".class")
        column = klass.columns_hash[self.attribute_name]
        return column.limit
      end
    rescue
      nil
    end
  end
end

#matches_filter(filter) ⇒ Object

matches in an AND method; if requested we can add a second filter method later to match on an OR basis



317
318
319
# File 'app/models/fe/element.rb', line 317

def matches_filter(filter)
  filter.all? { |method| self.send(method) }
end

#page_id(page = nil) ⇒ Object



207
208
209
210
211
212
213
# File 'app/models/fe/element.rb', line 207

def page_id(page = nil)
  if page
    page.id
  else
    pages.first.try(:id)
  end
end

#pages_onObject

returns all pages this element is on, whether that be directly, through a grid, or as a choice field conditional option



80
81
82
83
# File 'app/models/fe/element.rb', line 80

def pages_on
  all_pages = pages.reload + [question_grid, question_grid_with_total, choice_field].compact.collect(&:pages_on)
  all_pages.flatten.uniq
end

#position(page = nil) ⇒ Object



189
190
191
192
193
194
195
# File 'app/models/fe/element.rb', line 189

def position(page = nil)
  if page
    page_elements.where(page_id: page.id).first.try(:position)
  else
    self[:position]
  end
end

#previous_element(question_sheet, page = nil) ⇒ Object

assume each element is on a question sheet only once to make things simpler. if not, just take the first one NOTE: getting the previous_element isn’t an expensive operation any more because of the all_elements_id cache



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'app/models/fe/element.rb', line 105

def previous_element(question_sheet, page = nil)
  return false unless question_sheet
  page ||= pages_on.detect{ |p| p.question_sheet == question_sheet }

  index = page.all_element_ids_arr.index(self.id)
  unless index
    # this can happen for yesno options, since they're rendered as elements but aren't on the page or in a grid
    # but just in case self is an element on the page and the element_ids got out of sync, rebuild the all_element_ids
    # and try again
    page.rebuild_all_element_ids
    index = page.all_element_ids_arr.index(self.id)
  end
  if index && index > 0 && prev_el_id = page.all_element_ids_arr[index-1]
    # occasionally the all_elements_ids_arr can get out of sync here, resulting in no element found
    el = Fe::Element.find_by(id: prev_el_id)
    unless el
      page.rebuild_all_element_ids
      index = page.all_element_ids_arr.index(self.id)
      prev_el_id = page.all_element_ids_arr[index-1]
      el = Fe::Element.find(prev_el_id) # give an error at this point if it's not found
    end

    return el
  end
end

#ptemplateObject

by default the partial for an element matches the class name (override as necessary)



221
222
223
# File 'app/models/fe/element.rb', line 221

def ptemplate
  self.class.to_s.underscore
end

#question?Boolean

Returns:

  • (Boolean)


215
216
217
# File 'app/models/fe/element.rb', line 215

def question?
  self.kind_of?(Question)
end

#required?(answer_sheet = nil, page = nil) ⇒ Boolean

use page if it’s passed in, otherwise it will revert to the first page in answer_sheet

Returns:

  • (Boolean)


181
182
183
184
185
186
187
# File 'app/models/fe/element.rb', line 181

def required?(answer_sheet = nil, page = nil)
  if answer_sheet && hidden?(answer_sheet, page)
    return false
  else
    required == true
  end
end

#reuseable?Boolean

Returns:

  • (Boolean)


259
260
261
262
# File 'app/models/fe/element.rb', line 259

def reuseable?
  return false if Fe.never_reuse_elements
  (self.is_a?(Fe::Question) || self.is_a?(Fe::QuestionGrid) || self.is_a?(Fe::QuestionGridWithTotal))
end

#set_conditional_elementObject



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'app/models/fe/element.rb', line 278

def set_conditional_element
  case conditional_type
  when "Fe::Element"
    pages_on.each do |page|

      if index = page.all_element_ids_arr.index(self.id)
        self.conditional_id = page.all_element_ids_arr[index+1]
      else
        self.conditional_id = nil
      end
    end
  when ""
    # keep conditional_type nil instead of empty to be consistent
    self.conditional_type = nil
  end
end

#set_position(position, page = nil) ⇒ Object



197
198
199
200
201
202
203
204
205
# File 'app/models/fe/element.rb', line 197

def set_position(position, page = nil)
  if page
    pe = page_elements.where(page_id: page.id).first
    pe.update_attribute(:position, position) if pe
  else
    self[:position] = position
  end
  position
end

#tooltip(locale = nil) ⇒ Object



75
76
77
# File 'app/models/fe/element.rb', line 75

def tooltip(locale = nil)
  tip_translations[locale].present? ? tip_translations[locale] : self[:tooltip]
end

#update_any_previous_conditional_elementsObject



295
296
297
298
299
300
301
302
303
304
305
# File 'app/models/fe/element.rb', line 295

def update_any_previous_conditional_elements
  pages_on.each do |page|
    index = page.all_element_ids_arr.index(self.id)
    if index && index > 0
      prev_el = Fe::Element.find(page.all_element_ids_arr[index-1])
      if prev_el.conditional_type == "Fe::Element"
        prev_el.update_column(:conditional_id, id)
      end
    end
  end
end

#update_page_all_element_idsObject



307
308
309
310
311
312
313
# File 'app/models/fe/element.rb', line 307

def update_page_all_element_ids
  [question_grid, question_grid_with_total, choice_field].compact.each do |field|
    field.update_page_all_element_ids
  end

  pages.reload.each do |p| p.rebuild_all_element_ids end
end

#visibility_affecting_element_idsObject

return an array of all elements whose answers or visibility might affect the visibility of this element



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'app/models/fe/element.rb', line 133

def visibility_affecting_element_ids
  return @visibility_affecting_element_ids if @visibility_affecting_element_ids

  # the form doesn't change much so caching on the last updated element will
  # provide a good balance of speed and cache invalidation
  Rails.cache.fetch([self, 'element#visibility_affecting_element_ids', Fe::Element.order('updated_at desc, id desc').first]) do
    elements = []

    elements << question_grid if question_grid
    elements << choice_field if choice_field
    elements += Fe::Element.where(conditional_type: 'Fe::Element', conditional_id: id)
    element_ids = elements.collect(&:id) +
      elements.collect { |e| e.visibility_affecting_element_ids }.flatten
    element_ids.uniq
  end
end

#visibility_affecting_questionsObject



150
151
152
# File 'app/models/fe/element.rb', line 150

def visibility_affecting_questions
  Fe::Question.where(id: visibility_affecting_element_ids)
end

#visible?(answer_sheet = nil, page = nil) ⇒ Boolean

use page if it’s passed in, otherwise it will revert to the first page in answer_sheet

Returns:

  • (Boolean)


169
170
171
# File 'app/models/fe/element.rb', line 169

def visible?(answer_sheet = nil, page = nil)
  !hidden?(answer_sheet, page)
end