Rendering Partials/View in a Rake Task/Background Job/Model in Rails 4

Rendering partials / view in a rake task / background job / model in Rails 4

Here's what I compiled from lots of sources and what works for me in Rails 4.

With this Renderer class, you should be able to render Rails 4 views and partials in any context, like background jobs, service objects, models, workers, you name it.

# app/services/renderer.rb
# Render views and partials in rake tasks,
# background workers, service objects and more
#
# Use:
#
# class MyService
# def render_stuff
# result = renderer.render(partial: 'tweets/tweet', locals: {tweet: Tweet.first})
# # or even
# result = renderer.render(Tweet.first)
# end
#
# private
#
# def renderer
# @renderer ||= Renderer.new.renderer
# end
# end
#
class Renderer
def renderer
controller = ApplicationController.new
controller.request = ActionDispatch::TestRequest.new
ViewRenderer.new(Rails.root.join('app', 'views'), {}, controller)
end
end

# app/services/view_renderer.rb
# A helper class for Renderer
class ViewRenderer < ActionView::Base
include Rails.application.routes.url_helpers
include ApplicationHelper

def default_url_options
{host: Rails.application.routes.default_url_options[:host]}
end
end

Update:

There seems to be an easier solution: http://makandracards.com/makandra/17751-render-a-view-from-a-model-in-rails

ApplicationController.new.render_to_string(
:template => 'users/index',
:locals => { :@users => @users }
)
# Mind the weird syntax to set @ variables in :locals.

Update 2:

There's a gem called render_anywhere that allows for calling "render" from anywhere: models, background jobs, rake tasks, etc.

Update 3:

In Rails 5, the renderer has been extracted and can be used standalone from background jobs and other places:

ApplicationController.renderer.render(
partial: 'messages/message',
locals: { message: message }
)

For Rails <= 4.2, this functionality can be backported with the backport_new_renderer gem.

Rails, How to render a view/partial in a model

proper solution

Well, "they" are right. You really have to do the rendering in a controller -
but it's fair game to call that controller from a model! Fortunately, AbstractController
in Rails 3 makes it easier than I thought. I wound up making a simple
ActionPusher class, working just like ActionMailer. Perhaps I'll get ambitious and
make this a proper gem someday, but this should serve as a good start for anyone else in my shoes.

I got the most help from this link: http://www.amberbit.com/blog/2011/12/27/render-views-and-partials-outside-controllers-in-rails-3/

in lib/action_pusher.rb

class ActionPusher < AbstractController::Base
include AbstractController::Rendering
include AbstractController::Helpers
include AbstractController::Translation
include AbstractController::AssetPaths
include Rails.application.routes.url_helpers
helper ApplicationHelper
self.view_paths = "app/views"

class Pushable
def initialize(channel, pushtext)
@channel = channel
@pushtext = pushtext
end

def push
Pusher[@channel].trigger('rjs_push', @pushtext )
end
end
end

in app/pushers/users_pusher.rb. I guess the require could go somewhere more global?

require 'action_pusher'

class UsersPusher < ActionPusher
def initialize(user)
@user = user
end

def channel
@user.pusher_key
end

def add_notice(notice = nil)
@notice = notice
Pushable.new channel, render(template: 'users_pusher/add_notice')
end
end

Now in my model, I can just do this:

after_commit :push_add_notice

private

def push_add_notice
UsersPusher.new(user).add_notice(self).push
end

and then you'll want a partial, e.g. app/views/users_pusher/add_notice.js.haml, which could be as simple as:

alert('#{@notice.body}')

I guess you don't really need to do it with Pushable inner class and the .push
call at the end, but I wanted to make it look like ActiveMailer. I also have a
pusher_key method on my user model, to make a channel for each user - but this
is my first day with anything like Pusher, so I can't say for sure if that's the right
strategy. There's more to be fleshed out, but this is enough for me to get started.

Good luck!

(this was my first draft answer, leaving it in because it might help someone)

I've got the general outline of a solution working. Like this, in your model:

after_create :push_new_message

