This app's array views take the simple path
The junior course "API development: Jbuilder" read this app's .json.jbuilder views. Several of them build arrays: the catalogue search behind the command palette, and the partner API's lists of learners, certificates and activity. The search view is the simplest:
json.array! @result.courses + @result.lessons do |item|
if item.is_a?(::Catalog::Course)
json.title item.title
json.url catalog_course_path(item.slug)
json.type "course"
json.level item.level
else
json.title "#{item.course.title}: #{item.title}"
json.url catalog_lesson_path(course_slug: item.course.slug, slug: item.slug)
json.type "lesson"
json.level item.course.level
end
end
It's a block, called once per item. That's array!'s simplest form, and every array in the app's JSON views is built that way or with a plain map. This lesson covers what that form does, and what it can't do.
array!, from the installed gem
# jbuilder-2.15.1/lib/jbuilder/jbuilder_template.rb
def array!(collection = EMPTY_ARRAY, *args, &block)
options = args.first
if _partial_options?(options)
options = options.dup
options[:collection] = collection
_render_partial_with_options options
else
_array collection, args, &block
end
end
def _partial_options?(options)
::Hash === options && options.key?(:as) && options.key?(:partial)
end
It has one if. The search view's call, json.array! collection do |item| ... end, has no second argument, so options is nil and _partial_options? returns false straight away. array! goes to _array, which, given a block, maps over the collection: it yields each item to the block, captures the attributes the block sets, and appends them to a plain Array. There's no template, no partial and no cache.
The app's search results, through this path
In development:
result = Catalog::SearchQuery.call(query: "ruby", locale: :en)
renderer = ApplicationController.renderer
renderer.render(inline: 'json.array! @items do |c| json.title c.title end', type: :jbuilder, assigns: { items: result.courses })
search result: 8 courses, 8 lessons
block-form output: [{"title":"Ruby: programmer happiness"},{"title":"Ruby variables & assignment"},
{"title":"Ruby symbols vs strings"},{"title":"Ruby data: numbers & booleans"}, ...
These are the app's own course titles, serialised through the same block-form path the search view runs on every request. SearchQuery::LIMIT = 8 is why the output stops at 8 courses and 8 lessons, and that number matters again in lesson 6.
What the block form can't do
_partial_options? only returns true when the second argument is a Hash with both :as and :partial. A call with a block and nothing else has no options hash for cached: to be a key of. Caching isn't switched off somewhere; the block path never looks for a cached: option at all, because the branch that reads it is the other one, the branch _partial_options? guards. Lesson 2 takes that branch, which none of the app's JSON views use.
Key takeaways
- The search view,
catalog/search/index.json.jbuilder, callsarray!with a block and nothing else, and the partner API views build their arrays the same way or withmap. array!branches on_partial_options?, which needs aHashwith both:asand:partial; a bare block never satisfies it, so the call goes to_arrayand a plain per-item block.- Run on the app's
SearchQueryresults, this path produced the app's course titles, the same mechanism the search endpoint uses. - The block form has no
cached:option to pass: it's not a missed setting but a branch ofarray!the block call never reaches.
Keep going with lesson 2
The partial-and-cached path this app has never used
Runs array! with partial:, as: and cached: on search data, firing one batched cache read and write.
A free account also gives you the quizzes, your progress and notes, and the discussion.