Skip to content

Commit

Permalink
Completed base structure of document transformation logic.
Browse files Browse the repository at this point in the history
  • Loading branch information
Enrique Gonzalez committed Jan 29, 2015
1 parent a4e5aa8 commit 2ded516
Show file tree
Hide file tree
Showing 19 changed files with 458 additions and 14 deletions.
7 changes: 5 additions & 2 deletions lib/skoogle_docs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@
require "skoogle_docs/session"
require "skoogle_docs/browser"
require "skoogle_docs/document"

require "skoogle_docs/transforms"
require "skoogle_docs/transformer"
require "skoogle_docs/bots/bot"
require "skoogle_docs/bots/doctype_tag"
require "skoogle_docs/bots/meta_tags"
require "skoogle_docs/bots/styled_lists"
53 changes: 53 additions & 0 deletions lib/skoogle_docs/bots/bot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module SkoogleDocs
module Bots
# SkoogleDocs::Bots::Bot are the smallest components for transformations
#
# @api public
class Bot
# Defines an array that will hold all of the subclasses of `Bot`
@all = []

# Provides reference to the array with all of the `Bot` subclasses
#
# @return [Array]
def self.all
@all
end

# Callback to track of all the subclasses of `Bot` and store them
def self.inherited(subclass)
@all << subclass
end

# Fetches the html element of the document. It creates one if none exists
#
# @param dom [Nokogiri::HTML::Document] the html document
#
# @return [Nokogiri::XML::Element]
def self.html(dom)
doc_html = Nokogiri::HTML(dom.to_s).at_css("html")
doc_html || Nokogiri::HTML("<html></html>").at_css("html")
end

# Fetches the head element of the document. It creates one if none exits
#
# @param dom [Nokogiri::HTML::Document] the html document
#
# @return [Nokogiri::XML::Element]
def self.head(dom)
doc_head = Nokogiri::HTML(dom.to_s).at_css("head")
doc_head || Nokogiri::HTML("<head></head>").at_css("head")
end

# Fetches the body element of the document. It creates one if none exits
#
# @param dom [Nokogiri::HTML::Document] the html document
#
# @return [Nokogiri::XML::Element]
def self.body(dom)
doc_body = Nokogiri::HTML(dom.to_s).at_css("body")
doc_body || Nokogiri::HTML("<body></body>").at_css("body")
end
end
end
end
21 changes: 21 additions & 0 deletions lib/skoogle_docs/bots/doctype_tag.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module SkoogleDocs
module Bots
# Bot in charge of adding the HTML5 document declaration
#
# @api public
class DoctypeTag < SkoogleDocs::Bots::Bot
# Constant to hold the HTML5 document type declaration
DOCTYPE_DECLARATION = "<!DOCTYPE html>"

# Replaces or add an HTML5 document type declaration to the top of the dom
#
# @param dom [Nokogiri::HTML::Document] the html document to transform
#
# @return [Nokogiri::HTML::Document]
def self.transform(dom)
doc = "#{DOCTYPE_DECLARATION} #{head(dom)} #{body(dom)}"
Nokogiri::HTML(doc)
end
end
end
end
21 changes: 21 additions & 0 deletions lib/skoogle_docs/bots/meta_tags.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module SkoogleDocs
module Bots
# Bot in charge of adding the UTF-8 encoding meta tag
#
# @api public
class MetaTags < SkoogleDocs::Bots::Bot
# Constant holding the meta tags to add
METAS = ["UTF-8"]

# Replaces or adds meta tags to the given dom
#
# @param dom [Nokogiri::HTML::Document] the html document to transform
#
# @return [Nokogiri::HTML::Document]
def self.transform(dom)
dom.meta_encoding = METAS.first
Nokogiri::HTML(dom.to_s)
end
end
end
end
44 changes: 44 additions & 0 deletions lib/skoogle_docs/bots/styled_lists.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module SkoogleDocs
module Bots
# Bot in charge of applying custom directives to lists
#
# @api public
class StyledLists < SkoogleDocs::Bots::Bot
# Looks for lists with directives and applies css class to them
#
# @param dom [Nokogiri::HTML::Document] the html document to transform
#
# @return [Nokogiri::HTML::Document]
def self.transform(dom)
lists = extract_lists(dom)
build_lists(lists)

Nokogiri::HTML(dom.to_s)
end

# Searches for the lasts elements of every list in the dom
#
# @param dom [Nokogiri::HTML::Document] the html document to search in
#
# @return [Array]
def self.extract_lists(dom)
dom.search("li:last")
end

# Adds the CSS classes to the directive lists and removes the last element
#
# @param lists [Array<Nokogiri::XML::Element>] an array of li elements
#
# @return [Array]
def self.build_lists(lists)
lists.each do |li|
if "#" == li.content[0]
css_class = li.content.gsub("#", "").downcase
li.parent[:class] = "#{li.parent[:class]} #{css_class}"
li.remove
end
end
end
end
end
end
39 changes: 39 additions & 0 deletions lib/skoogle_docs/transformer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module SkoogleDocs
# SkoogleDocs Transformer object executes the transformations
#
# @api public
class Transformer
# Instantiates a new SkoogleDocs::Transformer object
#
# @param source [String] the document content that needs to be transformed
#
# @return [SkoogleDocs::Transformer]
#
# @example Passing a String
# transformer = SkoogleDocs::Transformer.new(source)
def initialize(source)
@dom = Nokogiri::HTML(source)
end

