How to pass ruby variables between .html.slim files - ruby-on-rails

I have email template in which I keep and use ruby variables:
### mailer/_body.html.slim
ruby:
start_day_number = #absence.starts_on.strftime('%d').to_i.ordinalize
end_day_number = #absence.ends_on.strftime('%d').to_i.ordinalize
start_date = #absence.starts_on.strftime('#{start_day_number} of %B (%A)')
end_date = #absence.ends_on.strftime('#{end_day_number} of %B (%A)')
type = #absence.type.capitalize
status = #absence.status.capitalize
p class="details"
| Starts: <b>#{start_date}</b><br />
| Ends: <b>#{end_date}</b><br />
| Type: <b>#{type}</b><br />
| Status: <b>#{status}<b><br />
Is there any more convenient and readable way to store these variables in another .slim file and pass them to the template?
Optimally, I'd like to have them stored in layouts directory like that:
### layouts/mailer.html.slim
doctype html
html
head
meta charset="utf-8"
css:
...
body
ruby:
start_date = #absence.starts_on.strftime('#{start_day_number} of %B (%A)')
end_date = #absence.ends_on.strftime('#{end_day_number} of %B (%A)')
...
== yield
..but it didn't work though.

Decorator pattern turned out to be exactly what I needed. Thanks Tom Lord!

Related

Ruby RegEx to locate image assets in an html/erb file

