From 8f3956e2b645f4c41a23e35dd3fcff33c11f0509 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 17 May 2016 15:48:42 -0700 Subject: [PATCH 1/7] Include Enumerable and demonstrate its usage --- README.md | 10 +++++----- lib/commonmarker.rb | 6 ++++-- test/test_attributes.rb | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e55ed616..589c69da 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ require 'commonmarker' doc = CommonMarker.render_doc('*Hello* world', :default) puts(doc.to_html) #

Hi there

\n -doc.walk do |node| +doc.each do |node| puts node.type # [:document, :paragraph, :text, :emph, :text] end ``` @@ -59,16 +59,16 @@ require 'commonmarker' doc = CommonMarker.render_doc("# The site\n\n [GitHub](https://www.github.com)") # Walk tree and print out URLs for links -doc.walk do |node| +doc.each do |node| if node.type == :link printf("URL = %s\n", node.url) end end # Capitalize all regular text in headers -doc.walk do |node| +doc.each 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 @@ -77,7 +77,7 @@ doc.walk do |node| end # Transform links to regular text -doc.walk do |node| +doc.each do |node| if node.type == :link node.insert_before(node.first_child) node.delete diff --git a/lib/commonmarker.rb b/lib/commonmarker.rb index 5ed88cc9..8da9e4ae 100755 --- a/lib/commonmarker.rb +++ b/lib/commonmarker.rb @@ -38,13 +38,15 @@ def self.render_doc(text, options = :default) end 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(&blk) + def each(&blk) yield self each_child do |child| - child.walk(&blk) + child.each(&blk) end end diff --git a/test/test_attributes.rb b/test/test_attributes.rb index 7c1ca237..95da31dd 100644 --- a/test/test_attributes.rb +++ b/test/test_attributes.rb @@ -9,7 +9,7 @@ def setup def test_sourcepos sourcepos = [] - @doc.walk do |node| + @doc.each do |node| sourcepos << node.sourcepos end From a1f87e90714533d7a0b66c0227a62bb9bb6311db Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 17 May 2016 16:59:55 -0700 Subject: [PATCH 2/7] Shuffle node into its own file --- lib/commonmarker.rb | 35 +---------------------------------- lib/commonmarker/node.rb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 34 deletions(-) create mode 100644 lib/commonmarker/node.rb diff --git a/lib/commonmarker.rb b/lib/commonmarker.rb index 8da9e4ae..cd2e2281 100755 --- a/lib/commonmarker.rb +++ b/lib/commonmarker.rb @@ -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' @@ -36,38 +37,4 @@ def self.render_doc(text, options = :default) text = text.encode('UTF-8') Node.parse_document(text, text.bytesize, opts) end - - 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 each(&blk) - yield self - each_child do |child| - child.each(&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 diff --git a/lib/commonmarker/node.rb b/lib/commonmarker/node.rb new file mode 100644 index 00000000..48b6079b --- /dev/null +++ b/lib/commonmarker/node.rb @@ -0,0 +1,35 @@ +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 each(&blk) + yield self + each_child do |child| + child.each(&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 From f47026d82729f415a6c22d1de63a8879b5df3e83 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Tue, 17 May 2016 17:00:09 -0700 Subject: [PATCH 3/7] Test the select functionality --- test/test_node.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/test_node.rb b/test/test_node.rb index 16991c67..b1aa1725 100644 --- a/test/test_node.rb +++ b/test/test_node.rb @@ -7,12 +7,18 @@ def setup def test_walk nodes = [] - @doc.walk do |node| + @doc.each do |node| nodes << node.type end assert_equal [:document, :paragraph, :text, :emph, :text], nodes end + def test_select + nodes = @doc.select { |node| node.type == :text } + assert_equal CommonMarker::Node, nodes.first.class + assert_equal [:text, :text], nodes.map(&:type) + end + def test_insert_illegal assert_raises NodeError do @doc.insert_before(@doc) @@ -30,7 +36,7 @@ def test_html_renderer end def test_walk_and_set_string_content - @doc.walk do |node| + @doc.each do |node| if node.type == :text && node.string_content == 'there' node.string_content = 'world' assert_equal 'world', node.string_content @@ -39,7 +45,7 @@ def test_walk_and_set_string_content end def test_walk_and_delete_node - @doc.walk do |node| + @doc.each do |node| if node.type == :emph node.insert_before(node.first_child) node.delete From 065c1bd404c4f057493d8e60d0b5f55f6b08ede6 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 18 May 2016 12:58:50 -0700 Subject: [PATCH 4/7] Redo the `each` paradigm --- README.md | 11 +++++--- lib/commonmarker/node.rb | 16 +++++++++--- lib/commonmarker/renderer.rb | 4 +-- test/test_attributes.rb | 2 +- test/test_node.rb | 49 ++++++++++++++++++++++++------------ 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 589c69da..b557d434 100644 --- a/README.md +++ b/README.md @@ -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' @@ -59,14 +64,14 @@ require 'commonmarker' doc = CommonMarker.render_doc("# The site\n\n [GitHub](https://www.github.com)") # Walk tree and print out URLs for links -doc.each do |node| +doc.walk do |node| if node.type == :link printf("URL = %s\n", node.url) end end # Capitalize all regular text in headers -doc.each do |node| +doc.walk do |node| if node.type == :header node.each do |subnode| if subnode.type == :text @@ -77,7 +82,7 @@ doc.each do |node| end # Transform links to regular text -doc.each do |node| +doc.walk do |node| if node.type == :link node.insert_before(node.first_child) node.delete diff --git a/lib/commonmarker/node.rb b/lib/commonmarker/node.rb index 48b6079b..cca73d7d 100644 --- a/lib/commonmarker/node.rb +++ b/lib/commonmarker/node.rb @@ -5,10 +5,10 @@ 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 each(&blk) + def walk(&block) yield self - each_child do |child| - child.each(&blk) + each do |child| + child.walk(&block) end end @@ -23,7 +23,9 @@ def to_html(options = :default) end # Internal: Iterate over the children (if any) of the current pointer. - def each_child + def each + return enum_for(:each) unless block_given? + child = first_child while child nextchild = child.next @@ -31,5 +33,11 @@ def each_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 diff --git a/lib/commonmarker/renderer.rb b/lib/commonmarker/renderer.rb index ce42e2fa..347868e3 100644 --- a/lib/commonmarker/renderer.rb +++ b/lib/commonmarker/renderer.rb @@ -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) @@ -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) diff --git a/test/test_attributes.rb b/test/test_attributes.rb index 95da31dd..7c1ca237 100644 --- a/test/test_attributes.rb +++ b/test/test_attributes.rb @@ -9,7 +9,7 @@ def setup def test_sourcepos sourcepos = [] - @doc.each do |node| + @doc.walk do |node| sourcepos << node.sourcepos end diff --git a/test/test_node.rb b/test/test_node.rb index b1aa1725..ab9b5da4 100644 --- a/test/test_node.rb +++ b/test/test_node.rb @@ -2,21 +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.each do |node| + @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.select { |node| node.type == :text } + nodes = @doc.first_child.select { |node| node.type == :text } assert_equal CommonMarker::Node, nodes.first.class - assert_equal [:text, :text], nodes.map(&:type) + 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 @@ -26,36 +47,32 @@ def test_insert_illegal end def test_to_html - assert_equal "

