Semi-global Rails partial - ruby-on-rails

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.

Related

Which controller should I put #notifications in?

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.

How to make a model aware of its controller in Rails?

I am making a Rails application, and i would like to be able use a model object passed to a view to get the URL of some action on this object, like this, for example:
link_to object.public_send(attribute),
{ :controller => object.controller_path,
:action => :show,
:id => object.id }
What would be a good way to do this? Can it be done with a decorator like Draper? Are there some examples online?
Update. I have thought about this and decided that a decorator is not a good place to keep controller information. It is not decorator's responsibility. A decorator should only know to render formatted data with markup. For now i have created a module called Accessor where i try to mix models with controller and routing awareness. I still wonder if there is a better way to do.
If you don't mind having another instance variable on your view, you can implement this using a very simple class (no need for decorators).
class MyRouter
def initialize(controller, object)
#controller = controller
#object = object
end
def url_for(action_name)
controller.url_for(object, :action => action_name)
end
end
On your controllers:
class AController
def edit
#router = MyRouter.new(self, object)
render 'shared_view'
end
end
class BController
def edit
#router = MyRouter.new(self, object)
render 'shared_view'
end
end
And on your shared view:
<%= #router.url_for(:show) # Varies with the controller that rendered the view %>
Of course, this assumes that the controller you want as target is the same controller that renders the view, which might not be true. Still, using this pattern you can accommodate a more complex logic that suits your needs (having multiple Router classes, for instance), without having to change the view.
I've found a very interesting solution in Objects on Rails by Avdi Grimm: Exhibits for REST. In short, his idea is to apply multiple Ruby's SimpleDelegators as decorators with various functions.

Rails Test helper method accepting ActionView::Helpers::FormBuilder object

I'm trying to write a test for a helper method that accepts a form helper object, is there a way to create a form object within the test?
/app/views/blahs/edit.html.erb
<% form_for :blahs do |blah| %>
<%= my_helper_method(blah) %>
<% end %>
/app/helpers/blahs_helper.rb
def my_helper_method(blah)
#
# blah is ActionView::Helpers::FormBuilder
# do something with the form object here
#
end
So in my test case how do I create the form object? I'm still on Rails 2.3.9.
Thanks in advance
try using it with a partial first, then get fancy with a helper maybe? Unless you are doing some fudged up "Model" type stuff, a partial is a great way to reuse form code and easier to test.
helpers are for "View logic" in a way. partials are for displaying form fields, even ones that repeat.
It could help to see what you are trying to do in a helper that you can't in a partial.
In theory you could require 'action_view/helpers/form_helper' (which lives in actionpack) in your helper spec which will make the method form_for available.
However I am with pjammer that you should question whether the logic you're putting in your helper_method really belongs there. If you tell us what you're trying to accomplish in helper_method we may be able to give you alternatives that don't require to pass the form object, which is probably a better approach.

How can I create a "partial" that has its own action?

In my rails application, I render a partial on multiple pages, and in that partial is a variable. So currently, lets say I have 5 pages that render :partial => "partialname", and inside of partialname is #variable.
Can I have it so that partialname has its own action with #variable instantiated inside, rather than having #variable be called 5 times from each action that renders the partial?
Thanks!
I would create a before_filter on all the methods that need the common behavior.
But if you really want the partial to have its own "action," make a helper method that does whatever "action-y" things you want and then renders the partial. That works out to essentially the same thing. I've done this before to make a template-type partial that contains various pieces of data that need processing.
Rails Sub-controllers?
See my answer on this.
Very similar method here, using before filters either using controller inheritance or modules when needed.
So, is this a problem of code running 5 times per request that you'd rather not? Like, you've got a partial and in it is:
#my_var = MyModel.some_expensive_method
If so, you could just cache the result in the model:
def cached_some_expensive_method
#some_expensive_method ||= some_expensive_method()
end
you could load #variable from the view:
&lt% #variable = Variable.find(:whatever) %>
but some consider this bad practice in not adhering to strict MVC. This does have the benefit of supporting fragment caching out of the box:
&lt% cache({:variable_id => :whatever}) do %>
&lt% #variable = Variable.find(:whatever) %>
. . .
&lt% end %>
Is there a common model that's being rendered in the main views that you could delegate the variable access to?
&lt%=h #model.variable %>

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 %>

Resources