Commits

Michael Granger  committed 8b00f8a

Add specs for generation

  • Participants
  • Parent commits 12f9ad3

Comments (0)

Files changed (4)

 # .gems generated gem export file. Note that any env variable settings will be missing. Append these after using a ';' field separator
 hoe-deveiate -v0.2.0
 inversion -v0.12.0
+loggability -v0.6.0
 yajl-ruby -v1.1.0
 rdoc -v4.0.0
 less -v2.3.1
 
 	self.developer 'Michael Granger', 'ged@FaerieMUD.org'
 
-	self.dependency 'inversion', '~> 0.12'
-	self.dependency 'yajl-ruby', '~> 1.1'
-	self.dependency 'rdoc',      '~> 4.0'
+	self.dependency 'inversion',    '~> 0.12'
+	self.dependency 'loggability',  '~> 0.6'
+	self.dependency 'yajl-ruby',    '~> 1.1'
+	self.dependency 'rdoc',         '~> 4.0'
 
 	self.dependency 'hoe-deveiate', '~> 0.2', :developer
 	self.dependency 'uglifier',     '~> 1.2', :developer

File lib/rdoc/generator/fivefish.rb

 require 'uri'
 require 'yajl'
 require 'inversion'
+require 'loggability'
 require 'fileutils'
 require 'pathname'
 require 'rdoc/rdoc'
 
 # The Fivefish generator class.
 class RDoc::Generator::Fivefish
+	extend Loggability
     include FileUtils
 
+
+	# Loggability API -- set up a Logger for Fivefish
+	log_as :fivefish
+
+
 	# The data directory in the project if that exists, otherwise the gem datadir
 	DATADIR = if ENV['FIVEFISH_DATADIR']
 			Pathname( ENV['FIVEFISH_DATADIR'] ).expand_path( Pathname.pwd )
 		@options    = options
 		$DEBUG_RDOC = $VERBOSE || $DEBUG
 
+		self.log.debug "Setting up generator for %p with options: %p" % [ @store, @options ]
+
 		extend( FileUtils::Verbose ) if $DEBUG_RDOC
 		extend( FileUtils::DryRun ) if options.dry_run
 
 
 	### Build the initial indices and output objects based on the files in the generator's store.
 	def generate
-		@files   = self.store.all_files.sort
-		@classes = self.store.all_classes_and_modules.sort
-		@methods = @classes.map {|m| m.method_list }.flatten.sort
-		@modsort = self.get_sorted_module_list( @classes )
+		self.populate_data_objects
 
 		self.generate_index_page
 		self.generate_class_files
 	end
 
 
