[DRE-commits] [ruby-rabl-rails] 01/07: Imported Upstream version 0.4.0

David Suárez deiv-guest at moszumanska.debian.org
Mon Oct 13 21:32:25 UTC 2014


This is an automated email from the git hooks/post-receive script.

deiv-guest pushed a commit to branch master
in repository ruby-rabl-rails.

commit ce88d3920ccbab6407b5c02f527156bc2a448de8
Author: David Suárez <david.sephirot at gmail.com>
Date:   Mon Oct 13 22:49:38 2014 +0200

    Imported Upstream version 0.4.0
---
 .travis.yml                             |   4 +-
 CHANGELOG.md                            |  10 ++
 Gemfile                                 |  14 +-
 README.md                               |   8 +-
 Rakefile                                |   4 +-
 lib/rabl-rails.rb                       |  95 +++--------
 lib/rabl-rails/compiler.rb              |  66 ++++----
 lib/rabl-rails/condition.rb             |  10 --
 lib/rabl-rails/configuration.rb         |  48 ++++++
 lib/rabl-rails/handler.rb               |   4 +-
 lib/rabl-rails/helpers.rb               |   7 +
 lib/rabl-rails/library.rb               |  59 +++++--
 lib/rabl-rails/nodes.rb                 |   6 +
 lib/rabl-rails/nodes/attribute.rb       |  17 ++
 lib/rabl-rails/nodes/child.rb           |  12 ++
 lib/rabl-rails/nodes/code.rb            |  19 +++
 lib/rabl-rails/nodes/condition.rb       |  14 ++
 lib/rabl-rails/nodes/glue.rb            |  25 +++
 lib/rabl-rails/nodes/node.rb            |   9 +
 lib/rabl-rails/railtie.rb               |   2 -
 lib/rabl-rails/renderer.rb              |  28 ++--
 lib/rabl-rails/renderers/base.rb        | 171 -------------------
 lib/rabl-rails/renderers/hash.rb        |  85 ++++++++++
 lib/rabl-rails/renderers/json.rb        |  14 +-
 lib/rabl-rails/renderers/plist.rb       |  10 +-
 lib/rabl-rails/renderers/xml.rb         |   9 +-
 lib/rabl-rails/responder.rb             |   2 +-
 lib/rabl-rails/template.rb              |  16 +-
 lib/rabl-rails/version.rb               |   2 +-
 lib/rabl-rails/visitors.rb              |   2 +
 lib/rabl-rails/visitors/to_hash.rb      | 131 +++++++++++++++
 lib/rabl-rails/visitors/visitor.rb      |  17 ++
 metadata.yml                            | 207 +++++++++++------------
 rabl-rails.gemspec                      |   8 +-
 test/base_renderer_test.rb              |  67 --------
 test/cache_templates_test.rb            |  35 ----
 test/compiler_test.rb                   | 233 --------------------------
 test/deep_nesting_test.rb               |  56 -------
 test/helper.rb                          |  75 +++++++++
 test/keyword_test.rb                    |  47 ------
 test/non_restful_response_test.rb       |  35 ----
 test/renderers/json_renderer_test.rb    | 189 ---------------------
 test/renderers/plist_renderer_test.rb   | 135 ---------------
 test/renderers/test_hash_renderer.rb    |  90 ++++++++++
 test/renderers/test_json_renderer.rb    |  46 ++++++
 test/renderers/test_plist_renderer.rb   |  42 +++++
 test/renderers/test_xml_renderer.rb     |  37 +++++
 test/renderers/xml_renderer_test.rb     | 137 ----------------
 test/test_compiler.rb                   | 283 ++++++++++++++++++++++++++++++++
 test/test_configuration.rb              |  31 ++++
 test/test_hash_visitor.rb               | 224 +++++++++++++++++++++++++
 test/test_helper.rb                     |  68 --------
 test/test_library.rb                    |  85 ++++++++++
 test/{render_test.rb => test_render.rb} |  42 ++---
 54 files changed, 1593 insertions(+), 1499 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 55a725c..a200689 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,11 @@
 language: ruby
 env:
-  - "RAILS_VERSION=3.1.0"
   - "RAILS_VERSION=3.2.0"
   - "RAILS_VERSION=4.0.0"
+  - "RAILS_VERSION=4.1.0"
 rvm:
   - 1.9.3
   - jruby-19mode
-  - rbx
+  - rbx-2
   - 2.0.0
   - 2.1.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51b78ff..3d8e95d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
 # CHANGELOG
 
+## 0.4.0
+  * Internal cleanup and refactor
+  * Remove the `allow_empty_format_in_template` option, since it has become
+    the default behavior.
+  * Remove multi_json dependency
+  * New options available
+    * replace_nil_values_with_empty_strings
+    * replace_empty_string_values_with_nil
+    * exclude_nil_values
+
 ## 0.3.4
   * Add `xml_options` option to root_level (brettallred)
 
diff --git a/Gemfile b/Gemfile
index 9d9ebc0..e008c04 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,18 +6,20 @@ rails_version = ENV['RAILS_VERSION'] || 'default'
 
 rails = case rails_version
 when 'master'
-  {github: 'rails/rails'}
+  { github: 'rails/rails' }
 when "default"
   '~> 3.2.0'
 else
   "~> #{rails_version}"
 end
 
+minitest_version = rails_version == '4.0.0' ? '~> 4.7' : '~> 5.4'
+
 gem 'activesupport', rails
 gem 'railties', rails
 
-group :development, :test do
-  gem 'actionpack', rails
+group :test do
+  gem 'minitest', minitest_version
 end
 
 gem 'plist'
@@ -34,9 +36,3 @@ platforms :jruby do
   gem 'nokogiri'
 end
 
-platforms :rbx do
-  gem 'rubysl', '~> 2.0'
-  gem 'minitest'
-  gem 'rubysl-test-unit'
-end
-
diff --git a/README.md b/README.md
index 3b2bbe7..9ed90c2 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ RABL (Ruby API Builder Language) is a ruby templating system for rendering resou
 
 rabl-rails is **faster** and uses **less memory** than the standard rabl gem while letting you access the same features. There are some slight changes to do on your templates to get this gem to work but it should't take you more than 5 minutes.
 
-rabl-rails only targets **Rails 3+ application** and is compatible with mri 1.9.3, jRuby and rubinius.
+rabl-rails only targets **Rails 3.2+ application** and is compatible with mri 1.9.3+, jRuby and rubinius.
 
 ## Installation
 
@@ -91,12 +91,14 @@ RablRails.configure do |config|
   # These are the default
   # config.cache_templates = true
   # config.include_json_root = true
-  # config.json_engine = :oj
-  # config.xml_engine = 'LibXML'
+  # config.json_engine = ::Oj
   # config.xml_options = { :dasherize => true, :skip_types => false }
   # config.use_custom_responder = false
   # config.default_responder_template = 'show'
   # config.enable_jsonp_callbacks = false
