class CodeTerminator::Html
Public Class Methods
new(args = {})
click to toggle source
# File lib/code_terminator/html.rb, line 5 def initialize(args = {}) @code = args[:code] @source = args[:source] @tags = Array.new @elements = Array.new args[:source_type] ||= "file" @source_type = args[:source_type] end
Public Instance Methods
get_elements(source)
click to toggle source
Get html elements of a html file. Return a list of Nokogiri XML objects.
Example:
>> CodeTerminator::Html.get_elements("hola_mundo.html") => [#<Nokogiri::XML::Element:0x3fe3391547d8 name="h1" children=[#<Nokogiri::XML::Text:0x3fe33915474c "Hola Mundo!">]>, #<Nokogiri::XML::Text:0x3fe33915474c "Hola Mundo!">]
Arguments:
source: (String)
# File lib/code_terminator/html.rb, line 25 def get_elements(source) @elements = Array.new #How to read if is an url if @source_type == "url" reader = Nokogiri::HTML(open(source).read) else reader = Nokogiri::HTML(File.open(source)) end #remove empty spaces from reader reader = remove_empty_text(reader) if reader.css('html').any? node = Hash.new node[:parent] = "" node[:tag] = "html" node[:pointer] = reader.css('html').first.pointer_id @elements << node end #search elements from body section if reader.at('body') node = Hash.new node[:parent] = "html" node[:tag] = "body" node[:pointer] = reader.css('body').first.pointer_id node[:parent_pointer] = reader.css('html').first.pointer_id @elements << node reader.at('body').attribute_nodes.each do |element_attribute| node = Hash.new node[:parent] = "html" node[:tag] = "body" node[:attribute] = element_attribute.name if element_attribute.name node[:value] = element_attribute.value if element_attribute.value node[:pointer] = element_attribute.pointer_id node[:parent_pointer] = reader.css('html').first.pointer_id @elements << node end end #end search #search elements from head section if reader.at('head') node = Hash.new node[:parent] = "html" node[:tag] = "head" node[:pointer] = reader.css('head').first.pointer_id node[:parent_pointer] = reader.css('html').first.pointer_id @elements << node reader.at('head').children.each do |child| if child.attribute_nodes.empty? node = Hash.new node[:parent] = "head" node[:tag] = child.name node[:content] = child.text if child.text or child.comment? node[:pointer] = child.pointer_id node[:parent_pointer] = child.parent.pointer_id @elements << node else child.attribute_nodes.each do |element_attribute| node = Hash.new node[:parent] = "head" if child.name == "#cdata-section" node[:tag] = "text" else node[:tag] = child.name end node[:content] = child.text if child.text node[:attribute] = element_attribute.name if element_attribute.name node[:value] = element_attribute.value if element_attribute.value node[:pointer] = element_attribute.pointer_id node[:parent_pointer] = child.pointer_id @elements << node end end add_children(child) if child.children.any? end end #end search elements #search elements inside body (children) if reader.at('body') reader.at('body').children.each do |child| if child.attribute_nodes.empty? node = Hash.new node[:parent] = "body" node[:tag] = child.name node[:content] = child.text if child.text? or child.comment? node[:pointer] = child.pointer_id node[:parent_pointer] = child.parent.pointer_id @elements << node else node = Hash.new node[:parent] = "body" node[:tag] = child.name node[:content] = child.text if child.text? or child.comment? node[:pointer] = child.pointer_id node[:parent_pointer] = child.parent.pointer_id @elements << node child.attribute_nodes.each do |element_attribute| node = Hash.new node[:parent] = "body" node[:tag] = child.name node[:attribute] = element_attribute.name if element_attribute.name node[:value] = element_attribute.value if element_attribute.value node[:pointer] = element_attribute.pointer_id node[:parent_pointer] = child.pointer_id @elements << node end end add_children(child) if child.children.any? end end #end search elements @elements end
get_instructions(source)
click to toggle source
Get the instructions to recreate the html code. Return an array with strings .
Example:
>> CodeTerminator::Html.get_instructions(file.get_elements("exercises/test.html")) => ["Add the tag h2 in body", "Add the tag text in h2 with content 'hola test' ", "Add the tag p in body"]
Arguments:
instructions: (Array)
# File lib/code_terminator/html2.rb, line 252 def get_instructions(source) elements = get_elements(source) text = "" instructions = Array.new elements.each do |child| if child[:tag]!="text" text << "Add the tag " + child[:tag] text << " in " + child[:parent] if !child[:parent].nil? text << " with an attribute '" + child[:attribute] + "' " if !child[:attribute].nil? text << " with value '" + child[:value] + "' " if !child[:value].nil? elsif child[:tag] == "comment" text << " In " + child[:tag]+ " add the text '" + child[:content] + "' " if !child[:content].nil? else text << " In " + child[:parent]+ " add the text '" + child[:content] + "' " if !child[:content].nil? end instructions.push(text) text = "" end instructions end
match(source, code)
click to toggle source
Match if the code have the same elements than the exercise. Return an array with the mismatches.
Example:
hola_mundo.html
> <h1>Hola Mundo!</h1>¶ ↑
>> CodeTerminator::Html.match("hola_mundo.html","<h2>Hola Mundo!</h2>") => ["h1 not exist"]
Arguments:
source: (String) code: (String)
# File lib/code_terminator/html.rb, line 158 def match(source, code) @html_errors = Array.new code = Nokogiri::HTML(code) @elements = get_elements(source) @elements.each do |e| p css_string = build_css(e,'').strip #search_attribute() if e[:attribute] # search_attribute = code.css(css_string).first # if !search_attribute # @html_errors << new_error(element: e, type: 334, description: "`<#{e[:tag]}>` should have an attribute named #{e[:attribute]} with the value #{e[:value]}") # end #search_text() elsif e[:tag] == "text" || e[:tag] == "comment" element_name = e[:tag]=="comment" ? "comment":e[:parent] search_elements = code.css(css_string) if e[:content].strip != "" element = search_elements.select{|hash| hash.text.strip == e[:content].strip} if element.empty? @html_errors << new_error(element: e, type: 330, description: "The text inside `<#{element_name}>` should be #{e[:content]}") end end #search_element() else search_element = code.css(css_string).first if !search_element @html_errors << new_error(element: e[:tag], type: 404, description: "Remember to add the `<#{e[:tag]}>` tag in " + css_string.chomp(e[:tag])) # else # if !are_all_elements(code,e[:tag], css_string) # # @html_errors << new_error(element: e[:tag], type: 404, description: "Remember to add the `<#{e[:tag]}>` tag.") # end end end end count_elements(code) search_attribute_value(code) p @html_errors end
new_file(source,code)
click to toggle source
Create a Html
file with the code of the editor. Return a boolean that indicate if the file was created or not.
Example:
>> CodeTerminator::Html.new_file("hola_mundo.html", "<h1>Hola Mundo!</h1>") => true
Arguments:
source: (String) code: (String)
# File lib/code_terminator/html2.rb, line 25 def new_file(source,code) fileHtml = File.new(source, "w+") result = true begin fileHtml.puts code rescue result = false ensure fileHtml.close unless fileHtml.nil? end #return true if file was succesfully created result end
print_elements(elements)
click to toggle source
Get the elements of the code in html format. Return a string with elements in html.
Example:
>> CodeTerminator::Html.print_elements("exercises/hola_mundo.html" ) => "name = h1<br><hr>name = text<br>content = hola mundo<br><hr>"
Arguments:
elements: (Array)
# File lib/code_terminator/html2.rb, line 230 def print_elements(elements) text = "" elements.each do |child| text << "parent = " + child[:parent] + "<br>" if child[:parent] text << "tag = " + child[:tag] + "<br>" if child[:tag] text << "attribute = " + child[:attribute] + "<br>" if child[:attribute] text << "value = " + child[:value] + "<br>" if child[:value] text << "content = " + child[:content] + "<br>" if child[:content] text << "<hr>" end text end
read_file(source)
click to toggle source
Read a html file. Return the text of the file.
Example:
>> CodeTerminator::Html.read_file("hola_mundo.html") => "<h1>Hola Mundo!</h1>\n"
Arguments:
source: (String)
# File lib/code_terminator/html2.rb, line 199 def read_file(source) if @source_type == "url" fileHtml = open(source).read else fileHtml = File.open(source, "r") end text = "" begin fileHtml.each_line do |line| text << line end fileHtml.close rescue text = false ensure #fileHtml.close unless fileHtml.nil? end text end
validate_syntax(code)
click to toggle source
Validate if the syntax is correct. Return an array with Nokogiri errors.
Example:
>> CodeTerminator::Html.validate_syntax("<h1>Hola Mundo!</h1") => [#<Nokogiri::XML::SyntaxError: expected '>'>]
Arguments:
code: (String)
# File lib/code_terminator/html2.rb, line 162 def validate_syntax(code) errors = Array.new begin Nokogiri::XML(code) { |config| config.strict } #validate if html follow w3, uncomment when check all the page #"<!DOCTYPE html> # <html> # <head> # <h1>asdasd</h1> # <title>asdasd</title> # </head> # <body> # <h1>hola</h1> # </body> # </html>" # @validator = Html5Validator::Validator.new # @validator.validate_text(@html) rescue Nokogiri::XML::SyntaxError => e #errors[0] = "Check if you close your tags" errors[0] = e end errors end
Private Instance Methods
add_children(parent)
click to toggle source
# File lib/code_terminator/html.rb, line 325 def add_children(parent) parent.children.each do |child| if child.attribute_nodes.empty? node = Hash.new node[:parent] = parent.name if child.name == "#cdata-section" node[:tag] = "text" else node[:tag] = child.name end node[:content] = child.text.strip if child.text and child.class != Nokogiri::XML::Element node[:pointer] = child.pointer_id node[:parent_pointer] = child.parent.pointer_id @elements << node else child.attribute_nodes.each do |element_attribute| node = Hash.new node[:parent] = parent.name if element_attribute.name == "#cdata-section" node[:tag] = "text" else node[:tag] = child.name end node[:attribute] = element_attribute.name if element_attribute.name node[:value] = element_attribute.value if element_attribute.value node[:pointer] = element_attribute.pointer_id node[:parent_pointer] = child.pointer_id @elements << node end end add_children(child) if child.children.any? end end
are_all_elements(code, tag, css_string)
click to toggle source
# File lib/code_terminator/html.rb, line 265 def are_all_elements(code, tag, css_string) element_count = @elements.select{|hash| hash[:tag] == tag && !hash[:attribute]}.count code_count = code.css(css_string).count element_count > code_count ? false:true end
build_css(element, css)
click to toggle source
# File lib/code_terminator/html.rb, line 209 def build_css(element, css) if !element[:parent].empty? if !element[:attribute] if element[:tag]=="comment" css += "//comment()" else parent = @elements.select{|hash| hash[:pointer].to_s == element[:parent_pointer].to_s}.first parent_css = parent[:tag].to_s if parent css += parent_css parent_attributes = @elements.select{|hash| hash[:parent_pointer].to_s == element[:parent_pointer].to_s && hash[:attribute]} parent_attributes.each do |par_attr| css += css_attribute_type(par_attr) end css += " " css += element[:tag].to_s + " " if element[:tag] != "text" end else search_attribute = @elements.select{|hash| hash[:parent_pointer].to_s == element[:parent_pointer].to_s && hash[:attribute].to_s == element[:attribute]}.first css += search_attribute[:tag].to_s attribute_css = css_attribute_type(search_attribute) if search_attribute css += attribute_css end else css += element[:tag].to_s + " " if element[:tag] != "text" end css end
check_children(parent)
click to toggle source
# File lib/code_terminator/html.rb, line 380 def check_children(parent) parent.children.each do |child| if child.text child.remove if child.content.to_s.squish.empty? && child.class == Nokogiri::XML::Text end check_children(child) if child.children.any? end end
count_elements(code)
click to toggle source
# File lib/code_terminator/html.rb, line 271 def count_elements(code) uniq_elements = @elements.group_by{|h| h[:tag]} uniq_elements.each do |e| if e[0] != "text" # "element " + e[0].to_s element_count = e[1].select{|hash| !hash[:attribute]}.count if e[0] != "comment" code_count = code.css(e[0]).count else code_count = code.css("//comment()").count end if element_count > code_count @html_errors << new_error(element: e[0], type: 404, description: "Remember to add the `<#{e[0]}>` tag.") end end end end
css_attribute_type(element)
click to toggle source
# File lib/code_terminator/html.rb, line 241 def css_attribute_type(element) case element[:attribute] when "id" css_symbol = '#' css = css_symbol.to_s + element[:value].to_s when "class" css_symbol = '.' css = css_symbol.to_s + element[:value].to_s when "src" css_symbol = "[src]" css = css_symbol.to_s when "href" css_symbol = "[href]" css = css_symbol.to_s when "alt" css_symbol = "[alt]" css = css_symbol.to_s else css_symbol = element[:attribute] css = css_symbol.to_s end css end
look_comment_or_text(code,e)
click to toggle source
methods of match
# File lib/code_terminator/html2.rb, line 580 def look_comment_or_text(code,e) error330 = nil text_found = false @comment_found = false if e[:tag] == "comment" #look for comments or text in code #code, e if code.css(e[:parent]).children.any? #look for comments and text in children of body # code, e #save #return code.css(e[:parent]).children.each do |node_child| #if class of the node is a comment, look in the code # @e, node_child #save error330 #return true (flag) if node_child.class == Nokogiri::XML::Comment error330 = new_error(element: e, type: 330, description: "The text inside the comment should be #{e[:content]}") if e[:content].strip != "" && node_child.text.strip! != e[:content].strip! @comment_found = true end #if class of node is text and element is not a comment #@e, node_child #save a error330, text_found #return true (flag) if node_child.class == Nokogiri::XML::Text && e[:content] != "comment" node_child.text.strip != e[:content].strip ? error330 = new_error(element: e, type: 330, description: "The text inside `<#{e[:parent]}>` should be #{e[:content]}") : text_found = true end end #each else #validate if comment exist and has the expected content in body #code, @e #save @comment_found, text_found if code.css(e[:parent]).text.strip != e[:content].strip if e[:tag] == "comment" error330 = new_error(element: e, type: 330, description: "The text inside the comment should be #{e[:content]}") @comment_found = true else error330 = new_error(element: e, type: 330, description: "The text inside `<#{e[:parent]}>` should be #{e[:content]}") end else text_found = true end end #end if parent has children #throw errors of comment or text #if comment not found, throw error if (defined? @comment_found) && !@comment_found @html_errors << new_error(element: e, type: 404, description: "Remember to add the comment tag") remove_instance_variable(:@comment_found) if !@comment_found end if !text_found && error330 @html_errors << error330 error330 = nil end #end throw errors end
look_parent_text(code,e)
click to toggle source
# File lib/code_terminator/html2.rb, line 641 def look_parent_text(code,e) exist = false #look for text in parent, if found check flag true code.css(e[:parent]).each do |code_css| if code_css.text == e[:content] exist = true end end if !exist @html_errors << new_error(element: e, type: 330, description: "The text inside `<#{e[:parent]}>` should be #{e[:content]}") end end
new_error(args = {})
click to toggle source
# File lib/code_terminator/html.rb, line 389 def new_error(args = {}) element = args[:element] type = args[:type] description = args[:description] node = Hash.new node[:element] = element node[:type] = type node[:description] = description node end
remove_empty_text(reader)
click to toggle source
# File lib/code_terminator/html.rb, line 360 def remove_empty_text (reader) if reader.at('head') reader.at('head').children.each do |child| if child.text child.remove if child.content.to_s.squish.empty? && child.class == Nokogiri::XML::Text end check_children(child) if child.children.any? end end if reader.at('body') reader.at('body').children.each do |child| if child.text child.remove if child.content.to_s.squish.empty? && child.class == Nokogiri::XML::Text end check_children(child) if child.children.any? end end reader end
search_attribute_value(code)
click to toggle source
def search_comments(code)
comment_elements = code.css('//comment()')
end
# File lib/code_terminator/html.rb, line 295 def search_attribute_value(code) uniq_elements = @elements.group_by{|h| h[:tag]} uniq_elements.each do |e| element_with_attributes = e[1].select{|hash| hash[:attribute]} element_with_attributes.each do |ewa| css_string = build_css(ewa, '') if code.css(css_string).empty? @html_errors << new_error(element: ewa, type: 334, description: "`<#{ewa[:tag]}>` should have an attribute named #{ewa[:attribute]}") else if ewa[:value] != "" && code.css(css_string).select{|x| x[ewa[:attribute]].to_s == ewa[:value].to_s}.empty? @html_errors << new_error(element: ewa, type: 333, description: "Make sure that the attribute #{ewa[:attribute]} in `<#{ewa[:tag]}>` has the value #{ewa[:value]}") end if code.css(css_string).select{|x| x[ewa[:attribute]].to_s == ""}.any? @html_errors << new_error(element: ewa, type: 335, description: "`<#{ewa[:attribute]}>` in `<#{ewa[:tag]}>` can't be empty") end end end end end