Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement parser.

  • Loading branch information...
commit 4aa3e2369570127ac51fc9a6f313f6823f76c4ef 1 parent dd03601
@ytaka authored
View
3  lib/cmdnote.rb
@@ -1,5 +1,6 @@
require "cmdnote/version"
module CmdNote
- # Your code goes here...
+ autoload :Client, "cmdnote/oauth"
+ autoload :Article, "cmdnote/parser"
end
View
215 lib/cmdnote/html_tags.rb
@@ -0,0 +1,215 @@
+require 'uri'
+require 'cgi'
+
+module CmdNote
+ class Article
+ class HTMLTag
+ module Utils
+ def enclose_tag(tag, str, opts = {})
+ s = "<#{tag}"
+ opts.each do |attr, val|
+ s << " #{attr}=\"#{val}\""
+ end
+ s << ">#{str}</#{tag}>"
+ end
+ module_function :enclose_tag
+ end
+
+ include Utils
+ attr_reader :elements
+
+ def self.responding_tag
+ @tag
+ end
+
+ def self.enclose_options
+ @opts_for_enclosing
+ end
+
+ def self.set_responding_tag(opts = {})
+ @tag = name.split("::")[-1].downcase
+ @opts_for_enclosing = opts
+
+ define_method(:enclose_responding_tag) do |str, opts = {}|
+ enclose_tag(self.class.responding_tag, str, opts)
+ end
+
+ alias_method :to_html_original, :to_html
+
+ define_method(:to_html) do
+ enclose_responding_tag(to_html_original, self.class.enclose_options)
+ end
+ end
+
+ # @param [Array] args An array of elements.
+ def initialize(*args)
+ @elements = parse_source(args)
+ end
+
+ def each(&block)
+ @elements.each(&block)
+ end
+
+ def to_html
+ s = ''
+ @elements.each do |el|
+ s << el.to_html << "\n"
+ end
+ s
+ end
+
+ def split_text_including_links(str)
+ html_tags = []
+ if (uris = URI.extract(str)).empty?
+ html_tags << CmdNote::Article::HTMLTag::Text.new(str)
+ else
+ uris.each do |uri|
+ if /^(.*?)#{Regexp.escape(uri)}(.*)$/m =~ str
+ text = Regexp.last_match[1]
+ str = Regexp.last_match[2]
+ if text.size > 0
+ html_tags << CmdNote::Article::HTMLTag::Text.new(text)
+ end
+ html_tags << CmdNote::Article::HTMLTag::URLString.new(uri)
+ end
+ end
+ if str.size > 0
+ html_tags << CmdNote::Article::HTMLTag::Text.new(str)
+ end
+ end
+ html_tags
+ end
+
+ # @param [Symbol] type The value is :p, :pre, :ul, :ol, :dl, :tr, :td, or :table.
+ def self.create(type, args)
+ CmdNote::Article::HTMLTag.const_get(type.upcase).new(*args)
+ end
+
+ class Text < String
+ def to_html
+ CGI.escapeHTML(self)
+ end
+ end
+
+ class RawText < String
+ def to_html
+ self
+ end
+ end
+
+ class URLString < String
+ include Utils
+
+ def to_html
+ enclose_tag("a", self, :href => self, :target => "_blank")
+ end
+ end
+
+ class P < HTMLTag
+ set_responding_tag
+
+ def parse_source(args)
+ split_text_including_links(args.join("\n"))
+ end
+ private :parse_source
+ end
+
+ class PRE < HTMLTag
+ set_responding_tag(:class => "prettyprint")
+
+ def parse_source(args)
+ [CmdNote::Article::HTMLTag::RawText.new(args.join("\n"))]
+ end
+ private :parse_source
+ end
+
+ class List < HTMLTag
+ def parse_source(args)
+ args.map do |arg|
+ CmdNote::Article::HTMLTag::LI.new(arg)
+ end
+ end
+ private :parse_source
+ end
+
+ class UL < List
+ set_responding_tag
+ end
+
+ class OL < List
+ set_responding_tag
+ end
+
+ class ListItem < HTMLTag
+ def parse_source(args)
+ args.inject([]) do |ary, arg|
+ ary.concat(split_text_including_links(arg))
+ end
+ end
+ private :parse_source
+ end
+
+ class LI < ListItem
+ set_responding_tag
+ end
+
+ class DL < HTMLTag
+ set_responding_tag
+
+ def parse_source(args)
+ ary = []
+ args.each do |dt, dd|
+ ary << CmdNote::Article::HTMLTag::DT.new(dt)
+ ary << CmdNote::Article::HTMLTag::DD.new(dd)
+ end
+ ary
+ end
+ private :parse_source
+ end
+
+ class DD < ListItem
+ set_responding_tag
+ end
+
+ class DT < ListItem
+ set_responding_tag
+ end
+
+ class TABLE < HTMLTag
+ set_responding_tag
+
+ def parse_source(args)
+ args.map do |els|
+ CmdNote::Article::HTMLTag::TR.new(*els)
+ end
+ end
+ private :parse_source
+ end
+
+ class TR < HTMLTag
+ set_responding_tag
+
+ def parse_source(args)
+ args.map do |el|
+ CmdNote::Article::HTMLTag::TD.new(el)
+ end
+ end
+ private :parse_source
+ end
+
+ class TD < ListItem
+ set_responding_tag
+ end
+ end
+
+ class HTMLRoot < HTMLTag
+ def initialize
+ @elements = []
+ end
+
+ def add(*args)
+ @elements << CmdNote::Article::HTMLTag.create(*args)
+ end
+ end
+ end
+end
View
158 lib/cmdnote/parser.rb
@@ -0,0 +1,158 @@
+require 'cmdnote/html_tags'
+require 'active_support/core_ext/object/blank'
+
+module CmdNote
+ class Article
+ TITLE_MAX_SIZE = 120
+ CONTENT_MAX_SIZE = 2048
+ LANG_CODE_LIST = ["en", "ja"]
+
+ attr_accessor :title, :content, :tags, :lang
+
+ def initialize(val)
+ @title = val[:title] || ''
+ @content = val[:content] || ''
+ @tags = val[:tags] || []
+ @lang = val[:lang]
+ end
+
+ def to_text
+ s = "#{@title}\n"
+ if @tags && !@tags.empty?
+ s << @tags.join(',') << "\n"
+ end
+ s << "\n"
+ s << @content
+ s
+ end
+
+ def valid?
+ (title_size = @title.size) > 0 && title_size <= TITLE_MAX_SIZE &&
+ (content_size = @content.size) > 0 && content_size <= CONTENT_MAX_SIZE &&
+ (!@lang || LANG_CODE_LIST.include?(@lang))
+ end
+
+ def content_to_html
+ CmdNote::Article::Parser.new.parse(@content).to_html
+ end
+
+ def self.parse(str)
+ lines = str.each_line.to_a
+ if lines.size < 3
+ raise ArgumentError, "Invalid format: more than three lines are needed."
+ end
+ title_str = lines[0].strip
+ tag_name_list, opts = CmdNote::Article::Parse.parse_option(lines[1])
+ [2, -1].each do |n|
+ while !lines.empty? && lines[n].blank?
+ lines.delete_at(n)
+ end
+ end
+ content_str = ''
+ lines[2..-1].each do |l|
+ content_str << l.chomp << "\r\n"
+ end
+ self.new(:title => title_str, :content => content_str, :tags => tag_name_list, :lang => opts['lang'])
+ end
+
+ module Parse
+ def parse_option(str)
+ tag_name_list = []
+ opts = {}
+ str.split(",").each do |word|
+ word.strip!
+ if word.size > 0
+ if word.include?("=>")
+ ary = word.split("=>").map(&:strip)
+ if ary.size > 1
+ opts[ary[0]] = ary[1]
+ end
+ else
+ tag_name_list << word
+ end
+ end
+ end
+ [tag_name_list, opts]
+ end
+ module_function :parse_option
+ end
+
+ class Parser
+ def clear
+ @root = CmdNote::Article::HTMLRoot.new
+ @last_line_type = nil
+ @last_elements = []
+ end
+ private :clear
+
+ def element_cache_empty?
+ @last_elements.empty?
+ end
+ private :element_cache_empty?
+
+ def shift_tag(line_type = nil, element = nil)
+ if @last_line_type
+ @root.add(@last_line_type, @last_elements)
+ end
+ @last_line_type = line_type
+ @last_elements = []
+ @last_elements << element if element
+ end
+ private :shift_tag
+
+ def line_type_chinge?(cur)
+ @last_line_type != cur
+ end
+ private :line_type_chinge?
+
+ def parse_current_line(cur_type, args)
+ if line_type_chinge?(cur_type)
+ shift_tag(cur_type, args)
+ else
+ @last_elements << args
+ end
+ end
+ private :parse_current_line
+
+ def parse(content)
+ clear
+ content.each_line do |l|
+ l.chomp!
+ if /^\Z/ =~ l
+ shift_tag if @last_line_type
+ else
+ parsed_current_line = true
+ if l[0] == '\\'
+ l = l[1..-1]
+ parsed_current_line = false
+ else
+ if l[0] == ' '
+ parse_current_line(:pre, l[1..-1])
+ elsif /^\* (.*)$/ =~ l
+ item = Regexp.last_match[1]
+ parse_current_line(:ul, item)
+ elsif /^\d+\. (.*)$/ =~ l
+ item = Regexp.last_match[1]
+ parse_current_line(:ol, item)
+ elsif /^\|(.*)\|$/ =~ l
+ table_els = Regexp.last_match[1].split("|").map(&:strip)
+ parse_current_line(:table, table_els)
+ elsif /^\[(.*)\](.*)$/ =~ l
+ dl = Regexp.last_match[1].strip
+ desc = Regexp.last_match[2].strip
+ parse_current_line(:dl, [dl, desc])
+ else
+ parsed_current_line = false
+ end
+ end
+ unless parsed_current_line
+ parse_current_line(:p, l)
+ end
+ end
+ end
+ shift_tag unless element_cache_empty?
+ @root
+ end
+ end
+ end
+end
View
2  lib/cmdnote/response.rb
@@ -13,6 +13,8 @@ def success?
def parse
unless success?
+ STDERR.puts @response.header
+ STDERR.puts @response.body
raise "Response is an error: #{@response.inspect}"
end
case @type
View
2  spec/spec_helper.rb
@@ -2,3 +2,5 @@
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'rspec'
+require 'cmdnote/oauth'
+require 'cmdnote/parser'
Please sign in to comment.
Something went wrong with that request. Please try again.