How to find the body and the subject in a mailto link? - ruby

I have this mailto link :
mailto:email#address.com?&subject=test&body=type%20your&body=message%20here
I would like to find to, subject, body.
Actually I use :
uri = URI('mailto:email#address.com?&subject=test&body=type%20your&body=message%20here')
<URI::MailTo mailto:email#address.com?&subject=test&body=type%20your&body=message%20here>
I have :to with that :
uri.to
but I can not extract subject and body.
Do you know how to do it ?

You can use URI::MailTo#headers which returns an array of arrays:
uri.headers
#=> [[], ["subject", "test"], ["body", "type%20your"], ["body", "message%20here"]]
However, your mailto link is slightly broken. It should look like this:
uri = URI('mailto:email#address.com?subject=test&body=type%20your%0D%0Amessage%20here')
# ^ ^
# no '&' here newline as %0D%0A
That gives:
uri.headers
#=> [["subject", "test"], ["body", "type%20your%0D%0Amessage%20here"]]
Which can be accessed via assoc:
uri.headers.assoc('subject').last
#=> "test"
Or be converted to a hash:
headers = uri.headers.to_h
#=> {"subject"=>"test", "body"=>"type%20your%0D%0Amessage%20here"}
To get decoded values:
URI.decode_www_form_component(headers['body'])
#=> "type your\r\nmessage here"

Related

Why am I not getting same output in Ruby

I'm not getting the consistent output in two cases:
Scenario 1:
humen = {"hand" => 1, "eye" => 2, "head" => 3, "hair"=>4}
puts "enter any body part name"
internal = gets.chomp.downcase.to_s
body = humen[internal]
puts body
#if input is "eye", it comes out 2
Scenario 2:
humen = {hand:1, eye:2, head:3, hair:4}
puts "enter any body part name"
internal = gets.chomp.downcase.to_s
body = humen[internal]
puts body
I see nothing in irb console. Can anyone please explain why that's the case?
keys are symbol in second case -
{:hand=>1, :eye=>2, :head=>3, :hair=>4}
whereas internal is a string.
humen[internal] is expecting the string assigned to internal to be present in hash humen which is not the case.
:hand != 'hand'
You should convert the string to symbol by:
humen[internal.to_sym]
String#to_sym converts a string into a symbol.

Extract url params in ruby

I would like to extract parameters from url. I have following path pattern:
pattern = "/foo/:foo_id/bar/:bar_id"
And example url:
url = "/foo/1/bar/2"
I would like to get {foo_id: 1, bar_id: 2}. I tried to convert pattern into something like this:
"\/foo\/(?<foo_id>.*)\/bar\/(?<bar_id>.*)"
I failed on first step when I wanted to replace backslash in url:
formatted = pattern.gsub("/", "\/")
Do you know how to fix this gsub? Maybe you know better solution to do this.
EDIT:
It is plain Ruby. I am not using RoR.
As I said above, you only need to escape slashes in a Regexp literal, e.g. /foo\/bar/. When defining a Regexp from a string it's not necessary: Regexp.new("foo/bar") produces the same Regexp as /foo\/bar/.
As to your larger problem, here's how I'd solve it, which I'm guessing is pretty much how you'd been planning to solve it:
PATTERN_PART_MATCH = /:(\w+)/
PATTERN_PART_REPLACE = '(?<\1>.+?)'
def pattern_to_regexp(pattern)
expr = Regexp.escape(pattern) # just in case
.gsub(PATTERN_PART_MATCH, PATTERN_PART_REPLACE)
Regexp.new(expr)
end
pattern = "/foo/:foo_id/bar/:bar_id"
expr = pattern_to_regexp(pattern)
# => /\/foo\/(?<foo_id>.+?)\/bar\/(?<bar_id>.+?)/
str = "/foo/1/bar/2"
expr.match(str)
# => #<MatchData "/foo/1/bar/2" foo_id:"1" bar_id:"2">
Try this:
regex = /\/foo\/(?<foo_id>.*)\/bar\/(?<bar_id>.*)/i
matches = "/foo/1/bar/2".match(regex)
Hash[matches.names.zip(matches[1..-1])]
IRB output:
2.3.1 :032 > regex = /\/foo\/(?<foo_id>.*)\/bar\/(?<bar_id>.*)/i
=> /\/foo\/(?<foo_id>.*)\/bar\/(?<bar_id>.*)/i
2.3.1 :033 > matches = "/foo/1/bar/2".match(regex)
=> #<MatchData "/foo/1/bar/2" foo_id:"1" bar_id:"2">
2.3.1 :034 > Hash[matches.names.zip(matches[1..-1])]
=> {"foo_id"=>"1", "bar_id"=>"2"}
I'd advise reading this article on how Rack parses query params. The above works for your example you gave, but is not extensible for other params.
http://codefol.io/posts/How-Does-Rack-Parse-Query-Params-With-parse-nested-query
This might help you, the foo id and bar id will be dynamic.
require 'json'
#url to scan
url = "/foo/1/bar/2"
#scanning ids from url
id = url.scan(/\d/)
#gsub method to replacing values from url
url_with_id = url.gsub(url, "{foo_id: #{id[0]}, bar_id: #{id[1]}}")
#output
=> "{foo_id: 1, bar_id: 2}"
If you want to change string to hash
url_hash = eval(url_with_id)
=>{:foo_id=>1, :bar_id=>2}

