Using Nokogiri to generate static header list in Slate/Middleman - ruby

I'm inexperienced with middleman and ruby, but I've been trying to get Slate working so it generates a side navigation/list of header during build time instead of client side using javascript. The problem I am running into is getting the code to include the headers from partials.
An example of the directory structure:
Source
+--config.rb
+--includes
+--file.md
+--otherfile.md
+--index.html
+--layouts
+--layout.erb
Gist of layout and config.rb
Config.rb snippet for this:
require 'nokogiri'
helpers do
def toc_data(page_content)
html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content)
# get a flat list of headers
headers = []
html_doc.css('h1, h2, h3').each do |header|
headers.push({
id: header.attribute('id').to_s,
content: header.content,
level: header.name[1].to_i,
children: []
})
end
[3,2].each do |header_level|
header_to_nest = nil
headers = headers.reject do |header|
if header[:level] == header_level
header_to_nest[:children].push header if header_to_nest
true
else
header_to_nest = header if header[:level] == (header_level - 1)
false
end
end
end
headers
end
end
Layout snippet for this:
<ul id="toc" class="toc">
<% toc_data(page_content).each do |h1| %>
<li>
<%= h1[:content] %>
<ul class="toc-section">
<% h1[:children].each do |h2| %>
<li>
<%= h2[:content] %>
<ul class="toc-submenu">
<% h2[:children].each do |h3| %>
<li>
<%= h3[:content] %>
</li>
<% end %>
</ul>
</li>
<% end %>
</ul>
</li>
<% end %>
</ul>
...
<div class="page-wrapper">
<div class="content">
<%= page_content %>
<% current_page.data.includes && current_page.data.includes.each do |include| %>
<%= partial "includes/#{include}" %>
<% end %>
</div>
</div>
Currently, only headers from the index.html file are getting populated and nothing from the included partials. I believe I may need the existing helper to occur post build similar to what is described in the Middleman docs for sitemaps using a ready helper. I believe I have to make another change to the config code so that it captures additional content outside of the page_content, but I'm not sure what that is due to lack of familiarity. Any pointers would be appreciated.
Edit: After looking into the middleman basics docs, there appear to be two helpers from the Padrino framework that I could make use of: capture_html and concat_content. I'm trying to find where the helper page_content is defined to get extra context for the specific changes I'm making.

Not familiar with that framework but looks like toc_data(page_content) only looks at the main content but not at the current_page.data.includes partials.
So guess you need to pass the partial to your toc_data function as well.
Maybe this works?
<%
full_content = page_content
current_page.data.includes && current_page.data.includes.each do |include|
full_content += partial("includes/#{include}")
end
toc_data(full_content).each do |h1|
%>
...
<% end %>
Hope that helps.

In order to concatenate the current page data with partials with the page_content, use the code below. This also changes what all is needed to yield a complete page.
<%
if current_page.data.includes
current_page.data.includes.each do |include|
page_content += partial("includes/#{include}")
end
end
%>
...
<%= page_content %>

Related

How to output variable in sinatra without <%=

In Rails, there exist such helper as "concat" to output variables inside <% %> block for erb remplates. Which helper can I use in Sinatra to perform the same action, without using <%= %> block ? I mean, something like
<%
#code
concat "This should be rendered in HTML, not in console"
#code
%>
EDIT
The code in the view is something like this (yep, too much logic, but this is my first app in Ruby):
<% Dir.glob('uploaded/*').select do |entry| %>
<div class="singleFileItem">
<% if File.directory? entry %>
<img src="images/folder.png">
<% else
case entry.to_s.split(".")[1]
when "doc","docx" %>
<img class="pictogram" src='images/word.png'>
<% when "xls","xlsx" %>
<img class="pictogram" src='images/excel.png'>
<% when "pdf" %>
<img class="pictogram" src='images/pdf.png'>
<% when "png", "jpg", "jpeg" %>
<img class="pictogram" class="imageRaw" src="<%= entry.to_s %>">
<% else %>
<% end
end %>
<br>
<span class="subFileText">
<%= entry.to_s.split("/")[1][0..14] %>...
</span>
</div>
<% end %>
Thanks, guys, I have finally found it.
I have extended the app.rb file with
set :erb, :outvar => '#output_buffer'
def concat(text)
#output_buffer << text
end
And it works. Just type in the .erb view
<% concat "Text that should be added to render" %>
And you all done. Hope, this will help to someone with similar question

