diff options
author | Paul Duncan <pabs@pablotron.org> | 2018-09-05 00:53:51 -0400 |
---|---|---|
committer | Paul Duncan <pabs@pablotron.org> | 2018-09-05 00:53:51 -0400 |
commit | 7fd6c6611252dbe1aae0ae61afab78f936b47c1f (patch) | |
tree | f4643a9b1465b329e0c7c570fcca19fe35b1de7a /ruby/luigi-template.rb | |
parent | 4f92438ba4cbc04016e2b06996bb7d7e99e01952 (diff) | |
download | luigi-template-7fd6c6611252dbe1aae0ae61afab78f936b47c1f.tar.bz2 luigi-template-7fd6c6611252dbe1aae0ae61afab78f936b47c1f.zip |
ruby: add gemspec, add Template::run
Diffstat (limited to 'ruby/luigi-template.rb')
-rw-r--r-- | ruby/luigi-template.rb | 289 |
1 files changed, 0 insertions, 289 deletions
diff --git a/ruby/luigi-template.rb b/ruby/luigi-template.rb deleted file mode 100644 index d737fdc..0000000 --- a/ruby/luigi-template.rb +++ /dev/null @@ -1,289 +0,0 @@ -# require 'pp' - -module Luigi - # - # library version - # - VERSION = '0.4.0' - - # - # built-in filters - # - FILTERS = { - # upper-case string - uc: proc { |v, args, row, t| - (v || '').to_s.upcase - }, - - # lower-case string - lc: proc { |v, args, row, t| - (v || '').to_s.downcase - }, - - # html-escape string - h: proc { |v, args, row, t| - (v || '').to_s.gsub(/&/, '&').gsub(/</, '<').gsub(/>/, '>').gsub(/'/, '&apos').gsub(/"/, '"') - }, - - # uri-escape string - u: proc( { |v, args, row, t| - require 'uri' - URI.escape((v || '').to_s) - }, - - # json-encode value - json: proc { |v, args, row, t| - require 'json' - v.to_json - }, - - # trim leading and trailing whitespace from string - trim: proc { |v, args, row, t| - (v || '').to_s.strip - }, - - # base64-encode string - base64: proc { |v, args, row, t| - [(v || '').to_s].pack('m') - }, - - # hash string - hash: proc { |v, args, row, t| - require 'openssl' - OpenSSL::Digest.new(args[0] || 'md5').hexdigest((v || '').to_s) - }, - } - - # - # Template parser. - # - module Parser - RES = { - action: %r{ - # match opening brace - %\{ - - # match optional whitespace - \s* - - # match key - (?<key>[^\s\|\}]+) - - # match filter(s) - (?<filters>(\s*\|(\s*[^\s\|\}]+)+)*) - - # match optional whitespace - \s* - - # match closing brace - \} - - # or match up all non-% chars or a single % char - | (?<text>[^%]* | %) - }mx, - - filter: %r{ - # match filter name - (?<name>\S+) - - # match filter arguments (optional) - (?<args>(\s*\S+)*) - - # optional trailing whitespace - \s* - }mx, - - delim_filters: %r{ - \s*\|\s* - }mx, - - delim_args: %r{ - \s+ - }, - } - - # - # Parse a (possibly empty) string into an array of actions. - # - def self.parse_template(str) - str.scan(RES[:action]).map { |m| - if m[0] && m[0].length > 0 - r = { - type: :action, - key: m[0].intern, - filters: parse_filters(m[1]), - } - else - # literal text - r = { type: :text, text: m[2] } - end - - # pp r - - # return result - r - } - end - - # - # Parse a (possibly empty) string into an array of filters. - # - def self.parse_filters(str) - # strip leading and trailing whitespace - str = (str || '').strip - - if str.length > 0 - str.strip.split(RES[:delim_filters]).inject([]) do |r, f| - # strip whitespace - f = f.strip - - if f.length > 0 - md = f.match(RES[:filter]) - raise "invalid filter: #{f}" unless md - # pp md - - # get args - args = md[:args].strip - - # add to result - r << { - name: md[:name].intern, - args: args.length > 0 ? args.split(RES[:delim_args]) : [], - } - end - - # return result - r - end - else - # return empty filter set - [] - end - end - end - - # - # Template class. - # - class Template - # - # Create a new Template from the given string. - # - def initialize(str, filters = FILTERS) - @str, @filters = str, filters - @actions = Parser.parse_template(str) - end - - # - # Run template with given arguments - # - def run(args) - @actions.map { |a| - # pp a - - case a[:type] - when :action - # check key and get value - val = if args.key?(a[:key]) - args[a[:key]] - elsif args.key?(a[:key].to_s) - args[a[:key].to_s] - else - # invalid key - raise "unknown argument: #{a[:key]}" - end - - # filter value - a[:filters].inject(val) do |r, f| - # check filter name - raise "unknown filter: #{f[:name]}" unless @filters.key?(f[:name]) - - # call filter, return result - @filters[f[:name]].call(r, f[:args], args, self) - end - when :text - # literal text - a[:text] - else - # never reached - raise "unknown action type: #{a[:type]}" - end - }.join - end - end - - # - # Simple template cache. - # - class Cache - # - # Create a new template cache with the given templates - # - def initialize(strings, filters = FILTERS) - @templates = Hash.new do |h, k| - # always deal with symbols - k = k.intern - - # make sure template exists - raise "unknown template: #{k}" unless strings.key?(k) - - # create template - h[k] = Template.new(strings[k], filters) - end - end - - # - # Run specified template with given arguments. - # - def run(key, args) - # run template with args and return result - @templates[key].run(args) - end - end - - # - # test module - # - module Test - # test template - TEMPLATE = ' - basic test: hello %{name} - test filters: hello %{name | uc | base64 | hash sha1} - test custom: hello %{name|custom} - test custom_with_arg: %{name|custom_with_arg asdf} - ' - - CUSTOM_FILTERS = { - custom: proc { - 'custom' - }, - - custom_with_arg: proc { |v, args| - args.first || 'custom' - }, - } - - # test template cache - CACHE = { - test: TEMPLATE - } - - # test arguments - ARGS = { - name: 'paul', - } - - def self.run - # add custom filters - Luigi::FILTERS.update(CUSTOM_FILTERS) - - # test individual template - puts Template.new(TEMPLATE).run(ARGS) - - # test template cache - puts Cache.new(CACHE).run(:test, ARGS) - end - end -end - -Luigi::Test.run if __FILE__ == $0 |