Class: Regenerate::WebPage
- Inherits:
-
Object
- Object
- Regenerate::WebPage
- Includes:
- Utils
- Defined in:
- lib/regenerate/web-page.rb
Overview
A web page which is read from a source file and regenerated to an output file (which may be the same as the source file)
Instance Attribute Summary collapse
-
#fileName ⇒ Object
readonly
The absolute name of the source file.
Instance Method Summary collapse
-
#addRubyComponent(rubyComponent) ⇒ Object
Add a Ruby page component to this web page (so that later on it will be executed).
-
#checkAndEnsureOutputFileUnchanged(outFile, oldFile) ⇒ Object
Check that a newly created output file has the same contents as another (backup) file containing the old contents If it has changed, actually reset the new file to have “.new” at the end of its name, and rename the backup file to be the output file (in effect reverting the newly written output).
-
#classFromString(str) ⇒ Object
Get a Ruby class from a normal Ruby class name formatted using “::” separators.
-
#diffReport(newString, oldString) ⇒ Object
Report the difference between two strings (that should be the same).
- #display ⇒ Object
-
#executeRubyComponents ⇒ Object
Execute the Ruby components which consist of Ruby code to be evaluated in the context of the page object.
-
#finishAtEndOfSourceFile ⇒ Object
Finish the current page component after we are at the end of the source file Anything other than static HTML should be explicitly finished, and if it isn’t finished, raise an error.
-
#getPageObjectInstanceVar(varName) ⇒ Object
Get the value of an instance variable of the page object.
-
#initialize(fileName) ⇒ WebPage
constructor
A new instance of WebPage.
-
#initializePageObject(pageObject) ⇒ Object
initialise the “page object”, which is the object that “owns” the defined instance variables, and the object in whose context the Ruby components are evaluated Three special instance variable values are set - @fileName, @baseDir, @baseFileName, so that they can be accessed, if necessary, by Ruby code in the Ruby code components.
-
#processCommandLine(parsedCommandLine, lineNumber) ⇒ Object
Process a line of source text that has been identified as a Regenerate start and/or end of comment line.
-
#processTextLine(line, lineNumber) ⇒ Object
Process a text line, by adding to the current page component, or if there is none, starting a new StaticHtml component.
-
#readFileLines ⇒ Object
Read in and parse lines from source file.
-
#regenerate ⇒ Object
Regenerate the source file (in-place).
-
#regenerateToOutputFile(outFile, checkNoChanges = false) ⇒ Object
Regenerate from the source file into the output file.
-
#setPageObject(className) ⇒ Object
Set the page object to be an object with the specified class name (this can only be done once).
-
#setPageObjectInstanceVar(varName, value) ⇒ Object
Set the value of an instance variable of the page object.
-
#startNewComponent(component, startComment = nil) ⇒ Object
Add a newly started page component to this page Also process the start comment, unless it was a static HTML component, in which case there is not start comment.
-
#writeRegeneratedFile(outFile, checkNoChanges) ⇒ Object
Write the output of the page components to the output file (optionally checking that there are no differences between the new output and the existing output..
Methods included from Utils
#ensureDirectoryExists, #makeBackupFile
Constructor Details
#initialize(fileName) ⇒ WebPage
Returns a new instance of WebPage.
334 335 336 337 338 339 340 341 342 343 |
# File 'lib/regenerate/web-page.rb', line 334 def initialize(fileName) @fileName = fileName @components = [] @currentComponent = nil @componentInstanceVariables = {} initializePageObject(PageObject.new) # default, can be overridden by SetPageObjectClass @pageObjectClassNameSpecified = nil # remember name if we have specified a page object class to override the default @rubyComponents = [] readFileLines end |
Instance Attribute Details
#fileName ⇒ Object (readonly)
The absolute name of the source file
332 333 334 |
# File 'lib/regenerate/web-page.rb', line 332 def fileName @fileName end |
Instance Method Details
#addRubyComponent(rubyComponent) ⇒ Object
Add a Ruby page component to this web page (so that later on it will be executed)
376 377 378 |
# File 'lib/regenerate/web-page.rb', line 376 def addRubyComponent(rubyComponent) @rubyComponents << rubyComponent end |
#checkAndEnsureOutputFileUnchanged(outFile, oldFile) ⇒ Object
Check that a newly created output file has the same contents as another (backup) file containing the old contents If it has changed, actually reset the new file to have “.new” at the end of its name, and rename the backup file to be the output file (in effect reverting the newly written output).
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
# File 'lib/regenerate/web-page.rb', line 491 def checkAndEnsureOutputFileUnchanged(outFile, oldFile) if File.exists? oldFile oldFileContents = File.read(oldFile) newFileContents = File.read(outFile) if oldFileContents != newFileContents newFileName = outFile + ".new" File.rename(outFile, newFileName) File.rename(oldFile, outFile) raise UnexpectedChangeError.new("New file #{newFileName} is different from old file #{outFile}: #{diffReport(newFileContents,oldFileContents)}") end else raise UnexpectedChangeError.new("Can't check #{outFile} against backup #{oldFile} " + "because backup file doesn't exist") end end |
#classFromString(str) ⇒ Object
Get a Ruby class from a normal Ruby class name formatted using “::” separators
404 405 406 407 408 409 |
# File 'lib/regenerate/web-page.rb', line 404 def classFromString(str) # Start with Object, and look up the module one path component at a time str.split('::').inject(Object) do |mod, class_name| mod.const_get(class_name) end end |
#diffReport(newString, oldString) ⇒ Object
Report the difference between two strings (that should be the same)
475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/regenerate/web-page.rb', line 475 def diffReport(newString, oldString) i = 0 minLength = [newString.length, oldString.length].min while i<minLength and newString[i] == oldString[i] do i += 1 end diffPos = i newStringEndPos = [diffPos+20,newString.length].min oldStringEndPos = [diffPos+20, newString.length].min startPos = [0, diffPos-10].max "Different from position #{diffPos}: \n #{newString[startPos...newStringEndPos].inspect}\n !=\n #{oldString[startPos...newStringEndPos].inspect}" end |
#display ⇒ Object
578 579 580 581 582 583 584 585 |
# File 'lib/regenerate/web-page.rb', line 578 def display puts "==========================================================================" puts "Output of #{@fileName}" for component in @components do puts "--------------------------------------" puts(component.output) end end |
#executeRubyComponents ⇒ Object
Execute the Ruby components which consist of Ruby code to be evaluated in the context of the page object
563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
# File 'lib/regenerate/web-page.rb', line 563 def executeRubyComponents fileDir = File.dirname(@fileName) #puts "Executing ruby components in directory #{fileDir} ..." Dir.chdir(fileDir) do for rubyComponent in @rubyComponents rubyCode = rubyComponent.text #puts ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" #puts "Executing ruby (line #{rubyComponent.lineNumber}) #{rubyCode.inspect} ..." @pageObject.instance_eval(rubyCode, @fileName, rubyComponent.lineNumber) #puts "Finished executing ruby at line #{rubyComponent.lineNumber}" end end #puts "Finished executing ruby components." end |
#finishAtEndOfSourceFile ⇒ Object
Finish the current page component after we are at the end of the source file Anything other than static HTML should be explicitly finished, and if it isn’t finished, raise an error.
463 464 465 466 467 468 469 470 471 472 |
# File 'lib/regenerate/web-page.rb', line 463 def finishAtEndOfSourceFile if @currentComponent if @currentComponent.is_a? StaticHtml @currentComponent.finishText @currentComponent = nil else raise ParseException.new("Unfinished last component at end of file") end end end |
#getPageObjectInstanceVar(varName) ⇒ Object
Get the value of an instance variable of the page object
365 366 367 |
# File 'lib/regenerate/web-page.rb', line 365 def getPageObjectInstanceVar(varName) @pageObject.instance_variable_get(varName) end |
#initializePageObject(pageObject) ⇒ Object
initialise the “page object”, which is the object that “owns” the defined instance variables, and the object in whose context the Ruby components are evaluated Three special instance variable values are set - @fileName, @baseDir, @baseFileName, so that they can be accessed, if necessary, by Ruby code in the Ruby code components. (if this is called a second time, it overrides whatever was set the first time) Notes on special instance variables -
@fileName and @baseDir are the absolute paths of the source file and it's containing directory.
They would be used in Ruby code that looked for other files with names or locations relative to these two.
They would generally not be expected to appear in the output content.
@baseFileName is the name of the file without any directory path components. In some cases it might be
used within output content.
356 357 358 359 360 361 362 |
# File 'lib/regenerate/web-page.rb', line 356 def initializePageObject(pageObject) @pageObject = pageObject setPageObjectInstanceVar("@fileName", @fileName) setPageObjectInstanceVar("@baseDir", File.dirname(@fileName)) setPageObjectInstanceVar("@baseFileName", File.basename(@fileName)) @initialInstanceVariables = Set.new(@pageObject.instance_variables) end |
#processCommandLine(parsedCommandLine, lineNumber) ⇒ Object
Process a line of source text that has been identified as a Regenerate start and/or end of comment line
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
# File 'lib/regenerate/web-page.rb', line 422 def processCommandLine(parsedCommandLine, lineNumber) #puts "command: #{parsedCommandLine}" if @currentComponent && (@currentComponent.is_a? StaticHtml) # finish any current static HTML component @currentComponent.finishText @currentComponent = nil end if @currentComponent # we are in a page component other than a static HTML component if parsedCommandLine.sectionStart # we have already started, so we cannot start again raise ParseException.new("Unexpected section start #{parsedCommandLine} inside component") end @currentComponent.processEndComment(parsedCommandLine) # so, command must be a command to end the page component @currentComponent = nil else # not in any page component, so we need to start a new one if !parsedCommandLine.sectionStart # if it's an end command, error, because there is nothing to end raise ParseException.new("Unexpected section end #{parsedCommandLine}, outside of component") end if parsedCommandLine.isInstanceVar # it's a page component that defines an instance variable value if parsedCommandLine.hasCommentEnd # the value will be an HTML value startNewComponent(HtmlVariable.new, parsedCommandLine) else # the value will be an HTML-commented value startNewComponent(CommentVariable.new, parsedCommandLine) end else # not an instance var, so it must be a special command if parsedCommandLine.name == "ruby" # Ruby page component containing Ruby that will be executed in the # context of the page object startNewComponent(RubyCode.new(lineNumber+1), parsedCommandLine) elsif parsedCommandLine.name == "class" # Specify Ruby class for the page object startNewComponent(SetPageObjectClass.new(parsedCommandLine.value), parsedCommandLine) else # not a known special command raise ParseException.new("Unknown section type #{parsedCommandLine.name.inspect}") end end if @currentComponent.finished # Did the processing cause the current page component to be finished? @currentComponent = nil # clear the current component end end end |
#processTextLine(line, lineNumber) ⇒ Object
Process a text line, by adding to the current page component, or if there is none, starting a new StaticHtml component.
395 396 397 398 399 400 401 |
# File 'lib/regenerate/web-page.rb', line 395 def processTextLine(line, lineNumber) #puts "text: #{line}" if @currentComponent == nil startNewComponent(StaticHtml.new) end @currentComponent.addLine(line) end |
#readFileLines ⇒ Object
Read in and parse lines from source file
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
# File 'lib/regenerate/web-page.rb', line 523 def readFileLines puts "Reading source file #{@fileName} ..." lineNumber = 0 File.open(@fileName).each_line do |line| line.chomp! lineNumber += 1 # track line numbers for when Ruby code needs to be executed (i.e. to populate stack traces) #puts "line #{lineNumber}: #{line}" commentLineMatch = COMMENT_LINE_REGEX.match(line) if commentLineMatch # it matches the Regenerate command line regex (but might not actually be a command ...) parsedCommandLine = ParsedRegenerateCommentLine.new(line, commentLineMatch) #puts "parsedCommandLine = #{parsedCommandLine}" if parsedCommandLine.isRegenerateCommentLine # if it is a Regenerate command line parsedCommandLine.checkIsValid # check it is valid, and then, processCommandLine(parsedCommandLine, lineNumber) # process the command line else processTextLine(line, lineNumber) # process a text line which is not a Regenerate command line end else processTextLine(line, lineNumber) # process a text line which is not a Regenerate command line end end # After processing all source lines, the only unfinished page component permitted is a static HTML component. finishAtEndOfSourceFile #puts "Finished reading #{@fileName}." end |
#regenerate ⇒ Object
Regenerate the source file (in-place)
550 551 552 553 554 |
# File 'lib/regenerate/web-page.rb', line 550 def regenerate executeRubyComponents writeRegeneratedFile(@fileName) #display end |
#regenerateToOutputFile(outFile, checkNoChanges = false) ⇒ Object
Regenerate from the source file into the output file
557 558 559 560 |
# File 'lib/regenerate/web-page.rb', line 557 def regenerateToOutputFile(outFile, checkNoChanges = false) executeRubyComponents writeRegeneratedFile(outFile, checkNoChanges) end |
#setPageObject(className) ⇒ Object
Set the page object to be an object with the specified class name (this can only be done once)
412 413 414 415 416 417 418 419 |
# File 'lib/regenerate/web-page.rb', line 412 def setPageObject(className) if @pageObjectClassNameSpecified raise ParseException("Page object class name specified more than once") end @pageObjectClassNameSpecified = className pageObjectClass = classFromString(className) initializePageObject(pageObjectClass.new) end |
#setPageObjectInstanceVar(varName, value) ⇒ Object
Set the value of an instance variable of the page object
370 371 372 373 |
# File 'lib/regenerate/web-page.rb', line 370 def setPageObjectInstanceVar(varName, value) #puts " setPageObjectInstanceVar, #{varName} = #{value.inspect}" @pageObject.instance_variable_set(varName, value) end |
#startNewComponent(component, startComment = nil) ⇒ Object
Add a newly started page component to this page Also process the start comment, unless it was a static HTML component, in which case there is not start comment.
383 384 385 386 387 388 389 390 391 |
# File 'lib/regenerate/web-page.rb', line 383 def startNewComponent(component, startComment = nil) component.parentPage = self @currentComponent = component #puts "startNewComponent, @currentComponent = #{@currentComponent.inspect}" @components << component if startComment component.processStartComment(startComment) end end |
#writeRegeneratedFile(outFile, checkNoChanges) ⇒ Object
Write the output of the page components to the output file (optionally checking that there are no differences between the new output and the existing output.
509 510 511 512 513 514 515 516 517 518 519 520 |
# File 'lib/regenerate/web-page.rb', line 509 def writeRegeneratedFile(outFile, checkNoChanges) backupFileName = makeBackupFile(outFile) File.open(outFile, "w") do |f| for component in @components do f.write(component.output) end end puts " wrote regenerated page to #{outFile}" if checkNoChanges checkAndEnsureOutputFileUnchanged(outFile, backupFileName) end end |