Rails: Loading in array of partials - ruby-on-rails

In my Rails app I already have the following code:
<% %w(number_of_students edit_class_name tech_help).each do |modal| %>
<%= render "common/modals/#{modal}" %>
<% end %>
There will be a few more modals added into app/views/common/modals and instead of explicitly listing them out in the %w() I was wanting to loop through the common/modals directory and just render each file.

Here is what I came up with:
def render_modals
files = Dir.glob("#{Rails.root}/app/views/common/modals/*").collect { |file| File.basename(file, ".html.erb").sub("_", "") }.flatten
files.collect do |modal|
render partial: "common/modals/#{modal}"
end.join.html_safe
end

define a simple method in where is more appropriate (maybe app helper?) like this:
def modals
%w(number_of_students edit_class_name tech_help)
end
if you need these modals in a controller/model too, maybe you should define this method in an appropriate class? For example
class Modal
def self.types
%w(number_of_students edit_class_name tech_help)
end
end
Also, if you are rendering the templates often, then also define
def render_modals
modals.map do |modal| # Modals here should be the method that you just defined, example, Modal.types
render partial: "common/modals/#{modal}"
end.join
end

Related

ruby on rails know when you come from a redirect_back

I would like to know if there's any way to know when you come from a redirect_back. I would like to show a popup on my html if I come from here:
class PaymentsController < ApplicationController
def addproduct
(session[:products] ||= []) << params[:item]
redirect_back(fallback_location: "/")
end
end
There is any way?
This is a bit hacky but it can work, the definition of redirect_back accepts a fallback_location and args. So maybe you can use flash to pass an argument like:
# controller
redirect_back(fallback_location: "/", flash: { from_add_product: true } )
# view
<% if flash[:from_add_product] %>
# show popup
<% end %>
Another option is to use request.referer to check the url like:
# view or a helper
<% if request.referer && request.referer =~ /payments\/addproduct/ %>
# show popup
<% end %>
You can use request.referrer.
You can see the controller and action (method) which you come from using request[:controller] and/or request[:action].
Put it in your app/layouts/application.html.erb and you'll be able to see each referrer:
<%= request[:controller] %>
<%= request[:action] %>

Rails Partials - conditionality with locals

