Giter VIP home page Giter VIP logo

jb's Introduction

Jb

A simpler and faster Jbuilder alternative.

Installation

Add this line to your application's Gemfile:

gem 'jb'

And bundle.

Usage

Put a template file named *.jb in your Rails app's app/views/* directory, and render it.

Features

  • No original builder syntax that you have to learn
  • No method_missing calls
  • render_partial with :collection option actually renders the collection (unlike Jbuilder)

Syntax

A .jb template should contain Ruby code that returns any Ruby Object that responds_to to_json (generally Hash or Array). Then the return value will be to_jsoned to a JSON String.

Examples

Let's start with a very simple one. Just write a Ruby Hash as a template:

{language: 'Ruby', author: {name: 'Matz'}}

This renders the following JSON text:

{"language": "Ruby", "author": {"name": "Matz"}}

Note that modern Ruby Hash syntax pretty much looks alike JSON syntax. It's super-straight forward. Who needs a DSL to do this?

Next one is a little bit advanced usage. The template doesn't have to be a single literal but can be any code that returns a Hash object:

# app/views/messages/show.json.jb

json = {
  content: format_content(@message.content),
  created_at: @message.created_at,
  updated_at: @message.updated_at,
  author: {
    name: @message.creator.name.familiar,
    email_address: @message.creator.email_address_with_name,
    url: url_for(@message.creator, format: :json)
  }
}

if current_user.admin?
  json[:visitors] = calculate_visitors(@message)
end

json[:comments] = @message.comments.map do |comment|
  {
    content: comment.content,
    created_at: comment.created_at
  }
end

json[:attachments] = @message.attachments.map do |attachment|
  {
    filename: attachment.filename,
    url: url_for(attachment)
  }
end

json

This will build the following structure:

{
  "content": "10x JSON",
  "created_at": "2016-06-29T20:45:28-05:00",
  "updated_at": "2016-06-29T20:45:28-05:00",

  "author": {
    "name": "Yukihiro Matz",
    "email_address": "[email protected]",
    "url": "http://example.com/users/1-matz.json"
  },

  "visitors": 1326,

  "comments": [
    { "content": "Hello, world!", "created_at": "2016-06-29T20:45:28-05:00" },
    { "content": "<script>alert('Hello, world!');</script>", "created_at": "2016-06-29T20:47:28-05:00" }
  ],

  "attachments": [
    { "filename": "sushi.png", "url": "http://example.com/downloads/sushi.png" },
    { "filename": "sake.jpg", "url": "http://example.com/downloads/sake.jpg" }
  ]
}

If you want to define attribute and structure names dynamically, of course you still can do this with a Ruby Hash literal.

# model_name, column_name = :author, :name

{model_name => {column_name => 'Matz'}}

# => {"author": {"name": "Matz"}}

Top level arrays can be handled directly. Useful for index and other collection actions. And you know, Ruby is such a powerful language for manipulating collections:

# @comments = @post.comments

@comments.reject {|c| c.marked_as_spam_by?(current_user) }.map do |comment|
  {
    body: comment.body,
    author: {
      first_name: comment.author.first_name,
      last_name: comment.author.last_name
    }
  }
end

# => [{"body": "🍣 is omakase...", "author": {"first_name": "Yukihiro", "last_name": "Matz"}}]

Jb has no special DSL method for extracting attributes from array directly, but you can do that with Ruby.

# @people = People.all

@people.map {|p| {id: p.id, name: p.name}}

# => [{"id": 1, "name": "Matz"}, {"id": 2, "name": "Nobu"}]

You can use Jb directly as an Action View template language. When required in Rails, you can create views ala show.json.jb. You'll notice in the following example that the .jb template doesn't have to be one big Ruby Hash literal as a whole but it can be any Ruby code that finally returns a Hash instance.

# Any helpers available to views are available in the template
json = {
  content: format_content(@message.content),
  created_at: @message.created_at,
  updated_at: @message.updated_at,

  author: {
    name: @message.creator.name.familiar,
    email_address: @message.creator.email_address_with_name,
    url: url_for(@message.creator, format: :json)
  }
}

