Hrb: not as good as I thought
This commit is contained in:
		
							
								
								
									
										18
									
								
								lib/hrb.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								lib/hrb.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "hrb/version"
 | 
			
		||||
require "hrb/template"
 | 
			
		||||
require "hrb/tilt" if defined?(Tilt)
 | 
			
		||||
require "hrb/action_view" if defined?(ActionView::Template)
 | 
			
		||||
 | 
			
		||||
module Hrb
 | 
			
		||||
  module_function
 | 
			
		||||
 | 
			
		||||
  def render(locals: {}, scope: nil, &block)
 | 
			
		||||
    Hrb::Template.render(locals: locals, scope: scope, &block)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def render_file(path, locals: {}, scope: nil)
 | 
			
		||||
    Hrb::Template.render_file(path, locals: locals, scope: scope)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										17
									
								
								lib/hrb/action_view.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/hrb/action_view.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Hrb
 | 
			
		||||
  module Rails
 | 
			
		||||
    class TemplateHandler
 | 
			
		||||
      def call(template)
 | 
			
		||||
        <<-CODE
 | 
			
		||||
          Hrb::Template.build do
 | 
			
		||||
            #{template.source}
 | 
			
		||||
          end.to_s
 | 
			
		||||
        CODE
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
ActionView::Template.register_template_handler :hrb, Hrb::Rails::TemplateHandler
 | 
			
		||||
							
								
								
									
										131
									
								
								lib/hrb/template.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								lib/hrb/template.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "erb"
 | 
			
		||||
 | 
			
		||||
module Hrb
 | 
			
		||||
  class Template
 | 
			
		||||
    TAGS = %i[a address article aside b bdi body button code details dialog div dd dl dt em fieldset figcaption figure
 | 
			
		||||
              footer form h1 h2 h3 h4 h5 h6 head header html i iframe label li main mark menuitem meter nav ol option
 | 
			
		||||
              option p pre progress rp rt ruby s script section select small span strong summary table tbody td textarea
 | 
			
		||||
              th thead time title tr u ul video wbr].freeze
 | 
			
		||||
    EMPTY_TAGS = %i[img br hr input link meta source].freeze
 | 
			
		||||
 | 
			
		||||
    ENDINGS = Hash[TAGS.map do |tag|
 | 
			
		||||
      [tag, "</#{tag}>"]
 | 
			
		||||
    end].freeze
 | 
			
		||||
 | 
			
		||||
    def self.render(locals: {}, scope: nil, &block)
 | 
			
		||||
      template = Template.new(locals: locals, scope: scope)
 | 
			
		||||
      template.instance_eval(&block)
 | 
			
		||||
      template.to_s
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.render_file(path, locals: {}, scope: nil)
 | 
			
		||||
      template = Template.new(locals: locals, scope: scope)
 | 
			
		||||
      template.instance_eval(File.read(path))
 | 
			
		||||
      template.to_s
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def initialize(locals: {}, scope: nil)
 | 
			
		||||
      @fragments = [[]]
 | 
			
		||||
      self.locals = locals
 | 
			
		||||
      @scope = scope
 | 
			
		||||
      @view = @fragments.last
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    TAGS.each do |name|
 | 
			
		||||
      define_method(name) do |content = nil, options = {}, &block|
 | 
			
		||||
        tag(name, content, options, &block)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    EMPTY_TAGS.each do |name|
 | 
			
		||||
      define_method(name) do |options = {}|
 | 
			
		||||
        tag(name, nil, options, end_tag: false)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def tag(name, content = nil, options = {}, end_tag: true, &block)
 | 
			
		||||
      output = "<#{name}#{tag_attributes(content&.is_a?(Hash) ? content : options)}>"
 | 
			
		||||
      output += content if content&.is_a?(String)
 | 
			
		||||
      output += capture(&block) if block
 | 
			
		||||
      output += ENDINGS[name] if end_tag
 | 
			
		||||
 | 
			
		||||
      raw output
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def method_missing(name, *args, &block)
 | 
			
		||||
      if @scope.respond_to?(name)
 | 
			
		||||
        @scope.send(name, *args, &block)
 | 
			
		||||
      elsif @locals&.key?(name) || @locals&.key?(name.to_s)
 | 
			
		||||
        @locals.fetch(name) { @locals[name.to_s] }
 | 
			
		||||
      else
 | 
			
		||||
        super
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def respond_to_missing?(name, include_all = false)
 | 
			
		||||
      @scope.respond_to?(name) || @locals&.key?(name) || @locals&.key?(name.to_s) || super
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def view
 | 
			
		||||
      @fragments.last
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def doctype
 | 
			
		||||
      raw "<!DOCTYPE html>"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def text(output)
 | 
			
		||||
      raw escape(output)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def raw(output)
 | 
			
		||||
      view.push output.to_s
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def capture(&block)
 | 
			
		||||
      @fragments.push []
 | 
			
		||||
      instance_eval(&block)
 | 
			
		||||
 | 
			
		||||
      view.join.tap do
 | 
			
		||||
        @fragments.pop
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def locals=(locals)
 | 
			
		||||
      locals.each do |key, value|
 | 
			
		||||
        metaclass do
 | 
			
		||||
          define_method key do
 | 
			
		||||
            value
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def to_s
 | 
			
		||||
      @fragments.join
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def metaclass(&block)
 | 
			
		||||
      metaclass = class << self; self; end
 | 
			
		||||
      metaclass.class_eval(&block)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def tag_attributes(options)
 | 
			
		||||
      attrs = ""
 | 
			
		||||
 | 
			
		||||
      options.each do |key, value|
 | 
			
		||||
        attrs += %( #{key.to_s.tr("_", "-")}="#{escape(value)}")
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      attrs
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def escape(value)
 | 
			
		||||
      ERB::Util.html_escape(value.respond_to?(:join) ? value.join(" ") : value.to_s)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										21
									
								
								lib/hrb/tilt.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/hrb/tilt.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Tilt
 | 
			
		||||
  class HrbTemplate < Tilt::Template
 | 
			
		||||
    def prepare; end
 | 
			
		||||
 | 
			
		||||
    def evaluate(scope, locals, &block)
 | 
			
		||||
      builder = Hrb::Template.new(locals: locals, scope: scope)
 | 
			
		||||
 | 
			
		||||
      if block
 | 
			
		||||
        builder.capture(&block)
 | 
			
		||||
      else
 | 
			
		||||
        builder.instance_eval(data)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      builder.to_s
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  register Tilt::HrbTemplate, "rb"
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										5
									
								
								lib/hrb/version.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								lib/hrb/version.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module Hrb
 | 
			
		||||
  VERSION = "1.0.0-alpha.1"
 | 
			
		||||
end
 | 
			
		||||
		Reference in New Issue
	
	Block a user