The following code is from a Ryan Bates' RailsCasts in which he turns the front page of a blog into a calendar, so that articles show up as links on days. The following helper module creates the Calendar. I have two questions about this code
In the day_cell
method, he uses a method called capture
. I found some docs on it but I still can't figure out how capture is working in this context. Also, what is the &callback
that's passed as an argument to capture? Would it be the same :callback
that's passed to Struct.new? If so, how does it get into capture? What is the :callback that's passed to Struct?
def day_cell(day)
content_tag :td, view.capture(day, &callback), class: day_classes(day)
end
source code
module CalendarHelper
def calendar(date = Date.today, &block)
binding.pry
Calendar.new(self, date, block).table
end
class Calendar < Struct.new(:view, :date, :callback)
HEADER = %w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday]
START_DAY = :sunday
delegate :content_tag, to: :view
def table
content_tag :table, class: "calendar" do
header + week_rows
end
end
def header
content_tag :tr do
HEADER.map { |day| content_tag :th, day }.join.html_safe
end
end
def week_rows
weeks.map do |week|
content_tag :tr do
week.map { |day| day_cell(day) }.join.html_safe
end
end.join.html_safe
end
def day_cell(day)
content_tag :td, view.capture(day, &callback), class: day_classes(day)
end
def day_classes(day)
classes = []
classes << "today" if day == Date.today
classes << "notmonth" if day.month != date.month
classes.empty? ? nil : classes.join(" ")
end
def weeks
first = date.beginning_of_month.beginning_of_week(START_DAY)
last = date.end_of_month.end_of_week(START_DAY)
(first..last).to_a.in_groups_of(7)
end
end
end
I have done my research, and I've finally unraveled the mystery.
So, a couple of things to start with; as usual the documentation isn't very clear, the capture(*args) method is supposed to grab a piece of template into a variable, but it doesn't dig deeper into explaining that you may pass variables to the grabbed piece of template, that of course comes in the form of a block
source code from Ryan Bate's Calendar Screen-cast:
<div id="articles">
<h2 id="month">
<%= link_to "<", date: @date.prev_month %>
<%= @date.strftime("%B %Y") %>
<%= link_to ">", date: @date.next_month %>
</h2>
<%= calendar @date do |date| %>
<%= date.day %>
<% if @articles_by_date[date] %>
<ul>
<% @articles_by_date[date].each do |article| %>
<li><%= link_to article.name, article %></li>
<% end %>
</ul>
<% end %>
<% end %>
</div>
In the code above, the block would be exclusively this part:
do |date| %>
<%= date.day %>
<% if @articles_by_date[date] %>
<ul>
<% @articles_by_date[date].each do |article| %>
<li><%= link_to article.name, article %></li>
<% end %>
</ul>
<% end %>
<% end %>
So, When he makes this call:
content_tag :td, view.capture(day, &callback), class: day_classes(day)
particularly:
view.capture(day, &callback)
What's happening here is that he's passing the day argument to the Block above as the |date| parameter (in the block).
What needs to be understood here, is that in the context of the Problem (making a 30-day Calendar); each day of the Month is passed to the capture method, along with the piece of template (&callback), doing so.. in consequence renders the block above for each day of a Given Month. The final step, of course is.. Placing that rendered content (for each day) as the content for the content_tag :td
A final note; Ryan is calling the capture method on a view variable, it isn't stated in the documentation either, but he does mention during the ScreenCast that he needs this view as a "proxy" to access the view, and of course the view is the only one that has access to ViewHelper methods.
So, in summary, it's very beautiful code, but it's only beautiful once you understand what it does, So, I agree is very confusing at first sight.
Hope this helps, it's the best explanation I could come up with. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With