Rails 4: how to identify and format links, hashtags and mentions in model attributes? - css

In my Rails 4 app, I have a Post model, with :copy and :short_copy as custom attributes (strings).
These attributes contain copies for social medias (Facebook, Twitter, Instagram, Pinterest, etc.).
I display the content of these attributes in my Posts#Show view.
Currently, URLs, #hashtags and #mentions are formatted like the rest of the text.
What I would like to do is to format them in a different fashion, for instance in another color or in bold.
I found the twitter-text gem, which seems to offer such features, but my problem is that I do NOT need — and do NOT want — to have these URLs, #hashtags and #mentions turn into real links.
Indeed, it looks like the twitter-text gem converts URLs, #hashtags and #mentions by default with Twitter::Autolink, as explained in this Stack Overflow question.
That's is not what I am looking for: I just want to update the style of my URLs, #hashtags and #mentions.
How can I do this in Ruby / Rails?
—————
UPDATE:
Following Wes Foster's answer, I implemented the following method in post.rb:
def highlight(string)
string.gsub!(/\S*#(\[[^\]]+\]|\S+)/, '<span class="highlight">\1</span>')
end
Then, I defined the following CSS class:
.highlight {
color: #337ab7;
}
Last, I implemented <%= highlight(post.copy) %> in the desired view.
I now get the following error:
ArgumentError
wrong number of arguments (1 for 2..3)
<td><%= highlight(post.copy) %></td>
What am I doing wrong?
—————

I'm sure each of the following regex patterns could be improved to match even more options, however, the following code works for me:
def highlight_url(str)
str.gsub!(/(https?:\/\/[\S]+)/, '[\1]')
end
def highlight_hashtag(str)
str.gsub!(/\S*#(\[[^\]]+\]|\S+)/, '[#\1]')
end
def highlight_mention(str)
str.gsub!(/\B(\#[a-z0-9_-]+)/i, '[\1]')
end
# Initial string
str = "Myself and #doggirl bought a new car: http://carpictures.com #nomoremoney"
# Pass through each
highlight_mention(str)
highlight_hashtag(str)
highlight_url(str)
puts str # > Myself and [#doggirl] bought a new car: [http://carpictures.com] [#nomoremoney]
In this example, I've wrapped the matches with brackets []. You should use a span tag and style it. Also, you can wrap all three gsub! into a single method for simplicity.
Updated for the asker's add-on error question
It looks like the error is references another method named highlight. Try changing the name of the method from highlight to new_highlight to see if that fixes the new problem.

Related

Atom data-grammar syntax for keybindings

Can someone give a full explanation of the syntax for Atom's data-grammar attribute (used in keybinding selectors)?
For instance, what is the difference between
[data-grammar='source example']
and
[data-grammar~='source example']
?
Also, how do you specify multiple grammars? For instance, how would you specify that a key binding should be limited to html or xml formats?
If there already exists documentation on this somewhere, I have not yet found it, but would appreciate being pointed to it.
Quick Example:
keymap.cson:
"atom-text-editor[data-grammar='text tex latex']":
'f5':'latex:build'
Grammar Information & Documentation
I began by looking at the file-types package. source and text categorize languages - source deals with development languages, while text deals with documentation/logs formats.
You can add and customize language recognition by reading the flight manual. I've linked some specific sections below that are helpful for that.
Flight Manual | Basic Customization:
Language Recognition
Language Specific Settings
Working with [data-grammar]:
The little doocumentation given is listed under the Keymaps in Depth section.
Flight Manual | Keymaps in Depth
Selectors and Custom Packages.
This also describes the not([...]) functionality used below and how to manipulate various rules.
Although in the above, grammars are listed in a dot format, ie source.c, to use them in the [data-grammar='<name>'] format spaces are instead required.
An example of how I might use the data grammar option in my keymap.cson config is as such (here I'm using the latex package):
"atom-text-editor[data-grammar='text tex latex']":
'f5':'latex:build'
The ~ is not the correct syntax for desired functionality with data-grammar. Instead, use something like "atom-text-editor:not([data-grammar='<name>'])":
Note that you wouldn't use data-grammar in something like config.cson. The syntax for language specifics looks something like this instead:
# **config.cson**
".latex.tex.text":
editor:
softWrap: true
Extra useful information - List of registered Grammars
A dump of the output of Object.keys(atom.grammars.grammarsByScopeName).sort().join('\n') through the Dev Console (View > Developer > Toggle Developer Options > Console)
source.c
source.cake
source.clojure
source.coffee
source.cpp
source.cs
source.css
source.css.less
source.css.scss
source.csx
source.diff
source.gfm
source.git-config
source.go
source.gotemplate
source.java
source.java-properties
source.js
source.js.rails source.js.jquery
source.js.regexp
source.js.regexp.replacement
source.json
source.litcoffee
source.makefile
source.nant-build
source.objc
source.objcpp
source.perl
source.perl6
source.plist
source.python
source.python.django
source.regexp.python
source.ruby
source.ruby.gemfile
source.ruby.rails
source.ruby.rails.rjs
source.sass
source.shell
source.sql
source.sql.mustache
source.sql.ruby
source.strings
source.toml
source.verilog
source.yaml
text.bibtex
text.git-commit
text.git-rebase
text.html.basic
text.html.erb
text.html.gohtml
text.html.jsp
text.html.mustache
text.html.php
text.html.ruby
text.hyperlink
text.junit-test-report
text.log.latex
text.plain
text.plain.null-grammar
text.python.console
text.python.traceback
text.shell-session
text.tex
text.tex.latex
text.tex.latex.beamer
text.tex.latex.memoir
text.todo
text.xml
text.xml.plist
text.xml.xsl
To complement Mr G's answer, atom-text-editor[data-grammar~='html'] with the ~= means match an <atom-text-editor> HTML element with a data-grammar attribute that contains "html" amongst any other possible whitespace separated words.
For example, if the current language of the file is PHP, then the text-editor HTML element will look something like this:
<atom-text-editor data-grammar="text html php">
And atom-text-editor[data-grammar~='html'] will match this.
More info on attribute selectors: https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors
As for trying to select multiple grammars, I don't think it's possible unless they share a common word in the data-grammar attribute, e.g., both HTML and PHP share "html", or both C and JavaScript share "source" (but in this case many other grammars share "source"). The only other way is to specify a keymap for each grammar individually even if it's the same key combination.

Randomizing URL numbers in iMacros

I'm using iMacros because I want to scrape a certain site for ID's which are used in the URL, after which I want to press a button.
I know you can't use Regular Expressions or globbing in the syntax for URL GOTO.
But I figured there might be a way to enter variables into the URL GOTO=?
Preferable I wouldn't want to randomize the variable, but have it try every page from [1 - 99999]
This is what I currently have:
VERSION BUILD=8940826 RECORDER=FX
TAB T=1
SET !ERRORIGNORE YES
SET !VAR3 ("Math.floor(Math.random()*99999 + 1);")
URL GOTO=http://example.com/id/ "randomized_variable_here"
TAG POS=1 TYPE=SPAN ATTR=TXT:press<SP>button
I have tried a few things, but I don't seem to be able to do this.
I have very little experience actually creating stuff for myself, I just modify scripts to fit my purposes, but should I look towards an HTML document or something like that to randomize that variable for me?
Thanks in advance!
It's pretty simple to get the string with a randomized variable:
' ...
SET !VAR3 EVAL("Math.floor(Math.random()*99999 + 1);")
URL GOTO=http://example.com/id/{{!VAR3}}
' ...
And the following code is for looping through [1 - 'Max:' value on the 'iMacros' sidebar]:
' ...
SET !LOOP 1
URL GOTO=http://example.com/id/{{!LOOP}}
' ...
Just play this macro in loop mode.

Cucumber: In which order the feature tags are followed in a cucumber script?

I am facing an issue where I need to run script with three features. Let's say we have 3 feature files with tag names as #smoke1, #smoke2 and #smoke3. And I want these to be executed in that sequence.
Issue is that smoke3 is executing first and rest of them afterwards.
This is my script:
#Cucumber.Options(
glue = { "com.abc", "cucumber.runtime.java.spring.hooks" },
features = "classpath:",
format = { "json", "json:target/cucumber.json" },
tags = "#smoke1, #smoke2, #smoke3"
)
public class ex_Test extends AbstractTest { }
Warning: This only works in older versions of Cucumber.
Cucumber feature files are executed in alphabetical order by path and filename. The execution order is not based on tags.
However, if you specifically specify features, they should be run in the order declared.
For example:
#Cucumber.Options(features={"first_smoke.feature", "another_smoke.feature"})
Should run first_smoke and then another_smoke (compared to the default which is to run in the other order.
Ok we got it , We can have multiple tags for a single scenario like this #tag1 #tag2 #tag3.
You can not define the order in way below.
#Cucumber.Options(features={"first_smoke.feature", "another_smoke.feature"})
Cucumber determines the only alphabetical order and even only first letter of the word.
You can have how many tags you want in feature file, if you want to trigger feature file more times, it's not working like you will add tag more time or more tags from feature like:
tags = {"#Reports,#Reports"}
And tests are triggered in alphabetical order, it's checking tags not feature file name.

How Can WYSIHTML5 output inline CSS?

I am running WYSIHTML5 to allow myself to enter email text and format it for sending as HTML. However when I view HTML of the formatted text I get classes associated with elements for Colors. This is expected behavior but since I need to send the output in an email hence I would like to have those colors to be in Inline CSS, since I cannot attach CSS files with the email like that. Example here
<span class="wysiwyg-color-green">Testing</span>
That is if I select green color for text: Testing. Is there any way to modify that green to become part of html itself like
<span style="color:green">Testing</span>
I have tried to search for this but could not find, so I am not asking without first looking for it. If anybody could please just point somewhere. Even a link to any guide to this, will do. I do not wish that you spend time writing code for me.
You could do it with php :
str_replace ( 'class="wysiwyg-color-green"', 'style="color:green"' ,$html)
You can do the same with javascript, altrough it's always safer to do everything server-side.
http://www.w3schools.com/jsref/jsref_replace.asp
Here's the javascript code I used but may be a good idea to heed Jean-Georges warning above:
replaceColorStylesWithInlineCss = function (htmlContents){
result = htmlContents.replace('class="wysiwyg-color-black"', 'class="wysiwyg-color-black" style="color:black"');
result = result.replace('class="wysiwyg-color-silver"', 'class="wysiwyg-color-silver" style="color:silver"');
result = result.replace('class="wysiwyg-color-gray"', 'class="wysiwyg-color-gray" style="color:gray"');
result = result.replace('class="wysiwyg-color-maroon"', 'class="wysiwyg-color-maroon" style="color:maroon"');
result = result.replace('class="wysiwyg-color-red"', 'class="wysiwyg-color-red" style="color:red"');
result = result.replace('class="wysiwyg-color-purple"', 'class="wysiwyg-color-purple" style="color:purple"');
result = result.replace('class="wysiwyg-color-green"', 'class="wysiwyg-color-green" style="color:green"');
result = result.replace('class="wysiwyg-color-olive"', 'class="wysiwyg-color-olive" style="color:olive"');
result = result.replace('class="wysiwyg-color-navy"', 'class="wysiwyg-color-navy" style="color:navy"');
result = result.replace('class="wysiwyg-color-blue"', 'class="wysiwyg-color-blue" style="color:blue"');
result = result.replace('class="wysiwyg-color-orange"', 'class="wysiwyg-color-orange" style="color:orange"');
return result
};
Note: I kept the wysiwyg styles in there because I'm saving to the db and want it to display properly in the wysihtml5 section when I load it again. DRY it up if you're clever.

ReST strikethrough

Is it possible to strike text through in Restructured Text?
Something that for example renders as a <strike> tag when converted to HTML, like:
ReSTructuredText
I checked the docs better, as suggested by Ville Säävuori, and I decided to add the strikethrough like this:
.. role:: strike
:class: strike
In the document, this can be applied as follows:
:strike:`This text is crossed out`
Then in my css file I have an entry:
.strike {
text-decoration: line-through;
}
There is at least three ways of doing it:
.. role:: strike
An example of :strike:`strike through text`.
.. container:: strike
Here the full block of test is striked through.
An undecorated paragraph.
.. class:: strike
This paragraph too is is striked through.
.. admonition:: cancelled
:class: strike
I strike through cancelled text.
After applying rst2html you get:
<p>An example of <span class="strike">strike through text</span>.</p>
<div class="strike container">
Here the full block of test is striked through.</div>
<p>An undecorated paragraph.</p>
<p class="strike">This paragraph too is is striked through.</p>
<div class="strike admonition">
<p class="first admonition-title">cancelled</p>
<p class="last">I strike through cancelled text.</p>
You use them with a style
.strike {
text-decoration: line-through;
}
Here I have taken the admonition directive as example but any
directive that allow the :class: option would do.
As it generates a span the role directive is the only one that
allow to apply your style to a part of a paragraph.
It is redundant to add a class strike to a directive also named
strike, as suggest Gozzilli, because the directive name is the default
class for the html output.
I have checked these syntax both with rest2html and Sphinx. But
while everything works as expected with rest2html the class
directive fail with Sphinx. You have to replace it with
.. rst-class:: strike
This paragraph too is is striked through.
This is only stated in a small
footnote of Sphinx reSt Primer.
According to the official spec there is no directive for strikethrough markup in ReST.
However, if the environment allows for :raw: role or you are able to write your own roles, then you can write a custom plugin for it.
I found the other answers very helpful.
I am not very familiar with Sphinx but I am using it for a project. I too wanted the strike-through ability and have got it working based on the previous answers.
To be clear, I added my strikethrough role as gozzilli mentioned but I saved it inside my conf.py using the rst_prolog variable as discussed in the stack overflow thread here. This means that this role is available to all of your rest files.
I then extended the base html template as described above by creating layout.htmlwithin _templatesinside my source directory. The contents of this file are:
{% extends "!layout.html" %}
{% set css_files = css_files + ["_static/myStyle.css"] %}
This basically includes a custom css file to all your built default html docs.
Finally, in my _static directory within my source directory I included the file myStyle.css which contains:
.strike {
text-decoration: line-through;
}
Which the other answers have already provided.
I am merely writing this answer as it wasn't obvious to me with my limited Sphinx experience which files to edit.
Here's a Python definition of a del role, which works better than the accepted answer if you want to use the role in multiple pages of a Pelican blog or a Sphinx documentation project:
from docutils import nodes
from docutils.parsers.rst import roles
def deleted_role(_role, rawtext, text, _lineno, _inliner, options={}, _content=[]):
roles.set_classes(options)
options.setdefault('classes', []).append("del")
return [nodes.inline(rawtext, text, **options)], []
roles.register_canonical_role('del', deleted_role)
Even better would be to extend the HTML writer to produce a proper <del> tag, like this:
from docutils import nodes
from docutils.parsers.rst import roles
from docutils.writers._html_base import HTMLTranslator
class delnode(nodes.inline):
pass
def visit_delnode(self, node):
self.body.append(self.starttag(node, 'del', ''))
def depart_delnode(self, node):
self.body.append('</del>')
HTMLTranslator.visit_delnode = visit_delnode
HTMLTranslator.depart_delnode = depart_delnode
def deleted_role(_role, rawtext, text, _lineno, _inliner, options={}, _content=[]):
roles.set_classes(options)
return [delnode(rawtext, text, **options)], []
roles.register_canonical_role('del', deleted_role)
You can trivially adjust it to produce an <s>, of course.
Consider the user may have a different background, so here is no one solution that can be suitable for everyone.
1.Only one file
If you only use it only on one file. For example, you published a simple project to PyPI, and you may probably just only one README.rst file. The following may you want.
.. |ss| raw:: html
<strike>
.. |se| raw:: html
</strike>
single line
=============
|ss| abc\ |se|\defg
multiple line
=============
|ss|
line 1
line 2
|se|
789
you can copy and paste it on this website: https://livesphinx.herokuapp.com/
and will see the picture as the following:
It's simple, and you can on directly see the preview on some IDE, for example, PyCharm.
bellow is writing for the users of Sphinx
2.beginner of Sphinx
If you are a beginner of Sphinx. ( I mean maybe you want to use Sphinx to create a document, but Python is not familiar for you ) then try as following:
# conf.py
from pathlib import Path
html_static_path = ['_static', ]
html_css_files = ['css/user.define.css'] # If you want to control which HTML should contain it, you can put it on the HTML, which is very like the answer by #Gregory Kuhn.
with open(Path(__file__).parent / Path('_static/css/user.define.rst'), 'r') as f:
user_define_role = f.read()
rst_prolog = '\n'.join([ user_define_role + '\n',]) # will be included at the beginning of every source file that is read.
# rst_epilog = '\n'.join([ user_define_role + '\n',]) # it's ok if you put it on the end.
user.define.rst
.. role:: strike
user.define.css
.strike {text-decoration: line-through;}
With the rst_prolog, It can auto-add the role on each rst files, but if you change the content( that file, it contains a format that you define), then you must rebuild to make the render is correct.
3.Create roles
You can create an extension to achieve it.
# conf.py
extensions = ['_ext.rst_roles', ]
html_static_path = ['_static', ]
html_css_files = ['css/user.define.css']
# rst_roles.py
from sphinx.application import Sphinx
from docutils.parsers.rst import roles
from docutils import nodes
from docutils.parsers.rst.states import Inliner
def strike_role(role, rawtext, text, lineno, inliner: Inliner, options={}, content=[]):
your_css_strike_name = 'strike'
return nodes.inline(rawtext, text, **dict(classes=[your_css_strike_name])), []
def setup(app: Sphinx):
roles.register_canonical_role('my-strike', strike_role) # usage: :my-strike:`content ...`
The full architecture:
conf.py
_ext/
rst_roles.py
_static/
css/
user.define.css
about the rules, you can reference this link rst-roles
And I vary recommended you to see the docutils.parsers.rst.roles.py .
I wrote an extension for this.
Just pip install sphinxnotes-strike and use:
:strike:`text`
or
:del:`text`
to show strike text.
For more info: https://sphinx-notes.github.io/strike/
Since Docutils 0.17, the HTML5-writer uses <del> if a matching class value is found in inline, literal, or container elements:
.. role:: del
:del:`This text has been deleted`, here is the rest of the paragraph.
.. container:: del
This paragraph has been deleted.

Resources