How can I parse out elements in a "< tag >"?

I have a string:
string = <RECALL>first_name</RECALL>, I'd like to send you something. It'll help you learn more about both me and yourself. What is your email?"
I want to pull out the value "first_name" of the tag <RECALL>.
I used gem crack, but it doesn't behave as I expected:
parsed = Crack::XML.parse(string) =>
{"RECALL"=>"first_name, I'd like to send you something. It'll help you learn more about both me and yourself. What is your email?"}
Maybe XML parsing isn't the right way. What is the way so that I could get the following, desired behavior, instead?
{"RECALL"=>"first_name"}
Does not look like valid XML to me. I would just try to use an REGEXP here:
string = "<RECALL>first_name</RECALL>, I'd like to send you something..."
/<RECALL>(.*)<\/RECALL>/.match(string)[1]
#=> "first_name"
Here's two ways you could get the content of the tags:
string = "<RECALL>first_name</RECALL>"
firstname = string[/<RECALL>([^<]+)</, 1]
firstname # => "first_name"
Parsing strings containing tags gets tricky. It's doable for simple content, but once tags are nested or additional < or > show up, it gets a lot harder.
You can use a trick using an XML parser:
require 'nokogiri'
string = "foo <RECALL>first_name</RECALL> bar"
doc = Nokogiri::XML::DocumentFragment.parse(string)
doc.at('RECALL').text # => "first_name"
Note that I'm using Nokogiri::XML::DocumentFragment.parse. That tells Nokogiri to only expect a partial XML document and relaxes a lot of its normally strict XML rules. Then I can tell the parser to find the <RECALL> tag and grab its contained text.
...wondering if there's a way to extract it (I use Crack to extract it, but it only works if the <tag> is at the end of the string.
This pattern matches mid-string:
str = "foo <RECALL>first_name</RECALL> bar"
str[%r!<RECALL>([^<]+)</RECALL>!, 1] # => "first_name"
This pattern fails if the tag is not at the end of the string:
str[%r!<RECALL>([^<]+)</RECALL>\z!, 1] # => nil
And succeeds if it is at the end of the string:
str = "foo <RECALL>first_name</RECALL>"
str[%r!<RECALL>([^<]+)</RECALL>\z!, 1] # => "first_name"
This is one place where a regexp pattern makes it easier to do something than using a parser.
Using a parser:
require 'nokogiri'
Normally we don't care where a tag occurs in a DOM, but if it's important we can figure out where it is in relation to the other tags. It won't always be this straightforward though:
This returns nil if the tag isn't at the end of the string/DOM:
str = "foo <RECALL>first_name</RECALL> bar"
doc = Nokogiri::XML::DocumentFragment.parse(str)
recall_node = doc.at('RECALL')
recall_node == doc.children.last ? doc.at('RECALL').text : nil # => nil
This returns the text of the node because it is at the end of the DOM:
str = "foo <RECALL>first_name</RECALL>"
doc = Nokogiri::XML::DocumentFragment.parse(str)
recall_node = doc.at('RECALL')
recall_node == doc.children.last ? doc.at('RECALL').text : nil # => "first_name"
This works because every node in a document has an identifier and we can ask whether the node of interest matches the last node in the DOM:
require 'nokogiri'
doc = Nokogiri::XML::DocumentFragment.parse("<node>first_name</node> text")
# => #(DocumentFragment:0x3ffc89c3d3e8 {
# name = "#document-fragment",
# children = [
# #(Element:0x3ffc89c3cf9c {
# name = "node",
# children = [ #(Text "first_name")]
# }),
# #(Text " text")]
# })
doc.at('node').object_id.to_s(16) # => "3ffc89c3cf9c"
doc.children.last.object_id.to_s(16) # => "3ffc89c3cec0"
doc = Nokogiri::XML::DocumentFragment.parse("<node>first_name</node>")
# => #(DocumentFragment:0x3ffc89c345cc {
# name = "#document-fragment",
# children = [
# #(Element:0x3ffc89c342c0 {
# name = "node",
# children = [ #(Text "first_name")]
# })]
# })
doc.at('node').object_id.to_s(16) # => "3ffc89c342c0"
doc.children.last.object_id.to_s(16) # => "3ffc89c342c0"

