From 280f3d0ea4ca5f1a2e1ae9b439bd5e89895fe484 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Wed, 12 Mar 2014 00:17:07 +0100 Subject: [PATCH] jbuilder - initial gem2deb package. --- ruby-jbuilder/.gitignore | 4 + ruby-jbuilder/.travis.yml | 23 + ruby-jbuilder/CHANGELOG.md | 85 ++++ ruby-jbuilder/Gemfile | 15 + ruby-jbuilder/Gemfile.old | 14 + ruby-jbuilder/MIT-LICENSE | 20 + ruby-jbuilder/README.md | 216 ++++++++ ruby-jbuilder/Rakefile | 14 + ruby-jbuilder/checksums.yaml.gz | Bin 0 -> 269 bytes ruby-jbuilder/debian/changelog | 5 + ruby-jbuilder/debian/compat | 1 + ruby-jbuilder/debian/control | 19 + ruby-jbuilder/debian/copyright | 35 ++ ruby-jbuilder/debian/ruby-jbuilder.docs | 2 + ruby-jbuilder/debian/ruby-test-files.yaml | 6 + ruby-jbuilder/debian/rules | 15 + ruby-jbuilder/debian/source/format | 1 + ruby-jbuilder/debian/watch | 2 + ruby-jbuilder/jbuilder.gemspec | 17 + .../lib/generators/rails/jbuilder_generator.rb | 48 ++ .../rails/scaffold_controller_generator.rb | 12 + .../lib/generators/rails/templates/controller.rb | 84 ++++ .../generators/rails/templates/index.json.jbuilder | 4 + .../generators/rails/templates/show.json.jbuilder | 1 + ruby-jbuilder/lib/jbuilder.rb | 332 +++++++++++++ ruby-jbuilder/lib/jbuilder/dependency_tracker.rb | 61 +++ ruby-jbuilder/lib/jbuilder/jbuilder_template.rb | 132 +++++ ruby-jbuilder/lib/jbuilder/railtie.rb | 11 + ruby-jbuilder/metadata.yml | 99 ++++ .../test/jbuilder_dependency_tracker_test.rb | 73 +++ ruby-jbuilder/test/jbuilder_generator_test.rb | 31 ++ ruby-jbuilder/test/jbuilder_template_test.rb | 292 +++++++++++ ruby-jbuilder/test/jbuilder_test.rb | 545 +++++++++++++++++++++ .../test/scaffold_controller_generator_test.rb | 56 +++ 34 files changed, 2275 insertions(+) create mode 100644 ruby-jbuilder/.gitignore create mode 100644 ruby-jbuilder/.travis.yml create mode 100644 ruby-jbuilder/CHANGELOG.md create mode 100644 ruby-jbuilder/Gemfile create mode 100644 ruby-jbuilder/Gemfile.old create mode 100644 ruby-jbuilder/MIT-LICENSE create mode 100644 ruby-jbuilder/README.md create mode 100644 ruby-jbuilder/Rakefile create mode 100644 ruby-jbuilder/checksums.yaml.gz create mode 100644 ruby-jbuilder/debian/changelog create mode 100644 ruby-jbuilder/debian/compat create mode 100644 ruby-jbuilder/debian/control create mode 100644 ruby-jbuilder/debian/copyright create mode 100644 ruby-jbuilder/debian/ruby-jbuilder.docs create mode 100644 ruby-jbuilder/debian/ruby-test-files.yaml create mode 100755 ruby-jbuilder/debian/rules create mode 100644 ruby-jbuilder/debian/source/format create mode 100644 ruby-jbuilder/debian/watch create mode 100644 ruby-jbuilder/jbuilder.gemspec create mode 100644 ruby-jbuilder/lib/generators/rails/jbuilder_generator.rb create mode 100644 ruby-jbuilder/lib/generators/rails/scaffold_controller_generator.rb create mode 100644 ruby-jbuilder/lib/generators/rails/templates/controller.rb create mode 100644 ruby-jbuilder/lib/generators/rails/templates/index.json.jbuilder create mode 100644 ruby-jbuilder/lib/generators/rails/templates/show.json.jbuilder create mode 100644 ruby-jbuilder/lib/jbuilder.rb create mode 100644 ruby-jbuilder/lib/jbuilder/dependency_tracker.rb create mode 100644 ruby-jbuilder/lib/jbuilder/jbuilder_template.rb create mode 100644 ruby-jbuilder/lib/jbuilder/railtie.rb create mode 100644 ruby-jbuilder/metadata.yml create mode 100644 ruby-jbuilder/test/jbuilder_dependency_tracker_test.rb create mode 100644 ruby-jbuilder/test/jbuilder_generator_test.rb create mode 100644 ruby-jbuilder/test/jbuilder_template_test.rb create mode 100644 ruby-jbuilder/test/jbuilder_test.rb create mode 100644 ruby-jbuilder/test/scaffold_controller_generator_test.rb diff --git a/ruby-jbuilder/.gitignore b/ruby-jbuilder/.gitignore new file mode 100644 index 0000000..acea2d2 --- /dev/null +++ b/ruby-jbuilder/.gitignore @@ -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 index 0000000..8f4fb50 --- /dev/null +++ b/ruby-jbuilder/.travis.yml @@ -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 index 0000000..a29274e --- /dev/null +++ b/ruby-jbuilder/CHANGELOG.md @@ -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 index 0000000..4bc536b --- /dev/null +++ b/ruby-jbuilder/Gemfile @@ -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 index 0000000..4c5903e --- /dev/null +++ b/ruby-jbuilder/Gemfile.old @@ -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 index 0000000..46206da --- /dev/null +++ b/ruby-jbuilder/MIT-LICENSE @@ -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 index 0000000..d3fc9cd --- /dev/null +++ b/ruby-jbuilder/README.md @@ -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": "

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 +# @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 index 0000000..04bf196 --- /dev/null +++ b/ruby-jbuilder/Rakefile @@ -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 index 0000000000000000000000000000000000000000..9e4b24ee605d3e3ee4683446b50d33af9cf8e026 GIT binary patch literal 269 zcmV+o0rLJIiwFQT$+$8|F?(V9K7x|eK+X^P%RB_jctN9pppT_-{L z=wV6(exeN~TO^b%YGgndbQ*vN@B-qQgK!-#^E~dFD>%JOGkOph)!#nrWT}|g>Fmcb zXq8iNXye4OS}A2R>UI?MVQe&wi?#I$L0`+2%>ks2Sq5tDExx@xB;u^n=LdxrTbny5 Txg!>M)6mNgH*^^Oq5%K^4M%%B literal 0 HcmV?d00001 diff --git a/ruby-jbuilder/debian/changelog b/ruby-jbuilder/debian/changelog new file mode 100644 index 0000000..46bf840 --- /dev/null +++ b/ruby-jbuilder/debian/changelog @@ -0,0 +1,5 @@ +ruby-jbuilder (2.0.4-1) UNRELEASED; urgency=medium + + * Initial release (Closes: #nnnn) + + -- MAINTAINER Wed, 12 Mar 2014 00:15:53 +0100 diff --git a/ruby-jbuilder/debian/compat b/ruby-jbuilder/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/ruby-jbuilder/debian/compat @@ -0,0 +1 @@ +7 diff --git a/ruby-jbuilder/debian/control b/ruby-jbuilder/debian/control new file mode 100644 index 0000000..68ef9b1 --- /dev/null +++ b/ruby-jbuilder/debian/control @@ -0,0 +1,19 @@ +Source: ruby-jbuilder +Section: ruby +Priority: optional +Maintainer: Debian Ruby Extras Maintainers +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 + diff --git a/ruby-jbuilder/debian/copyright b/ruby-jbuilder/debian/copyright new file mode 100644 index 0000000..7f14407 --- /dev/null +++ b/ruby-jbuilder/debian/copyright @@ -0,0 +1,35 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: jbuilder +Source: FIXME + +Files: * +Copyright: + +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 index 0000000..07b3c9e --- /dev/null +++ b/ruby-jbuilder/debian/ruby-jbuilder.docs @@ -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 index 0000000..17b1972 --- /dev/null +++ b/ruby-jbuilder/debian/ruby-test-files.yaml @@ -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 index 0000000..82ddc0c --- /dev/null +++ b/ruby-jbuilder/debian/rules @@ -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 index 0000000..163aaf8 --- /dev/null +++ b/ruby-jbuilder/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/ruby-jbuilder/debian/watch b/ruby-jbuilder/debian/watch new file mode 100644 index 0000000..41a5e95 --- /dev/null +++ b/ruby-jbuilder/debian/watch @@ -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 index 0000000..1d7bc66 --- /dev/null +++ b/ruby-jbuilder/jbuilder.gemspec @@ -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 index 0000000..22058a6 --- /dev/null +++ b/ruby-jbuilder/lib/generators/rails/jbuilder_generator.rb @@ -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 index 0000000..f6ce97a --- /dev/null +++ b/ruby-jbuilder/lib/generators/rails/scaffold_controller_generator.rb @@ -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 index 0000000..c6f78e6 --- /dev/null +++ b/ruby-jbuilder/lib/generators/rails/templates/controller.rb @@ -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 index 0000000..063780e --- /dev/null +++ b/ruby-jbuilder/lib/generators/rails/templates/index.json.jbuilder @@ -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 index 0000000..46dd412 --- /dev/null +++ b/ruby-jbuilder/lib/generators/rails/templates/show.json.jbuilder @@ -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 index 0000000..6db3584 --- /dev/null +++ b/ruby-jbuilder/lib/jbuilder.rb @@ -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 index 0000000..c937dff --- /dev/null +++ b/ruby-jbuilder/lib/jbuilder/dependency_tracker.rb @@ -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 index 0000000..6ec4ac8 --- /dev/null +++ b/ruby-jbuilder/lib/jbuilder/jbuilder_template.rb @@ -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 index 0000000..28a428f --- /dev/null +++ b/ruby-jbuilder/lib/jbuilder/railtie.rb @@ -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 index 0000000..30b5f57 --- /dev/null +++ b/ruby-jbuilder/metadata.yml @@ -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 index 0000000..51dfa30 --- /dev/null +++ b/ruby-jbuilder/test/jbuilder_dependency_tracker_test.rb @@ -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 index 0000000..64564ea --- /dev/null +++ b/ruby-jbuilder/test/jbuilder_generator_test.rb @@ -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 index 0000000..706e8a0 --- /dev/null +++ b/ruby-jbuilder/test/jbuilder_template_test.rb @@ -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 index 0000000..12c90e5 --- /dev/null +++ b/ruby-jbuilder/test/jbuilder_test.rb @@ -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 index 0000000..577afb1 --- /dev/null +++ b/ruby-jbuilder/test/scaffold_controller_generator_test.rb @@ -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 -- 1.8.2.3