How can I pass multiple params in Jekyll with Liquid? - ruby

I have found a Ruby plugin for Jekyll to obfuscate an email address like this in a Jekyll webpage with Liquid
{{ site.email | mailObfuscate }}
However, I would like to pass multiple params to mailObfuscate
I have tried the following
{{ email=site.email, linkText='foo bar' | mailObfuscate }}
However, this gives an error when building my site
Liquid Warning: Liquid syntax error (line 89): Unexpected character = in "{{ email=site.email, linkText='foo bar' | mailObfuscate }}" in privacy.html
Liquid Exception: undefined method gsub' for nil:NilClass in privacy.html
Error: undefined methodgsub' for nil:NilClass
Error: Run jekyll build --trace for more information.
Running the trace gives the following error
1: from D:/Ruby26-x64/lib/ruby/2.6.0/uri/common.rb:103:in escape'
D:/Ruby26-x64/lib/ruby/2.6.0/uri/rfc2396_parser.rb:305:inescape':
undefined method `gsub' for nil:NilClass (NoMethodError)
The complete trace can be found at Pastebin
How can I pass multiple variables?

You need to modify the method to take a 2nd argument, then you can use it as the link text. Try this:
require "base64"
require "uri"
module ObfuscateMailAddress
def mailObfuscate(email_address, link_text )
base64Mail = Base64.strict_encode64(URI::encode(email_address))
# See http://techblog.tilllate.com/2008/07/20/ten-methods-to-obfuscate-e-mail-addresses-compared/
output = "<a href=\"#\" "
output += "data-contact=\"#{base64Mail}\" target=\"_blank\" "
output += "onfocus=\"this.href = 'mailto:' + atob(this.dataset.contact)\">"
output += "<script type=\"text/javascript\">document.write(atob(\"#{base64Mail}\"));</script>#{link_text}</a>"
return output
end
end
Liquid::Template.register_filter(ObfuscateMailAddress)
To pass multiple arguments in your liquid template, the syntax is a bit strange, see documentation. The string on the left side of the pipe automatically get's passed as the first argument to your ruby method, while additional arguments get passed with a colon.
{{ 'test#example.com' | mailObfuscate:'myLinkText' }}
But you also, if you are on Ruby >= 2.3, you can make your method more readable with no need for all the escape characters and better syntax highlighting in your editor if you change your method to use SQUIGGLY HEREDOC for your string definition, explicit return is not required in anycase. For Ruby < 2.3 you can still use regular HEREDOC just replace ~ with - but you just have extra indentation in your string, which is no problem anyway for rendered html.
def mailObfuscate(email_address, link_text )
base64Mail = Base64.strict_encode64(URI::encode(email_address))
ouput = <<~HTML
<a href="#" data-contact="#{base64Mail}" target="_blank"
onfocus="this.href = 'mailto:' + atob(this.dataset.contact)" >
<script type="text/javascript">
document.write(atob("#{base64Mail}"));
</script>
#{link_text}
</a>
HTML
end
And when it is called like this:
puts mailObfuscate('foo#bar.com', 'foobar')
It will render:
<a href="#" data-contact="Zm9vQGJhci5jb20=" target="_blank"
onfocus="this.href = 'mailto:' + atob(this.dataset.contact)" >
<script type="text/javascript">
document.write(atob("Zm9vQGJhci5jb20="));
</script>
foobar
</a>
As a side note, ruby style guide recommends we use snake_case for method names so you might wanna use mail_obfuscate for your method name instead.

Related

Jekyll plugin change characters

I have made my onw jekyll plugin which gives some text special css (spoiler hider).
This is my code:
class Spoiler < Liquid::Tag
def initialize(tag_name, input, tokens)
super
#input = input
end
def render(context)
output = "<div class='spoiler'>" + #input + "<div>"
return output;
end
end
Liquid::Template.register_tag('spoiler', Spoiler)
There is example how I want to use it in my markdown posts:
---
layout: post
title: "testing file"
date: 2019-09-25
category: article
---
aaaaaaaaaaa {% spoiler secret text %} bbbbbbbbbbbb
but this is how page looks like:
When I look in to generated source code, the text looks like this:
<p>aaaaaaa <div class='spoiler'>secret text </div> bbbbbbbb</p>
What should I do make jekyll plugin generate html element instead of text ?
PS: If I manually replace < by < and > by >, it works fine.
Technically, every line separated by whitespace get rendered into an HTML <p> element.
To avoid generating <p> tags automatically, explicitly wrap lines in a <div>:
<div>
aaaaaaaaaaa {% spoiler secret text %} bbbbbbbbbbbb
</div>

Pass multiple arguments to custom plugin in jekyll

I'm working with jekyll to make a website.
And I built a custom table of content plugin with Ruby.
Here is the code:
require 'nokogiri'
module Jekyll
module TOCGenerator
TOC_CONTAINER_HTML = '<ul>%1</ul>'
def toc(html,op)
toc_top_tag = "h1"
item_number = 0
toc_html = ''
sub_url = html['url']
doc = Nokogiri::HTML(html['content'])
doc.css(toc_top_tag).each do |tag|
toc_html += create_level_html(sub_url, tag['id'], tag.text)
item_number += 1
end
return '' unless item_number > 0
if 0 < item_number
toc_table = TOC_CONTAINER_HTML
.gsub('%1', toc_html)
end
end
private
def create_level_html(url, anchor_id, tocText)
link = '%3'
.gsub('%1', url)
.gsub('%2', anchor_id.to_s)
.gsub('%3', tocText)
'<li>%1</li>'
.gsub('%1', link)
end
end
end
Liquid::Template.register_filter(Jekyll::TOCGenerator)
And in some document:
<div>
{{ page | toc }}
</div>
It works well.
To enhance its feature, I would like to add some argument to render toc. So I added argument head of the function just like this.
def toc(html,option)
But when I call the function in jekyll template, an error occurs like this:
Liquid Exception: Liquid error (line 41): wrong number of arguments (given 1, expected 2) in /_layouts/default.html
I have tried {{ (three,1) | toc }}, {{ three, 1 | toc }}, {{ three | 1 | toc }} to call the function with 2 arguments but the results turned out the same way.
How do I call a function in jekyll with multiple arguments?
This answer is unlikely to be relevant to the original poster, but if anyone has come here from Google, like I did, here's how I solved it.
Plugin code:
module Jekyll
module YourFilter
def yourFilter( input, arg1, arg2 )
# your code
end
end
end
Liquid::Template.register_filter(Jekyll::YourFilter)
Tag code in your content:
{{ 'Lorem ipsum' | yourFilter: 'argument 1', 'argument 2' }}
The key thing is that there's a semicolon after the filter name in the tag code. This seems to allow the plugin to parse multiple arguments, and not just the last one.

Return just the value of an xpath - Nokogiri Ruby

I'm using xpath to get some values on a website like this
auction_page = Nokogiri::HTML open(a, "User-Agent" => theagent)
auction_links = auction_page.xpath('//iframe[contains(#src, "near")]/#src')
Which returns what I need like this
#<Nokogiri::XML::Attr:0x3fcd7bef5730 name="src" value="http://thevalue.com">
I just want to get the value, not the value or anything else. How do I do this?
I think you are looking for the .text method.
So auction_links.text should return "http://thevalue.com".
Edit:
If that doesn't work try, auction_links.first which will return an array, I'm sure the link will be inside there. ; )
For further reference, here is a great tutorial for basic Nokogiri Crawling/Parsing.
You could do this as below:
require 'nokogiri'
doc = Nokogiri::HTML::Document.parse <<-end
<a id = "foo" class="bar baz" href = "www.test.com"> click here </a>
end
doc.at_xpath("//a[contains(#class,'bar')]/#href").to_s
# => "www.test.com"
So in your case you can write:
auction_page.at_xpath('//iframe[contains(#src, "near")]/#src').to_s
# => "http://thevalue.com"

Ruby:: How to search hidden elements with mechanize

I am trying to get hidden field with mechanize in ruby and trying to click on it.
agent = Mechanize.new
agent.get('http://www.example.com/')
agent.page.link_with(:text => "More Links...")
But this gives me:
=> nil
Actually, I want to click on it:
agent.page.link_with(:text => "More Links...").click
But this is an error:
undefined method `click' for nil:NilClass
And here is my HTML code:
<div id="rld-4" class="results_links_more highlight_d links_deep" style="display: none;">
<a class="large" href="javascript:;">More Links...</a>
</div>
Mechanize currently doesn't support javascript. I'd suggest you try and figure
out what the server expects the user-agent to send and then replicate this with
Mechanize. You can use a tool like HTTPFox which is a Firefox addon that monitors the traffic between a web server and your browser. Once you have this, you can easily replicate it with mechanize. Something like this;
agent = Mechanize.new
# Doesn't work
# home_page = agent.get('http://requestb.in/')
# agent.click(home_page.link_with(:text => "Create a RequestBin"))
# => undefined method `[]' for nil:NilClass (NoMethodError)
# Works
# The javascript code just makes a POST request with one parameter
request_bin = agent.post("http://requestb.in/api/v1/bins", { "private" => "false" })
puts request_bin.body
That should probably find the link if it's really on the page, but the bigger problem is that clicking on a link with a href of 'javascript:;' doesn't do what you think it does. That's because mechanize is not a full browser with a javascript interpreter, etc.