How to insert variables in an url?

I have this code for send a request to an url, and I wanted to place on the url two variables :
talksList = open('http://yolo.com/?action=cp_list&id=#{variable1}&key=#{variable2}')
But when I insert my variables like this, it doesn't work. Can you help me ?
Thanks in advance.
As #YuHao said, you're trying to interpolate a variable into a non-interpreted string. But you have a bigger long-term problem.
Don't try to inject unencoded variables into a URL. While it will work, you run the risk of generating nonsensical URLs, which a browser would accept, but code won't. Instead, use the appropriate tools to modify the URL, which will maintain appropriate encoding for you.
Here's an example using URI:
require 'uri'
variable1 = 'foo'
variable2 = 'bar'
uri = URI.parse('http://yolo.com/?action=cp_list')
params = URI.decode_www_form(uri.query)
params << ['id', variable1]
params << ['key', variable2]
uri.query = URI.encode_www_form(params)
uri.to_s # => "http://yolo.com/?action=cp_list&id=foo&key=bar"
You can do the same thing using the Addressable gem, which is more full-featured:
require 'addressable/uri'
variable1 = 'foo'
variable2 = 'bar'
uri = Addressable::URI.parse('http://yolo.com/?action=cp_list')
params = uri.query_values
uri.query_values = params.merge('id' => variable1, 'key' => variable2)
uri.to_s # => "http://yolo.com/?action=cp_list&id=foo&key=bar"
That's because you are using strings with single quotes. In single quoted strings, nothing is replaced except \\ and \'.
Specifically, interpolation is only available in double quoted strings, try
talksList = open("http://yolo.com/?action=cp_list&id=#{variable1}&key=#{variable2}")

ruby string splitting problem

i have this string:
"asdasda=asdaskdmasd&asmda=asdasmda&ACK=Success&asdmas=asdakmsd&asmda=adasda"
i want to get the value after between the ACK and the & symbol, the value between the ACK and the & symbol can be changed...
thanks
i want the solution in ruby.
require "cgi"
query_string = "asdasda=asdaskdmasd&asmda=asdasmda&ACK=Success&asmda=asdakmsd"
parsed_query_string = CGI.parse(query_string)
#=> { "asdasda" => ["asdaskdmasd"],
# "asmda" => ["asdasmda", "asdakmsd"],
# "ACK" => ["Success"] }
parsed_query_string["ACK"].first
#=> "Success"
If you also want to reconstruct the query string (especially together with the rest of a URL), I would recommend looking into the addressable gem.
require "addressable/uri"
# Note the leading '?'
query_string = "?asdasda=asdaskdmasd&asmda=asdasmda&ACK=Success&asmda=asdakmsd"
parsed_uri = Addressable::URI.parse(query_string)
parsed_uri.query_values["ACK"]
#=> "Success"
parsed_uri.query_values = parsed_uri.query_values.merge("ACK" => "Changed")
parsed_uri.to_s
#=> "?ACK=Changed&asdasda=asdaskdmasd&asmda=asdakmsd"
# Note how the order has changed and the duplicate key has been removed due to
# Addressable's built-in normalisation.
"asdasda=asdaskdmasd&asmda=asdasmda&ACK=Success&asdmas=asdakmsd&asmda=adasda"[/ACK=([^&]*)&/]
$1 # => 'Success'
A quick approach:
s = "asdasda=asdaskdmasd&asmda=asdasmda&ACK=Success&asdmas=asdakmsd&asmda=adasda"
s.gsub(/ACK[=\w]+&/,"ACK[changedValue]&")
#=> asdasda=asdaskdmasd&asmda=asdasmda&ACK[changedValue]&asdmas=asdakmsd&asmda=adasda
s = "asdasda=asdaskdmasd&asmda=asdasmda&ACK=Success&asdmas=asdakmsd&asmda=adasda"
m = s.match /.*ACK=(.*?)&/
puts m[1]
and just for fun without regexp:
Hash[s.split("&").map{|p| p.split("=")}]["ACK"]

Resources