+	### Populate the data objects necessary to generate documentation from the generator's
+	### #store.
+	def populate_data_objects
+		@files   = self.store.all_files.sort
+		@classes = self.store.all_classes_and_modules.sort
+		@methods = @classes.map {|m| m.method_list }.flatten.sort
+		@modsort = self.get_sorted_module_list( @classes )
+	end
+
+
+	### Generate an index page which lists all the classes which are documented.
+	def generate_index_page
+		self.log.debug "Generating index page"
+		layout = self.load_layout_template
+		template = self.load_template( 'index.tmpl' )
+		out_file = self.output_dir + 'index.html'
+		out_file.dirname.mkpath
+
+		mainpage = nil
+		if mpname = self.options.main_page
+			mainpage = @files.find {|f| f.full_name == mpname }
+		else
+			mainpage = @files.find {|f| f.full_name =~ /\breadme\b/i }
+		end
+		self.log.debug "  using main_page (%s)" % [ mainpage ]
+
+		if mainpage
+			template.mainpage = mainpage
+			template.synopsis = self.extract_synopsis( mainpage )
+		end
+
+		layout.rel_prefix = self.output_dir.relative_path_from( out_file.dirname )
+		layout.contents = template
+		layout.pageclass = 'index-page'
+
+		out_file.open( 'w', 0644 ) {|io| io.print(layout.render) }
+	end
+
+
+	### Generate a documentation file for each class and module
+	def generate_class_files
+		layout = self.load_layout_template
+		template = self.load_template( 'class.tmpl' )
+
+		self.log.debug "Generating class documentation in #{self.output_dir}"
+
+		@classes.each do |klass|
+			self.log.debug "  working on %s (%s)" % [klass.full_name, klass.path]
+
+			out_file = self.output_dir + klass.path
+			out_file.dirname.mkpath
+
+			template.klass = klass
+
+			layout.contents = template
+			layout.rel_prefix = self.output_dir.relative_path_from( out_file.dirname )
+			layout.pageclass = 'class-page'
+
+			out_file.open( 'w', 0644 ) {|io| io.print(layout.render) }
+		end
+	end
+
+
+	### Generate a documentation file for each file
+	def generate_file_files
+		layout = self.load_layout_template
+		template = self.load_template( 'file.tmpl' )
+
+		self.log.debug "Generating file documentation in #{self.output_dir}"
+
+		@files.select {|f| f.text? }.each do |file|
+			out_file = self.output_dir + file.path
+			out_file.dirname.mkpath
+
+			self.log.debug "  working on %s (%s)" % [file.full_name, out_file]
+
+			template.file = file
+
+			# If the page itself has an H1, use it for the header, otherwise make one
+			# out of the name of the file
+			if md = file.description.match( %r{<h1.*?>.*?</h1>}i )
+				template.header = md[ 0 ]
+				template.description = file.description[ md.offset(0)[1] + 1 .. -1 ]
+			else
+				template.header = File.basename( file.full_name, File.extname(file.full_name) )
+				template.description = file.description
+			end
+
+			layout.contents = template
+			layout.rel_prefix = self.output_dir.relative_path_from(out_file.dirname)
+			layout.pageclass = 'file-page'
+
+			out_file.open( 'w', 0644 ) {|io| io.print(layout.render) }
+		end
+	end
+
+
+	### Generate a JSON search index for the quicksearch blank.
+	def generate_search_index
+		out_file = self.output_dir + 'js/searchindex.js'
+
+		self.log.debug "Generating search index (%s)." % [ out_file ]
+		index = []
+
+	    objs = self.get_indexable_objects
+		objs.each do |codeobj|
+			self.log.debug "  #{codeobj.name}..."
+			record = codeobj.search_record
+			index << {
+				name:    record[2],
+				link:    record[4],
+				snippet: record[6],
+				type:    codeobj.class.name.downcase.sub( /.*::/, '' )
+			}
+		end
+
+		self.log.debug "  dumping JSON..."
+		out_file.dirname.mkpath
+		ofh = out_file.open( 'w:utf-8', 0644 )
+
+		json = Yajl.dump( index, pretty: true, indent: "\t" )
+
+		ofh.puts( 'var SearchIndex = ', json, ';' )
+	end
+
+
+	### Copies static files from the static_path into the output directory
+	def copy_static_assets
+		asset_paths = self.find_static_assets
+
+		self.log.debug "Copying assets from paths:", *asset_paths
+
+		asset_paths.each do |path|
+
+			# For plain files, just install them
+			if path.file?
+				self.log.debug "  plain file; installing as-is"
+				install( path, self.output_dir, :mode => 0644 )
+
+			# Glob all the files out of subdirectories and install them
+			elsif path.directory?
+				self.log.debug "  directory %p; copying contents" % [ path ]
+
+				Pathname.glob( path + '{css,fonts,img,js}/**/*'.to_s ).each do |asset|
+					next if asset.directory? || asset.basename.to_s.start_with?( '.' )
+
+					dst = asset.relative_path_from( path )
+					dst.dirname.mkpath
+
+					self.log.debug "    %p -> %p" % [ asset, dst ]
+					install asset, dst, :mode => 0644
+				end
+			end
+		end
+	end
+
+
 	#########
 	protected
 	#########
 	### list of static assets that should be copied into the output directory.
 	def find_static_assets
 		paths = self.options.static_path || []
-		self.debug_msg "Finding asset paths. Static paths: %p" % [ paths ]
+		self.log.debug "Finding asset paths. Static paths: %p" % [ paths ]
 
 		# Add each subdirectory of the template dir
-		self.debug_msg "  adding directories under %s" % [ self.template_dir ]
+		self.log.debug "  adding directories under %s" % [ self.template_dir ]
 		paths << self.template_dir
-		self.debug_msg "  paths are now: %p" % [ paths ]
+		self.log.debug "  paths are now: %p" % [ paths ]
 
 		return paths.flatten.compact.uniq
 	end
 
 
