DRY view components in Rails views - ruby-on-rails

I want to create abstract components for my views, that don't disclose the way they will be rendered.
The example is an tabbed nav box like the tabs from bootstrap
In my view I want to write s.th. like:
= tab_section(self) do
- tab 'tab1' do
%p tab1 content
= link_to example_var, "#top"
- tab 'tab2' do
%p tab2 content
= link_to 'http://example.com', "#top"
which should then be rendered to s.th. like this:
<ul>
<li>tab1</li>
<li>tab2</li>
</ul>
<div class='content'>
<div class='tab'>
<p>tab1 content</p>
<a href='#top'>this could also be an #var from the controller</a>
</div>
<div class='tab'>
<p>tab2 content</p>
<a href='#top'>http://example.com</a>
</div>
</div>
All my attempts to defer rendering of the tab 'content' failed. I created a minimal rails app demonstrating the three approaches I took.
Take a look at the application_helper.rb and the welcome#show view.
What is the correct way to do such thing?

I got some support and found the following solution:
The outer 'component' must be passed into the block to call the inner function upon:
= tab_section2 do |section|
- section.tab 'tab1' do
%p tab1 content
= link_to example_var, "#top"
- section.tab 'tab2' do
%p tab2 content
= link_to 'http://example.com', "#top"
Since we don't need to bind the block to the tab_section instance (previously done with instance_exec), we can yield the block directly.
def tab_section2(&blk)
raise "Y U NO SUPPLY block?" unless block_given?
ts = TabSection2.new(self, self)
yield(ts)
render :partial => '/tab2', locals: {section: ts}
end
The partial renders the output of the tabs render function:
%ul
- section.tabs.each do |tab|
%li= tab.name
.content
- section.tabs.each do |tab|
.tab
= tab.render.html_safe
Which is implemented as follows:
class Tab2
attr_reader :name, :blk
def initialize(name, context, &blk)
#context = context
#name = name
#blk = blk
end
def render
#context.capture_haml(&#blk)
end
end
class TabSection2
attr_reader :tabs
def initialize(context)
#context = context
#tabs = []
end
def tab(name, &blk)
raise "Y U NO SUPPLY block?" unless block_given?
#tabs << Tab2.new(name, #context, &blk)
end
end

Related

Uninitialized constant error in my controller after adding a presenter

I added this to my controller:
class HtmlreportController < ApplicationController
def index
#report = Report.new
#report_presenter = ReportPresenter.new(#report_presenter)
end
end
Then added this presenter to /app/presenters
# app/presenters/htmlreport_presenter.rb
class ReportPresenter
def initialize(report)
#report = report
end
def pass_fail(view)
arrs = ['onemon.rb','twomon.rb','threemon.rb','fourmon.rb','fivemon.rb']
arrs.each do |arr|
shortname = File.basename("#{arr}", ".rb")
newest_file = Dir.glob("ScriptResults/#{shortname}/*").max
#reporter = File.readlines("/Users/username/Automation/Code/Reports/MonthlyTracking/#{newest_file}")
if #reporter.grep(/test failed/).any?
view.concat content_tag(:div, 'FAILED', class: 'results_fail')
else
view.concat content_tag(:div, 'PASSED', class: 'results_pass')
end
end
end
end
With this in my view:
<% title "HTML Report" %>
<!-- This is where the HTML Report lies -->
<h1>HTML Report for Marines.com Daily Monitoring</h1>
<div>View the grids below for the following results:</div>
<div id="results">
<div class="results_grid">
<div class="results_title">Xbox</div>
<%= #report_presenter.show_credentials(self) %>
</div>
</div>
But, I get this error when running it: uninitialized constant HtmlreportController::Report for the line #report = Report.new
How do I get it initialized to make it recognize the functions in my presenter into my view?

Getting data out of a select box and passing it into params

