Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: adamtanner/pricecut
base: 1ca7877ed4
...
head fork: adamtanner/pricecut
compare: b41ca59355
  • 2 commits
  • 39 files changed
  • 0 commit comments
  • 1 contributor
Showing with 751 additions and 4 deletions.
  1. +22 −3 lib/pricecut.rb
  2. +12 −0 lib/pricecut/elements/a.rb
  3. +9 −0 lib/pricecut/elements/blockquote.rb
  4. +28 −0 lib/pricecut/elements/element.rb
  5. +12 −0 lib/pricecut/elements/em.rb
  6. +9 −0 lib/pricecut/elements/h1.rb
  7. +9 −0 lib/pricecut/elements/h2.rb
  8. +9 −0 lib/pricecut/elements/h3.rb
  9. +9 −0 lib/pricecut/elements/h4.rb
  10. +9 −0 lib/pricecut/elements/h5.rb
  11. +9 −0 lib/pricecut/elements/h6.rb
  12. +12 −0 lib/pricecut/elements/img.rb
  13. +14 −0 lib/pricecut/elements/li.rb
  14. +12 −0 lib/pricecut/elements/ol.rb
  15. +11 −0 lib/pricecut/elements/p.rb
  16. +12 −0 lib/pricecut/elements/strong.rb
  17. +9 −0 lib/pricecut/elements/text.rb
  18. +31 −0 lib/pricecut/markdown_visitor.rb
  19. +1 −1  lib/pricecut/version.rb
  20. +2 −0  pricecut.gemspec
  21. +20 −0 spec/pricecut/elements/a_spec.rb
  22. +20 −0 spec/pricecut/elements/blockquote_spec.rb
  23. +43 −0 spec/pricecut/elements/element_spec.rb
  24. +20 −0 spec/pricecut/elements/em_spec.rb
  25. +20 −0 spec/pricecut/elements/h1_spec.rb
  26. +20 −0 spec/pricecut/elements/h2_spec.rb
  27. +20 −0 spec/pricecut/elements/h3_spec.rb
  28. +20 −0 spec/pricecut/elements/h4_spec.rb
  29. +20 −0 spec/pricecut/elements/h5_spec.rb
  30. +20 −0 spec/pricecut/elements/h6_spec.rb
  31. +20 −0 spec/pricecut/elements/img_spec.rb
  32. +36 −0 spec/pricecut/elements/li_spec.rb
  33. +20 −0 spec/pricecut/elements/ol_spec.rb
  34. +20 −0 spec/pricecut/elements/p_spec.rb
  35. +20 −0 spec/pricecut/elements/strong_spec.rb
  36. +20 −0 spec/pricecut/elements/text_spec.rb
  37. +81 −0 spec/pricecut/markdown_visitor_spec.rb
  38. +57 −0 spec/pricecut/pricecut_spec.rb
  39. +13 −0 spec/spec_helper.rb
