Class Hobix::Weblog
In: lib/hobix/weblog.rb
lib/hobix/base.rb
Parent: Object

placed here to avoid dependency cycle between base.rb and weblog.rb

Methods

Included Modules

BaseProperties

Classes and Modules

Class Hobix::Weblog::AuthorNotFound

Attributes

hobix_yaml  [R] 
path  [RW] 

Public Class methods

[Source]

     # File lib/hobix/base.rb, line 295
295:     def self.add_entry_class( c )
296:         @@entry_classes << c
297:     end

Load the weblog information from a YAML file and start the Weblog.

[Source]

     # File lib/hobix/weblog.rb, line 404
404:     def Weblog::load( hobix_yaml )
405:         hobix_yaml = File.expand_path( hobix_yaml )
406:         weblog = YAML::load( File::open( hobix_yaml ) )
407:         weblog.start( hobix_yaml )
408:         weblog
409:     end

Public Instance methods

[Source]

     # File lib/hobix/weblog.rb, line 834
834:     def authorize( user, pass )
835:         require 'digest/sha1'
836:         authors[user]['password'] == Digest::SHA1.new( pass )
837:     end

Used by regenerate to construct the vars hash by calling the appropriate skel method for each page.

[Source]

     # File lib/hobix/weblog.rb, line 425
425:     def build_pages( page_name )
426:         vars = {}
427:         paths = page_name.split( '/' )
428:         loop do
429:             try_page = paths.join( '_' ).gsub('-','_')
430:             if respond_to? "skel_#{ try_page }"
431:                 section_path = File.dirname( page_name )
432:                 path_storage = storage.path_storage( section_path )
433:                 method( "skel_#{ try_page }" ).call( path_storage, section_path ) do |vars|
434:                     vars[:weblog] = self
435:                     raise TypeError, "No `page' variable returned from skel_#{ try_page }." unless vars[:page]
436:                     yield vars
437:                 end
438:                 return
439:             end
440:             break unless paths.slice!( -2 )  ## go up a directory
441:         end
442:         vars[:weblog] = self
443:         vars[:page] = Page.new( page_name )
444:         vars[:page].timestamp = Time.now
445:         yield vars
446:     end

[Source]

     # File lib/hobix/weblog.rb, line 359
359:     def central_ext; @central_ext =~ /^\w*$/ ? @central_ext.untaint : default_central_ext; end

[Source]

     # File lib/hobix/weblog.rb, line 358
358:     def central_prefix; @central_prefix =~ /^[\w\.]+$/ ? @central_prefix.untaint : default_central_prefix; end

[Source]

     # File lib/hobix/weblog.rb, line 350
350:     def default_central_ext; "html"; end

[Source]

     # File lib/hobix/weblog.rb, line 349
349:     def default_central_prefix; "entry"; end

[Source]

     # File lib/hobix/weblog.rb, line 351
351:     def default_entry_class; "Hobix::Entry"; end

[Source]

     # File lib/hobix/weblog.rb, line 345
345:     def default_entry_path; "entries"; end

[Source]

     # File lib/hobix/weblog.rb, line 352
352:     def default_index_class; "Hobix::IndexEntry"; end

[Source]

     # File lib/hobix/weblog.rb, line 348
348:     def default_lib_path; "lib"; end

[Source]

     # File lib/hobix/weblog.rb, line 347
347:     def default_output_path; "htdocs"; end

[Source]

     # File lib/hobix/weblog.rb, line 346
346:     def default_skel_path; "skel"; end

[Source]

     # File lib/hobix/weblog.rb, line 360
360:     def entry_class( tag = nil )
361:         tag = @entry_class =~ /^[\w:]+$/ ? @entry_class.untaint : default_entry_class unless tag
362:             
363:         found_class = nil
364:         if @@entry_classes
365:             found_class = @@entry_classes.find do |c|
366:                 tag == c.name.split( '::' ).last.downcase
367:             end
368:         end
369: 
370:         begin
371:             found_class || Hobix.const_find( tag )
372:         rescue NameError => e
373:             raise NameError, "No such entry class #{ tag }"
374:         end
375:     end