# Executes all bots on the source - (auto)bots roll out!
#
# @return [String]
#
# @example Rolling Out
# transformer = SkoogleDocs::Transformer.new(source)
# doc = transformer.rollout
def rollout
bots = filter_bots(Bots::Bot.all)
bots.each { |b| b.transform(@dom) }

@dom.to_s
end

private

# TODO: Filter which bots to run using configuration files/options
def filter_bots(bots)
bots
end
end
end
12 changes: 0 additions & 12 deletions spec/factories/document_factory.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
DOCUMENT_PATH = "spec/support/sample_document.html"
DOCTYPE_DOC_PATH = "spec/support/doctype_tag_doc.html"
META_TAGS_DOC_PATH = "spec/support/meta_tags_doc.html"

FactoryGirl.define do
# Valid document definition
Expand All @@ -9,15 +7,5 @@
sample_document = File.read(DOCUMENT_PATH) if File.exist?(DOCUMENT_PATH)
new(sample_document || "")
end

trait :doctype_tag do
doc = File.read(DOCTYPE_DOC_PATH) if File.exist?(DOCTYPE_DOC_PATH)
source doc || ""
end

trait :meta_tags do
doc = File.read(META_TAGS_DOC_PATH) if File.exist?(META_TAGS_DOC_PATH)
source doc || ""
end
end
end
52 changes: 52 additions & 0 deletions spec/factories/dom_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
FULL_DOC_PATH = "spec/support/sample_document.html"
NO_HEAD_DOC_PATH = "spec/support/no_head_doc.html"
NO_BODY_DOC_PATH = "spec/support/no_body_doc.html"
MINI_DOC_PATH = "spec/support/mini_doc.html"
LISTS_DOC_PATH = "spec/support/lists_dom.html"

FactoryGirl.define do
factory :dom, class: Nokogiri::HTML::Document do
initialize_with do
build_dom(FULL_DOC_PATH)
end

factory :no_head_dom do
initialize_with do
build_dom(NO_HEAD_DOC_PATH)
end
end

factory :no_body_dom do
initialize_with do
build_dom(NO_BODY_DOC_PATH)
end
end

factory :mini_dom do
initialize_with do
build_dom(MINI_DOC_PATH)
end
end

factory :lists_dom do
initialize_with do
build_dom(LISTS_DOC_PATH)
end
end

factory :empty_dom do
initialize_with do
new
end
end
end
end

def load_file(path)
return "" unless File.exist?(path)
File.read(path)
end

def build_dom(path)
Nokogiri::HTML::Document.parse(load_file(path))
end
7 changes: 7 additions & 0 deletions spec/factories/transformer_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryGirl.define do
factory :transformer, class: SkoogleDocs::Transformer do
initialize_with do
new(build(:document).source)
end
end
end
58 changes: 58 additions & 0 deletions spec/skoogle_docs/bots/bot_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require "spec_helper"

RSpec.describe SkoogleDocs::Bots::Bot do
subject { described_class }
let(:dom) { build(:mini_dom) }

describe ".all" do
it "does not return nil" do
bots = subject.all
expect(bots).not_to be_nil
end

it "returns an Array" do
bots = subject.all
expect(bots).to be_a Array
end

it "is not an empty Array" do
bots = subject.all
expect(bots).not_to be_empty
end

it "returns Bot instances" do
bots = subject.all
expect(bots.all? { |b| b <= subject }).to be_truthy
end
end

describe ".html" do
it "returns a Nokogiri::XML::Element" do
expect(subject.html(dom)).to be_a Nokogiri::XML::Element
end

it "returns an html element" do
expect(subject.html(dom).name).to eq "html"
end
end

describe ".head" do
it "returns a Nokogiri::XML::Element" do
expect(subject.head(dom)).to be_a Nokogiri::XML::Element
end

it "returns an head element" do
expect(subject.head(dom).name).to eq "head"
end
end

describe ".body" do
it "returns a Nokogiri::XML::Element" do
expect(subject.body(dom)).to be_a Nokogiri::XML::Element
end

it "returns an body element" do
expect(subject.body(dom).name).to eq "body"
end
end
end
16 changes: 16 additions & 0 deletions spec/skoogle_docs/bots/doctype_tag_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require "spec_helper"
require "skoogle_docs/bots/shared_examples_for_bots"

RSpec.describe SkoogleDocs::Bots::DoctypeTag do
subject { described_class }
let(:dom) { build(:mini_dom) }

it_behaves_like "a bot"

describe ".transform" do
it "has an HTML5 document type declaration" do
doc = subject.transform(dom)
expect(doc.internal_subset.html5_dtd?).to be_truthy
end
end
end
16 changes: 16 additions & 0 deletions spec/skoogle_docs/bots/meta_tags_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require "spec_helper"
require "skoogle_docs/bots/shared_examples_for_bots"

RSpec.describe SkoogleDocs::Bots::MetaTags do
subject { described_class }
let(:dom) { build(:mini_dom) }

it_behaves_like "a bot"

describe ".transform" do
it "sets UTF-8 as the meta tag encodig" do
doc = subject.transform(dom)
expect(doc.meta_encoding.to_s).to eq described_class::METAS[0]
end
end
end
Loading

0 comments on commit 2ded516

Please sign in to comment.