├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── gemfiles ├── activerecord_32.gemfile ├── activerecord_40.gemfile ├── activerecord_41.gemfile ├── activerecord_42.gemfile └── activerecord_edge.gemfile ├── lib ├── to_jbuilder.rb └── to_jbuilder │ ├── emitter.rb │ └── version.rb ├── test ├── fixtures │ ├── json_array.jbuilder │ ├── json_hash.jbuilder │ ├── json_nested_hash.jbuilder │ ├── json_three_nested_hash.jbuilder │ ├── json_with_empty_array.jbuilder │ └── json_with_nested_arrays.jbuilder ├── minitest_helper.rb └── test_to_jbuilder.rb └── to_jbuilder.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | gemfiles/*.lock 5 | /_yardoc/ 6 | /coverage/ 7 | /doc/ 8 | /pkg/ 9 | /spec/reports/ 10 | /tmp/ 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | script: bundle exec rake test 3 | cache: bundler 4 | sudo: false 5 | 6 | before_install: 7 | - gem i bundler -v=1.9.4 8 | 9 | rvm: 10 | - 2.0.0 11 | - 2.1 12 | - 2.2 13 | - ruby-head 14 | - jruby-head 15 | - rbx-2.4.1 16 | - rbx-2.5.2 17 | 18 | gemfile: 19 | - gemfiles/activerecord_32.gemfile 20 | - gemfiles/activerecord_40.gemfile 21 | - gemfiles/activerecord_41.gemfile 22 | - gemfiles/activerecord_42.gemfile 23 | - gemfiles/activerecord_edge.gemfile 24 | 25 | matrix: 26 | fast_finish: true 27 | allow_failures: 28 | - rvm: ruby-head 29 | - rvm: jruby-head 30 | - rvm: rbx-2.4.1 31 | - gemfile: gemfiles/activerecord_32.gemfile 32 | - gemfile: gemfiles/activerecord_edge.gemfile 33 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in to_jbuilder.gemspec 4 | gemspec 5 | 6 | gem 'pry' 7 | gem 'did_you_mean' 8 | 9 | platforms :mri do 10 | gem 'byebug' 11 | gem 'pry-byebug' 12 | end 13 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yuki Nishijima 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ToJbuilder 2 | 3 | Convet any JSON data to jbuilder templates! 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | ```ruby 10 | gem 'to_jbuilder' 11 | ``` 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install to_jbuilder 20 | 21 | ## Usage 22 | 23 | ```ruby 24 | require 'json' 25 | require 'to_jbuilder' 26 | 27 | { 28 | content: "

This is serious monkey business

", 29 | created_at: "2011-10-29T20:45:28-05:00", 30 | updated_at: "2011-10-29T20:45:28-05:00", 31 | 32 | author: { 33 | name: "Yuki Nishijima", 34 | email_address: "'Yuki Nishijima' ", 35 | url: "http://www.yukinishijima.net" 36 | }, 37 | 38 | visitors: 15, 39 | 40 | comments: [ 41 | { content: "Hello everyone!", created_at: "2011-10-29T20:45:28-05:00" }, 42 | { content: "To you my good sir!", created_at: "2011-10-29T20:47:28-05:00" } 43 | ], 44 | 45 | attachments: [ 46 | { filename: "forecast.xls", url: "http://example.com/downloads/forecast.xls" }, 47 | { filename: "presentation.pdf", url: "http://example.com/downloads/presentation.pdf" } 48 | ] 49 | }.to_jbuilder(:message) 50 | ``` 51 | 52 | will generate: 53 | 54 | ```ruby 55 | json.content @message.content 56 | json.created_at @message.created_at 57 | json.updated_at @message.updated_at 58 | 59 | json.author do 60 | json.name @author.name 61 | json.email_address @author.email_address 62 | json.url @author.url 63 | end 64 | 65 | json.visitors @message.visitors 66 | 67 | json.comments @comments do |comment| 68 | json.content comment.content 69 | json.created_at comment.created_at 70 | end 71 | 72 | json.attachments @attachments do |attachment| 73 | json.filename attachment.filename 74 | json.url attachment.url 75 | end 76 | ``` 77 | 78 | ## Contributing 79 | 80 | 1. Fork it ( https://github.com/[my-github-username]/to_jbuilder/fork ) 81 | 2. Create your feature branch (`git checkout -b my-new-feature`) 82 | 3. Commit your changes (`git commit -am 'Add some feature'`) 83 | 4. Push to the branch (`git push origin my-new-feature`) 84 | 5. Create a new Pull Request 85 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | desc "Run tests without re-compiling extensions" 4 | task :test do 5 | sh "bundle exec ruby -Ilib:test test/test_*.rb" 6 | end 7 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "to_jbuilder" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /gemfiles/activerecord_32.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "~> 3.2.0" 6 | 7 | platforms :ruby do 8 | gem "sqlite3" 9 | end 10 | 11 | platforms :jruby do 12 | gem "activerecord-jdbcsqlite3-adapter" 13 | end 14 | 15 | platforms :rbx do 16 | gem "rubysl", "~> 2.0" 17 | gem "racc" 18 | gem "rubinius-developer_tools" 19 | gem "rubysl-yaml", "2.0.4" 20 | end 21 | 22 | gemspec :path => "../" 23 | -------------------------------------------------------------------------------- /gemfiles/activerecord_40.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "~> 4.0.0" 6 | 7 | platforms :ruby do 8 | gem "sqlite3" 9 | end 10 | 11 | platforms :jruby do 12 | gem "activerecord-jdbcsqlite3-adapter" 13 | end 14 | 15 | platforms :rbx do 16 | gem "rubysl", "~> 2.0" 17 | gem "racc" 18 | gem "rubinius-developer_tools" 19 | gem "rubysl-yaml", "2.0.4" 20 | end 21 | 22 | gemspec :path => "../" 23 | -------------------------------------------------------------------------------- /gemfiles/activerecord_41.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "~> 4.1.0" 6 | 7 | platforms :ruby do 8 | gem "sqlite3" 9 | end 10 | 11 | platforms :jruby do 12 | gem "activerecord-jdbcsqlite3-adapter" 13 | end 14 | 15 | platforms :rbx do 16 | gem "rubysl", "~> 2.0" 17 | gem "racc" 18 | gem "rubinius-developer_tools" 19 | gem "rubysl-yaml", "2.0.4" 20 | end 21 | 22 | gemspec :path => "../" 23 | -------------------------------------------------------------------------------- /gemfiles/activerecord_42.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "~> 4.2.0" 6 | 7 | platforms :ruby do 8 | gem "sqlite3" 9 | end 10 | 11 | platforms :jruby do 12 | gem "activerecord-jdbcsqlite3-adapter" 13 | end 14 | 15 | platforms :rbx do 16 | gem "rubysl", "~> 2.0" 17 | gem "racc" 18 | gem "rubinius-developer_tools" 19 | gem "rubysl-yaml", "2.0.4" 20 | end 21 | 22 | gemspec :path => "../" 23 | -------------------------------------------------------------------------------- /gemfiles/activerecord_edge.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | git "git://github.com/rails/rails.git" do 6 | gem "activerecord", :require => "activerecord" 7 | end 8 | 9 | gem "arel", :github => "rails/arel" 10 | 11 | platforms :ruby do 12 | gem "sqlite3" 13 | end 14 | 15 | platforms :jruby do 16 | gem "activerecord-jdbcsqlite3-adapter" 17 | end 18 | 19 | platforms :rbx do 20 | gem "rubysl", "~> 2.0" 21 | gem "racc" 22 | gem "rubinius-developer_tools" 23 | gem "rubysl-yaml", "2.0.4" 24 | end 25 | 26 | gemspec :path => "../" 27 | -------------------------------------------------------------------------------- /lib/to_jbuilder.rb: -------------------------------------------------------------------------------- 1 | require "to_jbuilder/version" 2 | require "to_jbuilder/emitter" 3 | 4 | require "active_support/concern" 5 | require 'active_support/core_ext/string/inflections' 6 | 7 | module ToJbuilder 8 | module CoreExtension 9 | prepend_features Array 10 | prepend_features Hash 11 | 12 | def to_jbuilder(key) 13 | parser = Psych::Parser.new(Psych::JSON::TreeBuilder.new).parse(to_json) 14 | io = StringIO.new(''.encode('utf-8')) 15 | 16 | ToJbuilder::Visitors::Emitter.new(io, key.to_s).accept(parser.handler.root) 17 | io.string.tap(&:strip!) 18 | end 19 | end 20 | 21 | def self.model_names 22 | @model_names ||= if defined?(ActiveRecord::Base) 23 | ActiveRecord::Base.descendants.map(&:to_s).reject{|d| d.include?("HABTM") }.map(&:underscore) 24 | else 25 | [] 26 | end 27 | end 28 | 29 | module TemplateGenerator 30 | extend ActiveSupport::Concern 31 | 32 | included do 33 | after_action do 34 | views_path = Rails.root.join('app/views', params[:controller]).to_s 35 | template_path = Rails.root.join(views_path, params[:action]).to_s + ".json.jbuilder" 36 | 37 | if response.successful? && response.body.present? 38 | json = JSON.parse(response.body) 39 | context = params[:controller].split("/").last 40 | context = context.singularize if params[:action] != "index" 41 | 42 | FileUtils.mkdir_p(views_path) 43 | File.open(template_path, "w") do |file| 44 | file.write json.to_jbuilder(context) 45 | end 46 | end 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/to_jbuilder/emitter.rb: -------------------------------------------------------------------------------- 1 | require 'psych' 2 | 3 | module ToJbuilder 4 | module Visitors 5 | class Emitter < Psych::Visitors::Emitter 6 | def initialize(io, resource_name) 7 | @handler = ToJbuilder::Emitter.new(io, resource_name) 8 | end 9 | end 10 | end 11 | 12 | class Emitter < ::Psych::Emitter 13 | DOCUMENT_START = :document_start 14 | DOCUMENT_END = :document_end 15 | PAIR_KEY = :pair_key 16 | PAIR_VALUE = :pair_value 17 | HASH_START = :hash_start 18 | HASH_END = :hash_end 19 | ARRAY_START = :array_start 20 | ARRAY_END = :array_end 21 | 22 | EMPTY = ' '.freeze 23 | 24 | class JbuilderLine < Struct.new(:object, :key, :value, :prefix, :indent, :line_break) 25 | alias line_break? line_break 26 | 27 | def to_jbuilder(key_offset = 0) 28 | "#{("\n" if line_break?)}#{indent}json.#{key.ljust(key_offset)} #{prefix}#{object}.#{key}\n" 29 | end 30 | 31 | def to_hash_key 32 | "#{("\n" if line_break?)}#{indent}json.#{key} do\n" 33 | end 34 | end 35 | 36 | class JbuilderArray < Struct.new(:line, :key) 37 | def to_jbuilder 38 | if key 39 | "json.array! @#{key} do |#{key.singularize}|\n" 40 | else 41 | "#{("\n" if line.line_break?)}#{line.indent}json.#{line.key} @#{line.key} do |#{line.key.singularize}|\n" 42 | end 43 | end 44 | end 45 | 46 | class Nesting < Struct.new(:name, :type, :sequence_processed) 47 | def sequence_processed? 48 | !! sequence_processed 49 | end 50 | end 51 | 52 | def initialize(io, resource_name) 53 | @io, @resource_name = io, resource_name 54 | 55 | @jbuilder_array = nil 56 | @jbuilder_lines = [] 57 | @nesting = [Nesting.new(@resource_name, :root)] 58 | @trace = [] 59 | end 60 | 61 | def end_stream 62 | # nothing to do... 63 | end 64 | 65 | def start_document(version, tag_directives, implicit) 66 | raise "#{__method__} was called more than once." if @trace.include?(DOCUMENT_START) 67 | @trace << DOCUMENT_START 68 | end 69 | 70 | def end_document(implicit_end) 71 | if @trace.include?(DOCUMENT_END) 72 | raise "#{__method__} was called more than once." 73 | else 74 | @trace << DOCUMENT_END 75 | end 76 | end 77 | 78 | def start_mapping(anchor, tag, implicit, style) 79 | case @trace.last 80 | when ARRAY_START 81 | flush! 82 | 83 | unless @nesting.last.sequence_processed? 84 | @io.write "\n" if @trace[-3] != ARRAY_END && @trace[-3] != HASH_START 85 | @io.write @jbuilder_array.to_jbuilder 86 | end 87 | when PAIR_KEY 88 | line = @jbuilder_lines.pop 89 | flush! 90 | line.line_break = true if @trace[-2] != HASH_START 91 | @io.write line.to_hash_key 92 | @nesting.push Nesting.new(line.key.singularize, HASH_START) 93 | end 94 | 95 | @trace << HASH_START 96 | end 97 | 98 | def end_mapping 99 | flush! 100 | @nesting.last.sequence_processed = true if within_array? && !@nesting.last.sequence_processed? 101 | 102 | if @nesting.size > 1 && !within_array? 103 | @nesting.pop 104 | @io.write "#{indent}end\n" 105 | end 106 | 107 | @trace << HASH_END 108 | end 109 | 110 | def start_sequence(anchor, tag, implicit, style) 111 | case @trace.last 112 | when PAIR_KEY 113 | line = @jbuilder_lines.pop 114 | line.line_break = false if @trace[-2] == HASH_END 115 | 116 | @jbuilder_array = JbuilderArray.new(line) 117 | when DOCUMENT_START 118 | @jbuilder_array = JbuilderArray.new(nil, @nesting.last.name) 119 | end 120 | 121 | @nesting.push Nesting.new((@jbuilder_array.line || @jbuilder_array).key.singularize, ARRAY_START, @nesting.last.sequence_processed) 122 | @trace << ARRAY_START 123 | end 124 | 125 | def end_sequence 126 | case @trace.last 127 | when ARRAY_START 128 | @nesting.pop 129 | @jbuilder_lines << @jbuilder_array.line unless @nesting.last.sequence_processed? 130 | when PAIR_VALUE 131 | @nesting.pop 132 | @jbuilder_lines << @jbuilder_array.line 133 | when HASH_END 134 | @nesting.pop 135 | @io.write "#{indent}end\n" unless @nesting.last.sequence_processed? 136 | end 137 | 138 | @trace << ARRAY_END 139 | end 140 | 141 | def scalar(value, anchor, tag, plain, quoted, style) 142 | return if @nesting.last.sequence_processed? 143 | 144 | case @trace.last 145 | when DOCUMENT_START, HASH_START, HASH_END, ARRAY_END, PAIR_VALUE 146 | line_break = @trace.last == ARRAY_END || @trace.last == HASH_END 147 | prefix = within_array? ? "" : "@" 148 | 149 | @jbuilder_lines << JbuilderLine.new(@nesting.last.name, value, nil, prefix, indent, line_break) 150 | @trace << PAIR_KEY 151 | when ARRAY_START 152 | @nesting.last.sequence_processed = true 153 | 154 | @trace << PAIR_VALUE 155 | when PAIR_KEY 156 | @jbuilder_lines.last.value = value 157 | 158 | @trace << PAIR_VALUE 159 | else 160 | raise "Syntax error." 161 | end 162 | end 163 | 164 | def alias(anchor) 165 | # nothing to do... 166 | end 167 | 168 | private 169 | 170 | def within_array? 171 | @nesting.last.type == ARRAY_START 172 | end 173 | 174 | def indent 175 | EMPTY * (@nesting.size - 1) * 2 176 | end 177 | 178 | def flush! 179 | key_length = @jbuilder_lines.map(&:key).map(&:length).sort.last 180 | 181 | @jbuilder_lines.each do |line| 182 | @io.write line.to_jbuilder(key_length) 183 | end 184 | @jbuilder_lines.clear 185 | end 186 | end 187 | end 188 | -------------------------------------------------------------------------------- /lib/to_jbuilder/version.rb: -------------------------------------------------------------------------------- 1 | module ToJbuilder 2 | VERSION = "0.1.1" 3 | end 4 | -------------------------------------------------------------------------------- /test/fixtures/json_array.jbuilder: -------------------------------------------------------------------------------- 1 | json.array! @users do |user| 2 | json.first_name user.first_name 3 | json.last_name user.last_name 4 | json.email_address user.email_address 5 | json.url user.url 6 | end 7 | -------------------------------------------------------------------------------- /test/fixtures/json_hash.jbuilder: -------------------------------------------------------------------------------- 1 | json.content @message.content 2 | json.created_at @message.created_at 3 | json.updated_at @message.updated_at 4 | 5 | json.author do 6 | json.name @author.name 7 | json.email_address @author.email_address 8 | json.url @author.url 9 | end 10 | 11 | json.visitors @message.visitors 12 | 13 | json.comments @comments do |comment| 14 | json.content comment.content 15 | json.created_at comment.created_at 16 | end 17 | 18 | json.attachments @attachments do |attachment| 19 | json.filename attachment.filename 20 | json.url attachment.url 21 | end 22 | -------------------------------------------------------------------------------- /test/fixtures/json_nested_hash.jbuilder: -------------------------------------------------------------------------------- 1 | json.writer do 2 | json.location do 3 | json.current @location.current 4 | json.hometown @location.hometown 5 | end 6 | 7 | json.name @writer.name 8 | 9 | json.profile do 10 | json.gender @profile.gender 11 | end 12 | end 13 | 14 | json.post do 15 | json.title @post.title 16 | json.description @post.description 17 | json.tags @post.tags 18 | end 19 | -------------------------------------------------------------------------------- /test/fixtures/json_three_nested_hash.jbuilder: -------------------------------------------------------------------------------- 1 | json.albums @albums do |album| 2 | json.id album.id 3 | json.name album.name 4 | 5 | json._links do 6 | json.self do 7 | json.href @self.href 8 | end 9 | end 10 | 11 | json.files @files do |file| 12 | json.id file.id 13 | json.aws_object_key file.aws_object_key 14 | json.name file.name 15 | 16 | json._links do 17 | json.self do 18 | json.href @self.href 19 | end 20 | end 21 | end 22 | end 23 | 24 | json._links do 25 | json.self do 26 | json.href @self.href 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/fixtures/json_with_empty_array.jbuilder: -------------------------------------------------------------------------------- 1 | json.name @user.name 2 | json.tags @user.tags 3 | 4 | json.email_address @user.email_address 5 | -------------------------------------------------------------------------------- /test/fixtures/json_with_nested_arrays.jbuilder: -------------------------------------------------------------------------------- 1 | json.array! @user do |user| 2 | json.tags @tags do |tag| 3 | json.foo tag.foo 4 | json.hoge tag.hoge 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/minitest_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'to_jbuilder' 3 | 4 | begin 5 | require 'byebug' 6 | require 'did_you_mean' 7 | rescue LoadError 8 | end 9 | 10 | require 'minitest/autorun' 11 | 12 | begin 13 | MiniTest::Test 14 | rescue NameError 15 | require 'minitest/unit' 16 | MiniTest::Test = MiniTest::Unit::TestCase 17 | end 18 | -------------------------------------------------------------------------------- /test/test_to_jbuilder.rb: -------------------------------------------------------------------------------- 1 | require 'minitest_helper' 2 | require 'json' 3 | 4 | class TestToJbuilder < Minitest::Test 5 | def test_to_jbuilder_converts_a_json_hash_into_jbuilder 6 | actual = JSON.parse(<<-JSON_DATA).to_jbuilder(:message) 7 | { 8 | "content": "

This is serious monkey business

", 9 | "created_at": "2011-10-29T20:45:28-05:00", 10 | "updated_at": "2011-10-29T20:45:28-05:00", 11 | 12 | "author": { 13 | "name": "Yuki Nishijima", 14 | "email_address": "'Yuki Nishijima' ", 15 | "url": "http://www.yukinishijima.net" 16 | }, 17 | 18 | "visitors": 15, 19 | 20 | "comments": [ 21 | { "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" }, 22 | { "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" } 23 | ], 24 | 25 | "attachments": [ 26 | { "filename": "forecast.xls", "url": "http://example.com/downloads/forecast.xls" }, 27 | { "filename": "presentation.pdf", "url": "http://example.com/downloads/presentation.pdf" } 28 | ] 29 | } 30 | JSON_DATA 31 | 32 | assert_equal fixture('json_hash'), actual 33 | end 34 | 35 | def test_to_jbuilder_converts_a_json_array_into_jbuilder 36 | actual = JSON.parse(<<-JSON_DATA).to_jbuilder(:users) 37 | [ 38 | { 39 | "first_name": "Yuki", 40 | "last_name": "Nishijima", 41 | "email_address": "'Yuki Nishijima' ", 42 | "url": "http://www.yukinishijima.net" 43 | } 44 | ] 45 | JSON_DATA 46 | 47 | assert_equal fixture('json_array'), actual 48 | end 49 | 50 | 51 | def test_to_jbuilder_converts_a_nested_json_hash_into_jbuilder 52 | actual = JSON.parse(<<-JSON_DATA).to_jbuilder(:users) 53 | { 54 | "writer": { 55 | "location": { 56 | "current": "New York, US", 57 | "hometown": "Tokyo, Japan" 58 | }, 59 | "name": "Yuki Nishijima", 60 | "profile": { 61 | "gender": "male" 62 | } 63 | }, 64 | "post": { 65 | "title": "How does it work?", 66 | "description": "I don't know", 67 | "tags": ["Ruby", "JSON"] 68 | } 69 | } 70 | JSON_DATA 71 | 72 | assert_equal fixture('json_nested_hash'), actual 73 | end 74 | 75 | def test_to_jbuilder_with_three_nested_hash 76 | actual = JSON.parse(<<-JSON_DATA).to_jbuilder(:file_groups) 77 | { 78 | "albums": [ 79 | { 80 | "id": 85, 81 | "name": "My profile photos", 82 | "_links": { 83 | "self": { 84 | "href": "http://example.com/downloads/85" 85 | } 86 | }, 87 | "files": [ 88 | { 89 | "id": 181, 90 | "aws_object_key": "downloads/yuki24.jpg", 91 | "name": "yuki24.jpg", 92 | "_links": { 93 | "self": { 94 | "href": "http://example.com/downloads/85/yuki24.jpg" 95 | } 96 | } 97 | } 98 | ] 99 | } 100 | ], 101 | "_links": { 102 | "self": { 103 | "href": "http://example.com/downloads" 104 | } 105 | } 106 | } 107 | JSON_DATA 108 | 109 | assert_equal fixture('json_three_nested_hash'), actual 110 | end 111 | 112 | def test_to_jbuilder_with_empty_array 113 | actual = JSON.parse(<<-JSON_DATA).to_jbuilder(:user) 114 | { 115 | "name": "Yuki Nishijima", 116 | "tags": [ ], 117 | "email_address": "'Yuki Nishijima' " 118 | } 119 | JSON_DATA 120 | 121 | assert_equal fixture('json_with_empty_array'), actual 122 | end 123 | 124 | def test_to_jbuilder_with_nested_arrays 125 | actual = JSON.parse(<<-JSON_DATA).to_jbuilder(:user) 126 | [ 127 | { 128 | "tags": [ 129 | {"foo": "bar", "hoge": "fuga"}, 130 | {"foo": "bar", "hoge": "fuga"} 131 | ] 132 | }, 133 | { 134 | "tags": [ 135 | {"foo": "bar", "hoge": "fuga"}, 136 | {"foo": "bar", "hoge": "fuga"} 137 | ] 138 | } 139 | ] 140 | JSON_DATA 141 | 142 | assert_equal fixture('json_with_nested_arrays'), actual 143 | end 144 | 145 | private 146 | 147 | def fixture(path) 148 | open("#{__dir__}/fixtures/#{path}.jbuilder").read.strip 149 | end 150 | end 151 | -------------------------------------------------------------------------------- /to_jbuilder.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'to_jbuilder/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "to_jbuilder" 8 | spec.version = ToJbuilder::VERSION 9 | spec.authors = ["Yuki Nishijima"] 10 | spec.email = ["mail@yukinishijima.net"] 11 | 12 | spec.summary = %q{Convert JSON to jbuilder templates} 13 | spec.description = %q{Easily convert Ruby array/hash by just calling the #to_jbuilder method.} 14 | spec.homepage = "https://github.com/yuki24/to_jbuilder" 15 | spec.license = "MIT" 16 | 17 | # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or 18 | # delete this section to allow pushing this gem to any host. 19 | if spec.respond_to?(:metadata) 20 | spec.metadata['allowed_push_host'] = "https://rubygems.org" 21 | else 22 | raise "RubyGems 2.0 or newer is required to protect against public gem pushes." 23 | end 24 | 25 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 26 | spec.bindir = "exe" 27 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 28 | spec.require_paths = ["lib"] 29 | 30 | spec.add_dependency "activesupport", ">= 3.0.0" 31 | 32 | spec.add_development_dependency "bundler", "~> 1.9" 33 | spec.add_development_dependency "rake", "~> 10.0" 34 | end 35 | --------------------------------------------------------------------------------