-	### Copies static files from the static_path into the output directory
-	def copy_static_assets
-		asset_paths = self.find_static_assets
-
-		self.debug_msg "Copying assets from paths:", *asset_paths
-
-		asset_paths.each do |path|
-
-			# For plain files, just install them
-			if path.file?
-				self.debug_msg "  plain file; installing as-is"
-				install( path, self.output_dir, :mode => 0644 )
-
-			# Glob all the files out of subdirectories and install them
-			elsif path.directory?
-				self.debug_msg "  directory %p; copying contents" % [ path ]
-
-				Pathname.glob( path + '{css,fonts,img,js}/**/*'.to_s ).each do |asset|
-					next if asset.directory? || asset.basename.to_s.start_with?( '.' )
-
-					dst = asset.relative_path_from( path )
-					dst.dirname.mkpath
-
-					self.debug_msg "    %p -> %p" % [ asset, dst ]
-					install asset, dst, :mode => 0644
-				end
-			end
-		end
-	end
-
-
 	### Return a list of the documented modules sorted by salience first, then
 	### by name.
 	def get_sorted_module_list(classes)
 	end
 
 
-	### Generate an index page which lists all the classes which are documented.
-	def generate_index_page
-		self.debug_msg "Generating index page"
-		layout = self.load_layout_template
-		template = self.load_template( 'index.tmpl' )
-		out_file = self.output_dir + 'index.html'
-		out_file.dirname.mkpath
-
-		mainpage = nil
-		if mpname = self.options.main_page
-			mainpage = @files.find {|f| f.full_name == mpname }
-		else
-			mainpage = @files.find {|f| f.full_name =~ /\breadme\b/i }
-		end
-		self.debug_msg "  using main_page (%s)" % [ mainpage ]
-
-		if mainpage
-			template.mainpage = mainpage
-			template.synopsis = self.extract_synopsis( mainpage )
-		end
-
-		layout.rel_prefix = self.output_dir.relative_path_from( out_file.dirname )
-		layout.contents = template
-		layout.pageclass = 'index-page'
-
-		out_file.open( 'w', 0644 ) {|io| io.print(layout.render) }
-	end
-
-
-	### Generate a documentation file for each class and module
-	def generate_class_files
-		layout = self.load_layout_template
-		template = self.load_template( 'class.tmpl' )
-
-		debug_msg "Generating class documentation in #{self.output_dir}"
-
-		@classes.each do |klass|
-			debug_msg "  working on %s (%s)" % [klass.full_name, klass.path]
-
-			out_file = self.output_dir + klass.path
-			out_file.dirname.mkpath
-
-			template.klass = klass
-
-			layout.contents = template
-			layout.rel_prefix = self.output_dir.relative_path_from( out_file.dirname )
-			layout.pageclass = 'class-page'
-
-			out_file.open( 'w', 0644 ) {|io| io.print(layout.render) }
-		end
-	end
-
-
-	### Generate a documentation file for each file
-	def generate_file_files
-		layout = self.load_layout_template
-		template = self.load_template( 'file.tmpl' )
-
-		debug_msg "Generating file documentation in #{self.output_dir}"
-
-		@files.select {|f| f.text? }.each do |file|
-			out_file = self.output_dir + file.path
-			out_file.dirname.mkpath
-
-			debug_msg "  working on %s (%s)" % [file.full_name, out_file]
-
-			template.file = file
-
-			# If the page itself has an H1, use it for the header, otherwise make one
-			# out of the name of the file
-			if md = file.description.match( %r{<h1.*?>.*?</h1>}i )
-				template.header = md[ 0 ]
-				template.description = file.description[ md.offset(0)[1] + 1 .. -1 ]
-			else
-				template.header = File.basename( file.full_name, File.extname(file.full_name) )
-				template.description = file.description
-			end
-
-			layout.contents = template
-			layout.rel_prefix = self.output_dir.relative_path_from(out_file.dirname)
-			layout.pageclass = 'file-page'
-
-			out_file.open( 'w', 0644 ) {|io| io.print(layout.render) }
-		end
-	end
-
-
-	### Generate a JSON search index for the quicksearch blank.
-	def generate_search_index
-		out_file = self.output_dir + 'js/searchindex.js'
-
-		self.debug_msg "Generating search index (%s)." % [ out_file ]
-		index = []
-
-	    objs = self.get_indexable_objects
-		objs.each do |codeobj|
-			self.debug_msg "  #{codeobj.name}..."
-			record = codeobj.search_record
-			index << {
-				name:    record[2],
-				link:    record[4],
-				snippet: record[6],
-				type:    codeobj.class.name.downcase.sub( /.*::/, '' )
-			}
-		end
-
-		self.debug_msg "  dumping JSON..."
-		out_file.dirname.mkpath
-		ofh = out_file.open( 'w:utf-8', 0644 )
-
-		json = Yajl.dump( index, pretty: true, indent: "\t" )
-
-		ofh.puts( 'var SearchIndex = ', json, ';' )
-	end
-
-
 	### Return a list of CodeObjects that belong in the index.
 	def get_indexable_objects
 		objs = []

