diff options
-rw-r--r-- | ruby/Rakefile | 8 | ||||
-rw-r--r-- | ruby/lib/luigi-template.rb | 70 | ||||
-rw-r--r-- | ruby/test/test_template.rb | 150 |
3 files changed, 209 insertions, 19 deletions
diff --git a/ruby/Rakefile b/ruby/Rakefile new file mode 100644 index 0000000..debc11c --- /dev/null +++ b/ruby/Rakefile @@ -0,0 +1,8 @@ +require 'rake/testtask' + +Rake::TestTask.new do |t| + t.libs << 'test' +end + +desc "Run tests" +task :default => :test diff --git a/ruby/lib/luigi-template.rb b/ruby/lib/luigi-template.rb index 13244da..36faf74 100644 --- a/ruby/lib/luigi-template.rb +++ b/ruby/lib/luigi-template.rb @@ -6,6 +6,37 @@ module Luigi # VERSION = '0.4.0' + class LuigiError < Exception + end + + class BaseUnknownError < LuigiError + attr_reader :type_name, + :name + + def initialize(type_name, name) + @type_name, @name = type_name, name + super("unknown #{type_name}: #{name}") + end + end + + class UnknownKeyError < BaseUnknownError + def initialize(key) + super(:key, key) + end + end + + class UnknownFilterError < BaseUnknownError + def initialize(filter) + super(:filter, filter) + end + end + + class UnknownTemplateError < BaseUnknownError + def initialize(template) + super(:template, template); + end + end + # # built-in filters # @@ -100,7 +131,10 @@ module Luigi delim_args: %r{ \s+ }, - } + }.reduce({}) do |r, row| + r[row[0]] = row[1].freeze + r + end.freeze # # Parse a (possibly empty) string into an array of actions. @@ -108,21 +142,13 @@ module Luigi 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]), - } + fs = parse_filters(m[1]).freeze + { type: :action, key: m[0].intern, filters: fs } else # literal text - r = { type: :text, text: m[2] } - end - - # pp r - - # return result - r - } + { type: :text, text: m[2].freeze } + end.freeze + }.freeze end # @@ -180,8 +206,8 @@ module Luigi # Create a new Template from the given string. # def initialize(str, filters = FILTERS) - @str, @filters = str, filters - @actions = Parser.parse_template(str) + @str, @filters = str.freeze, filters + @actions = Parser.parse_template(str).freeze end # @@ -200,13 +226,15 @@ module Luigi args[a[:key].to_s] else # invalid key - raise "unknown argument: #{a[:key]}" + raise UnknownKeyError.new(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]) + unless @filters.key?(f[:name]) + raise UnknownFilterError.new(f[:name]) + end # call filter, return result @filters[f[:name]].call(r, f[:args], args, self) @@ -220,6 +248,10 @@ module Luigi end }.join end + + def to_s + @str + end end # @@ -235,7 +267,7 @@ module Luigi k = k.intern # make sure template exists - raise "unknown template: #{k}" unless strings.key?(k) + raise UnknownTemplateError.new(k) unless strings.key?(k) # create template h[k] = Template.new(strings[k], filters) diff --git a/ruby/test/test_template.rb b/ruby/test/test_template.rb new file mode 100644 index 0000000..3c8dd10 --- /dev/null +++ b/ruby/test/test_template.rb @@ -0,0 +1,150 @@ +require 'minitest/autorun' +require 'luigi-template' + +class TemplateTest < MiniTest::Test + def test_new + t = Luigi::Template.new('foo%{bar}') + assert_instance_of Luigi::Template, t + end + + def test_run + t = Luigi::Template.new('foo%{bar}') + r = t.run(bar: 'foo') + + assert_equal 'foofoo', r + end + + def test_run_with_string_key + t = Luigi::Template.new('foo%{bar}') + r = t.run('bar' => 'foo') + + assert_equal 'foofoo', r + end + + def test_self_run + r = Luigi::Template.run('foo%{bar}', bar: 'foo') + assert_equal 'foofoo', r + end + + def test_multiple_keys + r = Luigi::Template.run('foo%{bar}%{baz}', { + bar: 'foo', + baz: 'bar', + }) + + assert_equal 'foofoobar', r + end + + def test_whitespace + r = Luigi::Template.run('%{ bar}%{ bar }%{ bar}', { + bar: 'foo', + }) + + assert_equal 'foofoofoo', r + end + + def test_newlines + r = Luigi::Template.run('%{ + bar}%{ + bar + + }%{ + bar}', { + bar: 'foo', + }) + + assert_equal 'foofoofoo', r + end + + def test_filter + r = Luigi::Template.run('foo%{bar|h}', { + bar: '<', + }) + + assert_equal 'foo<', r + end + + def test_filter_chain + want = 'foofeab40e1fca77c7360ccca1481bb8ba5f919ce3a' + r = Luigi::Template.run('foo%{bar | uc | hash sha1}', { + bar: 'foo', + }) + + assert_equal want, r + end + + def test_custom_global_filter + Luigi::FILTERS[:barify] = proc { |v| 'BAR' } + + r = Luigi::Template.run('foo%{bar | barify}', { + bar: 'foo', + }) + + assert_equal 'fooBAR', r + end + + def test_custom_template_filter + r = Luigi::Template.run('foo%{bar | barify}', { + bar: 'foo', + }, { + barify: proc { |v| 'BAR' } + }) + + assert_equal 'fooBAR', r + end + + def test_cache + cache = Luigi::Cache.new({ + foo: 'foo%{bar}', + }) + + r = cache.run(:foo, bar: 'foo') + + assert_equal 'foofoo', r + end + + def test_filter_args + want = 'foo0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' + + r = Luigi::Template.run('foo%{bar | hash sha1}', { + bar: 'foo' + }) + + assert_equal want, r + end + + def test_unknown_key_error + assert_raises(Luigi::UnknownKeyError) do + Luigi::Template.run('foo%{unknown-key}', { + bar: 'foo', + }) + end + end + + def test_unknown_filter_error + assert_raises(Luigi::UnknownFilterError) do + Luigi::Template.run('foo%{bar | unknown-filter}', { + bar: 'foo', + }) + end + end + + def test_unknown_template_error + assert_raises(Luigi::UnknownTemplateError) do + cache = Luigi::Cache.new({ + foo: 'foo%{bar}', + }) + + cache.run('unknown-template', { + bar: 'foo' + }) + end + end + + def test_to_s + want = '%{val | h}' + t = Luigi::Template.new(want) + + assert_equal want, t.to_s + end +end |