Which controller should I put #notifications in? - ruby-on-rails

Imagine a typical, modern web app - where in the navbar or some navigation element that runs along the top there is a notifications menu where it tells the user how many notifications they have received since last visiting.
It also may have a dropdown menu that they can clear off existing notifications or just view them.
Given that these notifications need to be present across all views, where should I put the logic for that? In my ApplicationController? That feels wrong, for some reason - but I can't see any other explanation.
Also, should I put the actual partials within the generic /shared/ folder?
For those apps that have a Dashboard controller (i.e. a non-restful resource) that just acts as the central hub for a lot of this info, what's the best way to approach this? In terms of structuring partials that will be included in the Dashboard.
Edit 1
Building off of the answers given by both Kevin and Collin below, I need to go a bit further.
They recommend the Facade pattern as explained by Sandi Metz via Thoughtbot here.
However, this is my issue. In my application.html.erb, I have a partial being rendered - <%= render partial: "shared/navbar" %>. In that partial, I want to be able to call somethings (e.g. that #notifications). How would I access that instance variable and other shared variables/resources on other partials that would be declared in this facades/dashboard.rb?

Use a Facade pattern as described in this article.
See the section on: Only instantiate one object in the controller.
They even use notifications on a dashboard in their example. :)

It's important to remember a couple of things here:
First, only put this logic in your ApplicationController if it's the only place it could possibly go. Since this notifications menu is more than likely persisted across the entire application, that's probably where it belongs.
Secondly, you can implement this Facade while still keeping your DashboardsController quite RESTful. Controllers should only be responsible for instantiating a single object, right? Thus, it makes sense to do something like this:
app/controllers/dashboards_controller.rb
class DashboardsController < ApplicationController
def show
#dashboard = Dashboard.new(current_user)
end
end
app/facades/dashboard.rb
class Dashboard
def initialize(user)
#user = user
end
def new_status
#new_status ||= Status.new
end
def statuses
Status.for(user)
end
def notifications
#notifications ||= user.notifications
end
private
attr_reader :user
end
app/views/dashboards/show.html.erb
<%= render 'profile' %>
<%= render 'groups', groups: #dashboard.group %>
<%= render 'statuses/form', status: #dashboard.new_status %>
<%= render 'statuses', statuses: #dashboard.statuses %>
The Dashboard model gives you back all the pertinent information you need, and your controller gets to focus on a single entity. Beautiful!
UPDATE
In order to use your #dashboard instance variable inside of any partials, it's recommended you render it with a local, which is essentially a designated variable for use inside of a partial.
Here's a great link on how to get started passing locals to a partial.

Related

Semi-global Rails partial

Is there a better way to achieve what I'm going for?
I have a partial in the /views/shared/ folder that has all the fields that are in a form being used to send an email.
A helper method with default options to render said partial (render partial: 'shared/email_fields' locals: locals where locals is a hash of default variables).
A helper method for every form sending an email that calls the above helper method and passes in either a FormBuilder object or a string containing the beginning of the name html attribute.
The problem I'm having: Most of the email forms differ slightly which results in me having to add additional options to the locals hash and I feel like the global partial is becoming bloated. Is there some way of using a global partial in this way such that the partial doesn't become super bloated?
I've thought of having each form completely separate but that's bad for upkeep and DRY. I've thought of passing in the name of a partial to be rendered inside the global partial but some of these forms need the same options and are rendered from different controllers and I wouldn't want to put a bunch of partials that aren't global in the /views/shared/ folder. Right now, I'm just sticking with the bloated global partial.
Any help would be appreciated!
Here's how I do it. This is going to sound weird, but bear with me.
So, I have basically two forms in my applications. For a form that submits via javascript, it looks like this:
#views/shared/_remote_form.html.haml
= form_tag #presenter.form_path,
remote: true,
id: #presenter.form_id,
class: #presenter.form_classes,
data: #presenter.form_data,
method: #presenter.form_method do
.well
= #presenter.form_inner
.form-controls-container
.form-controls-wrapper
= #presenter.form_controls
As you can see, I use presenters. The presenters are instantiated in the relevant controller as a controller variable, so that the presenter is available to the partial. Something like:
class FooController < ApplicationController
def new
#presenter = NewFooFormPresenter.new(self)
render partial: 'shared/remote_form'
end
...
end
You can see that I'm passing in the controller so that the presenter is able to render various parts of the form.
All FormPresenters inherit from FormPresenterBase that has stubbed methods for each of the methods called in the form. Something like this:
class FormPresenterBase
def initialize(controller)
#controller = controller
end
def form_path
root_path
end
def form_id
'bogus-form-id'
end
def form_classes
'something-bogus'
end
def form_inner; end
def form_controls; end
...
end
That let's me bootstrap the form without throwing a bunch of errors all the time. Naturally, that stubbed form won't really work, but that's okay because each FormPresenter will override the stubbed methods with real values. So, something like:
class NewFooFormPresenter < FormPresenterBase
def form_path
new_for_form_path
end
def form_id
'new-foo-form'
end
def form_classes
'something-not-bogus'
end
# The form fields could be unique to this form. Or, I might have a set of common
# fields that I use across multiple forms. I just decide which partial has the
# correct set of fields and render it here.
def form_inner
render partial: 'new_inner_fields'
end
# The controls are also rendered from partials. Here, I want to have an okay
# button and a cancel button. So, I just call the correct partial that
# renders those. I call html_safe on the resultant string so that it renders
# correctly.
def form_controls
[:okay, :cancel].each_with_object("") do |control_sym, to_return|
render partial: "shared/form_widgets/#{control_sym.to_s}_button"
end.html_safe
end
...
end
Of course, I can get tricky with my FormPresenters. If there are families that share common methods, I can either use further inheritance or module inclusion to keep everything DRY.
So, once I have all my basic form widgets (field combinations, controls, etc.) configured as partials, I can just mix and match in my presenter to my heart's delight. And (at least for forms), I basically never have to write another partial for the rest of my life. Whenever I need a new variant, I just spin up a new FormPresenter and customize it to give me the form I desire.
Actually, there's a little bit more to it than all of that, but hopefully this gives you a sense of another way to skin the cat.
An approach is to have a separate partial for each form. Take all of the items the forms have in common and put them in a partial. You can then reference the "common items" partial within your individual form partials. Depending on how your forms are structured, you may have several "common items" partials, but that is okay. The goal is to keep the code organized and DRY.

