rabl 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+