+  # config.replace_nil_values_with_empty_strings = false
+  # config.replace_empty_string_values_with_nil = false
+  # config.exclude_nil_values = false
 end
 ```
 
diff --git a/Rakefile b/Rakefile
index e54349b..f30c34b 100755
--- a/Rakefile
+++ b/Rakefile
@@ -27,8 +27,8 @@ require 'rake/testtask'
 Rake::TestTask.new(:test) do |t|
   t.libs << 'lib'
   t.libs << 'test'
-  t.pattern = 'test/**/*_test.rb'
-  t.verbose = true
+  t.pattern = 'test/**/test_*.rb'
+  # t.verbose = true
 end
 
 
diff --git a/lib/rabl-rails.rb b/lib/rabl-rails.rb
index 68c87f8..3ab0bf5 100644
--- a/lib/rabl-rails.rb
+++ b/lib/rabl-rails.rb
@@ -1,96 +1,43 @@
 require 'rails/railtie'
 
 require 'active_support'
-require 'active_support/core_ext/class/attribute_accessors'
 
 require 'rabl-rails/version'
+require 'rabl-rails/helpers'
 require 'rabl-rails/template'
-require 'rabl-rails/condition'
+require 'rabl-rails/nodes'
 require 'rabl-rails/compiler'
 
+require 'rabl-rails/visitors'
 require 'rabl-rails/renderer'
-
 require 'rabl-rails/library'
+
 require 'rabl-rails/handler'
 require 'rabl-rails/railtie'
 
-require 'multi_json'
+require 'rabl-rails/configuration'
+
+begin
+  require 'oj'
+  Oj.default_options =  { mode: :compat, time_format: :ruby }
+rescue LoadError
+  require 'json'
+end
 
 module RablRails
   extend Renderer
 
-  autoload :Responder, 'rabl-rails/responder'
-
-  mattr_accessor :cache_templates
-  @@cache_templates = true
-
-  mattr_accessor :include_json_root
-  @@include_json_root = true
-
-  mattr_accessor :use_custom_responder
-  @@use_custom_responder = false
-
-  mattr_accessor :responder_default_template
-  @@responder_default_template = 'show'
-
-  mattr_reader :plist_engine
-  @@plist_engine = nil
-
-  mattr_accessor :include_plist_root
-  @@include_plist_root = nil
-
-  mattr_accessor :enable_jsonp_callbacks
-  @@enable_jsonp_callbacks = false
-
-  mattr_accessor :allow_empty_format_in_template
-  @@allow_empty_format_in_template = false
-
-  mattr_accessor :xml_options
-  @@xml_options = { dasherize: true, skip_types: false }
-
-  def self.configure
-    yield self
-
-    ActionController::Base.responder = Responder if self.use_custom_responder
-  end
-
-  def self.json_engine=(name)
-    MultiJson.engine = name
-  rescue LoadError
-    Rails.logger.warn %Q(WARNING: rabl-rails could not load "#{name}" as JSON engine, fallback to default)
-  end
-
-  def self.json_engine
-    MultiJson.engine
-  end
-
-  def self.xml_engine=(name)
-    ActiveSupport::XmlMini.backend = name
-  rescue LoadError, NameError
-    Rails.logger.warn %Q(WARNING: rabl-rails could not load "#{name}" as XML engine, fallback to default)
-  end
-
-  def self.xml_engine
-    ActiveSupport::XmlMini.backend
-  end
-
-  def self.plist_engine=(name)
-    raise "Your plist engine does not respond to #dump" unless name.respond_to?(:dump)
-    @@plist_engine = name
-  end
-
-  def self.cache_templates?
-    ActionController::Base.perform_caching && @@cache_templates
-  end
+  class << self
+    def configure
+      yield configuration
+    end
 
-  def self.load_default_engines!
-    self.json_engine  = MultiJson.default_engine
-    self.plist_engine = Plist::Emit if defined?(Plist)
+    def configuration
+      @_configuration ||= Configuration.new
+    end
 
-    if defined?(LibXML)
-      self.xml_engine = 'LibXML'
-    elsif defined?(Nokogiri)
-      self.xml_engine = 'Nokogiri'
+    def reset_configuration
+      @_configuration = nil
     end
   end
 end
diff --git a/lib/rabl-rails/compiler.rb b/lib/rabl-rails/compiler.rb
index e038ff0..5924515 100644
--- a/lib/rabl-rails/compiler.rb
+++ b/lib/rabl-rails/compiler.rb
@@ -4,8 +4,8 @@ module RablRails
   # representing data structure
   #
   class Compiler
-    def initialize
-      @i = -1
+    def initialize(view)
+      @view = view
     end
 
     #
@@ -41,15 +41,19 @@ module RablRails
     #   attribute :email => :super_secret
     #
     def attribute(*args)
+      node = Nodes::Attribute.new
+
       if args.first.is_a?(Hash)
-        args.first.each_pair { |k, v| @template[v] = k }
+        args.first.each_pair { |k, v| node[v] = k }
       else
         options = args.extract_options!
         args.each { |name|
           key = options[:as] || name
-          @template[key] = name
+          node[key] = name
         }
       end
+
+      @template.add_node node
     end
     alias_method :attributes, :attribute
 
@@ -66,12 +70,14 @@ module RablRails
       data, name = extract_data_and_name(name_or_data)
       name = options[:root] if options.has_key? :root
 
-      @template[name] = if options[:partial]
-        template = Library.instance.compile_template_from_path(options[:partial])
-        template.merge!(:_data => data)
+      if options.key?(:partial)
+        template = Library.instance.compile_template_from_path(options[:partial], @view)
+        template.data = data
       elsif block_given?
-        sub_compile(data) { yield }
+        template = sub_compile(data) { yield }
       end
+
+      @template.add_node Nodes::Child.new(name, template)
     end
 
     #
@@ -81,7 +87,9 @@ module RablRails
     #
     def glue(data)
       return unless block_given?
-      @template[sequence('glue')] = sub_compile(data) { yield }
+
+      template = sub_compile(data) { yield }
+      @template.add_node Nodes::Glue.new(template)
     end
 
     #
@@ -93,18 +101,8 @@ module RablRails
     #   node(:role, if: ->(u) { !u.admin? }) { |u| u.role }
     #
     def node(name = nil, options = {}, &block)
-      name ||= sequence('merge')
-      condition = options[:if]
-
-      if condition
-        if condition.is_a?(Proc)
-          @template[name] = [condition, block]
-        else
-          @template[name] = block if condition
-        end
-      else
-        @template[name] = block
-      end
+      return unless block_given?
+      @template.add_node Nodes::Code.new(name, block, options[:if])
     end
     alias_method :code, :node
 
@@ -114,9 +112,9 @@ module RablRails
     # Example:
     #   merge { |item| partial("specific/#{item.to_s}", object: item) }
     #
-    def merge(&block)
+    def merge
       return unless block_given?
-      node(sequence('merge'), &block)
+      node(nil) { yield }
     end
 
     #
@@ -125,8 +123,7 @@ module RablRails
     #   extends 'users/base'
     #
     def extends(path)
-      t = Library.instance.compile_template_from_path(path)
-      @template.merge!(t.source)
+      @template.extends Library.instance.compile_template_from_path(path, @view)
     end
 
     #
@@ -138,7 +135,7 @@ module RablRails
     #
     def condition(proc)
       return unless block_given?
-      @template[sequence('if')] = Condition.new(proc, sub_compile(nil) { yield })
+      @template.add_node Nodes::Condition.new(proc, sub_compile(nil, true) { yield })
     end
 
     def cache(&block)
@@ -148,14 +145,6 @@ module RablRails
     protected
 
     #
-    # Return unique symbol starting with given name
-    #
-    def sequence(name)
-      @i += 1
-      :"_#{name}#{@i}"
-    end
-
-    #
     # Extract data root_name and root name
     # Example:
     #   :@users -> [:@users, nil]
@@ -173,11 +162,12 @@ module RablRails
       end
     end
 
-    def sub_compile(data)
-      return {} unless block_given?
-      old_template, @template = @template, {}
+    def sub_compile(data, only_nodes = false)
+      raise unless block_given?
+      old_template, @template = @template, CompiledTemplate.new
       yield
-      data ? @template.merge!(:_data => data) : @template
+      @template.data = data
+      only_nodes ? @template.nodes : @template
     ensure
       @template = old_template
     end
diff --git a/lib/rabl-rails/condition.rb b/lib/rabl-rails/condition.rb
deleted file mode 100644
index 68cc5b3..0000000
--- a/lib/rabl-rails/condition.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module RablRails
-  class Condition
-    attr_reader :proc, :source
-
-    def initialize(proc, source)
-      @proc = proc
-      @source = source
-    end
-  end
-end
\ No newline at end of file
diff --git a/lib/rabl-rails/configuration.rb b/lib/rabl-rails/configuration.rb
new file mode 100644
index 0000000..51d0f71
--- /dev/null
+++ b/lib/rabl-rails/configuration.rb
@@ -0,0 +1,48 @@
+module RablRails
+  class Configuration
+    attr_accessor :json_engine, :include_json_root, :enable_jsonp_callbacks
+    attr_accessor :xml_options
+    attr_accessor :plist_engine, :include_plist_root
+    attr_accessor :cache_templates
+    attr_reader   :use_custom_responder
+    attr_accessor :responder_default_template
+    attr_accessor :replace_nil_values_with_empty_strings
+    attr_accessor :replace_empty_string_values_with_nil
+    attr_accessor :exclude_nil_values
+
+    def initialize
+      @json_engine            = defined?(::Oj) ? ::Oj : ::JSON
+      @include_json_root      = true
+      @enable_jsonp_callbacks = false
+
+      @xml_options = { dasherize: true, skip_types: false }
+
+      @plist_engine       = defined?(::Plist) ? ::Plist::Emit : nil
+      @include_plist_root = false
+
+      @cache_templates                = ActionController::Base.perform_caching
+
+      @use_custom_responder       = false
+      @responder_default_template = 'show'
+
+      @replace_nil_values_with_empty_strings  = false
+      @replace_empty_string_values_with_nil   = false
+      @exclude_nil_values                     = false
+    end
+
+    def use_custom_responder=(value)
+      @use_custom_responder = value
+      require 'rabl-rails/responder' if value
+    end
+
+    def result_flags
+      @result_flags ||= begin
+        result = 0
+        result |= 0b01  if @replace_nil_values_with_empty_strings
+        result |= 0b10  if @replace_empty_string_values_with_nil
+        result |= 0b100 if @exclude_nil_values
+        result
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/handler.rb b/lib/rabl-rails/handler.rb
index 6b1d9ab..37e45c7 100644
--- a/lib/rabl-rails/handler.rb
+++ b/lib/rabl-rails/handler.rb
@@ -1,7 +1,9 @@
+require 'active_support/core_ext/class/attribute'
+
 module RablRails
   module Handlers
     class Rabl
-      cattr_accessor :default_format
+      class_attribute :default_format
       self.default_format = 'application/json'
 
       def self.call(template)
diff --git a/lib/rabl-rails/helpers.rb b/lib/rabl-rails/helpers.rb
new file mode 100644
index 0000000..8948909
--- /dev/null
+++ b/lib/rabl-rails/helpers.rb
@@ -0,0 +1,7 @@
+module RablRails
+  module Helpers
+    def collection?(resource)
+      resource && resource.respond_to?(:each) && !resource.is_a?(Struct)
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/library.rb b/lib/rabl-rails/library.rb
index 54d15f4..ddd2419 100644
--- a/lib/rabl-rails/library.rb
+++ b/lib/rabl-rails/library.rb
@@ -1,37 +1,64 @@
 require 'singleton'
+require 'monitor'
+require 'thread_safe'
 
 module RablRails
   class Library
     include Singleton
 
     def initialize
-      @cached_templates = {}
+      @cached_templates = ThreadSafe::Cache.new
+      @mutex = Monitor.new
     end
 
-    def get_rendered_template(source, context, locals = nil)
-      path = context.instance_variable_get(:@virtual_path)
-      @lookup_context = context.lookup_context
+    def reset_cache!
+      @cached_templates = ThreadSafe::Cache.new
+    end
 
-      compiled_template = compile_template_from_source(source, path)
+    def get_rendered_template(source, view, locals = nil)
+      compiled_template = compile_template_from_source(source, view)
+      format = view.params[:format] ? view.params[:format].to_s.upcase : :JSON
+      Renderers.const_get(format).render(compiled_template, view, locals)
+    end
 
-      format = context.params[:format] ? context.params[:format].to_s.upcase : :JSON
-      Renderers.const_get(format).new(context, locals).render(compiled_template)
+    def compile_template_from_source(source, view)
+      if RablRails.configuration.cache_templates
+        path = view.instance_variable_get(:@virtual_path)
+        synchronized_compile(path, source, view)
+      else
+        compile(source, view)
+      end
     end
 
-    def compile_template_from_source(source, path = nil)
-      if path && RablRails.cache_templates?
-        @cached_templates[path] ||= Compiler.new.compile_source(source)
-        @cached_templates[path].dup
+    def compile_template_from_path(path, view)
+      if RablRails.configuration.cache_templates
+        synchronized_compile(path, nil, view)
       else
-        Compiler.new.compile_source(source)
+        source = fetch_source(path, view)
+        compile(source, view)
       end
     end
 
-    def compile_template_from_path(path)
-      return @cached_templates[path].dup if @cached_templates.has_key?(path)
+    private
+
+    def synchronized_compile(path, source, view)
+      @cached_templates[path] || @mutex.synchronize do
+        # Any thread holding this lock will be compiling the template needed
+        # by the threads waiting. So re-check the template presence to avoid
+        # re-compilation
+        @cached_templates.fetch(path) do
+          source ||= fetch_source(path, view)
+          @cached_templates[path] = compile(source, view)
+        end
+      end
+    end
+
+    def compile(source, view)
+      Compiler.new(view).compile_source(source)
+    end
 
-      t = @lookup_context.find_template(path, [], false)
-      compile_template_from_source(t.source, path)
+    def fetch_source(path, view)
+      view.lookup_context.find_template(path, [], false).source
     end
   end
 end
\ No newline at end of file
diff --git a/lib/rabl-rails/nodes.rb b/lib/rabl-rails/nodes.rb
new file mode 100644
index 0000000..68b0c3f
--- /dev/null
+++ b/lib/rabl-rails/nodes.rb
@@ -0,0 +1,6 @@
+require 'rabl-rails/nodes/node'
+require 'rabl-rails/nodes/attribute'
+require 'rabl-rails/nodes/glue'
+require 'rabl-rails/nodes/child'
+require 'rabl-rails/nodes/code'
+require 'rabl-rails/nodes/condition'
diff --git a/lib/rabl-rails/nodes/attribute.rb b/lib/rabl-rails/nodes/attribute.rb
new file mode 100644
index 0000000..5393942
--- /dev/null
+++ b/lib/rabl-rails/nodes/attribute.rb
@@ -0,0 +1,17 @@
+require 'forwardable'
+
+module RablRails
+  module Nodes
+    class Attribute
+      include Node
+      extend Forwardable
+
+      def_delegators :@hash, :[]=, :each
+      attr_reader :hash
+
+      def initialize(hash = {})
+        @hash = hash
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/nodes/child.rb b/lib/rabl-rails/nodes/child.rb
new file mode 100644
index 0000000..a15088e
--- /dev/null
+++ b/lib/rabl-rails/nodes/child.rb
@@ -0,0 +1,12 @@
+module RablRails
+  module Nodes
+    class Child < Glue
+      attr_reader :name
+
+      def initialize(name, template)
+        super(template)
+        @name = name
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/nodes/code.rb b/lib/rabl-rails/nodes/code.rb
new file mode 100644
index 0000000..713171d
--- /dev/null
+++ b/lib/rabl-rails/nodes/code.rb
@@ -0,0 +1,19 @@
+module RablRails
+  module Nodes
+    class Code
+      include Node
+
+      attr_reader :name, :block, :condition
+
+      def initialize(name, block, condition = nil)
+        @name = name
+        @block = block
+        @condition = condition
+      end
+
+      def merge?
+        !name
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/nodes/condition.rb b/lib/rabl-rails/nodes/condition.rb
new file mode 100644
index 0000000..7243b88
--- /dev/null
+++ b/lib/rabl-rails/nodes/condition.rb
@@ -0,0 +1,14 @@
+module RablRails
+  module Nodes
+    class Condition
+      include Node
+
+      attr_reader :condition, :nodes
+
+      def initialize(condition, nodes)
+        @condition = condition
+        @nodes = nodes
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/nodes/glue.rb b/lib/rabl-rails/nodes/glue.rb
new file mode 100644
index 0000000..f83b5a9
--- /dev/null
+++ b/lib/rabl-rails/nodes/glue.rb
@@ -0,0 +1,25 @@
+module RablRails
+  module Nodes
+    class Glue
+      include Node
+
+      attr_reader :template
+
+      def initialize(template)
+        @template = template
+      end
+
+      def data
+        @template.data
+      end
+
+      def nodes
+        @template.nodes
+      end
+
+      def instance_variable_data?
+        @instance_variable_data ||= data.to_s.start_with?('@')
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/nodes/node.rb b/lib/rabl-rails/nodes/node.rb
new file mode 100644
index 0000000..f15d41b
--- /dev/null
+++ b/lib/rabl-rails/nodes/node.rb
@@ -0,0 +1,9 @@
+module RablRails
+  module Nodes
+    module Node
+      def accept(visitor)
+        visitor.visit(self)
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/railtie.rb b/lib/rabl-rails/railtie.rb
index eee7e22..207bd61 100644
--- a/lib/rabl-rails/railtie.rb
+++ b/lib/rabl-rails/railtie.rb
@@ -1,8 +1,6 @@
 module RablRails
   class Railtie < Rails::Railtie
     initializer "rabl.initialize" do |app|
-      RablRails.load_default_engines!
-      
       ActiveSupport.on_load(:action_view) do
         ActionView::Template.register_template_handler :rabl, RablRails::Handlers::Rabl
       end
diff --git a/lib/rabl-rails/renderer.rb b/lib/rabl-rails/renderer.rb
index 059b281..2791be4 100644
--- a/lib/rabl-rails/renderer.rb
+++ b/lib/rabl-rails/renderer.rb
@@ -1,4 +1,4 @@
-require 'rabl-rails/renderers/base'
+require 'rabl-rails/renderers/hash'
 require 'rabl-rails/renderers/json'
 require 'rabl-rails/renderers/xml'
 require 'rabl-rails/renderers/plist'
@@ -6,16 +6,14 @@ require 'rabl-rails/renderers/plist'
 module RablRails
   module Renderer
     class TemplateNotFound < StandardError; end
-
-    mattr_reader :view_path
-    @@view_path = 'app/views'
+    class PartialError < StandardError; end
 
     class LookupContext
       T = Struct.new(:source)
 
       def initialize(view_path, format)
-        @view_path = view_path || RablRails::Renderer.view_path
-        @extension = format ? ".#{format.to_s.downcase}.rabl" : ".rabl"
+        @view_path = view_path || 'app/views'
+        @format = format.downcase
       end
 
       #
@@ -24,8 +22,14 @@ module RablRails
       # path is used
       #
       def find_template(name, opt, partial = false)
-        path = File.join(@view_path, "#{name}#{@extension}")
-        File.exists?(path) ? T.new(File.read(path)) : nil
+        paths = Dir["#@view_path/#{name}{.#@format,}.rabl"]
+        file_path = paths.find { |path| File.exists?(path) }
+
+        if file_path
+          T.new(File.read(file_path))
+        else
+          raise TemplateNotFound
+        end
       end
     end
 
@@ -33,12 +37,12 @@ module RablRails
     # Context class to emulate normal Rails view
     # context
     #
-    class Context
+    class ViewContext
       attr_reader :format
 
       def initialize(path, options)
         @virtual_path = path
-        @format = options.delete(:format) || (RablRails.allow_empty_format_in_template ? nil : 'json')
+        @format = options.delete(:format) || 'json'
         @_assigns = {}
         @options = options
 
@@ -80,11 +84,9 @@ module RablRails
     def render(object, template, options = {})
       object = options[:locals].delete(:object) if !object && options[:locals]
 
-      c = Context.new(template, options)
+      c = ViewContext.new(template, options)
       t = c.lookup_context.find_template(template, [], false)
 
-      raise TemplateNotFound unless t
-
       Library.instance.get_rendered_template(t.source, c, resource: object)
     end
   end
diff --git a/lib/rabl-rails/renderers/base.rb b/lib/rabl-rails/renderers/base.rb
deleted file mode 100644
index 46a5264..0000000
--- a/lib/rabl-rails/renderers/base.rb
+++ /dev/null
@@ -1,171 +0,0 @@
-module RablRails
-  module Renderers
-    class PartialError < StandardError; end
-
-    class Base
-      attr_accessor :_options
-
-      def initialize(context, locals = nil) # :nodoc:
-        @_context = context
-        @_options = {}
-        @_resource = locals[:resource] if locals
-        setup_render_context
-      end
-
-      #
-      # Render a template.
-      # Uses the compiled template source to get a hash with the actual
-      # data and then format the result according to the `format_result`
-      # method defined by the renderer.
-      #
-      def render(template)
-        collection_or_resource = if template.data
-          if @_context.respond_to?(template.data)
-            @_context.send(template.data)
-          else
-            instance_variable_get(template.data)
-          end
-        end
-        collection_or_resource ||= @_resource
-
-        render_with_cache(template.cache_key, collection_or_resource) do
-          output_hash = collection_or_resource.respond_to?(:each) ? render_collection(collection_or_resource, template.source)
-                                                                  : render_resource(collection_or_resource, template.source)
-          _options[:root_name] = template.root_name
-          format_output(output_hash)
-        end
-      end
-
-      #
-      # Format a hash into the desired output.
-      # Renderer subclasses must implement this method
-      #
-      def format_output(hash)
-        raise "Muse be implemented by renderer"
-      end
-
-      protected
-
-      def render_with_cache(key, collection_or_resource, &block)
-        if !key.is_a?(FalseClass) && ActionController::Base.perform_caching
-          Rails.cache.fetch(resolve_cache_key(key, collection_or_resource)) do
-            yield
-          end
-        else
-          yield
-        end
-      end
-
-      #
-      # Render a single resource as a hash, according to the compiled
-      # template source passed.
-      #
-      def render_resource(data, source)
-        source.inject({}) { |output, (key, value)|
-
-          out = case value
-          when Symbol
-            data.send(value) # attributes
-          when Proc
-            result = instance_exec data, &value
-
-            if key.to_s.start_with?('_') # merge
-              raise PartialError, '`merge` block should return a hash' unless result.is_a?(Hash)
-              output.merge!(result)
-              next output
-            else # node
-              result
-            end
-          when Array # node with condition
-            next output if !instance_exec data, &(value.first)
-            instance_exec data, &(value.last)
-          when Hash
-            current_value = value.dup
-            object = object_from_data(data, current_value.delete(:_data))
-
-            if key.to_s.start_with?('_') # glue
-              output.merge!(render_resource(object, current_value))
-              next output
-            else # child
-              if object
-                object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value)
-              else
-                nil
-              end
-            end
-          when Condition
-            if instance_exec data, &(value.proc)
-              output.merge!(render_resource(data, value.source))
-            end
-            next output
-          end
-          output[key] = out
-          output
-        }
-      end
-
-      def params
-        @_context.params
-      end
-
-      #
-      # Call the render_resource mtehod on each object of the collection
-      # and return an array of the returned values.
-      #
-      def render_collection(collection, source)
-        collection.map { |o| render_resource(o, source) }
-      end
-
-      #
-      # Allow to use partial inside of node blocks (they are evaluated at)
-      # rendering time.
-      #
-      def partial(template_path, options = {})
-        raise PartialError.new("No object was given to partial #{template_path}") unless options[:object]
-        object = options[:object]
-
-        return [] if object.respond_to?(:empty?) && object.empty?
-
-        template = Library.instance.compile_template_from_path(template_path)
-        object.respond_to?(:each) ? render_collection(object, template.source) : render_resource(object, template.source)
-      end
-
-      #
-      # If a method is called inside a 'node' property or a 'if' lambda
-      # it will be passed to context if it exists or treated as a standard
-      # missing method.
-      #
-      def method_missing(name, *args, &block)
-        @_context.respond_to?(name) ? @_context.send(name, *args, &block) : super
-      end
-
-      def resolve_cache_key(key, data)
-        return data.cache_key unless key
-        key.is_a?(Proc) ? instance_exec(data, &key) : key
-      end
-
-      private
-
-      def object_from_data(data, symbol)
-        return data if symbol == nil
-
-        if symbol.to_s.start_with?('@')
-          instance_variable_get(symbol)
-        else
-          data.respond_to?(symbol) ? data.send(symbol) : send(symbol)
-        end
-      end
-
-      #
-      # Copy assigns from controller's context into this
-      # renderer context to include instances variables when
-      # evaluating 'node' properties.
-      #
-      def setup_render_context
-        @_context.instance_variable_get(:@_assigns).each_pair { |k, v|
-          instance_variable_set("@#{k}", v) unless k.to_s.start_with?('_')
-        }
-      end
-    end
-  end
-end
\ No newline at end of file
diff --git a/lib/rabl-rails/renderers/hash.rb b/lib/rabl-rails/renderers/hash.rb
new file mode 100644
index 0000000..f271f68
--- /dev/null
+++ b/lib/rabl-rails/renderers/hash.rb
@@ -0,0 +1,85 @@
+module RablRails
+  module Renderers
+    module Hash
+      include ::RablRails::Helpers
+      extend self
+
+      #
+      # Render a template.
+      # Uses the compiled template source to get a hash with the actual
+      # data and then format the result according to the `format_result`
+      # method defined by the renderer.
+      #
+      def render(template, context, locals = nil)
+        visitor = Visitors::ToHash.new(context)
+
+        collection_or_resource = if template.data
+          if context.respond_to?(template.data)
+            context.send(template.data)
+          else
+            visitor.instance_variable_get(template.data)
+          end
+        end
+        collection_or_resource ||= locals[:resource] if locals
+
+        render_with_cache(template.cache_key, collection_or_resource) do
+          output_hash = if collection?(collection_or_resource)
+            render_collection(collection_or_resource, template.nodes, visitor)
+          else
+            render_resource(collection_or_resource, template.nodes, visitor)
+          end
+
+          format_output(output_hash, root_name: template.root_name, params: context.params)
+        end
+      end
+
+      protected
+
+      #
+      # Format a hash into the desired output.
+      # Renderer subclasses must implement this method
+      #
+      def format_output(hash, options = {})
+        hash = { options[:root_name] => hash } if options[:root_name]
+        hash
+      end
+
+      private
+
+      #
+      # Render a single resource as a hash, according to the compiled
+      # template source passed.
+      #
+      def render_resource(resource, nodes, visitor)
+        visitor.reset_for resource
+        visitor.visit nodes
+        visitor.result
+      end
+
+      #
+      # Call the render_resource mtehod on each object of the collection
+      # and return an array of the returned values.
+      #
+      def render_collection(collection, nodes, visitor)
+        collection.map { |o| render_resource(o, nodes, visitor) }
+      end
+
+      def resolve_cache_key(key, data)
+        return data.cache_key unless key
+        key.is_a?(Proc) ? instance_exec(data, &key) : key
+      end
+
+      private
+
+      def render_with_cache(key, collection_or_resource)
+        if !key.is_a?(FalseClass) && ActionController::Base.perform_caching
+          Rails.cache.fetch(resolve_cache_key(key, collection_or_resource)) do
+            yield
+          end
+        else
+          yield
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/renderers/json.rb b/lib/rabl-rails/renderers/json.rb
index 7bf5630..f5f66a3 100644
--- a/lib/rabl-rails/renderers/json.rb
+++ b/lib/rabl-rails/renderers/json.rb
@@ -1,11 +1,15 @@
 module RablRails
   module Renderers
-    class JSON < Base
-      def format_output(hash)
-        hash = { _options[:root_name] => hash } if _options[:root_name] && RablRails.include_json_root
-        json = MultiJson.encode(hash)
+    module JSON
+      include Renderers::Hash
+      extend self
 
-        RablRails.enable_jsonp_callbacks && params.has_key?(:callback) ? "#{params[:callback]}(#{json})" : json
+      def format_output(hash, options = {})
+        hash = { options[:root_name] => hash } if options[:root_name] && RablRails.configuration.include_json_root
+        json = RablRails.configuration.json_engine.dump(hash)
+        params = options.fetch(:params, {})
+
+        RablRails.configuration.enable_jsonp_callbacks && params.has_key?(:callback) ? "#{params[:callback]}(#{json})" : json
       end
 
       def resolve_cache_key(key, data)
diff --git a/lib/rabl-rails/renderers/plist.rb b/lib/rabl-rails/renderers/plist.rb
index 9bbca51..b4db84a 100644
--- a/lib/rabl-rails/renderers/plist.rb
+++ b/lib/rabl-rails/renderers/plist.rb
@@ -1,10 +1,12 @@
 module RablRails
   module Renderers
-    class PLIST < Base
+    module PLIST
+      include Renderers::Hash
+      extend self
 
-      def format_output(hash)
-        hash = { _options[:root_name] => hash } if _options[:root_name] && RablRails.include_plist_root
-        RablRails.plist_engine.dump(hash)
+      def format_output(hash, options = {})
+        hash = { options[:root_name] => hash } if options[:root_name] && RablRails.configuration.include_plist_root
+        RablRails.configuration.plist_engine.dump(hash)
       end
 
       def resolve_cache_key(key, data)
diff --git a/lib/rabl-rails/renderers/xml.rb b/lib/rabl-rails/renderers/xml.rb
index 4eee1d3..c21d5e8 100644
--- a/lib/rabl-rails/renderers/xml.rb
+++ b/lib/rabl-rails/renderers/xml.rb
@@ -2,9 +2,12 @@ require 'active_support/core_ext/hash/conversions'
 
 module RablRails
   module Renderers
-    class XML < Base
-      def format_output(hash)
-      	xml_options = { root: _options[:root_name] }.merge!(RablRails.xml_options)
+    module XML
+      include Renderers::Hash
+      extend self
+
+      def format_output(hash, options = {})
+      	xml_options = { root: options[:root_name] }.merge!(RablRails.configuration.xml_options)
 				hash.to_xml(xml_options)
       end
 
diff --git a/lib/rabl-rails/responder.rb b/lib/rabl-rails/responder.rb
index 13bd510..8b94a79 100644
--- a/lib/rabl-rails/responder.rb
+++ b/lib/rabl-rails/responder.rb
@@ -31,7 +31,7 @@ module RablRails
         template = if controller.respond_to?(:responder_default_template, true)
           controller.send(:responder_default_template)
         else
-          RablRails.responder_default_template
+          RablRails.configuration.responder_default_template
         end
         options[:prefixes] = controller._prefixes
         options[:template] ||= template
diff --git a/lib/rabl-rails/template.rb b/lib/rabl-rails/template.rb
index ecdc23f..0e7cd7c 100644
--- a/lib/rabl-rails/template.rb
+++ b/lib/rabl-rails/template.rb
@@ -1,17 +1,23 @@
 module RablRails
   class CompiledTemplate
-    attr_accessor :source, :data, :root_name, :cache_key
-
-    delegate :[], :[]=, :merge!, :to => :source
+    attr_accessor :nodes, :data, :root_name, :cache_key
 
     def initialize
-      @source = {}
+      @nodes = []
       @cache_key = false
     end
 
     def initialize_dup(other)
       super
-      self.source = other.source.dup
+      self.nodes = other.nodes.dup
+    end
+
+    def add_node(n)
+      @nodes << n
+    end
+
+    def extends(template)
+      @nodes.concat template.nodes
     end
   end
 end
\ No newline at end of file
diff --git a/lib/rabl-rails/version.rb b/lib/rabl-rails/version.rb
index 4890fa4..c9b814c 100644
--- a/lib/rabl-rails/version.rb
+++ b/lib/rabl-rails/version.rb
@@ -1,3 +1,3 @@
 module RablRails
-  VERSION = '0.3.4'
+  VERSION = '0.4.0'
 end
diff --git a/lib/rabl-rails/visitors.rb b/lib/rabl-rails/visitors.rb
new file mode 100644
index 0000000..8b1b260
--- /dev/null
+++ b/lib/rabl-rails/visitors.rb
@@ -0,0 +1,2 @@
+require 'rabl-rails/visitors/visitor'
+require 'rabl-rails/visitors/to_hash'
diff --git a/lib/rabl-rails/visitors/to_hash.rb b/lib/rabl-rails/visitors/to_hash.rb
new file mode 100644
index 0000000..4e6fb28
--- /dev/null
+++ b/lib/rabl-rails/visitors/to_hash.rb
@@ -0,0 +1,131 @@
+module Visitors
+  class ToHash < Visitor
+    include RablRails::Helpers
+
+    attr_reader :_resource
+
+    def initialize(view_context, resource = nil)
+      @_context  = view_context
+      @_result   = {}
+      @_resource = resource
+
+      copy_instance_variables_from_context
+    end
+
+    def reset_for(resource)
+      @_resource = resource
+      @_result = {}
+    end
+
+    def visit_Array n
+      n.each { |i| visit i }
+    end
+
+    def visit_Attribute n
+      n.each { |k, v| @_result[k] = _resource.send(v) }
+    end
+
+    def visit_Child n
+      object = object_from_data(_resource, n.data, n.instance_variable_data?)
+
+      @_result[n.name] = if object
+        collection?(object) ? object.map { |o| sub_visit(o, n.nodes) } : sub_visit(object, n.nodes)
+      else
+        nil
+      end
+    end
+
+    def visit_Code n
+      if !n.condition || instance_exec(_resource, &(n.condition))
+        result = instance_exec _resource, &(n.block)
+
+        if n.merge?
+          raise RablRails::Renderer::PartialError, '`merge` block should return a hash' unless result.is_a?(Hash)
+          @_result.merge!(result)
+        else
+          @_result[n.name] = result
+        end
+      end
+    end
+
+    def visit_Condition n
+      @_result.merge!(sub_visit(_resource, n.nodes)) if instance_exec _resource, &(n.condition)
+    end
+
+    def visit_Glue n
+      object = object_from_data(_resource, n.data, n.instance_variable_data?)
+      @_result.merge! sub_visit(object, n.template.nodes)
+    end
+
+    def result
+      case RablRails.configuration.result_flags
+      when 0
+        @_result
+      when 1
+        @_result.each { |k, v| @_result[k] = '' if v == nil }
+      when 2, 3
+        @_result.each { |k, v| @_result[k] = nil if v == '' }
+      when 4, 5
+        @_result.delete_if { |_, v| v == nil }
+      when 6
+        @_result.delete_if { |_, v| v == nil || v == '' }
+      end
+    end
+
+    protected
+
+    #
+    # If a method is called inside a 'node' property or a 'if' lambda
+    # it will be passed to context if it exists or treated as a standard
+    # missing method.
+    #
+    def method_missing(name, *args, &block)
+      @_context.respond_to?(name) ? @_context.send(name, *args, &block) : super
+    end
+
+    #
+    # Allow to use partial inside of node blocks (they are evaluated at
+    # rendering time).
+    #
+    def partial(template_path, options = {})
+      raise RablRails::Renderer::PartialError.new("No object was given to partial #{template_path}") unless options[:object]
+      object = options[:object]
+
+      return [] if object.respond_to?(:empty?) && object.empty?
+
+      template = RablRails::Library.instance.compile_template_from_path(template_path, @_context)
+      if object.respond_to?(:each)
+        object.map { |o| sub_visit o, template.nodes }
+      else
+        sub_visit object, template.nodes
+      end
+    end
+
+    private
+
+    def copy_instance_variables_from_context
+      @_context.instance_variable_get(:@_assigns).each_pair { |k, v|
+        instance_variable_set("@#{k}", v) unless k.to_s.start_with?('_')
+      }
+    end
+
+    def sub_visit(resource, nodes)
+      old_result, old_resource, @_result = @_result, @_resource, {}
+      reset_for resource
+      visit nodes
+      result
+    ensure
+      @_result, @_resource = old_result, old_resource
+    end
+
+    def object_from_data(resource, symbol, is_variable)
+      return resource if symbol == nil
+
+      if is_variable
+        instance_variable_get(symbol)
+      else
+        resource.respond_to?(symbol) ? resource.send(symbol) : @_context.send(symbol)
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/rabl-rails/visitors/visitor.rb b/lib/rabl-rails/visitors/visitor.rb
new file mode 100644
index 0000000..b8a39f2
--- /dev/null
+++ b/lib/rabl-rails/visitors/visitor.rb
@@ -0,0 +1,17 @@
+module Visitors
+  class Visitor
+    def visit(node)
+      dispatch node
+    end
+
+    private
+
+    DISPATCH = Hash.new do |hash, node_class|
+      hash[node_class] = "visit_#{node_class.name.split('::').last}"
+    end
+
+    def dispatch(node)
+      send DISPATCH[node.class], node
+    end
+  end
+end
\ No newline at end of file
diff --git a/metadata.yml b/metadata.yml
index 7879027..c6f89f3 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,83 +1,71 @@
---- !ruby/object:Gem::Specification 
+--- !ruby/object:Gem::Specification
 name: rabl-rails
-version: !ruby/object:Gem::Version 
-  prerelease: false
-  segments: 
-  - 0
-  - 3
-  - 4
-  version: 0.3.4
+version: !ruby/object:Gem::Version
+  version: 0.4.0
+  prerelease: 
 platform: ruby
-authors: 
+authors:
 - Christopher Cocchi-Perrier
 autorequire: 
 bindir: bin
 cert_chain: []
-
-date: 2014-04-02 00:00:00 +02:00
-default_executable: 
-dependencies: 
-- !ruby/object:Gem::Dependency 
+date: 2014-10-12 00:00:00.000000000 Z
+dependencies:
+- !ruby/object:Gem::Dependency
   name: activesupport
-  prerelease: false
-  requirement: &id001 !ruby/object:Gem::Requirement 
-    requirements: 
-    - - ">="
-      - !ruby/object:Gem::Version 
-        segments: 
-        - 3
-        - 1
-        version: "3.1"
+  requirement: !ruby/object:Gem::Requirement
+    none: false
+    requirements:
+    - - ! '>='
+      - !ruby/object:Gem::Version
+        version: '3.1'
   type: :runtime
-  version_requirements: *id001
-- !ruby/object:Gem::Dependency 
-  name: railties
   prerelease: false
-  requirement: &id002 !ruby/object:Gem::Requirement 
-    requirements: 
-    - - ">="
-      - !ruby/object:Gem::Version 
-        segments: 
-        - 3
-        - 1
-        version: "3.1"
+  version_requirements: !ruby/object:Gem::Requirement
+    none: false
+    requirements:
+    - - ! '>='
+      - !ruby/object:Gem::Version
+        version: '3.1'
+- !ruby/object:Gem::Dependency
+  name: railties
+  requirement: !ruby/object:Gem::Requirement
+    none: false
+    requirements:
+    - - ! '>='
+      - !ruby/object:Gem::Version
+        version: '3.1'
   type: :runtime
-  version_requirements: *id002
-- !ruby/object:Gem::Dependency 
-  name: actionpack
   prerelease: false
-  requirement: &id003 !ruby/object:Gem::Requirement 
-    requirements: 
-    - - ">="
-      - !ruby/object:Gem::Version 
-        segments: 
-        - 3
-        - 1
-        version: "3.1"
-  type: :development
-  version_requirements: *id003
-- !ruby/object:Gem::Dependency 
-  name: rspec-mocks
+  version_requirements: !ruby/object:Gem::Requirement
+    none: false
+    requirements:
+    - - ! '>='
+      - !ruby/object:Gem::Version
+        version: '3.1'
+- !ruby/object:Gem::Dependency
+  name: thread_safe
+  requirement: !ruby/object:Gem::Requirement
+    none: false
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: 0.3.1
+  type: :runtime
   prerelease: false
-  requirement: &id004 !ruby/object:Gem::Requirement 
-    requirements: 
-    - - ">="
-      - !ruby/object:Gem::Version 
-        segments: 
-        - 0
-        version: "0"
-  type: :development
-  version_requirements: *id004
+  version_requirements: !ruby/object:Gem::Requirement
+    none: false
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: 0.3.1
 description: Fast Rails 3+ templating system with JSON, XML and PList support
-email: 
+email:
 - cocchi.c at gmail.com
 executables: []
-
 extensions: []
-
 extra_rdoc_files: []
-
-files: 
+files:
 - .gitignore
 - .travis.yml
 - CHANGELOG.md
@@ -87,70 +75,73 @@ files:
 - Rakefile
 - lib/rabl-rails.rb
 - lib/rabl-rails/compiler.rb
-- lib/rabl-rails/condition.rb
+- lib/rabl-rails/configuration.rb
 - lib/rabl-rails/handler.rb
+- lib/rabl-rails/helpers.rb
 - lib/rabl-rails/library.rb
+- lib/rabl-rails/nodes.rb
+- lib/rabl-rails/nodes/attribute.rb
+- lib/rabl-rails/nodes/child.rb
+- lib/rabl-rails/nodes/code.rb
+- lib/rabl-rails/nodes/condition.rb
+- lib/rabl-rails/nodes/glue.rb
+- lib/rabl-rails/nodes/node.rb
 - lib/rabl-rails/railtie.rb
 - lib/rabl-rails/renderer.rb
-- lib/rabl-rails/renderers/base.rb
+- lib/rabl-rails/renderers/hash.rb
 - lib/rabl-rails/renderers/json.rb
 - lib/rabl-rails/renderers/plist.rb
 - lib/rabl-rails/renderers/xml.rb
 - lib/rabl-rails/responder.rb
 - lib/rabl-rails/template.rb
 - lib/rabl-rails/version.rb
+- lib/rabl-rails/visitors.rb
+- lib/rabl-rails/visitors/to_hash.rb
+- lib/rabl-rails/visitors/visitor.rb
 - lib/tasks/rabl-rails.rake
 - rabl-rails.gemspec
-- test/base_renderer_test.rb
-- test/cache_templates_test.rb
-- test/compiler_test.rb
-- test/deep_nesting_test.rb
-- test/keyword_test.rb
-- test/non_restful_response_test.rb
-- test/render_test.rb
-- test/renderers/json_renderer_test.rb
-- test/renderers/plist_renderer_test.rb
-- test/renderers/xml_renderer_test.rb
-- test/test_helper.rb
-has_rdoc: true
+- test/helper.rb
+- test/renderers/test_hash_renderer.rb
+- test/renderers/test_json_renderer.rb
+- test/renderers/test_plist_renderer.rb
+- test/renderers/test_xml_renderer.rb
+- test/test_compiler.rb
+- test/test_configuration.rb
+- test/test_hash_visitor.rb
+- test/test_library.rb
+- test/test_render.rb
 homepage: https://github.com/ccocchi/rabl-rails
 licenses: []
-
 post_install_message: 
 rdoc_options: []
-
-require_paths: 
+require_paths:
 - lib
-required_ruby_version: !ruby/object:Gem::Requirement 
-  requirements: 
-  - - ">="
-    - !ruby/object:Gem::Version 
-      segments: 
-      - 0
-      version: "0"
-required_rubygems_version: !ruby/object:Gem::Requirement 
-  requirements: 
-  - - ">="
-    - !ruby/object:Gem::Version 
-      segments: 
-      - 0
-      version: "0"
+required_ruby_version: !ruby/object:Gem::Requirement
+  none: false
+  requirements:
+  - - ! '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+required_rubygems_version: !ruby/object:Gem::Requirement
+  none: false
+  requirements:
+  - - ! '>='
+    - !ruby/object:Gem::Version
+      version: '0'
 requirements: []
-
 rubyforge_project: 
-rubygems_version: 1.3.6
+rubygems_version: 1.8.23
 signing_key: 
 specification_version: 3
 summary: Fast Rails 3+ templating system with JSON, XML and PList support
-test_files: 
-- test/base_renderer_test.rb
-- test/cache_templates_test.rb
-- test/compiler_test.rb
-- test/deep_nesting_test.rb
-- test/keyword_test.rb
-- test/non_restful_response_test.rb
-- test/render_test.rb
-- test/renderers/json_renderer_test.rb
-- test/renderers/plist_renderer_test.rb
-- test/renderers/xml_renderer_test.rb
-- test/test_helper.rb
+test_files:
+- test/helper.rb
+- test/renderers/test_hash_renderer.rb
+- test/renderers/test_json_renderer.rb
+- test/renderers/test_plist_renderer.rb
+- test/renderers/test_xml_renderer.rb
+- test/test_compiler.rb
+- test/test_configuration.rb
+- test/test_hash_visitor.rb
+- test/test_library.rb
+- test/test_render.rb
diff --git a/rabl-rails.gemspec b/rabl-rails.gemspec
index 64ab854..112524d 100644
--- a/rabl-rails.gemspec
+++ b/rabl-rails.gemspec
@@ -15,9 +15,7 @@ Gem::Specification.new do |s|
   s.test_files    = `git ls-files -- test/*`.split("\n")
   s.require_paths = ["lib"]
 
-  s.add_dependency "activesupport", ">= 3.1"
-  s.add_dependency "railties", ">= 3.1"
-
-  s.add_development_dependency "actionpack", ">= 3.1"
-  s.add_development_dependency "rspec-mocks"
+  s.add_dependency 'activesupport', '>= 3.1'
+  s.add_dependency 'railties', '>= 3.1'
+  s.add_dependency 'thread_safe', '~> 0.3.1'
 end
diff --git a/test/base_renderer_test.rb b/test/base_renderer_test.rb
deleted file mode 100644
index c22cdb7..0000000
--- a/test/base_renderer_test.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-require 'test_helper'
-
-class TestBaseRenderer < ActiveSupport::TestCase
-
-  RablRails::Renderers::Base.class_eval do
-    def format_output(hash)
-      hash
-    end
-  end
-
-  setup do
-    @data = User.new(1, 'foobar', 'male')
-
-    @context = Context.new
-    @context.assigns['data'] = @data
-
-    @template = RablRails::CompiledTemplate.new
-    @template.source = {}
-    @template.data = :@data
-
-    @cache = double
-    Rails.stub(:cache).and_return(@cache)
-  end
-
-  def render_hash
-    RablRails::Renderers::Base.new(@context).render(@template)
-  end
-
-  test "child with nil data should render nil" do
-    @template.source = { :author => { :_data => :@nil, :name => :name } }
-    assert_equal({ :author => nil }, render_hash)
-  end
-
-  test "properly handle assigns with symbol keys" do
-    @context.assigns[:foo] = 'bar'
-    assert_nothing_raised { render_hash }
-  end
-
-  test "cache should be applied if no cache key is given" do
-    @cache.should_not_receive(:fetch)
-    render_hash
-  end
-
-  test "cache should not be used if disabled in Rails configuration" do
-    ActionController::Base.stub(:perform_caching).and_return(false)
-    @cache.should_not_receive(:fetch)
-    @template.cache_key = 'something'
-    render_hash
-  end
-
-  test "cache shoud use #cache_key as default" do
-    ActionController::Base.stub(:perform_caching).and_return(true)
-    @data.stub(:cache_key).and_return('data_cache_key')
-    @cache.should_receive(:fetch).with('data_cache_key').and_return({ some: 'hash' })
-    @template.cache_key = nil
-
-    assert_equal({ some: 'hash' }, render_hash)
-  end
-
-  test "cache should use the proc if given" do
-    ActionController::Base.stub(:perform_caching).and_return(true)
-    @template.cache_key = ->(u) { 'proc_cache_key' }
-    @cache.should_receive(:fetch).with('proc_cache_key').and_return({ some: 'hash' })
-
-    assert_equal({ some: 'hash' }, render_hash)
-  end
-end
\ No newline at end of file
diff --git a/test/cache_templates_test.rb b/test/cache_templates_test.rb
deleted file mode 100644
index feb1d58..0000000
--- a/test/cache_templates_test.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'test_helper'
-
-class CacheTemplatesTest < ActiveSupport::TestCase
-
-  setup do
-    RablRails::Library.reset_instance
-    @library = RablRails::Library.instance
-    RablRails.cache_templates = true
-  end
-
-  test "cache templates if perform_caching is active and cache_templates is enabled" do
-    ActionController::Base.stub(:perform_caching).and_return(true)
-    @library.compile_template_from_source('', 'some/path')
-    t = @library.compile_template_from_source("attribute :id", 'some/path')
-
-    assert_equal({}, t.source)
-  end
-
-  test "cached templates should not be modifiable in place" do
-    ActionController::Base.stub(:perform_caching).and_return(true)
-    t = @library.compile_template_from_source('', 'some/path')
-
-    t.merge!(:_data => :foo)
-
-    assert_equal({}, @library.compile_template_from_path('some/path').source)
-  end
-
-  test "don't cache templates cache_templates is enabled but perform_caching is not active" do
-    ActionController::Base.stub(:perform_caching).and_return(false)
-    @library.compile_template_from_source('', 'some/path')
-    t = @library.compile_template_from_source("attribute :id", 'some/path')
-
-    assert_equal({ :id => :id }, t.source)
-  end
-end
\ No newline at end of file
diff --git a/test/compiler_test.rb b/test/compiler_test.rb
deleted file mode 100644
index 4469d10..0000000
--- a/test/compiler_test.rb
+++ /dev/null
@@ -1,233 +0,0 @@
-require 'test_helper'
-
-class CompilerTest < ActiveSupport::TestCase
-
-  setup do
-    @user = User.new
-    @compiler = RablRails::Compiler.new
-  end
-
-  test "compiler return a compiled template" do
-    assert_instance_of RablRails::CompiledTemplate, @compiler.compile_source("")
-  end
-
-  test "object set data for the template" do
-    t = @compiler.compile_source(%{ object :@user })
-    assert_equal :@user, t.data
-    assert_equal({}, t.source)
-  end
-
-  test "object property can define root name" do
-    t = @compiler.compile_source(%{ object :@user => :author })
-    assert_equal :@user, t.data
-    assert_equal :author, t.root_name
-    assert_equal({}, t.source)
-  end
-
-  test "root can be defined via keyword" do
-    t = @compiler.compile_source(%{ root :author })
-    assert_equal :author, t.root_name
-  end
-
-  test "root keyword override object root" do
-    t = @compiler.compile_source(%{ object :@user ; root :author })
-    assert_equal :author, t.root_name
-  end
-
-  test "collection set the data for the template" do
-    t = @compiler.compile_source(%{ collection :@user })
-    assert_equal :@user, t.data
-    assert_equal({}, t.source)
-  end
-
-  test "collection property can define root name" do
-    t = @compiler.compile_source(%{ collection :@user => :users })
-    assert_equal :@user, t.data
-    assert_equal :users, t.root_name
-    assert_equal({}, t.source)
-  end
-
-  test "collection property can define root name via options" do
-    t = @compiler.compile_source(%{ collection :@user, :root => :users })
-    assert_equal :@user, t.data
-    assert_equal :users, t.root_name
-  end
-
-  test "root can be set to false via options" do
-    t = @compiler.compile_source(%( object :@user, root: false))
-    assert_equal false, t.root_name
-  end
-
-  test "template should not have a cache key if cache is not enable" do
-    t = @compiler.compile_source('')
-    assert_equal false, t.cache_key
-  end
-
-  test "cache can take no argument" do
-    t = @compiler.compile_source(%{ cache })
-    assert_nil t.cache_key
-  end
-
-  test "cache can take a block" do
-    t = @compiler.compile_source(%( cache { 'foo' }))
-    assert_instance_of Proc, t.cache_key
-  end
-
-  # Compilation
-
-  test "simple attributes are compiled to hash" do
-    t = @compiler.compile_source(%{ attributes :id, :name })
-    assert_equal({ :id => :id, :name => :name}, t.source)
-  end
-
-  test "attributes appeared only once even if called mutiple times" do
-    t = @compiler.compile_source(%{ attribute :id ; attribute :id })
-    assert_equal({ :id => :id }, t.source)
-  end
-
-  test "attribute can be aliased through :as option" do
-    t = @compiler.compile_source(%{ attribute :foo, :as => :bar })
-    assert_equal({ :bar => :foo}, t.source)
-  end
-
-  test "attribute can be aliased through hash" do
-    t = @compiler.compile_source(%{ attribute :foo => :bar })
-    assert_equal({ :bar => :foo }, t.source)
-  end
-
-  test "multiple attributes can be aliased" do
-    t = @compiler.compile_source(%{ attributes :foo => :bar, :id => :uid })
-    assert_equal({ :bar => :foo, :uid => :id }, t.source)
-  end
-
-  test "child with association use association name as data" do
-    t = @compiler.compile_source(%{ child :address do attributes :foo end})
-    assert_equal({ :address => { :_data => :address, :foo => :foo } }, t.source)
-  end
-
-  test "child with association can be aliased" do
-    t = @compiler.compile_source(%{ child :address => :bar do attributes :foo end})
-    assert_equal({ :bar => { :_data => :address, :foo => :foo } }, t.source)
-  end
-
-  test "child with root name defined as option" do
-    t = @compiler.compile_source(%{ child(:user, :root => :author) do attributes :foo end })
-    assert_equal({ :author => { :_data => :user, :foo => :foo } }, t.source)
-  end
-
-  test "child with arbitrary source store the data with the template" do
-    t = @compiler.compile_source(%{ child :@user => :author do attribute :name end })
-    assert_equal({ :author => { :_data => :@user, :name => :name } }, t.source)
-  end
-
-  test "child with succint partial notation" do
-    mock_template = RablRails::CompiledTemplate.new
-    mock_template.source = { :id => :id }
-    RablRails::Library.reset_instance
-    RablRails::Library.instance.stub(:compile_template_from_path).with('users/base').and_return(mock_template)
-
-    t = @compiler.compile_source(%{child(:user, :partial => 'users/base') })
-    assert_equal({:user => { :_data => :user, :id => :id } }, t.source)
-  end
-
-  test "glue is compiled as a child but with anonymous name" do
-    t = @compiler.compile_source(%{ glue(:@user) do attribute :name end })
-    assert_equal({ :_glue0 => { :_data => :@user, :name => :name } }, t.source)
-  end
-
-  test "multiple glue don't come with name collisions" do
-    t = @compiler.compile_source(%{
-      glue :@user do attribute :name end
-      glue :@user do attribute :foo end
-    })
-
-    assert_equal({
-      :_glue0 => { :_data => :@user, :name => :name},
-      :_glue1 => { :_data => :@user, :foo => :foo}
-    }, t.source)
-  end
-
-  test "glue accepts all dsl in its body" do
-    t = @compiler.compile_source(%{
-      glue :@user do node(:foo) { |u| u.name } end
-    })
-
-    assert_not_nil(t.source[:_glue0])
-    s = t.source[:_glue0]
-
-    assert_equal(:@user, s[:_data])
-    assert_instance_of(Proc, s[:foo])
-  end
-
-  test "extends use other template source as itself" do
-    template = double('template', :source => { :id => :id })
-    RablRails::Library.reset_instance
-    RablRails::Library.instance.stub(:compile_template_from_path).with('users/base').and_return(template)
-    t = @compiler.compile_source(%{ extends 'users/base' })
-    assert_equal({ :id => :id }, t.source)
-  end
-
-  test "extends should not overwrite nodes previously defined" do
-    skip('Bug reported by @abrisse')
-
-    template = mock('file_template', :source => %(condition(-> { true }) { 'foo' }))
-    lookup_context = mock
-    lookup_context.stub(:find_template).with('users/xtnd', [], false).and_return(template)
-    RablRails::Library.reset_instance
-    RablRails::Library.instance.instance_variable_set(:@lookup_context, lookup_context)
-
-    t = @compiler.compile_source(%{
-      condition(-> { false }) { 'bar' }
-      extends('users/xtnd')
-    })
-
-    assert_equal 2, t.source.keys.size
-  end
-
-  test "node are compiled without evaluating the block" do
-    t = @compiler.compile_source(%{ node(:foo) { bar } })
-    assert_not_nil t.source[:foo]
-    assert_instance_of Proc, t.source[:foo]
-  end
-
-  test "node with condition are compiled as an array of procs" do
-    t = @compiler.compile_source(%{ node(:foo, :if => lambda { |m| m.foo.present? }) do |m| m.foo end })
-    assert_not_nil t.source[:foo]
-    assert_instance_of Array, t.source[:foo]
-    assert_equal 2, t.source[:foo].size
-  end
-
-  test "node can take no arguments and behave like a merge" do
-    t = @compiler.compile_source(%{ node do |m| m.foo end })
-    assert_instance_of Proc, t.source[:_merge0]
-  end
-
-  test "merge compile like a node but with a reserved keyword as name" do
-    t = @compiler.compile_source(%{ merge do |m| m.foo end })
-    assert_instance_of Proc, t.source[:_merge0]
-  end
-
-  test "conditionnal block compile nicely" do
-    t = @compiler.compile_source(%{ condition(->(u) {}) do attributes :secret end })
-    assert_instance_of RablRails::Condition, t.source[:_if0]
-    assert_equal({ :secret => :secret }, t.source[:_if0].source)
-  end
-
-  test "compile with no object" do
-    t = @compiler.compile_source(%{
-     object false
-     child(:@user => :user) do
-       attribute :id
-     end
-    })
-
-    assert_equal({ :user => { :_data => :@user, :id => :id } }, t.source)
-    assert_equal false, t.data
-  end
-
-  test "name extraction from argument" do
-    assert_equal [:@users, 'users'], @compiler.send(:extract_data_and_name, :@users)
-    assert_equal [:users, :users], @compiler.send(:extract_data_and_name, :users)
-    assert_equal [:@users, :authors], @compiler.send(:extract_data_and_name, :@users => :authors)
-  end
-end
\ No newline at end of file
diff --git a/test/deep_nesting_test.rb b/test/deep_nesting_test.rb
deleted file mode 100644
index 2cdf636..0000000
--- a/test/deep_nesting_test.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require 'test_helper'
-
-class DeepNestingTest < ActiveSupport::TestCase
-
-  class Post
-    attr_accessor :id, :title
-
-    def initialize(id, title)
-      @id, @title = id, title
-    end
-
-    def comments
-      [Struct.new(:id, :content).new(1, 'first'), Struct.new(:id, :content).new(2, 'second')]
-    end
-  end
-
-  setup do
-    RablRails::Library.reset_instance
-    @post = Post.new(42, 'I rock !')
-    @user = User.new(1, 'foobar', 'male')
-    @user.stub(:posts).and_return([@post])
-
-    @context = Context.new
-    @context.assigns['user'] = @user
-    @context.virtual_path = 'users/show'
-    @context.stub(:lookup_context).and_return(double(:find_template => double(:source => %{ object :@comment\n attribute :content })))
-  end
-
-  test "compile and render deep nesting template" do
-    source = %{
-      object :@user
-      attributes :id, :name
-      child :posts do
-        attribute :title
-        child :comments do
-          extends 'comments/show'
-        end
-      end
-    }
-
-    assert_equal(MultiJson.encode(:user => {
-      :id => 1,
-      :name => 'foobar',
-      :posts => [{
-        :title => 'I rock !',
-        :comments => [
-          { :content => 'first' },
-          { :content => 'second' }
-        ]
-      }]
-    }), RablRails::Library.instance.get_rendered_template(source, @context))
-  end
-end
-
-
-
diff --git a/test/helper.rb b/test/helper.rb
new file mode 100644
index 0000000..caef3d1
--- /dev/null
+++ b/test/helper.rb
@@ -0,0 +1,75 @@
+ENV['RAILS_ENV'] = 'test'
+$:.unshift File.expand_path('../../lib', __FILE__)
+
+# require 'rspec/mocks'
+require 'minitest/mock'
+require 'minitest/autorun'
+
+require 'rabl-rails'
+require 'plist'
+
+if RUBY_ENGINE == 'jruby'
+  require 'nokogiri'
+elsif RUBY_ENGINE == 'ruby'
+  require 'libxml'
+end
+
+MINITEST_TEST_CLASS = if defined?(Minitest::Test)
+  Minitest::Test
+else
+  Minitest::Unit::TestCase
+end
+
+module Configurable
+  def with_configuration(key, value)
+    accessor = "#{key}="
+    old_value = RablRails.configuration.send(key)
+    RablRails.configuration.send(accessor, value)
+    yield
+  ensure
+    RablRails.configuration.send(accessor, old_value)
+  end
+end
+MINITEST_TEST_CLASS.send(:include, Configurable)
+
+module Rails
+  def self.cache
+  end
+end
+
+module ActionController
+  module Base
+    def self.perform_caching
+      false
+    end
+  end
+end
+
+class Context
+  attr_writer :virtual_path
+
+  def initialize
+    @_assigns = {}
+    @virtual_path = nil
+  end
+
+  def assigns
+    @_assigns
+  end
+
+  def params
+    {}
+  end
+
+  def context_method
+  end
+end
+
+class User
+  attr_accessor :id, :name
+
+  def initialize(id = nil, name = nil)
+    @id = id
+    @name = name
+  end
+end
\ No newline at end of file
diff --git a/test/keyword_test.rb b/test/keyword_test.rb
deleted file mode 100644
index d895aa9..0000000
--- a/test/keyword_test.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require 'test_helper'
-
-class KeywordTest < ActiveSupport::TestCase
-  class Collection
-    attr_accessor :id, :name
-
-    def initialize(id, name)
-      @id   = id
-      @name = name
-    end
-
-    def cover(size)
-      "foo_#{size}"
-    end
-  end
-
-  setup do
-    RablRails::Library.reset_instance
-    @context = Context.new
-    @user = User.new(1, 'Marty')
-    @collections = [Collection.new(1, 'first'), Collection.new(2, 'last')]
-    @context.assigns['user'] = @user
-    @context.assigns['collections'] = @collections
-    @context.virtual_path = 'user/show'
-    @context.stub(lookup_context: nil)
-  end
-
-  test "collections model should not collide with rabl-rails reserved keyword" do
-    source = %{
-      object :@user
-      child(:@collections => :collections) do
-        attributes :id, :name
-        node(:cover_url) { |c|
-          c.cover(:medium)
-        }
-      end
-    }
-
-    assert_equal(MultiJson.encode(
-      user: { collections: [{
-        id: 1, name: 'first', cover_url: "foo_medium"
-      }, {
-        id: 2, name: 'last', cover_url: "foo_medium"
-      }] }
-    ), RablRails::Library.instance.get_rendered_template(source, @context))
-  end
-end
\ No newline at end of file
diff --git a/test/non_restful_response_test.rb b/test/non_restful_response_test.rb
deleted file mode 100644
index 5228f15..0000000
--- a/test/non_restful_response_test.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'test_helper'
-
-class NonRestfulResponseTest < ActiveSupport::TestCase
-  setup do
-    RablRails::Library.reset_instance
-
-    @user = User.new(1, 'foo', 'male')
-    @user.stub_chain(:posts, :count).and_return(10)
-    @user.stub(:respond_to?).with(:each).and_return(false)
-
-    @context = Context.new
-    @context.stub(:instance_variable_get).with(:@user).and_return(@user)
-    @context.stub(:instance_variable_get).with(:@virtual_path).and_return('user/show')
-    @context.stub(:instance_variable_get).with(:@_assigns).and_return({'user' => @user})
-    @context.stub(:lookup_context)
-  end
-
-  test "compile and render non restful resource" do
-    source = %{
-      object false
-      node(:post_count) { @user.posts.count }
-      child(:@user => :user) do
-        attributes :id, :name
-      end
-    }
-
-    assert_equal(MultiJson.encode({
-      :post_count => 10,
-      :user => {
-        :id => 1,
-        :name => 'foo'
-      }
-    }), RablRails::Library.instance.get_rendered_template(source, @context))
-  end
-end
\ No newline at end of file
diff --git a/test/renderers/json_renderer_test.rb b/test/renderers/json_renderer_test.rb
deleted file mode 100644
index c45ed46..0000000
--- a/test/renderers/json_renderer_test.rb
+++ /dev/null
@@ -1,189 +0,0 @@
-require 'test_helper'
-
-class TestJsonRenderer < ActiveSupport::TestCase
-
-  setup do
-    @data = User.new(1, 'foobar', 'male')
-
-    @context = Context.new
-    @context.assigns['data'] = @data
-
-    @template = RablRails::CompiledTemplate.new
-    @template.data = :@data
-  end
-
-  def render_json_output
-    RablRails::Renderers::JSON.new(@context).render(@template)
-  end
-
-  test "render object wth empty template" do
-    @template.source = {}
-    assert_equal %q({}), render_json_output
-  end
-
-  test "render collection with empty template" do
-    @context.assigns['data'] = [@data]
-    @template.source = {}
-    assert_equal %q([{}]), render_json_output
-  end
-
-  test "render object with local methods (used by decent_exposure)" do
-    @context.stub(:user).and_return(@data)
-    @template.data = :user
-    @template.source = { :id => :id }
-    assert_equal %q({"id":1}), render_json_output
-  end
-
-  test "render single object attributes" do
-    @template.source = { :id => :id, :name => :name }
-    assert_equal %q({"id":1,"name":"foobar"}), render_json_output
-  end
-
-  test "render child with object association" do
-    @data.stub(:address).and_return(double(:city => 'Paris'))
-    @template.source = { :address => { :_data => :address, :city => :city } }
-    assert_equal %q({"address":{"city":"Paris"}}), render_json_output
-  end
-
-  test "render child with arbitrary data source" do
-    @template.source = { :author => { :_data => :@data, :name => :name } }
-    assert_equal %q({"author":{"name":"foobar"}}), render_json_output
-  end
-
-  test "render child with local methods (used by decent_exposure)" do
-    @context.stub(:user).and_return(@data)
-    @template.source = { :author => { :_data => :user, :name => :name } }
-    assert_equal %q({"author":{"name":"foobar"}}), render_json_output
-  end
-
-  test "render glued attributes from single object" do
-    @template.source = { :_glue0 => { :_data => :@data, :name => :name } }
-    assert_equal %q({"name":"foobar"}), render_json_output
-  end
-
-  test "render glued node" do
-    @template.source = { :_glue0 => { :_data => :@data, :foo => lambda { |u| u.name } } }
-    assert_equal(%q({"foo":"foobar"}), render_json_output)
-  end
-
-  test "render collection with attributes" do
-    @data = [User.new(1, 'foo', 'male'), User.new(2, 'bar', 'female')]
-    @context.assigns['data'] = @data
-    @template.source = { :uid => :id, :name => :name, :gender => :sex }
-    assert_equal %q([{"uid":1,"name":"foo","gender":"male"},{"uid":2,"name":"bar","gender":"female"}]), render_json_output
-  end
-
-  test "render node property" do
-    proc = lambda { |object| object.name }
-    @template.source = { :name => proc }
-    assert_equal %q({"name":"foobar"}), render_json_output
-  end
-
-  test "render node property with true condition" do
-    condition = lambda { |u| true }
-    proc = lambda { |object| object.name }
-    @template.source = { :name => [condition, proc] }
-    assert_equal %q({"name":"foobar"}), render_json_output
-  end
-
-  test "render node property with false condition" do
-    condition = lambda { |u| false }
-    proc = lambda { |object| object.name }
-    @template.source = { :name => [condition, proc] }
-    assert_equal %q({}), render_json_output
-  end
-
-  test "node with context method call" do
-    @context.stub(:respond_to?).with(:@data).and_return(false)
-    @context.stub(:respond_to?).with(:context_method).and_return(true)
-    @context.stub(:context_method).and_return('marty')
-    proc = lambda { |object| context_method }
-    @template.source = { :name => proc }
-    assert_equal %q({"name":"marty"}), render_json_output
-  end
-
-  test "partial should be evaluated at rendering time" do
-    # Set assigns
-    @context.assigns['user'] = @data
-
-    # Stub Library#get
-    t = RablRails::CompiledTemplate.new
-    t.source = { :name => :name }
-    RablRails::Library.reset_instance
-    RablRails::Library.instance.should_receive(:compile_template_from_path).with('users/base').and_return(t)
-
-    @template.data = false
-    @template.source = { :user => ->(s) { partial('users/base', :object => @user) } }
-
-    assert_equal %q({"user":{"name":"foobar"}}), render_json_output
-  end
-
-  test "partial with no values should raise an error" do
-    @template.data = false
-    @template.source = { :user => ->(s) { partial('users/base') } }
-
-    assert_raises(RablRails::Renderers::PartialError) { render_json_output }
-  end
-
-  test "partial with empty values should not raise an error" do
-    @template.data = false
-    @template.source = { :users => ->(s) { partial('users/base', :object => []) } }
-
-    assert_equal %q({"users":[]}), render_json_output
-  end
-
-  test "condition blocks are transparent if the condition passed" do
-    c = RablRails::Condition.new(->(u) { true }, { :name => :name })
-    @template.source = { :_if0 => c }
-    assert_equal %q({"name":"foobar"}), render_json_output
-  end
-
-  test "condition blocks are ignored if the condition is not met" do
-    c = RablRails::Condition.new(->(u) { false }, { :name => :name })
-    @template.source = { :_if0 => c }
-    assert_equal %q({}), render_json_output
-  end
-
-  test "render object with root node" do
-    RablRails.include_json_root = true
-    @template.root_name = :author
-    @template.source = { :id => :id, :name => :name }
-    assert_equal %q({"author":{"id":1,"name":"foobar"}}), render_json_output
-  end
-
-  test "render object with root options set to false" do
-    RablRails.include_json_root = false
-    @template.root_name = :author
-    @template.source = { :id => :id, :name => :name }
-    assert_equal %q({"id":1,"name":"foobar"}), render_json_output
-  end
-
-  test "merge should raise is return from given block is not a hash" do
-    @template.source = { :_merge0 => ->(c) { 'foo' } }
-    assert_raises(RablRails::Renderers::PartialError) { render_json_output }
-  end
-
-  test "result from merge is merge inside current response" do
-    @template.source = { :_merge0 => ->(c) { { :custom => c.name } } }
-    assert_equal %q({"custom":"foobar"}), render_json_output
-  end
-
-  test "render with jsonp callback" do
-    RablRails.enable_jsonp_callbacks = true
-    @context.stub(:params).and_return({ callback: 'some_callback' })
-    @template.source = { :name => :name }
-    assert_equal %q[some_callback({"name":"foobar"})], render_json_output
-  end
-
-  test "cache key should be different from Base to avoid name collisions" do
-    ActionController::Base.stub(:perform_caching).and_return(true)
-    @data.stub(:cache_key).and_return('data_cache_key')
-    @template.cache_key = nil
-
-    @cache = double
-    @cache.should_receive(:fetch).with('data_cache_key.json').and_return(%("some":"json"))
-    Rails.stub(:cache).and_return(@cache)
-
-    assert_equal %("some":"json"), render_json_output
-  end
-end
\ No newline at end of file
diff --git a/test/renderers/plist_renderer_test.rb b/test/renderers/plist_renderer_test.rb
deleted file mode 100644
index 0709c32..0000000
--- a/test/renderers/plist_renderer_test.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-require 'test_helper'
-
-class TestPlistRenderer < ActiveSupport::TestCase
-  INDENT_REGEXP = /\n(\s)*/
-  HEADER_REGEXP = /<\?[^>]+><![^>]+>/
-
-  setup do
-    @data = User.new(1, 'foobar', 'male')
-
-    @context = Context.new
-    @context.assigns['data'] = @data
-
-    @template = RablRails::CompiledTemplate.new
-    @template.data = :@data
-    @template.root_name = :user
-  end
-
-  def render_plist_output
-    output = RablRails::Renderers::PLIST.new(@context).render(@template).to_s.gsub!(INDENT_REGEXP, '')
-    output.sub!(HEADER_REGEXP, '').gsub!(%r(</?plist[^>]*>), '').sub!(%r(<dict/?>), '').sub(%r(</dict>), '')
-  end
-
-  test "plist engine should responsd to #dump" do
-    assert_raises(RuntimeError) { RablRails.plist_engine = Object.new }
-  end
-
-  test "render object wth empty template" do
-    @template.source = {}
-    assert_equal %q(), render_plist_output
-  end
-
-  test "render collection with empty template" do
-    @context.assigns['data'] = [@data]
-    @template.source = {}
-    assert_equal %q(<array></array>), render_plist_output
-  end
-
-  test "render object with local methods (used by decent_exposure)" do
-    @context.stub(:user).and_return(@data)
-    @template.data = :user
-    @template.source = { :id => :id }
-    assert_equal %q(<key>id</key><integer>1</integer>), render_plist_output
-  end
-
-  test "render single object attributes" do
-    @template.source = { :id => :id, :name => :name }
-    assert_equal %q(<key>id</key><integer>1</integer><key>name</key><string>foobar</string>), render_plist_output
-  end
-
-  test "render child with object association" do
-    @data.stub(:address).and_return(double(:city => 'Paris'))
-    @template.source = { :address => { :_data => :address, :city => :city } }
-    assert_equal %q(<key>address</key><dict><key>city</key><string>Paris</string></dict>), render_plist_output
-  end
-
-  test "render child with arbitrary data source" do
-    @template.source = { :author => { :_data => :@data, :name => :name } }
-    assert_equal %q(<key>author</key><dict><key>name</key><string>foobar</string></dict>), render_plist_output
-  end
-
-  test "render child with local methods (used by decent_exposure)" do
-    @context.stub(:user).and_return(@data)
-    @template.source = { :author => { :_data => :user, :name => :name } }
-    assert_equal %q(<key>author</key><dict><key>name</key><string>foobar</string></dict>), render_plist_output
-  end
-
-  test "render node property" do
-    proc = lambda { |object| object.name }
-    @template.source = { :name => proc }
-    assert_equal %q(<key>name</key><string>foobar</string>), render_plist_output
-  end
-
-  test "render node property with true condition" do
-    condition = lambda { |u| true }
-    proc = lambda { |object| object.name }
-    @template.source = { :name => [condition, proc] }
-    assert_equal %q(<key>name</key><string>foobar</string>), render_plist_output
-  end
-
-  test "render node property with false condition" do
-    condition = lambda { |u| false }
-    proc = lambda { |object| object.name }
-    @template.source = { :name => [condition, proc] }
-    assert_equal %q(), render_plist_output
-  end
-
-  test "node with context method call" do
-    @context.stub(:respond_to?).with(:@data).and_return(false)
-    @context.stub(:respond_to?).with(:context_method).and_return(true)
-    @context.stub(:context_method).and_return('marty')
-    proc = lambda { |object| context_method }
-    @template.source = { :name => proc }
-    assert_equal %q(<key>name</key><string>marty</string>), render_plist_output
-  end
-
-  test "partial with no values should raise an error" do
-    @template.data = false
-    @template.source = { :user => ->(s) { partial('users/base') } }
-
-    assert_raises(RablRails::Renderers::PartialError) { render_plist_output }
-  end
-
-  test "partial with empty values should not raise an error" do
-    @template.data = false
-    @template.source = { :users => ->(s) { partial('users/base', :object => []) } }
-
-    assert_equal %q(<key>users</key><array/>), render_plist_output
-  end
-
-  test "condition blocks are transparent if the condition passed" do
-    c = RablRails::Condition.new(->(u) { true }, { :name => :name })
-    @template.source = { :_if0 => c }
-    assert_equal %q(<key>name</key><string>foobar</string>), render_plist_output
-  end
-
-  test "condition blocks are ignored if the condition is not met" do
-    c = RablRails::Condition.new(->(u) { false }, { :name => :name })
-    @template.source = { :_if0 => c }
-    assert_equal %q(), render_plist_output
-  end
-
-  test "render object with root node" do
-    RablRails.include_plist_root = true
-    @template.root_name = :author
-    @template.source = { :id => :id, :name => :name }
-    assert_equal %q(<key>author</key><dict><key>id</key><integer>1</integer><key>name</key><string>foobar</string></dict>), render_plist_output
-  end
-
-  test "render object with root options set to false" do
-    RablRails.include_plist_root = false
-    @template.root_name = :author
-    @template.source = { :id => :id, :name => :name }
-    assert_equal %q(<key>id</key><integer>1</integer><key>name</key><string>foobar</string>), render_plist_output
-  end
-end
\ No newline at end of file
diff --git a/test/renderers/test_hash_renderer.rb b/test/renderers/test_hash_renderer.rb
new file mode 100644
index 0000000..cb75402
--- /dev/null
+++ b/test/renderers/test_hash_renderer.rb
@@ -0,0 +1,90 @@
+require 'helper'
+
+class TestHashRenderer < MINITEST_TEST_CLASS
+  describe 'hash renderer' do
+    def render(locals = nil)
+      RablRails::Renderers::Hash.render(@template, @context, locals)
+    end
+
+    def with_cache
+      ActionController::Base.stub :perform_caching, true do
+        Rails.stub :cache, @cache do
+          yield
+        end
+      end
+    end
+
+    before do
+      @cache    = MiniTest::Mock.new
+      @resource = User.new(1, 'Marty')
+      @context  = Context.new
+      @context.assigns['user'] = @resource
+      @template = RablRails::CompiledTemplate.new
+      @template.data = :@user
+      @template.add_node RablRails::Nodes::Attribute.new(name: :name)
+    end
+
+    describe 'cache' do
+      it 'uses resource cache_key by default' do
+        def @resource.cache_key; 'marty_cache' end
+        @template.cache_key = nil
+        @cache.expect :fetch, { user: 'Marty' }, ['marty_cache']
+        with_cache {
+          assert_equal({ user: 'Marty' }, render)
+        }
+        @cache.verify
+      end
+
+      it 'uses template cache_key if present' do
+        @template.cache_key = ->(u) { u.name }
+        @cache.expect :fetch, { user: 'Marty' }, ['Marty']
+        with_cache {
+          assert_equal({ user: 'Marty' }, render)
+        }
+        @cache.verify
+      end
+    end
+
+    it 'uses a to_hash visitor' do
+      visitor = MiniTest::Mock.new
+      visitor.expect :instance_variable_get, @resource, [:@user]
+      visitor.expect :reset_for, nil, [@resource]
+      visitor.expect :visit, nil, [Array]
+      visitor.expect :result, { some: 'result' }
+
+      Visitors::ToHash.stub :new, visitor do
+        assert_equal({ some: 'result' }, render)
+      end
+
+      visitor.verify
+    end
+
+    it 'retrieves data from context if exist' do
+      @template.data = :context_method
+      resource = User.new(2, 'Biff')
+      @context.stub :context_method, resource do
+        assert_equal({ name: 'Biff' }, render)
+      end
+    end
+
+    it 'uses assigns from context if context has no data method' do
+      assert_equal({ name: 'Marty' }, render)
+    end
+
+    it "uses resource passed in locals if template don't have data" do
+      @template.data = nil
+      resource = User.new(2, 'Biff')
+      assert_equal({ name: 'Biff' }, render(resource: resource))
+    end
+
+    it 'uses template root_name option' do
+      @template.root_name = :user
+      assert_equal({ user: { name: 'Marty' } }, render)
+    end
+
+    it 'renders collection' do
+      @context.assigns['user'] = [@resource]
+      assert_equal([{ name: 'Marty' }], render)
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/renderers/test_json_renderer.rb b/test/renderers/test_json_renderer.rb
new file mode 100644
index 0000000..d93b52d
--- /dev/null
+++ b/test/renderers/test_json_renderer.rb
@@ -0,0 +1,46 @@
+require 'helper'
+
+class TestJSONRenderer < MINITEST_TEST_CLASS
+  describe 'JSON renderer' do
+    def render
+      RablRails::Renderers::JSON.render(@template, @context)
+    end
+
+    before do
+      @resource = User.new(1, 'Marty')
+      @context  = Context.new
+      @context.assigns['user'] = @resource
+      @template = RablRails::CompiledTemplate.new
+      @template.data = :@user
+      @template.add_node RablRails::Nodes::Attribute.new(name: :name)
+    end
+
+    it 'extends hash renderer' do
+      RablRails::Renderers::JSON.ancestors.include?(RablRails::Renderers::Hash)
+    end
+
+    it 'renders JSON' do
+      assert_equal %q({"name":"Marty"}), render
+    end
+
+    it 'uses template root_name option' do
+      @template.root_name = :user
+      assert_equal %q({"user":{"name":"Marty"}}), render
+    end
+
+    it 'ignores template root_name option if include_json_root is disabled' do
+      @template.root_name = :user
+      with_configuration :include_json_root, false do
+        assert_equal %q({"name":"Marty"}), render
+      end
+    end
+
+    it 'renders jsonp callback' do
+      @context.stub :params, { callback: 'some_callback' } do
+        with_configuration :enable_jsonp_callbacks, true do
+          assert_equal %q[some_callback({"name":"Marty"})], render
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/renderers/test_plist_renderer.rb b/test/renderers/test_plist_renderer.rb
new file mode 100644
index 0000000..54e9651
--- /dev/null
+++ b/test/renderers/test_plist_renderer.rb
@@ -0,0 +1,42 @@
+require 'helper'
+
+class TestPListRenderer < MINITEST_TEST_CLASS
+  INDENT_REGEXP = /\n(\s)*/
+  HEADER_REGEXP = /<\?[^>]+><![^>]+>/
+
+   describe 'PList renderer' do
+    def render
+      output = RablRails::Renderers::PLIST.render(@template, @context).to_s.gsub!(INDENT_REGEXP, '')
+      output.sub!(HEADER_REGEXP, '').gsub!(%r(</?plist[^>]*>), '').sub!(%r(<dict/?>), '').sub(%r(</dict>), '')
+    end
+
+    before do
+      @resource = User.new(1, 'Marty')
+      @context  = Context.new
+      @context.assigns['user'] = @resource
+      @template = RablRails::CompiledTemplate.new
+      @template.data = :@user
+      @template.add_node RablRails::Nodes::Attribute.new(name: :name)
+    end
+
+    it 'extends hash renderer' do
+      RablRails::Renderers::PLIST.ancestors.include?(RablRails::Renderers::Hash)
+    end
+
+    it 'renders PList' do
+      assert_equal %q(<key>name</key><string>Marty</string>), render
+    end
+
+    it 'uses template root_name option if include_plist_root is set' do
+      @template.root_name = :user
+      with_configuration :include_plist_root, true do
+        assert_equal %q(<key>user</key><dict><key>name</key><string>Marty</string></dict>), render
+      end
+    end
+
+    it 'ignores template root_name by default' do
+      @template.root_name = :user
+      assert_equal %q(<key>name</key><string>Marty</string>), render
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/renderers/test_xml_renderer.rb b/test/renderers/test_xml_renderer.rb
new file mode 100644
index 0000000..7698b99
--- /dev/null
+++ b/test/renderers/test_xml_renderer.rb
@@ -0,0 +1,37 @@
+require 'helper'
+
+class TestXMLRenderer < MINITEST_TEST_CLASS
+  INDENT_REGEXP = /\n(\s)*/
+  HEADER_REGEXP = /<[^>]+>/
+
+  describe 'XML renderer' do
+    def render
+      RablRails::Renderers::XML.render(@template, @context).to_s.gsub!(INDENT_REGEXP, '').sub!(HEADER_REGEXP, '')
+    end
+
+    before do
+      @resource = User.new(1, 'Marty')
+      @context  = Context.new
+      @context.assigns['user'] = @resource
+      @template = RablRails::CompiledTemplate.new
+      @template.data = :@user
+      @template.add_node RablRails::Nodes::Attribute.new(name: :name)
+    end
+
+    it 'extends hash renderer' do
+      RablRails::Renderers::XML.ancestors.include?(RablRails::Renderers::Hash)
+    end
+
+    it 'uses global XML options' do
+      @template.nodes = [RablRails::Nodes::Attribute.new(first_name: :name)]
+      with_configuration :xml_options, { dasherize: false, skip_types: false } do
+        assert_equal %q(<hash><first_name>Marty</first_name></hash>), render
+      end
+    end
+
+    it 'uses template root_name option' do
+      @template.root_name = :user
+      assert_equal %q(<user><name>Marty</name></user>), render
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/renderers/xml_renderer_test.rb b/test/renderers/xml_renderer_test.rb
deleted file mode 100644
index 6a01d68..0000000
--- a/test/renderers/xml_renderer_test.rb
+++ /dev/null
@@ -1,137 +0,0 @@
-require 'test_helper'
-
-class TestXmlRenderer < ActiveSupport::TestCase
-  INDENT_REGEXP = /\n(\s)*/
-  HEADER_REGEXP = /<[^>]+>/
-
-  setup do
-    @data = User.new(1, 'foobar', 'male')
-
-    @context = Context.new
-    @context.assigns['data'] = @data
-
-    @template = RablRails::CompiledTemplate.new
-    @template.data = :@data
-    @template.root_name = :user
-  end
-
-  def render_xml_output
-    RablRails::Renderers::XML.new(@context).render(@template).to_s.gsub!(INDENT_REGEXP, '').sub!(HEADER_REGEXP, '')
-  end
-
-  test "render object simple object" do
-    @template.source = {}
-    assert_equal %q(<user></user>), render_xml_output
-  end
-
-  test "render collection with empty template" do
-    @context.assigns['data'] = [@data]
-    @template.source = {}
-    @template.root_name = :users
-    assert_equal %q(<users type="array"><user></user></users>), render_xml_output
-  end
-
-  test "render object with local methods (used by decent_exposure)" do
-    @context.stub(:user).and_return(@data)
-    @template.source = { :id => :id }
-    assert_equal %q(<user><id type="integer">1</id></user>), render_xml_output
-  end
-
-  test "render single object attributes" do
-    @template.source = { :name => :name }
-    assert_equal %q(<user><name>foobar</name></user>), render_xml_output
-  end
-
-  test "render child with arbitrary data source" do
-    @template.source = { :author => { :_data => :@data, :name => :name } }
-    @template.root_name = :post
-    assert_equal %q(<post><author><name>foobar</name></author></post>), render_xml_output
-  end
-
-  test "render child with local methods (used by decent_exposure)" do
-    @context.stub(:user).and_return(@data)
-    @template.source = { :author => { :_data => :user, :name => :name } }
-    @template.root_name = :post
-    assert_equal %q(<post><author><name>foobar</name></author></post>), render_xml_output
-  end
-
-  test "render glued attributes from single object" do
-    @template.source = { :_glue0 => { :_data => :@data, :name => :name } }
-    assert_equal %q(<user><name>foobar</name></user>), render_xml_output
-  end
-
-  test "render collection with attributes" do
-    @data = [User.new(1, 'foo', 'male'), User.new(2, 'bar', 'female')]
-    @context.assigns['data'] = @data
-    @template.root_name = :users
-    @template.source = { :uid => :id, :name => :name }
-    assert_equal %q(<users type="array"><user><uid type="integer">1</uid><name>foo</name></user><user><uid type="integer">2</uid><name>bar</name></user></users>), render_xml_output
-  end
-
-  test "render node property" do
-    proc = lambda { |object| object.name }
-    @template.source = { :name => proc }
-    assert_equal %q(<user><name>foobar</name></user>), render_xml_output
-  end
-
-  test "render node property with true condition" do
-    condition = lambda { |u| true }
-    proc = lambda { |object| object.name }
-    @template.source = { :name => [condition, proc] }
-    assert_equal %q(<user><name>foobar</name></user>), render_xml_output
-  end
-
-  test "render node property with false condition" do
-    condition = lambda { |u| false }
-    proc = lambda { |object| object.name }
-    @template.source = { :name => [condition, proc] }
-    assert_equal %q(<user></user>), render_xml_output
-  end
-
-  test "node with context method call" do
-    @context.stub(:respond_to?).with(:@data).and_return(false)
-    @context.stub(:respond_to?).with(:context_method).and_return(true)
-    @context.stub(:context_method).and_return('marty')
-    proc = lambda { |object| context_method }
-    @template.source = { :name => proc }
-    assert_equal %q(<user><name>marty</name></user>), render_xml_output
-  end
-
-  test "partial should be evaluated at rendering time" do
-    # Set assigns
-    @context.assigns['user'] = @data
-
-    # Stub Library#get
-    t = RablRails::CompiledTemplate.new
-    t.source = { :name => :name }
-    RablRails::Library.reset_instance
-    RablRails::Library.instance.should_receive(:compile_template_from_path).with('users/base').and_return(t)
-
-    @template.data = false
-    @template.root_name = :post
-    @template.source = { :user => ->(s) { partial('users/base', :object => @user) } }
-
-    assert_equal %q(<post><user><name>foobar</name></user></post>), render_xml_output
-  end
-
-  test "partial with no values should raise an error" do
-    @template.data = false
-    @template.source = { :user => ->(s) { partial('users/base') } }
-
-    assert_raises(RablRails::Renderers::PartialError) { render_xml_output }
-  end
-
-  test "partial with empty values should not raise an error" do
-    @template.data = false
-    @template.root_name = :list
-    @template.source = { :users => ->(s) { partial('users/base', :object => []) } }
-
-    assert_equal %q(<list><users type="array"/></list>), render_xml_output
-  end
-
-  test "render underscorized xml" do
-    RablRails.xml_options = { :dasherize => false, :skip_types => false }
-    @template.source = { :first_name => :name }
-    assert_equal %q(<user><first_name>foobar</first_name></user>), render_xml_output
-  end
-end
\ No newline at end of file
diff --git a/test/test_compiler.rb b/test/test_compiler.rb
new file mode 100644
index 0000000..c915cba
--- /dev/null
+++ b/test/test_compiler.rb
@@ -0,0 +1,283 @@
+require 'helper'
+
+class TestCompiler < MINITEST_TEST_CLASS
+  describe 'compiler' do
+    def extract_attributes(nodes)
+      nodes.map(&:hash)
+    end
+
+    before do
+      @view = Context.new
+      @compiler = RablRails::Compiler.new(@view)
+    end
+
+    it "returns a compiled template instance" do
+      assert_instance_of RablRails::CompiledTemplate, @compiler.compile_source("")
+    end
+
+    describe '#object' do
+      it "sets data for the template" do
+        t = @compiler.compile_source(%{ object :@user })
+        assert_equal :@user, t.data
+        assert_equal([], t.nodes)
+      end
+
+      it "can define root name" do
+        t = @compiler.compile_source(%{ object :@user => :author })
+        assert_equal :@user, t.data
+        assert_equal :author, t.root_name
+        assert_equal([], t.nodes)
+      end
+    end
+
+    describe '#root' do
+      it "defines root via keyword" do
+        t = @compiler.compile_source(%{ root :author })
+        assert_equal :author, t.root_name
+      end
+
+      it "overrides object root" do
+        t = @compiler.compile_source(%{ object :@user ; root :author })
+        assert_equal :author, t.root_name
+      end
+
+      it "can set root to false via options" do
+        t = @compiler.compile_source(%( object :@user, root: false))
+        assert_equal false, t.root_name
+      end
+    end
+
+    describe '#collection' do
+      it "sets the data for the template" do
+        t = @compiler.compile_source(%{ collection :@user })
+        assert_equal :@user, t.data
+        assert_equal([], t.nodes)
+      end
+
+      it "can define root name" do
+        t = @compiler.compile_source(%{ collection :@user => :users })
+        assert_equal :@user, t.data
+        assert_equal :users, t.root_name
+        assert_equal([], t.nodes)
+      end
+
+      it "can define root name via options" do
+        t = @compiler.compile_source(%{ collection :@user, :root => :users })
+        assert_equal :@user, t.data
+        assert_equal :users, t.root_name
+      end
+    end
+
+    it "should not have a cache key if cache is not enable" do
+      t = @compiler.compile_source('')
+      assert_equal false, t.cache_key
+    end
+
+    describe '#cache' do
+      it "can take no argument" do
+        t = @compiler.compile_source(%{ cache })
+        assert_nil t.cache_key
+      end
+
+      it "sets the given block as cache key" do
+        t = @compiler.compile_source(%( cache { 'foo' }))
+        assert_instance_of Proc, t.cache_key
+      end
+    end
+
+    # Compilation
+
+    it "compiles single attributes" do
+      t = @compiler.compile_source(%{ attributes :id, :name })
+      assert_equal([{ :id => :id, :name => :name }], extract_attributes(t.nodes))
+    end
+
+    it "compiles attributes with the same name once" do
+      skip('Failing')
+      t = @compiler.compile_source(%{ attribute :id ; attribute :id })
+      assert_equal([{ :id => :id }], extract_attributes(t.nodes))
+    end
+
+    it "aliases attributes through :as option" do
+      t = @compiler.compile_source(%{ attribute :foo, :as => :bar })
+      assert_equal([{ :bar => :foo }], extract_attributes(t.nodes))
+    end
+
+    it "aliases attributes through a hash" do
+      t = @compiler.compile_source(%{ attribute :foo => :bar })
+      assert_equal([{ :bar => :foo }], extract_attributes(t.nodes))
+    end
+
+    it "aliases multiple attributes" do
+      t = @compiler.compile_source(%{ attributes :foo => :bar, :id => :uid })
+      assert_equal([{ :bar => :foo, :uid => :id }], extract_attributes(t.nodes))
+    end
+
+    it "compiles child with record association" do
+      t = @compiler.compile_source(%{ child :address do attributes :foo end})
+
+      assert_equal(1, t.nodes.size)
+      child_node = t.nodes.first
+
+      assert_equal(:address, child_node.name)
+      assert_equal(:address, child_node.data)
+      assert_equal([{ foo: :foo }], extract_attributes(child_node.nodes))
+    end
+
+    it "compiles child with association aliased" do
+      t = @compiler.compile_source(%{ child :address => :bar do attributes :foo end})
+      child_node = t.nodes.first
+
+      assert_equal(:bar, child_node.name)
+      assert_equal(:address, child_node.data)
+    end
+
+    it "compiles child with root name defined as option" do
+      t = @compiler.compile_source(%{ child(:user, :root => :author) do attributes :foo end })
+      child_node = t.nodes.first
+
+      assert_equal(:author, child_node.name)
+      assert_equal(:user, child_node.data)
+    end
+
+    it "compiles child with arbitrary source" do
+      t = @compiler.compile_source(%{ child :@user => :author do attribute :name end })
+      child_node = t.nodes.first
+
+      assert_equal(:author, child_node.name)
+      assert_equal(:@user, child_node.data)
+    end
+
+    it "compiles child with inline partial notation" do
+      mock_template = RablRails::CompiledTemplate.new
+      mock_template.add_node(RablRails::Nodes::Attribute.new(id: :id))
+
+      t = RablRails::Library.instance.stub :compile_template_from_path, mock_template do
+        @compiler.compile_source(%{child(:user, :partial => 'users/base') })
+      end
+
+      child_node = t.nodes.first
+
+      assert_equal(:user, child_node.name)
+      assert_equal(:user, child_node.data)
+      assert_equal([{ id: :id }], extract_attributes(child_node.nodes))
+    end
+
+    it "compiles glue as a child but without a name" do
+      t = @compiler.compile_source(%{ glue(:@user) do attribute :name end })
+
+      assert_equal(1, t.nodes.size)
+      glue_node = t.nodes.first
+
+      assert_equal(:@user, glue_node.data)
+      assert_equal([{ name: :name }], extract_attributes(glue_node.nodes))
+    end
+
+    it "allows multiple glue within same template" do
+      t = @compiler.compile_source(%{
+        glue :@user do attribute :name end
+        glue :@user do attribute :foo end
+      })
+
+      assert_equal(2, t.nodes.size)
+    end
+
+    it "compiles glue with RablRails DSL in its body" do
+      t = @compiler.compile_source(%{
+        glue :@user do node(:foo) { |u| u.name } end
+      })
+
+      glue_node = t.nodes.first
+      assert_equal(1, glue_node.nodes.size)
+
+      code_node = glue_node.nodes.first
+      assert_instance_of(RablRails::Nodes::Code, code_node)
+      assert_equal(:foo, code_node.name)
+    end
+
+    it "extends other template" do
+      template = RablRails::CompiledTemplate.new
+      template.add_node RablRails::Nodes::Attribute.new(id: :id)
+
+      library = MiniTest::Mock.new
+      library.expect :compile_template_from_path, template, ['users/base', @view]
+
+      t = RablRails::Library.stub :instance, library do
+        @compiler.compile_source(%{ extends 'users/base' })
+      end
+      assert_equal([{ :id => :id }], extract_attributes(t.nodes))
+      library.verify
+    end
+
+    it "compiles extend without overwriting nodes previously defined" do
+      template = RablRails::CompiledTemplate.new
+      template.add_node(RablRails::Nodes::Condition.new(->{ true }, ->{ 'foo' }))
+
+      t = RablRails::Library.instance.stub :compile_template_from_path, template do
+        @compiler.compile_source(%{
+          condition(-> { false }) { 'bar' }
+          extends('users/xtnd')
+        })
+      end
+      assert_equal(2, t.nodes.size)
+    end
+
+    it "compiles node" do
+      t = @compiler.compile_source(%{ node(:foo) { bar } })
+
+      assert_equal(1, t.nodes.size)
+      code_node = t.nodes.first
+
+      assert_equal(:foo, code_node.name)
+      assert_instance_of Proc, code_node.block
+    end
+
+    it "compiles node with condition option" do
+      t = @compiler.compile_source(%{ node(:foo, :if => lambda { |m| m.foo.present? }) do |m| m.foo end })
+      code_node = t.nodes.first
+      assert_instance_of Proc, code_node.condition
+    end
+
+    it "compiles node with no argument" do
+      t = @compiler.compile_source(%{ node do |m| m.foo end })
+      node = t.nodes.first
+      assert_nil node.name
+    end
+
+    it "compiles merge like a node but with a reserved keyword as name" do
+      t = @compiler.compile_source(%{ merge do |m| m.foo end })
+      node = t.nodes.first
+      assert_instance_of RablRails::Nodes::Code, node
+      assert_nil node.name
+    end
+
+    it "compiles condition" do
+      t = @compiler.compile_source(%{ condition(->(u) {}) do attributes :secret end })
+
+      assert_equal(1, t.nodes.size)
+      node = t.nodes.first
+
+      assert_instance_of RablRails::Nodes::Condition, node
+      assert_equal([{ secret: :secret }], extract_attributes(node.nodes))
+    end
+
+    it "compiles with no object" do
+      t = @compiler.compile_source(%{
+       object false
+       child(:@user => :user) do
+         attribute :id
+       end
+      })
+
+      assert_equal false, t.data
+    end
+
+    describe '#extract_data_and_name' do
+      it "extracts name from argument" do
+        assert_equal [:@users, 'users'], @compiler.send(:extract_data_and_name, :@users)
+        assert_equal [:users, :users], @compiler.send(:extract_data_and_name, :users)
+        assert_equal [:@users, :authors], @compiler.send(:extract_data_and_name, :@users => :authors)
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/test_configuration.rb b/test/test_configuration.rb
new file mode 100644
index 0000000..d4d022b
--- /dev/null
+++ b/test/test_configuration.rb
@@ -0,0 +1,31 @@
+require 'helper'
+
+class TestConfiguration < MINITEST_TEST_CLASS
+  describe 'Configuration' do
+    it 'has a zero score by default' do
+      config = RablRails::Configuration.new
+      assert_equal 0, config.result_flags
+    end
+
+    it 'sets a bit per option' do
+      config = RablRails::Configuration.new
+      config.replace_nil_values_with_empty_strings = true
+      assert_equal 1, config.result_flags
+
+      config = RablRails::Configuration.new
+      config.replace_empty_string_values_with_nil = true
+      assert_equal 2, config.result_flags
+
+      config = RablRails::Configuration.new
+      config.exclude_nil_values = true
+      assert_equal 4, config.result_flags
+    end
+
+    it 'allows mutiple bits to be set at the same time' do
+      config = RablRails::Configuration.new
+      config.replace_nil_values_with_empty_strings = true
+      config.replace_empty_string_values_with_nil = true
+      assert_equal 3, config.result_flags
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/test_hash_visitor.rb b/test/test_hash_visitor.rb
new file mode 100644
index 0000000..77797cc
--- /dev/null
+++ b/test/test_hash_visitor.rb
@@ -0,0 +1,224 @@
+require 'helper'
+
+class TestHashVisitor < MINITEST_TEST_CLASS
+  describe 'hash visitor' do
+    def visitor_result
+      visitor = Visitors::ToHash.new(@context)
+      visitor.reset_for @resource
+      visitor.visit @nodes
+      visitor.result
+    end
+
+    before do
+      @context  = Context.new
+      @resource = User.new(1, 'Marty')
+      @nodes    = []
+    end
+
+    it 'renders empty nodes list' do
+      assert_equal({}, visitor_result)
+    end
+
+    it 'renders attributes node' do
+      @nodes << RablRails::Nodes::Attribute.new(id: :id)
+      assert_equal({ id: 1 }, visitor_result)
+    end
+
+    it 'renders array of nodes' do
+      @nodes = [
+        RablRails::Nodes::Attribute.new(id: :id),
+        RablRails::Nodes::Attribute.new(name: :name)
+      ]
+      assert_equal({ id: 1, name: 'Marty' }, visitor_result)
+    end
+
+    describe 'with a child node' do
+      Address = Struct.new(:city)
+
+      before do
+        @template = RablRails::CompiledTemplate.new
+        @template.add_node(RablRails::Nodes::Attribute.new(city: :city))
+        @nodes << RablRails::Nodes::Child.new(:address, @template)
+        @address = Address.new('Paris')
+      end
+
+      it 'renders with resource association as data source' do
+        @template.data = :address
+        def @resource.address; end
+        @resource.stub :address, @address do
+          assert_equal({ address: { city: 'Paris' } }, visitor_result)
+        end
+      end
+
+      it 'renders with arbitrary data source' do
+        @template.data = :@address
+        @context.assigns['address'] = @address
+        assert_equal({ address: { city: 'Paris' } }, visitor_result)
+      end
+
+      it 'renders with local method as data source' do
+        @template.data = :address
+        def @context.address; end
+        @context.stub :address, @address do
+          assert_equal({ address: { city: 'Paris' } }, visitor_result)
+        end
+      end
+
+      it 'renders with a collection as data source' do
+        @template.data = :address
+        def @context.address; end
+        @context.stub :address, [@address, @address] do
+          assert_equal({ address: [
+            { city: 'Paris' },
+            { city: 'Paris' }
+          ]}, visitor_result)
+        end
+      end
+
+      it 'renders if the source is nil' do
+        @template.data = :address
+        def @resource.address; end
+        @resource.stub :address, nil do
+          assert_equal({ address: nil }, visitor_result)
+        end
+      end
+    end
+
+    it 'renders glue nodes' do
+      template = RablRails::CompiledTemplate.new
+      template.add_node(RablRails::Nodes::Attribute.new(name: :name))
+      template.data = :@user
+
+      @nodes << RablRails::Nodes::Glue.new(template)
+      @context.assigns['user'] = @resource
+      assert_equal({ name: 'Marty'}, visitor_result)
+    end
+
+    describe 'with a code node' do
+      before do
+        @proc = ->(object) { object.name }
+      end
+
+      it 'renders the evaluated proc' do
+        @nodes << RablRails::Nodes::Code.new(:name, @proc)
+        assert_equal({ name: 'Marty'}, visitor_result)
+      end
+
+      it 'renders with a true condition' do
+        @nodes << RablRails::Nodes::Code.new(:name, @proc, ->(o) { true })
+        assert_equal({ name: 'Marty'}, visitor_result)
+      end
+
+      it 'renders nothing with a false condition' do
+        @nodes << RablRails::Nodes::Code.new(:name, @proc, ->(o) { false })
+        assert_equal({}, visitor_result)
+      end
+
+      it 'renders method called from context' do
+        @proc = ->(object) { context_method }
+        def @context.context_method; end
+
+        @nodes = [RablRails::Nodes::Code.new(:name, @proc)]
+        @context.stub :context_method, 'Biff' do
+          assert_equal({ name: 'Biff'}, visitor_result)
+        end
+      end
+    end
+
+    describe 'with a condition node' do
+      before do
+        @ns = [RablRails::Nodes::Attribute.new(name: :name)]
+      end
+
+      it 'renders transparently if the condition is met' do
+        @nodes << RablRails::Nodes::Condition.new(->(o) { true }, @ns)
+        assert_equal({ name: 'Marty' }, visitor_result)
+      end
+
+      it 'renders nothing if the condition is not met' do
+        @nodes << RablRails::Nodes::Condition.new(->(o) { false }, @ns)
+        assert_equal({}, visitor_result)
+      end
+    end
+
+    it 'renders a merge node' do
+      proc = ->(c) { { custom: c.name } }
+      @nodes << RablRails::Nodes::Code.new(nil, proc)
+      assert_equal({ custom: 'Marty' }, visitor_result)
+    end
+
+    it 'raises an exception when trying to merge a non hash object' do
+      proc = ->(c) { c.name }
+      @nodes << RablRails::Nodes::Code.new(nil, proc)
+      assert_raises(RablRails::Renderer::PartialError) { visitor_result }
+    end
+
+    it 'renders partial defined in code node' do
+      template = RablRails::CompiledTemplate.new
+      template.add_node(RablRails::Nodes::Attribute.new(name: :name))
+      proc = ->(u) { partial('users/base', object: u) }
+
+      library = MiniTest::Mock.new
+      library.expect :compile_template_from_path, template, ['users/base', @context]
+
+      @nodes << RablRails::Nodes::Code.new(:user, proc)
+      RablRails::Library.stub :instance, library do
+        assert_equal({ user: { name: 'Marty' } }, visitor_result)
+      end
+
+      library.verify
+    end
+
+    it 'renders partial with empty target' do
+      proc = ->(u) { partial('users/base', object: []) }
+      @nodes << RablRails::Nodes::Code.new(:users, proc)
+      assert_equal({ users: [] }, visitor_result)
+    end
+
+    it 'raises an exception when calling a partial without a target' do
+      proc = ->(u) { partial('users/base') }
+      @nodes << RablRails::Nodes::Code.new(:user, proc)
+      assert_raises(RablRails::Renderer::PartialError) { visitor_result }
+    end
+
+    describe 'when hash options are set' do
+      before do
+        RablRails.reset_configuration
+        @nodes << RablRails::Nodes::Attribute.new(name: :name)
+      end
+
+      after { RablRails.reset_configuration }
+
+      it 'replaces nil values by strings' do
+        RablRails.configuration.replace_nil_values_with_empty_strings = true
+        @resource = User.new(1, nil)
+
+        assert_equal({ name: '' }, visitor_result)
+      end
+
+      it 'replaces empty string by nil' do
+        RablRails.configuration.replace_empty_string_values_with_nil = true
+        @resource = User.new(1, '')
+
+        assert_equal({ name: nil }, visitor_result)
+      end
+
+      it 'excludes nil values' do
+        RablRails.configuration.exclude_nil_values = true
+        @resource = User.new(1, nil)
+        @nodes << RablRails::Nodes::Attribute.new(id: :id)
+
+        assert_equal({ id: 1 }, visitor_result)
+      end
+
+      it 'excludes nil values and empty strings' do
+        RablRails.configuration.replace_empty_string_values_with_nil = true
+        RablRails.configuration.exclude_nil_values = true
+        @resource = User.new(nil, '')
+        @nodes << RablRails::Nodes::Attribute.new(id: :id)
+
+        assert_equal({}, visitor_result)
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/test_helper.rb b/test/test_helper.rb
deleted file mode 100644
index 71fb09c..0000000
--- a/test/test_helper.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-ENV["RAILS_ENV"] = "test"
-$:.unshift File.expand_path('../../lib', __FILE__)
-
-require 'rspec/mocks'
-require 'minitest/unit'
-MiniTest::Unit.autorun
-
-require 'active_support/test_case'
-
-require 'action_controller'
-
-require 'singleton'
-class <<Singleton
-  def included_with_reset(klass)
-    included_without_reset(klass)
-    class <<klass
-      def reset_instance
-        Singleton.send :__init__, self
-        self
-      end
-    end
-  end
-  alias_method_chain :included, :reset
-end
-
-require 'rabl-rails'
-require 'plist'
-
-if RUBY_ENGINE == 'jruby'
-  require 'nokogiri'
-elsif RUBY_ENGINE == 'ruby'
-  require 'libxml'
-end
-
-RablRails.load_default_engines!
-
-module ActiveSupport
-  class TestCase
-    RSpec::Mocks::setup(self)
-    include RSpec::Mocks::ExampleMethods
-  end
-end
-
-class Context
-  attr_writer :virtual_path
-
-  def initialize
-    @_assigns = {}
-    @virtual_path = nil
-  end
-
-  def assigns
-    @_assigns
-  end
-
-  def params
-    {}
-  end
-end
-
-class User
-  attr_accessor :id, :name, :sex
-  def initialize(id=nil, name=nil, sex=nil)
-    @id = id
-    @name = name
-    @sex = sex
-  end
-end
\ No newline at end of file
diff --git a/test/test_library.rb b/test/test_library.rb
new file mode 100644
index 0000000..84e0ac3
--- /dev/null
+++ b/test/test_library.rb
@@ -0,0 +1,85 @@
+require 'helper'
+
+class TestLibrary < MINITEST_TEST_CLASS
+  RablRails::Library.send(:attr_reader, :cached_templates)
+
+  describe 'library' do
+    before do
+      @library = RablRails::Library.instance
+      @library.reset_cache!
+      @context = Context.new
+      @template = RablRails::CompiledTemplate.new
+    end
+
+    describe '#get_rendered_template' do
+      it 'compiles and renders template' do
+        renderer = MiniTest::Mock.new
+        renderer.expect :render, '{}', [@template, @context, nil]
+
+        result = @library.stub :compile_template_from_source, @template do
+          RablRails::Renderers.stub :const_get, renderer do
+            @library.get_rendered_template '', @context
+          end
+        end
+
+        assert_equal '{}', result
+        assert renderer.verify
+      end
+
+      it 'accepts format as string' do
+        result = @library.stub :compile_template_from_source, @template do
+          @context.stub :params, { format: 'xml' } do
+            RablRails::Renderers::XML.stub :render, '<xml>' do
+              @library.get_rendered_template '', @context
+            end
+          end
+        end
+
+        assert_equal '<xml>', result
+      end
+
+      it 'accepts format as symbol' do
+        result = @library.stub :compile_template_from_source, @template do
+          @context.stub :params, { format: :plist } do
+            RablRails::Renderers::PLIST.stub :render, '<plist>' do
+              @library.get_rendered_template '', @context
+            end
+          end
+        end
+
+        assert_equal '<plist>', result
+      end
+    end
+
+    describe '#compile_template_from_source' do
+      it 'compiles a template' do
+        compiler = MiniTest::Mock.new
+        compiler.expect :compile_source, @template, ['attribute :id']
+
+        result = RablRails::Compiler.stub :new, compiler do
+          @library.compile_template_from_source('attribute :id', @context)
+        end
+
+        assert_equal @template, result
+      end
+
+      it 'caches compiled template if option is set' do
+        @context.virtual_path = 'users/base'
+        template = with_configuration :cache_templates, true do
+          @library.compile_template_from_source("attribute :id", @context)
+        end
+
+        assert_equal(template, @library.cached_templates['users/base'])
+      end
+
+      it 'compiles source without caching it if options is not set' do
+        @context.virtual_path = 'users/base'
+        template = with_configuration :cache_templates, false do
+          @library.compile_template_from_source("attribute :id", @context)
+        end
+
+        assert_empty @library.cached_templates
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/test/render_test.rb b/test/test_render.rb
similarity index 69%
rename from test/render_test.rb
rename to test/test_render.rb
index 140fad5..6f46c4a 100644
--- a/test/render_test.rb
+++ b/test/test_render.rb
@@ -1,15 +1,16 @@
-require 'test_helper'
+require 'helper'
 require 'pathname'
 require 'tmpdir'
 