File spec/rdoc/generator/fivefish_spec.rb

 	#   https://github.com/rdoc/rdoc/blob/master/LICENSE.rdoc
 
 	before( :all ) do
+		setup_logging()
+
 		@libdir = Pathname.pwd + 'lib'
 		@datadir = RDoc::Generator::Fivefish::DATADIR
 		@tmpdir = Pathname( Dir.tmpdir ) + "test_rdoc_generator_fivefish_#{$$}"
+		@opdir     = @tmpdir + 'docs'
+		@storefile = @tmpdir + '.rdoc_store'
 
 		@options = RDoc::Options.new
 		@options.option_parser = OptionParser.new
 
-		@options.op_dir = @tmpdir.to_s
+		@options.op_dir = @opdir.to_s
 		@options.generator = described_class
 		@options.template_dir = @datadir.to_s
-		@options.op_dir = @tmpdir.to_s
+
+		@tmpdir.mkpath
+		@store = RDoc::Store.new( @storefile.to_s )
+		@store.load_cache
+
+		$stderr.puts "Tmpdir is: %s" % [@tmpdir] if ENV['FIVEFISH_DEVELMODE']
 	end
 
 	before( :each ) do
-		@store          = RDoc::Store.new
-		@generator      = described_class.new( @store, @options )
+		@generator          = described_class.new( @store, @options )
 
-		@rdoc           = RDoc::RDoc.new
-		@rdoc.options   = @options
-		@rdoc.store     = @store
-		@rdoc.generator = @generator
+		@rdoc               = RDoc::RDoc.new
+		@rdoc.options       = @options
+		@rdoc.store         = @store
+		@rdoc.generator     = @generator
 
-		@top_level      = add_code_objects( @store )
+		@top_level, @readme = add_code_objects( @store )
 	end
 
 	around( :each ) do |example|
-		@tmpdir.mkpath
-		Dir.chdir( @tmpdir ) { example.run }
-		@tmpdir.rmtree
+		@opdir.mkpath
+		Dir.chdir( @opdir ) { example.run }
+		@opdir.rmtree unless ENV['FIVEFISH_DEVELMODE']
 	end
 
 
 	end
 
 
-	describe "all pages" do
-	
-		it "get the top-level code objects via the layout template" do
-			pending "figuring out how to test this" do
-				layout_template = stub( "layout template" )
-				Inversion::Template.stub( :load ).with( 'layout.tmpl', encoding: 'utf-8' ).
-					and_return( layout_template )
+	describe "generation" do
 
