concatenate link_to with pipe - ruby-on-rails

I want to concatenate a few links with a pipe. But all links are surrounded by an if-statement.
Example:
- if condition1
= link_to link1
- if condition2
= link_to link2
- if condition3
= link_to link3
If condition 1 and 2 are true, the result should be
link1 | link2
Any hints how to do this?

I would use smth like that for that purpose:
= [[l1, c1], [l2, c2], [l3, c3]].map{ |l, c| link_to(l) if c }.compact.join('|')
or
= [(link_to(l1) if c1),(link_to(l2) if c2),(link_to(l3) if c3)].compact.join('|')
The last one is a bit clumsy but it's a matter of taste. Both works perfectly for filtering out unnecessary links and joining the rest of them with |.
Though, if your conditions are non-trivial and you have quite a few of them it'd be better to move that logic outside of view into controller or some helper (depending on the situation).
And if you have some common method for testing whether you should display link or not, let's say show?(link) helper, then things become a bit nicer and you can do it like that:
= [l1, l2, l3, l4].map{ |l| link_to(l) if show?(l) }.compact.join('|')
or like that:
= [l1, l2, l3, l4].select{ |l| show?(l) }.map{ |l| link_to(l) }.join('|')

Related

Remove certain regex from a string in Rails

I am building a tweet-like system that includes #mentions and #hashtags. Right now, I need to take a tweet that will come to the server like this:
hi [#Bob D](member:Bob D) whats the deal with [#red](tag:red)
and save it in the database as:
hi #Bob P whats the deal with #red
I have the flow of what the code looks like in my mind but can't get it to work. Basically, I need to do the following:
Scan the string for any [#...] (an array like structure that begins with an #)
Delete the paranthesis after the array like structure(so for [#Bob D](member:Bob D), remove everything in paranthesis)
Remove the brackets surrounding a substring that begins with #(meaning, delete the [] from [#...])
I will also need to do the same for #. I'm almost certain this can be done by using regular expressions the slice! method, but i'm really having trouble coming up with the regular expressions needed and the control flow.
I think it would be something like this:
a = "hi [#Bob D](member:Bob D) whats the deal with [#red](tag:red)"
substring = a.scan <regular expression here>
substring.each do |matching_substring| #the loop should get rid of the paranthesis but not the brackets
a.slice! matching_substring
end
#Something here should get rid of brackets
The problem with the code above is that I can't figure out the regex and it doesn't get rid of the brackets.
This regex should work for this
/(\[(#.*?)\]\((.*?)\))/
you can use this rubular to test it
the ? after the * makes it non-greedy so it should capture each match
the code would look something like
a = "hi [#Bob D](member:Bob D) whats the deal with [#red](tag:red)"
substring = a.scan (\[(#.*?)\]\((.*?)\))
substring.each do |matching_substring|
a.gsub(matching_substring[0], matching_substring[1]) # replaces [#Bob D](member:Bob D) with #Bob D
matching_substring[1] #the part in the brackets sans brackets
matching_substring[2] #the part in the parentheses sans parentheses
end
Consider this:
str = "hi [#Bob D](member:Bob D) whats the deal with [#red](tag:red)"
BRACKET_RE_STR = '\[
(
[##]
[^\]]+
)
\]'
PARAGRAPH_RE_STR = '\(
[^)]+
\)'
BRACKET_RE = /#{BRACKET_RE_STR}/x
PARAGRAPH_RE = /#{PARAGRAPH_RE_STR}/x
BRACKET_AND_PARAGRAPH_RE = /#{BRACKET_RE_STR}#{PARAGRAPH_RE_STR}/x
str.gsub(BRACKET_AND_PARAGRAPH_RE) { |s| s.sub(PARAGRAPH_RE, '').sub(BRACKET_RE, '\1') }
# => "hi #Bob D whats the deal with #red"
The longer, or more complex the pattern, the harder it is to maintain or update, so keep them as small as possible. Build complex patterns from simple ones so it's easier to debug and extend.

Regular expression to remove only beginning and end html tags from string?

I would like to remove for example <div><p> and </p></div> from the string below. The regex should be able to remove an arbitrary number of tags from the beginning and end of the string.
<div><p>text to <span class="test">test</span> the selection on.
Kibology for <b>all</b><br>. All <i>for</i> Kibology.</p></div>
I have been tinkering with rubular.com without success. Thanks!
def remove_html_end_tags(html_str)
html_str.match(/\<(.+)\>(?!\W*\<)(.+)\<\/\1\>/m)[2]
end
I'm not seeing the problem of \<(.+)> consuming multiple opening tags that Alan Moore pointed out below, which is odd because I agree it's incorrect. It should be changed to \<([^>\<]+)> or something similar to disambiguate.
def remove_html_end_tags(html_str)
html_str.match(/\<([^\>\<]+)\>(?!\W*?\<)(.+)\<\/\1\>/m)[2]
end
The idea is that you want to capture everything between the open/close of the first tag encountered that is not followed immediately by another tag, even with spaces between.
Since I wasn't sure how (with positive lookahead) to say give me the first key whose closing angle bracket is followed by at least one word character before the next opening angle bracket, I said
\>(?!\W*\<)
find the closing angle bracket that does not have all non-word characters before the next open angle bracket.
Once you've identified the key with that attribute, find its closing mate and return the stuff between.
Here's another approach. Find tags scanning forward and remove the first n. Would blow up with nested tags of the same type, but I wouldn't take this approach for any real work.
def remove_first_n_html_tags(html_str, skip_count=0)
matches = []
tags = html_str.scan(/\<([\w\s\_\-\d\"\'\=]+)\>/).flatten
tags.each do |tag|
close_tag = "\/%s" % tag.split(/\s+/).first
match_str = "<#{tag}>(.+)<#{close_tag}>"
match = html_str.match(/#{match_str}/m)
matches << match if match
end
matches[skip_count]
end
Still involves some programming:
str = '<div><p>text to <span class="test">test</span> the selection on.
Kibology for <b>all</b><br>. All <i>for</i> Kibology.</p></div>'
while (m = /\A<.+?>/.match(str)) && str.end_with?('</' + m[0][1..-1])
str = str[m[0].size..-(m[0].size + 2)]
end
Cthulhu you out there?
I am going to go ahead and answer my own question. Below is the programmatic route:
The input string goes into the first loop as an array in order to remove the front tags. The resulting string is looped through in reverse order in order to remove the end tags. The string is then reversed in order to put it in the correct order.
def remove_html_end_tags(html_str)
str_no_start_tag = ''
str_no_start_and_end_tag = ''
a = html_str.split("")
i= 0
is_text = false
while i <= (a.length - 1)
if (a[i] == '<') && !is_text
while (a[i] != '>')
i+= 1
end
i+=1
else
is_text = true
str_no_start_tag << a[i]
i+=1
end
end
a = str_no_start_tag.split("")
i= a.length - 1
is_text = false
while i >= 0
if (a[i] == '>') && !is_text
while (a[i] != '<')
i-= 1
end
i-=1
else
is_text = true
str_no_start_and_end_tag << a[i]
i-=1
end
end
str_no_start_and_end_tag.reverse!
end
(?:\<div.*?\>\<p.*?\>)|(?:\<\/p\>\<\/div\>) is the expression you need. But this doesn't check for every scenario... if you are trying to parse any possible combination of tags, you may want to look at other ways to parse.
Like for example, this expression doesn't allow for any whitespace between the div and p tag. So if you wanted to allow for that, you would add \s* inbetween the \>\< sections of the tag like so: (?:\<div.*?\>\s*\<p.*?\>)|(?:\<\/p\>\s*\<\/div\>).
The div tag and the p tag are expected to be lowercase, as the expression is written. So you may want to figure out a way to check for upper or lower case letters for each, so that Div or dIV would be found too.
Use gskinner's RegEx tool for testing and learning Regular Expressions.
So your end ruby code should look something like this:
# Ruby sample for showing the use of regular expressions
str = "<div><p>text to <span class=\"test\">test</span> the selection on.
Kibology for <b>all</b><br>. All <i>for</i> Kibology.</p></div>"
puts 'Before Reguar Expression: "', str, '"'
str.gsub!(/(?:\<div.*?\>\s*\<p.*?\>)|(?:\<\/p\>\s*\<\/div\>)/, "")
puts 'After Regular Expression', str
system("pause")
EDIT: Replaced div*? to div.*? and replaced p*? to p.*? per suggestions in the comments.
EDIT: This answer doesn't allow for any set of tags, just the two listed in the first line of the question.

How to use html lists in Nitrogen

I would like to create an html list in Nitrogen but can't figure out how to do it. Basically I want to output:
<ul>
<li>One</li>
<li>Two</li>
</ul>
I found some source for lists under apps/nitrogen/src/elements/html in my Nitrogen distribution so it seems like there is an element that can do this but none of my attempts to use the element compile. Can someone please provide a snippet which will produce the above html?
Here's a version that uses the #list and #listitem nitrogen elements rather than raw HTML.
body() ->
List = ["Apple","Orange","Banana"],
#list{
body=[#listitem{body=Fruit} || Fruit <- ListOfFruit]
}.
For any expressions more complicated than that, I'll usually switch from a list comprehension to lists:map/2
Just use your erlang string and list manips like this:
body()->
Items = ["Joe Armstrong","Robert Virding","Mike Williams"],
UL = "<ul>" ++ lists:flatten(["<li>" ++ X ++ "</li>" || X <- Items]) ++"</ul>",
[
#panel{body=[
UL,
#br{},
#button {text="Continue", postback=continue}
]}
].
You have a list of items, the list can be from a database or anything. The list items should be strings for tis to work well, if a different type then you need to convert first. Then a list comprehension and latter you flatten the whole list to be one thing. Hope it helps !

counting line numbers of a poem with nokogiri / ruby

I've struggling to try to do this with a simple regex but it's never been very accurate. It doesn't have to be perfect.
Source has a combination of and tags. I don't want to count blank lines.
Old way:
self.words = rendered.gsub(/<p> <\/p>/,'').gsub(/<p><br\s?\/?>|(?:<br\s?\/?>){2,}/,'<br>').scan(/<br>|<br \/>|<p/).size+1
New way (not working:
Tries to turn all the + into paragraphs, then throw it into nokogiri to count paragraph tags with more than 3 chars in them (I have no idea how? Counting 1 letter lines would be nice too, but this worked ok in javascript)
h = rendered
h.gsub!(/<br>\s*<br>/gi,"<p>")
h.gsub!(/<br>/gi,"<p>") if h =~ /<br>\s*<br>/
h.prepend "<p>" if !h =~ /^\s*<p[^>]*>/i
h.replace(/<p>\s*<p>/g,"<p> </p><p>")
Nokogiri::HTML(rendered)
# find+count p tags with at least 1-3 chars?
# this is javascript not ruby, but you get the idea
$('p', c).each(function(i) { // had to trim it to remove whitespaces from start/end.
if ($(this).children('img').length) return; // skip if it's just an image.
if ($.trim($(this).text()).length > 3)
$(this).append("<div class='num'>"+ (n += 1) +"</div>");
})
Other methods are welcome!
Example poem ( http://allpoetry.com/poem/7429983-the_many_endings-by-Kevin )
<p>
from the other side of silence<br>
you met me with change and a pocket<br>
of unhappy apples.</p>
<p>
</p>
<p>
<br>
we bled together to black<br>
and chose the path carefully to<br>
france.<br><br>
sometimes when you smile<br>
your radiant footsteps fall<br>
and all around us is silence:<br>
each dream step is<br>
false but full of such glory</p>
<p>
</p>
<p>
<br>
unhappiness never made a student of you:<br>
just two by two by two. now three<br>
this great we that overflows our<br>
heart-cave<br><br>
each jewel-like addition to the delicate<br>
crown. but flowers fall and dreams,<br>
all dreams, come to and end with death.</p>
Thank you!
For posterity, here's what I'm using now and it seems to be quite accurate. Non latin chars cause some problems sometimes from ckeditor, so I'm stripping them out for now.
html = Nokogiri::HTML(rendered)
text = html.at('body').inner_text rescue nil
return self.words = rendered.gsub(/<p> <\/p>/,'').gsub(/<p><br\s?\/?>|(?:<br\s?\/?>){2,}/,'<br>').scan(/<br>|<br \/>|<p/).size+1 if !text
#bonus points to strip lines entirely non-letter. idk
#d "text is", text.gsub!(/([\x09|\x0D|\t])|(\xc2\xa0){1,}|[^A-z]/u,'')
text.gsub!(/[^A-z\n]/u,'')
#d "text is", text
self.words = text.strip.scan(/(\s*\n\s*)+/).size+1

Regular Expression Attack Vector?

How does one "parameterize" variable input into a Regex in Ruby? For example, I'm doing the following:
q = params[:q]
all_values.collect { | col | [col.name] if col.name =~ /(\W|^)#{q}/i }.compact
Since it (#{q}) is a variable from an untrusted source (the query string), I have to assume it could be an attack vector. Any best practices here?
Try Regexp.escape:
>> Regexp.escape('foo\bar\baz$+')
=> "foo\\\\bar\\\\baz\\$\\+"
So your code would look something like:
q = params[:q]
re = Regexp.escape(q)
all_values.collect { | col | [col.name] if col.name =~ /(\W|^)#{re}/i }.compact
So, do you want the user to be able to provide an arbitrary regular expression, or just some literal text that surely can be quoted? If the user can provide both a regular expression and the text it will be attempted matched against, it's not hard to do a DoS-attack by providing an expression with exponential runtime.

Resources