Skip to content

Commit

Permalink
Refactor to class (#8)
Browse files Browse the repository at this point in the history
* Extract logic to classes
* Update specs
  • Loading branch information
ahangarha committed Jul 2, 2023
1 parent 95afc56 commit 1d131f8
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 202 deletions.
45 changes: 14 additions & 31 deletions lib/bidify.rb
Original file line number Diff line number Diff line change
@@ -1,43 +1,26 @@
# frozen_string_literal: true

require 'nokogiri'
require 'bidify/html_string_bidifier'

# Bidify applies `dir="auto"` to the given html string
# == Bidify module
#
# ### Example:
# The namespace and helper methods for bidiying HTML contents
#
# bidified_html = Bidify.bidify(regular_html)
# == Example:
#
# bidified_html = Bidify.bidify_html_string(regular_html)
module Bidify
@bidifiable_tags = %w[div h1 h2 h3 h4 h5 h6 p ul ol blockquote]
DEFAULT_BIDIFIABLE_TAGS = %w[div h1 h2 h3 h4 h5 h6 p ul ol blockquote].freeze

class << self
###
# bidify applies dir="auto" to the given html string
def bidify(input_html)
html_node = Nokogiri::HTML.fragment(input_html)
bidify_recursively(html_node, { root: true })

html_node.to_s
end

private

def bidify_recursively(html_node, options = {})
seen_the_first_bidifiable_element = false

html_node.children.each do |child_node|
bidify_recursively(child_node)

if (options[:root] || seen_the_first_bidifiable_element) && @bidifiable_tags.include?(child_node.name)
child_node.set_attribute('dir', 'auto')
end

seen_the_first_bidifiable_element = true if actual_content?(child_node)
end
end

def actual_content?(node)
node.element? || (node.text? && !node.blank?)
# Applies dir="auto" to the given html string with default configuration
#
# @param [String] html_string a stringified HTML content
# @return [String] Bidified version of the input string
#
def bidify_html_string(html_string)
Bidify::HtmlStringBidifier.new.apply(html_string)
end
end
end
38 changes: 38 additions & 0 deletions lib/bidify/bidifier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module Bidify
# Super class for custom bidifiers
class Bidifier
def initialize
configure
end

def apply
raise NotImplementedError
end

private

def configure
@bidifiable_tags = DEFAULT_BIDIFIABLE_TAGS.dup
end

def bidify_recursively(html_node, options = {})
seen_the_first_bidifiable_element = false

html_node.children.each do |child_node|
bidify_recursively(child_node)

if (options[:root] || seen_the_first_bidifiable_element) && @bidifiable_tags.include?(child_node.name)
child_node.set_attribute('dir', 'auto')
end

seen_the_first_bidifiable_element = true if actual_content?(child_node)
end
end

def actual_content?(node)
node.element? || (node.text? && !node.blank?)
end
end
end
31 changes: 31 additions & 0 deletions lib/bidify/html_string_bidifier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require 'nokogiri'
require_relative 'bidifier'

module Bidify
###
# Bidifying html input as string
#
# == Example Usage
#
# Here's an example of how to use the class:
#
# bidifier = HtmlStringBidifier.new
# bidifier.apply("<p>text</p>")
#
class HtmlStringBidifier < Bidifier
###
# Applies dir="auto" to the given html string with default configuration
#
# @param [String] html_string a stringified HTML content
# @return [String] Bidified version of the input string
#
def apply(html_input)
html_node = Nokogiri::HTML.fragment(html_input)
bidify_recursively(html_node, { root: true })

html_node.to_s
end
end
end
178 changes: 7 additions & 171 deletions spec/bidify_spec.rb
Original file line number Diff line number Diff line change
@@ -1,181 +1,17 @@
# frozen_string_literal: true

require 'bidify'
require 'bidify/html_string_bidifier'

describe 'Bidify' do
describe '#bidify' do
it 'bidifies a single paragraph' do
input = '<p>some text</p>'
expected_output = '<p dir="auto">some text</p>'
it '#bidify_html_string calls HtmlStringBidifier#apply with the given input' do
input = '<p>some text</p>'

actual_output = Bidify.bidify(input)
html_string_bidifier = instance_spy(Bidify::HtmlStringBidifier)
allow(Bidify::HtmlStringBidifier).to receive(:new).and_return(html_string_bidifier)

expect(actual_output).to eq expected_output
end
Bidify.bidify_html_string(input)

it 'bidifies all non-list tags in bidifiable tags list' do
input = <<~HTML
<div>Content</div>
<h1>Content</h1>
<h2>Content</h2>
<h3>Content</h3>
<h4>Content</h4>
<h5>Content</h5>
<h6>Content</h6>
<p>Content</p>
<blockquote>Content</blockquote>
HTML

expected_output = <<~HTML
<div dir="auto">Content</div>
<h1 dir="auto">Content</h1>
<h2 dir="auto">Content</h2>
<h3 dir="auto">Content</h3>
<h4 dir="auto">Content</h4>
<h5 dir="auto">Content</h5>
<h6 dir="auto">Content</h6>
<p dir="auto">Content</p>
<blockquote dir="auto">Content</blockquote>
HTML

actual_output = Bidify.bidify(input)

expect(actual_output).to eq expected_output
end

it 'bidifies non list nested elements and ignore the first inner element' do
input = <<~HTML
<blockquote>
<p>Item 1</p>
<p>Item 2</p>
</blockquote>
HTML

expected_output = <<~HTML
<blockquote dir="auto">
<p>Item 1</p>
<p dir="auto">Item 2</p>
</blockquote>
HTML

actual_output = Bidify.bidify(input)

expect(actual_output).to eq expected_output
end

it "doesn't bidify li elements by default" do
input = <<~HTML
<ul>
<li>
Item 1
<p>with paragraph</p>
</li>
<li>
<p>Paragram at the begining of</p>
Item 2
</li>
</ul>
HTML

expected_output = <<~HTML
<ul dir="auto">
<li>
Item 1
<p dir="auto">with paragraph</p>
</li>
<li>
<p>Paragram at the begining of</p>
Item 2
</li>
</ul>
HTML

actual_output = Bidify.bidify(input)

expect(actual_output).to eq expected_output
end

it 'bidifies non-list nested elements with plain text as the first content' do
input = <<~HTML
<blockquote>
blah
<p>Item 1</p>
<p>Item 2</p>
</blockquote>
HTML

expected_output = <<~HTML
<blockquote dir="auto">
blah
<p dir="auto">Item 1</p>
<p dir="auto">Item 2</p>
</blockquote>
HTML

actual_output = Bidify.bidify(input)

expect(actual_output).to eq expected_output
end

it 'bidifies non-list nested elements and ignores html comments' do
input = <<~HTML
<blockquote>
<!-- comment -->
<p>Item 1</p>
<p>Item 2</p>
</blockquote>
HTML

expected_output = <<~HTML
<blockquote dir="auto">
<!-- comment -->
<p>Item 1</p>
<p dir="auto">Item 2</p>
</blockquote>
HTML

actual_output = Bidify.bidify(input)

expect(actual_output).to eq expected_output
end

it 'skips tags not included in bidifiable tags by default' do
input = <<~HTML
<span>Not getting affected</span>
<img src="image.png">
<main>content</main>
<section>content</section>
<aside>content</aside>
<a>content</a>
HTML

expected_output = input

actual_output = Bidify.bidify(input)

expect(actual_output).to eq expected_output
end

it 'skips blank lines' do
input = <<~HTML
<blockquote>
<p>Item 1</p>
<p>Item 2</p>
</blockquote>
HTML

expected_output = <<~HTML
<blockquote dir="auto">
<p>Item 1</p>
<p dir="auto">Item 2</p>
</blockquote>
HTML

actual_output = Bidify.bidify(input)

expect(actual_output).to eq expected_output
end
expect(html_string_bidifier).to have_received(:apply).with(input)
end
end
Loading

0 comments on commit 1d131f8

Please sign in to comment.