My end goal is to write a script that will loop through all my app/views folders and find any image assets being used within them (jpg, png, svg, gifs) and I can't quite get it but I feel I am close but need a little assistance.
This is how I am getting all my assets
assets_in_assets = []
# I searched for image asset names in this folder
image_asset_path = './app/assets/images'
# I haven't made use the below global variables yet
assets_in_use = []
# I plan to loop through the below folders to see if and where the image
# assets are being used
public_folder = './public'
app_folder = './app'
Find.find(image_asset_path) do |path|
# returns path and file names of all files extensions recursively
if !File.directory?(path) && path =~ /.*[\.jpg$ | \.png$ | .svg$ | \.gif$]/
&& !(path =~ /\.DS_Store/)
new_path = File.basename(path) # equiv to path.to_s.split('/').last
assets_in_assets << new_path
end
end
# The above seems to work, it gives me all the asset image names in an array.
This is how i am trying read a html.erb file to find if and where images are being used.
Here is a sample of part of the page:
<div class="wrapper">
<div class="content-wrapper pull-center center-text">
<img class="pattern-stars" src="<%= image_path('v3/super/pattern-
stars.png') %>" aria-hidden="true">
<h2 class="pull-center uppercase">Built by the Obsessed People at the
Company</h2>
<p class="top-mini">Our pets needed a challenge.</p>
<p class="italicize">So we made one.</p>
<img class="stroke" src="<%= image_path('v3/super/stroke.png') %>"
aria-hidden="true">
</div>
</div>
# The assets I am expecting to find, in this small section, are:
#- pattern-stars.png
#- stroke.png
And my code (I tried two different ways, here is the first):
# My plan is start with one specific file, then expand it once the code works
lines = File.open('./app/views/pages/chewer.html.erb', 'r')
lines.each do |f|
if f =~ / [\w]+\.(jpe?g | png | gif | svg) /xi
puts 'match: ' + f # just wanted to see what's being returned
end
end
# This is what gets returned
# match: <img class="pattern-stars" src="<%= image_path('v3/super
# /pattern-stars.png') %>" aria-hidden="true">
# match: <img class="stroke" src="<%= image_path('v3/super/stroke.png')
# %>" aria-hidden="true">
Not what I was hoping for. I also tried the following:
lines = File.open('./app/views/pages/chewer.html.erb', 'r')
lines.each do |f|
new_f = File.basename(f)
puts 'after split' + new_f # I wanted to see what was being returned
if new_f =~ / [\w]+\.(jpe?g | png | gif | svg) /xi
puts 'match: ' + new_f
end
end
# This is what gets returned
# after split: pattern-stars.png') %>" aria-hidden="true">
# match: pattern-stars.png') %>" aria-hidden="true">
# after split: stroke.png') %>" aria-hidden="true">
# match: stroke.png') %>" aria-hidden="true">
And here I remain blocked. I have searched through S.O. and tried a few things but nothing I have found has helped but it could be that I implemented the solutions incorrectly. I also tried look-behind (using the single ' as a end point) and look-ahead (using a / as a starting point)
If this is a dup or similar to another question, please let me know. I'd appreciate the help (plus an brief explanation, I really want to get a better understanding to improve my skills.
(?:['"])([^'"]+\.(?:png|jpe?g|gif|svg)) seems to work in the one test case you supplied us. It relies on the image paths always being within a string as the 'this is the start of the image path' delimiter and terminates at the extension so even if the string is unclosed should stop at an appropriate place.
Using the above, I eventually got to the following solution;
Find.find(app_folder, public_folder) do |path|
if !File.directory?(path)
&& !(path =~/\.\/app\/assets\/images/)
&& !(path =~ /\.DS_Store/)
&& !(path =~ /\.\/app\/assets\/fonts/)
asset_file = File.read(path)
image_asset = asset_file.scan(/ (?:['"|\s|#])([^'"|\s|#]+\.(?:png | jpe?g |gif | svg)) /xi).flatten
image_asset.each do |image_name|
assets_in_use << [path, File.basename(image_name)]
end
end
end

Convert html to text in ROR

HTML
<p>Hello</p>
<p>this is <br></p>
<p>a <br></p>
<p>test message</p><br>
I have already tried 'strip tags' which gives me the following output :
"Hellothis is a test message"
The output I want:
Hello
this is
a
test message
html = "<p>Hello</p>
<p>this is <br></p>
<p>a <br></p>
<p>test message</p><br>"
strip_tags
strip_tags helper seems to work fine :
puts ActionController::Base.helpers.strip_tags(html)
# =>
# Hello
# this is
# a
# test message
Nokogiri
Nokogiri is included by default in Rails, so you could also use :
doc = Nokogiri::HTML(html)
puts doc.xpath("//text()").to_s
It outputs :
Hello
this is
a
test message
Convert newlines to spaces
If you want to remove newlines :
ActionController::Base.helpers.strip_tags(html).gsub(/\s+/,' ')
#=> "Hello this is a test message"
The HTML is rendered by a browser like:
Hello
this is
a
test message
This isn't quite correct though, because the HTML contains trailing <br> tags in the <p> tags, which should be a string like:
this is \n\n\n
which is normally considered a paragraph plus a new-line. But, browsers play games when rendering text in order to make it more readable, and gobble blank lines and spaces. For example, this HTML:
<p>foo</p>
<p></p>
<p></p>
<p></p>
<p>bar</p>
renders as:
foo
bar
and:
<p>foo bar</p>
renders as:
foo bar
So, you have to decide do you want to render the text using Nokogiri like the browser for readability, or do it accurately?
This does it like the browser:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<p>Hello</p>
<p>this is <br></p>
<p>a <br></p>
<p>test message</p><br>
EOT
doc.search('br').remove
text = doc.search('p').map { |p| p.text + "\n\n" }
puts text
# >> Hello
# >>
# >> this is
# >>
# >> a
# >>
# >> test message
# >>
It removes the breaks, then converts the <p> contained text by appending two new-lines.
Doing it accurately, as per how the markup shows, is a little different:
doc.search('br').map { |br| br.replace("\n") }
text = doc.search('p').map { |p| p.text + "\n\n" }
puts text
# >> Hello
# >>
# >> this is
# >>
# >>
# >> a
# >>
# >>
# >> test message
# >>
This is just a simplified way of doing it to get you started. Rails does the opposite of this in ActionView's simple_format method.
Browsers have a lot more rules used to determine when and how to display the text and their rendering can be influenced by CSS and JavaScript which won't necessarily translate to text, especially plain text.

Dashing (Ruby) Nokogiri LoadError

I've been working on a dashboard on the Dashing framework, and I'm currently trying to make a little crawler to collect specific data on Jenkins-CI, and pass it to the Number widget. Here's the crawler (it's just a stub, it counts the number of "p" elements on a stub html page):
require 'nokogiri'
require 'open-uri'
class ActiveBuilds
def initialize()
#jenkins_page = nil
#build_count = nil
end
# !STUB! Gets the jenkins page to parse to XML on Nokogiri
#jenkins_page = Nokogiri::HTML(open("http://localhost:80"))
# !STUB! Counts the number of 'p' items found on the page
#build_count = #jenkins_page.css("p").length
# !STUB! Returns the amount of active builds
def amountOfActiveBuilds
return #build_count
end
end
and for reference, not really necessary, is the HTML page:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Number Stub | Project</title>
</head>
<body>
<h1>Test</h1>
<ul>
<!-- Count these -->
<li> <div> <p>Item 1 </div>
<li> <div> <p>Item 2 </div>
<li> <div> <p>Item 3 </div>
<li> <div> <p>Item 4 </div>
<li> <div> <p>Item 5 </div>
<!-- Stop counting -->
<li> <div> Item 6 </div>
<li> <div> Item 7 </div>
</ul>
</body>
</html>
and now, the jobs/sample.rb file from dashing, modified (the only thing that matters is the builds/valuation stuff):
require './ActiveBuilds.rb'
active_builds = ActiveBuilds.new
current_valuation = active_builds.amountOfActiveBuilds
current_karma = 0
SCHEDULER.every '2s' do
last_valuation = current_valuation
last_karma = current_karma
current_karma = rand(200000)
send_event('valuation', { current: current_valuation, last: last_valuation })
send_event('karma', { current: current_karma, last: last_karma })
send_event('synergy', { value: rand(100) })
end
The thing is, before I had it working, it would get the page on localhost, count the number of "p" items and print it on a file, and then the dashing file would read it and display it correctly, but it wasn't updating the value on the dashboard unless I'd restart it, which defeats the purpose of this framework.
now to the errors:
When attempting to compile sample.rb (the dashing file):
$ ruby sample.rb
sample.rb:12:in '<main>': uninitialized constant SCHEDULER (NameError)
When attempting to run the dashing server:
$ dashing start
/home/yadayada/.rvm/gems/ruby-2.2.0/gems/backports-3.6.4/lib/backports/std_lib.rb:9:in 'require': cannot load such file -- nokogiri (LoadError)
from /home/yadayada/.rvm/gems/ruby-2.2.0/gems/backports-3.6.4/lib/backports/std_lib.rb:9:in 'require_with_backports'
from /home/yadayada/Desktop/dashing/project/jobs/ActiveBuilds.rb:2:in '<top (required)>'
(...)
I could also post the HTML/CSS/CoffeScript components of the Number widget, but I believe the problem lies on the sample.rb, and the Number widget is completely default.
In case the code wasn't clear enough, what I'm trying to do is to get the localhost page, count the number of "p" items (later it'll be the active builds when I switch to jenkins, didn't switch yet because i'm dealing with the certificates), then send it over to sample.rb, which will get the data and update it every 2 seconds on the dashboard display.
Any suggestions are welcome! Thanks in advance!
Found the solution:
uninstall/reinstall nokogiri gem (without sudo)
put my crawler into the lib folder and require it inside the jobs
on the job itself, placed everything into the SCHEDULER function, like this:
# This job provides the data of the amount of active builds on Jenkins using the Number widget
# Updates every 2 seconds
SCHEDULER.every '2s' do
# Invokes the crawlers from the lib folder
Dir[File.dirname(__FILE__) + '/lib/*rb'].each { |file| require file }
# Create the ActiveBuilds reference
builds = ActiveBuilds.new
# Attributes the amount of active builds to the current valuation
current_valuation = builds.get_amount_of_active_builds
# Pass the current valuation to the last to present the change percentage on the dashboard
last_valuation = current_valuation
# Sends the values to the Number widget (widget id is valuation)
send_event('valuation', { current: current_valuation, last: last_valuation })
end

Is there a way of iterating through a specific XML tag in Ruby?

Is it possible to iterate over a specific XML tag in Ruby? In my case I want iterate over the desc tag in the following XML code:
<desc>
<id>2408</id>
<who name="Joe Silva">joe#silva.com</who>
<when>Today</when>
<thetext>Hello World</thetext>
</desc>
<desc>
<id>2409</id>
<who name="Joe Silva2">joe2#silva.com</who>
<when>Future</when>
<thetext>Hello World Again</thetext>
</desc>
So far, here is the code I use:
xml_doc = agent.get("www.somewhere.com/file.xml")
document = REXML::Document.new(xml_doc.body);
# iterate over desc here
I want to iterate over each desc tags so that I get the following output:
commentid : 2408
name : Joe Silva
who : joe#silva.com
bug_when : Today
thetext : Hello World
commentid : 2409
name : Joe Silva2
who : joe2#silva.com
bug_when : Future
thetext : Hello World Again
Any suggestions?
Nokogiri example that includes the name attribute for the who node:
require 'nokogiri'
doc = Nokogiri.XML '
<root>
<desc>
<id>2408</id>
<who name="Joe Silva">joe#silva.com</who>
<when>Today</when>
<thetext>Hello World</thetext>
</desc>
<desc>
<id>2409</id>
<who name="Joe Silva2">joe2#silva.com</who>
<when>Future</when>
<thetext>Hello World Again</thetext>
</desc>
</root>
'
doc.css("desc").each do |desc|
puts "commentid : #{desc.css("id").text}"
puts "name : #{desc.css("who").attribute("name")}"
puts "who : #{desc.css("who").text}"
puts "bug_when : #{desc.css("when").text}"
puts "the text : #{desc.css("thetext").text}"
end
I'd also recommend using the Nokogiri gem. Something like this ought to work:
require 'open-uri'
require 'nokogiri'
# fetch and parse the document
doc = Nokogiri::HTML(open('www.somewhere.com/file.xml'))
# search with css selectors
puts doc.at('desc id').text
# search by xpath
puts doc.at_xpath('//desc/id').text
# to iterate over a specific tag
doc.css('desc').each do |tag|
puts tag.css('id').text
# ...
end

How to write a channel.html file in Rails (for Facebook)

According to the FB SDK I must include a channel file with the appropriate headers.
Being a major NOOB and a Rails not PHP developer I have no idea how to do this.
Here is the example they provide for php:
<?php
$cache_expire = 60*60*24*365;
header("Pragma: public");
header("Cache-Control: max-age=".$cache_expire);
header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$cache_expire) . ' GMT');
?>
<script src="//connect.facebook.net/en_US/all.js"></script>
I want to know how do I do the same thing in Rails 3
I got tired of polluting my routes.rb file in every facebook connected app so I wrapped a rack handler that gives the correct channel.html response in a Rails Engine and published it as a gem. You can simply include the 'fb-channel-file' gem in your Gemfile and it will be automatically mounted at /channel.html
https://github.com/peterlind/fb-channel-file
Inside your controller:
cache_expire = 1.year
response.headers["Pragma"] = "public"
response.headers["Cache-Control"] = "max-age=#{cache_expire.to_i}"
response.headers["Expires"] = (Time.now + cache_expire).strftime("%d %m %Y %H:%I:%S %Z")
render :layout => false, :inline => "<script src='//connect.facebook.net/en_US/all.js'></script>"
Use the response.headers hash in your controller. Docs
Example from your example
cache_expire = 60*60*24*365
response.headers["Pragma"] = "public"
response.headers["Cache-Control"] = "max-age=#{cache_expire}"
response.headers["Expires"] = ... # I'll leave this one to you.
# (Or ask another Q.)
# gmdate('D, d M Y H:i:s', time()+$cache_expire) . ' GMT');
I simply added 'channel.html' to my public directory and inserted this one line in it:
<script src="//connect.facebook.net/en_US/all.js"></script>

Resources