[Source]

     # File lib/hobix/weblog.rb, line 354
354:     def entry_path; File.expand_path( @entry_path || default_entry_path, @path ).untaint; end

Translate paths relative to the weblahhg‘s URL. This is especially important if a weblog isn‘t at the root directory for a domain.

[Source]

     # File lib/hobix/weblog.rb, line 399
399:     def expand_path( path )
400:         File.expand_path( path.gsub( /^\/+/, '' ), self.link.path.gsub( /\/*$/, '/' ) )
401:     end

[Source]

     # File lib/hobix/weblog.rb, line 482
482:     def facet_for( app )
483:         facets.each { |p| return if p.get app }
484:         Hobix::BaseFacet.not_found app
485:     end

Returns an Array of all facet plugins in use. (There can be many.)

[Source]

     # File lib/hobix/weblog.rb, line 478
478:     def facets
479:         @plugins.find_all { |p| p.is_a? BaseFacet }
480:     end

[Source]

     # File lib/hobix/weblog.rb, line 376
376:     def index_class( tag = nil )
377:         tag = @index_class =~ /^[\w:]+$/ ? @index_class.untaint : default_index_class unless tag
378:         begin
379:             Hobix.const_find( tag )
380:         rescue NameError => e
381:             raise NameError, "No such index class #{ tag }"
382:         end
383:     end

[Source]

     # File lib/hobix/weblog.rb, line 813
813:     def join_path( prefix, suffix )
814:         case prefix
815:         when '', '.'
816:             suffix
817:         else
818:             "#{ prefix }/#{ suffix }"
819:         end
820:     end

[Source]

     # File lib/hobix/weblog.rb, line 357
357:     def lib_path; File.expand_path( @lib_path || default_lib_path, @path ).untaint; end

[Source]

     # File lib/hobix/weblog.rb, line 385
385:     def link
386:         URI::parse( @link.gsub( /\/$/, '' ) ).extend Hobix::UriStr
387:     end

[Source]

     # File lib/hobix/weblog.rb, line 389
389:     def linklist
390:         if @linklist.class == ::Array
391:             YAML::transfer( 'hobix.com,2004/linklist', {'links' => @linklist} )
392:         else
393:            @linklist
394:         end
395:     end

Loads an entry from storage, first validating that the author is listed in the weblog config.

[Source]

     # File lib/hobix/weblog.rb, line 826
826:     def load_and_validate_entry( entry_id )
827:         entry = storage.load_entry( entry_id )
828:         unless authors.has_key?( entry.author )
829:             raise AuthorNotFound, "Invalid author '#{ entry.author }' found in entry #{ entry_id }"
830:         end
831:         entry
832:     end

For convenience, storage queries can be made through the Weblog class. Queries will return the full Entry data, though, so it‘s best to use this only when you‘re scripting and need data quick.

[Source]

     # File lib/hobix/weblog.rb, line 842
842:     def method_missing( methId, *args )
843:         if storage.respond_to? methId
844:             storage.method( methId ).call( *args ).collect do |e|
845:                 load_and_validate_entry( e.id )
846:             end
847:         end
848:     end

Built from the map of output destinations described by output_map, this map pairs entry IDs against their canonical destinations. The @central_prefix and @central_ext variables determine what output is canonical.

[Source]

     # File lib/hobix/weblog.rb, line 545
545:     def output_entry_map
546:         output_map
547:         @output_entry_map
548:     end

Reads skel_path for templates and builds a hash of all the various output files which will be generated. This method will cache the output_map once. Subsequent calls to output_map will quickly return the cached hash. To reset the cache, use reset_output_map.

[Source]

     # File lib/hobix/weblog.rb, line 494
494:     def output_map
495:         @output_map ||= nil
496:         return @output_map if @output_map
497:         path_watch = {}
498:         @output_entry_map = {}
499:         Find::find( skel_path ) do |path|
500:             path.untaint
501:             if File.basename(path)[0] == ?.
502:                 Find.prune 
503:             elsif not FileTest.directory? path
504:                 tpl_path = path.gsub( /^#{ Regexp::quote( skel_path ) }\/?/, '' )
505:                 output = outputs.detect { |p| if tpl_path =~ /\.#{ p.extension }$/; tpl_path = $`; end }
506:                 if output
507:                     ## Figure out template extension and output filename
508:                     page_name, tpl_ext = tpl_path.dup, ''
509:                     while page_name =~ /\.\w+$/; page_name = $`; tpl_ext = $& + tpl_ext; end
510:                     next if tpl_ext.empty?
511:                     ## Build the output pages
512:                     build_pages( page_name ) do |vars|
513:                         ## Extension and Path
514:                         vars[:page].add_ext( tpl_ext )
515:                         vars[:template] = path
516:                         vars[:output] = output
517:                         eid = ( vars[:entry] && vars[:entry].id ) || page_name
518:                         if not @output_entry_map[ eid ]
519:                             @output_entry_map[ eid ] = vars
520:                         elsif tpl_ext.split( '.' )[1] == central_ext
521:                             @output_entry_map[ eid ] = vars
522:                         end
523: 
524:                         ## If output by a deeper page, skip
525:                         pub_name, = path_watch[vars[:page].link]
526:                         next if pub_name and !( vars[:page].link.index( page_name ) == 0 and
527:                                               page_name.length > pub_name.length )
528: 
529:                         path_watch[vars[:page].link] = [page_name, vars]
530:                     end
531:                 end
532:             end
533:         end
534:         @output_map = {}
535:         path_watch.each_value do |page_name, vars|
536:             @output_map[page_name] ||= []
537:             @output_map[page_name] << vars
538:         end
539:         @output_map
540:     end

[Source]

     # File lib/hobix/weblog.rb, line 356
356:     def output_path; File.expand_path( @output_path || default_output_path, @path ).untaint; end

Returns an Array of all output plugins in use. (There can be many.)

[Source]

     # File lib/hobix/weblog.rb, line 466
466:     def outputs
467:         @plugins.find_all { |p| p.is_a? BaseOutput }
468:     end

Prints publication information the screen. Override this if you want to suppress output or change the display.

[Source]

     # File lib/hobix/weblog.rb, line 852
852:     def p_publish( obj )
853:         puts "## Page: #{ obj.link }, updated #{ obj.updated }"
854:     end

Returns an Array of all publisher plugins in use. (There can be many.)

[Source]

     # File lib/hobix/weblog.rb, line 472
472:     def publishers
473:         @plugins.find_all { |p| p.is_a? BasePublish }
474:     end

Regenerates the weblog, processing templates in skel_path with the data found in entry_path, storing output in output_path.

The how parameter dictates how this is done, Currently, if how is nil the weblog is completely regen‘d. If it is :update, the weblog is only upgen‘d.

How Updates Work

It‘s very important to know how updates work, especially if you are writing custom skel methods or devious new kinds of templates. When performing an update, this method will skip pages if the following conditions are met:

  1. The Page object for a given output page must have its updated timestamp set.
  2. The output file pointed to by the Page object must already exist.
  3. The updated timestamp must be older than than the modification time of the output file.
  4. The modification time of the input template must be older than than the modification time of the output file.

To ensure that your custom methods and templates are qualifying to be skipped on an upgen, be sure to set the updated timestamp of the Page object to the latest date of the content‘s modification.

[Source]

     # File lib/hobix/weblog.rb, line 578
578:     def regenerate( how = nil )
579:         retouch nil, how
580:     end

Clears the hash used to cache the results of output_map.

[Source]

     # File lib/hobix/weblog.rb, line 488
488:     def reset_output_map; @output_map = nil; end

[Source]

     # File lib/hobix/weblog.rb, line 581
581:     def retouch( only_path = nil, how = nil )
582:         published = {}
583:         published_types = []
584:         output_map.each do |page_name, outputs|
585:             puts "[Building #{ page_name } pages]"
586:             outputs.each do |vars|
587:                 full_out_path = File.join( output_path, vars[:page].link.split( '/' ) )
588:                 ## If retouching, skip pages outside of path
589:                 next if only_path and vars[:page].link.index( "/" + only_path ) != 0
590: 
591:                 ## If updating, skip any that are unchanged
592:                 next if how == :update and 
593:                         File.exists?( full_out_path ) and
594:                         File.mtime( vars[:template] ) < File.mtime( full_out_path ) and
595:                         ( vars[:page].updated.nil? or 
596:                           vars[:page].updated < File.mtime( full_out_path ) )
597: 
598:                 p_publish vars[:page]
599:                 vars.keys.each do |var_name|
600:                     case var_name.to_s
601:                     when /entry$/
602:                         unless vars[:no_load]
603:                             vars[var_name] = load_and_validate_entry( vars[var_name].id )
604:                         end
605:                     when /entries$/
606:                         unless vars[:no_load]
607:                             vars[var_name].collect! do |e|
608:                                 load_and_validate_entry( e.id )
609:                             end
610:                         end
611:                         vars[var_name].extend Hobix::EntryEnum
612:                     end
613:                 end
614: 
615:                 ## Publish the page
616:                 vars = vars.dup
617:                 output = vars.delete( :output )
618:                 template = vars.delete( :template )
619:                 txt = output.load( template, vars )
620:                 ## A plugin perhaps needs to change the output page name
621:                 full_out_path = File.join( output_path, vars[:page].link.split( '/' ) )
622:                 saved_umask = File.umask( 0002 ) rescue nil
623:                 begin
624:                   File.makedirs( File.dirname( full_out_path ) )
625:                   File.open( full_out_path, 'w' ) do |f| 
626:                       f << txt
627:                   end
628:                 ensure
629:                   File.umask( saved_umask ) rescue nil
630:                 end
631:                 published[vars[:page].link] = vars[:page]
632:                 published_types << page_name
633:             end
634:         end
635:         published_types.uniq!
636:         publishers.each do |p|
637:             if p.respond_to? :watch
638:                 if p.watch & published_types != []
639:                     p.publish( published )
640:                 end
641:             else
642:                 p.publish( published )
643:             end
644:         end
645:         reset_output_map
646:     end

Save the weblog configuration to its hobix.yaml (or optionally provide a path where you would like to save.)

[Source]

     # File lib/hobix/weblog.rb, line 413
413:     def save( file = @hobix_yaml )
414:         unless file
415:             raise ArgumentError, "Missing argument: path to save configuration (0 of 1)"
416:         end
417:         File::open( file, 'w' ) do |f|
418:             YAML::dump( self, f )
419:         end
420:         self
421:     end

Receive a Hash pairing all section ids with the options for that section.

[Source]

     # File lib/hobix/weblog.rb, line 767
767:     def sections( opts = nil )
768:         sections = Marshal::load( Marshal::dump( @sections ) )
769:         observes = !sections.values.detect { |s| s['observe'] }
770:         storage.sections.each do |s|
771:             sections[s] ||= {}
772:             sections[s]['observe'] ||= sections[s].has_key?( 'ignore' ) ? !sections[s]['ignore'] : observes
773:             sections[s]['ignore'] ||= !sections[s]['observe']
774:         end
775:         sections
776:     end

Returns an Array of entry paths ignored by general querying. Storage plugins must withhold these entries from queries, unless the :all => true setting is passed to the query.

[Source]

     # File lib/hobix/weblog.rb, line 792
792:     def sections_ignored
793:         sections.collect do |k, v|
794:             k if v['ignore']
795:         end.compact
796:     end

Returns a hash of special sorting cases. Key is the entry path, value is the sorting method. Storage plugins must honor these default sorts.

[Source]

     # File lib/hobix/weblog.rb, line 781
781:     def sections_sorts
782:         @sections.inject( {} ) do |sorts, set|
783:             k, v = set
784:             sorts[k] = v['sort_by'] if v['sort_by']
785:             sorts
786:         end
787:     end

Sets up a weblog. Should only be run once (which Hobix performs automatically upon blog creation).

[Source]

     # File lib/hobix/weblog.rb, line 450
450:     def setup
451:         @plugins.each do |p|
452:             if p.respond_to? :setup
453:                 p.setup
454:             end
455:         end
456:     end

Handler for templates with `daily’ prefix. These templates will receive a list of entries for each day that has at least one entry created in its time period. This handler requests daily pages to be output as `/%Y/%m/%d.ext’.

[Source]

     # File lib/hobix/weblog.rb, line 664
664:     def skel_daily( path_storage, section_path )
665:         entry_range = path_storage.find
666:         first_time, last_time = entry_range.last.created, entry_range.first.created
667:         start = Time.mktime( first_time.year, first_time.month, first_time.day, 0, 0, 0 ) + 1
668:         stop = Time.mktime( last_time.year, last_time.month, last_time.day, 23, 59, 59 )
669:         days = []
670:         one_day = 24 * 60 * 60
671:         until start > stop
672:             day_entries = path_storage.within( start, start + one_day - 1 )
673:             days << [day_entries.last.created, day_entries] unless day_entries.empty?
674:             start += one_day
675:         end
676:         days.extend Hobix::Enumerable
677:         days.each_with_neighbors do |prev, curr, nextd| 
678:             page = Page.new( curr[0].strftime( "%Y/%m/%d" ), section_path )
679:             page.prev = prev[0].strftime( "%Y/%m/%d" ) if prev
680:             page.next = nextd[0].strftime( "%Y/%m/%d" ) if nextd
681:             page.timestamp = curr[0]
682:             page.updated = path_storage.last_updated( curr[1] )
683:             yield :page => page, :entries => curr[1]
684:         end
685:     end

Handler for templates with `entry’ prefix. These templates will receive one entry for each entry in the weblog. The handler requests entry pages to be output as `/shortName.ext’.

[Source]

     # File lib/hobix/weblog.rb, line 730
730:     def skel_entry( path_storage, section_path )
731:         all_entries = [path_storage.find]
732:         all_entries += sections_ignored.collect { |ign| path_storage.find( :all => true, :inpath => ign ) }
733:         all_entries.each do |entry_set|
734:             entry_set.extend Hobix::Enumerable
735:             entry_set.each_with_neighbors do |nexte, entry, prev|
736:                 page = Page.new( entry.id )
737:                 page.prev = prev.id if prev
738:                 page.next = nexte.id if nexte
739:                 page.timestamp = entry.created
740:                 page.updated = path_storage.updated( entry.id )
741:                 yield :page => page, :entry => entry
742:             end
743:         end
744:     end

Handler for templates with `index’ prefix. These templates will receive entries loaded by +Hobix::BaseStorage#lastn+. Only one index page is requested by this handler.

[Source]

     # File lib/hobix/weblog.rb, line 651
651:     def skel_index( path_storage, section_path )
652:         index_entries = path_storage.lastn( @lastn )
653:         page = Page.new( 'index', section_path )
654:         page.prev = index_entries.last.created.strftime( "%Y/%m/index" )
655:         page.timestamp = index_entries.first.created
656:         page.updated = path_storage.last_updated( index_entries )
657:         yield :page => page, :entries => index_entries
658:     end

Handler for templates with `monthly’ prefix. These templates will receive a list of entries for each month that has at least one entry created in its time period. This handler requests monthly pages to be output as `/%Y/%m/index.ext’.

[Source]

     # File lib/hobix/weblog.rb, line 691
691:     def skel_monthly( path_storage, section_path )
692:         months = path_storage.get_months( path_storage.find )
693:         months.extend Hobix::Enumerable
694:         months.each_with_neighbors do |prev, curr, nextm| 
695:             entries = path_storage.within( curr[0], curr[1] )
696:             page = Page.new( curr[0].strftime( "%Y/%m/index" ), section_path )
697:             page.prev = prev[0].strftime( "%Y/%m/index" ) if prev
698:             page.next = nextm[0].strftime( "%Y/%m/index" ) if nextm
699:             page.timestamp = curr[1]
700:             page.updated = path_storage.last_updated( entries )
701:             yield :page => page, :entries => entries
702:         end
703:     end

[Source]

     # File lib/hobix/weblog.rb, line 355
355:     def skel_path; File.expand_path( @skel_path || default_skel_path, @path ).untaint; end

Handler for templates with `section’ prefix. These templates will receive all entries below a given directory. The handler requests will be output as `/section/index.ext’.

[Source]

     # File lib/hobix/weblog.rb, line 749
749:     def skel_section( path_storage, section_path )
750:         section_map = {}
751:         path_storage.all.each do |entry|
752:             dirs = entry.id.split( '/' )
753:             while ( dirs.pop; dirs.first )
754:                 section = dirs.join( '/' )
755:                 section_map[ section ] ||= []
756:                 section_map[ section ] << entry
757:             end
758:         end
759:         section_map.each do |section, entries|
760:             page = Page.new( "/#{ section }/index" )
761:             page.updated = path_storage.last_updated( entries )
762:             yield :page => page, :entries => entries
763:         end
764:     end

Handler for templates with `tags’ prefix. These templates will receive a tag with all entries tagged with it. The handler requests will be output as `/tags/<tag>/index.ext’.

[Source]

     # File lib/hobix/weblog.rb, line 801
801:     def skel_tags( path_storage, section_path ) 
802:       # Get a list of all known tags
803:       tags = path_storage.find( :all => true ).map { |e| e.tags }.flatten.uniq
804:       
805:       tags.each do |tag|
806:         entries = path_storage.find.find_all { |e| e.tags.member? tag }
807:         page = Page.new( File::join( 'tags',tag,'index' ), section_path )
808:         page.updated = path_storage.last_updated( entries ) 
809:         yield :page => page, :entries => entries
810:       end
811:     end

Handler for templates with `yearly’ prefix. These templates will receive a list of entries for each month that has at least one entry created in its time period. This handler requests yearly pages to be output as `/%Y/index.ext’.

[Source]

     # File lib/hobix/weblog.rb, line 709
709:     def skel_yearly( path_storage, section_path )
710:         entry_range = path_storage.find
711:         first_time, last_time = entry_range.last.created, entry_range.first.created
712:         years = (first_time.year..last_time.year).collect do |y|
713:             [ Time.mktime( y, 1, 1 ), Time.mktime( y + 1, 1, 1 ) - 1 ]
714:         end
715:         years.extend Hobix::Enumerable
716:         years.each_with_neighbors do |prev, curr, nextm| 
717:             entries = path_storage.within( curr[0], curr[1] )
718:             page = Page.new( curr[0].strftime( "%Y/index" ), section_path )
719:             page.prev = prev[0].strftime( "%Y/index" ) if prev
720:             page.next = nextm[0].strftime( "%Y/index" ) if nextm
721:             page.timestamp = curr[1]
722:             page.updated = path_storage.last_updated( entries )
723:             yield :page => page, :entries => entries
724:         end
725:     end

After the weblog is initialize, the start method is called with the full system path to the directory containing the configuration.

This method sets up all the paths and loads the plugins.

[Source]

     # File lib/hobix/weblog.rb, line 318
318:     def start( hobix_yaml )
319:         @hobix_yaml = hobix_yaml
320:         @path = File.dirname( hobix_yaml )
321:         @sections ||= {}
322:         if File.exists?( lib_path )
323:             $LOAD_PATH << lib_path
324:         end
325:         @plugins = []
326:         @requires.each do |req|
327:             opts = nil
328:             unless req.respond_to? :to_str
329:                 req, opts = req.to_a.first
330:             end
331:             plugin_conf = File.join( @path, req.gsub( /\W+/, '.' ) )
332:             if File.exists? plugin_conf
333:                 puts "*** Loading #{ plugin_conf }"
334:                 plugin_conf = YAML::load_file plugin_conf
335:                 if opts
336:                     opts.merge! plugin_conf
337:                 else
338:                     opts = plugin_conf
339:                 end
340:             end
341:             @plugins += Hobix::BasePlugin::start( req, opts, self )
342:         end
343:     end

Returns the storage plugin currently in use. (There can be only one per weblog.)

[Source]

     # File lib/hobix/weblog.rb, line 460
460:     def storage
461:         @plugins.detect { |p| p.is_a? BaseStorage }
462:     end

Returns the YAML type information, which expands to tag:hobix.com,2004:weblog.

[Source]

     # File lib/hobix/weblog.rb, line 860
860:     def to_yaml_type
861:         "!hobix.com,2004/weblog"
862:     end

[Validate]