class Rticles::Paragraph

Attributes

after_id[RW]
before_id[RW]
choices[W]

Public Class Methods

generate_html(paragraphs, options={}) click to toggle source
# File lib/rticles/paragraph.rb, line 238
def self.generate_html(paragraphs, options={})
  paragraph_groups = []
  paragraphs.each do |paragraph|
    if paragraph.continuation?
      paragraph_groups.last.push(paragraph)
    else
      paragraph_groups.push([paragraph])
    end
  end
  generate_html_for_paragraph_groups(paragraph_groups, options)
end

Protected Class Methods

generate_html_for_paragraph_groups(paragraph_groups, options={}) click to toggle source
# File lib/rticles/paragraph.rb, line 252
def self.generate_html_for_paragraph_groups(paragraph_groups, options={})
  previous_type = nil

  # Remove paragraph groups that should not be displayed, given the choices that have been passed in
  paragraph_groups = paragraph_groups.map do |pg|
    first_paragraph = pg[0]
    first_paragraph.choices = options[:choices]
    if first_paragraph.resolve_choices(first_paragraph.body)
      pg
    else
      nil
    end
  end
  paragraph_groups = paragraph_groups.compact

  return "" if paragraph_groups.empty?

  # Handle last paragraph group separately so we can do list punctuation if necessary.

  paragraph_groups_except_last = paragraph_groups[0..-2]
  last_paragraph_group = paragraph_groups[-1]

  html = paragraph_groups_except_last.inject("") do |memo, paragraph_group|
    # FIXME: Don't generate HTML by interpolating into a string;
    # use some standard library function that provides some safe
    # escaping defaults, etc..

    if options[:list]
      options[:list_punctuation] = ';'
    end

    first_paragraph = paragraph_group[0]

    if first_paragraph.heading?
      if previous_type == :paragraph
        memo += "</ol>"
      end
      if paragraph_group.length == 1
        memo += generate_html_for_paragraphs(paragraph_group, options)
      else
        memo += "<hgroup>#{generate_html_for_paragraphs(paragraph_group, options)}</hgroup>"
      end
      previous_type = :heading
    else
      unless previous_type == :paragraph
        memo += "<ol>"
      end
      index = first_paragraph.index(options[:choices])
      li_opening_tag = "<li value=\"#{index}\">"
      memo += "#{li_opening_tag}#{generate_html_for_paragraphs(paragraph_group, options)}</li>"
      previous_type = :paragraph
    end

    memo
  end

  # Process final paragraph group
  if options[:list]
    options[:list_punctuation] = '.'
  end

  first_paragraph = last_paragraph_group[0]

  if first_paragraph.heading?
    if previous_type == :paragraph
      html += "</ol>"
    end
    if last_paragraph_group.length == 1
      html += generate_html_for_paragraphs(last_paragraph_group, options)
    else
      html += "<hgroup>#{generate_html_for_paragraphs(last_paragraph_group, options)}</hgroup>"
    end
    previous_type = :heading
  else
    unless previous_type == :paragraph
      html += "<ol>"
    end
    index = first_paragraph.index(options[:choices])
    li_opening_tag = "<li value=\"#{index}\">"
    html += "#{li_opening_tag}#{generate_html_for_paragraphs(last_paragraph_group, options)}</li>"
    previous_type = :paragraph
  end

  if previous_type == :paragraph
    html += "</ol>"
  end
end
generate_html_for_paragraphs(paragraphs, options={}) click to toggle source
# File lib/rticles/paragraph.rb, line 340
def self.generate_html_for_paragraphs(paragraphs, options={})
  paragraphs.inject("") do |memo, paragraph|
    body = paragraph.body_for_display({:with_index => true}.merge(options))
    return memo if body.nil?

    if paragraph.heading?
      memo += "<h#{paragraph.heading_level}>#{body}#{options[:list_punctuation]}</h#{paragraph.heading_level}>"
    else
      memo += "#{body}#{options[:list_punctuation]}"
    end

    if !paragraph.children.empty?
      memo += generate_html(paragraph.children, options.merge(list: paragraph.list?, list_punctuation: nil))
    end
    memo
  end
