Skip to content

Commit

Permalink
Merge pull request #24 from gjtorikian/enumerate
Browse files Browse the repository at this point in the history
Make `Node` an `Enumerable`
  • Loading branch information
gjtorikian committed May 18, 2016
2 parents 910e937 + 97ee07a commit 89f1a52
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 46 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ The second argument is optional--[see below](#options) for more information.

#### Example: walking the AST

You can use `walk` or `each` to iterate over nodes:

- `walk` will iterate on a node and recursively iterate on a node's children.
- `each` will iterate on a node and its children, but no further.

``` ruby
require 'commonmarker'

Expand All @@ -68,7 +73,7 @@ end
# Capitalize all regular text in headers
doc.walk do |node|
if node.type == :header
node.walk do |subnode|
node.each do |subnode|
if subnode.type == :text
subnode.string_content = subnode.string_content.upcase
end
Expand Down
33 changes: 1 addition & 32 deletions lib/commonmarker.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env ruby
require 'commonmarker/commonmarker'
require 'commonmarker/config'
require 'commonmarker/node'
require 'commonmarker/renderer'
require 'commonmarker/renderer/html_renderer'
require 'commonmarker/version'
Expand Down Expand Up @@ -36,36 +37,4 @@ def self.render_doc(text, options = :default)
text = text.encode('UTF-8')
Node.parse_document(text, text.bytesize, opts)
end

class Node
# Public: An iterator that "walks the tree," descending into children recursively.
#
# blk - A {Proc} representing the action to take for each child
def walk(&blk)
yield self
each_child do |child|
child.walk(&blk)
end
end

# Public: Convert the node to an HTML string.
#
# options - A {Symbol} or {Array of Symbol}s indicating the render options
#
# Returns a {String}.
def to_html(options = :default)
opts = Config.process_options(options, :render)
_render_html(opts).force_encoding('utf-8')
end

# Internal: Iterate over the children (if any) of the current pointer.
def each_child
child = first_child
while child
nextchild = child.next
yield child
child = nextchild
end
end
end
end
45 changes: 45 additions & 0 deletions lib/commonmarker/node.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module CommonMarker
class Node
include Enumerable

# Public: An iterator that "walks the tree," descending into children recursively.
#
# blk - A {Proc} representing the action to take for each child
def walk(&block)
return enum_for(:walk) unless block_given?

yield self
each do |child|
child.walk(&block)
end
end

# Public: Convert the node to an HTML string.
#
# options - A {Symbol} or {Array of Symbol}s indicating the render options
#
# Returns a {String}.
def to_html(options = :default)
opts = Config.process_options(options, :render)
_render_html(opts).force_encoding('utf-8')
end

# Public: Iterate over the children (if any) of the current pointer.
def each(&block)
return enum_for(:each) unless block_given?

child = first_child
while child
nextchild = child.next
yield child
child = nextchild
end
end

# Deprecated: Please use `each` instead
def each_child(&block)
warn '[DEPRECATION] `each_child` is deprecated. Please use `each` instead.'
each(&block)
end
end
end
4 changes: 2 additions & 2 deletions lib/commonmarker/renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def initialize
def out(*args)
args.each do |arg|
if arg == :children
@node.each_child { |child| out(child) }
@node.each { |child| out(child) }
elsif arg.is_a?(Array)
arg.each { |x| render(x) }
elsif arg.is_a?(Node)
Expand All @@ -34,7 +34,7 @@ def render(node)
document(node)
return @stream.string
elsif @in_plain && node.type != :text && node.type != :softbreak
node.each_child { |child| render(child) }
node.each { |child| render(child) }
else
begin
send(node.type, node)
Expand Down
45 changes: 34 additions & 11 deletions test/test_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,42 @@

class TestNode < Minitest::Test
def setup
@doc = CommonMarker.render_doc('Hi *there*')
@doc = CommonMarker.render_doc('Hi *there*, I am mostly text!')
end

def test_walk
nodes = []
@doc.walk do |node|
nodes << node.type
end
assert_equal [:document, :paragraph, :text, :emph, :text], nodes
assert_equal [:document, :paragraph, :text, :emph, :text, :text, :text], nodes
end

def test_each
nodes = []
@doc.first_child.each do |node|
nodes << node.type
end
assert_equal [:text, :emph, :text, :text], nodes
end

def test_deprecated_each_child
nodes = []
@doc.first_child.each_child do |node|
nodes << node.type
end
assert_equal [:text, :emph, :text, :text], nodes
end

def test_select
nodes = @doc.first_child.select { |node| node.type == :text }
assert_equal CommonMarker::Node, nodes.first.class
assert_equal [:text, :text, :text], nodes.map(&:type)
end

def test_map
nodes = @doc.first_child.map(&:type)
assert_equal [:text, :emph, :text, :text], nodes
end

def test_insert_illegal
Expand All @@ -20,22 +47,23 @@ def test_insert_illegal
end

def test_to_html
assert_equal "<p>Hi <em>there</em></p>\n", @doc.to_html
assert_equal "<p>Hi <em>there</em>, I am mostly text!</p>\n", @doc.to_html
end

def test_html_renderer
renderer = HtmlRenderer.new
result = renderer.render(@doc)
assert_equal "<p>Hi <em>there</em></p>\n", result
assert_equal "<p>Hi <em>there</em>, I am mostly text!</p>\n", result
end

def test_walk_and_set_string_content
@doc.walk do |node|
if node.type == :text && node.string_content == 'there'
node.string_content = 'world'
assert_equal 'world', node.string_content
end
end
result = HtmlRenderer.new.render(@doc)
assert_equal "<p>Hi <em>world</em>, I am mostly text!</p>\n", result
end

def test_walk_and_delete_node
Expand All @@ -45,11 +73,6 @@ def test_walk_and_delete_node
node.delete
end
end
assert_equal "<p>Hi there</p>\n", @doc.to_html
end

def test_markdown_to_html
html = CommonMarker.render_html('Hi *there*')
assert_equal "<p>Hi <em>there</em></p>\n", html
assert_equal "<p>Hi there, I am mostly text!</p>\n", @doc.to_html
end
end

0 comments on commit 89f1a52

Please sign in to comment.