Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Updates and prepare for initial release

  • Loading branch information...
commit 3951fb705645e6abe9b91590871b8d02df29d305 1 parent eec7acb
Nathan Esquenazi nesquena authored
53 README.md
View
@@ -18,9 +18,58 @@ This general templating system solves all of those problems.
## Usage ##
+Basic usage of the templater:
+ # app/views/users/show.json.rabl
+ attributes :id, :foo, :bar
-## Issues ##
+This will generate a json response with the attributes specified. You can also include arbitrary code:
+
+ # app/views/users/show.json.rabl
+ code :full_name do |u|
+ u.first_name + " " + u.last_name
+ end
+
+You can also add children nodes:
+
+ child @posts => :foobar do
+ attributes :id, :title
+ end
+
+or use existing model associations:
+
+ child :posts => :foobar do
+ attributes :id, :title
+ end
+
+You can also extend other rabl templates to reduce duplication:
+
+ # app/views/users/show.json.rabl
+ child @address do
+ extends "address/item"
+ end
- * I am sloppy and once again failed to unit test this. Don't use it in production until I do obviously.
+or get access to the hash representation of another object:
+
+ code :location do
+ { :place => partial("web/users/address", :object => @address) }
+ end
+
+You can also append attributes to the root node:
+
+ glue @post do
+ attribute :id => :post_id
+ end
+
+There is also the ability to extend other rabl templates with additional attributes:
+
+ extends "base"
+
+ code :release_year do |m|
+ date = m.release_date || m.series_start
+ date.try(:year)
+ end
+
+## Issues ##
+ * I am sloppy and once again failed to unit test this. Don't use it in production until I do obviously.
7 lib/rabl.rb
View
@@ -1,3 +1,8 @@
+require 'rabl/version'
+require 'rabl/engine'
+require 'rabl/builder'
+require 'rabl/template' if defined?(Rails)
+
module Rabl
- # Your code goes here...
+ # Nothing yet
end
117 lib/rabl/builder.rb
View
@@ -0,0 +1,117 @@
+module JRB
+ class Builder
+ # Constructs a new ejs generator based on given object and options
+ def initialize(object, options, &block)
+ @_object = object
+ @_result = {}
+ @options = options
+ end
+
+ # Returns a hash representation of the data object
+ # to_hash(:root => true)
+ def to_hash(options={})
+ # Attributes
+ @options[:attributes].each_pair do |attribute, name|
+ attribute(attribute, :as => name)
+ end if @options.has_key?(:attributes)
+ # Code
+ @options[:code].each_pair do |name, block|
+ code(name, &block)
+ end if @options.has_key?(:code)
+ # Children
+ @options[:child].each do |settings|
+ child(settings[:data], settings[:options], &settings[:block])
+ end if @options.has_key?(:child)
+ # Glues
+ @options[:glue].each do |settings|
+ glue(settings[:data], &settings[:block])
+ end if @options.has_key?(:glue)
+ # Extends
+ @options[:extends].each do |settings|
+ extends(settings[:file], settings[:options], &settings[:block])
+ end if @options.has_key?(:extends)
+
+ @_root_name ||= @_object.class.model_name.element
+ options[:root] ? { @_root_name => @_result } : @_result
+ end
+
+ # Indicates an attribute or method should be included in the json output
+ # attribute :foo, :as => "bar"
+ # attribute :foo => :bar
+ def attribute(*args)
+ if args.first.is_a?(Hash)
+ args.first.each_pair { |k,v| self.attribute(k, :as => v) }
+ else # array of attributes
+ options = args.extract_options!
+ args.each do |attribute|
+ @_result[options[:as] || attribute] = @_object.try(attribute) if @_object.respond_to?(attribute)
+ end
+ end
+ end
+ alias_method :attributes, :attribute
+
+ # Creates an arbitrary code node that is included in the json output
+ # code(:foo) { "bar" }
+ def code(name, &block)
+ @_result[name] = block.call(@_object)
+ end
+
+ # Creates a child node that is included in json output
+ # child(@user) { attribute :full_name }
+ # child(@user => :person) { ... }
+ def child(data, options={}, &block)
+ return false unless data.present?
+ name, object = data_name(data), data_object(data)
+ @_result[name] = self.object_to_hash(object, &block)
+ end
+
+ # Glues data from a child node to the json_output
+ # glue(@user) { attribute :full_name => :user_full_name }
+ def glue(data, &block)
+ return false unless data.present?
+ object = data_object(data)
+ glued_attributes = self.object_to_hash(object, &block)
+ @_result.merge!(glued_attributes)
+ end
+
+ # Renders a partial hash based on another jrb template
+ # partial("users/show", :object => @user)
+ def partial(file, options={}, &block)
+ source = File.read(Rails.root.join("app/views/" + file + ".json.jrb"))
+ self.object_to_hash(options[:object], source, &block)
+ end
+
+ # Extends an existing jrb template with additional attributes in the block
+ # extends("users/show") { attribute :full_name }
+ def extends(file, options={}, &block)
+ options = options.merge!(:object => @_object)
+ result = partial(file, options, &block)
+ @_result.merge!(result)
+ end
+
+ protected
+
+ # Returns a hash based representation of any data object given ejs template block
+ # object_to_hash(@user) { attribute :full_name } => { ... }
+ def object_to_hash(object, source=nil, &block)
+ @options[:generator].object_to_hash(object, source, &block)
+ end
+
+ # data_object(data) => <AR Object>
+ # data_object(@user => :person) => @user
+ # data_object(:user => :person) => @_object.send(:user)
+ def data_object(data)
+ data = (data.is_a?(Hash) && data.keys.one?) ? data.keys.first : data
+ data.is_a?(Symbol) ? @_object.send(data) : data
+ end
+
+ # data_name(data) => "user"
+ # data_name(@user => :person) => :person
+ # data_name(@users) => :user
+ def data_name(data)
+ return data.values.first if data.is_a?(Hash)
+ return data.first.class.model_name.element.pluralize if data.first.is_a?(ActiveRecord::Base)
+ data.class.model_name.element
+ end
+ end
+end
93 lib/rabl/engine.rb
View
@@ -0,0 +1,93 @@
+module JRB
+ class Engine
+ # Constructs a new ejs generator based on given vars, handler and declarations
+ def initialize(vars, handler, source_string=nil, &block)
+ @_vars = vars
+ @_handler = handler
+ @_options = { :handler => @_handler, :vars => @_vars, :generator => self }
+ self.copy_instance_variables_from(@_handler, [:@assigns, :@helpers]);
+ @_object = vars[:object] || instance_variable_get("@#{@_handler.controller.controller_name}")
+ instance_eval(source_string) if source_string.present?
+ instance_eval(&block) if block_given?
+ end
+
+ # Sets the object to be used as the data source for this template
+ # object(@user)
+ def object(data)
+ @_object = data
+ end
+
+ # Indicates an attribute or method should be included in the json output
+ # attribute :foo, :as => "bar"
+ # attribute :foo => :bar
+ def attribute(*args)
+ if args.first.is_a?(Hash)
+ args.first.each_pair { |k,v| self.attribute(k, :as => v) }
+ else # array of attributes
+ options = args.extract_options!
+ @_options[:attributes] ||= {}
+ args.each { |name| @_options[:attributes][name] = options[:as] || name }
+ end
+ end
+ alias_method :attributes, :attribute
+
+ # Creates an arbitrary code node that is included in the json output
+ # code(:foo) { "bar" }
+ def code(name, &block)
+ @_options[:code] ||= {}
+ @_options[:code][name] = block
+ end
+
+ # Creates a child node that is included in json output
+ # child(@user) { attribute :full_name }
+ def child(data, options={}, &block)
+ @_options[:child] ||= []
+ @_options[:child].push({ :data => data, :options => options, :block => block })
+ end
+
+ # Glues data from a child node to the json_output
+ # glue(@user) { attribute :full_name => :user_full_name }
+ def glue(data, &block)
+ @_options[:glue] ||= []
+ @_options[:glue].push({ :data => data, :block => block })
+ end
+
+ # Extends an existing jrb template with additional attributes in the block
+ # extends("users/show", :object => @user) { attribute :full_name }
+ def extends(file, options={}, &block)
+ @_options[:extends] ||= []
+ @_options[:extends].push({ :file => file, :options => options, :block => block })
+ end
+
+ # Renders a partial hash based on another jrb template
+ # partial("users/show", :object => @user)
+ def partial(file, options={}, &block)
+ source = File.read(Rails.root.join("app/views/" + file + ".json.jrb"))
+ self.object_to_hash(options[:object], source, &block)
+ end
+
+ # Returns a hash representation of the data object
+ # to_hash(:root => true)
+ def to_hash(options={})
+ if @_object.is_a?(ActiveRecord::Base)
+ JRB::Builder.new(@_object, @_options).to_hash(options)
+ elsif @_object.respond_to?(:each)
+ @_object.map { |object| JRB::Builder.new(object, @_options).to_hash(options) }
+ end
+ end
+
+ # Returns a json representation of the data object
+ # to_json(:root => true)
+ def to_json(options={})
+ options.reverse_merge!(:root => true)
+ to_hash(options).to_json
+ end
+
+ # Returns a hash based representation of any data object given ejs template block
+ # object_to_hash(@user) { attribute :full_name } => { ... }
+ def object_to_hash(object, source=nil, &block)
+ return object unless object.is_a?(ActiveRecord::Base) || object.first.is_a?(ActiveRecord::Base)
+ self.class.new(@_vars.merge(:object => object), @_handler, source, &block).to_hash(:root => false)
+ end
+ end
+end
18 lib/rabl/template.rb
View
@@ -0,0 +1,18 @@
+require 'action_view/base'
+require 'action_view/template'
+
+module ActionView
+ module TemplateHandlers
+ class JRBHandler < TemplateHandler
+ include Compilable
+
+ def compile(template) %{
+ ::JRB::Generator.new(assigns.merge(local_assigns), self) do
+ #{template.source}
+ end.to_#{template.format}
+ } end
+ end
+ end
+end
+
+ActionView::Template.register_template_handler :jrb, ActionView::TemplateHandlers::JRBHandler
Please sign in to comment.
Something went wrong with that request. Please try again.