end

Public Instance Methods

ancestors() click to toggle source
# File lib/rticles/paragraph.rb, line 91
def ancestors
  node = self
  nodes = []
  nodes.push(node = node.parent) while node.parent
  nodes
end
body_for_display(options={}) click to toggle source
# File lib/rticles/paragraph.rb, line 164
def body_for_display(options={})
  options = options.with_indifferent_access

  if options[:insertions]
    @insertions = options[:insertions]
  end

  if options[:choices]
    @choices = options[:choices]
  end

  with_meta_characters = options[:with_meta_characters] || false

  result = resolve_choices(body)
  return result if result.nil?

  result = resolve_references(result, with_meta_characters)
  result = resolve_insertions(result)

  if options[:with_index] && full_index(true, choices, options[:numbering_config])
    result = "#{full_index} #{result}"
  end

  result
end
body_with_resolved_references(with_meta_characters=false) click to toggle source
# File lib/rticles/paragraph.rb, line 190
def body_with_resolved_references(with_meta_characters=false)
  resolve_references(body, with_meta_characters)
end
can_indent?() click to toggle source
# File lib/rticles/paragraph.rb, line 106
def can_indent?
  !!higher_item
end
can_move_higher?() click to toggle source
# File lib/rticles/paragraph.rb, line 102
def can_move_higher?
  !!higher_item
end
can_move_lower?() click to toggle source
# File lib/rticles/paragraph.rb, line 98
def can_move_lower?
  !!lower_item
end
can_outdent?() click to toggle source
# File lib/rticles/paragraph.rb, line 118
def can_outdent?
  !!parent_id
end
full_index(recalculate=false, choices=nil, numbering_config=nil) click to toggle source
# File lib/rticles/paragraph.rb, line 72
def full_index(recalculate=false, choices=nil, numbering_config=nil)
  return nil if heading? || continuation?

  return @full_index if @full_index && !recalculate

  if numbering_config.nil?
    numbering_config = Rticles::Numbering::Config.new
  end

  if numbering_config.innermost_only
    @full_index = numbering_config[level].format.sub('#', Rticles::Numbering.number_to_string(index(choices), numbering_config[level].style))
  else
    @full_index = ancestors.unshift(self).reverse.map do |p|
      numbering_config[p.level].format.sub('#', Rticles::Numbering.number_to_string(p.index(choices), numbering_config[p.level].style))
    end
    @full_index = @full_index.join(numbering_config.separator)
  end
end
heading?() click to toggle source
# File lib/rticles/paragraph.rb, line 48
def heading?
  heading && heading > 0
end
heading_level() click to toggle source
# File lib/rticles/paragraph.rb, line 52
def heading_level
  ancestors.length + (heading ? heading : 0)
end
higher_items() click to toggle source
# File lib/rticles/paragraph.rb, line 132
def higher_items
  return nil unless in_list?
  acts_as_list_class.where(
    "#{scope_condition} AND #{position_column} < #{(send(position_column).to_i).to_s}"
  )
end
indent!() click to toggle source
# File lib/rticles/paragraph.rb, line 110
def indent!
  return unless can_indent?
  new_parent_id = higher_item.id
  remove_from_list
  update_attribute(:parent_id, new_parent_id)
  send(:assume_bottom_position)
end
index(choices=nil) click to toggle source
# File lib/rticles/paragraph.rb, line 60
def index(choices=nil)
  return nil if heading? || continuation?

  predecessors = higher_items.where(['(heading = 0 OR heading IS NULL) AND (continuation = ? OR continuation IS NULL)', false])

  if choices.present?
    predecessors = predecessors.for_choices(choices)
  end

  predecessors.count + 1
end
level() click to toggle source
# File lib/rticles/paragraph.rb, line 56
def level
  ancestors.length + 1
end
lower_items() click to toggle source
# File lib/rticles/paragraph.rb, line 139
def lower_items
  return nil unless in_list?
  acts_as_list_class.where(
    "#{scope_condition} AND #{position_column} > #{(send(position_column).to_i).to_s}"
  )
