summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ruby/Rakefile8
-rw-r--r--ruby/lib/luigi-template.rb70
-rw-r--r--ruby/test/test_template.rb150
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&lt;', 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