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



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'app/models/fe/element.rb', line 318

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



267
268
269
# File 'app/models/fe/element.rb', line 267

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



240
241
242
243
244
245
246
247
248
249
250
# File 'app/models/fe/element.rb', line 240

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



257
258
259
# File 'app/models/fe/element.rb', line 257

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

#conditional_match(answer_sheet) ⇒ Object



261
262
263
264
265
# File 'app/models/fe/element.rb', line 261

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



64
65
66
# File 'app/models/fe/element.rb', line 64

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

#css_classesObject



314
315
316
# File 'app/models/fe/element.rb', line 314

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

#duplicate(page, parent = nil) ⇒ Object

copy an item and all it’s children



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'app/models/fe/element.rb', line 219

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



338
339
340
341
# File 'app/models/fe/element.rb', line 338

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

#export_to_yamlObject



343
344
345
# File 'app/models/fe/element.rb', line 343

def export_to_yaml
  export_hash.to_yaml
end

#has_response?(answer_sheet = nil) ⇒ Boolean

Returns:

  • (Boolean)


78
79
80
# File 'app/models/fe/element.rb', line 78

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)


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

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)


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

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)


147
148
149
150
151
152
153
# File 'app/models/fe/element.rb', line 147

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



60
61
62
# File 'app/models/fe/element.rb', line 60

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

#limit(answer_sheet = nil) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/models/fe/element.rb', line 82

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



310
311
312
# File 'app/models/fe/element.rb', line 310

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

#page_id(page = nil) ⇒ Object



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

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



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

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



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

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



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'app/models/fe/element.rb', line 98

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)



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

def ptemplate
  self.class.to_s.underscore
end

#question?Boolean

Returns:

  • (Boolean)


208
209
210
# File 'app/models/fe/element.rb', line 208

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)


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

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)


252
253
254
255
# File 'app/models/fe/element.rb', line 252

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



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'app/models/fe/element.rb', line 271

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



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

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



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

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

#update_any_previous_conditional_elementsObject



288
289
290
291
292
293
294
295
296
297
298
# File 'app/models/fe/element.rb', line 288

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



300
301
302
303
304
305
306
# File 'app/models/fe/element.rb', line 300

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



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'app/models/fe/element.rb', line 126

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



143
144
145
# File 'app/models/fe/element.rb', line 143

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)


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

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