Is there a more elegant way? (accessing a rails controller from a static home page?)

OK, this is working but I feel there is a better way to do this in Rails... I have a home page which, if you have not signed in, is not currently pulling in anything from any model or controller. It exists at /pages/home.html.erb
On that page, I want to grab the next party from my Parties model and tell the website visitor about that party. Easy enough, right?:
/app/controllers/parties_controller.rb
def nextparty
#party = Party.find(:first, :order => "begins_on")
end
Now, in my home page, I used this and it works fine:
/app/views/pages/home.html.erb
<% #PartyCont = PartiesController.new() %>
<% #party = #PartyCont.nextparty() %>
<h3>The next party is <%= #party.name %></h3>
I tried helper methods, partials, ApplicationHelper, but this was the only code that actually worked. Most of the other things I tried seemed to fail because the #Party class was not instantiated (typically the error indicated the class with a temporary name and "undefined method").
Hey, I'm happy that it works, but I feel like there is a better way in Rails. I've seen a few posts that use code like the above example and then say "But you really shouldn't ever need to do this!".
Is this just fine, or is there a more Rails-like way?
UPDATE:
I think the problem is more than just elegance... I just realized that all RSPEC tests that hit the home page are failing with:
Failure/Error: get 'home'
ActionView::Template::Error:
undefined method `begins_on' for nil:NilClass
Thanks!
You want a controller behind every view and you don't want views crossing controller boundaries in order to present information. Consider having a welcome controller (or whatever you prefer to call it). It can have an index action:
def index
#party = Party.find(:first, :order => "begins_on")
end
In config/routes.rb, make it the root controller action:
root :to => "welcome#index"
Also, to DRY that up add a .nextparty class method to the Party model and call that from both of your controller actions instead of the find method.
Your view should only show data that already was made available by your controller. You want to display a party resource, so the request should go to the parties controller. If I understand your use case correctly, than more specifically to the index method on the PartiesController.
There you should have the following code:
def index
#party = Party.find(:first, :order => "begins_on")
end
That instance method will be available in your corresponding view app/views/parties/index.html.erb
<h3>The next party is <%= #party.name %></h3>
To make this available as your homepage you will have to adjust your route as well:
config/routes.rb
root :to => "parties#index"
Your view should contain as little logic as possible and mainly be concerned with how things look.
Your controller should get data for the view ready and make sure to call the right method on the model.
All the heavy business logic should be in your model.
I think you should work through a basic introductory Rails tutorial.

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

In my model I have:
after_create :push_create
I push_create I need to render a view. I'm trying to do that like so:
def push_event(event_type)
X["XXXXX-#{Rails.env}"].trigger(event_type,
{
:content => render( :partial =>"feeds/feed_item", :locals => { :feed_item => self })
}
)
end
This angers rails as it doesn't like me rendering a view in the model but I need it there.
Error:
NoMethodError (undefined method `render' for #<WallFeed:0x1039be070>):
Suggestions? Should I render it somewhere else somehow? Or how can I render in the model to set content? Thanks
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!
I just do this:
ApplicationController.new.render_to_string(partial: 'messages/any', locals: { variable: 'value' })
Rails 5 way
In Rails 5 rendering outside a controller became pretty straightforward due to implemented render controller class method:
# render template
ApplicationController.render 'templates/name'
# render action
FooController.render :index
# render file
ApplicationController.render file: 'path'
# render inline
ApplicationController.render inline: 'erb content'
When calling render outside of a controller, one can assign instance variables via assigns option and use any other options available from within a controller:
ApplicationController.render(
assigns: { article: Article.take },
template: 'articles/show',
layout: false
)
Request environment can be tailored either through default options
ApplicationController.render inline: '<%= users_url %>'
# => 'http://default_host.com/users'
ApplicationController.renderer.defaults[:http_host] = 'custom_host.org'
# => "custom_host.org"
ApplicationController.render inline: '<%= users_url %>'
# => 'http://custom_host.org/users'
or explicitly by initializing a new renderer
renderer = ApplicationController.renderer.new(
http_host: 'custom_host.org',
https: true
)
renderer.render inline: '<%= users_url %>'
# => 'https://custom_host.org/users'
Hope that helps.
You can use ActionView directly and render partials to string without having a controller. I find that pattern useful to create models that encapsulate some javascript generation, for instance.
html = ActionView::Base.new(Rails.configuration.paths['app/views']).render(
partial: 'test',
formats: [:html],
handlers: [:erb],
locals: { variable: 'value' }
)
Then, just put your _test.html.erb in you view folder and try it out!
Rails 6.0.0 compatible answer, since I ended up on this page while searching for a solution:
lookup_context = ActionView::LookupContext.new(Rails.configuration.paths["app/views"])
renderer = ActionView::Base.new(lookup_context)
renderer.extend(Rails.application.helpers)
renderer.render \
template: "foo/bar",
formats: [:html],
handlers: [:erb],
locals: { user: User.new }
I'm fairly sure the answers you seek lie within Crafting Rails Applications where Jose Valim goes into great detail about how and why you would want to render views straight from your db
Sorry I can't be of more help yet because I've just started reading it myself tonight.
You might find some help here - it's a blog post about doing this sort of thing, albeit using different methods than yours
the "proper" way to do this is to push an object in serialized form(json), and then have the view deal with it once the event is received. Perhaps you want to use Handlebars to render the object.
Edit: I originally wrote about how, despite my answer, I was going to follow your example. But I just realized there is a HUGE gotcha with your approach when it comes to push notifications.
In your problem, you are doing push notifications to one user. For me, I was broadcasting out to a set of users. So I was going to render html with a presumption of a "current_user" and all that comes with it(eg logic, permissions, etc). This is NO BUENO as each push notification will be received by a different "current user".
Therefore, really, you need to just send back the data, and let each individual view handle it.
You should call all render methods from a controller. So, in this case, you can notify the controller that the object has been created and the controller can then render the view. Also, since you can render only once, I think you can wait for all your server side operations to complete before invoking the render.
The render methods are defined on the ActiveController class and its progeny. Inherently you do not have access to it on the model, nor is it a class method so you can't use it without an instance of the controller.
I've never tried to instantiate a controller for the express purpose of simply stringifying a partial, but if you can get your hands on a controller, render_to_string seems to be the way to go.
I will chime in by saying that if you're going down this path you're taking RoR "off the Rails". This is a violation of MVC and fundamentally poor program design.This doesn't mean I think you're a bad person :P Sometimes life drives us off the rails, so to speak.
I can't speak to the details that have driven you to do this, but I'd strongly suggest you rethink your approach.
I have created a gist for this.
I needed something similar, where the models don't necessarily (or in my case, ever) get updated via a controller, so the logic can't sit there.
Created a server-push based controller:
https://gist.github.com/4707055

