class MdTransformer::Markdown
Class representing a parsed markdown file
Constants
- LOWEST_PRECEDENCE
The lowest valid precedence of a header, allows for up to H6 (###### Header)
Attributes
@return [Array] the array of child objects
@return [String] the content of the Markdown
object
@return [MdTransformer::Markdown, nil] nil or the parent of the current Markdown
object
@return [String] the title of the Markdown
object
Public Class Methods
Creates a new Markdown
object @param source [String] the markdown content or path to a markdown file @param options [Hash] the options hash @option options [Boolean] :file whether to treat the passed source as a file path @option options [MdTransformer::Markdown] :parent the parent of the new object @option options [String] :title the title of the new object @return [MdTransformer::Markdown] the new Markdown
object
# File lib/md_transformer/markdown.rb, line 32 def initialize(source = '', options = {}) @parent = options[:parent] @title = options[:title] || '' if options[:file] raise InvalidMarkdownPath, "Could not find markdown file at #{source}" unless File.exist?(source) source = File.read(source) @title ||= options[:file] end source = translate(source) parse!(source) end
Public Instance Methods
Compares Markdown
objects with other objects through string conversion @param other [Object] object to compare @return [Integer] the result of the <=> operator on the string values of both objects
# File lib/md_transformer/markdown.rb, line 164 def <=>(other) to_s <=> other.to_s end
Retrieves the child object at a given key if it exists @param key [String] the key of the child object @return [MdTransformer::Markdown, nil] the found child object or nil
# File lib/md_transformer/markdown.rb, line 102 def [](key) @children.find { |c| c.title == key } end
Sets the value of the child object at a given key. If the key does not exist, a new child object is created. @param key [String] the key of the child object @param value [String] the new value of the child object @return [String] the newly assigned value
# File lib/md_transformer/markdown.rb, line 110 def []=(key, value) child = self[key] || Markdown.new('', title: key, parent: self) child.content = value.to_s @children.push(child) unless key?(key) end
Updates the current object's Markdown
content @param value [String] the new content @return [String] the newly added content
# File lib/md_transformer/markdown.rb, line 48 def content=(value) # Reflow the content headers based on the child's levels (raise exception if level exceeds 6) m = Markdown.new(value.to_s) # Reassign the parent of the children to the current object and ensure the new depth is valid @children = m.children @children.each do |c| c.instance_variable_set(:@parent, self) validate_levels(c) end @content = m.content end
Digs through the hash for the child object at the given nested key(s) @param key [String] the first key to check @param rest [Array<String>] any number of nested string keys for which to find @return [MdTransformer::Markdown, nil] the found child object or nil
# File lib/md_transformer/markdown.rb, line 92 def dig(key, *rest) value = self[key] return value if value.nil? || rest.empty? value.dig(*rest) end
For a block { |k, v| … } @yield [k, v] Gives the key and value of the object
# File lib/md_transformer/markdown.rb, line 153 def each return enum_for(__method__) unless block_given? @children.each do |child| yield child.title, child end end
Checks for whether a child object has a given key @param key [String] the key of the child object @return [Boolean] whether the passed key exists
# File lib/md_transformer/markdown.rb, line 71 def key?(key) !self[key].nil? end
Gets all child object keys @return [Array] the array of child keys
# File lib/md_transformer/markdown.rb, line 64 def keys @children.map(&:title) end
Calculates the current nesting level of the Markdown
object @return [Integer] the nesting level of the object (0 for the root, +1 for each additional level)
# File lib/md_transformer/markdown.rb, line 145 def level return 0 if root? @parent.level + 1 end
Checks whether the current object is the root of the Markdown
object @return [Boolean] whether the current object is the root
# File lib/md_transformer/markdown.rb, line 139 def root? @parent.nil? end
Creates a string representing the markdown document's content from current content and all child content @param options [Hash] the options hash @option options [Boolean] :title (true) whether to include the title of the current object in the output @return [String] the constructed Markdown
string
# File lib/md_transformer/markdown.rb, line 120 def to_s(options = { title: true }) title_str = root? ? '' : "#{'#' * level} #{@title}\n" md_string = "#{options[:title] ? title_str : ''}#{@content}#{@children.map(&:to_s).join}" md_string << "\n" unless md_string.end_with?("\n") md_string end
Checks for whether a child object has a given value @param value [String] the value to check for @return [Boolean] whether the passed value exists
# File lib/md_transformer/markdown.rb, line 84 def value?(value) !@children.find { |c| c == value }.nil? end
Gets all child object values @return [Array] the array of child objects
# File lib/md_transformer/markdown.rb, line 77 def values @children end
Writes the current markdown object to a file @param path [String] the path to the new file @param options [Hash] the options hash @option options [Boolean] :create_dir (true) whether to create the parent directories of the path @return [Integer] the length of the newly created file
# File lib/md_transformer/markdown.rb, line 132 def write(path, options: { create_dir: true }) FileUtils.mkdir_p(File.dirname(path)) if options[:create_dir] File.write(path, to_s) end
Private Instance Methods
Parses the provided markdown string content into a the current object's content and children @param content [String] the string Markdown
content to parse
# File lib/md_transformer/markdown.rb, line 191 def parse!(content) @children = [] @content = '' # Parse all direct children and create new markdown objects sections = Section.generate_sections(content) # No children! if sections.empty? @content = content return end # Populate content prior to found headers @content = content[0..sections.first.header_location.begin - 1] if sections.first.header_location.begin > 0 parse_children!(sections) end
Parses all available sections into direct children of the current object @param sections [Array] the array of Markdown::Section
objects to parse
# File lib/md_transformer/markdown.rb, line 212 def parse_children!(sections) # Go through the headers sequentially to find all direct children (base on header level vs. current level) last_child_level = LOWEST_PRECEDENCE + 1 sections.each do |s| # Finish parsing if we encounter a sibling (same level) or aunt/uncle (higher level) break if s.level <= level if s.level <= last_child_level @children.push(Markdown.new(s.content, title: s.title, parent: self)) last_child_level = s.level end end end
Translates content to markdown prior to parsing @param content [String] the string Markdown
content to translate @return [String] the translated content
# File lib/md_transformer/markdown.rb, line 185 def translate(content) content.gsub(%r{<h([1-6])>(.*?)</h[1-6]>}m) { '#' * Regexp.last_match[1].to_i + ' ' + Regexp.last_match[2] } end
Validates that all children have valid precedence levels and raises an exception if they are not @param child [MdTransformer::Markdown] child object ot validate @return [MdTransformer::Markdown] the validated child object
# File lib/md_transformer/markdown.rb, line 173 def validate_levels(child) if child.level >= LOWEST_PRECEDENCE raise HeaderTooDeep, "#{child.title} header level (h#{child.level}) is beyond h#{LOWEST_PRECEDENCE}" end child.children.each { |c| validate_levels(c) } child end