Using carmen gem with rails

What I am trying to do is show the 2 letter abbreviation code of the countries, i.e 'US'.
After the country is selected I need to show its state or province.
but I am having a problem.
My code looks like
<%= f.select :country_code, region_options_for_select(only_us_and_france) %>
and define this in helper:
def only_us_and_france
Carmen::Country.all.select{|c| %w{US FR}.include?(c.code)}
end
I am using Rails 4.1.0.
I have solved this by :
Step1: Generated migration
rails g migration addStateCodeFieldToAccounts state_code:string
Step2: Define a method inside controller
def subregion_options
render partial: 'subregion_select'
end
Step3: Declare in routes
resources :accounts do
collection do
get 'subregion_options'
end
end
Step4: In view
<div class="input-control select state_input" data-role="input-control">
<%= f.select :country, region_options_for_select(only_us_and_france) %>
</div>
<div class="input-control select state_input" data-role="input-control">
<%= render partial: 'subregion_select', locals: {parent_region: f.object.country} %>
</div>
Step5: Make partial subregion_select
<div id="account_state_code_wrapper">
<% parent_region ||= params[:parent_region] %>
<% country = Carmen::Country.coded(parent_region) %>
<% if country.nil? %>
<em>Please select a country above</em>
<% elsif country.subregions? %>
<%= subregion_select(:order, :state_code, parent_region) %>
<% else %>
<%= text_field(:order, :state_code) %>
<% end %>
</div>
Step6: In my js file written this
$('select#account_detail_country').change(function(){
selectWrapper = $('#account_state_code_wrapper')
countryCode = $(this).val()
url = "/account_details/subregion_options?parent_region="+countryCode
console.log(url)
selectWrapper.load(url)
});
yeah and it works :) Hope this will help you.

DocPad: Empty or Missing YAML Elements

I have the following in one of my Layout files...
<% for link in #document.nextlink[0..0]: %>
<% if (link.name): %>
<ul class="actions">
<li><%- "#{link.helptext}" %></li>
</ul>
<% end %>
<% end %>
This works great. Unless, nextlink is not in my YAML at all. But that's what I want. Sometimes I want to have that button called Next and sometimes I do not.
if nextlink is not in my YAML, the DocPad compiler throws an error.
Any help would be much appreciated.

Bring code from view into a popup in Rails