I am doing this for a school project and it is not working. :(
How do I get out the selected[petowner_id] from the view and make it usable in a ruby controller?
How do I make the #selected_pet = params([petowner_id]) in the controller that comes in from the view to function? Currently it renders an error message when I try to set it. :(
I am getting very tired of it not working.
The controller from Pets controller
class PetsController < ApplicationController
# GET /pets
# GET /pets.json
def monsters
#Finds the current user
if current_user
#user = current_user
#pets_kept = [] #why?
##petowner = Petowner.find(params[:petowner][:id])
#if(params[:commit])
#end
#monster = "Eeeep"
#mypets=[]
#all_my_pets = #user.petowners
#options value = 2
#params { selected_petowner[petowner_id]}
#selectpet = params{[selected][petowner_id]}
#petowner = Petowner.find_by_id(params[:id])
#pet = Pet.find_by_id(params[:pet_id])
#Find the petowners that the user currently has
##mypets = #user.petowners
#This is my way of doing things in a C++ fashion, I don't get all ruby things
#user.petowners.each do |pet|
##selected_pet = pet.find(params[:selected])
# if pet.hp != 0
# #pets_kept << pet #Dont recall seeing the << before in ruby but for C++ statement used for cout statements
#if pet.select
# #selected_pet = pet.select
#end
end
##selected_pet = Petowner.find(params[:petowner][:selected])
#end
#selected_pet = 1 ##user.petowners.find(params[:id])
#mypets = current_user.petowners.select{|pet| pet.hp !=0}
#raise "I am here"
##selected_pet = #mypets.find(params[:id][:selected])
##mypets = #pets_kept
end
The code from the view that doesn't go back to the controller variable and set it. :(
<select id="petowner_id" name="selected[petowner_id]">
<%= #all_my_pets.each do |pet| %>
<option value="<%= pet.id %>"><%= pet.pet_name %></option>
<% end %>
</select>
Previous step from pets/monsters view that doesn't work at all from previous collection. :(
<%= form_for :petowner, :url => petowner_fights_path(#selected_pet, #pet) do |f| %>
<p>Select a pet <%#= f.collection_select(:petowner, :petowner_id, #user.petowners, :petowner_id, :pet_name) %></p>
<%= render 'monsterinfo' %>
<div class="outer"></div>
<%= f.submit "Fight Selected Monster" %>
<% end %>
You probably want params[:petowner][:petowner_id]. Definitely don't construct the select with html in a view.
By the way, it's really helpful to see all of the params passed in to a controller action. I tend to raise params.to_yaml when I need to do that.

Pass variable to a Fancybox in Rails with Content

I have this link in Rails:
<%= link_to "Add to Journal", add_post_journal_path(post), :method => :put %>
However I want transform this link to show a fancybox with the content listing my content to choose. First, I use this code:
<%= link_to "fancy", "#add_post", :class=>"fancybox" %>
but I have errors, because I want pass the actual post to fancybox, so I'm using this code: in add_post.html.erb:
<h1>Escolha o Jornal que deseja adicionar:</h1>
<ul>
<% current_user.journals.each do |journal| %>
<li><%= link_to journal.name,add_post_complete_journal_path(journal),:remote=>true %> </li>
<% end %>
</ul>
and my controller is:
def add_post
#journal_post = JournalsPosts.new
session[:post_add] = params[:id]
end
def add_post_complete
#journal_post = JournalsPosts.create(:post_id => session[:post_add],:journal_id => params[:id])
respond_with #journal_post
end
How can I transform this code to use my content in my fancybox?
Add on your action add_post the next respond with js:
def add_post
#journal_post = JournalsPosts.new
session[:post_add] = params[:id]
respond_to do |format|
format.js
end
end
Add on a file on your views add_post.js.erb with the next content:
$.fancybox('<%= escape_javascript(render(:partial => 'path_to/add_post'))%>',
{
openEffect: "fade",
closeEffect: "fade",
autoSize: true,
minWidth: 480,
scrolling: 'auto',
});
For example, you have add a partial _add_post.html.erb on your views. Now inside this partial you can write your code view:
#code for your view inside partial `add_post.html.erb`
<%= #journal_post %>
<h1>Escolha o Jornal que deseja adicionar:</h1>
<ul>
.
.
Regards!

blocks in views and refactor in rails

I have notes attribute in Product model with text "something, something else".
In views I wanted see:
<div>
<span>Something</span>
<span>Something else</span>
</div>
Also I have working code, but I want refactor with decorator(draper) or maybe use helpers.
%div
- product.notes.split(/,/).each do |e|
%span= e.strip.capitalize
In decorator:
def notes_list
model.notes.split(/,/).each do |e|
h.content_tag(:span, e.strip.capitalize)
end
end
In views:
%div
= product.notes_list
(or analog in helpers:
def notes_list(product)
product.notes.split(/,/).each do |element|
content_tag(:span, element.strip.capitalize)
end
end
call:
%div
= notes_list(product)
)
But this returns
<div>
"
["something", " something else"]
"
</div>
What is wrong?
your notes_list is returning product.notes.split(/,/)
Try
def notes_list(product)
result = product.notes.split(/,/).inject([]) do |result, element|
result << content_tag(:span, element.strip.mb_chars.capitalize)
end
result.join("\n")
end

How can I collapse this very repetitive Ruby/Rails code?

I've got two small structural issues that I'm not sure how to handle given my relative newbie-ness with RoR.
First issue: In one of my views, I have code that looks like this:
<ul style="list-style-type: circle">
<li><%= #apples.size %> apples</li>
<li><%= #oranges.size %> oranges</li>
<li><%= #bananas.size %> bananas</li>
<li><%= #grapefruits.size %> grapefruits</li>
</ul>
Is it possible to refactor this so that I only need to iterate once over some list of different kinds of fruit, and have the appropriate <li>'s be automatically generated? Edit: I forgot to add that #apples, #oranges, etc., might be nil. Is there an idiomatic way to handle that?
Second issue: In my controller, I have code that looks like this:
#apples = Apple.find(:all)
#apples.each { |apple| apple.do_stuff(:xyz) }
#bananas = Banana.find(:all)
#bananas.each = { |banana| banana.do_stuff(:xyz) }
# ... &c
As you can see, the same operation is invoked many times in exactly the same way. Is there a way to shorten this to something like [Apple.find(:all), ...].each { |fruit| ... } and have that work instead?
Thanks very much for your help!
I'd do this in a helper
def fruit_size(fruit)
list = #fruits[fruit]
return if list.empty?
content_tag(:li, "#{list.size} #{fruit}")
end
And this in the view:
<% ["apples", "oranges", "bananas", .....].each do |fruit| %>
<%= fruit_size(fruit)
<% end %>
In your controller:
#fruits = {}
["apples", "oranges", "bananas", ......].each do |fruit|
#fruits[fruit] = fruit.classify.constantize.find(:all).each {|record|
record.whatever_here
end
end
It makes sense to store all the items in a hash, #fruits, so that you don't have to use instance_variable_get and stuff.
Perhaps you also want to define that array somewhere, so that you don't have to repeat it in the controller and in the view. Let's pretend that you have a fruit model.
class Fruit < ActiveRecord::Base
FRUITS = ["apples", "oranges", "bananas", ....]
end
Then, use Fruit::FRUITS in the view and controller.
For the first part:
#li = ''
[#apples, #oranges, #bananas, #grapefruit].each{|fruit|
#li << "<li>#{fruit.size}</li>"}
<ul style="list-style-type: circle">
<%=#li%>
</ul>
You can actually do it pretty simply.
In your controller:
def whatever
#fruits = {
:apples => Apple.find(:all).each{ |a| a.do_stuff(:xyz) },
:bananas => Banana.find(:all).each{ |a| a.do_stuff(:xyz) } # ...
}
end
In your view:
<% #fruits.each |k, v| %>
<li><%= v.nil? ? 0 : v.size %> <%= k.to_s %></li>
<% end %>
Although you might want to consider whether do_stuff is something that could be done via a more complex finder, or by named scope.

Resources