-class RenderTest < ActiveSupport::TestCase
+class TestRender < MINITEST_TEST_CLASS
+  @@tmp_path = Pathname.new(Dir.mktmpdir)
 
-  setup do
+  def setup
     @user = User.new(1, 'Marty')
-    @tmp_path = Pathname.new(Dir.mktmpdir)
+    @tmp_path = @@tmp_path
   end
 
-  test "allow object to be passed as an option" do
+  def test_object_as_option
     File.open(@tmp_path + "nil.json.rabl", "w") do |f|
       f.puts %q{
         object :@user
@@ -19,7 +20,7 @@ class RenderTest < ActiveSupport::TestCase
     assert_equal %q({"user":{"name":"Marty"}}), RablRails.render(nil, 'nil', locals: { object: @user }, view_path: @tmp_path)
   end
 
-  test "load source from file" do
+  def test_load_source_from_file
     File.open(@tmp_path + "show.json.rabl", "w") do |f|
       f.puts %q{
         object :@user
@@ -29,11 +30,11 @@ class RenderTest < ActiveSupport::TestCase
     assert_equal %q({"user":{"id":1,"name":"Marty"}}), RablRails.render(@user, 'show', view_path: @tmp_path)
   end
 
-  test "raise error if template is not found" do
+  def test_template_not_found
     assert_raises(RablRails::Renderer::TemplateNotFound) { RablRails.render(@user, 'not_found') }
   end
 
-  test "instance variables can be passed via options[:locals]" do
+  def test_with_locals_options
     File.open(@tmp_path + "instance.json.rabl", "w") do |f|
       f.puts %q{
         object false
@@ -43,7 +44,7 @@ class RenderTest < ActiveSupport::TestCase
     assert_equal %q({"username":"Marty"}), RablRails.render(nil, 'instance', view_path: @tmp_path, locals: { user: @user })
   end
 
-  test "handle path for extends" do
+  def test_extend_with_view_path
     File.open(@tmp_path + "extend.json.rabl", "w") do |f|
       f.puts %q{
         object :@user
@@ -60,7 +61,7 @@ class RenderTest < ActiveSupport::TestCase
     assert_equal %q({"user":{"extended_name":"Marty"}}), RablRails.render(@user, 'extend', view_path: @tmp_path)
   end
 
-  test "format can be passed as symbol or a string" do
+  def test_format_as_symbol_or_string
     File.open(@tmp_path + "show.json.rabl", "w") do |f|
       f.puts %q{
         object :@user
@@ -73,20 +74,13 @@ class RenderTest < ActiveSupport::TestCase
     assert_equal %q({"user":{"id":1,"name":"Marty"}}), RablRails.render(@user, 'show', view_path: @tmp_path, format: 'JSON')
   end
 
-  test "format can be omitted if option is set" do
-    begin
-      RablRails.allow_empty_format_in_template = true
-      File.open(@tmp_path + "show.rabl", "w") do |f|
-        f.puts %q{
-          object :@user
-          attributes :id, :name
-        }
-      end
-
-      assert_equal %q({"user":{"id":1,"name":"Marty"}}), RablRails.render(@user, 'show', view_path: @tmp_path)
-    ensure
-      RablRails.allow_empty_format_in_template = false
+  def test_format_omitted
+    File.open(@tmp_path + "show.rabl", "w") do |f|
+      f.puts %q{
+        object :@user
+        attributes :id, :name
+      }
     end
+    assert_equal %q({"user":{"id":1,"name":"Marty"}}), RablRails.render(@user, 'show', view_path: @tmp_path)
   end
-
 end
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-rabl-rails.git



More information about the Pkg-ruby-extras-commits mailing list