Hi there

\n", @doc.to_html + assert_equal "

Hi there, I am mostly text!

\n", @doc.to_html end def test_html_renderer renderer = HtmlRenderer.new result = renderer.render(@doc) - assert_equal "

Hi there

\n", result + assert_equal "

Hi there, I am mostly text!

\n", result end def test_walk_and_set_string_content - @doc.each do |node| + @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 "

Hi world, I am mostly text!

\n", result end def test_walk_and_delete_node - @doc.each do |node| + @doc.walk do |node| if node.type == :emph node.insert_before(node.first_child) node.delete end end - assert_equal "

Hi there

\n", @doc.to_html - end - - def test_markdown_to_html - html = CommonMarker.render_html('Hi *there*') - assert_equal "

Hi there

\n", html + assert_equal "

Hi there, I am mostly text!

\n", @doc.to_html end end From 40b1d94fc7830dce7d059af32eaf0b7952f283bd Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 18 May 2016 13:00:42 -0700 Subject: [PATCH 5/7] Use correct method --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b557d434..f1861e47 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ require 'commonmarker' doc = CommonMarker.render_doc('*Hello* world', :default) puts(doc.to_html) #

Hi there

\n -doc.each do |node| +doc.walk do |node| puts node.type # [:document, :paragraph, :text, :emph, :text] end ``` From b0b8aca3326b6d166f0b7fa75b5e3b04f2398152 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 18 May 2016 13:01:40 -0700 Subject: [PATCH 6/7] Correct comment --- lib/commonmarker/node.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commonmarker/node.rb b/lib/commonmarker/node.rb index cca73d7d..12508e80 100644 --- a/lib/commonmarker/node.rb +++ b/lib/commonmarker/node.rb @@ -22,7 +22,7 @@ def to_html(options = :default) _render_html(opts).force_encoding('utf-8') end - # Internal: Iterate over the children (if any) of the current pointer. + # Public: Iterate over the children (if any) of the current pointer. def each return enum_for(:each) unless block_given? From 97ee07ac3f5c75a4bd36abe3e6ccc72c35c581ae Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Wed, 18 May 2016 13:30:29 -0700 Subject: [PATCH 7/7] Return an enumerator if no block is given for `walk` --- lib/commonmarker/node.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/commonmarker/node.rb b/lib/commonmarker/node.rb index 12508e80..d86c725d 100644 --- a/lib/commonmarker/node.rb +++ b/lib/commonmarker/node.rb @@ -6,6 +6,8 @@ class Node # # 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) @@ -23,7 +25,7 @@ def to_html(options = :default) end # Public: Iterate over the children (if any) of the current pointer. - def each + def each(&block) return enum_for(:each) unless block_given? child = first_child