jbuilder - initial gem2deb package.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Tue, 11 Mar 2014 23:17:07 +0000 (00:17 +0100)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Tue, 11 Mar 2014 23:17:07 +0000 (00:17 +0100)
34 files changed:
ruby-jbuilder/.gitignore [new file with mode: 0644]
ruby-jbuilder/.travis.yml [new file with mode: 0644]
ruby-jbuilder/CHANGELOG.md [new file with mode: 0644]
ruby-jbuilder/Gemfile [new file with mode: 0644]
ruby-jbuilder/Gemfile.old [new file with mode: 0644]
ruby-jbuilder/MIT-LICENSE [new file with mode: 0644]
ruby-jbuilder/README.md [new file with mode: 0644]
ruby-jbuilder/Rakefile [new file with mode: 0644]
ruby-jbuilder/checksums.yaml.gz [new file with mode: 0644]
ruby-jbuilder/debian/changelog [new file with mode: 0644]
ruby-jbuilder/debian/compat [new file with mode: 0644]
ruby-jbuilder/debian/control [new file with mode: 0644]
ruby-jbuilder/debian/copyright [new file with mode: 0644]
ruby-jbuilder/debian/ruby-jbuilder.docs [new file with mode: 0644]
ruby-jbuilder/debian/ruby-test-files.yaml [new file with mode: 0644]
ruby-jbuilder/debian/rules [new file with mode: 0755]
ruby-jbuilder/debian/source/format [new file with mode: 0644]
ruby-jbuilder/debian/watch [new file with mode: 0644]
ruby-jbuilder/jbuilder.gemspec [new file with mode: 0644]
ruby-jbuilder/lib/generators/rails/jbuilder_generator.rb [new file with mode: 0644]
ruby-jbuilder/lib/generators/rails/scaffold_controller_generator.rb [new file with mode: 0644]
ruby-jbuilder/lib/generators/rails/templates/controller.rb [new file with mode: 0644]
ruby-jbuilder/lib/generators/rails/templates/index.json.jbuilder [new file with mode: 0644]
ruby-jbuilder/lib/generators/rails/templates/show.json.jbuilder [new file with mode: 0644]
ruby-jbuilder/lib/jbuilder.rb [new file with mode: 0644]
ruby-jbuilder/lib/jbuilder/dependency_tracker.rb [new file with mode: 0644]
ruby-jbuilder/lib/jbuilder/jbuilder_template.rb [new file with mode: 0644]
ruby-jbuilder/lib/jbuilder/railtie.rb [new file with mode: 0644]
ruby-jbuilder/metadata.yml [new file with mode: 0644]
ruby-jbuilder/test/jbuilder_dependency_tracker_test.rb [new file with mode: 0644]
ruby-jbuilder/test/jbuilder_generator_test.rb [new file with mode: 0644]
ruby-jbuilder/test/jbuilder_template_test.rb [new file with mode: 0644]
ruby-jbuilder/test/jbuilder_test.rb [new file with mode: 0644]
ruby-jbuilder/test/scaffold_controller_generator_test.rb [new file with mode: 0644]

