class LiveBlog
Public Class Methods
new(x=nil, config: nil, datetoday: Date.today, plugins: {}, log: nil)
click to toggle source
the config can either be a hash, a config filepath, or nil
# File lib/liveblog.rb, line 17 def initialize(x=nil, config: nil, datetoday: Date.today, plugins: {}, log: nil) @log = log log.info 'LiveBlog: inside initialize' if @log config = if x or config then x || config else if File.exists? 'liveblog.conf' then 'liveblog.conf' else sc = SimpleConfig.new(new_config()) File.write 'liveblog.conf', sc.write sc end end h = SimpleConfig.new(config).to_h @dir, @urlbase, @edit_url, @css_url, @xsl_path, @xsl_today_path, \ @xsl_url, @bannertext, @title, @rss_title, @rss_lang, \ @hyperlink_today, plugins, @photo_upload_url = \ (%i(dir urlbase edit_url css_url xsl_path xsl_today_path xsl_url) \ + %i( bannertext title rss_title rss_lang hyperlink_today ) \ + %i(plugins photo_upload_url)).map{|x| h[x]} @title ||= 'LiveBlog' @rss_lang ||= 'en-gb' Dir.chdir @dir @d = datetoday dxfile = File.join(@dir, path(), 'index.xml') @plugins = initialize_plugins(plugins || []) if File.exists? dxfile then load_file(dxfile) else new_day() end end
Public Instance Methods
add_entry(raw_entry)
click to toggle source
add a single line entry
# File lib/liveblog.rb, line 72 def add_entry(raw_entry) new_day() if date() != Date.today entry, hashtag = raw_entry.split(/\s*#(?=\w+$)/) hashtag.downcase! success, msg = case raw_entry when /^#\s*\w.*#\w+$/ then add_section raw_entry, hashtag when /#\w+$/ then entry.gsub!(/(?:^|\s)!t\s/, '\1' + time()) add_section_entry entry, hashtag else [false, 'no valid entry found'] end return [false, msg] unless success save() # we reserve 30 characters for the link len = (140 - 30 - hashtag.length) raw_entry.gsub!(/(?:^|\s)!t\z/,'') entry = raw_entry.length > len ? "%s... %s" % [raw_entry.slice(0, len),\ hashtag] : raw_entry message = "%s %s#%s" % [entry, static_urlpath(), hashtag] [true, message] end
date()
click to toggle source
# File lib/liveblog.rb, line 108 def date() #@logger = Logger.new '/tmp/liveblog.log', 'daily' #@logger.debug 'inside date' @d end
find_hashtag(hashtag)
click to toggle source
returns a RecordX object for a given hashtag
# File lib/liveblog.rb, line 116 def find_hashtag(hashtag) @dx.find hashtag[/\w+$/] end
initialize_plugins(plugins)
click to toggle source
# File lib/liveblog.rb, line 120 def initialize_plugins(plugins) plugins.inject([]) do |r, plugin| name, settings = plugin return r if settings[:active] == false and !settings[:active] klass_name = 'LiveBlogPlugin' + name.to_s r << Kernel.const_get(klass_name).new(settings: settings, \ variables: {filepath: @dir, todays_filepath: path(@d), \ urlbase: @urlbase }) end end
link_today()
click to toggle source
Use with yesterday's liveblog;
# File lib/liveblog.rb, line 140 def link_today() raw_formatted_filepath = File.join(@dir, path(@d-1), 'formatted.xml') return unless File.exists? raw_formatted_filepath doc = Rexle.new File.read(raw_formatted_filepath) doc.root.element('summary/next_day').text = static_urlpath() File.write raw_formatted_filepath, doc.xml(pretty: true) render_html doc, @d-1 end
load_file(dxfile)
click to toggle source
# File lib/liveblog.rb, line 154 def load_file(dxfile) @dx = Dynarex.new(dxfile) @d = Date.parse @dx.title end
new_day(date: Date.today)
click to toggle source
# File lib/liveblog.rb, line 159 def new_day(date: Date.today) @d = date new_file() link_today() if @log then @log.info 'LiveBlog/new_day: before [plugins]#on_new_day' end @plugins.each do |x| if x.respond_to? :on_new_day then yesterdays_index_file = File.join(@dir, path(@d-1), 'index.xml') x.on_new_day(yesterdays_index_file, urlpath(@d-1)) end end @log.info 'LiveBlog: inside new_day' if @log 'new_day() successful' end
new_file(x=nil)
click to toggle source
# File lib/liveblog.rb, line 186 def new_file(x=nil) @log.info 'LiveBlog/new_file: inside new_file()' if @log s = nil s, _ = RXFHelper.read(x) if x @log.info 'LiveBlog/new_file: s: ' + s.inspect if @log s ||= <<EOF <?dynarex schema="sections[title]/section(x)"?> title: #{@title} #{ordinalize(@d.day) + @d.strftime(" %B %Y")} --# EOF t = Time.now # keyword substitions # a !t becomes the Time.now.strftime("%-I:%M%P") #=> 4:27pm s.gsub!(/\B\*started\s+<time>(\d+:\d+[ap]m)<\/time>\*\B\s*!tc\z/) do |x| raw_start_time = $1 start_time = Time.parse raw_start_time seconds = t - start_time list = Subunit.new(units={minutes:60, hours:60}, seconds: seconds )\ .to_h.to_a n = list.find {|_,v| v > 0 } duration = list[list.index(n)..-2].map {|x|"%d %s" % x.reverse}\ .join(', ') "*completed %s; duration: %s*" % [time(t), duration] end s.gsub!(/(?:^|\s)ts\z/, "*started #{time(t)}*") s.gsub!(/(?:^|\s)!tc\z/, "*completed #{time(t)}*") s.gsub!(/(?:^|\s)!t\s/, '\1' + time(t)) @log.info 'LiveBLog/new_file: before mkdir_p: ' + path().inspect if @log FileUtils.mkdir_p File.join(@dir, path()) @dx = Dynarex.new @dx.import(s) {|x| sanitise x } @dx.xpath('records/section').each do |rec| rec.attributes[:uid] = rec.attributes[:id] rec.attributes[:id] = rec.text('x').lines.first[/#(\w+)$/,1] end @dx.instance_variable_set :@dirty_flag, true @log.info 'LiveBlog/new_file: about to save new_file' if @log save() end
Also aliased as: import
plugins()
click to toggle source
# File lib/liveblog.rb, line 244 def plugins() @plugins.map(&:class) end
raw_view(tag)
click to toggle source
# File lib/liveblog.rb, line 248 def raw_view(tag) r = self.find_hashtag tag r.x if r end
save()
click to toggle source
# File lib/liveblog.rb, line 253 def save() @dx.save File.join(@dir, path(), 'index.xml') File.write File.join(@dir, path(), 'index.txt'), @dx.to_s save_html() save_rss() save_frontpage() FileUtils.cp File.join(@dir, path(),'raw_formatted.xml'), \ File.join(@dir, path(),'raw_formatted.xml.bak') 'save() successful' end
update(val)
click to toggle source
# File lib/liveblog.rb, line 283 def update(val) self.method(val[/^<\?dynarex/] ? :import : :update_entry).call val end
update_entry(raw_entry)
click to toggle source
update a page (contains multiple liveblog entries for a section) entry
# File lib/liveblog.rb, line 289 def update_entry(raw_entry) hashtag = raw_entry.lines.first[/#(\w+)$/,1] record_found = find_hashtag hashtag if record_found then record_found.x = sanitise raw_entry save() if @log then @log.info 'LiveBlog/update_entry: before [plugins]#on_update_entry' end @plugins.each do |x| if x.respond_to? :on_update_entry then x.on_update_section(raw_entry, hashtag) end end [true] else [false, 'record for #' + hashtag + ' not found.'] end end
valid_entry?(entry)
click to toggle source
determines if the entry to be added uses a valid hashtag
# File lib/liveblog.rb, line 320 def valid_entry?(entry) return false unless entry =~ /#\w+/ tag = entry[/#(\w+)$/,1] entry.lstrip[/^#\s+/] or (tag and tags().include?(tag)) ? true : false end
Private Instance Methods
add(summary, name, s)
click to toggle source
# File lib/liveblog.rb, line 595 def add(summary, name, s) summary.add Rexle::Element.new(name).add_text s end
add_section(raw_entry, hashtag)
click to toggle source
# File lib/liveblog.rb, line 358 def add_section(raw_entry, hashtag) records = @dx.records uid = if records then r = records.max_by {|k,v| v[:uid].to_i} r ? r[1][:uid].succ : '1' else '1' end @dx.create({x: raw_entry.sub(/(#\w+)$/){|x| x.downcase}}, \ id: hashtag.downcase, custom_attributes: {uid: uid}) if @log then @log.info 'LiveBlog/add_section: before [plugins]#on_new_section' end @plugins.each do |x| x.on_new_section(raw_entry, hashtag) if x.respond_to? :on_new_section end [true, 'section added'] end
add_section_entry(raw_entry, hashtag)
click to toggle source
# File lib/liveblog.rb, line 333 def add_section_entry(raw_entry, hashtag) rec = @dx.find hashtag return [false, 'rec not found'] unless rec entry = sanitise raw_entry.chomp rec.x += "\n\n" + entry + "\n" if @log then @log.info 'LiveBlog/add_section_entry: before ' + '[plugins]#on_new_section_entry' end @plugins.each do |x| if x.respond_to? :on_new_section_entry then x.on_new_section_entry(raw_entry, hashtag) end end [true, 'entry added to section ' + hashtag] end
code2indent(s2)
click to toggle source
used by the sanitise method
# File lib/liveblog.rb, line 387 def code2indent(s2) # find the beginning x1 = s2 =~ /^```/ return s2 unless x1 # find the end x2 = s2[x1+3..-1] =~ /^```/ return s2 unless x2 codeblock = s2[x1+3..x1+x2+2] s3 = s2[0..x1-1] + codeblock.lines.map{|x| x.prepend(' ' * 4)}.join + \ "\n" + s2[x1+x2+6..-1] return code2indent(s3) end
new_config()
click to toggle source
# File lib/liveblog.rb, line 407 def new_config() dir = Dir.pwd { dir: dir, urlbase: 'http://www.yourwebsitehere.org/liveblog/', edit_url: 'http://www.yourwebsitehere.org/do/liveblog/edit', photo_upload_url: 'http://www.yourwebsitehere.org/do/liveblog/upload', css_url: '/liveblog/liveblog.css', xsl_path: File.join(dir, 'liveblog.xsl'), xsl_today_path: File.join(dir, 'liveblog_today.xsl'), xsl_url: '/liveblog/liveblog.xsl', hyperlink_today: 'http://yourwebsite.org/go_to_today', bannertext: '', title: "John Smith's LiveBlog", rss_title: "John Smith's LiveBlog", rss_lang: 'en_gb' } end
ordinalize(n)
click to toggle source
# File lib/liveblog.rb, line 590 def ordinalize(n) n.to_s + ( (10...20).include?(n) ? 'th' : %w{ th st nd rd th th th th th th }[n % 10] ) end
path(d=@d)
click to toggle source
returns an array with a fractured date in the format [YYYY,mmm,dd] e.g. #=> ['2015','apr','11']
# File lib/liveblog.rb, line 431 def path(d=@d) [d.year.to_s, Date::MONTHNAMES[d.month].downcase[0..2], d.day.to_s] end
render_html(doc, d=@d, xsl: @xsl_path)
click to toggle source
# File lib/liveblog.rb, line 599 def render_html(doc, d=@d, xsl: @xsl_path) xslt_buffer = if xsl then buffer, _ = RXFHelper.read xsl buffer else lib = File.exists?('liveblog.xsl') ? '.' : File.dirname(__FILE__) File.read(File.join(lib,'liveblog.xsl')) end #jr270327 xslt = Nokogiri::XSLT(xslt_buffer) #jr270327 out = xslt.transform(Nokogiri::XML(doc.xml)) out = Rexslt.new(xslt_buffer, doc.xml).to_s File.write File.join(path(d), 'index.html'), \ out.to_s.gsub(/\s+(?=<\/?(?:pre|code)>)/,'') end
rss_timestamp(s)
click to toggle source
# File lib/liveblog.rb, line 620 def rss_timestamp(s) Time.parse(s).strftime("%a, %-d %b %Y %H:%M:%S %Z") end
sanitise(raw_s)
click to toggle source
# File lib/liveblog.rb, line 446 def sanitise(raw_s) # Transform any code encapsulated within 3 backticks to the # 4 spaces indented style. This prevents Ruby comments being # wrongfully identified as a sectionx heading. code2indent(raw_s) end
save_frontpage()
click to toggle source
# File lib/liveblog.rb, line 687 def save_frontpage() # this method is still under developement. If the previous day's # file doesn't exist it will simply return nil. begin doc, doc2 = [@d-1, @d].map do |d| url = @urlbase + d.strftime("%Y/%b/%-d/formatted.xml") Rexle.new RXFHelper.read(url).first end doc2.root.add doc.root.element('records') File.write 'formatted.xml', doc2.xml rescue nil end end
save_html()
click to toggle source
# File lib/liveblog.rb, line 456 def save_html() formatted2_filepath = File.join(@dir, path(), 'formatted2.xml') FileUtils.cp File.join(@dir, path(), 'index.xml'), formatted2_filepath doc = Rexle.new File.read(formatted2_filepath) summary = doc.root.element('summary') summary.element('recordx_type').delete add summary, 'edit_url', "%s/%s" % [@edit_url, urlpath()] add summary, 'photo_upload_url', "%s/%s" % [@photo_upload_url, urlpath()] add summary, 'date', @d.strftime("%d-%b-%Y").upcase add summary, 'day', @d.strftime("%A") add summary, 'date_uri', urlpath() add summary, 'css_url', @css_url add summary, 'published', Time.now.strftime("%d-%m-%Y %H:%M") add summary, 'prev_day', static_urlpath(@d-1) add summary, 'next_day', @d == Date.today ? '' : static_urlpath(@d+1) add summary, 'bannertext', @bannertext add summary, 'link', static_urlpath(@d) add summary, 'hyperlink_today', @hyperlink_today tags = Rexle::Element.new('tags') doc.root.xpath('records/section/x/text()').each do |x| tags.add Rexle::Element.new('tag').add_text x.lines.first.rstrip[/#(\w+)$/,1]\ .downcase end summary.add tags domain = @urlbase[/https?:\/\/([^\/]+)/,1].split('.')[-2..-1].join('.') dxsx = DxSectionX.new(doc, domain: domain, xsl_url: @xsl_url) xmldoc = dxsx.to_doc File.write formatted2_filepath, xmldoc.xml(pretty: true) # save the related CSS file locally if the file doesn't already exist if not File.exists? 'liveblog.css' then FileUtils.cp File.join(File.dirname(__FILE__), 'liveblog.css'),\ 'liveblog.css' end raw_formatted_filepath = File.join(@dir, path(), 'raw_formatted.xml') formatted_filepath = File.join(@dir, path(), 'formatted.xml') unless File.exists? raw_formatted_filepath then doc2 = xmldoc.root.deep_clone doc2.root.traverse do |node| node.attributes[:created] = Time.now.to_s end File.write raw_formatted_filepath, doc2.xml(pretty: true) return end buffer = File.read(raw_formatted_filepath) buffer2 = File.read(formatted2_filepath) doc = RexleDiff.new(buffer, buffer2, fuzzy_match: true).to_doc File.write raw_formatted_filepath, doc.xml(pretty: true) doc.root.xpath('records/section').each do |node| node.attributes[:created] ||= Time.now.to_s #t = Time.parse(node.attributes[:last_modified].empty? ? \ # node.attributes[:created] : node.attributes[:last_modified]) t = Time.parse(node.attributes[:created]) element = Rexle::Element.new('time', value: t.strftime("%-I:%M%P"), \ attributes: {class: 'border'}) node.element('details/summary').add element node.xpath('details/p').each do |e| e.attributes[:created] ||= Time.now.to_s t = Time.parse(e.attributes[:created]) element = Rexle::Element.new('time', value: t.strftime("%-I:%M%P"), \ attributes: {class: 'border'}) e.prepend element end node.xpath('//h2').each do |h2| h2.attributes[:id] = node.attributes[:id] \ + Time.parse(h2.attributes[:created]).strftime("%H%M") end end related_links_filepath = File.join(@dir, path(), 'related_links.xml') if File.exists? related_links_filepath then related_links = Rexle::Element.new 'related_links' doc2 = Rexle.new(File.read(related_links_filepath)) doc2.root.xpath('body/*').each {|element| related_links.add element } doc.root.add related_links end if @log then @log.info 'LiveBlog/save_html: before [plugins]#on_doc_update' end @plugins.each do |x| if x.respond_to? :on_doc_update then begin x.on_doc_update(doc) rescue if @log then @log.debug "LiveBlog/save_html/error: %s#on_doc_update %s" % \ [x.class.to_s, ($!)] end end end end render_html doc, xsl: @xsl_today_path File.write formatted_filepath, doc.xml(pretty: true) end
save_rss()
click to toggle source
# File lib/liveblog.rb, line 624 def save_rss() buffer = File.read File.join(@dir, path(), 'raw_formatted.xml') doc = Rexle.new buffer summary = doc.root.element 'summary' dx = Dynarex.new 'liveblog[title,desc,link, pubdate, '\ + 'lastbuild_date, lang]/entry(title, desc, created_at, uri, guid)' dx.title = @rss_title || 'LiveBlog' dx.desc = 'Latest LiveBlog posts fetched from ' + @urlbase dx.link = @urlbase dx.order = 'descending' dx.pubdate = rss_timestamp(summary.text('published')) dx.lastbuild_date = Time.now.strftime("%a, %-d %b %Y %H:%M:%S %Z") dx.lang = @rss_lang doc.root.xpath('records/section/details').each do |x| next if x.elements.empty? or x.element('summary').elements.empty? a = x.elements.to_a h1 = a.shift.element('h1') hashtag = h1.text[/#\w+$/] next unless h1.attributes[:created] created_at = rss_timestamp(h1.attributes[:created]) a.each do |node| node.attributes.delete :created node.attributes.delete :last_modified node.traverse do |x| x.attributes.delete :created x.attributes.delete :last_modified end end uri = doc.root.element('summary/link/text()') + hashtag record = { title: h1.text, desc: a.map(&:xml).join("\n"), created_at: created_at, uri: uri, guid: uri } dx.create record end dx.xslt_schema = 'channel[title:title,description:desc,link:link,'\ + 'pubDate:pubdate,lastBuildDate:lastbuild_date,language:lang]/'\ + 'item(title:title,link:uri,description:desc,pubDate:created_at,'\ + 'guid:guid)' File.write 'rss.xml', dx.to_rss end
static_urlpath(d=@d)
click to toggle source
# File lib/liveblog.rb, line 442 def static_urlpath(d=@d) @urlbase.sub(/[^\/]$/,'\0/') + urlpath(d) end
time(t=Time.now)
click to toggle source
# File lib/liveblog.rb, line 616 def time(t=Time.now) t.strftime "<time>%-I:%M%P</time>" end
urlpath(d=@d)
click to toggle source
returns a string representing a date within a directory path e.g. #=> 2015/apr/11/
# File lib/liveblog.rb, line 438 def urlpath(d=@d) path(d).join('/') + '/' end