tilt.rb 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. module Tilt
  2. VERSION = '1.3.3'
  3. @preferred_mappings = Hash.new
  4. @template_mappings = Hash.new { |h, k| h[k] = [] }
  5. # Hash of template path pattern => template implementation class mappings.
  6. def self.mappings
  7. @template_mappings
  8. end
  9. def self.normalize(ext)
  10. ext.to_s.downcase.sub(/^\./, '')
  11. end
  12. # Register a template implementation by file extension.
  13. def self.register(template_class, *extensions)
  14. if template_class.respond_to?(:to_str)
  15. # Support register(ext, template_class) too
  16. extensions, template_class = [template_class], extensions[0]
  17. end
  18. extensions.each do |ext|
  19. ext = normalize(ext)
  20. mappings[ext].unshift(template_class).uniq!
  21. end
  22. end
  23. # Makes a template class preferred for the given file extensions. If you
  24. # don't provide any extensions, it will be preferred for all its already
  25. # registered extensions:
  26. #
  27. # # Prefer RDiscount for its registered file extensions:
  28. # Tilt.prefer(Tilt::RDiscountTemplate)
  29. #
  30. # # Prefer RDiscount only for the .md extensions:
  31. # Tilt.prefer(Tilt::RDiscountTemplate, '.md')
  32. def self.prefer(template_class, *extensions)
  33. if extensions.empty?
  34. mappings.each do |ext, klasses|
  35. @preferred_mappings[ext] = template_class if klasses.include? template_class
  36. end
  37. else
  38. extensions.each do |ext|
  39. ext = normalize(ext)
  40. register(template_class, ext)
  41. @preferred_mappings[ext] = template_class
  42. end
  43. end
  44. end
  45. # Returns true when a template exists on an exact match of the provided file extension
  46. def self.registered?(ext)
  47. mappings.key?(ext.downcase) && !mappings[ext.downcase].empty?
  48. end
  49. # Create a new template for the given file using the file's extension
  50. # to determine the the template mapping.
  51. def self.new(file, line=nil, options={}, &block)
  52. if template_class = self[file]
  53. template_class.new(file, line, options, &block)
  54. else
  55. fail "No template engine registered for #{File.basename(file)}"
  56. end
  57. end
  58. # Lookup a template class for the given filename or file
  59. # extension. Return nil when no implementation is found.
  60. def self.[](file)
  61. pattern = file.to_s.downcase
  62. until pattern.empty? || registered?(pattern)
  63. pattern = File.basename(pattern)
  64. pattern.sub!(/^[^.]*\.?/, '')
  65. end
  66. # Try to find a preferred engine.
  67. preferred_klass = @preferred_mappings[pattern]
  68. return preferred_klass if preferred_klass
  69. # Fall back to the general list of mappings.
  70. klasses = @template_mappings[pattern]
  71. # Try to find an engine which is already loaded.
  72. template = klasses.detect do |klass|
  73. if klass.respond_to?(:engine_initialized?)
  74. klass.engine_initialized?
  75. end
  76. end
  77. return template if template
  78. # Try each of the classes until one succeeds. If all of them fails,
  79. # we'll raise the error of the first class.
  80. first_failure = nil
  81. klasses.each do |klass|
  82. begin
  83. klass.new { '' }
  84. rescue Exception => ex
  85. first_failure ||= ex
  86. next
  87. else
  88. return klass
  89. end
  90. end
  91. raise first_failure if first_failure
  92. end
  93. # Deprecated module.
  94. module CompileSite
  95. end
  96. # Extremely simple template cache implementation. Calling applications
  97. # create a Tilt::Cache instance and use #fetch with any set of hashable
  98. # arguments (such as those to Tilt.new):
  99. # cache = Tilt::Cache.new
  100. # cache.fetch(path, line, options) { Tilt.new(path, line, options) }
  101. #
  102. # Subsequent invocations return the already loaded template object.
  103. class Cache
  104. def initialize
  105. @cache = {}
  106. end
  107. def fetch(*key)
  108. @cache[key] ||= yield
  109. end
  110. def clear
  111. @cache = {}
  112. end
  113. end
  114. # Template Implementations ================================================
  115. require 'tilt/string'
  116. register StringTemplate, 'str'
  117. require 'tilt/erb'
  118. register ERBTemplate, 'erb', 'rhtml'
  119. register ErubisTemplate, 'erb', 'rhtml', 'erubis'
  120. require 'tilt/haml'
  121. register HamlTemplate, 'haml'
  122. require 'tilt/css'
  123. register SassTemplate, 'sass'
  124. register ScssTemplate, 'scss'
  125. register LessTemplate, 'less'
  126. require 'tilt/coffee'
  127. register CoffeeScriptTemplate, 'coffee'
  128. require 'tilt/nokogiri'
  129. register NokogiriTemplate, 'nokogiri'
  130. require 'tilt/builder'
  131. register BuilderTemplate, 'builder'
  132. require 'tilt/markaby'
  133. register MarkabyTemplate, 'mab'
  134. require 'tilt/liquid'
  135. register LiquidTemplate, 'liquid'
  136. require 'tilt/radius'
  137. register RadiusTemplate, 'radius'
  138. require 'tilt/markdown'
  139. register MarukuTemplate, 'markdown', 'mkd', 'md'
  140. register KramdownTemplate, 'markdown', 'mkd', 'md'
  141. register BlueClothTemplate, 'markdown', 'mkd', 'md'
  142. register RDiscountTemplate, 'markdown', 'mkd', 'md'
  143. register RedcarpetTemplate, 'markdown', 'mkd', 'md'
  144. require 'tilt/textile'
  145. register RedClothTemplate, 'textile'
  146. require 'tilt/rdoc'
  147. register RDocTemplate, 'rdoc'
  148. require 'tilt/wiki'
  149. register CreoleTemplate, 'wiki', 'creole'
  150. register WikiClothTemplate, 'wiki', 'mediawiki', 'mw'
  151. require 'tilt/yajl'
  152. register YajlTemplate, 'yajl'
  153. end