if current_user.admin?
  json[:visitors] = calculate_visitors(@message)
end

json

You can use partials as well. The following will render the file views/comments/_comments.json.jb, and set a local variable comments with all this message's comments, which you can use inside the partial.

render 'comments/comments', comments: @message.comments

It's also possible to render collections of partials:

render partial: 'posts/post', collection: @posts, as: :post

NOTE: Don't use render @post.comments because if the collection is empty, render will return nil instead of an empty array.

You can pass any objects into partial templates with or without :locals option.

render 'sub_template', locals: {user: user}

# or

render 'sub_template', user: user

You can of course include Ruby nil as a Hash value if you want. That would become null in the JSON.

You can use Hash#compact/! method to prevent including null values in the output:

{foo: nil, bar: 'bar'}.compact

# => {"bar": "bar"}

If you want to cache a template fragment, just directly call Rails.cache.fetch:

Rails.cache.fetch ['v1', @person], expires_in: 10.minutes do
  {name: @person.name, age: @person.age}
end

The Generator

Jb extends the default Rails scaffold generator and adds some .jb templates. If you don't need them, please configure like so.

Rails.application.config.generators.jb false

Why is Jb fast?

Jbuilder's partial + :collection internally calls array! method inside which _render_partial is called per each element of the given collection, and then it falls back to the view_context's render method.

So, for example if the collection has 100 elements, Jbuilder's render partial: performs render method 100 times, and so it calls find_template method (which is known as one of the heaviest parts of Action View) 100 times.

OTOH, Jb simply calls ActionView::PartialRenderer's render which is cleverly implemented to find_template only once beforehand, then pass each element to that template.

Benchmarks

Here're the results of a benchmark (which you can find here in this repo) rendering a collection to JSON.

RAILS_ENV=development

% ./bin/benchmark.sh
* Rendering 10 partials via render_partial
Warming up --------------------------------------
                  jb    15.000  i/100ms
            jbuilder     8.000  i/100ms
Calculating -------------------------------------
                  jb    156.375  (± 7.0%) i/s -    780.000  in   5.016581s
            jbuilder     87.890  (± 6.8%) i/s -    440.000  in   5.037225s

Comparison:
                  jb:      156.4 i/s
            jbuilder:       87.9 i/s - 1.78x slower


* Rendering 100 partials via render_partial
Warming up --------------------------------------
                  jb    13.000  i/100ms
            jbuilder     1.000  i/100ms
Calculating -------------------------------------
                  jb    121.187  (±14.0%) i/s -    598.000  in   5.049667s
            jbuilder     11.478  (±26.1%) i/s -     54.000  in   5.061996s

Comparison:
                  jb:      121.2 i/s
            jbuilder:       11.5 i/s - 10.56x slower


* Rendering 1000 partials via render_partial
Warming up --------------------------------------
                  jb     4.000  i/100ms
            jbuilder     1.000  i/100ms
Calculating -------------------------------------
                  jb     51.472  (± 7.8%) i/s -    256.000  in   5.006584s
            jbuilder      1.510  (± 0.0%) i/s -      8.000  in   5.383548s

Comparison:
                  jb:       51.5 i/s
            jbuilder:        1.5 i/s - 34.08x slower

RAILS_ENV=production

% RAILS_ENV=production ./bin/benchmark.sh
* Rendering 10 partials via render_partial
Warming up --------------------------------------
                  jb   123.000  i/100ms
            jbuilder    41.000  i/100ms
Calculating -------------------------------------
                  jb      1.406k (± 4.2%) i/s -      7.134k in   5.084030s
            jbuilder    418.360  (± 9.8%) i/s -      2.091k in   5.043381s

Comparison:
                  jb:     1405.8 i/s
            jbuilder:      418.4 i/s - 3.36x slower


* Rendering 100 partials via render_partial
Warming up --------------------------------------
                  jb    37.000  i/100ms
            jbuilder     5.000  i/100ms
