Parse href from <a> using XPath (#href doesn't work) - parsing

This should be simple but isn't.
I am using:
//*[#class="mainbody right"]//div[2]/div[2]/div[1]/div[1]/a
to generate a list of elements:
< a href="https://someurl1.com" class="title getFull"
data-view="full"> Some plain text 1 < /a>
< a href="https://someurl2.com" class="title getFull"
data-view="full"> Some plain text 2 < /a>
< a href="https://someurl3.com" class="title getFull"
data-view="full"> Some plain text 3 < /a>
What I want instead is either:
href="https://someurl1.com"
href="https://someurl2.com"
href="https://someurl3.com"
or
https://someurl1.com
https://someurl2.com
https://someurl3.com
How do I get rid of the unwanted class & data-view & plain text? I have tried appending /#href and a great number of other things but to no avail.

If you really want to take values from position elements then you need to change the XPath like:
//*[#class="mainbody right"]//div[2]/div[2]/div[1]/div[1]/a/#href

Related

html_safe and non html_safe concatenation

I have a rails controller with the following code:
class ProgramController < ApplicationController
def data
#out="User <i>n<p>ut! </p></i>"+"<br>A bit of<h1>HTML!</h1>".html_safe
end
end
When going into the page, it escapes all the html characters instead of just the characters in the string without .html_safe.
The <%=#out%> I have in my erb file returnes "User <i>n<p>ut! </p></i><br>A bit of<h1>HTML!</h1>", but I want it to return somthing more like "User <i>n<p>ut! </p></i><br>A bit of<h1>HTML!</h1>".
To make it a bit clearer:
Expected output:
User <i>n<p>ut! </p></i><br>A bit of<h1>HTML!</h1>
Actual output:
User <i>n<p>ut! </p></i><br>A bit of<h1>HTML!</h1>
(Output is as shown on view-source)
This is obviously a problem with concatenation, as
#out="User <i>n<p>ut! </p></i>".html_safe+"<br>A bit of<h1>HTML!</h1>".html_safe
causes
User <i>n<p>ut! </p></i><br>A bit of<h1>HTML!</h1>
to be displayed.
How should I concatenate a html_safe string and a non html_safe string, or escape html characters before concatenation?
First lets try to understand what's happening
to_escape = '<br>'
not_to_escape = '<br>'.html_safe
to_escape.class # String < Object
not_to_escape.class # ActiveSupport::SafeBuffer < String
Okay, they're different classes. If we mark the string safe, it's no longer just a string. Makes sense so far.
total = to_escape + not_to_escape # "<br><br>"
total.class # String < Object
Okay, adding them like this gives you a string. No info about anything being safe in here. This happens because you're adding onto String so, String#+ is called which knows nothing about html safety and just adds them together.
Let's try it the other way:
total = not_to_escape + to_escape # "<br><br>"
total.class # ActiveSupport::SafeBuffer < String
Aha!, now escaping worked correctly and the class is correct, but the order in the result is different. But now we know that adding onto an escaped string does what we want.
Solution:
total = ''.html_safe + to_escape + not_to_escape # "<br><br>"
total.class # ActiveSupport::SafeBuffer < String
I'd use ERB::Util.html_escape to escape the HTML, something like this:
#out = "#{ERB::Util.html_escape('User <i>n<p>ut! </p></i>')}<br>A bit of<h1>HTML!</h1>".html_safe
Or you could use CGI.escapeHTML:
#out = "#{CGI.escapeHTML('User <i>n<p>ut! </p></i>')}<br>A bit of<h1>HTML!</h1>".html_safe

Finding the content in the bracket rails

So I have a variable called #gases that holds gas element such as Carbon Monoxide [CO], Carbon Dioxide [CO2]. Right now if someone searches CO in the search function Carbon Dioxide [CO2] shows up first. So I wanted to know if I could do something like this
where("(lower(gas_analytes.gas)\[.*?\]) LIKE lower(?)", "#{gas_analyte}")
so the code above, if someone only searches for the element and not the whole content, I used \[.*?\], so if the search input equals the element in the bracket, but this does not work.
Is there anything similar I can do?
I would probably just add the square brackets to the search pattern like this:
where("lower(gas_analytes.gas) LIKE ?", "%[#{gas_analyte.downcase}]%")
Note that if it is possible that gas_analyze might include % characters then you should sanitize that string before using it:
where("lower(gas_analytes.gas) LIKE ?", "%[#{sanitize_sql_like(gas_analyte.downcase)}]%")
Because like is very slow you might consider splitting the string and extracting the abbreviation into a database column on it own. That would improve the query time a lot.
This code worked for my (I'm using SQLite database):
# seeds.rb
# Type `rake db:seed` in terminal to run this file
# GasAnalyte.destroy_all
10.times do |t|
GasAnalyte.create!(
gas: "#{SecureRandom.uuid} [CO2]"
)
end
p ['All', GasAnalyte.all].inspect
p ['Searching for "CO"', GasAnalyte.search_gas("CO")].inspect
# gas_analyte.rb
class GasAnalyte < ApplicationRecord
validates :gas, presence: true
scope :search_gas,
->(gas_analyte) { where("lower(gas_analytes.gas) LIKE lower(?)", "%[#{gas_analyte}%]") }
end
Here you can read about LIKE operator: https://www.w3schools.com/sql/sql_like.asp

Rails set has_many value dynamically

I have an entity: book of class Book.
The entity class has has_many relation with other tables, pages for example.
Let's say that page_1 and page_2 are valid values that I'de like to save. The non-dynamic version would be something like:
entity.pages = [page_1, page_2]
How can I set this dynamically?
I tried using send (which works fine for has_one) with no luck:
attr = :pages # my dynamic attribute
book.send(attr) = [page_1, page_2]
# SyntaxError: unexpected '=', expecting end-of-input
# mc.send(:diagnoses, '=') = [s]
# ^
When I use << it seems to work:
book.send(attr) << page_1
but the issue is that I need to support deletion, e.g. if the book had page3, and now it has page1 and page2.
I don't want to use eval, both due to performance and security. Not sure it's related, but these dynamic attributes all have the same class - has__many with a dynamic condition.
The correct format is to call the setter (assignment) method. Which is usually the attribute followed by an equal sign. In your case, you want pages=
book.send(attr.to_s + '=', [page_1, page_2] )
Equivalent to
book.send('pages=', [page_1, page_2])
which is...
book.pages=([page_1, page_2])
or more conventionally written
book.pages = [page_1, page_2]
Try book.association(:pages).target

Replacing {phrase} with phrase in rails

I'd like to search and replace any occurrence of {phrase} with with phrase using rails (erb.html file). Multiple phrases will need to be substituted, and the phrases aren't known in advance.
Full Example:
Hi {guys}, I really like {ruby on rails}
Needs to become
Hi guys, ruby on rails
This is for a user-generated content site (GMT)
it's simple regexp, just use
your_string.gsub(/{(.*?)}/, '\\1')
Example:
"{aaa} is not {bbb} you know".gsub(/{(.*?)}/, '\\1')
will produce
aaa is not bbb you know
You can do this using gsub
irb(main):001:0> str = " I have written this phrase statement, I want to replace occurences of all phrase with other statement"
=> " I have written this phrase statement, I want to replace occurences of all phrase with other statement"
irb(main):002:0> str.gsub("phrase",'phrase')
=> " I have written this phrase statement, I want to replace occurences of all phrase with other statement"
A better way to do this will be to use a Markdown output engine (Redcarpet being one of the most robust)
You'd have to create a custom renderer:
#lib/custom_renderer.rb
class AutoLinks < Redcarpet::Render::HTML
def auto_link(phrase) #-> will need to search through content. Can research further
link_to phrase, "/#{phrase}"
end
end
#controller
markdown = Redcarpet::Markdown.new(AutoLinks, auto_link: "ruby on rails")
Just use a helper in your erb. For example:
tag_helper.rb:
module TagHelper
def atag(phrase)
"<a href='/#{phrase}'>#{phrase}</a>"
end
end
some.html.erb:
<%= atag('guys')%>

Rails 3, best way to be DRY with radio button labels?

Suppose for an integer field named 'favfood' we represent the radio button choices as
0 indicates "no favorite"
1 indicates "Wine and cheese"
2 indicates "Burger with everything"
On our _edit view we display the radio buttons with the friendly labels above
On the /show view and /index view (and in several other places) when we display the user preference we display the same corresponding long text.
It seems non-DRY to put literal strings next to each radio button in _edit and then provide some logic to display the SAME literal string on /show and /index etc etc etc based upon the current value.
Furthermore, we have to repeatedly use the SAME logic that says if the value=1 display "The first choice", if the value = 2 display "The second choice".
What's the "rails-way" to handle user-friendly labels for radio labels so the labels (AND their association with values of the field) are defined once ?
There are two things you should do. One use Internationalization (I18n) so that your text is stored in your en.yml file and you can use the labels instead of strings:
# en.yml
en:
no_favorite: 'No favorite'
wine_and_cheese: 'Wine and Cheese'
burger_with_everything: 'Burger with everything'
# You can then translate the labels like this
I18n.t :no_favorite
I18n.t :wine_and_cheese
I18n.t :burger_with_everything
The next step would be to create some kind of hash or class that stores and translates the integer values for you. A simple array solution might look like this:
OPTIONS = [:no_favorite, :wine_and_cheese, :burger_with_everything]
You could then use the options like this:
selected_value = 1
I18n.t OPTIONS[selected_value] # => wine_and_cheese
OPTIONS.index :wine_and_cheese # => 1
Furthermore, if the selected option was a model value (say User#food_preference) you could define a function for displaying that user's food preference:
class User
OPTIONS = [:no_favorite, :wine_and_cheese, :burger_with_everything]
def display_food_preference
I18n.t OPTIONS[food_preference]
end
end
I'm not sure what you're trying to do here, but why not store the strings in the model. Sth like:
class User < ActiveRecord::Base
def favfood_description
"bla bla" if favfood == 0
end
end

Resources