How to replace erb with liquid? - ruby-on-rails

I'd like to use liquid in my Rails app. I've installed the gem. In order to use in all templates, I've created a library (lib/liquid_view.rb:):
class LiquidView
def self.call(template)
"LiquidView.new(self).render(#{template.source.inspect}, local_assigns)"
end
def initialize(view)
#view = view
end
def render(template, local_assigns = {})
#view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'
assigns = #view.assigns
if #view.content_for?(:layout)
assigns["content_for_layout"] = #view.content_for(:layout)
end
assigns.merge!(local_assigns.stringify_keys)
controller = #view.controller
filters = if controller.respond_to?(:liquid_filters, true)
controller.send(:liquid_filters)
elsif controller.respond_to?(:master_helper_module)
[controller.master_helper_module]
else
[controller._helpers]
end
liquid = Liquid::Template.parse(template)
liquid.render(assigns, :filters => filters, :registers => {:action_view => #view, :controller => #view.controller})
end
def compilable?
false
end
end
And added the following initialiser (config/initializers/liquid_template_handler.rb:):
require 'liquid_view'
ActionView::Template.register_template_handler :liquid, LiquidView
PS: I've followed these instructions.
Now, if rename a template file with liquid my_template.html.liquid the <%= stylesheet_link_tag 'mycss' %> stopped working, but more importantly the {{user.first_name}} variable did not print. In my controller I have #user = current_user
What am I missing?
My intention is to completely override erb with liquid in some templates, so ideally it should work like erb (in a sense that I can pass variables from the controller and simply render it in the template without using Liquid::Template.parse(#page.template) which by the way, I don't understand how it works on a file-based template.
PS: I'm also using [this] gem (https://github.com/yoolk/themes_on_rails) for separate templates. I'm not sure it does any impact on it.
PPS: I've seen this but doesn't apply as its a older version of Rails and I'm not using prepend.
PPPS: I'm using Ruby 2.2.2 and Rails 4.2

I hope this not the problem you are thinking it is . You can check the way as it was said here Github Description

Did you create a Drop to access #user?
https://github.com/Shopify/liquid/wiki/Introduction-to-Drops
Liquid is a safe template system, so we can interpret on the backend templates that are created by the user. To access anything non trivial (number, string, hashes or arrays) you need a Drop, which is a controlled interface to define what the templates can access.
This is by design and for security reasons.

Related

Rails 4: What Goes in Controller and Model?

I have been reading and watching few videos on learning Rails 4. All tutorials has their own code so, in my views, easy to follow. I can't seem to learn anything or remember few things so I have decided to use my own code and see if I could follow instead of using their code.
So far I understand the Controller corresponds with the views:
# In my controller
def index
#x = "I love Ruby"
end
And in my views (index.html.erb)
<% = #x %> #=> I love Ruby
That simple thing would work for the index page. Now what if want to refer other method calls in that view's index, how to do that? In the controller:
def index
#x = "I love Ruby"
end
Still within the controller's class:
def languages_i_hate
languages = %w[
Perl
PHP
C#
C++ ]
end
And in my index.html.erb:
<%= These are the languages I hate to bits: #{languages_i_hate.upcase}!
I got undeclared method or variable "languages_i_hate"
How do I call method names in a webpage?
What you are trying to do here is access a controller method in the view. When you do this the controller method is accessed as if it was a helper method. Normally controller methods aren't available to be used in this way, but you can tell the controller to make them available as helpers.
See http://apidock.com/rails/ActionController/Helpers/ClassMethods/helper_method
BTW, when you have methods in the controller which aren't actions, ie don't correspond to a route/url, you should put them in a protected section, by convention at the bottom of the controller. This makes it clear to rails and to the reader that they're not actions.
def index
#x = "I love Ruby"
languages_i_hate
end
def languages_i_hate
#languages = %w[Perl PHP C# C++ ]
end
index.html.erb:
<%= "These are the languages I hate to bits: #{#languages_i_hate}" %>
According to rails convention you have to make use of Helpers. Other approach is by use of locals while rendering template.
def index
#x = "I love Ruby"
render :template => "index.html.erb", :locals =>{:languages_i_hate => languages_i_hate}
end
def languages_i_hate
languages = %w[
Perl
PHP
C#
C++ ]
end
And in my index.html.erb:
<%= These are the languages I hate to bits: #{languages_i_hate.upcase}!%>

How does one use instance methods in a static method? I'm trying to async'ly create a document

In my controller, i have a method defined as:
def self.store_pdf(id)
...
end
in that method, I need to call render_to_string to render the correct file / layout:
render_to_string(
:action => "../view/current_version/show.pdf.erb",
:layout => false)
but because render_to_string is both an instance method and protected, I need to do the following:
me = self.new # self is the cortroller
me.send(:render_to_string,
:action => "../view/current_version/show.pdf.erb",
:layout => false)
but then there are dependencies such as the response object that render_to_string needs to work, as shown here: http://apidock.com/rails/ActionController/Base/render_to_string
So, I began adding them
me.send(:response=, ActionController::Response.new)
But, more and more of the global instance variables need to be defined, and I decided it was too much work just to try to get one static method to work.
The method needs to be static, so that delayed_job can run the method in the background at a later time.
Anyone have an idea as to how to pull this off?
You can read erb via ERB if you are not using any rails helper,If you are using any rails helper then include Rails helper.
you can refer using here or
require 'erb'
class PdfRender
#include ActionView::Helpers::OutputSafetyHelper
#include helper if any is present any
def self.render_pdf(id)
#set any instance variable if you are using in pdf
content = File.read('path/of/erb/template')
template = ERB.new(content)
# template content will give you text now you can render or generate pdf
template_content = template.result(binding)
end
end
Note:
replace h() with CGI.escapeHTML()

Render ERB Template in RABL Template

I have a scenario where I'd like to pass back a long message with my JSON. Instead of writing it out with string concatenation I'd rather put together an erb template that I can render into my JSON. Below is the code I'm currently trying:
object #invitation
node(:phone_message) do |invitation|
begin
old_formats = formats
self.formats = [:text] # hack so partials resolve with html not json format
view_renderer.render( self, {:template => "invitation_mailer/rsvp_sms", :object => #invitation})
ensure
self.formats = old_formats
end
end
Everything works as expected the first time this code is run, however, I run into problems the second time I run it because it says there is a missing instance variable (which I assume was generated and cached during the first run).
undefined method
_app_views_invitation_mailer_rsvp_sms_text_erb___2510743827238765954_2192068340
for # (ActionView::Template::Error)
Is there a better way to render erb templates into rabl?
You could try using ERB as standalone, and not going through the view renderer, like so:
object #invitation
node(:phone_message) do |invitation|
begin
template = ERB.new(File.read("path/to/template.erb"))
template.result(binding)
end
end
binding is a method on Object (through the Kernel module) and it returns the binding which holds the current context, which also includes instance variables (#invitation in this case)
Update:
Don't really know if this will help you get any further (and I also realised it's been more than a year since you posted this), but here's another way to render ERB templates in a standalone fashion:
view = ActionView::Base.new(ActionController::Base.view_paths, {})
class << view
include ApplicationHelper
include Rails.application.routes.url_helpers
end
Rails.application.routes.default_url_options = ActionMailer::Base.default_url_options
view.render(:file => "path/to/template.html.erb", :locals => {:local_var => 'content'})
When I have time I should actually try this with Rabl.

Ruby w/ Sinatra: what is the equivalent of a .js.erb from rails?

.js.erb's are nice, because you can use them to replace parts of a page without having to leave the current page, which gives a cleaner and unchopped up feel to the site / app.
Is there a way to use them in sinatra? or an equivalent?
Just add .js to the end of the symbol you're passing erb(). A la (to call mypage.js.erb):
erb "mypage.js".to_sym
Dirty, but it works.
Based on your description, I'm guessing that your desire is to have portions of a page editable and replaced via AJAX. If this is wrong, please clarify.
I do this in my Sinatra apps by including (my own) AJAXFetch jQuery library and writing code as shown below. This lets me use the partial both when rendering the page initially as well as when editing via AJAX, for maximum DRYness. The AJAXFetch library handles all AJAX fetch/swap through markup alone, without needing to write custom JS on the pages that use it.
helpers/partials.rb
require 'sinatra/base'
module Sinatra
module PartialPartials
ENV_PATHS = %w[ REQUEST_PATH PATH_INFO REQUEST_URI ]
def spoof_request( uri, headers=nil )
new_env = env.dup
ENV_PATHS.each{ |k| new_env[k] = uri.to_s }
new_env.merge!(headers) if headers
call( new_env ).last.join
end
def partial( page, variables={} )
haml page, {layout:false}, variables
end
end
helpers PartialPartials
end
routes/bug.rb
get '/bug/:bug_id' do
if #bug = Bug[params[:bug_id]]
# ...
haml :bug
end
end
# Generate routes for each known partial
partials = %w[ bugdescription bughistory bugtitle fixer
pain project relatedbugs status tags version votes ]
partials.each do |part|
[ part, "#{part}_edit" ].each do |name|
get "/partial/#{name}/:bug_id" do
id = params[:bug_id]
login_required
halt 404, "(no bug ##{id})" unless #bug = Bug[id]
partial :"_#{name}"
end
end
end
post "/update_bug/:partial" do
id = params[:bug_id]
unless params['cancel']=='cancel'
# (update the bug based on fields)
#bug.save
end
spoof_request "/partial/#{params[:partial]}/#{id}", 'REQUEST_METHOD'=>'GET'
end
views/bug.haml
#main
#bug.section
= partial :_bugtitle
.section-body
= partial :_bugdescription
<!-- many more partials used -->
views/_bugtitle.haml
%h1.ajaxfetch-andswap.editable(href="/partial/bugtitle_edit/#{#bug.pk}")= title
views/_bugtitle_edit.haml
%form.ajaxfetch-andswap(method='post' action='/update_bug/bugtitle')
%input(type="hidden" name="bug_id" value="#{#bug.id}")
%h1
%input(type="text" name="name" value="#{h #bug.name}")
%span.edit-buttons
%button(type="submit") update
%button(type="submit" name="cancel" value="cancel") cancel
sinatra really isn't meant to be a full stack framework. Its supposed to get you on the road very quickly. You could use an erb separately and then load into your sinatra code.

render_to_string in lib class not working

I'm trying to use delayed_job to update a remote database via xml
In my lib folder I put a file with a class that should do a render_to_text with template.xml.builder, but I get:
undefined method `render_to_string' for #<SyncJob:0x7faf4e6c0480>...
What am I doing wrong?
ac = ActionController::Base.new()
ac.render_to_string(:partial => '/path/to/your/template', :locals => {:varable => somevarable})
I had problems with a undefined helper method then I used ApplicationController
ApplicationController.new.render_to_string
render_to_string is defined in ActionController::Base. Since the class/module is defined outside the scope of the Rails controllers the function is not available.
You are going to have to manually render the file. I don't know what you are using for your templates (ERB, Haml, etc.). But you are going to have load the template and parse it yourself.
So if ERB, something like this:
require 'erb'
x = 42
template = ERB.new <<-EOF
The value of x is: <%= x %>
EOF
puts template.result(binding)
You will have to open the template file and send the contents to ERB.new, but that an exercise left for you. Here are the docs for ERB.
That's the general idea.
Rails 5
render_to_string and others are now available as class methods on the controller. So you may do the following with whatever controller you prefer: ApplicationController.render_to_string
I specifically needed to assign a dynamic instance variable for the templates based on an object's class so my example looked like:
ApplicationController.render_to_string(
assigns: { :"#{lowercase_class}" => document_object },
inline: '' # or whatever templates you want to use
)
Great blog post by the developer who made the rails PR: https://evilmartians.com/chronicles/new-feature-in-rails-5-render-views-outside-of-actions
You could turn your template.xml.builder into a partial (_template.xml.builder) and then render it by instantiating an ActionView::Base and calling render
av = ActionView::Base.new(Rails::Configuration.new.view_path)
av.extend ApplicationController.master_helper_module
xml = av.render :partial => 'something/template'
I haven't tried it with xml yet, but it works well with html partials.

Resources