private

def render_anywhere(partial, assigns = {})
view = ActionView::Base.new(ActionController::Base.view_paths, assigns)
view.extend ApplicationHelper
view.render(:partial => partial)
end

def push_new_message
pushstring = render_anywhere('notices/push_new_message', :message_text => self.body)
Pusher[user.pusher_key].trigger!('new_message', pushstring)
end

that is definitely working - the template is rendering, and gets eval()'ed on the client side successfully. I'm planning to clean it up, almost certainly move render_anywhere somewhere more general, and probably try something like this

I can see that pushes will need their own templates, calling the generally available ones, and I may try to collect them all in one place. One nice little problem is that I sometimes use controller_name in my partials, like to light up a menu item, but I'll obviously have to take a different tactic there. I'm guessing I might have to do something to get more helpers available, but I haven't gotten there yet.

Success! Hooray! This should answer your question, and mine - I'll add more detail if it seems appropriate later. Good luck!!!!

original non-answer from an hour ago left for clarity

I don't have an answer, but this timely question deserves more clarification, and I'm hoping to get closer to my answer by helping ask :)

I'm facing the same problem. To explain a little more clearly, Pusher asynchronously sends content to a connected user browser. A typical use case would be a showing the user they have a new message from another user. With Pusher, you can push a message to the receiver's browser, so they get an immediate notification if they are logged in. For a really great demo of what Pusher can do, check out http://wordsquared.com/

You can send any data you like, such as a JSON hash to interpret how you like it, but it would be very convenient to send RJS, just like with any other ajax call and eval() it on the client side. That way, you could (for example) render the template for your menu bar, updating it in its entirety, or just the new message count displayed to the user, using all the same partials to keep it bone-DRY. In principle, you could render the partial from the sender's controller, but that doesn't make much sense either, and there might not even be a request, it could be triggered by a cron job, for example, or some other event, like a stock price change. The sender controller just should not have to know about it - I like to keep my controllers on a starvation diet ;)

It might sound like a violation of MVC, but it's really not - and it really should be solved with something like ActionMailer, but sharing helpers and partials with the rest of the app. I know in my app, I'd like to send a Pusher event at the same time as (or instead of) an ActionMailer call. I want to render an arbitrary partial for user B based on an event from user A.

These links may point the way towards a solution:

  • http://blog.choonkeat.com/weblog/2006/08/rails-calling-r.html
  • How to render a Partial from a Model in Rails 2.3.5
  • http://mattwindsurfs.wordpress.com/2008/06/19/rails-render-in-a-model/
  • http://davetroy.blogspot.com/2008/02/actsasrenderer-brings-output-to-models.html
  • https://github.com/asapnet/acts_as_renderer
  • http://ethilien.net/archives/render-rails-templates-anywhere-even-in-a-model/

The last one looks the most promising, offering up this tantalizing snippet:

def render_anywhere(partial, assigns)
view = ActionView::Base.new(Rails::Configuration.new.view_path, assigns)
ActionView::Base.helper_modules.each { |helper| view.extend helper }
view.extend ApplicationHelper
view.render(:partial => partial)
end

As does this link provided by another poster above.

I'll report back if I get something working

tl;dr: me too!

Rails 4: why is one way of rendering partials so much faster?

example 1:
you are rendering a partial x times (depending on events).
which means you are compiling html x times(once each time the loop runs). which is slow

example 2:
you are rendering one partial with a collection of events
the html is compiled once(as there is only one partial). which is fast

Rails 5 rendering partials and passing data

Your questions are answered in the Ruby on Rails Guides on Layouts and Rendering. It's worth reading the information on partials that comes before the quoted passages below as well:

Every partial also has a local variable with the same name as the
partial (minus the underscore). You can pass an object in to this
local variable via the :object option:

<%= render partial: "customer", object: @new_customer %>

Within the customer partial, the customer variable will refer to
@new_customer from the parent view. (Earlier the Guide instructs that to specify other options for render(), e.g. object:, you have to explicitly specify partial: and the name of the partial.)