diff --git a/ruby-jbuilder/.gitignore b/ruby-jbuilder/.gitignore
new file mode 100644 (file)
index 0000000..acea2d2
--- /dev/null
@@ -0,0 +1,4 @@
+tmp
+Gemfile.lock
+Gemfile.old.lock
+.ruby-version
diff --git a/ruby-jbuilder/.travis.yml b/ruby-jbuilder/.travis.yml
new file mode 100644 (file)
index 0000000..8f4fb50
--- /dev/null
@@ -0,0 +1,23 @@
+language: ruby
+
+rvm:
+  - 1.9.3
+  - 2.0.0
+  - 2.1.0
+  - ruby-head
+  - jruby-19mode
+  - rbx
+
+gemfile:
+  - Gemfile.old
+  - Gemfile
+
+matrix:
+  allow_failures:
+    - rvm: 2.1.0
+    - rvm: ruby-head
+    - rvm: jruby-19mode
+    - rvm: rbx
+
+notifications:
+  email: false
diff --git a/ruby-jbuilder/CHANGELOG.md b/ruby-jbuilder/CHANGELOG.md
new file mode 100644 (file)
index 0000000..a29274e
--- /dev/null
@@ -0,0 +1,85 @@
+# Changelog
+
+2.0.4
+-----
+* [Add cache_if! to conditionally cache JSON fragments](https://github.com/rails/jbuilder/commit/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
diff --git a/ruby-jbuilder/Gemfile b/ruby-jbuilder/Gemfile
new file mode 100644 (file)
index 0000000..4bc536b
--- /dev/null
@@ -0,0 +1,15 @@
+source 'https://rubygems.org'
+
+gemspec
+
+gem 'rake'
+gem 'railties',   '~> 4.0.0'
+gem 'actionpack', '~> 4.0.0'
+gem 'mocha', require: false
+
+platforms :rbx do
+  gem 'rubysl', '~> 2.0'
+  gem 'json', '~> 1.8'
+  gem 'rubysl-test-unit'
+  gem 'rubinius-developer_tools'
+end
diff --git a/ruby-jbuilder/Gemfile.old b/ruby-jbuilder/Gemfile.old
new file mode 100644 (file)
index 0000000..4c5903e
--- /dev/null
@@ -0,0 +1,14 @@
+source 'https://rubygems.org'
+
+gemspec
+
+gem 'rake'
+gem 'mocha', :require => false
+gem 'actionpack', '~> 3.0'
+
+platforms :rbx do
+  gem 'rubysl', '~> 2.0'
+  gem 'json', '~> 1.8'
+  gem 'rubysl-test-unit'
+  gem 'rubinius-developer_tools'
+end
diff --git a/ruby-jbuilder/MIT-LICENSE b/ruby-jbuilder/MIT-LICENSE
new file mode 100644 (file)
index 0000000..46206da
--- /dev/null
@@ -0,0 +1,20 @@
+Copyright (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.
diff --git a/ruby-jbuilder/README.md b/ruby-jbuilder/README.md
new file mode 100644 (file)
index 0000000..d3fc9cd
--- /dev/null
@@ -0,0 +1,216 @@
+Jbuilder [![Build Status](https://travis-ci.org/rails/jbuilder.png)](https://travis-ci.org/rails/jbuilder) [![Gem Version](https://badge.fury.io/rb/jbuilder.png)](https://rubygems.org/gems/jbuilder) [![Code Climate](https://codeclimate.com/github/rails/jbuilder.png)](https://codeclimate.com/github/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
+Jbuilder.encode do |json|
+  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
+end
+```
+
+This will build the following structure:
+
+``` javascript
+{
+  "content": "<p>This is <i>serious</i> monkey business</p>",
+  "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' <david@heinemeierhansson.com>",
+    "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
+# @people = People.all
+json.array! @people do |person|
+  json.name person.name
+  json.age calculate_age(person.birthday)
+end
+
+# => [ { "name": "David", "age": 32 }, { "name": "Jamie", "age": 31 } ]
+```
+
+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 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
+```
+
+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 (work is being done to correct this for a future version of Rails). 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
+ ```
diff --git a/ruby-jbuilder/Rakefile b/ruby-jbuilder/Rakefile
new file mode 100644 (file)
index 0000000..04bf196
--- /dev/null
@@ -0,0 +1,14 @@
+require 'bundler'
+require 'rake/testtask'
+
+Bundler.require
+
+Rake::TestTask.new do |test|
+  if /old$/ === ENV['BUNDLE_GEMFILE']
+    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
diff --git a/ruby-jbuilder/checksums.yaml.gz b/ruby-jbuilder/checksums.yaml.gz
new file mode 100644 (file)
index 0000000..9e4b24e
Binary files /dev/null and b/ruby-jbuilder/checksums.yaml.gz differ
diff --git a/ruby-jbuilder/debian/changelog b/ruby-jbuilder/debian/changelog
new file mode 100644 (file)
index 0000000..46bf840
--- /dev/null
@@ -0,0 +1,5 @@
+ruby-jbuilder (2.0.4-1) UNRELEASED; urgency=medium
+
+  * Initial release (Closes: #nnnn)
+
+ -- MAINTAINER <valtri@myriad14.zcu.cz>  Wed, 12 Mar 2014 00:15:53 +0100
diff --git a/ruby-jbuilder/debian/compat b/ruby-jbuilder/debian/compat
new file mode 100644 (file)
index 0000000..7f8f011
--- /dev/null
@@ -0,0 +1 @@
+7
diff --git a/ruby-jbuilder/debian/control b/ruby-jbuilder/debian/control
new file mode 100644 (file)
index 0000000..68ef9b1
--- /dev/null
@@ -0,0 +1,19 @@
+Source: ruby-jbuilder
+Section: ruby
+Priority: optional
+Maintainer: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers@lists.alioth.debian.org>
+Uploaders:  <>
+Build-Depends: debhelper (>= 7.0.50~), gem2deb (>= 0.6.1~)
+Standards-Version: 3.9.4
+#Vcs-Git: git://anonscm.debian.org/pkg-ruby-extras/ruby-jbuilder.git
+#Vcs-Browser: http://anonscm.debian.org/gitweb/?p=pkg-ruby-extras/ruby-jbuilder.git;a=summary
+Homepage: https://github.com/rails/jbuilder
+XS-Ruby-Versions: all
+
+Package: ruby-jbuilder
+Architecture: all
+XB-Ruby-Versions: ${ruby:Versions}
+Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter
+# activesupport (>= 3.0.0), multi_json (>= 1.2.0)
+Description: Create JSON structures via a Builder-style DSL
+ <insert long description, indented with spaces>
diff --git a/ruby-jbuilder/debian/copyright b/ruby-jbuilder/debian/copyright
new file mode 100644 (file)
index 0000000..7f14407
--- /dev/null
@@ -0,0 +1,35 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: jbuilder
+Source: FIXME <http://example.com/>
+
+Files: *
+Copyright: <years> <put author's name and email here>
+           <years> <likewise for another author>
+License: GPL-2+ (FIXME)
+
+Files: debian/*
+Copyright: 2014  <>
+License: GPL-2+ (FIXME)
+Comment: the Debian packaging is licensed under the same terms as the original package.
+
+License: GPL-2+ (FIXME)
+ This program is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ .
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE.  See the GNU General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this package; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA  02110-1301 USA
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2'.
diff --git a/ruby-jbuilder/debian/ruby-jbuilder.docs b/ruby-jbuilder/debian/ruby-jbuilder.docs
new file mode 100644 (file)
index 0000000..07b3c9e
--- /dev/null
@@ -0,0 +1,2 @@
+# FIXME: READMEs found
+# README.md
diff --git a/ruby-jbuilder/debian/ruby-test-files.yaml b/ruby-jbuilder/debian/ruby-test-files.yaml
new file mode 100644 (file)
index 0000000..17b1972
--- /dev/null
@@ -0,0 +1,6 @@
+---
+- test/jbuilder_dependency_tracker_test.rb
+- test/jbuilder_generator_test.rb
+- test/jbuilder_template_test.rb
+- test/jbuilder_test.rb
+- test/scaffold_controller_generator_test.rb
diff --git a/ruby-jbuilder/debian/rules b/ruby-jbuilder/debian/rules
new file mode 100755 (executable)
index 0000000..82ddc0c
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/make -f
+#export DH_VERBOSE=1
+#
+# Uncomment to ignore all test failures (but the tests will run anyway)
+#export DH_RUBY_IGNORE_TESTS=all
+#
+# Uncomment to ignore some test failures (but the tests will run anyway).
+# Valid values:
+#export DH_RUBY_IGNORE_TESTS=ruby1.9.1 ruby2.0 require-rubygems
+#
+# If you need to specify the .gemspec (eg there is more than one)
+#export DH_RUBY_GEMSPEC=gem.gemspec
+
+%:
+       dh $@ --buildsystem=ruby --with ruby
diff --git a/ruby-jbuilder/debian/source/format b/ruby-jbuilder/debian/source/format
new file mode 100644 (file)
index 0000000..163aaf8
--- /dev/null
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/ruby-jbuilder/debian/watch b/ruby-jbuilder/debian/watch
new file mode 100644 (file)
index 0000000..41a5e95
--- /dev/null
@@ -0,0 +1,2 @@
+version=3
+http://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/jbuilder .*/jbuilder-(.*).tar.gz
diff --git a/ruby-jbuilder/jbuilder.gemspec b/ruby-jbuilder/jbuilder.gemspec
new file mode 100644 (file)
index 0000000..1d7bc66
--- /dev/null
@@ -0,0 +1,17 @@
+Gem::Specification.new do |s|
+  s.name     = 'jbuilder'
+  s.version  = '2.0.4'
+  s.author   = 'David Heinemeier Hansson'
+  s.email    = 'david@37signals.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'
+  s.add_dependency 'multi_json',    '>= 1.2.0'
+
+  s.files         = `git ls-files`.split("\n")
+  s.test_files    = `git ls-files -- test/*`.split("\n")
+end
diff --git a/ruby-jbuilder/lib/generators/rails/jbuilder_generator.rb b/ruby-jbuilder/lib/generators/rails/jbuilder_generator.rb
new file mode 100644 (file)
index 0000000..22058a6
--- /dev/null
@@ -0,0 +1,48 @@
+require '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
diff --git a/ruby-jbuilder/lib/generators/rails/scaffold_controller_generator.rb b/ruby-jbuilder/lib/generators/rails/scaffold_controller_generator.rb
new file mode 100644 (file)
index 0000000..f6ce97a
--- /dev/null
@@ -0,0 +1,12 @@
+require 'rails/generators'
+require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
+
+module Rails
+  module Generators
+    class ScaffoldControllerGenerator
+      source_root File.expand_path('../templates', __FILE__)
+
+      hook_for :jbuilder, default: true
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby-jbuilder/lib/generators/rails/templates/controller.rb b/ruby-jbuilder/lib/generators/rails/templates/controller.rb
new file mode 100644 (file)
index 0000000..c6f78e6
--- /dev/null
@@ -0,0 +1,84 @@
+<% 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 action: 'show', status: :created, location: <%= "@#{singular_table_name}" %> }
+      else
+        format.html { render action: '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 action: 'show', status: :ok, location: <%= "@#{singular_table_name}" %> }
+      else
+        format.html { render action: '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 }
+      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 -%>
diff --git a/ruby-jbuilder/lib/generators/rails/templates/index.json.jbuilder b/ruby-jbuilder/lib/generators/rails/templates/index.json.jbuilder
new file mode 100644 (file)
index 0000000..063780e
--- /dev/null
@@ -0,0 +1,4 @@
+json.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
diff --git a/ruby-jbuilder/lib/generators/rails/templates/show.json.jbuilder b/ruby-jbuilder/lib/generators/rails/templates/show.json.jbuilder
new file mode 100644 (file)
index 0000000..46dd412
--- /dev/null
@@ -0,0 +1 @@
+json.extract! @<%= singular_table_name %>, <%= attributes_list_with_timestamps %>
diff --git a/ruby-jbuilder/lib/jbuilder.rb b/ruby-jbuilder/lib/jbuilder.rb
new file mode 100644 (file)
index 0000000..6db3584
--- /dev/null
@@ -0,0 +1,332 @@
+require 'active_support/core_ext/array/access'
+require 'active_support/core_ext/enumerable'
+require 'active_support/core_ext/hash'
+require 'multi_json'
+
+begin
+  require 'active_support/proxy_object'
+  JbuilderProxy = ActiveSupport::ProxyObject
+rescue LoadError
+  require 'active_support/basic_object'
+  JbuilderProxy = ActiveSupport::BasicObject
+end
+
+class Jbuilder < JbuilderProxy
+  class NullError < ::NoMethodError
+    def initialize(key)
+      super "Failed to add #{key.to_s.inspect} property to null object"
+    end
+  end
+
+  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
+
+  # Yields a builder and automatically turns the result into a JSON string
+  def self.encode(*args, &block)
+    new(*args, &block).target!
+  end
+
+  @@key_formatter = KeyFormatter.new
+  @@ignore_nil    = false
+
+  def initialize(options = {}, &block)
+    @attributes = {}
+
+    @key_formatter = options.fetch(:key_formatter){ @@key_formatter.clone }
+    @ignore_nil = options.fetch(:ignore_nil, @@ignore_nil)
+    yield self if block
+  end
+
+  BLANK = ::Object.new
+
+  def set!(key, value = BLANK, *args, &block)
+
+    result = if block
+      if BLANK != value
+        # json.comments @post.comments { |comment| ... }
+        # { "comments": [ { ... }, { ... } ] }
+        _scope{ array! value, &block }
+      else
+        # json.comments { ... }
+        # { "comments": ... }
+        _scope { 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 _mapable_arguments?(value, *args)
+      # 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" } }
+      _scope { extract! value, *args }
+    end
+
+    _set_value key, result
+  end
+
+  alias_method :method_missing, :set!
+  private :method_missing
+
+
+  # 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, &block)
+    @attributes = if block
+      _map_collection(collection, &block)
+    elsif attributes.any?
+      _map_collection(collection) { |element| extract! element, *attributes }
+    else
+      collection
+    end
+  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, &block)
+    if block
+      array! object, &block
+    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)
+    if ::Array === hash_or_array
+      @attributes = [] unless ::Array === @attributes
+      @attributes.concat hash_or_array
+    else
+      @attributes.update hash_or_array
+    end
+  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 _set_value(key, value)
+      raise NullError, key if @attributes.nil?
+      unless @ignore_nil && value.nil?
+        @attributes[@key_formatter.format(key)] = value
+      end
+    end
+
+    def _map_collection(collection)
+      return [] if collection.nil?
+
+      collection.map do |element|
+        _scope { yield element }
+      end
+    end
+
+    def _scope
+      parent_attributes, parent_formatter = @attributes, @key_formatter
+      @attributes = {}
+      yield
+      @attributes
+    ensure
+      @attributes, @key_formatter = parent_attributes, parent_formatter
+    end
+
+    def _mapable_arguments?(value, *args)
+      value.respond_to?(:map)
+    end
+end
+
+require 'jbuilder/jbuilder_template' if defined?(ActionView::Template)
+require 'jbuilder/dependency_tracker'
+require 'jbuilder/railtie' if defined?(Rails::VERSION::MAJOR) && Rails::VERSION::MAJOR == 4
diff --git a/ruby-jbuilder/lib/jbuilder/dependency_tracker.rb b/ruby-jbuilder/lib/jbuilder/dependency_tracker.rb
new file mode 100644 (file)
index 0000000..c937dff
--- /dev/null
@@ -0,0 +1,61 @@
+require '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
diff --git a/ruby-jbuilder/lib/jbuilder/jbuilder_template.rb b/ruby-jbuilder/lib/jbuilder/jbuilder_template.rb
new file mode 100644 (file)
index 0000000..6ec4ac8
--- /dev/null
@@ -0,0 +1,132 @@
+require '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, &block)
+    @context = context
+    super(*args, &block)
+  end
+
+  def partial!(name_or_options, locals = {})
+    case name_or_options
+    when ::Hash
+      # partial! partial: 'name', locals: { foo: 'bar' }
+      options = name_or_options
+    else
+      # partial! 'name', foo: 'bar'
+      options = { partial: name_or_options, locals: locals }
+      as = locals.delete(:as)
+      options[:as] = as if as.present?
+      options[:collection] = locals[:collection] if locals.key?(:collection)
+    end
+
+    _handle_partial_options options
+  end
+
+  def array!(collection = [], *attributes, &block)
+    options = attributes.extract_options!
+
+    if options.key?(:partial)
+      partial! options[:partial], options.merge(collection: collection)
+    else
+      super
+    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={}, &block)
+    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 catches 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, &block)
+    condition ? cache!(*args, &block) : yield
+  end
+
+  protected
+    def _handle_partial_options(options)
+      options.reverse_merge! locals: {}
+      options.reverse_merge! ::JbuilderTemplate.template_lookup_options
+      as = options[:as]
+
+      if as && options.key?(:collection)
+        collection = options.delete(:collection) || []
+        array!(collection) do |member|
+          options[:locals].merge! as => member
+          options[:locals].merge! collection: collection
+          _render_partial options
+        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)
+      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
+        ::ActiveSupport::Cache.expand_cache_key(key.is_a?(::Hash) ? url_for(key).split('://').last : key, :jbuilder)
+      end
+    end
+
+  private
+
+    def _mapable_arguments?(value, *args)
+      return true if super
+      options = args.last
+      ::Hash === options && options.key?(:as)
+    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}
+  end
+end
+
+ActionView::Template.register_template_handler :jbuilder, JbuilderHandler
diff --git a/ruby-jbuilder/lib/jbuilder/railtie.rb b/ruby-jbuilder/lib/jbuilder/railtie.rb
new file mode 100644 (file)
index 0000000..28a428f
--- /dev/null
@@ -0,0 +1,11 @@
+require 'rails/railtie'
+
+class Jbuilder
+  class Railtie < ::Rails::Railtie
+    generators do |app|
+      Rails::Generators.configure! app.config.generators
+      Rails::Generators.hidden_namespaces.uniq!
+      require 'generators/rails/scaffold_controller_generator'
+    end
+  end
+end
\ No newline at end of file
diff --git a/ruby-jbuilder/metadata.yml b/ruby-jbuilder/metadata.yml
new file mode 100644 (file)
index 0000000..30b5f57
--- /dev/null
@@ -0,0 +1,99 @@
+--- !ruby/object:Gem::Specification
+name: jbuilder
+version: !ruby/object:Gem::Version
+  version: 2.0.4
+platform: ruby
+authors:
+- David Heinemeier Hansson
+autorequire: 
+bindir: bin
+cert_chain: []
+date: 2014-03-04 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
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ">="
+      - !ruby/object:Gem::Version
+        version: 3.0.0
+- !ruby/object:Gem::Dependency
+  name: multi_json
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ">="
+      - !ruby/object:Gem::Version
+        version: 1.2.0
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ">="
+      - !ruby/object:Gem::Version
+        version: 1.2.0
+description: 
+email: david@37signals.com
+executables: []
+extensions: []
+extra_rdoc_files: []
+files:
+- ".gitignore"
+- ".travis.yml"
+- CHANGELOG.md
+- Gemfile
+- Gemfile.old
+- MIT-LICENSE
+- README.md
+- Rakefile
+- jbuilder.gemspec
+- lib/generators/rails/jbuilder_generator.rb
+- lib/generators/rails/scaffold_controller_generator.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/dependency_tracker.rb
+- lib/jbuilder/jbuilder_template.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_controller_generator_test.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.2.2
+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_controller_generator_test.rb
diff --git a/ruby-jbuilder/test/jbuilder_dependency_tracker_test.rb b/ruby-jbuilder/test/jbuilder_dependency_tracker_test.rb
new file mode 100644 (file)
index 0000000..51dfa30
--- /dev/null
@@ -0,0 +1,73 @@
+require 'test/unit'
+require 'active_support/test_case'
+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
diff --git a/ruby-jbuilder/test/jbuilder_generator_test.rb b/ruby-jbuilder/test/jbuilder_generator_test.rb
new file mode 100644 (file)
index 0000000..64564ea
--- /dev/null
@@ -0,0 +1,31 @@
+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
diff --git a/ruby-jbuilder/test/jbuilder_template_test.rb b/ruby-jbuilder/test/jbuilder_template_test.rb
new file mode 100644 (file)
index 0000000..706e8a0
--- /dev/null
@@ -0,0 +1,292 @@
+require 'test/unit'
+require 'mocha/setup'
+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
+    name = blog_post.author_name.split(nil, 2)
+    json.first_name name[0]
+    json.last_name  name[1]
+  end
+JBUILDER
+
+BlogPost = Struct.new(:id, :body, :author_name)
+blog_authors = [ 'David Heinemeier Hansson', 'Pavel Pravosud' ].cycle
+BLOG_POST_COLLECTION = 10.times.map{ |i| BlogPost.new(i+1, "post body #{i+1}", blog_authors.next) }
+
+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 partials
+    {
+      '_partial.json.jbuilder'  => 'json.content "hello"',
+      '_blog_post.json.jbuilder' => BLOG_POST_PARTIAL
+    }
+  end
+
+  def render_jbuilder(source)
+    @rendered = []
+    lookup_context.view_paths = [ActionView::FixtureResolver.new(partials.merge('test.json.jbuilder' => source))]
+    ActionView::Template.new(source, 'test', JbuilderHandler, :virtual_path => 'test').render(self, {}).strip
+  end
+
+  def undef_context_methods(*names)
+    self.class_eval do
+      names.each do |name|
+        undef_method name.to_sym if self.method_defined?(name.to_sym)
+      end
+    end
+  end
+
+  def assert_collection_rendered(json, context = nil)
+    result = MultiJson.load(json)
+    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
+    json = render_jbuilder <<-JBUILDER
+      json.content 'hello'
+    JBUILDER
+
+    assert_equal 'hello', MultiJson.load(json)['content']
+  end
+
+  test 'key_format! with parameter' do
+    json = render_jbuilder <<-JBUILDER
+      json.key_format! :camelize => [:lower]
+      json.camel_style 'for JS'
+    JBUILDER
+
+    assert_equal ['camelStyle'], MultiJson.load(json).keys
+  end
+
+  test 'key_format! propagates to child elements' do
+    json = render_jbuilder <<-JBUILDER
+      json.key_format! :upcase
+      json.level1 'one'
+      json.level2 do
+        json.value 'two'
+      end
+    JBUILDER
+
+    result = MultiJson.load(json)
+    assert_equal 'one', result['LEVEL1']
+    assert_equal 'two', result['LEVEL2']['VALUE']
+  end
+
+  test 'partial! renders partial' do
+    json = render_jbuilder <<-JBUILDER
+      json.partial! 'partial'
+    JBUILDER
+
+    assert_equal 'hello', MultiJson.load(json)['content']
+  end
+
+  test 'partial! renders collections' do
+    json = render_jbuilder <<-JBUILDER
+      json.partial! 'blog_post', :collection => BLOG_POST_COLLECTION, :as => :blog_post
+    JBUILDER
+
+    assert_collection_rendered json
+  end
+
+  test 'partial! renders as empty array for nil-collection' do
+    json = render_jbuilder <<-JBUILDER
+      json.partial! 'blog_post', :collection => nil, :as => :blog_post
+    JBUILDER
+
+    assert_equal '[]', json
+  end
+
+  test 'partial! renders collection (alt. syntax)' do
+    json = render_jbuilder <<-JBUILDER
+      json.partial! :partial => 'blog_post', :collection => BLOG_POST_COLLECTION, :as => :blog_post
+    JBUILDER
+
+    assert_collection_rendered json
+  end
+
+  test 'partial! renders as empty array for nil-collection (alt. syntax)' do
+    json = render_jbuilder <<-JBUILDER
+      json.partial! :partial => 'blog_post', :collection => nil, :as => :blog_post
+    JBUILDER
+
+    assert_equal '[]', json
+  end
+
+  test 'render array of partials' do
+    json = render_jbuilder <<-JBUILDER
+      json.array! BLOG_POST_COLLECTION, :partial => 'blog_post', :as => :blog_post
+    JBUILDER
+
+    assert_collection_rendered json
+  end
+
+  test 'render array of partials as empty array with nil-collection' do
+    json = render_jbuilder <<-JBUILDER
+      json.array! nil, :partial => 'blog_post', :as => :blog_post
+    JBUILDER
+
+    assert_equal '[]', json
+  end
+
+  test 'render array if partials as a value' do
+    json = render_jbuilder <<-JBUILDER
+      json.posts BLOG_POST_COLLECTION, :partial => 'blog_post', :as => :blog_post
+    JBUILDER
+
+    assert_collection_rendered json, 'posts'
+  end
+
+  test 'render as empty array if partials as a nil value' do
+    json = render_jbuilder <<-JBUILDER
+      json.posts nil, :partial => 'blog_post', :as => :blog_post
+    JBUILDER
+
+    assert_equal '{"posts":[]}', json
+  end
+
+  test 'fragment caching a JSON object' do
+    undef_context_methods :fragment_name_with_digest, :cache_fragment_name
+
+    render_jbuilder <<-JBUILDER
+      json.cache! 'cachekey' do
+        json.name 'Cache'
+      end
+    JBUILDER
+
+    json = render_jbuilder <<-JBUILDER
+      json.cache! 'cachekey' do
+        json.name 'Miss'
+      end
+    JBUILDER
+
+    parsed = MultiJson.load(json)
+    assert_equal 'Cache', parsed['name']
+  end
+
+  test 'conditionally fragment caching a JSON object' do
+    undef_context_methods :fragment_name_with_digest, :cache_fragment_name
+
+    render_jbuilder <<-JBUILDER
+      json.cache_if! true, 'cachekey' do
+        json.test1 'Cache'
+      end
+      json.cache_if! false, 'cachekey' do
+        json.test2 'Cache'
+      end
+    JBUILDER
+
+    json = render_jbuilder <<-JBUILDER
+      json.cache_if! true, 'cachekey' do
+        json.test1 'Miss'
+      end
+      json.cache_if! false, 'cachekey' do
+        json.test2 'Miss'
+      end
+    JBUILDER
+
+    parsed = MultiJson.load(json)
+    assert_equal 'Cache', parsed['test1']
+    assert_equal 'Miss', parsed['test2']
+  end
+
+  test 'fragment caching deserializes an array' do
+    undef_context_methods :fragment_name_with_digest, :cache_fragment_name
+
+    render_jbuilder <<-JBUILDER
+      json.cache! 'cachekey' do
+        json.array! %w(a b c)
+      end
+    JBUILDER
+
+    json = render_jbuilder <<-JBUILDER
+      json.cache! 'cachekey' do
+        json.array! %w(1 2 3)
+      end
+    JBUILDER
+
+    parsed = MultiJson.load(json)
+    assert_equal %w(a b c), parsed
+  end
+
+  test 'fragment caching works with previous version of cache digests' do
+    undef_context_methods :cache_fragment_name
+
+    @context.expects :fragment_name_with_digest
+
+    render_jbuilder <<-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
+
+    render_jbuilder <<-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)
+
+    render_jbuilder <<-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
+    render_jbuilder <<-JBUILDER
+      json.cache! 'cachekey' do
+        json.name 'Cache'
+      end
+    JBUILDER
+
+    assert_equal Rails.cache.inspect[/entries=(\d+)/, 1], '0'
+  end
+
+  test 'fragment caching falls back on ActiveSupport::Cache.expand_cache_key' do
+    undef_context_methods :fragment_name_with_digest, :cache_fragment_name
+
+    ActiveSupport::Cache.expects :expand_cache_key
+
+    render_jbuilder <<-JBUILDER
+      json.cache! 'cachekey' do
+        json.name 'Cache'
+      end
+    JBUILDER
+  end
+
+end
\ No newline at end of file
diff --git a/ruby-jbuilder/test/jbuilder_test.rb b/ruby-jbuilder/test/jbuilder_test.rb
new file mode 100644 (file)
index 0000000..12c90e5
--- /dev/null
@@ -0,0 +1,545 @@
+require 'test/unit'
+require 'active_support/test_case'
+require 'active_support/inflector'
+require 'jbuilder'
+
+Comment = Struct.new(:content, :id)
+
+class NonEnumerable
+  def initialize(collection)
+    @collection = collection
+  end
+
+  def map(&block)
+    @collection.map(&block)
+  end
+end
+
+class JbuilderTest < ActiveSupport::TestCase
+  test 'single key' do
+    json = Jbuilder.encode do |json|
+      json.content 'hello'
+    end
+
+    assert_equal 'hello', MultiJson.load(json)['content']
+  end
+
+  test 'single key with false value' do
+    json = Jbuilder.encode do |json|
+      json.content false
+    end
+
+    assert_equal false, MultiJson.load(json)['content']
+  end
+
+  test 'single key with nil value' do
+    json = Jbuilder.encode do |json|
+      json.content nil
+    end
+
+    assert MultiJson.load(json).has_key?('content')
+    assert_equal nil, MultiJson.load(json)['content']
+  end
+
+  test 'multiple keys' do
+    json = Jbuilder.encode do |json|
+      json.title 'hello'
+      json.content 'world'
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'hello', parsed['title']
+    assert_equal 'world', parsed['content']
+  end
+
+  test 'extracting from object' do
+    person = Struct.new(:name, :age).new('David', 32)
+
+    json = Jbuilder.encode do |json|
+      json.extract! person, :name, :age
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'David', parsed['name']
+    assert_equal 32, parsed['age']
+  end
+
+  test 'extracting from object using call style for 1.9' do
+    person = Struct.new(:name, :age).new('David', 32)
+
+    json = Jbuilder.encode do |json|
+      json.(person, :name, :age)
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'David', parsed['name']
+    assert_equal 32, parsed['age']
+  end
+
+  test 'extracting from hash' do
+    person = {:name => 'Jim', :age => 34}
+
+    json = Jbuilder.encode do |json|
+      json.extract! person, :name, :age
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'Jim', parsed['name']
+    assert_equal 34, parsed['age']
+  end
+
+  test 'nesting single child with block' do
+    json = Jbuilder.encode do |json|
+      json.author do
+        json.name 'David'
+        json.age  32
+      end
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'David', parsed['author']['name']
+    assert_equal 32, parsed['author']['age']
+  end
+
+  test 'nesting multiple children with block' do
+    json = Jbuilder.encode do |json|
+      json.comments do
+        json.child! { json.content 'hello' }
+        json.child! { json.content 'world' }
+      end
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'hello', parsed['comments'].first['content']
+    assert_equal 'world', parsed['comments'].second['content']
+  end
+
+  test 'nesting single child with inline extract' do
+    person = Class.new do
+      attr_reader :name, :age
+
+      def initialize(name, age)
+        @name, @age = name, age
+      end
+    end.new('David', 32)
+
+    json = Jbuilder.encode do |json|
+      json.author person, :name, :age
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'David', parsed['author']['name']
+    assert_equal 32,      parsed['author']['age']
+  end
+
+  test 'nesting multiple children from array' do
+    comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
+
+    json = Jbuilder.encode do |json|
+      json.comments comments, :content
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal ['content'], parsed['comments'].first.keys
+    assert_equal 'hello', parsed['comments'].first['content']
+    assert_equal 'world', parsed['comments'].second['content']
+  end
+
+  test 'nesting multiple children from array when child array is empty' do
+    comments = []
+
+    json = Jbuilder.encode do |json|
+      json.name 'Parent'
+      json.comments comments, :content
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'Parent', parsed['name']
+    assert_equal [], parsed['comments']
+  end
+
+  test 'nesting multiple children from array with inline loop' do
+    comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
+
+    json = Jbuilder.encode do |json|
+      json.comments comments do |comment|
+        json.content comment.content
+      end
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal ['content'], parsed['comments'].first.keys
+    assert_equal 'hello', parsed['comments'].first['content']
+    assert_equal 'world', parsed['comments'].second['content']
+  end
+
+  test 'handles nil-collections as empty arrays' do
+    json = Jbuilder.encode do |json|
+      json.comments nil do |comment|
+        json.content comment.content
+      end
+    end
+
+    assert_equal [], MultiJson.load(json)['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) ])
+
+    json = Jbuilder.encode do |json|
+      json.comments comments, :content
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal ['content'], parsed['comments'].first.keys
+    assert_equal 'hello', parsed['comments'].first['content']
+    assert_equal 'world', parsed['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) ])
+
+    json = Jbuilder.encode do |json|
+      json.comments comments do |comment|
+        json.content comment.content
+      end
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal ['content'], parsed['comments'].first.keys
+    assert_equal 'hello', parsed['comments'].first['content']
+    assert_equal 'world', parsed['comments'].second['content']
+  end
+
+  test 'nesting multiple children from array with inline loop on root' do
+    comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
+
+    json = Jbuilder.encode do |json|
+      json.call(comments) do |comment|
+        json.content comment.content
+      end
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'hello', parsed.first['content']
+    assert_equal 'world', parsed.second['content']
+  end
+
+  test 'array nested inside nested hash' do
+    json = Jbuilder.encode 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
+
+    parsed = MultiJson.load(json)
+    assert_equal 'hello', parsed['author']['comments'].first['content']
+    assert_equal 'world', parsed['author']['comments'].second['content']
+  end
+
+  test 'array nested inside array' do
+    json = Jbuilder.encode 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', MultiJson.load(json)['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'] } ]
+    json = Jbuilder.encode do |json|
+      json.array! data do |object|
+        json.department object[:department]
+        json.names do
+          json.array! object[:names]
+        end
+      end
+    end
+
+    assert_equal 'David', MultiJson.load(json)[0]['names'].last
+    assert_not_equal 'hello', MultiJson.load(json)[0]['not_in_json']
+  end
+
+  test 'nested jbuilder objects' do
+    to_nest = Jbuilder.new
+    to_nest.nested_value 'Nested Test'
+    json = Jbuilder.encode do |json|
+      json.value 'Test'
+      json.nested to_nest
+    end
+
+    result = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
+    assert_equal result, MultiJson.load(json)
+  end
+
+  test 'nested jbuilder object via set!' do
+    to_nest = Jbuilder.new
+    to_nest.nested_value 'Nested Test'
+    json = Jbuilder.encode do |json|
+      json.value 'Test'
+      json.set! :nested, to_nest
+    end
+
+    result = {'value' => 'Test', 'nested' => {'nested_value' => 'Nested Test'}}
+    assert_equal result, MultiJson.load(json)
+  end
+
+  test 'top-level array' do
+    comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
+
+    json = Jbuilder.encode do |json|
+      json.array!(comments) do |comment|
+        json.content comment.content
+      end
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'hello', parsed.first['content']
+    assert_equal 'world', parsed.second['content']
+  end
+
+  test 'extract attributes directly from array' do
+    comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
+
+    json = Jbuilder.encode do |json|
+      json.array! comments, :content, :id
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'hello', parsed.first['content']
+    assert_equal       1, parsed.first['id']
+    assert_equal 'world', parsed.second['content']
+    assert_equal       2, parsed.second['id']
+  end
+
+  test 'empty top-level array' do
+    comments = []
+
+    json = Jbuilder.encode do |json|
+      json.array!(comments) do |comment|
+        json.content comment.content
+      end
+    end
+
+    assert_equal [], MultiJson.load(json)
+  end
+
+  test 'dynamically set a key/value' do
+    json = Jbuilder.encode do |json|
+      json.set! :each, 'stuff'
+    end
+
+    assert_equal 'stuff', MultiJson.load(json)['each']
+  end
+
+  test 'dynamically set a key/nested child with block' do
+    json = Jbuilder.encode do |json|
+      json.set!(:author) do
+        json.name 'David'
+        json.age 32
+      end
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal 'David', parsed['author']['name']
+    assert_equal 32, parsed['author']['age']
+  end
+
+  test 'dynamically sets a collection' do
+    comments = [ Comment.new('hello', 1), Comment.new('world', 2) ]
+
+    json = Jbuilder.encode do |json|
+      json.set! :comments, comments, :content
+    end
+
+    parsed = MultiJson.load(json)
+    assert_equal ['content'], parsed['comments'].first.keys
+    assert_equal 'hello', parsed['comments'].first['content']
+    assert_equal 'world', parsed['comments'].second['content']
+  end
+
+  test 'query like object' do
+    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
+
+    result = Jbuilder.encode do |json|
+      json.relations RelationMock.new, :name, :age
+    end
+
+    parsed = MultiJson.load(result)
+    assert_equal 2, parsed['relations'].length
+    assert_equal 'Bob', parsed['relations'][0]['name']
+    assert_equal 50, parsed['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
+    json = Jbuilder.new
+    json.key_format! :camelize => [:lower]
+    json.camel_style 'for JS'
+
+    assert_equal ['camelStyle'], json.attributes!.keys
+  end
+
+  test 'key_format! with parameter not as an array' do
+    json = Jbuilder.new
+    json.key_format! :camelize => :lower
+    json.camel_style 'for JS'
+
+    assert_equal ['camelStyle'], json.attributes!.keys
+  end
+
+  test 'key_format! propagates to child elements' do
+    json = Jbuilder.new
+    json.key_format! :upcase
+    json.level1 'one'
+    json.level2 do
+      json.value 'two'
+    end
+
+    result = json.attributes!
+    assert_equal 'one', result['LEVEL1']
+    assert_equal 'two', result['LEVEL2']['VALUE']
+  end
+
+  test 'key_format! resets after child element' do
+    json = Jbuilder.new
+    json.level2 do
+      json.key_format! :upcase
+      json.value 'two'
+    end
+    json.level1 'one'
+
+    result = json.attributes!
+    assert_equal 'two', result['level2']['VALUE']
+    assert_equal 'one', result['level1']
+  end
+
+  test 'key_format! with no parameter' do
+    json = Jbuilder.new
+    json.key_format! :upcase
+    json.lower 'Value'
+
+    assert_equal ['LOWER'], json.attributes!.keys
+  end
+
+  test 'key_format! with multiple steps' do
+    json = Jbuilder.new
+    json.key_format! :upcase, :pluralize
+    json.pill ''
+
+    assert_equal ['PILLs'], json.attributes!.keys
+  end
+
+  test 'key_format! with lambda/proc' do
+    json = Jbuilder.new
+    json.key_format! lambda { |key| key + ' and friends' }
+    json.oats ''
+
+    assert_equal ['oats and friends'], json.attributes!.keys
+  end
+
+  test 'default key_format!' do
+    Jbuilder.key_format :camelize => :lower
+    json = Jbuilder.new
+    json.camel_style 'for JS'
+
+    assert_equal ['camelStyle'], json.attributes!.keys
+    Jbuilder.send(:class_variable_set, '@@key_formatter', Jbuilder::KeyFormatter.new)
+  end
+
+  test 'do not use default key formatter directly' do
+    json = Jbuilder.new
+    json.key 'value'
+
+    assert_equal [], Jbuilder.send(:class_variable_get, '@@key_formatter').instance_variable_get('@cache').keys
+  end
+
+  test 'ignore_nil! without a parameter' do
+    json = Jbuilder.new
+    json.ignore_nil!
+    json.test nil
+
+    assert_equal [], json.attributes!.keys
+  end
+
+  test 'ignore_nil! with parameter' do
+    json = Jbuilder.new
+    json.ignore_nil! true
+    json.name 'Bob'
+    json.dne nil
+
+    assert_equal ['name'], json.attributes!.keys
+
+    json = Jbuilder.new
+    json.ignore_nil! false
+    json.name 'Bob'
+    json.dne nil
+
+    assert_equal ['name', 'dne'], json.attributes!.keys
+  end
+
+  test 'default ignore_nil!' do
+    Jbuilder.ignore_nil
+    json = Jbuilder.new
+    json.name 'Bob'
+    json.dne nil
+
+    assert_equal ['name'], json.attributes!.keys
+    Jbuilder.send(:class_variable_set, '@@ignore_nil', false)
+  end
+
+  test 'nil!' do
+    json = Jbuilder.new
+    json.key 'value'
+    json.nil!
+    assert_nil json.attributes!
+  end
+
+  test 'null!' do
+    json = Jbuilder.new
+    json.key 'value'
+    json.null!
+    assert_nil json.attributes!
+  end
+
+  test 'throws meaningfull error when on trying to add properties to null' do
+    json = Jbuilder.new
+    json.null!
+    assert_raise(Jbuilder::NullError) { json.foo 'bar' }
+  end
+end
diff --git a/ruby-jbuilder/test/scaffold_controller_generator_test.rb b/ruby-jbuilder/test/scaffold_controller_generator_test.rb
new file mode 100644 (file)
index 0000000..577afb1
--- /dev/null
@@ -0,0 +1,56 @@
+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 action: 'show', status: :created, location: @post \}/, m
+        assert_match /format\.html \{ render action: '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 action: 'show', status: :ok, location: @post \}/, m
+        assert_match /format\.html \{ render action: '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 \}/, 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
\ No newline at end of file