jbuilder-2.3.1/0000755000004100000410000000000012556325572013361 5ustar www-datawww-datajbuilder-2.3.1/Rakefile0000644000004100000410000000077612556325572015040 0ustar www-datawww-datarequire "bundler/setup" require "bundler/gem_tasks" require "rake/testtask" if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"] require "appraisal/task" Appraisal::Task.new task default: :appraisal else Rake::TestTask.new do |test| require "rails/version" test.libs << "test" if Rails::VERSION::MAJOR == 3 test.test_files = %w[test/jbuilder_template_test.rb test/jbuilder_test.rb] else test.test_files = FileList["test/*_test.rb"] end end task default: :test end jbuilder-2.3.1/Gemfile0000644000004100000410000000015112556325572014651 0ustar www-datawww-datasource "https://rubygems.org" gemspec gem "rake" gem "mocha", require: false gem "appraisal" gem "pry" jbuilder-2.3.1/MIT-LICENSE0000644000004100000410000000207412556325572015020 0ustar www-datawww-dataCopyright (c) 2011-2014 David Heinemeier Hansson, 37signals Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jbuilder-2.3.1/.travis.yml0000644000004100000410000000140012556325572015465 0ustar www-datawww-datalanguage: ruby rvm: - 1.9 - 2.0 - 2.1 - 2.2 - ruby-head - jruby-19mode - rbx gemfile: - gemfiles/rails_3_0.gemfile - gemfiles/rails_3_1.gemfile - gemfiles/rails_3_2.gemfile - gemfiles/rails_4_0.gemfile - gemfiles/rails_4_1.gemfile - gemfiles/rails_4_2.gemfile - gemfiles/rails_edge.gemfile matrix: allow_failures: - rvm: jruby-19mode - rvm: rbx - rvm: ruby-head fast_finish: true exclude: - rvm: 1.9 gemfile: gemfiles/rails_edge.gemfile - rvm: 2.0 gemfile: gemfiles/rails_edge.gemfile - rvm: 2.1 gemfile: gemfiles/rails_edge.gemfile - rvm: jruby-19mode gemfile: gemfiles/rails_edge.gemfile - rvm: rbx gemfile: gemfiles/rails_edge.gemfile notifications: email: false jbuilder-2.3.1/lib/0000755000004100000410000000000012556325572014127 5ustar www-datawww-datajbuilder-2.3.1/lib/jbuilder/0000755000004100000410000000000012556325572015727 5ustar www-datawww-datajbuilder-2.3.1/lib/jbuilder/errors.rb0000644000004100000410000000060112556325572017565 0ustar www-datawww-datarequire 'jbuilder/jbuilder' class Jbuilder class NullError < ::NoMethodError def self.build(key) message = "Failed to add #{key.to_s.inspect} property to null object" new(message) end end class ArrayError < ::StandardError def self.build(key) message = "Failed to add #{key.to_s.inspect} property to an array" new(message) end end end jbuilder-2.3.1/lib/jbuilder/jbuilder_template.rb0000644000004100000410000001176212556325572021756 0ustar www-datawww-datarequire 'jbuilder/jbuilder' require 'action_dispatch/http/mime_type' require 'active_support/cache' class JbuilderTemplate < Jbuilder class << self attr_accessor :template_lookup_options end self.template_lookup_options = { handlers: [:jbuilder] } def initialize(context, *args) @context = context super *args end def partial!(*args) if args.one? && _is_active_model?(args.first) _render_active_model_partial args.first else _render_explicit_partial *args end end # Caches the json constructed within the block passed. Has the same signature as the `cache` helper # method in `ActionView::Helpers::CacheHelper` and so can be used in the same way. # # Example: # # json.cache! ['v1', @person], expires_in: 10.minutes do # json.extract! @person, :name, :age # end def cache!(key=nil, options={}) if @context.controller.perform_caching value = ::Rails.cache.fetch(_cache_key(key, options), options) do _scope { yield self } end merge! value else yield end end # Conditionally caches the json depending in the condition given as first parameter. Has the same # signature as the `cache` helper method in `ActionView::Helpers::CacheHelper` and so can be used in # the same way. # # Example: # # json.cache_if! !admin?, @person, expires_in: 10.minutes do # json.extract! @person, :name, :age # end def cache_if!(condition, *args) condition ? cache!(*args, &::Proc.new) : yield end def array!(collection = [], *args) options = args.first if args.one? && _partial_options?(options) partial! options.merge(collection: collection) else super end end def set!(name, object = BLANK, *args) options = args.first if args.one? && _partial_options?(options) _set_inline_partial name, object, options else super end end private def _render_partial_with_options(options) options.reverse_merge! locals: {} options.reverse_merge! ::JbuilderTemplate.template_lookup_options as = options[:as] if as && options.key?(:collection) as = as.to_sym collection = options.delete(:collection) locals = options.delete(:locals) array! collection do |member| member_locals = locals.clone member_locals.merge! collection: collection member_locals.merge! as => member _render_partial options.merge(locals: member_locals) end else _render_partial options end end def _render_partial(options) options[:locals].merge! json: self @context.render options end def _cache_key(key, options) key = _fragment_name_with_digest(key, options) key = url_for(key).split('://', 2).last if ::Hash === key ::ActiveSupport::Cache.expand_cache_key(key, :jbuilder) end def _fragment_name_with_digest(key, options) if @context.respond_to?(:cache_fragment_name) # Current compatibility, fragment_name_with_digest is private again and cache_fragment_name # should be used instead. @context.cache_fragment_name(key, options) elsif @context.respond_to?(:fragment_name_with_digest) # Backwards compatibility for period of time when fragment_name_with_digest was made public. @context.fragment_name_with_digest(key) else key end end def _partial_options?(options) ::Hash === options && options.key?(:as) && options.key?(:partial) end def _is_active_model?(object) object.class.respond_to?(:model_name) && object.respond_to?(:to_partial_path) end def _set_inline_partial(name, object, options) value = if object.nil? [] elsif _is_collection?(object) _scope{ _render_partial_with_options options.merge(collection: object) } else locals = ::Hash[options[:as], object] _scope{ _render_partial options.merge(locals: locals) } end set! name, value end def _render_explicit_partial(name_or_options, locals = {}) case name_or_options when ::Hash # partial! partial: 'name', foo: 'bar' options = name_or_options else # partial! 'name', locals: {foo: 'bar'} if locals.one? && (locals.keys.first == :locals) options = locals.merge(partial: name_or_options) else options = { partial: name_or_options, locals: locals } end # partial! 'name', foo: 'bar' as = locals.delete(:as) options[:as] = as if as.present? options[:collection] = locals[:collection] if locals.key?(:collection) end _render_partial_with_options options end def _render_active_model_partial(object) @context.render object, json: self end end class JbuilderHandler cattr_accessor :default_format self.default_format = Mime::JSON def self.call(template) # this juggling is required to keep line numbers right in the error %{__already_defined = defined?(json); json||=JbuilderTemplate.new(self); #{template.source} json.target! unless (__already_defined && __already_defined != "method")} end end jbuilder-2.3.1/lib/jbuilder/blank.rb0000644000004100000410000000020312556325572017336 0ustar www-datawww-dataclass Jbuilder class Blank def ==(other) super || Blank === other end def empty? true end end end jbuilder-2.3.1/lib/jbuilder/key_formatter.rb0000644000004100000410000000125512556325572021132 0ustar www-datawww-datarequire 'jbuilder/jbuilder' require 'active_support/core_ext/array' class Jbuilder class KeyFormatter def initialize(*args) @format = {} @cache = {} options = args.extract_options! args.each do |name| @format[name] = [] end options.each do |name, paramaters| @format[name] = paramaters end end def initialize_copy(original) @cache = {} end def format(key) @cache[key] ||= @format.inject(key.to_s) do |result, args| func, args = args if ::Proc === func func.call result, *args else result.send func, *args end end end end end jbuilder-2.3.1/lib/jbuilder/railtie.rb0000644000004100000410000000155412556325572017712 0ustar www-datawww-datarequire 'rails/railtie' require 'jbuilder/jbuilder_template' class Jbuilder class Railtie < ::Rails::Railtie initializer :jbuilder do |app| ActiveSupport.on_load :action_view do ActionView::Template.register_template_handler :jbuilder, JbuilderHandler require 'jbuilder/dependency_tracker' end if app.config.respond_to?(:api_only) && app.config.api_only ActiveSupport.on_load :action_controller do include ActionView::Rendering include ActionController::Helpers include ActionController::ImplicitRender end end end if Rails::VERSION::MAJOR >= 4 generators do |app| Rails::Generators.configure! app.config.generators Rails::Generators.hidden_namespaces.uniq! require 'generators/rails/scaffold_controller_generator' end end end end jbuilder-2.3.1/lib/jbuilder/jbuilder.rb0000644000004100000410000000027312556325572020056 0ustar www-datawww-dataJbuilder = Class.new(begin require 'active_support/proxy_object' ActiveSupport::ProxyObject rescue LoadError require 'active_support/basic_object' ActiveSupport::BasicObject end) jbuilder-2.3.1/lib/jbuilder/dependency_tracker.rb0000644000004100000410000000326512556325572022113 0ustar www-datawww-datarequire 'jbuilder/jbuilder' dependency_tracker = false begin require 'action_view' require 'action_view/dependency_tracker' dependency_tracker = ::ActionView::DependencyTracker rescue LoadError begin require 'cache_digests' dependency_tracker = ::CacheDigests::DependencyTracker rescue LoadError end end if dependency_tracker class Jbuilder module DependencyTrackerMethods # Matches: # json.partial! "messages/message" # json.partial!('messages/message') # DIRECT_RENDERS = / \w+\.partial! # json.partial! \(?\s* # optional parenthesis (['"])([^'"]+)\1 # quoted value /x # Matches: # json.partial! partial: "comments/comment" # json.comments @post.comments, partial: "comments/comment", as: :comment # json.array! @posts, partial: "posts/post", as: :post # = render partial: "account" # INDIRECT_RENDERS = / (?::partial\s*=>|partial:) # partial: or :partial => \s* # optional whitespace (['"])([^'"]+)\1 # quoted value /x def dependencies direct_dependencies + indirect_dependencies + explicit_dependencies end private def direct_dependencies source.scan(DIRECT_RENDERS).map(&:second) end def indirect_dependencies source.scan(INDIRECT_RENDERS).map(&:second) end end end ::Jbuilder::DependencyTracker = Class.new(dependency_tracker::ERBTracker) ::Jbuilder::DependencyTracker.send :include, ::Jbuilder::DependencyTrackerMethods dependency_tracker.register_tracker :jbuilder, ::Jbuilder::DependencyTracker end jbuilder-2.3.1/lib/generators/0000755000004100000410000000000012556325572016300 5ustar www-datawww-datajbuilder-2.3.1/lib/generators/rails/0000755000004100000410000000000012556325572017412 5ustar www-datawww-datajbuilder-2.3.1/lib/generators/rails/jbuilder_generator.rb0000644000004100000410000000256712556325572023617 0ustar www-datawww-datarequire 'rails/generators/named_base' require 'rails/generators/resource_helpers' module Rails module Generators class JbuilderGenerator < NamedBase # :nodoc: include Rails::Generators::ResourceHelpers source_root File.expand_path('../templates', __FILE__) argument :attributes, type: :array, default: [], banner: 'field:type field:type' def create_root_folder path = File.join('app/views', controller_file_path) empty_directory path unless File.directory?(path) end def copy_view_files %w(index show).each do |view| filename = filename_with_extensions(view) template filename, File.join('app/views', controller_file_path, filename) end end protected def attributes_names [:id] + super end def filename_with_extensions(name) [name, :json, :jbuilder] * '.' end def attributes_list_with_timestamps attributes_list(attributes_names + %w(created_at updated_at)) end def attributes_list(attributes = attributes_names) if self.attributes.any? {|attr| attr.name == 'password' && attr.type == :digest} attributes = attributes.reject {|name| %w(password password_confirmation).include? name} end attributes.map { |a| ":#{a}"} * ', ' end end end end jbuilder-2.3.1/lib/generators/rails/templates/0000755000004100000410000000000012556325572021410 5ustar www-datawww-datajbuilder-2.3.1/lib/generators/rails/templates/index.json.jbuilder0000644000004100000410000000034312556325572025211 0ustar www-datawww-datajson.array!(@<%= plural_table_name %>) do |<%= singular_table_name %>| json.extract! <%= singular_table_name %>, <%= attributes_list %> json.url <%= singular_table_name %>_url(<%= singular_table_name %>, format: :json) end jbuilder-2.3.1/lib/generators/rails/templates/api_controller.rb0000644000004100000410000000367012556325572024757 0ustar www-datawww-data<% if namespaced? -%> require_dependency "<%= namespaced_file_path %>/application_controller" <% end -%> <% module_namespacing do -%> class <%= controller_class_name %>Controller < ApplicationController before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy] # GET <%= route_url %> # GET <%= route_url %>.json def index @<%= plural_table_name %> = <%= orm_class.all(class_name) %> end # GET <%= route_url %>/1 # GET <%= route_url %>/1.json def show end # POST <%= route_url %> # POST <%= route_url %>.json def create @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> if @<%= orm_instance.save %> render :show, status: :created, location: <%= "@#{singular_table_name}" %> else render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity end end # PATCH/PUT <%= route_url %>/1 # PATCH/PUT <%= route_url %>/1.json def update if @<%= orm_instance.update("#{singular_table_name}_params") %> render :show, status: :ok, location: <%= "@#{singular_table_name}" %> else render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity end end # DELETE <%= route_url %>/1 # DELETE <%= route_url %>/1.json def destroy @<%= orm_instance.destroy %> end private # Use callbacks to share common setup or constraints between actions. def set_<%= singular_table_name %> @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> end # Never trust parameters from the scary internet, only allow the white list through. def <%= "#{singular_table_name}_params" %> <%- if attributes_names.empty? -%> params[<%= ":#{singular_table_name}" %>] <%- else -%> params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) <%- end -%> end end <% end -%> jbuilder-2.3.1/lib/generators/rails/templates/show.json.jbuilder0000644000004100000410000000012212556325572025055 0ustar www-datawww-datajson.extract! @<%= singular_table_name %>, <%= attributes_list_with_timestamps %> jbuilder-2.3.1/lib/generators/rails/templates/controller.rb0000644000004100000410000000540112556325572024120 0ustar www-datawww-data<% if namespaced? -%> require_dependency "<%= namespaced_file_path %>/application_controller" <% end -%> <% module_namespacing do -%> class <%= controller_class_name %>Controller < ApplicationController before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy] # GET <%= route_url %> # GET <%= route_url %>.json def index @<%= plural_table_name %> = <%= orm_class.all(class_name) %> end # GET <%= route_url %>/1 # GET <%= route_url %>/1.json def show end # GET <%= route_url %>/new def new @<%= singular_table_name %> = <%= orm_class.build(class_name) %> end # GET <%= route_url %>/1/edit def edit end # POST <%= route_url %> # POST <%= route_url %>.json def create @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> respond_to do |format| if @<%= orm_instance.save %> format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> } format.json { render :show, status: :created, location: <%= "@#{singular_table_name}" %> } else format.html { render :new } format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity } end end end # PATCH/PUT <%= route_url %>/1 # PATCH/PUT <%= route_url %>/1.json def update respond_to do |format| if @<%= orm_instance.update("#{singular_table_name}_params") %> format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> } format.json { render :show, status: :ok, location: <%= "@#{singular_table_name}" %> } else format.html { render :edit } format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity } end end end # DELETE <%= route_url %>/1 # DELETE <%= route_url %>/1.json def destroy @<%= orm_instance.destroy %> respond_to do |format| format.html { redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %> } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_<%= singular_table_name %> @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> end # Never trust parameters from the scary internet, only allow the white list through. def <%= "#{singular_table_name}_params" %> <%- if attributes_names.empty? -%> params[<%= ":#{singular_table_name}" %>] <%- else -%> params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) <%- end -%> end end <% end -%> jbuilder-2.3.1/lib/generators/rails/scaffold_controller_generator.rb0000644000004100000410000000046212556325572026033 0ustar www-datawww-datarequire 'rails/generators' require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator' module Rails module Generators class ScaffoldControllerGenerator source_paths << File.expand_path('../templates', __FILE__) hook_for :jbuilder, default: true end end end jbuilder-2.3.1/lib/jbuilder.rb0000644000004100000410000002063712556325572016264 0ustar www-datawww-datarequire 'jbuilder/jbuilder' require 'jbuilder/blank' require 'jbuilder/key_formatter' require 'jbuilder/errors' require 'multi_json' require 'ostruct' class Jbuilder @@key_formatter = nil @@ignore_nil = false def initialize(options = {}) @attributes = {} @key_formatter = options.fetch(:key_formatter){ @@key_formatter ? @@key_formatter.clone : nil} @ignore_nil = options.fetch(:ignore_nil, @@ignore_nil) yield self if ::Kernel.block_given? end # Yields a builder and automatically turns the result into a JSON string def self.encode(*args, &block) new(*args, &block).target! end BLANK = Blank.new NON_ENUMERABLES = [ ::Struct, ::OpenStruct ].to_set def set!(key, value = BLANK, *args) result = if ::Kernel.block_given? if !_blank?(value) # json.comments @post.comments { |comment| ... } # { "comments": [ { ... }, { ... } ] } _scope{ array! value, &::Proc.new } else # json.comments { ... } # { "comments": ... } _merge_block(key){ yield self } end elsif args.empty? if ::Jbuilder === value # json.age 32 # json.person another_jbuilder # { "age": 32, "person": { ... } value.attributes! else # json.age 32 # { "age": 32 } value end elsif _is_collection?(value) # json.comments @post.comments, :content, :created_at # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] } _scope{ array! value, *args } else # json.author @post.creator, :name, :email_address # { "author": { "name": "David", "email_address": "david@loudthinking.com" } } _merge_block(key){ extract! value, *args } end _set_value key, result end def method_missing(*args) if ::Kernel.block_given? set! *args, &::Proc.new else set! *args end end # Specifies formatting to be applied to the key. Passing in a name of a function # will cause that function to be called on the key. So :upcase will upper case # the key. You can also pass in lambdas for more complex transformations. # # Example: # # json.key_format! :upcase # json.author do # json.name "David" # json.age 32 # end # # { "AUTHOR": { "NAME": "David", "AGE": 32 } } # # You can pass parameters to the method using a hash pair. # # json.key_format! camelize: :lower # json.first_name "David" # # { "firstName": "David" } # # Lambdas can also be used. # # json.key_format! ->(key){ "_" + key } # json.first_name "David" # # { "_first_name": "David" } # def key_format!(*args) @key_formatter = KeyFormatter.new(*args) end # Same as the instance method key_format! except sets the default. def self.key_format(*args) @@key_formatter = KeyFormatter.new(*args) end # If you want to skip adding nil values to your JSON hash. This is useful # for JSON clients that don't deal well with nil values, and would prefer # not to receive keys which have null values. # # Example: # json.ignore_nil! false # json.id User.new.id # # { "id": null } # # json.ignore_nil! # json.id User.new.id # # {} # def ignore_nil!(value = true) @ignore_nil = value end # Same as instance method ignore_nil! except sets the default. def self.ignore_nil(value = true) @@ignore_nil = value end # Turns the current element into an array and yields a builder to add a hash. # # Example: # # json.comments do # json.child! { json.content "hello" } # json.child! { json.content "world" } # end # # { "comments": [ { "content": "hello" }, { "content": "world" } ]} # # More commonly, you'd use the combined iterator, though: # # json.comments(@post.comments) do |comment| # json.content comment.formatted_content # end def child! @attributes = [] unless ::Array === @attributes @attributes << _scope{ yield self } end # Turns the current element into an array and iterates over the passed collection, adding each iteration as # an element of the resulting array. # # Example: # # json.array!(@people) do |person| # json.name person.name # json.age calculate_age(person.birthday) # end # # [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ] # # If you are using Ruby 1.9+, you can use the call syntax instead of an explicit extract! call: # # json.(@people) { |person| ... } # # It's generally only needed to use this method for top-level arrays. If you have named arrays, you can do: # # json.people(@people) do |person| # json.name person.name # json.age calculate_age(person.birthday) # end # # { "people": [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ] } # # If you omit the block then you can set the top level array directly: # # json.array! [1, 2, 3] # # [1,2,3] def array!(collection = [], *attributes) array = if collection.nil? [] elsif ::Kernel.block_given? _map_collection(collection, &::Proc.new) elsif attributes.any? _map_collection(collection) { |element| extract! element, *attributes } else collection.to_a end merge! array end # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON. # # Example: # # @person = Struct.new(:name, :age).new('David', 32) # # or you can utilize a Hash # # @person = { name: 'David', age: 32 } # # json.extract! @person, :name, :age # # { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } # # You can also use the call syntax instead of an explicit extract! call: # # json.(@person, :name, :age) def extract!(object, *attributes) if ::Hash === object _extract_hash_values(object, attributes) else _extract_method_values(object, attributes) end end def call(object, *attributes) if ::Kernel.block_given? array! object, &::Proc.new else extract! object, *attributes end end # Returns the nil JSON. def nil! @attributes = nil end alias_method :null!, :nil! # Returns the attributes of the current builder. def attributes! @attributes end # Merges hash or array into current builder. def merge!(hash_or_array) @attributes = _merge_values(@attributes, hash_or_array) end # Encodes the current builder as JSON. def target! ::MultiJson.dump(@attributes) end private def _extract_hash_values(object, attributes) attributes.each{ |key| _set_value key, object.fetch(key) } end def _extract_method_values(object, attributes) attributes.each{ |key| _set_value key, object.public_send(key) } end def _merge_block(key) current_value = _blank? ? BLANK : @attributes.fetch(_key(key), BLANK) raise NullError.build(key) if current_value.nil? new_value = _scope{ yield self } _merge_values(current_value, new_value) end def _merge_values(current_value, updates) if _blank?(updates) current_value elsif _blank?(current_value) || updates.nil? updates elsif ::Array === updates ::Array === current_value ? current_value + updates : updates elsif ::Hash === current_value current_value.merge(updates) else raise "Can't merge #{updates.inspect} with #{current_value.inspect}" end end def _key(key) @key_formatter ? @key_formatter.format(key) : key.to_s end def _set_value(key, value) raise NullError.build(key) if @attributes.nil? raise ArrayError.build(key) if ::Array === @attributes return if @ignore_nil && value.nil? or _blank?(value) @attributes = {} if _blank? @attributes[_key(key)] = value end def _map_collection(collection) collection.map do |element| _scope{ yield element } end - [BLANK] end def _scope parent_attributes, parent_formatter = @attributes, @key_formatter @attributes = BLANK yield @attributes ensure @attributes, @key_formatter = parent_attributes, parent_formatter end def _is_collection?(object) _object_respond_to?(object, :map, :count) && NON_ENUMERABLES.none?{ |klass| klass === object } end def _blank?(value=@attributes) BLANK == value end def _object_respond_to?(object, *methods) methods.all?{ |m| object.respond_to?(m) } end end require 'jbuilder/railtie' if defined?(Rails) jbuilder-2.3.1/gemfiles/0000755000004100000410000000000012556325572015154 5ustar www-datawww-datajbuilder-2.3.1/gemfiles/rails_4_1.gemfile0000644000004100000410000000037112556325572020264 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "rake" gem "mocha", :require => false gem "appraisal" gem "pry" gem "railties", "~> 4.1.0" gem "actionpack", "~> 4.1.0" gem "activemodel", "~> 4.1.0" gemspec :path => "../" jbuilder-2.3.1/gemfiles/rails_4_2.gemfile0000644000004100000410000000037112556325572020265 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "rake" gem "mocha", :require => false gem "appraisal" gem "pry" gem "railties", "~> 4.2.0" gem "actionpack", "~> 4.2.0" gem "activemodel", "~> 4.2.0" gemspec :path => "../" jbuilder-2.3.1/gemfiles/rails_3_0.gemfile0000644000004100000410000000041112556325572020255 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "rake" gem "mocha", :require => false gem "appraisal" gem "pry" gem "test-unit" gem "railties", "~> 3.0.0" gem "actionpack", "~> 3.0.0" gem "activemodel", "~> 3.0.0" gemspec :path => "../" jbuilder-2.3.1/gemfiles/rails_3_2.gemfile0000644000004100000410000000041112556325572020257 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "rake" gem "mocha", :require => false gem "appraisal" gem "pry" gem "test-unit" gem "railties", "~> 3.2.0" gem "actionpack", "~> 3.2.0" gem "activemodel", "~> 3.2.0" gemspec :path => "../" jbuilder-2.3.1/gemfiles/rails_4_0.gemfile0000644000004100000410000000037112556325572020263 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "rake" gem "mocha", :require => false gem "appraisal" gem "pry" gem "railties", "~> 4.0.0" gem "actionpack", "~> 4.0.0" gem "activemodel", "~> 4.0.0" gemspec :path => "../" jbuilder-2.3.1/gemfiles/rails_edge.gemfile0000644000004100000410000000034312556325572020604 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "rake" gem "mocha", :require => false gem "appraisal" gem "rails", :github => "rails/rails" gem "arel", :github => "rails/arel" gemspec :path => "../" jbuilder-2.3.1/gemfiles/rails_3_1.gemfile0000644000004100000410000000041112556325572020256 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "rake" gem "mocha", :require => false gem "appraisal" gem "pry" gem "test-unit" gem "railties", "~> 3.1.0" gem "actionpack", "~> 3.1.0" gem "activemodel", "~> 3.1.0" gemspec :path => "../" jbuilder-2.3.1/metadata.yml0000644000004100000410000000615012556325572015666 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: jbuilder version: !ruby/object:Gem::Version version: 2.3.1 platform: ruby authors: - David Heinemeier Hansson - Pavel Pravosud autorequire: bindir: bin cert_chain: [] date: 2015-07-06 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: activesupport requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 3.0.0 - - "<" - !ruby/object:Gem::Version version: '5' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 3.0.0 - - "<" - !ruby/object:Gem::Version version: '5' - !ruby/object:Gem::Dependency name: multi_json requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.2' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.2' description: email: - david@37signals.com - pavel@pravosud.com executables: [] extensions: [] extra_rdoc_files: [] files: - ".gitignore" - ".travis.yml" - Appraisals - CHANGELOG.md - Gemfile - MIT-LICENSE - README.md - Rakefile - gemfiles/rails_3_0.gemfile - gemfiles/rails_3_1.gemfile - gemfiles/rails_3_2.gemfile - gemfiles/rails_4_0.gemfile - gemfiles/rails_4_1.gemfile - gemfiles/rails_4_2.gemfile - gemfiles/rails_edge.gemfile - jbuilder.gemspec - lib/generators/rails/jbuilder_generator.rb - lib/generators/rails/scaffold_controller_generator.rb - lib/generators/rails/templates/api_controller.rb - lib/generators/rails/templates/controller.rb - lib/generators/rails/templates/index.json.jbuilder - lib/generators/rails/templates/show.json.jbuilder - lib/jbuilder.rb - lib/jbuilder/blank.rb - lib/jbuilder/dependency_tracker.rb - lib/jbuilder/errors.rb - lib/jbuilder/jbuilder.rb - lib/jbuilder/jbuilder_template.rb - lib/jbuilder/key_formatter.rb - lib/jbuilder/railtie.rb - test/jbuilder_dependency_tracker_test.rb - test/jbuilder_generator_test.rb - test/jbuilder_template_test.rb - test/jbuilder_test.rb - test/scaffold_api_controller_generator_test.rb - test/scaffold_controller_generator_test.rb - test/test_helper.rb homepage: https://github.com/rails/jbuilder licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 1.9.3 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.4.8 signing_key: specification_version: 4 summary: Create JSON structures via a Builder-style DSL test_files: - test/jbuilder_dependency_tracker_test.rb - test/jbuilder_generator_test.rb - test/jbuilder_template_test.rb - test/jbuilder_test.rb - test/scaffold_api_controller_generator_test.rb - test/scaffold_controller_generator_test.rb - test/test_helper.rb jbuilder-2.3.1/test/0000755000004100000410000000000012556325572014340 5ustar www-datawww-datajbuilder-2.3.1/test/jbuilder_template_test.rb0000644000004100000410000002304112556325572021417 0ustar www-datawww-datarequire "test_helper" require "mocha/setup" require "active_model" require "action_view" require "action_view/testing/resolvers" require "active_support/cache" require "jbuilder/jbuilder_template" BLOG_POST_PARTIAL = <<-JBUILDER json.extract! blog_post, :id, :body json.author do first_name, last_name = blog_post.author_name.split(nil, 2) json.first_name first_name json.last_name last_name end JBUILDER COLLECTION_PARTIAL = <<-JBUILDER json.extract! collection, :id, :name JBUILDER RACER_PARTIAL = <<-JBUILDER json.extract! racer, :id, :name JBUILDER class Racer extend ActiveModel::Naming include ActiveModel::Conversion def initialize(id, name) @id, @name = id, name end attr_reader :id, :name end BlogPost = Struct.new(:id, :body, :author_name) Collection = Struct.new(:id, :name) blog_authors = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle BLOG_POST_COLLECTION = Array.new(10){ |i| BlogPost.new(i+1, "post body #{i+1}", blog_authors.next) } COLLECTION_COLLECTION = Array.new(5){ |i| Collection.new(i+1, "collection #{i+1}") } ActionView::Template.register_template_handler :jbuilder, JbuilderHandler PARTIALS = { "_partial.json.jbuilder" => "foo ||= 'hello'; json.content foo", "_blog_post.json.jbuilder" => BLOG_POST_PARTIAL, "racers/_racer.json.jbuilder" => RACER_PARTIAL, "_collection.json.jbuilder" => COLLECTION_PARTIAL } module Rails def self.cache @cache ||= ActiveSupport::Cache::MemoryStore.new end end class JbuilderTemplateTest < ActionView::TestCase setup do @context = self Rails.cache.clear end def jbuild(source) @rendered = [] partials = PARTIALS.clone partials["test.json.jbuilder"] = source resolver = ActionView::FixtureResolver.new(partials) lookup_context.view_paths = [resolver] template = ActionView::Template.new(source, "test", JbuilderHandler, virtual_path: "test") json = template.render(self, {}).strip MultiJson.load(json) end def undef_context_methods(*names) self.class_eval do names.each do |name| undef_method name.to_sym if method_defined?(name.to_sym) end end end def assert_collection_rendered(result, context = nil) result = result.fetch(context) if context assert_equal 10, result.length assert_equal Array, result.class assert_equal "post body 5", result[4]["body"] assert_equal "Heinemeier Hansson", result[2]["author"]["last_name"] assert_equal "Pavel", result[5]["author"]["first_name"] end test "rendering" do result = jbuild(<<-JBUILDER) json.content "hello" JBUILDER assert_equal "hello", result["content"] end test "key_format! with parameter" do result = jbuild(<<-JBUILDER) json.key_format! camelize: [:lower] json.camel_style "for JS" JBUILDER assert_equal ["camelStyle"], result.keys end test "key_format! propagates to child elements" do result = jbuild(<<-JBUILDER) json.key_format! :upcase json.level1 "one" json.level2 do json.value "two" end JBUILDER assert_equal "one", result["LEVEL1"] assert_equal "two", result["LEVEL2"]["VALUE"] end test "partial! renders partial" do result = jbuild(<<-JBUILDER) json.partial! "partial" JBUILDER assert_equal "hello", result["content"] end test "partial! + locals via :locals option" do result = jbuild(<<-JBUILDER) json.partial! "partial", locals: { foo: "howdy" } JBUILDER assert_equal "howdy", result["content"] end test "partial! + locals without :locals key" do result = jbuild(<<-JBUILDER) json.partial! "partial", foo: "goodbye" JBUILDER assert_equal "goodbye", result["content"] end test "partial! renders collections" do result = jbuild(<<-JBUILDER) json.partial! "blog_post", collection: BLOG_POST_COLLECTION, as: :blog_post JBUILDER assert_collection_rendered result end test "partial! renders collections when as argument is a string" do result = jbuild(<<-JBUILDER) json.partial! "blog_post", collection: BLOG_POST_COLLECTION, as: "blog_post" JBUILDER assert_collection_rendered result end test "partial! renders collections as collections" do result = jbuild(<<-JBUILDER) json.partial! "collection", collection: COLLECTION_COLLECTION, as: :collection JBUILDER assert_equal 5, result.length end test "partial! renders as empty array for nil-collection" do result = jbuild(<<-JBUILDER) json.partial! "blog_post", collection: nil, as: :blog_post JBUILDER assert_equal [], result end test "partial! renders collection (alt. syntax)" do result = jbuild(<<-JBUILDER) json.partial! partial: "blog_post", collection: BLOG_POST_COLLECTION, as: :blog_post JBUILDER assert_collection_rendered result end test "partial! renders as empty array for nil-collection (alt. syntax)" do result = jbuild(<<-JBUILDER) json.partial! partial: "blog_post", collection: nil, as: :blog_post JBUILDER assert_equal [], result end test "render array of partials" do result = jbuild(<<-JBUILDER) json.array! BLOG_POST_COLLECTION, partial: "blog_post", as: :blog_post JBUILDER assert_collection_rendered result end test "render array of partials as empty array with nil-collection" do result = jbuild(<<-JBUILDER) json.array! nil, partial: "blog_post", as: :blog_post JBUILDER assert_equal [], result end test "render array of partials as a value" do result = jbuild(<<-JBUILDER) json.posts BLOG_POST_COLLECTION, partial: "blog_post", as: :blog_post JBUILDER assert_collection_rendered result, "posts" end test "render as empty array if partials as a nil value" do result = jbuild <<-JBUILDER json.posts nil, partial: "blog_post", as: :blog_post JBUILDER assert_equal [], result["posts"] end test "cache an empty block" do undef_context_methods :fragment_name_with_digest, :cache_fragment_name jbuild <<-JBUILDER json.cache! "nothing" do end JBUILDER result = nil assert_nothing_raised do result = jbuild(<<-JBUILDER) json.foo "bar" json.cache! "nothing" do end JBUILDER end assert_equal "bar", result["foo"] end test "fragment caching a JSON object" do undef_context_methods :fragment_name_with_digest, :cache_fragment_name jbuild <<-JBUILDER json.cache! "cachekey" do json.name "Cache" end JBUILDER result = jbuild(<<-JBUILDER) json.cache! "cachekey" do json.name "Miss" end JBUILDER assert_equal "Cache", result["name"] end test "conditionally fragment caching a JSON object" do undef_context_methods :fragment_name_with_digest, :cache_fragment_name jbuild <<-JBUILDER json.cache_if! true, "cachekey" do json.test1 "Cache" end json.cache_if! false, "cachekey" do json.test2 "Cache" end JBUILDER result = jbuild(<<-JBUILDER) json.cache_if! true, "cachekey" do json.test1 "Miss" end json.cache_if! false, "cachekey" do json.test2 "Miss" end JBUILDER assert_equal "Cache", result["test1"] assert_equal "Miss", result["test2"] end test "fragment caching deserializes an array" do undef_context_methods :fragment_name_with_digest, :cache_fragment_name jbuild <<-JBUILDER json.cache! "cachekey" do json.array! %w[a b c] end JBUILDER result = jbuild(<<-JBUILDER) json.cache! "cachekey" do json.array! %w[1 2 3] end JBUILDER assert_equal %w[a b c], result end test "fragment caching works with previous version of cache digests" do undef_context_methods :cache_fragment_name @context.expects :fragment_name_with_digest jbuild <<-JBUILDER json.cache! "cachekey" do json.name "Cache" end JBUILDER end test "fragment caching works with current cache digests" do undef_context_methods :fragment_name_with_digest @context.expects :cache_fragment_name ActiveSupport::Cache.expects :expand_cache_key jbuild <<-JBUILDER json.cache! "cachekey" do json.name "Cache" end JBUILDER end test "current cache digest option accepts options" do undef_context_methods :fragment_name_with_digest @context.expects(:cache_fragment_name).with("cachekey", skip_digest: true) ActiveSupport::Cache.expects :expand_cache_key jbuild <<-JBUILDER json.cache! "cachekey", skip_digest: true do json.name "Cache" end JBUILDER end test "does not perform caching when controller.perform_caching is false" do controller.perform_caching = false jbuild <<-JBUILDER json.cache! "cachekey" do json.name "Cache" end JBUILDER assert_equal Rails.cache.inspect[/entries=(\d+)/, 1], "0" end test "invokes templates via params via set!" do @post = BLOG_POST_COLLECTION.first result = jbuild(<<-JBUILDER) json.post @post, partial: "blog_post", as: :blog_post JBUILDER assert_equal 1, result["post"]["id"] assert_equal "post body 1", result["post"]["body"] assert_equal "David", result["post"]["author"]["first_name"] end test "invokes templates implicitly for ActiveModel objects" do @racer = Racer.new(123, "Chris Harris") result = jbuild(<<-JBUILDER) json.partial! @racer JBUILDER assert_equal %w[id name], result.keys assert_equal 123, result["id"] assert_equal "Chris Harris", result["name"] end end jbuilder-2.3.1/test/jbuilder_test.rb0000644000004100000410000003772512556325572017542 0ustar www-datawww-datarequire 'test_helper' require 'active_support/inflector' require 'jbuilder' def jbuild(*args, &block) Jbuilder.new(*args, &block).attributes! end Comment = Struct.new(:content, :id) class NonEnumerable def initialize(collection) @collection = collection end delegate :map, :count, to: :@collection end class VeryBasicWrapper < BasicObject def initialize(thing) @thing = thing end def method_missing(name, *args, &block) @thing.send name, *args, &block end end # This is not Struct, because structs are Enumerable class Person attr_reader :name, :age def initialize(name, age) @name, @age = name, age end end class RelationMock include Enumerable def each(&block) [Person.new('Bob', 30), Person.new('Frank', 50)].each(&block) end def empty? false end end class JbuilderTest < ActiveSupport::TestCase setup do Jbuilder.send :class_variable_set, '@@key_formatter', nil end test 'single key' do result = jbuild do |json| json.content 'hello' end assert_equal 'hello', result['content'] end test 'single key with false value' do result = jbuild do |json| json.content false end assert_equal false, result['content'] end test 'single key with nil value' do result = jbuild do |json| json.content nil end assert result.has_key?('content') assert_equal nil, result['content'] end test 'multiple keys' do result = jbuild do |json| json.title 'hello' json.content 'world' end assert_equal 'hello', result['title'] assert_equal 'world', result['content'] end test 'extracting from object' do person = Struct.new(:name, :age).new('David', 32) result = jbuild do |json| json.extract! person, :name, :age end assert_equal 'David', result['name'] assert_equal 32, result['age'] end test 'extracting from object using call style for 1.9' do person = Struct.new(:name, :age).new('David', 32) result = jbuild do |json| json.(person, :name, :age) end assert_equal 'David', result['name'] assert_equal 32, result['age'] end test 'extracting from hash' do person = {:name => 'Jim', :age => 34} result = jbuild do |json| json.extract! person, :name, :age end assert_equal 'Jim', result['name'] assert_equal 34, result['age'] end test 'nesting single child with block' do result = jbuild do |json| json.author do json.name 'David' json.age 32 end end assert_equal 'David', result['author']['name'] assert_equal 32, result['author']['age'] end test 'empty block handling' do result = jbuild do |json| json.foo 'bar' json.author do end end assert_equal 'bar', result['foo'] assert !result.key?('author') end test 'blocks are additive' do result = jbuild do |json| json.author do json.name 'David' end json.author do json.age 32 end end assert_equal 'David', result['author']['name'] assert_equal 32, result['author']['age'] end test 'support merge! method' do result = jbuild do |json| json.merge! 'foo' => 'bar' end assert_equal 'bar', result['foo'] end test 'support merge! method in a block' do result = jbuild do |json| json.author do json.merge! 'name' => 'Pavel' end end assert_equal 'Pavel', result['author']['name'] end test 'blocks are additive via extract syntax' do person = Person.new('Pavel', 27) result = jbuild do |json| json.author person, :age json.author person, :name end assert_equal 'Pavel', result['author']['name'] assert_equal 27, result['author']['age'] end test 'arrays are additive' do result = jbuild do |json| json.array! %w[foo] json.array! %w[bar] end assert_equal %w[foo bar], result end test 'nesting multiple children with block' do result = jbuild do |json| json.comments do json.child! { json.content 'hello' } json.child! { json.content 'world' } end end assert_equal 'hello', result['comments'].first['content'] assert_equal 'world', result['comments'].second['content'] end test 'nesting single child with inline extract' do person = Person.new('David', 32) result = jbuild do |json| json.author person, :name, :age end assert_equal 'David', result['author']['name'] assert_equal 32, result['author']['age'] end test 'nesting multiple children from array' do comments = [ Comment.new('hello', 1), Comment.new('world', 2) ] result = jbuild do |json| json.comments comments, :content end assert_equal ['content'], result['comments'].first.keys assert_equal 'hello', result['comments'].first['content'] assert_equal 'world', result['comments'].second['content'] end test 'nesting multiple children from array when child array is empty' do comments = [] result = jbuild do |json| json.name 'Parent' json.comments comments, :content end assert_equal 'Parent', result['name'] assert_equal [], result['comments'] end test 'nesting multiple children from array with inline loop' do comments = [ Comment.new('hello', 1), Comment.new('world', 2) ] result = jbuild do |json| json.comments comments do |comment| json.content comment.content end end assert_equal ['content'], result['comments'].first.keys assert_equal 'hello', result['comments'].first['content'] assert_equal 'world', result['comments'].second['content'] end test 'handles nil-collections as empty arrays' do result = jbuild do |json| json.comments nil do |comment| json.content comment.content end end assert_equal [], result['comments'] end test 'nesting multiple children from a non-Enumerable that responds to #map' do comments = NonEnumerable.new([ Comment.new('hello', 1), Comment.new('world', 2) ]) result = jbuild do |json| json.comments comments, :content end assert_equal ['content'], result['comments'].first.keys assert_equal 'hello', result['comments'].first['content'] assert_equal 'world', result['comments'].second['content'] end test 'nesting multiple chilren from a non-Enumerable that responds to #map with inline loop' do comments = NonEnumerable.new([ Comment.new('hello', 1), Comment.new('world', 2) ]) result = jbuild do |json| json.comments comments do |comment| json.content comment.content end end assert_equal ['content'], result['comments'].first.keys assert_equal 'hello', result['comments'].first['content'] assert_equal 'world', result['comments'].second['content'] end test 'array! casts array-like objects to array before merging' do wrapped_array = VeryBasicWrapper.new(%w[foo bar]) result = jbuild do |json| json.array! wrapped_array end assert_equal %w[foo bar], result end test 'nesting multiple children from array with inline loop on root' do comments = [ Comment.new('hello', 1), Comment.new('world', 2) ] result = jbuild do |json| json.call(comments) do |comment| json.content comment.content end end assert_equal 'hello', result.first['content'] assert_equal 'world', result.second['content'] end test 'array nested inside nested hash' do result = jbuild do |json| json.author do json.name 'David' json.age 32 json.comments do json.child! { json.content 'hello' } json.child! { json.content 'world' } end end end assert_equal 'hello', result['author']['comments'].first['content'] assert_equal 'world', result['author']['comments'].second['content'] end test 'array nested inside array' do result = jbuild do |json| json.comments do json.child! do json.authors do json.child! do json.name 'david' end end end end end assert_equal 'david', result['comments'].first['authors'].first['name'] end test 'directly set an array nested in another array' do data = [ { :department => 'QA', :not_in_json => 'hello', :names => ['John', 'David'] } ] result = jbuild do |json| json.array! data do |object| json.department object[:department] json.names do json.array! object[:names] end end end assert_equal 'David', result[0]['names'].last assert !result[0].key?('not_in_json') end test 'nested jbuilder objects' do to_nest = Jbuilder.new{ |json| json.nested_value 'Nested Test' } result = jbuild do |json| json.value 'Test' json.nested to_nest end expected = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}} assert_equal expected, result end test 'nested jbuilder object via set!' do to_nest = Jbuilder.new{ |json| json.nested_value 'Nested Test' } result = jbuild do |json| json.value 'Test' json.set! :nested, to_nest end expected = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}} assert_equal expected, result end test 'top-level array' do comments = [ Comment.new('hello', 1), Comment.new('world', 2) ] result = jbuild do |json| json.array! comments do |comment| json.content comment.content end end assert_equal 'hello', result.first['content'] assert_equal 'world', result.second['content'] end test 'it allows using next in array block to skip value' do comments = [ Comment.new('hello', 1), Comment.new('skip', 2), Comment.new('world', 3) ] result = jbuild do |json| json.array! comments do |comment| next if comment.id == 2 json.content comment.content end end assert_equal 2, result.length assert_equal 'hello', result.first['content'] assert_equal 'world', result.second['content'] end test 'extract attributes directly from array' do comments = [ Comment.new('hello', 1), Comment.new('world', 2) ] result = jbuild do |json| json.array! comments, :content, :id end assert_equal 'hello', result.first['content'] assert_equal 1, result.first['id'] assert_equal 'world', result.second['content'] assert_equal 2, result.second['id'] end test 'empty top-level array' do comments = [] result = jbuild do |json| json.array! comments do |comment| json.content comment.content end end assert_equal [], result end test 'dynamically set a key/value' do result = jbuild do |json| json.set! :each, 'stuff' end assert_equal 'stuff', result['each'] end test 'dynamically set a key/nested child with block' do result = jbuild do |json| json.set! :author do json.name 'David' json.age 32 end end assert_equal 'David', result['author']['name'] assert_equal 32, result['author']['age'] end test 'dynamically sets a collection' do comments = [ Comment.new('hello', 1), Comment.new('world', 2) ] result = jbuild do |json| json.set! :comments, comments, :content end assert_equal ['content'], result['comments'].first.keys assert_equal 'hello', result['comments'].first['content'] assert_equal 'world', result['comments'].second['content'] end test 'query like object' do result = jbuild do |json| json.relations RelationMock.new, :name, :age end assert_equal 2, result['relations'].length assert_equal 'Bob', result['relations'][0]['name'] assert_equal 50, result['relations'][1]['age'] end test 'initialize via options hash' do jbuilder = Jbuilder.new(key_formatter: 1, ignore_nil: 2) assert_equal 1, jbuilder.instance_eval{ @key_formatter } assert_equal 2, jbuilder.instance_eval{ @ignore_nil } end test 'key_format! with parameter' do result = jbuild do |json| json.key_format! camelize: [:lower] json.camel_style 'for JS' end assert_equal ['camelStyle'], result.keys end test 'key_format! with parameter not as an array' do result = jbuild do |json| json.key_format! :camelize => :lower json.camel_style 'for JS' end assert_equal ['camelStyle'], result.keys end test 'key_format! propagates to child elements' do result = jbuild do |json| json.key_format! :upcase json.level1 'one' json.level2 do json.value 'two' end end assert_equal 'one', result['LEVEL1'] assert_equal 'two', result['LEVEL2']['VALUE'] end test 'key_format! resets after child element' do result = jbuild do |json| json.level2 do json.key_format! :upcase json.value 'two' end json.level1 'one' end assert_equal 'two', result['level2']['VALUE'] assert_equal 'one', result['level1'] end test 'key_format! with no parameter' do result = jbuild do |json| json.key_format! :upcase json.lower 'Value' end assert_equal ['LOWER'], result.keys end test 'key_format! with multiple steps' do result = jbuild do |json| json.key_format! :upcase, :pluralize json.pill 'foo' end assert_equal ['PILLs'], result.keys end test 'key_format! with lambda/proc' do result = jbuild do |json| json.key_format! ->(key){ key + ' and friends' } json.oats 'foo' end assert_equal ['oats and friends'], result.keys end test 'default key_format!' do Jbuilder.key_format camelize: :lower result = jbuild{ |json| json.camel_style 'for JS' } assert_equal ['camelStyle'], result.keys end test 'do not use default key formatter directly' do Jbuilder.key_format jbuild{ |json| json.key 'value' } formatter = Jbuilder.send(:class_variable_get, '@@key_formatter') cache = formatter.instance_variable_get('@cache') assert_empty cache end test 'ignore_nil! without a parameter' do result = jbuild do |json| json.ignore_nil! json.test nil end assert_empty result.keys end test 'ignore_nil! with parameter' do result = jbuild do |json| json.ignore_nil! true json.name 'Bob' json.dne nil end assert_equal ['name'], result.keys result = jbuild do |json| json.ignore_nil! false json.name 'Bob' json.dne nil end assert_equal ['name', 'dne'], result.keys end test 'default ignore_nil!' do Jbuilder.ignore_nil result = jbuild do |json| json.name 'Bob' json.dne nil end assert_equal ['name'], result.keys Jbuilder.send(:class_variable_set, '@@ignore_nil', false) end test 'nil!' do result = jbuild do |json| json.key 'value' json.nil! end assert_nil result end test 'null!' do result = jbuild do |json| json.key 'value' json.null! end assert_nil result end test 'null! in a block' do result = jbuild do |json| json.author do json.name 'David' end json.author do json.null! end end assert result.key?('author') assert_nil result['author'] end test 'empty attributes respond to empty?' do attributes = Jbuilder.new.attributes! assert attributes.empty? assert attributes.blank? assert !attributes.present? end test 'throws ArrayError when trying to add a key to an array' do assert_raise Jbuilder::ArrayError do jbuild do |json| json.array! %w[foo bar] json.fizz "buzz" end end end test 'throws NullError when trying to add properties to null' do assert_raise Jbuilder::NullError do jbuild do |json| json.null! json.foo 'bar' end end end test 'throws NullError when trying to add properties to null using block syntax' do assert_raise Jbuilder::NullError do jbuild do |json| json.author do json.null! end json.author do json.name "Pavel" end end end end end jbuilder-2.3.1/test/scaffold_controller_generator_test.rb0000644000004100000410000000426012556325572024020 0ustar www-datawww-datarequire 'test_helper' require 'rails/generators/test_case' require 'generators/rails/scaffold_controller_generator' class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase tests Rails::Generators::ScaffoldControllerGenerator arguments %w(Post title body:text) destination File.expand_path('../tmp', __FILE__) setup :prepare_destination test 'controller content' do run_generator assert_file 'app/controllers/posts_controller.rb' do |content| assert_instance_method :index, content do |m| assert_match /@posts = Post\.all/, m end assert_instance_method :show, content do |m| assert m.blank? end assert_instance_method :new, content do |m| assert_match /@post = Post\.new/, m end assert_instance_method :edit, content do |m| assert m.blank? end assert_instance_method :create, content do |m| assert_match /@post = Post\.new\(post_params\)/, m assert_match /@post\.save/, m assert_match /format\.html \{ redirect_to @post, notice: 'Post was successfully created\.' \}/, m assert_match /format\.json \{ render :show, status: :created, location: @post \}/, m assert_match /format\.html \{ render :new \}/, m assert_match /format\.json \{ render json: @post\.errors, status: :unprocessable_entity \}/, m end assert_instance_method :update, content do |m| assert_match /format\.html \{ redirect_to @post, notice: 'Post was successfully updated\.' \}/, m assert_match /format\.json \{ render :show, status: :ok, location: @post \}/, m assert_match /format\.html \{ render :edit \}/, m assert_match /format\.json \{ render json: @post.errors, status: :unprocessable_entity \}/, m end assert_instance_method :destroy, content do |m| assert_match /@post\.destroy/, m assert_match /format\.html \{ redirect_to posts_url, notice: 'Post was successfully destroyed\.' \}/, m assert_match /format\.json \{ head :no_content \}/, m end assert_match(/def post_params/, content) assert_match(/params\.require\(:post\)\.permit\(:title, :body\)/, content) end end end jbuilder-2.3.1/test/jbuilder_dependency_tracker_test.rb0000644000004100000410000000405012556325572023434 0ustar www-datawww-datarequire 'test_helper' require 'jbuilder/dependency_tracker' class FakeTemplate attr_reader :source, :handler def initialize(source, handler = :jbuilder) @source, @handler = source, handler end end class JbuilderDependencyTrackerTest < ActiveSupport::TestCase def make_tracker(name, source) template = FakeTemplate.new(source) Jbuilder::DependencyTracker.new(name, template) end def track_dependencies(source) make_tracker('jbuilder_template', source).dependencies end test 'detects dependency via direct partial! call' do dependencies = track_dependencies <<-RUBY json.partial! 'path/to/partial', foo: bar json.partial! 'path/to/another/partial', :fizz => buzz RUBY assert_equal %w[path/to/partial path/to/another/partial], dependencies end test 'detects dependency via direct partial! call with parens' do dependencies = track_dependencies <<-RUBY json.partial!("path/to/partial") RUBY assert_equal %w[path/to/partial], dependencies end test 'detects partial with options (1.9 style)' do dependencies = track_dependencies <<-RUBY json.partial! hello: 'world', partial: 'path/to/partial', foo: :bar RUBY assert_equal %w[path/to/partial], dependencies end test 'detects partial with options (1.8 style)' do dependencies = track_dependencies <<-RUBY json.partial! :hello => 'world', :partial => 'path/to/partial', :foo => :bar RUBY assert_equal %w[path/to/partial], dependencies end test 'detects partial in indirect collecton calls' do dependencies = track_dependencies <<-RUBY json.comments @post.comments, partial: 'comments/comment', as: :comment RUBY assert_equal %w[comments/comment], dependencies end test 'detects explicit depedency' do dependencies = track_dependencies <<-RUBY # Template Dependency: path/to/partial json.foo 'bar' RUBY assert_equal %w[path/to/partial], dependencies end end jbuilder-2.3.1/test/test_helper.rb0000644000004100000410000000041412556325572017202 0ustar www-datawww-datarequire "bundler/setup" require "active_support" require "rails/version" if Rails::VERSION::STRING > "4.0" require "active_support/testing/autorun" else require "test/unit" end if ActiveSupport.respond_to?(:test_order=) ActiveSupport.test_order = :random end jbuilder-2.3.1/test/jbuilder_generator_test.rb0000644000004100000410000000176212556325572021600 0ustar www-datawww-datarequire 'test_helper' require 'rails/generators/test_case' require 'generators/rails/jbuilder_generator' class JbuilderGeneratorTest < Rails::Generators::TestCase tests Rails::Generators::JbuilderGenerator arguments %w(Post title body:text password:digest) destination File.expand_path('../tmp', __FILE__) setup :prepare_destination test 'views are generated' do run_generator %w(index show).each do |view| assert_file "app/views/posts/#{view}.json.jbuilder" end end test 'index content' do run_generator assert_file 'app/views/posts/index.json.jbuilder' do |content| assert_match /json\.array!\(@posts\) do \|post\|/, content assert_match /json\.extract! post, :id, :title, :body/, content assert_match /json\.url post_url\(post, format: :json\)/, content end assert_file 'app/views/posts/show.json.jbuilder' do |content| assert_match /json\.extract! @post, :id, :title, :body, :created_at, :updated_at/, content end end end jbuilder-2.3.1/test/scaffold_api_controller_generator_test.rb0000644000004100000410000000303312556325572024646 0ustar www-datawww-datarequire 'test_helper' require 'rails/generators/test_case' require 'generators/rails/scaffold_controller_generator' if Rails::VERSION::MAJOR > 4 class ScaffoldApiControllerGeneratorTest < Rails::Generators::TestCase tests Rails::Generators::ScaffoldControllerGenerator arguments %w(Post title body:text --api) destination File.expand_path('../tmp', __FILE__) setup :prepare_destination test 'controller content' do run_generator assert_file 'app/controllers/posts_controller.rb' do |content| assert_instance_method :index, content do |m| assert_match /@posts = Post\.all/, m end assert_instance_method :show, content do |m| assert m.blank? end assert_instance_method :create, content do |m| assert_match /@post = Post\.new\(post_params\)/, m assert_match /@post\.save/, m assert_match /render :show, status: :created, location: @post/, m assert_match /render json: @post\.errors, status: :unprocessable_entity/, m end assert_instance_method :update, content do |m| assert_match /render :show, status: :ok, location: @post/, m assert_match /render json: @post.errors, status: :unprocessable_entity/, m end assert_instance_method :destroy, content do |m| assert_match /@post\.destroy/, m end assert_match(/def post_params/, content) assert_match(/params\.require\(:post\)\.permit\(:title, :body\)/, content) end end end end jbuilder-2.3.1/.gitignore0000644000004100000410000000006312556325572015350 0ustar www-datawww-datatmp gemfiles/*.lock Gemfile.lock .ruby-version pkg jbuilder-2.3.1/jbuilder.gemspec0000644000004100000410000000111312556325572016522 0ustar www-datawww-dataGem::Specification.new do |s| s.name = 'jbuilder' s.version = '2.3.1' s.authors = ['David Heinemeier Hansson', 'Pavel Pravosud'] s.email = ['david@37signals.com', 'pavel@pravosud.com'] s.summary = 'Create JSON structures via a Builder-style DSL' s.homepage = 'https://github.com/rails/jbuilder' s.license = 'MIT' s.required_ruby_version = '>= 1.9.3' s.add_dependency 'activesupport', '>= 3.0.0', '< 5' s.add_dependency 'multi_json', '~> 1.2' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- test/*`.split("\n") end jbuilder-2.3.1/Appraisals0000644000004100000410000000161112556325572015402 0ustar www-datawww-dataappraise "rails-3-0" do gem "test-unit" gem "railties", "~> 3.0.0" gem "actionpack", "~> 3.0.0" gem "activemodel", "~> 3.0.0" end appraise "rails-3-1" do gem "test-unit" gem "railties", "~> 3.1.0" gem "actionpack", "~> 3.1.0" gem "activemodel", "~> 3.1.0" end appraise "rails-3-2" do gem "test-unit" gem "railties", "~> 3.2.0" gem "actionpack", "~> 3.2.0" gem "activemodel", "~> 3.2.0" end appraise "rails-4-0" do gem "railties", "~> 4.0.0" gem "actionpack", "~> 4.0.0" gem "activemodel", "~> 4.0.0" end appraise "rails-4-1" do gem "railties", "~> 4.1.0" gem "actionpack", "~> 4.1.0" gem "activemodel", "~> 4.1.0" end appraise "rails-4-2" do gem "railties", "~> 4.2.0" gem "actionpack", "~> 4.2.0" gem "activemodel", "~> 4.2.0" end appraise "rails-edge" do gem "rails", github: "rails/rails" gem "arel", github: "rails/arel" end jbuilder-2.3.1/CHANGELOG.md0000644000004100000410000002163212556325572015176 0ustar www-datawww-data# Changelog 2.3.1 ----- * [Explicitly require ostruct to prevent NameError](https://github.com/rails/jbuilder/pull/281) 2.3.0 ----- * [Add new in-place partial invocation support](https://github.com/rails/jbuilder/commit/1feda7ee605c136e59fb4de970f4674de518e6de) * [Add implicit partial rendering for AM::Models](https://github.com/rails/jbuilder/commit/4d5bf7d0ea92765adb7be36834e84f9855a061df) * [Generate API controller if Rails API option is enabled](https://github.com/rails/jbuilder/commit/db68f6bd327cf42b47ef22d455fb5721a8c2cf5f) * [JBuilder's templates have less priority than app templates](https://github.com/rails/jbuilder/commit/7c1a5f25603ec1f4e51fba3dbba9db23726a5d69) * [Add AC::Helpers module to jbuilder for api only apps](https://github.com/rails/jbuilder/commit/7cf1d1eb7d125caf38309b5427952030011c1aa0) 2.2.16 ------ * [Fix NoMethodError around `api_only` in railtie](https://github.com/rails/jbuilder/commit/b08d1da10b14720b46d383b2917e336060fd9ffa) 2.2.14 ------ * [Make Jbuilder compatible with Rails API](https://github.com/rails/jbuilder/commit/29c0014a9c954c990075d42c45c66075260e924b) 2.2.13 ------ * Several peformance optimizations: [#260](https://github.com/rails/jbuilder/pull/260) & [#261](https://github.com/rails/jbuilder/pull/261) 2.2.12 ------ * [Replace explici block calls with yield for performance](https://github.com/rails/jbuilder/commit/3184f941276ad03a071cf977133d1a32302afa47) 2.2.11 ------ * Generate the templates for Rails 5+ [#258](https://github.com/rails/jbuilder/pull/258) [@amatsuda](https://github.com/amatsuda) 2.2.10 ------ * Add Jbuilder::Blank#empty? to tell if attributes are empty [#257](https://github.com/rails/jbuilder/pull/257) [@a2ikm](https://github.com/a2ikm) 2.2.9 ----- * Support `partial!` call with `locals` option in `JbuilderTemplate` [#251](https://github.com/rails/jbuilder/pull/251) 2.2.8 ----- * [Raise ArrayError when trying to add key to an array](https://github.com/rails/jbuilder/commit/869e4be1ad165ce986d8fca78311bdd3ed166087) 2.2.7 ----- * [Make Blank object serializable with Marshal](https://github.com/rails/jbuilder/commit/7083f28d8b665aa60d0d1b1927ae88bb5c6290ba) 2.2.6 ----- * [Make sure dependency tracker loads after template handler](https://github.com/rails/jbuilder/commit/3ba404b1207b557e14771c90b8832bc01ae67a42) 2.2.5 ----- * [Refactor merge block behavior to raise error for unexpected values](https://github.com/rails/jbuilder/commit/4503162fb26f53f613fc83ac081fd244748b6fe9) 2.2.4 ----- * [Typecast locals hash key during collection render](https://github.com/rails/jbuilder/commit/a6b0c8651a08e01cb53eee38e211c65423f275f7) 2.2.3 ----- * [Move template handler registration into railtie](https://github.com/rails/jbuilder/commit/c8acc5cea6da2a79b7b345adc301cb5ff2517647) * [Do not capture the block where it is possible](https://github.com/rails/jbuilder/commit/973b382c3924cb59fc0e4e25266b18e74d41d646) 2.2.2 ----- * [Fix `Jbuilder#merge!` inside block](https://github.com/rails/jbuilder/commit/a7b328552eb0d36315f75bff813bea7eecf8c1d7) 2.2.1 ----- * [Fix empty block handling](https://github.com/rails/jbuilder/commit/972a11141403269e9b17b45b0c95f8a9788245ee) 2.2.0 ----- * [Allow to skip `array!` iterations by calling `next`](https://github.com/rails/jbuilder/commit/81a63308fb9d5002519dd871f829ccc58067251a) 2.1.2 ----- * [Cast array-like objects to array before merging](https://github.com/rails/jbuilder/commit/7b8c8a1cb09b7f3dd26e5643ebbd6b2ec67185db) 2.1.1 ----- * [Remove unused file](https://github.com/rails/jbuilder/commit/e49e1047976fac93b8242ab212c7b1a463b70809) 2.1.0 ----- * [Blocks and their extract! shortcuts are additive by default](https://github.com/rails/jbuilder/commit/a49390736c5f6e2d7a31111df6531bc28dba9fb1) 2.0.8 ----- * [Eliminate circular dependencies](https://github.com/rails/jbuilder/commit/0879484dc74e7be93b695f66e3708ba48cdb1be3) * [Support cache key generation for complex objects](https://github.com/rails/jbuilder/commit/ca9622cca30c1112dd4408fcb2e658849abe1dd5) * [Remove JbuilderProxy class](https://github.com/rails/jbuilder/commit/5877482fc7da3224e42d4f72a1386f7a3a08173b) * [Move KeyFormatter into a separate file](https://github.com/rails/jbuilder/commit/13fee8464ff53ce853030114283c03c135c052b6) * [Move NullError into a separate file](https://github.com/rails/jbuilder/commit/13fee8464ff53ce853030114283c03c135c052b6) 2.0.7 ----- * [Add destroy notice to scaffold generator](https://github.com/rails/jbuilder/commit/8448e86f8cdfa0f517bd59576947875775a1d43c) 2.0.6 ----- * [Use render short form in controller generator](https://github.com/rails/jbuilder/commit/acf37320a7cea7fcc70c791bc94bd5f46b8349ff) 2.0.5 ----- * [Fix edgecase when json is defined as a method](https://github.com/rails/jbuilder/commit/ca711a0c0a5760e26258ce2d93c14bef8fff0ead) 2.0.4 ----- * [Add cache_if! to conditionally cache JSON fragments](https://github.com/rails/jbuilder/commit/14a5afd8a2c939a6fd710d355a194c114db96eb2) 2.0.3 ----- * [Pass options when calling cache_fragment_name](https://github.com/rails/jbuilder/commit/07c2cc7486fe9ef423d7bc821b83f6d485f330e0) 2.0.2 ----- * [Fix Dependency Tracking fail to detect single-quoted partial correctly](https://github.com/rails/jbuilder/commit/448679a6d3098eb34d137f782a05f1767711991a) * [Prevent Dependency Tracker constants leaking into global namespace](https://github.com/rails/jbuilder/commit/3544b288b63f504f46fa8aafd1d17ee198d77536) 2.0.1 ----- * [Dependency tracking support for Rails 3 with cache_digest gem](https://github.com/rails/jbuilder/commit/6b471d7a38118e8f7645abec21955ef793401daf) 2.0.0 ----- * [Respond to PUT/PATCH API request with :ok](https://github.com/rails/jbuilder/commit/9dbce9c12181e89f8f472ac23c764ffe8438040a) * [Remove Ruby 1.8 support](https://github.com/rails/jbuilder/commit/d53fff42d91f33d662eafc2561c4236687ecf6c9) * [Remove deprecated two argument block call](https://github.com/rails/jbuilder/commit/07a35ee7e79ae4b06dba9dbff5c4e07c1e627218) * [Make Jbuilder object initialize with single hash](https://github.com/rails/jbuilder/commit/38bf551db0189327aaa90b9be010c0d1b792c007) * [Track template dependencies](https://github.com/rails/jbuilder/commit/8e73cea39f60da1384afd687cc8e5e399630d8cc) * [Expose merge! method](https://github.com/rails/jbuilder/commit/0e2eb47f6f3c01add06a1a59b37cdda8baf24f29) 1.5.3 ----- * [Generators add `:id` column by default](https://github.com/rails/jbuilder/commit/0b52b86773e48ac2ce35d4155c7b70ad8b3e8937) 1.5.2 ----- * [Nil-collection should be treated as empty array](https://github.com/rails/jbuilder/commit/2f700bb00ab663c6b7fcb28d2967aeb989bd43c7) 1.5.1 ----- * [Expose template lookup options](https://github.com/rails/jbuilder/commit/404c18dee1af96ac6d8052a04062629ef1db2945) 1.5.0 ----- * [Do not perform any caching when `controller.perform_caching` is false](https://github.com/rails/jbuilder/commit/94633facde1ac43580f8cd5e13ae9cc83e1da8f4) * [Add partial collection rendering](https://github.com/rails/jbuilder/commit/e8c10fc885e41b18178aaf4dcbc176961c928d76) * [Deprecate extract! calling private methods](https://github.com/rails/jbuilder/commit/b9e19536c2105d7f2e813006bbcb8ca5730d28a3) * [Add array of partials rendering](https://github.com/rails/jbuilder/commit/7d7311071720548047f98f14ad013c560b8d9c3a) 1.4.2 ----- * [Require MIME dependency explicitly](https://github.com/rails/jbuilder/commit/b1ed5ac4f08b056f8839b4b19b43562e81e02a59) 1.4.1 ----- * [Removed deprecated positioned arguments initializer support](https://github.com/rails/jbuilder/commit/6e03e0452073eeda77e6dfe66aa31e5ec67a3531) * [Deprecate two-arguments block calling](https://github.com/rails/jbuilder/commit/2b10bb058bb12bc782cbcc16f6ec67b489e5ed43) 1.4.0 ----- * [Add quick collection attribute extraction](https://github.com/rails/jbuilder/commit/c2b966cf653ea4264fbb008b8cc6ce5359ebe40a) * [Block has priority over attributes extraction](https://github.com/rails/jbuilder/commit/77c24766362c02769d81dac000b1879a9e4d4a00) * [Meaningfull error messages when adding properties to null](https://github.com/rails/jbuilder/commit/e26764602e34b3772e57e730763d512e59489e3b) * [Do not enforce template format, enforce handlers instead](https://github.com/rails/jbuilder/commit/72576755224b15da45e50cbea877679800ab1398) 1.3.0 ----- * [Add nil! method for nil JSON](https://github.com/rails/jbuilder/commit/822a906f68664f61a1209336bb681077692c8475) 1.2.1 ----- * [Added explicit dependency for MultiJson](https://github.com/rails/jbuilder/commit/4d58eacb6cd613679fb243484ff73a79bbbff2d2 1.2.0 ----- * Multiple documentation improvements and internal refactoring * [Fixes fragment caching to work with latest digests](https://github.com/rails/jbuilder/commit/da937d6b8732124074c612abb7ff38868d1d96c0) 1.0.2 ----- * [Support non-Enumerable collections](https://github.com/rails/jbuilder/commit/4c20c59bf8131a1e419bb4ebf84f2b6bdcb6b0cf) * [Ensure that the default URL is in json format](https://github.com/rails/jbuilder/commit/0b46782fb7b8c34a3c96afa801fe27a5a97118a4) 1.0.0 ----- * Adopt Semantic Versioning * Add rails generators jbuilder-2.3.1/README.md0000644000004100000410000001515512556325572014647 0ustar www-datawww-data# Jbuilder [![Build Status](https://api.travis-ci.org/rails/jbuilder.svg)][travis] [![Gem Version](http://img.shields.io/gem/v/jbuilder.svg)][gem] [![Code Climate](http://img.shields.io/codeclimate/github/rails/jbuilder.svg)][codeclimate] [![Dependencies Status](http://img.shields.io/gemnasium/rails/jbuilder.svg)][gemnasium] [travis]: https://travis-ci.org/rails/jbuilder [gem]: https://rubygems.org/gems/jbuilder [codeclimate]: https://codeclimate.com/github/rails/jbuilder [gemnasium]: https://gemnasium.com/rails/jbuilder Jbuilder gives you a simple DSL for declaring JSON structures that beats massaging giant hash structures. This is particularly helpful when the generation process is fraught with conditionals and loops. Here's a simple example: ``` ruby # app/views/message/show.json.jbuilder json.content format_content(@message.content) json.(@message, :created_at, :updated_at) json.author do json.name @message.creator.name.familiar json.email_address @message.creator.email_address_with_name json.url url_for(@message.creator, format: :json) end if current_user.admin? json.visitors calculate_visitors(@message) end json.comments @message.comments, :content, :created_at json.attachments @message.attachments do |attachment| json.filename attachment.filename json.url url_for(attachment) end ``` This will build the following structure: ``` javascript { "content": "

This is serious monkey business

", "created_at": "2011-10-29T20:45:28-05:00", "updated_at": "2011-10-29T20:45:28-05:00", "author": { "name": "David H.", "email_address": "'David Heinemeier Hansson' ", "url": "http://example.com/users/1-david.json" }, "visitors": 15, "comments": [ { "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" }, { "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" } ], "attachments": [ { "filename": "forecast.xls", "url": "http://example.com/downloads/forecast.xls" }, { "filename": "presentation.pdf", "url": "http://example.com/downloads/presentation.pdf" } ] } ``` To define attribute and structure names dynamically, use the `set!` method: ``` ruby json.set! :author do json.set! :name, 'David' end # => "author": { "name": "David" } ``` Top level arrays can be handled directly. Useful for index and other collection actions. ``` ruby # @comments = @post.comments json.array! @comments do |comment| next if comment.marked_as_spam_by?(current_user) json.body comment.body json.author do json.first_name comment.author.first_name json.last_name comment.author.last_name end end # => [ { "body": "great post...", "author": { "first_name": "Joe", "last_name": "Bloe" }} ] ``` You can also extract attributes from array directly. ``` ruby # @people = People.all json.array! @people, :id, :name # => [ { "id": 1, "name": "David" }, { "id": 2, "name": "Jamie" } ] ``` Jbuilder objects can be directly nested inside each other. Useful for composing objects. ``` ruby class Person # ... Class Definition ... # def to_builder Jbuilder.new do |person| person.(self, :name, :age) end end end class Company # ... Class Definition ... # def to_builder Jbuilder.new do |company| company.name name company.president president.to_builder end end end company = Company.new('Doodle Corp', Person.new('John Stobs', 58)) company.to_builder.target! # => {"name":"Doodle Corp","president":{"name":"John Stobs","age":58}} ``` You can either use Jbuilder stand-alone or directly as an ActionView template language. When required in Rails, you can create views ala show.json.jbuilder (the json is already yielded): ``` ruby # Any helpers available to views are available to the builder json.content format_content(@message.content) json.(@message, :created_at, :updated_at) json.author do json.name @message.creator.name.familiar json.email_address @message.creator.email_address_with_name json.url url_for(@message.creator, format: :json) end if current_user.admin? json.visitors calculate_visitors(@message) end ``` You can use partials as well. The following will render the file `views/comments/_comments.json.jbuilder`, and set a local variable `comments` with all this message's comments, which you can use inside the partial. ```ruby json.partial! 'comments/comments', comments: @message.comments ``` It's also possible to render collections of partials: ```ruby json.array! @posts, partial: 'posts/post', as: :post # or json.partial! 'posts/post', collection: @posts, as: :post # or json.partial! partial: 'posts/post', collection: @posts, as: :post # or json.comments @post.comments, partial: 'comment/comment', as: :comment ``` You can pass any objects into partial templates with or without `:locals` option. ```ruby json.partial! 'sub_template', locals: { user: user } # or json.partial! 'sub_template', user: user ``` You can explicitly make Jbuilder object return null if you want: ``` ruby json.extract! @post, :id, :title, :content, :published_at json.author do if @post.anonymous? json.null! # or json.nil! else json.first_name @post.author_first_name json.last_name @post.author_last_name end end ``` Fragment caching is supported, it uses `Rails.cache` and works like caching in HTML templates: ```ruby json.cache! ['v1', @person], expires_in: 10.minutes do json.extract! @person, :name, :age end ``` You can also conditionally cache a block by using `cache_if!` like this: ```ruby json.cache_if! !admin?, ['v1', @person], expires_in: 10.minutes do json.extract! @person, :name, :age end ``` If you are rendering fragments for a collection of objects, have a look at `jbuilder_cache_multi` gem. It uses fetch_multi (>= Rails 4.1) to fetch multiple keys at once. Keys can be auto formatted using `key_format!`, this can be used to convert keynames from the standard ruby_format to camelCase: ``` ruby json.key_format! camelize: :lower json.first_name 'David' # => { "firstName": "David" } ``` You can set this globally with the class method `key_format` (from inside your environment.rb for example): ``` ruby Jbuilder.key_format camelize: :lower ``` Faster JSON backends -------------------- Jbuilder uses MultiJson, which by default will use the JSON gem. That gem is currently tangled with ActiveSupport's all-Ruby `#to_json` implementation, which is slow (fixed in Rails >= 4.1). For faster Jbuilder rendering, you can specify something like the Yajl JSON generator instead. You'll need to include the `yajl-ruby` gem in your Gemfile and then set the following configuration for MultiJson: ``` ruby require 'multi_json' MultiJson.use :yajl ```