hpricot: get image from URL and parse element

i am trying to get the exact URL of an image inside a page and then download it. i haven't yet gotten to the download point, as i am trying to isolate the URL of the image. here is the code:
#!/usr/bin/ruby -w
require 'rubygems'
require 'hpricot'
require 'open-uri'
raw = Hpricot(open("http://www.amazon.com/Weezer/dp/B000003TAW/"))
ele = raw.search("img[#src*=jpg]").first
img = ele.match("(\")(.*?)(\")").captures
puts img[1]
when i run it as it is, i receive:
undefined method `match' for #<Hpricot::Elem:0xb731948c> (NoMethodError)
if i comment out the last 2 lines and add
puts ele
i get:
<img src="http://ecx.images-amazon.com/images/I/51rpVNqXmYL._SL500_AA240_.jpg" style="display:none;" />
which is the correct portion of the page i want to parse. however, the error is when i try to get just the "http://ecx.images-amazon.com/images/I/51rpVNqXmYL._SL500_AA240_.jpg" style="display:none;" part.
i am not totally sure why it can't perform a match, as I understand the search i am running should be getting an array of the image elements and returning the first. so i assumed that i could not run the match on the entire array, so i tried
img = ele[1].match("(\")(.*?)(\")").captures
puts img
and that returns
undefined method `match' for nil:NilClass (NoMethodError)
i am lost. please excuse my ignorance, as i am just beginning to learn ruby. any help is appreciated.
Change this line:
img = ele.match("(\")(.*?)(\")").captures
To:
img = ele[:src]
The reason for the errors is that Hpricot:Elem isn't a string. Try:
ele.responde.to? :match
and you get false.
However, you could do:
ele.to_s.match("(\")(.*?)(\")").captures[1]
the secret is in the to_s

Resources