jbuilder-2.7.0/0000755000004100000410000000000013273321662013355 5ustar www-datawww-datajbuilder-2.7.0/.travis.yml0000644000004100000410000000263013273321662015467 0ustar www-datawww-datalanguage: ruby sudo: false cache: bundler rvm: - 1.9 - 2.0 - 2.1 - 2.2.5 - 2.3.1 - 2.4.0 - 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_5_0.gemfile - gemfiles/rails_5_1.gemfile matrix: allow_failures: - rvm: jruby-19mode - rvm: rbx - rvm: ruby-head fast_finish: true exclude: - rvm: 1.9 gemfile: gemfiles/rails_5_0.gemfile - rvm: 1.9 gemfile: gemfiles/rails_5_1.gemfile - rvm: 2.0 gemfile: gemfiles/rails_5_0.gemfile - rvm: 2.0 gemfile: gemfiles/rails_5_1.gemfile - rvm: 2.1 gemfile: gemfiles/rails_5_0.gemfile - rvm: 2.1 gemfile: gemfiles/rails_5_1.gemfile - rvm: jruby-19mode gemfile: gemfiles/rails_5_0.gemfile - rvm: jruby-19mode gemfile: gemfiles/rails_5_1.gemfile - rvm: rbx gemfile: gemfiles/rails_5_0.gemfile - rvm: rbx gemfile: gemfiles/rails_5_1.gemfile - rvm: 2.4.0 gemfile: gemfiles/rails_3_0.gemfile - rvm: 2.4.0 gemfile: gemfiles/rails_3_1.gemfile - rvm: 2.4.0 gemfile: gemfiles/rails_3_2.gemfile - rvm: 2.4.0 gemfile: gemfiles/rails_4_0.gemfile - rvm: 2.4.0 gemfile: gemfiles/rails_4_1.gemfile notifications: email: false jbuilder-2.7.0/test/0000755000004100000410000000000013273321662014334 5ustar www-datawww-datajbuilder-2.7.0/test/jbuilder_template_test.rb0000644000004100000410000002731713273321662021425 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, options = {}) @rendered = [] partials = options.fetch(: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 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 "fragment caching uses fragment_cache_key" do undef_context_methods :fragment_name_with_digest, :cache_fragment_name @context.expects(:fragment_cache_key).with("cachekey") jbuild <<-JBUILDER json.cache! "cachekey" do json.name "Cache" end JBUILDER end test "fragment caching instrumentation" do undef_context_methods :fragment_name_with_digest, :cache_fragment_name payloads = {} ActiveSupport::Notifications.subscribe("read_fragment.action_controller") { |*args| payloads[:read_fragment] = args.last } ActiveSupport::Notifications.subscribe("write_fragment.action_controller") { |*args| payloads[:write_fragment] = args.last } jbuild <<-JBUILDER json.cache! "cachekey" do json.name "Cache" end JBUILDER assert_equal "jbuilder/cachekey", payloads[:read_fragment][:key] assert_equal "jbuilder/cachekey", payloads[:write_fragment][:key] 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 "fragment caching accepts expires_in option" do undef_context_methods :fragment_name_with_digest @context.expects(:cache_fragment_name).with("cachekey", {}) jbuild <<-JBUILDER json.cache! "cachekey", expires_in: 1.minute do json.name "Cache" end JBUILDER end test "caching root structure" do undef_context_methods :fragment_name_with_digest, :cache_fragment_name cache_miss_result = jbuild <<-JBUILDER json.cache_root! "cachekey" do json.name "Miss" end JBUILDER cache_hit_result = jbuild <<-JBUILDER json.cache_root! "cachekey" do json.name "Hit" end JBUILDER assert_equal cache_miss_result, cache_hit_result end test "failing to cache root after attributes have been defined" do assert_raises ActionView::Template::Error, "cache_root! can't be used after JSON structures have been defined" do jbuild <<-JBUILDER json.name "Kaboom" json.cache_root! "cachekey" do json.name "Miss" end JBUILDER end 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 test "renders partial via set! with same name as HTML partial" do partials = { "_blog_post.html.erb" => "Hello!", "_blog_post.json.jbuilder" => BLOG_POST_PARTIAL } @post = BLOG_POST_COLLECTION.first result = jbuild(<<-JBUILDER, partials: partials) json.post @post, partial: "blog_post", as: :blog_post JBUILDER assert_not_nil result["post"] assert_equal 1, result["post"]["id"] end end jbuilder-2.7.0/test/scaffold_api_controller_generator_test.rb0000644000004100000410000000353713273321662024653 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 %r{@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 %r{@post = Post\.new\(post_params\)}, m assert_match %r{@post\.save}, m assert_match %r{render :show, status: :created, location: @post}, m assert_match %r{render json: @post\.errors, status: :unprocessable_entity}, m end assert_instance_method :update, content do |m| assert_match %r{render :show, status: :ok, location: @post}, m assert_match %r{render json: @post.errors, status: :unprocessable_entity}, m end assert_instance_method :destroy, content do |m| assert_match %r{@post\.destroy}, m end assert_match %r{def post_params}, content assert_match %r{params\.require\(:post\)\.permit\(:title, :body\)}, content end end test 'dont use require and permit if there are no attributes' do run_generator %w(Post --api) assert_file 'app/controllers/posts_controller.rb' do |content| assert_match %r{def post_params}, content assert_match %r{params\.fetch\(:post, \{\}\)}, content end end end end jbuilder-2.7.0/test/jbuilder_generator_test.rb0000644000004100000410000000221313273321662021564 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 assert_file "app/views/posts/_post.json.jbuilder" end test 'index content' do run_generator assert_file 'app/views/posts/index.json.jbuilder' do |content| assert_match %r{json.array! @posts, partial: 'posts/post', as: :post}, content end assert_file 'app/views/posts/show.json.jbuilder' do |content| assert_match %r{json.partial! \"posts/post\", post: @post}, content end assert_file 'app/views/posts/_post.json.jbuilder' do |content| assert_match %r{json\.extract! post, :id, :title, :body}, content assert_match %r{json\.url post_url\(post, format: :json\)}, content end end end jbuilder-2.7.0/test/jbuilder_dependency_tracker_test.rb0000644000004100000410000000405013273321662023430 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.7.0/test/scaffold_controller_generator_test.rb0000644000004100000410000000475613273321662024026 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 %r{@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 %r{@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 %r{@post = Post\.new\(post_params\)}, m assert_match %r{@post\.save}, m assert_match %r{format\.html \{ redirect_to @post, notice: 'Post was successfully created\.' \}}, m assert_match %r{format\.json \{ render :show, status: :created, location: @post \}}, m assert_match %r{format\.html \{ render :new \}}, m assert_match %r{format\.json \{ render json: @post\.errors, status: :unprocessable_entity \}}, m end assert_instance_method :update, content do |m| assert_match %r{format\.html \{ redirect_to @post, notice: 'Post was successfully updated\.' \}}, m assert_match %r{format\.json \{ render :show, status: :ok, location: @post \}}, m assert_match %r{format\.html \{ render :edit \}}, m assert_match %r{format\.json \{ render json: @post.errors, status: :unprocessable_entity \}}, m end assert_instance_method :destroy, content do |m| assert_match %r{@post\.destroy}, m assert_match %r{format\.html \{ redirect_to posts_url, notice: 'Post was successfully destroyed\.' \}}, m assert_match %r{format\.json \{ head :no_content \}}, m end assert_match %r{def post_params}, content assert_match %r{params\.require\(:post\)\.permit\(:title, :body\)}, content end end test 'dont use require and permit if there are no attributes' do run_generator %w(Post) assert_file 'app/controllers/posts_controller.rb' do |content| assert_match %r{def post_params}, content assert_match %r{params\.fetch\(:post, \{\}\)}, content end end end jbuilder-2.7.0/test/test_helper.rb0000644000004100000410000000041413273321662017176 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.7.0/test/jbuilder_test.rb0000644000004100000410000004110713273321662017523 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 test "throws MergeError when trying to merge array with non-empty hash" do assert_raise Jbuilder::MergeError do jbuild do |json| json.name "Daniel" json.merge! [] end end end test "throws MergeError when trying to merge hash with array" do assert_raise Jbuilder::MergeError do jbuild do |json| json.array! json.merge!({}) end end end test "throws MergeError when trying to merge invalid objects" do assert_raise Jbuilder::MergeError do jbuild do |json| json.name "Daniel" json.merge! "Nope" end end end end jbuilder-2.7.0/README.md0000644000004100000410000001510513273321662014636 0ustar www-datawww-data# Jbuilder Jbuilder gives you a simple DSL for declaring JSON structures that beats manipulating 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/messages/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 a la 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: 'comments/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 ``` To prevent Jbuilder from including null values in the output, you can use the `ignore_nil!` method: ```ruby json.ignore_nil! json.foo nil json.bar "bar" # => { "bar": "bar" } ``` 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 ``` ## Contributing to Jbuilder Jbuilder is the work of many contributors. You're encouraged to submit pull requests, propose features and discuss issues. See [CONTRIBUTING](CONTRIBUTING.md). ## License Jbuilder is released under the [MIT License](http://www.opensource.org/licenses/MIT). jbuilder-2.7.0/gemfiles/0000755000004100000410000000000013273321662015150 5ustar www-datawww-datajbuilder-2.7.0/gemfiles/rails_4_2.gemfile0000644000004100000410000000036313273321662020262 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.7.0/gemfiles/rails_5_0.gemfile0000644000004100000410000000036313273321662020261 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", "~> 5.0.0" gem "actionpack", "~> 5.0.0" gem "activemodel", "~> 5.0.0" gemspec path: "../" jbuilder-2.7.0/gemfiles/rails_5_1.gemfile0000644000004100000410000000041613273321662020261 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", ">= 5.1.0", "< 5.2" gem "actionpack", ">= 5.1.0", "< 5.2" gem "activemodel", ">= 5.1.0", "< 5.2" gemspec path: "../" jbuilder-2.7.0/CHANGELOG.md0000644000004100000410000002377213273321662015201 0ustar www-datawww-data# Changelog 2.6.4 ----- * Drop the pessimistic upper-bound restriction on Active Support and MultiJSON to prevent future gemfile resolution deadlocks *DHH* 2.6.3 ----- * Support Rails 5.1 2.6.2 ----- * Fix thor warnings 2.6.1 ----- * [Optimize root caches with cache_root!](https://github.com/rails/jbuilder/pull/370) 2.6.0 ----- * [Rails 5 cache! with expire support](https://github.com/rails/jbuilder/commit/d61e3354563863731bc1f358f495b1dbb7ae9d32) * [Generated view DRYed by using model partial](https://github.com/rails/jbuilder/commit/83256f4d7e9211c9dc47972feaed7fd31e4f7cac) 2.5.0 ----- * [Rails 5 compatibility](https://github.com/rails/jbuilder/commit/64c510ec69d9e63b73ffd5942e802d21a7d14701) 2.4.1 ----- * [Fix controller generators to be Rails 5 compatible](https://github.com/rails/jbuilder/commit/2dc6203c5c4a98701d5b64c2a5200835a48bb533) 2.4.0 ----- * [Rails 5 compatibility](https://github.com/rails/jbuilder/commit/4aa2cfcc19a4634d65a28ffc75f0ac0cb8304115) 2.3.2 ----- * [Remove Mime Types deprecation message](https://github.com/rails/jbuilder/commit/5ba4e4ac654cc8388619538f576fe234659b84ec) 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 performance optimizations: [#260](https://github.com/rails/jbuilder/pull/260) & [#261](https://github.com/rails/jbuilder/pull/261) 2.2.12 ------ * [Replace explicit 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) * [Meaningful 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.7.0/Appraisals0000644000004100000410000000062113273321662015376 0ustar www-datawww-dataappraise "rails-4-2" do gem "railties", "~> 4.2.0" gem "actionpack", "~> 4.2.0" gem "activemodel", "~> 4.2.0" end appraise "rails-5-0" do gem "railties", "~> 5.0.0" gem "actionpack", "~> 5.0.0" gem "activemodel", "~> 5.0.0" end appraise "rails-5-1" do gem "railties", ">= 5.1.0", "< 5.2" gem "actionpack", ">= 5.1.0", "< 5.2" gem "activemodel", ">= 5.1.0", "< 5.2" end jbuilder-2.7.0/.gitignore0000644000004100000410000000006313273321662015344 0ustar www-datawww-datatmp gemfiles/*.lock Gemfile.lock .ruby-version pkg jbuilder-2.7.0/Rakefile0000644000004100000410000000057013273321662015024 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" test.test_files = FileList["test/*_test.rb"] end task default: :test end jbuilder-2.7.0/lib/0000755000004100000410000000000013273321662014123 5ustar www-datawww-datajbuilder-2.7.0/lib/generators/0000755000004100000410000000000013273321662016274 5ustar www-datawww-datajbuilder-2.7.0/lib/generators/rails/0000755000004100000410000000000013273321662017406 5ustar www-datawww-datajbuilder-2.7.0/lib/generators/rails/jbuilder_generator.rb0000644000004100000410000000301713273321662023602 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 template filename_with_extensions('partial'), File.join('app/views', controller_file_path, filename_with_extensions("_#{singular_table_name}")) 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.7.0/lib/generators/rails/scaffold_controller_generator.rb0000644000004100000410000000050213273321662026022 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, type: :boolean, default: true end end end jbuilder-2.7.0/lib/generators/rails/templates/0000755000004100000410000000000013273321662021404 5ustar www-datawww-datajbuilder-2.7.0/lib/generators/rails/templates/index.json.jbuilder0000644000004100000410000000020713273321662025204 0ustar www-datawww-datajson.array! @<%= plural_table_name %>, partial: '<%= plural_table_name %>/<%= singular_table_name %>', as: :<%= singular_table_name %> jbuilder-2.7.0/lib/generators/rails/templates/api_controller.rb0000644000004100000410000000370213273321662024747 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.fetch(<%= ":#{singular_table_name}" %>, {}) <%- else -%> params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) <%- end -%> end end <% end -%> jbuilder-2.7.0/lib/generators/rails/templates/partial.json.jbuilder0000644000004100000410000000024413273321662025532 0ustar www-datawww-datajson.extract! <%= singular_table_name %>, <%= attributes_list_with_timestamps %> json.url <%= singular_table_name %>_url(<%= singular_table_name %>, format: :json) jbuilder-2.7.0/lib/generators/rails/templates/controller.rb0000644000004100000410000000541313273321662024117 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.fetch(<%= ":#{singular_table_name}" %>, {}) <%- else -%> params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) <%- end -%> end end <% end -%> jbuilder-2.7.0/lib/generators/rails/templates/show.json.jbuilder0000644000004100000410000000017513273321662025061 0ustar www-datawww-datajson.partial! "<%= plural_table_name %>/<%= singular_table_name %>", <%= singular_table_name %>: @<%= singular_table_name %> jbuilder-2.7.0/lib/jbuilder/0000755000004100000410000000000013273321662015723 5ustar www-datawww-datajbuilder-2.7.0/lib/jbuilder/railtie.rb0000644000004100000410000000174213273321662017705 0ustar www-datawww-datarequire 'rails/railtie' require 'jbuilder/jbuilder_template' class Jbuilder class Railtie < ::Rails::Railtie initializer :jbuilder do ActiveSupport.on_load :action_view do ActionView::Template.register_template_handler :jbuilder, JbuilderHandler require 'jbuilder/dependency_tracker' end if Rails::VERSION::MAJOR >= 5 module ::ActionController module ApiRendering include ActionView::Rendering end end ActiveSupport.on_load :action_controller do if self == ActionController::API include ActionController::Helpers include ActionController::ImplicitRender end 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.7.0/lib/jbuilder/errors.rb0000644000004100000410000000110213273321662017556 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 class MergeError < ::StandardError def self.build(current_value, updates) message = "Can't merge #{updates.inspect} into #{current_value.inspect}" new(message) end end end jbuilder-2.7.0/lib/jbuilder/jbuilder_template.rb0000644000004100000410000001513213273321662021745 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 @cached_root = nil 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 = _cache_fragment_for(key, options) do _scope { yield self } end merge! value else yield end end # Caches the json structure at the root using a string rather than the hash structure. This is considerably # faster, but the drawback is that it only works, as the name hints, at the root. So you cannot # use this approach to cache deeper inside the hierarchy, like in partials or such. Continue to use #cache! there. # # Example: # # json.cache_root! @person do # json.extract! @person, :name, :age # end # # # json.extra 'This will not work either, the root must be exclusive' def cache_root!(key=nil, options={}) if @context.controller.perform_caching raise "cache_root! can't be used after JSON structures have been defined" if @attributes.present? @cached_root = _cache_fragment_for([ :root, key ], options) { yield; target! } 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 target! @cached_root || super 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_fragment_for(key, options, &block) key = _cache_key(key, options) _read_fragment_cache(key, options) || _write_fragment_cache(key, options, &block) end def _read_fragment_cache(key, options = nil) @context.controller.instrument_fragment_cache :read_fragment, key do ::Rails.cache.read(key, options) end end def _write_fragment_cache(key, options = nil) @context.controller.instrument_fragment_cache :write_fragment, key do yield.tap do |value| ::Rails.cache.write(key, value, options) end end end def _cache_key(key, options) name_options = options.slice(:skip_digest, :virtual_path) key = _fragment_name_with_digest(key, name_options) if @context.respond_to?(:fragment_cache_key) key = @context.fragment_cache_key(key) else key = url_for(key).split('://', 2).last if ::Hash === key end ::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_with_options 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.7.0/lib/jbuilder/blank.rb0000644000004100000410000000020313273321662017332 0ustar www-datawww-dataclass Jbuilder class Blank def ==(other) super || Blank === other end def empty? true end end end jbuilder-2.7.0/lib/jbuilder/key_formatter.rb0000644000004100000410000000125513273321662021126 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, parameters| @format[name] = parameters 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.7.0/lib/jbuilder/jbuilder.rb0000644000004100000410000000027313273321662020052 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.7.0/lib/jbuilder/dependency_tracker.rb0000644000004100000410000000326513273321662022107 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.7.0/lib/jbuilder.rb0000644000004100000410000002070713273321662016256 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? || current_value.empty? && ::Array === updates updates elsif ::Array === current_value && ::Array === updates current_value + updates elsif ::Hash === current_value && ::Hash === updates current_value.merge(updates) else raise MergeError.build(current_value, updates) 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.7.0/CONTRIBUTING.md0000644000004100000410000000614613273321662015615 0ustar www-datawww-dataContributing to Jbuilder ===================== [![Build Status](https://api.travis-ci.org/rails/jbuilder.svg?branch=master)][travis] [![Gem Version](https://badge.fury.io/rb/jbuilder.svg)][gem] [![Code Climate](https://codeclimate.com/github/rails/jbuilder/badges/gpa.svg)][codeclimate] [![Dependencies Status](https://gemnasium.com/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 is work of [many contributors](https://github.com/rails/jbuilder/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/rails/jbuilder/pulls), [propose features and discuss issues](https://github.com/rails/jbuilder/issues). #### Fork the Project Fork the [project on Github](https://github.com/rails/jbuilder) and check out your copy. ``` git clone https://github.com/contributor/jbuilder.git cd jbuilder git remote add upstream https://github.com/rails/jbuilder.git ``` #### Create a Topic Branch Make sure your fork is up-to-date and create a topic branch for your feature or bug fix. ``` git checkout master git pull upstream master git checkout -b my-feature-branch ``` #### Bundle Install and Test Ensure that you can build the project and run tests. ``` bundle install appraisal install appraisal rake test ``` #### Write Tests Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [test](test). We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix. #### Write Code Implement your feature or bug fix. Make sure that `appraisal rake test` completes without errors. #### Write Documentation Document any external behavior in the [README](README.md). #### Commit Changes Make sure git knows your name and email address: ``` git config --global user.name "Your Name" git config --global user.email "contributor@example.com" ``` Writing good commit logs is important. A commit log should describe what changed and why. ``` git add ... git commit ``` #### Push ``` git push origin my-feature-branch ``` #### Make a Pull Request Visit your forked repo and click the 'New pull request' button. Select your feature branch, fill out the form, and click the 'Create pull request' button. Pull requests are usually reviewed within a few days. #### Rebase If you've been working on a change for a while, rebase with upstream/master. ``` git fetch upstream git rebase upstream/master git push origin my-feature-branch -f ``` #### Check on Your Pull Request Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above. #### Be Patient It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang in there! #### Thank You Please do know that we really appreciate and value your time and work. We love you, really. jbuilder-2.7.0/jbuilder.gemspec0000644000004100000410000000102713273321662016522 0ustar www-datawww-dataGem::Specification.new do |s| s.name = 'jbuilder' s.version = '2.7.0' s.authors = 'David Heinemeier Hansson' s.email = 'david@basecamp.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', '>= 4.2.0' 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.7.0/Gemfile0000644000004100000410000000015113273321662014645 0ustar www-datawww-datasource "https://rubygems.org" gemspec gem "rake" gem "mocha", require: false gem "appraisal" gem "pry" jbuilder-2.7.0/MIT-LICENSE0000644000004100000410000000207413273321662015014 0ustar www-datawww-dataCopyright (c) 2011-2017 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.