-				layout_template.should_receive( :files= ).
-					with( @store.all_files.sort )
-				layout_template.should_receive( :classes= ).
-					with( @store.all_classes_and_modules.sort )
-				layout_template.should_receive( :methods= ).
-					with( @store.all_classes_and_modules.map(&:method_list).flatten.sort )
-				layout_template.should_receive( :modsort= ).
-					with( 'modsort' )
-			
-				@generator.generate
+		before( :each ) do
+			@generator.populate_data_objects
+		end
+
+
+		it "combines an index template with the layout template to make the index page" do
+			layout_template = get_fixtured_layout_template_mock()
+
+			index_template = mock( "index template" )
+			Inversion::Template.stub( :load ).with( 'index.tmpl', encoding: 'utf-8' ).
+				and_return( index_template )
+
+			index_template.should_receive( :dup ).and_return( index_template )
+			index_template.should_receive( :mainpage= ).with( @readme )
+			index_template.should_receive( :synopsis= ).
+				with( %{<h1 id="label-Testing+README">Testing <a href="README_md} +
+				      %{.html">README</a><span><a href="#label-Testing+README">} +
+				      %{&para;</a> <a href="#documentation">&uarr;</a></span></h1>} +
+				      %{<p>This is a readme for testing.</p>} )
+
+			layout_template.should_receive( :contents= ).with( index_template )
+			layout_template.should_receive( :pageclass= ).with( 'index-page' )
+			layout_template.should_receive( :rel_prefix= ).with( Pathname('.') )
+
+			layout_template.should_receive( :render ).and_return( 'Index page!' )
+
+			@generator.generate_index_page
+		end
+
+
+		it "combines a class template with the layout template to make class pages" do
+			classes = @store.all_classes_and_modules
+
+			layout_template = get_fixtured_layout_template_mock()
+
+			class_template = mock( "class template" )
+			Inversion::Template.stub( :load ).with( 'class.tmpl', encoding: 'utf-8' ).
+				and_return( class_template )
+			class_template.should_receive( :dup ).and_return( class_template )
+
+			classes.each do |klass|
+				class_template.should_receive( :klass= ).with( klass )
 			end
+
+			layout_template.should_receive( :contents= ).with( class_template ).
+				exactly( classes.length ).times
+			layout_template.should_receive( :pageclass= ).with( 'class-page' ).
+				exactly( classes.length ).times
+			layout_template.should_receive( :rel_prefix= ).with( Pathname('.') ).
+				exactly( classes.length ).times
+
+			layout_template.should_receive( :render ).
+				and_return( *classes.map {|k| "#{k.name} class page!"} )
+
+			@generator.generate_class_files
 		end
-		
+
+
+		it "combines a file template with the layout template to make file pages" do
+			files = @store.all_files
+
+			layout_template = get_fixtured_layout_template_mock()
+
+			file_template = mock( "file template" )
+			Inversion::Template.stub( :load ).with( 'file.tmpl', encoding: 'utf-8' ).
+				and_return( file_template )
+			file_template.should_receive( :dup ).and_return( file_template )
+			file_template.should_receive( :header= ).
+				with( %{<h1 id="label-Testing+README">Testing <a href="README_md} +
+				      %{.html">README</a><span><a href="#label-Testing+README">} +
+				      %{&para;</a> <a href="#documentation">&uarr;</a></span></h1>} )
+			file_template.should_receive( :description= ).
+				with( %{\n<p>This is a readme for testing.</p>\n\n<p>It has some more} +
+				      %{ stuff</p>\n\n<p>And even more stuff.</p>\n} )
+
+			file_template.should_receive( :file= ).with( @readme )
+
+			layout_template.should_receive( :contents= ).with( file_template ).once
+			layout_template.should_receive( :pageclass= ).with( 'file-page' )
+			layout_template.should_receive( :rel_prefix= ).with( Pathname('.') )
+			layout_template.should_receive( :render ).and_return( "README file page!" )
+
+			@generator.generate_file_files
+		end
+
 	end
 
 
 	# Helpers
 	#
 
+	def any_method( name, comment=nil )
+		return RDoc::AnyMethod.new( comment, name )
+	end
+
+
 	def add_code_objects( store )
 		top_level = store.add_file( 'file.rb' )
 		top_level.parser = RDoc::Parser::Ruby
 		ignored = top_level.add_class( RDoc::NormalClass, 'Ignored' )
 		ignored.ignore
 
+		readme = store.add_file( 'README.md' )
+		readme.parser = RDoc::Parser::Markdown
+		readme.comment = "= Testing README\n\nThis is a readme for testing.\n\n" +
+			"It has some more stuff\n\nAnd even more stuff.\n\n"
+
 		store.complete :private
-		
-		return top_level
+
+		return top_level, readme
 	end
 
 
+	def get_fixtured_layout_template_mock
+		layout_template = mock( "layout template" )
+		Inversion::Template.stub( :load ).with( 'layout.tmpl', encoding: 'utf-8' ).
+			and_return( layout_template )
 
+		# Work around caching
+		layout_template.should_receive( :dup ).and_return( layout_template )
+
+		layout_template.should_receive( :files= ).with( [@readme, @top_level] )
+		layout_template.should_receive( :classes= ).
+			with( @store.all_classes_and_modules.sort )
+		layout_template.should_receive( :methods= ).
+			with( @store.all_classes_and_modules.flat_map(&:method_list).sort )
+		layout_template.should_receive( :modsort= ).with do |sorted_mods|
+			sorted_mods.should include( @store.find_class_named('Klass') )
+		end
+		layout_template.should_receive( :rdoc_options= ).with( @options )
+		layout_template.should_receive( :rdoc_version= ).with( RDoc::VERSION )
+		layout_template.should_receive( :fivefish_version= ).with( Fivefish.version_string )
+
+		return layout_template
+	end
 
 end