module YMDP module Compiler #:nodoc: module Template #:nodoc: # Process source files into usable code. # # Source files can be HTML, Haml, ERB, JavaScript, or CSS files. # # Files with an extension of ".haml" will be processed with Haml, all others will # use ERB. # # ==== Examples # # YMDP::Compiler::Template::View.new(params).build # # YMDP::Compiler::Template::JavaScript.new(params).build # # @template = YMDP::Compiler::Template::Base.new(params) # # ==== Options # # verbose: boolean value, output verbose notices, # domain: string, indicates which domain the template is compiling to, # file: filename of the template in question, # hash: git hash of the latest commit, # message: commit message of the latest commit. # class Base < YMDP::Base attr_accessor :domain, :server, :file, :assets_directory, :hash, :message def initialize(params) @verbose = params[:verbose] @domain = params[:domain] server_settings = servers[@domain] if server_settings @server = server_settings["server"] else raise StandardError.new("Server settings are required.") end raise StandardError.new("Server name does not exist in server settings.") unless @server @file = params[:file] @assets_directory = "/om/assets/#{servers[@domain]['assets_id']}" @hash = params[:git_hash] @message = params[:message] set_content_variables @view = base_filename(@file.split("/").last) Application.current_view = @view end # Is the verbose setting on? # def verbose? @verbose end # Parses the file 'content.yml' and adds each of its keys to the environment as # an instance variable, so they will be available inside the template. # def set_content_variables content_variables.each do |key, value| attribute = "@#{key}" instance_variable_set(attribute, value) unless instance_variable_defined?(attribute) class_eval %( attr_accessor :#{key} ) end end # If the filename begins with a _ it's a partial. # def partial? File.basename(@file) =~ /^_/ end # Compile this view unless it is a partial. # def build # puts "Base build" unless partial? write_template(processed_template) end end # Returns the compiled template code after its Haml or ERB has been processed. # def processed_template # "Base processed_template" result = "" template = File.read(@file) if @file =~ /\.haml$/ result = process_haml(template, @file) else result = process_template(template) end result end def base_filename(filename) raise "Define in child" end # Implemented in child classes, this defines what must be done to process a template. # def process_template(template) raise "Define in child" end # Produces the destination path of this template, in the servers directory for # the given domain. # # ==== Examples # # If the source file is: # # app/views/authorize.html.haml # # The destination file will be: # # servers/staging/views/authorize.html.haml # def destination_path # just the file, with no directory filename = File.basename(@file) # just the filename, with no extension filename = convert_filename(filename) # just the directory, with no file directory = File.dirname(@file) # replace the app directory with the server directory relative_directory = directory.gsub!("#{paths[:base_path]}/app", server_path) # make the directory if it doesn't exist FileUtils.mkdir_p(relative_directory) "#{relative_directory}/#{filename}" end # Path to the servers directory for the current domain. # def server_path "#{servers_path}/#{@domain}" end # Outputs a message if @verbose is on. # def verbose(message) $stdout.puts(message) if @verbose end # Writes the input string to the destination file without adding any layout. # def write_template_without_layout(result) path = destination_path # puts "\n\n\nBase write_template_without_layout: #{result}, #{path}" File.open(path, "w") do |f| f.write(result) end verbose "Finished writing #{path}.\n" result end # Writes the input string to the destination file, passing it through the # application template. # # The application layout can be either Haml or ERB. # def write_template_with_layout(result) # puts "Base write_template_with_layout" @content = result layout = result application_layout = "#{paths[:base_path]}\/app\/views\/layouts\/application.html" haml_layout = application_layout + ".haml" if File.exists?(haml_layout) template = File.read(haml_layout) layout = process_haml(template, haml_layout) end write_template_without_layout(layout) end # Write this processed template to its destination file. # # Overwrite this method in child class to define whether the class # uses a template or not. # def write_template(result) # puts "Base write_template" write_template_with_layout(result) end def servers_path "#{paths[:base_path]}/servers" end end class View < Base # TODO: Refactor this. Right now it includes all the YMDP::ApplicationView and other helper files # into the same namespace where we're processing the templates. It does this so it can # send its 'binding' into the ERB or Haml template and the template will be able to # process methods like "render :partial => 'sidebar'" and so on. # # All the methods which are meant to be run from inside a view need to be refactored into # their own class, which can be sent into the template as a binding. # include ActionView::Helpers::TagHelper include ApplicationHelper if defined?(ApplicationHelper) include YMDP::ApplicationView include YMDP::AssetTagHelper include YMDP::FormTagHelper include YMDP::LinkTagHelper attr_accessor :output_buffer # Filename without its extension: # # - "authorize.html.haml" becomes "authorize" # def base_filename(filename) filename.gsub(/(\.html|\.erb|\.haml)/, "") end # Filename without its extension: # # - "authorize.html.haml" becomes "authorize" # def convert_filename(filename) base_filename(filename) end # Process this template with ERB. # def process_template(template) # puts "View process_template" ERB.new(template, 0, "%<>").result(binding) end # Process this template with Haml. # def process_haml(template, filename=nil) # puts "View process_haml" options = {} if filename options[:filename] = filename end Haml::Engine.new(template, options).render(self) end # Write this template with the application layout applied. # # Validate the resulting HTML file if that option is turned on. # def write_template(result) # puts "View write_template" result = super(result) YMDP::Validator::HTML.validate(destination_path) if CONFIG.validate_html? result end end # Process templates for JavaScript files. # # JavaScript files support ERB tags. # class JavaScript < View # Write the processed template without any layout. # # Run the JavaScript compressor on the file if that option is turned on. # def write_template(result) filename = @file.split("/").last tmp_filename = "./tmp/#{filename}" F.save_to_file(result, tmp_filename) result = YMDP::Compressor::JavaScript.compress(tmp_filename) if CONFIG.compress_embedded_js? write_template_without_layout(result) end end # Process Yahoo! Resource Bundle format translation files. # # Convert them to a hash and write the hash to a JSON file. # # Each language can have as many YRB translation files (with an extension of ".pres") # as necessary. The files are concatenated together and translated into a single JSON file # for each language. # class YRB < Base # Base directory for translations for this domain. # def directory directory = "#{paths[:base_path]}/servers/#{@domain}/assets/yrb" FileUtils.mkdir_p(directory) directory end # The destination of the compiled JSON file. # def destination_path filename = convert_filename(@file.split("/").last) "#{directory}/#{filename}" end # JSON values of the compiled translations. # def to_json processed_template end # Turn it back into a hash. # def to_hash JSON.parse(to_json) end # Convert the hash to Yaml if you should want to do that. # def to_yaml h = {} to_hash.each do |k,v| k = k.downcase h[k] = "#{v}" end h.to_yaml end # This function is the file which is written to the destination--in this # case, the JSON file. # def processed_template super.to_json end # Validate the JSON file. # def validate YMDP::Validator::JSON.validate(destination_path) end private # Strip off the ".pres" extension from original YRB files. # def base_filename(filename) filename.gsub(/\.pres/, "") end # Take the base filename and add the ".json" extension. # def convert_filename(filename) "#{base_filename(filename)}.json" end # Is this line a valid comment in YRB? def comment?(line) line =~ /^[\s]*#/ end # Is this line valid YRB syntax? # def key_and_value_from_line(line) if line =~ /^([^\=]+)=(.+)/ return $1, $2.strip else return nil, nil end end # Parse YRB and add it to a hash. Raise an error if the key already exists in the hash. # def process_template(template) @hash = {} lines = template.split("\n") lines.each do |line| unless comment?(line) key, value = key_and_value_from_line(line) unless key.blank? if @hash.has_key?(key) $stdout.puts $stdout.puts "Duplicate value in #{destination_path}" $stdout.puts " #{key}=#{@hash[key]}" $stdout.puts " #{key}=#{value}" $stdout.puts if @hash[key] == value $stdout.puts " Values are the same but duplicate values still should not exist!" $stdout.puts end raise "Duplicate key error" end @hash[key] = value end end end @hash end # Write JSON file to its destination. # def write_template(result) $stdout.puts destination_path if CONFIG.verbose? write_template_without_layout(result) end end end end end