Using the suggestions in ccocchi/rabl-rails#88 (comment) I was able to resolve my problem I described in ccocchi/rabl-rails#88 (comment) but I needed few customizations to be made. Following is the list of customizations I made:
-
Created
lib
folder underapp
folder. -
Created
rabl-rails
folder underapp/lib
folder. -
Created
renderers
folder underapp/lib/rabl-rails
folder. -
Created
renderer.rb
file underapp/lib/rabl-rails
folder. -
Created
hash_supporting_locals.rb
file underapp/lib/rabl-rails/renderers
folder and added following content to it
module RablRails
module Renderers
module HashSupportingLocals
include Renderers::Hash
extend self
#
# Render a template.
# Uses the compiled template source to get a hash with the actual
# data and then format the result according to the `format_result`
# method defined by the renderer.
#
def render(template, context, locals = nil)
visitor = Visitors::ToHash.new(context)
collection_or_resource = if template.data
if context.respond_to?(template.data)
context.send(template.data)
else
visitor.instance_variable_get(template.data)
end
end
# Note: This is the line added to original implementation.
collection_or_resource ||= locals[:resource] if locals
render_with_cache(template.cache_key, collection_or_resource) do
output_hash = if collection?(collection_or_resource)
render_collection(collection_or_resource, template.nodes, visitor)
else
render_resource(collection_or_resource, template.nodes, visitor)
end
format_output(output_hash, root_name: template.root_name, params: context.params)
end
end
end
end
end
- Created
json_supporting_locals.rb
file underapp/lib/rabl-rails/renderers
folder and added following content to it
module RablRails
module Renderers
module JSONSupportingLocals
include Renderers::JSON
include Renderers::HashSupportingLocals
extend self
end
end
end
- Created
library_extended.rb
file underapp/lib/rabl-rails
folder and added following content to it
module RablRails
class LibraryExtended < Library
# This is the customized-version of RENDERER_MAP in super class.
RENDERER_MAP = begin
h = Library::RENDERER_MAP.dup
h.merge!(
json: Renderers::JSONSupportingLocals,
ruby: Renderers::HashSupportingLocals
)
h.freeze
end
# Overridden method so that `RENDERER_MAP` defined in this file
# gets used by this method.
def get_rendered_template(source, view, locals = nil)
compiled_template = compile_template_from_source(source, view)
format = view.lookup_context.formats.first || :json
raise UnknownFormat, "#{format} is not supported in rabl-rails" unless RENDERER_MAP.key?(format)
RENDERER_MAP[format].render(compiled_template, view, locals)
end
end
end
-
Copied the contents of https://github.com/ccocchi/rabl-rails/blob/v0.4.3/lib/rabl-rails/renderer.rb file and added them to the file
app/lib/rabl-rails/renderer.rb
. -
Update
app/lib/rabl-rails/renderer.rb
by making following changes:
-
Replaced following
require 'rabl-rails/renderers/hash' require 'rabl-rails/renderers/json'
WITH
require 'rabl-rails/renderers/hash_supporting_locals' require 'rabl-rails/renderers/json_supporting_locals'
-
Added
require 'rabl-rails/library_extended'
as the lastrequire
statement. -
In
RablRails::Renderer::LookupContext
class made following changes:-
removed method
rendered_format
-
added following method
def formats [ @format ].map(&:to_sym) end
-
-
In
render
method replacedLibrary
withLibraryExtended
.
- Finally you should have a ruby file in which there should be a method which has following line
RablRails.render(serializable_object, <template_name>, view_path: <template_path>, format: :json)
At the top of that file add following code:
require 'rabl-rails/renderer'
module RablRails
extend RablRails::Renderer
end
and that method should work without needing any changes.
Details behind the customizations listed in https://gist.github.com/jiggneshhgohel/1f0137e5dc294cc94141785eaf836c87#file-rabl-rails-directly-use-object-md.
Copied https://github.com/ccocchi/rabl-rails/blob/v0.4.3/lib/rabl-rails/renderer.rb as it is in
app/lib/rabl-rails/renderer.rb
file as was suggested at ccocchi/rabl-rails#88 (comment) to fix the problem mentioned in that issue.However using this file with the codebase in the latest version of rabl-rails (which was 0.6.2 at the time of writing this (ref: ccocchi/rabl-rails@12c7103) ) it raised error
Investigating the problem it was found that in v0.4.3 codebase in
RablRails::Renderer::LookupContext
was manually defined and following method was defined in itwhich was used by
RablRails::Library#get_rendered_template(....)
method at https://github.com/ccocchi/rabl-rails/blob/v0.4.3/lib/rabl-rails/library.rb#L29.But in the versions greater-than 0.4.3 the
RablRails::Renderer
module itself is removed and consequentlyRablRails::Renderer::LookupContext
also is removed and in the latest versionRablRails::Library#get_rendered_template(....)
is updated to use
in which
view.lookup_context
returns an instance ofActionView::LookupContext
and which has aformats
method. In Rails latest version (v 7.1.3.2 at the time of writing this) docs atlookup_context
method doc can be found at https://api.rubyonrails.org/classes/ActionView/ViewPaths.html#method-i-lookup_context andActionView::LookupContext.formats
method can be found at https://github.com/rails/rails/blob/v7.1.3.2/actionview/lib/action_view/lookup_context.rb#L50.So to make the code in this file compatible with latest version of
RablRails::Library#get_rendered_template(....)
we needed to replacewith
After doing above change and testing it following error was encountered
That was unexpected.
Adding following debug statements at the start of
RablRails::Library#get_rendered_template(source, view, locals = nil)
method (see https://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/library.rb#L26 which is the link to rabl-rails's latest-version code at the time of writing this (which was 0.6.2) which was invoked by the code in this file)
following was found
And the line which was causing problem was found to be
https://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/library.rb#L30
So debugged the method at
https://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/renderers/hash.rb#L13
and found that in the 0.6.2 version, the
locals
passed to that method wasn't used in the method which was the case in 0.4.3 version of that method (see https://github.com/ccocchi/rabl-rails/blob/v0.4.3/lib/rabl-rails/renderers/hash.rb#L23) and in our caselocals
contained the "object which contained methods which were invoked in the template's source".So the solution was to make set
data
to the object (which was received inlocals
) on thetemplate
so thatif template.data
on linehttps://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/renderers/hash.rb#L16
evaluates to true.
template
was found to be an instance ofRablRails::CompiledTemplate
. So explored the implementation of following related fileshttps://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/compiler.rb
https://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/template.rb
but couldn't find a straightforward/reliable way to set the
data
ontemplate
instance.Thus the last option was to override following method
https://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/renderers/hash.rb#L13
in our application and restore the usage of
locals
in overridden version of the method.But taking that approach I ended up creating
RablRails::Renderer::HashSupportingLocals
which includedRablRails::Renderer::Hash
and overriddenrender(template, context, locals = nil)
by restoring the linecollection_or_resource ||= locals[:resource] if locals
which was there inRablRails::Renderer::Hash
's code in 0.4.3 version.After doing that and trying it out another need came up to customize
RablRails::Library::RENDERER_MAP
constant such thatruby
key is mapped toRenderers::HashSupportingLocals
. But to achieve that there was no choice except to extendRablRails::Library
class and override that constant.So I ended up implementing a
RablRails::LibraryExtended
and overriding the RENDERER_MAP constant in it in following mannerThen I replaced
Library
, inRablRails::Render#render(object, template, options = {})
method in fileapp/lib/rabl-rails/renderer.rb
, withLibraryExtended
.But still it didn't worked because
RablRails::Library#get_rendered_template(source, view, locals = nil)
method'sRENDERER_MAP
still used the Hash set in its rabl-rails based version ofRablRails::Library
class instead of the one defined in my classRablRails::LibraryExtended
.So another step I took was to override
RablRails::Library#get_rendered_template(source, view, locals = nil)
method in my classRablRails::LibraryExtended
but I realized that if we override that method then there is no need to use customizedRenderers::HashSupportingLocals
because inRablRails::LibraryExtended#get_rendered_template(source, view, locals = nil)
method I could set following
which should make it compatible with the
RablRails::Renderer::Hash#render(template, context, locals = nil)
method. So I decided to discardRablRails::Renderer::HashSupportingLocals
.So the changes in
RablRails::LibraryExtended#get_rendered_template(source, view, locals = nil)
worked but following new error was encountered:That error occurred on following line
https://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/renderers/hash.rb#L17
So I debugged
context
and it was found to be an instance ofRablRails::Renderer::ViewContext
which is defined in fileapp/lib/rabl-rails/renderer.rb
.So I defined an overridden version of
respond_to?
method like following inRablRails::Renderer::ViewContext
class defined in fileapp/lib/rabl-rails/renderer.rb
.And that worked but following new error was encountered:
That error occurred on following line
https://github.com/ccocchi/rabl-rails/blob/12c7103432f734acff949d514045194b408d9736/lib/rabl-rails/renderers/hash.rb#L20
and the error made sense and there was no reliable way to avoid that.
So I came back to my earlier idea of creating
RablRails::Renderer::HashSupportingLocals
an overridden version ofRablRails::Renderer::Hash
but I also realized that to make this idea work we also needed to createRablRails::Renderer::JSONSupportingLocals
because originalRablRails::Renderer::JSON
includedRablRails::Renderer::Hash
and thus for
json
format templates it would invoke that original version ofRablRails::Renderer::Hash
which doesn't supportlocals
.So this way I was able to fix the problem referenced about in https://gist.github.com/jiggneshhgohel/1f0137e5dc294cc94141785eaf836c87#file-rabl-rails-directly-render-object-md by making the listed customizations in that file.