Calculating -------------------------------------
                  jb    383.082  (± 8.4%) i/s -      1.924k in   5.061973s
            jbuilder     49.914  (± 8.0%) i/s -    250.000  in   5.040364s

Comparison:
                  jb:      383.1 i/s
            jbuilder:       49.9 i/s - 7.67x slower


* Rendering 1000 partials via render_partial
Warming up --------------------------------------
                  jb     4.000  i/100ms
            jbuilder     1.000  i/100ms
Calculating -------------------------------------
                  jb     43.017  (± 9.3%) i/s -    216.000  in   5.080482s
            jbuilder      4.604  (±21.7%) i/s -     23.000  in   5.082100s

Comparison:
                  jb:       43.0 i/s
            jbuilder:        4.6 i/s - 9.34x slower

Summary

According to the benchmark results, you can expect 2-30x performance improvement in development env, and 3-10x performance improvement in production env.

Contributing

Pull requests are welcome on GitHub at https://github.com/amatsuda/jb.

License

The gem is available as open source under the terms of the MIT License.

jb's People

Contributors

amatsuda avatar bogdanvlviv avatar cohei avatar dzhikvas avatar ken3ypa avatar luikore avatar sebyx07 avatar takkanm avatar ulissesalmeida avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jb's Issues

How to resolve the template digest

Curently I have a template that has the following

Rails.cache.fetch( ['v1', assignment] ) do
{
  id: assignment.id,
  description: assignment.description,
  start: assignment.start,
  end: assignment.end
}
end

And it caches out nicely.

But when I edit the template contents, say add a new item there, the cache does not invalidate. I know that ActionView::Helpers::CacheHelpers has functionality for this, but how to take advantage of it?

Any way to automatically camelCase all keys?

Hello! In ruby we use snake_case everywhere and I suffer when I have to camelCase in any place.
Is there any option to camelKeys automatically all keys right before spitting out the JSON?

I would use it so that my JSON api stays consistent with javascript notation

Provide a better way for caching

Other solution like JBuilder and RABL has some caching function built-in

And they do make the caching key different so that it won't collide so easily

got ActionView::SyntaxErrorInTemplate error