I want to bring this code from my view html into a popup when a link is clicked
Is that possible in Ruby on Rails? I already have the pop up working but I'm wondering about the code to show just the comments:
<div class = "comments"><% if post.comments.exists? %>
<% post.comments.each do |comment| %>
<%= image_tag("http://www.gravatar.com/someavatarlink %) <!-- Retrieves Gravatar -->
<%= link_to comment.user.name, comment.user %>
<span class="timestamp"><%= time_ago_in_words(comment.created_at) %> ago</span>
<span class="content2"><%= comment.comment_content %></span>
<% end %>
<% end %></div>
Added Ajax call to _comment_form.html.erb
<%= link_to "Link", comment, :remote => true %>
Comments
<% end %></div></div>
<div id ="modal" class = "comments"><% if post.comments.exists? %>
<% post.comments.each do |comment| %>
<%= link_to comment.user.name, comment.user %>
<span class="timestamp"><%= time_ago_in_words(comment.created_at) %> ago</span>
<span class="content2"><%= comment.comment_content %></span>
<% end %>
<% end %></div>
Added def show into comments controller
class CommentsController < ApplicationController
def new
#post = post.new(params[:post])
end
def show
#comment = Comment.find(params[:id])
respond_to do |format|
format.js
end
def create
#post = post.find(params[:micropost_id])
#comment = Comment.new(params[:comment])
#comment.post = #post
#comment.user = current_user
if #comment.save
redirect_to(:back)
else
render 'shared/_comment_form'
end
end
end
Created show.erb.js and put it into 'comments' and 'shared' folders
$("#popup").html('<%= escape_javascript(render "comments") %>');
Then finally wrote my partial which is in comments/_comment.html.erb
<% if post.comments.exists? %>
<% post.comments.each do |comment| %>
<%= link_to comment.user.name, comment.user %>
<span class="timestamp"><%= time_ago_in_words(comment.created_at) %> ago</span>
<span class="content2"><%= comment.comment_content %></span>
<% end %>
<% end %>
1. Ajax call
To retrieve the data you use an Ajax call.
<%= link_to "Link", comment, :remote => true %>
Rails will evaluate these requests and will look for a .js view first (it will use .html if it does not exist).
Make also sure that the controller accepts requests to .js like
def show
#comment = Comment.find(params[:id])
respond_to do |format|
format.js
end
end
2. write js view
Add a show.erb.js view to your Comments . This is a JavaScript file with ERB evaluation.
In this template use your js popup code and tell it to fill a div with your html code like so:
$("#popup").html('<%= escape_javascript(render #comment) %>');
This will render the comment. The only thing we need then is a partial to render the html of the comment.
3. write partial for html
Write a partial for the view part you want to have in the popup. This can then be used in a normal html view or the js view. To make it work with the code above call it _comment.html.erb
To know more about partials you can check the guides here:
http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

Ruby on Rails 3 : Playing with views

I am sorry for my bad english. I just try to description my question. :)
I have an application layout that have a yield for display post in body. I have another yield :footerpost3 for display title of recent post on the footer.
When I in localhost:3000, the yield :footerpost3 display a recent of title correctly. but when i am click a post link, which is the url is localhost:3000/posts/3, the yield :footerpost3 display nothing.
Here is my code:
app/views/layout/application.html.erb
<!-- begin footer comment widget -->
<div class="footer_list widget_recent_comments">
<div class="title"><h5>Artikel Terkini</h5></div>
<%= yield :footerpost3 %>
</div>
<!-- end footer comment widget -->
app/views/store/index.html.erb
<% content_for :footerpost3 do %>
<% #postsMain.each do |dopostAll| %>
<div class="entry">
<ul>
<li class="recentcomments"><%= link_to dopostAll.title, dopostAll %></li>
</ul>
</div>
<% end %>
<% end %>
i hope my question is easy to understand.. :)
Looks like your root url is stores#index .
You must be initializing #postsMain in the stores#index action and generating the content_for footerpost3 in stores/index.html.erb.
When you click on a post, you will be taken to posts#show page. So you have to initialize #postsMain even in posts#show action and generate the content for footerpost3 even in posts/show.html.erb
The answer is there in your question. You are defining the "content for" footerpost3 in that block, which exists in index.html.erb. When you're on /posts/3, index.html.erb is not rendered, but rather show.html.erb is.
To solve this, you'd need to add the content in the show.html.erb template as well.
You could solve this in multiple ways. Using nested layouts would be one. For example, you might create a posts layout at app/views/layout/posts.html.erb, like so:
<% content_for :footerpost3 do %>
<% #postsMain.each do |dopostAll| %>
<div class="entry">
<ul>
<li class="recentcomments"><%= link_to dopostAll.title, dopostAll %></li>
</ul>
</div>
<% end %>
<% end %>
<%= render :file => 'layouts/application' %>
In this way, all the views of your PostsController would use this layout, which simply adds your footer content, then renders the application_layout.

Resources