Rails Sub-controllers?

I'm pretty new to Rails and have an issue which I can't quite get my
head around as to the architecturally 'correct' way of doing it.
Problem relates to what I kinda call sub-controllers. The scenario is
this:
I have a series of pages, on which is a panel of some form containing
some information (think the user panel on gitHub top right).
So, in my app, I have controllers that generate the data for the pages
and render out the responses which is fine, but when it comes to this
panel, it seems to me that you would want some sort of controller action
dedicated to generating this panel and it's view.
Question is, how do you go about doing this? How do I render a 'sub
controller' from within a view?
I would put the logic in a helper or a module. (http://api.rubyonrails.org/classes/ActionController/Helpers/ClassMethods.html)
Then render partials where you want these things displayed. (http://api.rubyonrails.org/classes/ActionView/Partials.html)
Like Herman said, if it's logic that you need generated after the controller hands off to the view (ie, the Pages controller generates a page view, but you want a customized panel) then put it in a helper. Or, call a separate method in your Pages controller before handing off to the view. Or, if it's a lot of logic, create a Module and stick it in your /lib folder. So you could have a whole Panel module with methods that generate different parts of your Panel and which are called by your controller. But if you want to call these methods from within the view, then you should use a helper instead.
I dont think a module is what is required here, modules are required for shared behaviour across a small subset of your classes.
What I think is required here is the understanding of the inheritance of ApplicationController and also layouts
so, for example, my layout might look like:
<html>
<head><title>Foo</title></head>
<body>
<%= render :partial => (current_user ? "/shared/user_widget_bar" : "/shared/login_bar") %>
<%= yield %>
</body>
</html>
Any code that i want to use for it would go in my ApplicationController since it would be shared across the majority of my app:
before_filter :generate_user_widget
def generate_user_widget
if current_user
#avatar = ...
#unread_messages = ...
end
end
I understand that it might be cleaner for it to belong in a separate controller BUT honestly, unless the code is huge, it doesn't matter and can even still be put inside a module which is then included by ActionController. However it does need to be inside ApplicationController if you consider the scope of it.
If there are more related pages, say for example, you have a Rails app that manages multiple sites and you want shared behaviour across a particular site, try creating a parent controller which has no actions and only private methods, any controllers that need to have access to those methods can inherit off it. That way you can apply before filters to all controllers which inherit off it, saving you the pain of forgetting to add one in your non-parent controllers.
e.g:
class SiteA::SiteAParentController < ApplicationController
before_filter :generate_user_widget
...
end
class SiteA::ProductController < SiteA::SiteAParentController
def index
...
end
end
well, if you really need to call a controller action from the view, you can use components. They were part of the framework, now they only exist as plugins. One such plugin that seems to be well maintained is here: http://github.com/cainlevy/components/tree/master
from its docs:
== Usage
Note that these examples are very simplistic and would be better implemented using Rails partials.
=== Generator
Running script/generator users details will create a UsersComponent with a "details" view. You might then flesh out
the templates like this:
class UsersComponent < Components::Base
def details(user_or_id)
#user = user_or_id.is_a?(User) ? user_or_id : User.find(user_or_id)
render
end
end
=== From ActionController
class UsersController < ApplicationController
def show
return :text => component("users/detail", params[:id])
end
end
=== From ActionView
<%= component "users/detail", #user %>

What is the best way to avoid code duplication between views and controllers in Ruby on Rails?

I currently have code in my ApplicationController to check if a user is logged in and has the required access to perform a given action (the tests take place in a before_filter).
I require the same functionality in the views to decide if I should be showing the admin links in a list view, but how do I best avoid duplicating code in the controllers and views?
The way I have chosen to do it now, is to have the user_can_edit_customers? essentially be a wrapper for 'can_edit_customers?' on my User class:
ApplicationController:
class ApplicationController
And then do something similar in my view helpers.
This way all functionality is encapsulated in the User model but I still need to define wrappers in my controllers and helpers, but are there smarter ways of doing this?
Note, the user stuff is only an example - this also applies to other pieces of functionality.
I would say do away with the wrappers and just call can_edit_customers? directly on the user object passed to the view.
If you want to keep them a solution might be to use helper_method in your controller.
helper_method :current_user, :can_edit_customers?
def current_user
#current_user ||= User.find_by_id(session[:user])
end
def can_edit_customers?
#current_user.can_edit_customers?
end
This way the method also becomes available in the view.
<% if can_edit_customers? -%>...<% end -%>
Just to be more direct. The helper_method "macro" in a controller causes a controller method to behave as if it's also a method in the application helper.
helper_method :current_user, :can_edit_customers?
Personally I think you should not use helpers.
I would take a different solution.
Let's say that we have Cucumber, a model, that should not be editable by some users. I create #editable? method as follows:
class Cucumber < ActiveRecord::Base
def editable?(current_user)
# Something happens here.
end
end
Note that if a page is accessible by everyone, current_user might be false.
Then, in the views you're able to do:
<%- if #cucumber.editable?(current_user) -%>
<%# Something happens here. -%>
<%- end -%>
And, in the controllers, use a filter.
The best thing about this approach is that it follows Fat Model, and enables you to easily cover your permissions with unit tests.
I think helpers are the way to do what you want. As for checking in views whether the user has or not the priviledge to do something, you could put a flag in your session data (e.g. session[:admin] = true and check that in your view.
It is common practice to make methods like logged_in? available in the controller and the views. For most cases, you don't need to push down authorization logic into the models.
Definetly go with with the approach user Hates described. Have a look at plugins like restful_authentication and acts_as_authenticated, to see how they did it.
There are several railscasts (http://railscasts.com/episodes?search=authentication) covering this topic. For instance, you could write a helper that takes a block and is then used like this:
<%- admin_user_ do %>
<%= link_to .. %>
<%= link_to .. %>
<%= link_to .. %>
<%- end %>

Resources