I have the following code in a Rails partial being used in some mailers but am not happy with my solution and have the feeling this is far from optimal.
I have an email which
From my mailer:
def the_email_i_am_sending(user, inquiry, params = {})
get_variables(inquiry) #This also provides access to my `#user` object
#contact_name = [params[:guest_last_name].to_s, " ", params[:guest_first_name].to_s].join
I always have #user but on occasion a specific partner will call our API with additional params of [:guest_last_name] and [:guest_first_name] as defined above. This allows me to define #contact_name as a separate instance variable.
When this is .present? i.e. not nil, I want to render #contact_name in a field on the email rather than the #user.login that would pull from our DB.
My mailer view then uses the following code to decide which partial it will render.
<% if #contact_name.present? %>
<%= render 'meet_your_guest_v3', tujia_guest: #contact_name %>
<% else %>
<%= render 'meet_your_guest_v3' %>
<% end %>
My solution is then to utilise this code in the partial being rendered in the mailer. It seems a little verbose but I am unsure about the correct usage of local_assigns.has_key?
<% if local_assigns.has_key?(:partner_guest) %>
<%= partner_guest %> <p>(via our partner</p>
<% else %>
<%= #user.login %>
<% end %>
Is there a better way?
You should definitely follow the advice from #Jon regarding dealing with params in your controller/mailer. Additionally you should just pass #contact_name every time to the underlying partial, regardless if it is present or not, then check only where you want to render it, if it is present. This way you would skip one conditional:
#email_view.html.erb
render 'meet_your_guest_v3', parnter_guest: #contact_name
_contact_name.html.erb
<% partner_guest.present? %>
...
A further step could be using a special decorator object, which would deal with the presentation logick. It would check wether contact_name was provided from outside or from the model and render the desired html tag for the contact_name (or it could just return it as string). See following pseudocode using the draper gem:
class MyController < ApplicationController
def send_mail
#user = User.find(...).decorate(
contact_name: [params[:guest_last_name].to_s, " ", params[:guest_first_name].to_s].join
)
MyMailer.the_email_i_am_sending(#user)
end
end
class MyMailer < ApplicationMailer
def the_email_i_am_sending(user)
#user = user
mail(to: ..., subject: ...)
end
end
class UserDecorator < Draper::Decorator
def contact_name_tag
if (contact_name.present?)
h.content_tag(:div, contact_name)
else
h.content_tag(:div, user_name)
end
end
end
#email_view.html.erb
<%= #user.contact_name_tag %>
However if the presentation logic isn't very complicated, going with a couple conditionals and perhaps extracting them into basic rails helpers is fine and using a presenter may be an overkill

Use the same partials for two controllers in two different folders

I have two controllers like this:
app/controllers/collection_controller.rb:
class CollectionController < ApplicationController
def create
#collection = Collection.new(name: params[:name])
#collection.save!
render #collection
end
end
And an inherited class:
app/controllers/enterprise/collection_controller.rb:
class Enterprise::CollectionController < ::CollectionController
def create
#collection = Collection.new(name: params[:name])
#collection.company = Company.find(params[:company])
#collection.save!
render #collection
end
end
I have two partials:
app/view/collections/_collection.json.jbuilder:
json.extract! collection, :title, :description
json.users do
json.partial! collection.user
end
app/view/collections/_user.json.jbuilder:
json.extract! user, :name, :surname
The problem is:
When I load Enterprise::CollectionController#create, I get missing template app/views/enterprise/collections/_collection ....
I want Enterprise::CollectionController to use app/view/collections/_collection.json.jbuilder instead of app/view/enterprise/collections/_collection.json.jbuilder.
I tried to do something like:
render #collection, partial: 'collections/collection', but I receive:
But I receive:
missing template for ... app/views/enterprise/users/_user ...
How can I solve this?
After you changed your render partial to
render #collection, partial: 'collections/collection'
you are not getting an error for collection partial. you are getting an error for user partial. You will have to change the way you are rendering user partial to
json.partial! "collections/user", user: collection.user
Update:
you can try append_view_path. So basically you will append to the default search locations
class Enterprise::CollectionController < ::CollectionController
before_filter :append_view_paths
def append_view_paths
append_view_path "app/views/collections"
end
end
So rails will search in app/views/enterprise/collections, app/views/shared, app/views/collections in order
You can also use prepend_view_path if you want rails to search in app/views/collections first
PS: I haven't tested this.

Why my helpers does not outputting nothing in my view?

I'm new on rails and I have a book to study them. In one practice, I created a helper in my Application Helper, the test from RSpec work fine, until I have to print the result of my helper. No show any result and no error happens.
application_helper.rb
module ApplicationHelper
def title(*parts)
unless parts.empty?
content_for :title do
(parts << "Ticketee").join(" - ")
end
end
end
end
show.html.erb
<% title(#project.name) %>
projects_controller.rb
class ProjectsController < ApplicationController
def show
#project = Project.find(params[:id])
end
end
and when I go to the show link I supposed to see "Random Project name - Ticketee", however only they show me "Ticketee".
Any help...
<% title(#project.name) %>
Means don't show to the user
<%= title(#project.name) %>
Means show to the user - notice the equals.

Getting 'form_for(#something)' to work outside of new.html.erb

I want to place my <%= form_for(#something) do |f| %> which is currently located in app/views/something/new.html -- inside multiple pages, so maybe in app/views/layouts/application.html.erb
How do I get the #something variable and the form to work properly there, or somewhere else -- since it's defined in the controller #new action of SomethingController, it only seems to be available in the appropriate new.html.erb view..
You can put the form anywhere, just provide an instance variable of #something in controller
The basic usage is here.
ThisThingsController
def show
#this_thing = foo
#that_thing = bar
end
end
# View
<%= #this_thing %>
<%= form_for #that_thing %>
Of course you can use partial to render the form, as long as you feed it with variable it needs.
Try
<%= form_for SomeThing.new do |f| %>
Without fully understanding what you are trying to accomplish, I'll make this suggestion.
Add a before filter to your ApplicationController (alternatively you could create a module and mix it in where needed). Then call the before_filter when needed. This example will always run the before filter:
class ApplicationController
before_filter :set_something
private
def set_something
#something = ... # Fill in the logic here
end
end
Then add your form where needed. You can even make it appear conditionally depending on whether #something is set.
<% if #something %>
# Form goes here
<% end %>

Resources