end
normalise_references() click to toggle source
# File lib/rticles/paragraph.rb, line 154
def normalise_references
  return if body.blank?
  raw_reference_re = /!(\d\.)*\d/
  Rails.logger.debug("Body: #{body}")
  self.body = body.gsub(raw_reference_re) do |match|
    raw_reference = match.sub('!', '')
    '#rticles#' + document.paragraph_for_reference(raw_reference).id.to_s
  end
end
outdent!() click to toggle source
# File lib/rticles/paragraph.rb, line 122
def outdent!
  return unless can_outdent?
  new_parent_id = parent.parent_id
  new_position = parent.position + 1
  reparent_lower_items_under_self
  remove_from_list
  update_attribute(:parent_id, new_parent_id)
  insert_at(new_position)
end
prepare_for_editing() click to toggle source
# File lib/rticles/paragraph.rb, line 233
def prepare_for_editing
  self.body = body_with_resolved_references(true)
  self
end
reparent_lower_items_under_self() click to toggle source
# File lib/rticles/paragraph.rb, line 146
def reparent_lower_items_under_self
  return unless in_list?
  acts_as_list_class.update_all(
    "#{position_column} = (#{position_column} - #{position}), parent_id = #{id}", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
  )
end
resolve_choices(string) click to toggle source
# File lib/rticles/paragraph.rb, line 218
def resolve_choices(string)
  choice_re = /\A#rticles#(true|false)#([A-Za-z_]+) /
  match = string.match(choice_re)
  return string if !match

  choice_name = match[2]
  choice_parameter = match[1]

  if (choices[choice_name] && choice_parameter == 'true') || (!choices[choice_name] && choice_parameter == 'false')
    string.sub(choice_re, '')
  else
    nil
  end
end
resolve_insertions(string) click to toggle source
# File lib/rticles/paragraph.rb, line 205
def resolve_insertions(string)
  return string if string.blank?
  insertion_re = /#rticles#([A-Za-z_]+)/
  string.gsub(insertion_re) do |match|
    insertion_name = match.sub('#rticles#', '')
    if insertions[insertion_name].present?
      insertions[insertion_name].to_s.gsub("\n", "<br>")
    else
      "[#{insertion_name.humanize.upcase}]"
    end
  end
end
resolve_references(string, with_meta_characters=false) click to toggle source
# File lib/rticles/paragraph.rb, line 194
def resolve_references(string, with_meta_characters=false)
  return string if string.blank?
  normalised_reference_re = /#rticles#(\d+)/
  string.gsub(normalised_reference_re) do |match|
    normalised_reference = match.sub('#rticles#', '')
    result = with_meta_characters ? '!' : ''
    result += document.paragraphs.find(normalised_reference).full_index
    result
  end
end
set_document_id() click to toggle source
# File lib/rticles/paragraph.rb, line 27
def set_document_id
  if parent
    self.document_id ||= parent.document_id
  end
end
set_parent_and_position() click to toggle source
# File lib/rticles/paragraph.rb, line 36
def set_parent_and_position
  if before_id.present?
    sibling = self.class.find(before_id)
    self.update_attribute(:parent_id, sibling.parent_id)
    insert_at(sibling.position)
  elsif after_id.present?
    sibling = self.class.find(after_id)
    self.update_attribute(:parent_id, sibling.parent_id)
    insert_at(self.class.find(after_id).position + 1)
  end
end

Protected Instance Methods

choices() click to toggle source
# File lib/rticles/paragraph.rb, line 367
def choices
  if @choices
    @choices.with_indifferent_access
  else
    {}.with_indifferent_access
  end
end
insertions() click to toggle source
# File lib/rticles/paragraph.rb, line 358
def insertions
  return @insertions.with_indifferent_access if @insertions
  begin
    (parent || document).insertions.with_indifferent_access
  rescue NoMethodError
    raise RuntimeError, "parent was nil when finding insertions; I am: #{self.inspect}"
  end
end