# Gemfile
gem 'jb', '~> 0.7.1'
ActionView::SyntaxErrorInTemplate (Encountered a syntax error while rendering template: check
16:58:04 backend.1  | @posts.map do |post|
16:58:04 backend.1  |   {
16:58:04 backend.1  |     title: post.title
16:58:04 backend.1  |   }
16:58:04 backend.1  | end

16:58:04 backend.1  | app/views/api/posts/latest.json.jb:5: syntax error, unexpected tIDENTIFIER, expecting '}'
16:58:04 backend.1  | app/views/api/posts/latest.json.jb:6: syntax error, unexpected ':', expecting end
16:58:04 backend.1  | app/views/api/posts/latest.json.jb:7: syntax error, unexpected ':', expecting end
16:58:04 backend.1  | app/views/api/posts/latest.json.jb:8: syntax error, unexpected ':', expecting end

rails: 6.0.2.2
ruby: 2.6.6

TypeError: singleton can't be dumped

Just wanted to say that I really enjoy the library. Thank you for all of the work that you have put in!

I am attempting to render an object to JSON using your gem and Rails 7.1. I am using Rails.cache.fetch in the jb file and am getting the error below.

A few interesting things. If I disable caching it works (that isn't feasible though). If I remove rendering additional partials it also works. This leads me to believe it has something to do with the call to render(partial: …).

Let me how I can help or if you need additional information.

# ./app/views/showcase_widget/api/showcase_projects/index.json.jb:3:in `_app_views_showcase_widget_api_showcase_projects_index_json_jb__984211662139926002_220520'
      # /bundle/ruby/3.2.0/gems/jb-0.8.1/lib/jb/action_view_monkeys.rb:19:in `_run'
      # /bundle/ruby/3.2.0/gems/jb-0.8.1/lib/jb/action_view_monkeys.rb:9:in `render_template'
      # /bundle/ruby/3.2.0/gems/benchmark-0.2.0/lib/benchmark.rb:311:in `realtime'
      # /bundle/ruby/3.2.0/gems/searchkick-5.3.0/lib/searchkick/controller_runtime.rb:20:in `cleanup_view_runtime'
      # /bundle/ruby/3.2.0/gems/mongoid-8.1.3/lib/mongoid/railties/controller_runtime.rb:27:in `cleanup_view_runtime'
      # /bundle/ruby/3.2.0/gems/wicked_pdf-2.6.3/lib/wicked_pdf/pdf_helper.rb:18:in `render'
      # /bundle/ruby/3.2.0/gems/actiontext-7.1.1/lib/action_text/rendering.rb:23:in `with_renderer'
      # /bundle/ruby/3.2.0/gems/actiontext-7.1.1/lib/action_text/engine.rb:69:in `block (4 levels) in <class:Engine>'
      # /bundle/ruby/3.2.0/gems/marginalia-1.11.1/lib/marginalia.rb:109:in `record_query_comment'
      # /bundle/ruby/3.2.0/gems/searchkick-5.3.0/lib/searchkick/controller_runtime.rb:15:in `process_action'
      # /bundle/ruby/3.2.0/gems/mongoid-8.1.3/lib/mongoid/railties/controller_runtime.rb:21:in `process_action'
      # /bundle/ruby/3.2.0/gems/rails-controller-testing-1.0.5/lib/rails/controller/testing/template_assertions.rb:62:in `process'
      # /bundle/ruby/3.2.0/gems/devise-4.9.0/lib/devise/test/controller_helpers.rb:35:in `block in process'
      # /bundle/ruby/3.2.0/gems/devise-4.9.0/lib/devise/test/controller_helpers.rb:104:in `catch'
      # /bundle/ruby/3.2.0/gems/devise-4.9.0/lib/devise/test/controller_helpers.rb:104:in `_catch_warden'
      # /bundle/ruby/3.2.0/gems/devise-4.9.0/lib/devise/test/controller_helpers.rb:35:in `process'
      # /bundle/ruby/3.2.0/gems/gon-6.4.0/lib/gon/spec_helpers.rb:15:in `process'
      # /bundle/ruby/3.2.0/gems/rails-controller-testing-1.0.5/lib/rails/controller/testing/integration.rb:16:in `block (2 levels) in <module:Integration>'
      # ./spec/controllers/showcase_widget/api/showcase_projects_controller_spec.rb:186:in `perform_get_request'
      # ./spec/controllers/showcase_widget/api/showcase_projects_controller_spec.rb:27:in `block (5 levels) in <top (required)>'
      # ./spec/spec_helper.rb:92:in `block (3 levels) in <top (required)>'
      # ./spec/spec_helper.rb:91:in `block (2 levels) in <top (required)>'
      # /bundle/ruby/3.2.0/gems/webmock-3.19.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
      # ------------------
      # --- Caused by: ---
      # TypeError:
      #   singleton can't be dumped
      #   ./app/views/showcase_widget/api/showcase_projects/index.json.jb:3:in _app_views_showcase_widget_api_showcase_projects_index_json_jb__984211662139926002_220520'

My Views

# index.json.jb
Rails.cache.fetch ["showcase-projects", @showcase, @showcase_params_cache_key] do
  json = {
    "totalCount": @total_count,
    "filteredCount": @filtered_count
  }

  json[:showcaseprojects] = render(partial: 'showcase_widget/api/showcase_projects/showcase_project', collection: @showcase_projects, as: :showcase_project)

  json
end
# showcase_project.json.jb

Rails.cache.fetch ["showcase-project", showcase_project] do
  json = {
    id: showcase_project.id.to_s,
    title: showcase_project.title,
    description: showcase_project.description,
    city: showcase_project.project.city,
    state: showcase_project.project.state,
    zipcode: showcase_project.project.postal_code,
    'lastUpdated': showcase_project.updated_at,
    'createdAt': showcase_project.created_at,
    'featuredPhoto': showcase_project.featured_image.url_medium,
    'materials': showcase_project.materials_used,
    'types': showcase_project.project_types,
    'mediaCount': showcase_project.showcase_project_media_count
  }

  json[:media] = render(
    partial: 'showcase_widget/api/showcase_projects/media',
    collection: showcase_project.showcase_project_media.visible.sort_by_captured_at.limit(4),
    as: :showcase_project_media
  )

  json
end
# media.json.jb
Rails.cache.fetch ["showcase-project-media", showcase_project_media] do
  {
    id: showcase_project_media.id.to_s,
    url: showcase_project_media.media.url_large,
  }
end

Updates

I attempted to inline rendering the entire object (removed calls to render(partial: …) and it works. Something about dumping the partial seems to be what is messing it up.

This works

Rails.cache.fetch ["showcase-projects", @showcase, @showcase_params_cache_key] do
  json = {
    "totalCount": @total_count,
    "filteredCount": @filtered_count
  }

  json[:showcaseprojects] = @showcase_projects.map do |showcase_project|
    project_json = {
      id: showcase_project.id.to_s,
      title: showcase_project.title,
      description: showcase_project.description,
      city: showcase_project.project.city,
      state: showcase_project.project.state,
      zipcode: showcase_project.project.postal_code,
      'lastUpdated': showcase_project.updated_at,
      'createdAt': showcase_project.created_at,
      'featuredPhoto': showcase_project.featured_image.url_medium,
      'materials': showcase_project.materials_used,
      'types': showcase_project.project_types,
      'mediaCount': showcase_project.showcase_project_media_count
    }

    project_json[:media] = showcase_project.showcase_project_media.visible.sort_by_captured_at.limit(4).map do |media|
      {
        id: media.id.to_s,
        url: media.media.url_large,
      }
    end

    project_json
  end

  json
end

Partial syntax is fussy

Can you tell me why this works:

render "comments/comment_hide_delete.json.jb"

but this doesn't

render "comments/comment_hide_delete"

and neither does this?

render partial: "comments/comment_hide_delete.json.jb"

jb renders an empty string in rspec controller test in both Rails4 and Rails5

It sometimes renders an empty string with status 200, or sometimes just raises 404. The app below reproduces the former, though.

The demo is based on Rails5 but I have found this problem on my Rails4 app.

How to reproduce it

https://github.com/gfx/test_jb (Rails5)

  1. git clone the repo
  2. bundle install
  3. bundle exec rspec

will shows:

$ bundle exec rspec
F

Failures:

  1) FoosController#index should eq "[]"
     Failure/Error: expect(response.body).to eq("[]")

       expected: "[]"
            got: ""

       (compared using ==)
     # ./spec/controllers/foos_controller_spec.rb:8:in `block (3 levels) in <top (required)>'

Finished in 0.06748 seconds (files took 4.92 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/controllers/foos_controller_spec.rb:5 # FoosController#index should eq "[]"

curl http://localhost:3000/foos.json with rails server does return [] as expected.

JB returns output in a string when used with Rails 7.1

It seems that using the gem in conjunction with Rails 7.1, the output is all wrapped in double quotes (i.e. a string) instead of returning correctly. This of course breaks most clients.

i.e. the raw response is "{}" instead of {}.

Content-Type is still correct: Content-Type: application/json; charset=utf-8

In rails 5 without "jbuilder" gem in Gemfile it returns "204 no content"

If I delete in Gemfile line:
gem 'jbuilder'
it returns:
Completed 204 No Content
when I try to run http://localhost:3000/api/v1/customers/1 in browser

Some files:

# routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :customers
    end
  end
end

# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include ActionView::Rendering
end

# app/controllers/api_controller.rb
class ApiController < ApplicationController
  before_action :set_default_format

private

  def set_default_format
    request.format = :json
  end
end

# app/controllers/api/v1/customers_controller.rb
class Api::V1::CustomersController < ApiController
  before_action :set_customer, only: [:show, :update, :destroy]

  def show
  end

private

  def set_customer
    @customer = Customer.find(params[:id])
  end
end

# app/views/api/v1/customers/show.json.jb
json = {
    id: @customer.id,
    name: @customer.name
}

json

Next release?

Hi @amatsuda

I am wondering when will the next release shipped

I am looking forward to see #15 included in next release so we don't have to reference to master branch in our Gemfile

Thanks

Are nested partials supported?

In jbuilder one could reuse partials within the main template either for collections of just for better code organization.

Is this possible with jb?

I haven't been able to find relevant info in the readme.

TIA

undefined local variable or method `json'

I'm trying to implement this in my index.json.jb

json[:data] = @positions.map do |p|
  {
    id: p.id,
    name: p.name,
    ptype: p.ptype,
    address: p.address,
    code: p.code,
    light: p.light,
    countries: {
      id: p.country.id,
      name: p.country.name
    },
    cities: {
      id: p.city.id,
      name: p.city.name
    }
  }
end
json

however I get this error:

undefined local variable or method `json'
Did you mean?  JSON

What am I doing wrong here, please?

I need to have something like this as result:

{
    "data": [{
        "id": 2,
        "name": "my_name",
        "ptype": "my_type",
        "address": "some street",
        "code": "some code",
        "light": "false",
        "countries": {
            "id": 1,
            "name": "Germany"
        },
        "cities": {
            "id": 1,
            "name": "Berlin"
        }
    }]
}

I'm using Rails 5.1.4 and Ruby 2.4.2.

Question: Does the gem work with i.e. rails 5.2.pre?

I encounted that rails render is not working with jbuilder in API mode.

class ApplicationController < ActionController::API
end
class BooksController < ApplicationController
  def index
  end
end
# views/books/index.json.jb
{author: {name: 'Matz'}}

Rails renders empty string with jb also like with jbuilder.
Does the gem work with rails 5.1 or 5.2.pre?

When partial returns nil, the result of `nil.to_s` is overwritten

how to reproduce

  1. prepare a partial template
  2. the partial template returns nil

this is an example

# foo.json.jb
{
  p: render(partial 'bar')
}

# _bar.json.jb
return if true
{
  age: 10,
  name: 'name'
}

versions
jb: 0.8.1
ruby: 3.2.2
rails: 7.1.1

I guess fb4b0de has overwritten the NilClass object behavior.
Do you expect a partial to be implemented that return Hash or Array?

Partials render weirdly

Hi, I have a common situation: index and show actions both should render an object partial. When I place the code into the corresponding views (index.json.jb and show.json.jb) everything is good, but when I place there any modifications of render partial: 'object', collection: @objects, it renders weird output:

"<!-- begin: app/views/api/v1/objects/_object.json.jb (from app/views/api/v1/objects/index.json.jb:1) -->\n[{:id=>1, :name=>\"Name 1\", :position=>0, :description=>\"Test 1\"}, {:id=>2, :name=>\"Name 2\", :position=>1, :description=>\"Test 2\"}]<!-- end: app/views/api/v1/objects/_object.json.jb (from app/views/api/v1/objects/index.json.jb:1) -->"

Is it me doing something wrong or is it a bug? Thank you.

Btw, I'm on Ruby 2.3.4 Rails 4.1.1

Rails 6.0.0.beta3: render partial: wrong number of arguments (given 2, expected 0)

layouts/application.html.erb

<html>
  <head>
    <title><%= app_title + (content_for?(:page_title) ? ' | ' + yield(:page_title) : '') %></title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <%= csrf_meta_tags %>
    <%= stylesheet_pack_tag 'application' %>
    <%= content_tag :div, hidden: true, id: "js_data", js_data: @js_data.to_json do %>
    <% end %>
    <%= content_for?(:js) ? yield(:js) : javascript_packs_with_chunks_tag('application') %>
  </head>
  <body>
    <% unless hide_navbar? %>
      <%= render 'layouts/navbar' %>
    <% end %>

    <section class="section">

      <div class="container">
        <%= yield %>
        <%= render 'layouts/footer' %>
      </div>

    </session>
  </body>
</html>

This causes an error in rails 6.0.0.beta3:

ActionView::Template::Error (wrong number of arguments (given 2, expected 0)):
    11:   </head>
    12:   <body>
    13:     <% unless hide_navbar? %>
    14:       <%= render 'layouts/navbar' %>
    15:     <% end %>
    16: 
    17:     <section class="section">

Can be reproduced on a fresh new app rails _6.0.0.beta3_ new test_render
example: https://github.com/olegantonyan/jb_render_bug

Rendering partials inline requires #to_json

When calling rendering a partial a different template format, such as slim in my case, the render call needs to have #to_json called on it otherwise it returns a ruby hash instead of valid json. I didn't see any heads ups on this in the documentation so I'm not sure if this is intentional behavior or not.

For example, using jbuilder you can use something like this:

render partial: 'entry', object: @entry, formats: :json

whereas with jb you need this:

render(partial: 'entry', object: @entry, formats: :json).to_json

empty array rendered as null instead of []

Say I have in projects_controller#index action
@projects = []
and I have views/projects/_project.json.rb
{ name: project.name }
and views/projects/index.json.rb
render @projects

whenever @projects = [], I get null as the output json. If, however, I modified index.json.jb to

render partial: 'projects/project', collection: @projects, as: :project

it will output [] is this by design?

TypeError when rendering a partial directly

class TeasController < ApplicationController
  def create
    result = NewTeaService.new.call(params.to_unsafe_h)

    case result
    in Success[:created, Tea => tea]
      render tea, status: :created
    in Failure[_, errors]
      render json: errors, status: :unprocessable_entity
    end
  end
end

This action tries to render app/views/teas/_tea.json.jbuilder

# app/views/teas/_tea.json.jbuilder
tea.slice(*%i[
  id
  name
  country
  created_at
  updated_at
])

but fails with

TypeError (no implicit conversion of Array into String):

rack (2.2.3) lib/rack/etag.rb:69:in `<<'
rack (2.2.3) lib/rack/etag.rb:69:in `block in digest_body'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:158:in `each'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:158:in `each_chunk'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:140:in `each'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:76:in `each'
actionpack (6.1.4.1) lib/action_dispatch/http/response.rb:493:in `each'
rack (2.2.3) lib/rack/etag.rb:67:in `digest_body'
rack (2.2.3) lib/rack/etag.rb:31:in `call'
rack (2.2.3) lib/rack/conditional_get.rb:40:in `call'
rack (2.2.3) lib/rack/head.rb:12:in `call'
activerecord (6.1.4.1) lib/active_record/migration.rb:601:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (6.1.4.1) lib/active_support/callbacks.rb:98:in `run_callbacks'
actionpack (6.1.4.1) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/actionable_exceptions.rb:18:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/debug_exceptions.rb:29:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
railties (6.1.4.1) lib/rails/rack/logger.rb:37:in `call_app'
railties (6.1.4.1) lib/rails/rack/logger.rb:26:in `block in call'
activesupport (6.1.4.1) lib/active_support/tagged_logging.rb:99:in `block in tagged'
activesupport (6.1.4.1) lib/active_support/tagged_logging.rb:37:in `tagged'
activesupport (6.1.4.1) lib/active_support/tagged_logging.rb:99:in `tagged'
railties (6.1.4.1) lib/rails/rack/logger.rb:26:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/request_id.rb:26:in `call'
rack (2.2.3) lib/rack/runtime.rb:22:in `call'
activesupport (6.1.4.1) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/static.rb:24:in `call'
rack (2.2.3) lib/rack/sendfile.rb:110:in `call'
actionpack (6.1.4.1) lib/action_dispatch/middleware/host_authorization.rb:98:in `call'
railties (6.1.4.1) lib/rails/engine.rb:539:in `call'
puma (5.5.0) lib/puma/configuration.rb:249:in `call'
puma (5.5.0) lib/puma/request.rb:77:in `block in handle_request'
puma (5.5.0) lib/puma/thread_pool.rb:340:in `with_force_shutdown'
puma (5.5.0) lib/puma/request.rb:76:in `handle_request'
puma (5.5.0) lib/puma/server.rb:447:in `process_client'
puma (5.5.0) lib/puma/thread_pool.rb:147:in `block in spawn_thread'

unless I append .to_json to the partial. Is this working as intended?

JSONP support?

Hi~ First, thank you for this gem. I started to use this gem in my projects because the syntax is enjoyable.

I want to ask, if there is jsonp support with this? so the output can be wrapped in function, defined by params[:callback] for example.

Thank you

Rendering with layouts

I may be doing something wrong, but I'm trying to add a layout so I can standardize some metadata in the JSON output.

I've got an layouts\api.jb file and I've requested that layout in the controller generating the json output.

ArgumentError in Api::PostsController#show
unknown keywords: id, title, slug, blurb, classification, higher_ed, sponsor_label, comment_count, share_count, tags, authors, image, date, content, description, schema, meta, chartbeat

Extracted source (around line #172):
    def initialize(*)
      @html_safe = true
      super
    end

    def initialize_copy(other)
....
.bundle/gems/activesupport-4.2.7.1/lib/active_support/core_ext/string/output_safety.rb:172:in `initialize'
.bundle/gems/activesupport-4.2.7.1/lib/active_support/core_ext/string/output_safety.rb:172:in `initialize'
.bundle/gems/actionview-4.2.7.1/lib/action_view/flows.rb:18:in `new'
.bundle/gems/actionview-4.2.7.1/lib/action_view/flows.rb:18:in `set'
.bundle/gems/actionview-4.2.7.1/lib/action_view/renderer/template_renderer.rb:65:in `render_with_layout'
.bundle/gems/skylight-0.10.6/lib/skylight/probes/action_view.rb:32:in `block in render_with_layout'
.bundle/gems/actionview-4.2.7.1/lib/action_view/renderer/abstract_renderer.rb:39:in `block in instrument'
.bundle/gems/activesupport-4.2.7.1/lib/active_support/notifications.rb:164:in `block in instrument'
.bundle/gems/activesupport-4.2.7.1/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
.bundle/gems/activesupport-4.2.7.1/lib/active_support/notifications.rb:164:in `instrument'
.bundle/gems/actionview-4.2.7.1/lib/action_view/renderer/abstract_renderer.rb:39:in `instrument'
.bundle/gems/skylight-0.10.6/lib/skylight/probes/action_view.rb:31:in `render_with_layout'
.bundle/gems/actionview-4.2.7.1/lib/action_view/renderer/template_renderer.rb:52:in `render_template'

I've tried ERB layout template, using content_for, assigning the template output to variables (including instance vars) and using yield or ERB rendering. All lead to similar issues.

Am I totally missing something simple @amatsuda ?

Deprecation warning with generators in Rails 6.0.2

In Rails 6.0.2, running any rails generate commands with the jb gem installed results in this deprecation warning:

Deprecation warning: Expected string default value for '--jb'; got true (boolean).
This will be rejected in the future unless you explicitly pass the options `check_default_type: false` or call `allow_incompatible_default_type!` in your code
You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.

I believe this issue is coming from the scaffold generator controller: https://github.com/amatsuda/jb/blob/master/lib/generators/rails/scaffold_controller_generator.rb. For some reason Rails 6.0.2 is expecting a string value here. This issue did not occur on Rails 6.0.1 or any other prior versions as far as I know.

I was able to eliminate the deprecation warning by adding the line config.generators.jb = "true" in my config/application.rb file, which is providing a string value as opposed to a boolean. However, I'm not sure if that's the correct approach, setting it to "true" or "false" didn't seem to have an effect on the generator functionality, although I didn't test every scenario. Any thoughts are appreciated, thanks!

Unknown keyword problem

I am using rails 4.2.7.1. Basically when I set { id: ''} value to .jb template file it says unknown keyword: id. Then I add .to_json at and end of { id: ''} hash expression. But at this stage it produces bad JSON-like string which browsers do not understand.

"{\"id\":\"\"}\n}\n\n"

Due to the fact that there are special characters in this string browsers do not understand whether the string is JSON or not even if the server sends Content-Type: application/json header. Maybe there is a proper way but as I read from the documentation I could not find any way.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.