diff --git a/CHANGES b/CHANGES index 5d85ad3f..2ea68708 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ = 1.4.0 / Not Yet Released + * Add support for Yajl templates. (Jamie Hodge) + * No longer include Sinatra::Delegator in Object, instead extend the main object only. (Konstantin Haase) diff --git a/Gemfile b/Gemfile index 9cbb80a9..f8b9679e 100644 --- a/Gemfile +++ b/Gemfile @@ -42,6 +42,7 @@ gem 'maruku' gem 'creole' gem 'markaby' gem 'radius' +gem 'yajl-ruby' if RUBY_ENGINE == 'jruby' gem 'nokogiri', '!= 1.5.0' diff --git a/README.rdoc b/README.rdoc index 681f6c69..1c72c15a 100644 --- a/README.rdoc +++ b/README.rdoc @@ -514,6 +514,21 @@ Dependency:: {coffee-script}[https://github.com/josh/ruby-coffee-script] File Extensions:: .coffee Example:: coffee :index +=== Yajl Templates + +Dependency:: {yajl-ruby}[https://github.com/brianmario/yajl-ruby] +File Extensions:: .yajl +Example:: yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource' + +The template source is evaluated as a Ruby string, and the resulting json variable is converted #to_json. + + json = { :foo => 'bar' } + json[:baz] = key + +The :callback and :variable options can be used to decorate the rendered object. + + var resource = {"foo":"bar","baz":"qux"}; present(resource); + === Embedded Templates get '/' do diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 7cd0825d..dc4a7de2 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -603,6 +603,11 @@ module Sinatra render :creole, template, options, locals end + def yajl(template, options={}, locals={}) + options[:default_content_type] = :json + render :yajl, template, options, locals + end + # Calls the given block for every possible template file in views, # named name.ext, where ext is registered on engine. def find_template(views, name, engine) diff --git a/test/views/hello.yajl b/test/views/hello.yajl new file mode 100644 index 00000000..68ef6a6c --- /dev/null +++ b/test/views/hello.yajl @@ -0,0 +1 @@ +json = { :yajl => "hello" } \ No newline at end of file diff --git a/test/yajl_test.rb b/test/yajl_test.rb new file mode 100644 index 00000000..546ee5a3 --- /dev/null +++ b/test/yajl_test.rb @@ -0,0 +1,80 @@ +require File.expand_path('../helper', __FILE__) + +begin +require 'yajl' + +class YajlTest < Test::Unit::TestCase + def yajl_app(&block) + mock_app { + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + } + get '/' + end + + it 'renders inline Yajl strings' do + yajl_app { yajl 'json = { :foo => "bar" }' } + assert ok? + assert_body '{"foo":"bar"}' + end + + it 'renders .yajl files in views path' do + yajl_app { yajl :hello } + assert ok? + assert_body '{"yajl":"hello"}' + end + + it 'raises error if template not found' do + mock_app { + get('/') { yajl :no_such_template } + } + assert_raise(Errno::ENOENT) { get('/') } + end + + it 'accepts a :locals option' do + yajl_app { + locals = { :object => { :foo => 'bar' } } + yajl 'json = object', :locals => locals + } + assert ok? + assert_body '{"foo":"bar"}' + end + + it 'accepts a :scope option' do + yajl_app { + scope = { :object => { :foo => 'bar' } } + yajl 'json = self[:object]', :scope => scope + } + assert ok? + assert_body '{"foo":"bar"}' + end + + it 'decorates the json with a callback' do + yajl_app { + yajl 'json = { :foo => "bar" }', { :callback => 'baz' } + } + assert ok? + assert_body 'baz({"foo":"bar"});' + end + + it 'decorates the json with a variable' do + yajl_app { + yajl 'json = { :foo => "bar" }', { :variable => 'qux' } + } + assert ok? + assert_body 'var qux = {"foo":"bar"};' + end + + it 'decorates the json with a callback and a variable' do + yajl_app { + yajl 'json = { :foo => "bar" }', + { :callback => 'baz', :variable => 'qux' } + } + assert ok? + assert_body 'var qux = {"foo":"bar"}; baz(qux);' + end +end + +rescue LoadError + warn "#{$!.to_s}: skipping yajl tests" +end