simlock
병렬 코딩 에이전트가 동일 시뮬레이터를 놓고 충돌하지 않도록 제어 평면 제공
Ruby on Rails용 간단하고 빠른 JSON API 템플릿 엔진
A simple and fast JSON API template engine for Ruby on Rails
개발 도구Ruby · JavaScriptGitHub @amatsuda직접 설치운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
설치형 프로젝트는 배포 도메인 없이도 확인할 수 있습니다. 개인 저장소는 GitHub 로그인으로, 조직 저장소는 위의 저장소 확인 파일로 직접 확인해 주세요.
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A simpler and faster Jbuilder alternative.
Add this line to your application's Gemfile:
gem 'jb'
And bundle.
Put a template file named *.jb in your Rails app's app/views/* directory, and render it.
method_missing callsrender_partial with :collection option actually renders the collection (unlike Jbuilder)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.
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.commentsbecause if the collection is empty,renderwill returnnilinstead 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
ActionController::API doesn't render view templates by default. So, in order to render .jb templates from API controllers, Jb includes ActionView::Rendering, ActionController::Helpers, and ActionController::ImplicitRender into ActionController::API (just like Jbuilder does).
Note that this changes the behavior of API controller actions that don't explicitly call render. Instead of always responding with 204 No Content, such an action now renders its template if one exists, raises ActionController::UnknownFormat if templates exist only for other formats, and raises ActionController::MissingExactTemplate for a GET request for HTML from a browser with no template.
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
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.
Here're the results of a benchmark (which you can find here in this repo) rendering a collection to JSON.
% ./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 ./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
According to the benchmark results, you can expect 2-30x performance improvement in development env, and 3-10x performance improvement in production env.
Pull requests are welcome on GitHub at https://github.com/amatsuda/jb.
The gem is available as open source under the terms of the MIT License.