rabl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rabl.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # RABL #
2
+
3
+ RABL is a ruby templating system for Rails that takes a different approach for generating JSON and other formats. Rather than using the ActiveRecord 'to_json', I generally find myself wanting to use a more expressive and flexible system for generating my Public APIs. This is especially true when I the json doesn't match to the exact schema defined in the database.
4
+
5
+ There were a few things in particular I wanted to do easily:
6
+
7
+ * Create arbitrary nodes named based on combining data in the object
8
+ * Include nodes only if a condition is met
9
+ * Pass arguments to methods and store the result as a node
10
+ * Include partial templates to reduce code duplication
11
+ * Easily rename attributes from their name in the model
12
+
13
+ This general templating system solves all of those problems.
14
+
15
+ ## Installation ##
16
+
17
+ gem install rabl
18
+
19
+ ## Usage ##
20
+
21
+ Basic usage of the templater:
22
+
23
+ # app/views/users/show.json.rabl
24
+ attributes :id, :foo, :bar
25
+
26
+ This will generate a json response with the attributes specified. You can also include arbitrary code:
27
+
28
+ # app/views/users/show.json.rabl
29
+ code :full_name do |u|
30
+ u.first_name + " " + u.last_name
31
+ end
32
+
33
+ You can also add children nodes:
34
+
35
+ child @posts => :foobar do
36
+ attributes :id, :title
37
+ end
38
+
39
+ or use existing model associations:
40
+
41
+ child :posts => :foobar do
42
+ attributes :id, :title
43
+ end
44
+
45
+ You can also extend other rabl templates to reduce duplication:
46
+
47
+ # app/views/users/show.json.rabl
48
+ child @address do
49
+ extends "address/item"
50
+ end
51
+
52
+ or get access to the hash representation of another object:
53
+
54
+ code :location do
55
+ { :place => partial("web/users/address", :object => @address) }
56
+ end
57
+
58
+ You can also append attributes to the root node:
59
+
60
+ glue @post do
61
+ attribute :id => :post_id
62
+ end
63
+
64
+ There is also the ability to extend other rabl templates with additional attributes:
65
+
66
+ extends "base"
67
+
68
+ code :release_year do |m|
69
+ date = m.release_date || m.series_start
70
+ date.try(:year)
71
+ end
72
+
73
+ ## Issues ##
74
+
75
+ * I am sloppy and once again failed to unit test this. Don't use it in production until I do obviously.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/lib/rabl.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'rabl/version'
2
+ require 'rabl/engine'
3
+ require 'rabl/builder'
4
+ require 'rabl/template' if defined?(Rails)
5
+
6
+ module Rabl
7
+ # Nothing yet
8
+ end
@@ -0,0 +1,117 @@
1
+ module JRB
2
+ class Builder
3
+ # Constructs a new ejs generator based on given object and options
4
+ def initialize(object, options, &block)
5
+ @_object = object
6
+ @_result = {}
7
+ @options = options
8
+ end
9
+
10
+ # Returns a hash representation of the data object
11
+ # to_hash(:root => true)
12
+ def to_hash(options={})
13
+ # Attributes
14
+ @options[:attributes].each_pair do |attribute, name|
15
+ attribute(attribute, :as => name)
16
+ end if @options.has_key?(:attributes)
17
+ # Code
18
+ @options[:code].each_pair do |name, block|
19
+ code(name, &block)
20
+ end if @options.has_key?(:code)
21
+ # Children
22
+ @options[:child].each do |settings|
23
+ child(settings[:data], settings[:options], &settings[:block])
24
+ end if @options.has_key?(:child)
25
+ # Glues
26
+ @options[:glue].each do |settings|
27
+ glue(settings[:data], &settings[:block])
28
+ end if @options.has_key?(:glue)
29
+ # Extends
30
+ @options[:extends].each do |settings|
31
+ extends(settings[:file], settings[:options], &settings[:block])
32
+ end if @options.has_key?(:extends)
33
+
34
+ @_root_name ||= @_object.class.model_name.element
35
+ options[:root] ? { @_root_name => @_result } : @_result
36
+ end
37
+
38
+ # Indicates an attribute or method should be included in the json output
39
+ # attribute :foo, :as => "bar"
40
+ # attribute :foo => :bar
41
+ def attribute(*args)
42
+ if args.first.is_a?(Hash)
43
+ args.first.each_pair { |k,v| self.attribute(k, :as => v) }
44
+ else # array of attributes
45
+ options = args.extract_options!
46
+ args.each do |attribute|
47
+ @_result[options[:as] || attribute] = @_object.try(attribute) if @_object.respond_to?(attribute)
48
+ end
49
+ end
50
+ end
51
+ alias_method :attributes, :attribute
52
+
53
+ # Creates an arbitrary code node that is included in the json output
54
+ # code(:foo) { "bar" }
55
+ def code(name, &block)
56
+ @_result[name] = block.call(@_object)
57
+ end
58
+
59
+ # Creates a child node that is included in json output
60
+ # child(@user) { attribute :full_name }
61
+ # child(@user => :person) { ... }
62
+ def child(data, options={}, &block)
63
+ return false unless data.present?
64
+ name, object = data_name(data), data_object(data)
65
+ @_result[name] = self.object_to_hash(object, &block)
66
+ end
67
+
68
+ # Glues data from a child node to the json_output
69
+ # glue(@user) { attribute :full_name => :user_full_name }
70
+ def glue(data, &block)
71
+ return false unless data.present?
72
+ object = data_object(data)
73
+ glued_attributes = self.object_to_hash(object, &block)
74
+ @_result.merge!(glued_attributes)
75
+ end
76
+
77
+ # Renders a partial hash based on another jrb template
78
+ # partial("users/show", :object => @user)
79
+ def partial(file, options={}, &block)
80
+ source = File.read(Rails.root.join("app/views/" + file + ".json.jrb"))
81
+ self.object_to_hash(options[:object], source, &block)
82
+ end
83
+
84
+ # Extends an existing jrb template with additional attributes in the block
85
+ # extends("users/show") { attribute :full_name }
86
+ def extends(file, options={}, &block)
87
+ options = options.merge!(:object => @_object)
88
+ result = partial(file, options, &block)
89
+ @_result.merge!(result)
90
+ end
91
+
92
+ protected
93
+
94
+ # Returns a hash based representation of any data object given ejs template block
95
+ # object_to_hash(@user) { attribute :full_name } => { ... }
96
+ def object_to_hash(object, source=nil, &block)
97
+ @options[:generator].object_to_hash(object, source, &block)
98
+ end
99
+
100
+ # data_object(data) => <AR Object>
101
+ # data_object(@user => :person) => @user
102
+ # data_object(:user => :person) => @_object.send(:user)
103
+ def data_object(data)
104
+ data = (data.is_a?(Hash) && data.keys.one?) ? data.keys.first : data
105
+ data.is_a?(Symbol) ? @_object.send(data) : data
106
+ end
107
+
108
+ # data_name(data) => "user"
109
+ # data_name(@user => :person) => :person
110
+ # data_name(@users) => :user
111
+ def data_name(data)
112
+ return data.values.first if data.is_a?(Hash)
113
+ return data.first.class.model_name.element.pluralize if data.first.is_a?(ActiveRecord::Base)
114
+ data.class.model_name.element
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,93 @@
1
+ module JRB
2
+ class Engine
3
+ # Constructs a new ejs generator based on given vars, handler and declarations
4
+ def initialize(vars, handler, source_string=nil, &block)
5
+ @_vars = vars
6
+ @_handler = handler
7
+ @_options = { :handler => @_handler, :vars => @_vars, :generator => self }
8
+ self.copy_instance_variables_from(@_handler, [:@assigns, :@helpers]);
9
+ @_object = vars[:object] || instance_variable_get("@#{@_handler.controller.controller_name}")
10
+ instance_eval(source_string) if source_string.present?
11
+ instance_eval(&block) if block_given?
12
+ end
13
+
14
+ # Sets the object to be used as the data source for this template
15
+ # object(@user)
16
+ def object(data)
17
+ @_object = data
18
+ end
19
+
20
+ # Indicates an attribute or method should be included in the json output
21
+ # attribute :foo, :as => "bar"
22
+ # attribute :foo => :bar
23
+ def attribute(*args)
24
+ if args.first.is_a?(Hash)
25
+ args.first.each_pair { |k,v| self.attribute(k, :as => v) }
26
+ else # array of attributes
27
+ options = args.extract_options!
28
+ @_options[:attributes] ||= {}
29
+ args.each { |name| @_options[:attributes][name] = options[:as] || name }
30
+ end
31
+ end
32
+ alias_method :attributes, :attribute
33
+
34
+ # Creates an arbitrary code node that is included in the json output
35
+ # code(:foo) { "bar" }
36
+ def code(name, &block)
37
+ @_options[:code] ||= {}
38
+ @_options[:code][name] = block
39
+ end
40
+
41
+ # Creates a child node that is included in json output
42
+ # child(@user) { attribute :full_name }
43
+ def child(data, options={}, &block)
44
+ @_options[:child] ||= []
45
+ @_options[:child].push({ :data => data, :options => options, :block => block })
46
+ end
47
+
48
+ # Glues data from a child node to the json_output
49
+ # glue(@user) { attribute :full_name => :user_full_name }
50
+ def glue(data, &block)
51
+ @_options[:glue] ||= []
52
+ @_options[:glue].push({ :data => data, :block => block })
53
+ end
54
+
55
+ # Extends an existing jrb template with additional attributes in the block
56
+ # extends("users/show", :object => @user) { attribute :full_name }
57
+ def extends(file, options={}, &block)
58
+ @_options[:extends] ||= []
59
+ @_options[:extends].push({ :file => file, :options => options, :block => block })
60
+ end
61
+
62
+ # Renders a partial hash based on another jrb template
63
+ # partial("users/show", :object => @user)
64
+ def partial(file, options={}, &block)
65
+ source = File.read(Rails.root.join("app/views/" + file + ".json.jrb"))
66
+ self.object_to_hash(options[:object], source, &block)
67
+ end
68
+
69
+ # Returns a hash representation of the data object
70
+ # to_hash(:root => true)
71
+ def to_hash(options={})
72
+ if @_object.is_a?(ActiveRecord::Base)
73
+ JRB::Builder.new(@_object, @_options).to_hash(options)
74
+ elsif @_object.respond_to?(:each)
75
+ @_object.map { |object| JRB::Builder.new(object, @_options).to_hash(options) }
76
+ end
77
+ end
78
+
79
+ # Returns a json representation of the data object
80
+ # to_json(:root => true)
81
+ def to_json(options={})
82
+ options.reverse_merge!(:root => true)
83
+ to_hash(options).to_json
84
+ end
85
+
86
+ # Returns a hash based representation of any data object given ejs template block
87
+ # object_to_hash(@user) { attribute :full_name } => { ... }
88
+ def object_to_hash(object, source=nil, &block)
89
+ return object unless object.is_a?(ActiveRecord::Base) || object.first.is_a?(ActiveRecord::Base)
90
+ self.class.new(@_vars.merge(:object => object), @_handler, source, &block).to_hash(:root => false)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,18 @@
1
+ require 'action_view/base'
2
+ require 'action_view/template'
3
+
4
+ module ActionView
5
+ module TemplateHandlers
6
+ class JRBHandler < TemplateHandler
7
+ include Compilable
8
+
9
+ def compile(template) %{
10
+ ::JRB::Generator.new(assigns.merge(local_assigns), self) do
11
+ #{template.source}
12
+ end.to_#{template.format}
13
+ } end
14
+ end
15
+ end
16
+ end
17
+
18
+ ActionView::Template.register_template_handler :jrb, ActionView::TemplateHandlers::JRBHandler
@@ -0,0 +1,3 @@
1
+ module Rabl
2
+ VERSION = "0.0.1"
3
+ end
data/rabl.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rabl/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rabl"
7
+ s.version = Rabl::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Nathan Esquenazi"]
10
+ s.email = ["nesquena@gmail.com"]
11
+ s.homepage = "https://github.com/nesquena/rabl"
12
+ s.summary = %q{General ruby templating for json or xml}
13
+ s.description = %q{General ruby templating for json or xml}
14
+
15
+ s.rubyforge_project = "rabl"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rabl
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Nathan Esquenazi
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-12 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: General ruby templating for json or xml
22
+ email:
23
+ - nesquena@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - README.md
34
+ - Rakefile
35
+ - lib/rabl.rb
36
+ - lib/rabl/builder.rb
37
+ - lib/rabl/engine.rb
38
+ - lib/rabl/template.rb
39
+ - lib/rabl/version.rb
40
+ - rabl.gemspec
41
+ homepage: https://github.com/nesquena/rabl
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project: rabl
70
+ rubygems_version: 1.7.2
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: General ruby templating for json or xml
74
+ test_files: []
75
+