View
25 lib/pricecut.rb
@@ -1,5 +1,24 @@
-require "pricecut/version"
+require "pricecut/markdown_visitor"
+require "pricecut/elements/element"
+require "pricecut/elements/strong"
+require "pricecut/elements/em"
+require "pricecut/elements/ol"
+require "pricecut/elements/li"
+require "pricecut/elements/p"
+require "pricecut/elements/img"
+require "pricecut/elements/blockquote"
+require "pricecut/elements/h1"
+require "pricecut/elements/h2"
+require "pricecut/elements/h3"
+require "pricecut/elements/h4"
+require "pricecut/elements/h5"
+require "pricecut/elements/h6"
+require "pricecut/elements/text"
+require "pricecut/elements/a"
module Pricecut
- # Your code goes here...
-end
+ def self.parse(html)
+ root = Nokogiri::HTML::DocumentFragment.parse(html)
+ MarkdownVisitor.new.visit(root).output
+ end
+end
View
12 lib/pricecut/elements/a.rb
@@ -0,0 +1,12 @@
+module Pricecut
+ module Elements
+ class A < Element
+ def output!
+ text = node.text
+ href = node["href"]
+
+ p "[#{ text }](#{ href })"
+ end
+ end
+ end
+end
View
9 lib/pricecut/elements/blockquote.rb
@@ -0,0 +1,9 @@
+module Pricecut
+ module Elements
+ class Blockquote < Element
+ def output!
+ p "> "; yield_children
+ end
+ end
+ end
+end
View
28 lib/pricecut/elements/element.rb
@@ -0,0 +1,28 @@
+module Pricecut
+ module Elements
+ class Element
+ attr_accessor :visitor, :node
+
+ def initialize(visitor, node)
+ @visitor = visitor
+ @node = node
+ end
+
+ def append_newline
+ append_output("\n")
+ end
+
+ alias_method :eol, :append_newline
+
+ def append_output(text)
+ visitor.append_output(text)
+ end
+
+ alias_method :p, :append_output
+
+ def yield_children
+ visitor.visit_children(node)
+ end
+ end
+ end
+end
View
12 lib/pricecut/elements/em.rb
@@ -0,0 +1,12 @@
+module Pricecut
+ module Elements
+ class Em < Element
+ def output!
+ p "_"; yield_children; p "_"
+ end
+ end
+
+ # <i> tag is the same as <em>
+ class I < Em; end
+ end
+end
View
9 lib/pricecut/elements/h1.rb
@@ -0,0 +1,9 @@
+module Pricecut
+ module Elements
+ class H1 < Element
+ def output!
+ p "# "; yield_children; p " #"; eol
+ end
+ end
+ end
+end
View
9 lib/pricecut/elements/h2.rb
@@ -0,0 +1,9 @@
+module Pricecut
+ module Elements
+ class H2 < Element
+ def output!
+ p "## "; yield_children; p " ##"; eol
+ end
+ end
+ end
+end
View
9 lib/pricecut/elements/h3.rb
@@ -0,0 +1,9 @@
+module Pricecut
+ module Elements
+ class H3 < Element
+ def output!
+ p "### "; yield_children; p " ###"; eol
+ end
+ end
+ end
+end
View
9 lib/pricecut/elements/h4.rb
@@ -0,0 +1,9 @@
+module Pricecut
+ module Elements
+ class H4 < Element
+ def output!
+ p "#### "; yield_children; p " ####"; eol
+ end
+ end
+ end
+end
View
9 lib/pricecut/elements/h5.rb
@@ -0,0 +1,9 @@
+module Pricecut
+ module Elements
+ class H5 < Element
+ def output!
+ p "##### "; yield_children; p " #####"; eol
+ end
+ end
+ end
+end
View
9 lib/pricecut/elements/h6.rb
@@ -0,0 +1,9 @@
+module Pricecut
+ module Elements
+ class H6 < Element
+ def output!
+ p "###### "; yield_children; p " ######"; eol
+ end
+ end
+ end
+end
View
12 lib/pricecut/elements/img.rb
@@ -0,0 +1,12 @@
+module Pricecut
+ module Elements
+ class Img < Element
+ def output!
+ alt = node["alt"]
+ src = node["src"]
+
+ p "![#{ alt }](#{ src })"
+ end
+ end
+ end
+end
View
14 lib/pricecut/elements/li.rb
@@ -0,0 +1,14 @@
+module Pricecut
+ module Elements
+ class Li < Element
+ def output!
+ case node.parent.name
+ when "ol"
+ p "1. "; yield_children; eol
+ when "ul"
+ p "- "; yield_children; eol
+ end
+ end
+ end
+ end
+end
View
12 lib/pricecut/elements/ol.rb
@@ -0,0 +1,12 @@
+module Pricecut
+ module Elements
+ class Ol < Element
+ def output!
+ eol; yield_children; eol
+ end
+ end
+
+ # <ol> and <ul> act the same, the differences are in <li>
+ class Ul < Ol; end
+ end
+end
View
11 lib/pricecut/elements/p.rb
@@ -0,0 +1,11 @@
+module Pricecut
+ module Elements
+ class P < Element
+ def output!
+ yield_children; eol
+ end
+ end
+
+ class Div < P; end
+ end
+end
View
12 lib/pricecut/elements/strong.rb
@@ -0,0 +1,12 @@
+module Pricecut
+ module Elements
+ class Strong < Element
+ def output!
+ p "**"; yield_children; p "**"
+ end
+ end
+
+ # <b> tag is the same as <strong>
+ class B < Strong; end
+ end
+end
View
9 lib/pricecut/elements/text.rb
@@ -0,0 +1,9 @@
+module Pricecut
+ module Elements
+ class Text < Element
+ def output!
+ p node.text.strip
+ end
+ end
+ end
+end
View
31 lib/pricecut/markdown_visitor.rb
@@ -0,0 +1,31 @@
+require "nokogiri"
+
+module Pricecut
+ class MarkdownVisitor
+ attr_reader :output
+
+ def initialize
+ @output = ""
+ end
+
+ def visit(node)
+ begin
+ element = Pricecut::Elements.const_get(node.name.capitalize)
+ element.new(self, node).output!
+ rescue NameError
+ # Unsupported element, continue visiting children.
+ visit_children(node)
+ end
+
+ self
+ end
+
+ def append_output(string)
+ @output << string
+ end
+
+ def visit_children(node)
+ node.children.each {|child| child.accept(self) }
+ end
+ end
+end
View
2  lib/pricecut/version.rb
@@ -1,3 +1,3 @@
module Pricecut
- VERSION = "0.0.1"
+ VERSION = "0.0.2"
end
View
2  pricecut.gemspec
@@ -15,5 +15,7 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.version = Pricecut::VERSION
+ gem.add_runtime_dependency "nokogiri"
+
gem.add_development_dependency "rspec"
end
View
20 spec/pricecut/elements/a_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::A do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<a href="http://example.com">Example</a>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown link to the output" do
+ subject.output!
+
+ visitor.output.should eq("[Example](http://example.com)")
+ end
+ end
+end
View
20 spec/pricecut/elements/blockquote_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::Blockquote do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<blockquote>Blockquote</blockquote>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown blockquote to the output" do
+ subject.output!
+
+ visitor.output.should eq("> Blockquote")
+ end
+ end
+end
View
43 spec/pricecut/elements/element_spec.rb
@@ -0,0 +1,43 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::Element do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+ let(:node) { parse "<strong><em>I am strongly emphasized.</em></strong>" }
+
+ subject { described_class.new(visitor, node) }
+
+ describe "#initialize" do
+ it "initializes the visitor" do
+ subject.visitor.should be(visitor)
+ end
+
+ it "initializes the node" do
+ subject.node.should be(node)
+ end
+ end
+
+ describe "#append_newline" do
+ it "appends a newline to the output" do
+ subject.append_newline
+
+ visitor.output.should eq("\n")
+ end
+ end
+
+ describe "#append_output" do
+ it "appends output to the output" do
+ subject.append_output("Output")
+
+ visitor.output.should eq("Output")
+ end
+ end
+
+ describe "#yield_children" do
+ it "writes the output of the child nodes to the output" do
+ subject.yield_children
+
+ visitor.output.should eq("_I am strongly emphasized._")
+ end
+ end
+end
View
20 spec/pricecut/elements/em_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::Em do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<em>Emphasized.</em>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown emphasis to the output" do
+ subject.output!
+
+ visitor.output.should eq("_Emphasized._")
+ end
+ end
+end
View
20 spec/pricecut/elements/h1_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::H1 do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<h1>Heading</h1>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown header with newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("# Heading #\n")
+ end
+ end
+end
View
20 spec/pricecut/elements/h2_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::H2 do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<h2>Heading</h2>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown header with newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("## Heading ##\n")
+ end
+ end
+end
View
20 spec/pricecut/elements/h3_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::H3 do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<h3>Heading</h3>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown header with newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("### Heading ###\n")
+ end
+ end
+end
View
20 spec/pricecut/elements/h4_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::H4 do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<h4>Heading</h4>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown header with newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("#### Heading ####\n")
+ end
+ end
+end
View
20 spec/pricecut/elements/h5_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::H5 do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<h5>Heading</h5>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown header with newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("##### Heading #####\n")
+ end
+ end
+end
View
20 spec/pricecut/elements/h6_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::H6 do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<h6>Heading</h6>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown header with newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("###### Heading ######\n")
+ end
+ end
+end
View
20 spec/pricecut/elements/img_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::Img do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<img alt="Image" src="image.png" />>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown image to the output" do
+ subject.output!
+
+ visitor.output.should eq("![Image](image.png)")
+ end
+ end
+end
View
36 spec/pricecut/elements/li_spec.rb
@@ -0,0 +1,36 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::Li do
+ describe "#output" do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ subject { described_class.new(visitor, root) }
+
+ describe "when in an ordered list" do
+ let(:root) do
+ # We need to grab the first child to grab the <li>.
+ parse(%<<ol><li>Ordered List<li></ol>>).children.first
+ end
+
+ it "appends a Markdown ordered list item with newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("1. Ordered List\n")
+ end
+ end
+
+ describe "when in an unordered list" do
+ let(:root) do
+ # We need to grab the first child to grab the <li>.
+ parse(%<<ul><li>Unordered List<li></ul>>).children.first
+ end
+
+ it "appends a Markdown ordered list item with newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("- Unordered List\n")
+ end
+ end
+ end
+end
View
20 spec/pricecut/elements/ol_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::Ol do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<ol>Ordered List.</ol>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a newline wrapped list to the output" do
+ subject.output!
+
+ visitor.output.should eq("\nOrdered List.\n")
+ end
+ end
+end
View
20 spec/pricecut/elements/p_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::P do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<p>Paragraph.</p>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends the text and a newline to the output" do
+ subject.output!
+
+ visitor.output.should eq("Paragraph.\n")
+ end
+ end
+end
View
20 spec/pricecut/elements/strong_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::Strong do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<<strong>Strong.</strong>>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends a Markdown strong to the output" do
+ subject.output!
+
+ visitor.output.should eq("**Strong.**")
+ end
+ end
+end
View
20 spec/pricecut/elements/text_spec.rb
@@ -0,0 +1,20 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::Elements::Text do
+ let(:visitor) { Pricecut::MarkdownVisitor.new }
+
+ let(:root) do
+ parse %<\n\nText.\n\n>
+ end
+
+ subject { described_class.new(visitor, root) }
+
+ describe "#output!" do
+ it "appends the whitespace-stripped text to the output" do
+ subject.output!
+
+ visitor.output.should eq("Text.")
+ end
+ end
+end
View
81 spec/pricecut/markdown_visitor_spec.rb
@@ -0,0 +1,81 @@
+require "spec_helper"
+require "pricecut"
+
+describe Pricecut::MarkdownVisitor do
+ describe "#initialize" do
+ it "initializes the output" do
+ described_class.new.output.should_not be_nil
+ end
+ end
+
+ describe "#visit" do
+ let(:node) { parse "<test><strong>Test</strong></test>" }
+
+ describe "with supported element" do
+ before do
+ # Define the element class so that
+ # it is supported.
+ # Maps to a <test> element.
+ module Pricecut
+ module Elements
+ class Test < Element
+ def output!
+ p "?? "; yield_children; p " ??"
+ end
+ end
+ end
+ end
+ end
+
+ it "writes the output of the element to output" do
+ subject.visit(node)
+
+ subject.output.should eq("?? **Test** ??")
+ end
+ end
+
+ describe "with unsupported element" do
+ before do
+ # Remove Test from the supported elements
+ Pricecut::Elements.send(:remove_const, "Test")
+ end
+ it "skips the unsupported element" do
+ subject.visit(node)
+
+ subject.output.should eq("**Test**")
+ end
+ end
+ end
+
+ describe "#append_output" do
+ it "appends text to the output" do
+ subject.append_output("Output")
+
+ subject.output.should eq("Output")
+ end
+ end
+
+ describe "#visit_children" do
+ before do
+ class Node
+ attr_reader :visited
+
+ def accept(visitor)
+ @visited = true
+ end
+
+ def children
+ @children ||= (1..3).map { Node.new }
+ end
+ end
+ end
+
+ let(:node) { Node.new }
+
+ it "visits the children of the node" do
+ subject.visit_children(node)
+
+ node.children.all?(&:visited).should be_true
+ end
+ end
+end
View
57 spec/pricecut/pricecut_spec.rb
@@ -0,0 +1,57 @@
+require "pricecut"
+
+describe Pricecut do
+ describe ".parse" do
+ it "parses HTML into Markdown" do
+ html = <<-HTML.unindent
+ <h1>H1</h1>
+ <h2>H2</h2>
+ <h3>H3</h3>
+ <h4>H4</h4>
+ <h5>H5</h5>
+ <h6>H6</h6>
+
+ <p>Paragraph</p>
+
+ <ol><li>This is an ordered list.</li></ol>
+
+ <ul><li>This is an unordered list.</li></ul>
+
+ <p><em>This is emphasized.</em></p>
+ <p><i>This is italic.</em></p>
+
+ <p><strong>This is strong.</strong></p>
+ <p><b>This is bold.</b></p>
+
+ <blockquote><p>This is a blockquote.</p></blockquote>
+
+ <p><img alt="Image" src="image.png" /></p>
+
+ <p><a href="http://example.com">Example</a></p>
+ HTML
+
+ Pricecut.parse(html).should eq <<-MARKDOWN.unindent
+ # H1 #
+ ## H2 ##
+ ### H3 ###
+ #### H4 ####
+ ##### H5 #####
+ ###### H6 ######
+ Paragraph
+
+ 1. This is an ordered list.
+
+
+ - This is an unordered list.
+
+ _This is emphasized._
+ _This is italic._
+ **This is strong.**
+ **This is bold.**
+ > This is a blockquote.
+ ![Image](image.png)
+ [Example](http://example.com)
+ MARKDOWN
+ end
+ end
+end
View
13 spec/spec_helper.rb
@@ -0,0 +1,13 @@
+def parse(html)
+ Nokogiri::HTML::DocumentFragment.parse(html).children.first
+end
+
+class String
+ # Strip leading whitespace from each line that is the same as the
+ # amount of whitespace on the first line of the string.
+ # Leaves _additional_ indentation on later lines intact.
+ # @see http://stackoverflow.com/q/3772864
+ def unindent
+ gsub /^#{self[/\A\s*/]}/, ''
+ end
+end

No commit comments for this range

Something went wrong with that request. Please try again.