If you have an instance of a model to render into a partial, you can
use a shorthand syntax:

<%= render @customer %>

Assuming that the @customer instance variable contains an instance of
the Customer model, this will use _customer.html.erb to render it and
will pass the local variable customer into the partial which will
refer to the @customer instance variable in the parent view.

3.4.5 Rendering Collections

Partials are very useful in rendering collections. When you pass a
collection to a partial via the :collection option, the partial will
be inserted once for each member in the collection:

index.html.erb:

<h1>Products</h1>
<%= render partial: "product", collection: @products %>

_product.html.erb:

<p>Product Name: <%= product.name %></p>

When a partial is called with a pluralized collection, then the
individual instances of the partial have access to the member of the
collection being rendered via a variable named after the partial. In
this case, the partial is _product, and within the _product partial,
you can refer to product to get the instance that is being rendered.

There is also a shorthand for this. Assuming @products is a collection
of product instances, you can simply write this in the index.html.erb
to produce the same result:

<h1>Products</h1>
<%= render @products %>

Rails determines the name of the partial to use by looking at the
model name in the collection. In fact, you can even create a
heterogeneous collection and render it this way, and Rails will choose
the proper partial for each member of the collection:

index.html.erb:

<h1>Contacts</h1>
<%= render [customer1, employee1, customer2, employee2] %>

customers/_customer.html.erb:

<p>Customer: <%= customer.name %></p>

employees/_employee.html.erb:

<p>Employee: <%= employee.name %></p>

In this case, Rails will use the customer or employee partials as
appropriate for each member of the collection.

In the event that the collection is empty, render will return nil, so
it should be fairly simple to provide alternative content.

<h1>Products</h1>
<%= render(@products) || "There are no products available." %>

Print file path when rendering a partial in Rails Views

If you really want debugging be eased by getting partial rendering information you can use gem "xray-rails"

group :development do
gem 'xray-rails'
end

Then bundle and delete your cached assets:

$ bundle && rm -rf tmp/cache/assets

Restart your app, visit it in your browser, and press cmd+shift+x (Mac) or ctrl+shift+x to reveal the overlay ( Documentation)

In this way you can debug all your partials when they are rendered.

Hope this Helps..

Rails: Rendering Models?

If you pass a model directly to render it will attempt to render a partial for it.

<%= render @thing %>

That is the same as.

<%= render :partial => 'things/thing', :object => @thing %>

If you pass an array of models...

<%= render @things %>

It will render the _thing partial for each as if you did this.

<%= render :partial => 'things/thing', :collection => @things %>

Note: this requires Rails 2.3. If you have earlier versions of Rails you'll need to use the :partial option to do the same thing.

<%= render :partial => @thing %>

How can I render my defined model in my view?

It should be:

<%= event.open_student_spots %>

1) Use the = to denote output.

2) It's a method on the model, so you should call it on the event instance. (Which itself should perhaps be an instance variable rather than a local variable)

conditional formatting in rails partials

You could use the Cycle helper. Something like this:

<tr class="<%= cycle("even", "odd") %>">
<td><%= row.name %></td>
</tr>

Or in your case use bgcolor instead, although i would recomend using css classes.

You can cycle through more than two values: cycle(‘first’, ‘second’, ‘third’, ‘and_more’).

There is also: reset_cycle(‘cycle_name’) This makes sure that on each iteration, you will start again with your first value of the cycle list.

Check the rails documentation for more examples.

Avoid using instance variables in partials views

Yes. It is located in a partial file

That's what the warning/error tells you: don't use instance variables in partial files. Rather, use local variables. Example:

Your partial render looks something like this:

= render 'my_partial'

Change it like this to pass all variables explicitly:

= render 'my_partial', productrecords: @productrecords, clone_record: @clone_record

And your partial will use the locals now:

= f.association :product, :collection => Category.products,:include_blank => "Select Product",
:label => "* Product",
:selected =>productrecords.product_id ? productrecords.product_id : clone_record.product_id


Related Topics



Leave a reply



Submit