Empty attribute with Ruby HAML - ruby-on-rails

I'm implementing Schema microformats on a Ruby project using HAML and can't figure out how to set an empty attribute on a tag. I tried nil and false, but they simply do not shown.
Example: <div itemscope>
I'm tring to set an empty itemscope attribute.
Code added from comment by #StrangeElement:
My code:
.agency.premium{:itemscope => true, :itemtype => 'schema.org/ProfessionalService';}
:itemscope => true seems to be the recommended approach from HAML's documentation. I get the same result as I would get with :itemscope => '', a XHTML-valid attribute with an empty value (i.e. <div itemscope="">).
Probably fine, but I'd rather have it empty as is documented in the Schema doc.

Using something like
%div{:itemscope => true}
is the correct way to specify this in your Haml file.
How this is rendered depends on how you set Haml's format option. The default in Haml 3.1 is xhtml, and with that it will render as itemprop='itemprop', which is valid xhtml. To render with minimized attributes (like <div itemscope>) you need to set the format to html4 or html5. (In Rails 3 the default is html5, and in Haml 4.0 the default is html5).
How to set the Haml options depends on how you are using it, see the options section in the docs.
For example, using Haml directly in Ruby, this:
engine = Haml::Engine.new '%div{:itemscope => true}'
puts engine.render
produces the default xhtml with full attributes:
<div itemscope='itemscope'></div>
But this:
engine = Haml::Engine.new '%div{:itemscope => true}', :format => :html5
puts engine.render
produces the desired result with minimized attributes:
<div itemscope></div>

If someone is interested in how to put more words in that way, he may use "foo bar" => true:
%option{ "disabled selected value" => true } Choose an option
results is:
<option disabled="" selected="" value="">Choose an option</option>
and works as expected.

The accepted answer works, but it produces an HTML attribute with value.
If you want the attribute only to be output on HTML, without value, you can use the HTML-style attributes syntax of HAML:
%div(itemscope)

Related

How do I implement Rouge syntax highlighting in Rails?

There are a bunch of tutorials floating around, but they seem to be incomplete or not fully current or don't fully work for me.
This is what I have done.
Gemfile:
gem 'rouge'
gem 'redcarpet'
Then I created a config/initializer/rouge.rb:
require 'rouge/plugins/redcarpet'
Then I created a file called app/assets/stylesheets/rouge.css.erb
<%= Rouge::Themes::Github.render(:scope => '.highlight') %>
Then in my app/helpers/application_helper.rb, I added this:
module ApplicationHelper
class HTML < Redcarpet::Render::HTML
include Rouge::Plugins::Redcarpet
def block_code(code, language)
Rouge.highlight(code, language || 'text', 'html')
end
end
def markdown(text)
render_options = {
filter_html: true,
hard_wrap: true,
link_attributes: { rel: 'nofollow' }
}
renderer = HTML.new(render_options)
extensions = {
autolink: true,
fenced_code_blocks: true,
lax_spacing: true,
no_intra_emphasis: true,
strikethrough: true,
superscript: true
}
Redcarpet::Markdown.new(renderer, extensions).render(text).html_safe
end
end
Then in my show.html.erb, I did this:
<%= markdown(#question.body) %>
But that literally does not work. It outputs my ruby code snippet like this:
How do I get this snippet of code to be formatted like Github? Or even just the first step being to be formatted any at all, then how do I tweak the formatting?
I don't see a stylesheet included in the source of the page, so I don't know which styles to tweak for what I want.
Edit 1
Or even when I do this:
<div class="highlight">
<%= #question.test_suite %>
</div>
It renders like this:
Edit 2
I attempted BoraMa's suggestion and I got output that looks like this:
Edit 3
I made a modification to BoraMa's answer as follows.
In my block_code method, I call highlight as follows:
Rouge.highlight(code, 'ruby', 'html')
Then in my view I do this:
<%= raw rouge_markdown(<<-'EOF'
def rouge_me
puts "this is a #{'test'} for rouge"
end
EOF
) %>
Then that produces this:
Note I am referring to the code snippet at the bottom of the screenshot.
However, the text at the top is generated with this:
<pre class="highlight ruby">
<%= rouge_markdown(#question.body) %>
</pre>
And it is rendered as is shown in the screenshot.
Edit 4
After removing the <div class="highlight">, I see this:
Aka....nothing is being rendered at all.
Once I add raw to my view...aka <%= raw rouge_markdown(#question.body) %>
The view renders this:
Edit 5
Here is the content for various #question objects:
[1] pry(#<#<Class:0x007fc041b97ce8>>)> #question.body
=> "5.times do\r\n puts \"Herro Rerl!\"\r\nend"
[1] pry(#<#<Class:0x007fc041b97ce8>>)> #question.body
=> "puts \"Hello World version 9\"\r\nputs \"This comes after version 8.\"\r\nputs \"This comes after version 7.\"\r\nputs \"This comes after version 6.\"\r\nputs \"This comes after version 5.\"\r\nputs \"This comes after version 4.\"\r\nputs \"This comes after version 3.\"\r\nputs \"This comes after version 2.\"\r\nputs \"This definitely comes after version 1.\""
[1] pry(#<#<Class:0x007fc041b97ce8>>)> #question.body
=> "def convert_relation(invited_gender, relation)\r\n case invited_gender\r\n \twhen \"male\"\r\n \tcase relation\r\n when \"daughter\", \"son\" then \"dad\"\r\n when \"mom\", \"dad\" then \"son\"\r\n when \"grandfather\", \"grandmother\" then \"grandson\"\r\n when \"sister\", \"brother\" then \"brother\"\r\n when \"wife\" then \"husband\"\r\n when \"husband\" then \"husband\"\r\n end\r\n when \"female\"\r\n \tcase relation\r\n when \"daughter\", \"son\" then \"mom\"\r\n when \"mom\", \"dad\" then \"daughter\"\r\n when \"grandfather\", \"grandmother\" then \"granddaughter\"\r\n when \"sister\", \"brother\" then \"sister\"\r\n when \"wife\" then \"wife\"\r\n when \"husband\" then \"wife\"\r\n end\r\n end\r\nend\r\n\r\nputs convert_relation(\"male\", \"wife\")"
The original question indicated (in the solution attempted) that markdown would be used in the highlighted questions but it turned out not to be the case. So this answer is split to two distinct sections, one for highlighting pure code without markdown, the other one for markdown text with code.
A) You want to highlight pure code (no Markdown involved)
In this case, and according to the README, all you need to highlight the code with Rouge is a lexer and a formatter. Since the highlighted text will be displayed on a web page, you need the HTML formatter. For the lexer, you need to know the language the code is in beforehand (or you may try guessing it from the source code itself but it does not seem to be very reliable for small code snippets).
You can create a simple helper method for the highlighting:
module RougeHelper
def rouge(text, language)
formatter = Rouge::Formatters::HTML.new(css_class: 'highlight')
lexer = Rouge::Lexer.find(language)
formatter.format(lexer.lex(text))
end
end
Then in the template, simply call this helper with a text to highlight and the language:
<%= raw rouge("def rouge_me\n puts 'hey!'\nend", "ruby") %>
Which will render:
To get a list of all languages that Rouge supports and their corresponding names that should be passed to the rouge helper, you can use the following code. The code gets all defined lexers from Rouge and shows their tags (i.e. the names Rouge recognizes them with):
Rouge::Lexer.all.map(&:tag).sort
# => ["actionscript", "apache", "apiblueprint", "applescript", ..., "xml", "yaml"]
You can (and probably should) use this list when showing users the languages to choose from in the selection box. Note that each lexer also has the title and desc methods defined that will give you a human-readable name and a short description of each of them. You might want to use this info to be shown to the user, too.
Note: you should get rid of the initializer, the custom HTML class and the div wrapped around the rouge helper call (all of these you have in your original attempt). The only thing you need besides the code above is the CSS rules, which you have already correctly included in the web page.
B) The highlighted text is a Markdown text with code blocks
A couple of changes from your attempt to make it work:
The initializer is not needed, you can remove it I think (but if you don't want to require all the files later in the helper, I guess you can leave it).
Remove the block_code method from the helper class, the same is already done by including the markdown plugin.
Remove the <div class="highlight"> wrapper div from your template and just use the helper in it. Rouge adds its own wrapper with the "highlight" class and another div seems to confuse it.
Try the following helper code. BTW, I moved the code from ApplicationHelper to a separate RougeHelper (but that is not a required change):
module RougeHelper
require 'redcarpet'
require 'rouge'
require 'rouge/plugins/redcarpet'
class HTML < Redcarpet::Render::HTML
include Rouge::Plugins::Redcarpet
end
def rouge_markdown(text)
render_options = {
filter_html: true,
hard_wrap: true,
link_attributes: { rel: 'nofollow' }
}
renderer = HTML.new(render_options)
extensions = {
autolink: true,
fenced_code_blocks: true,
lax_spacing: true,
no_intra_emphasis: true,
strikethrough: true,
superscript: true
}
markdown = Redcarpet::Markdown.new(renderer, extensions)
markdown.render(text)
end
end
Then, in the template, I tried to highlight a test ruby code:
<%= raw rouge_markdown(<<-'EOF'
```ruby
def rouge_me
puts "this is a #{'test'} for rouge"
end
```
EOF
) %>
Note that I needed to specify the language manually, which made me use the 3 backticks way to delimit the code instead of space indentation. I have no clue why the code language autodetection did not work here, perhaps it's a too short code.
In the end this rendered the colors for me nicely:

Exact match in capybara

I'm having a problem with Capybara 2.1 to match exact text.
I have a select form looking like this one :
<select class='tags-select'>
<option>Tag 1</options>
<option>Tag 2</options>
</select>
My test should select Tag 2
page.find('.tags-select', :text => 'Tag 2').click
Otherwise that keeps selecting Tag 1 even with this Capybara config in my helper :
Capybara.configure do |config|
config.match = :prefer_exact
config.exact = true
config.exact_options = true
config.ignore_hidden_elements = true
config.visible_text_only = true
config.default_wait_time = 10
end
I know that I can use some regex here to avoid the problem, but I want to understand what I'm doing wrong with that test.
The main problem, based on the query.rb file, is that the :exact option does not apply to the :text option:
def matches_filters?(node)
if options[:text]
regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
return false if not node.text(visible).match(regexp)
end
So the finder:
page.click_link('id', :text => 'Password', :exact => true)
Will match both of the following elements
<a id="id" href="#">Password</a>
and
<a id="id" href="#">Password Other</a>
Your approach might also be failing because your find method is actually returning the select list rather than the option (ie I believe you are clicking the wrong element). The following will click the right option, however you would still have the exactness problem.
page.find('.tags-select option', :text => 'Tag 2').click
To address the exactness issue, you need to pass the text value to the finder without using the text option.
If you are just selecting an option, use the select method as DevDude mentioned:
page.find('.tags-select').select("Tag 2")
If you really need to do a click instead, you could do:
page.find('.tags-select').find(:option, 'Tag 2').click
The problem lies in the fact that you are using a text selector on an Element with multiple options. You should be using another specification for your selector that includes the option. Also, you should add a value for each option, as they are both equivalent (empty value). I would redo your work like this:
<select class='tags-select'>
<option value="1">Tag 1</options>
<option value="2">Tag 2</options>
</select>
And then try to select the option with this:
page.find('.tags-select').select("1")

Capybara testing value of hidden field

I have a form with a hidden field that contains the current date.
I'm trying to figure out how to write a capybara finder to:
Check that the field is there
Check the value of the field
Is this possible with Capybara?
just do this:
find("#id_of_hidden_input", :visible => false).value
the matcher has_field? works with hidden fields as well. no need to do weird gymnastics with find or all in this context.
page.has_field? "label of the field", type: :hidden, with: "field value"
page.has_field? "id_of_the_field", type: :hidden, with: "field value"
the key here is setting the :type option to :hidden explicitly.
why use a label with a hidden field? this comes in handy if you're using a js library, like flatpickr, that cloaks your original text field to hide it. not coupling your behavior tests to specific markup is always a good thing.
You could also instruct Capybara to not ignore hidden elements globally in your spec_helper.rb or equivalent. The default behaviour can be overridden:
# default behavior for hidden elements
# Capybara.ignore_hidden_elements = false
# find all elements (hidden or visible)
page.all(".articles .article[id='foo']")
# find visible elements only (overwrite the standard behavior just for this query)
page.all(".articles .article[id='foo']", :visible => true)
# changing the default behavior (e.g. in your features/support/env.rb file)
Capybara.ignore_hidden_elements = true
# now the query just finds visible nodes by default
page.all(".articles .article[id='foo']")
# but you can change the default behaviour by passing the :visible option again
page.all(".articles .article[id='foo']", :visible => false)
Examples taken from this article.
its simple you can do it by using find_by_css or by using xpath as follow
page.find_by_css('#foo .bar a') #foo is the id the foo has .bar class
page.find('/table/tbody/tr[3]') #path of the element we want to find

HTML_SAFE allow everything except <span style="font-size: TAG

How do I disallow font size change in html_safe rails 3
I have here a truncated description of an article, and I want to disallow big font sizes in display mode when the user inputs a big font size using tinymce editor
= truncate(event.description.html_safe, :length => 110, :omission => "...")
How can i do that?
You will want to use the sanitize helper before marking it as html_safe. Unfortunately for you, in this case, the blacklist functionality has been removed, so you will need to list literally all of the attributes you do want, in addition to the defaults. It may be easier to use a regex to remove the attribute in question.
Also, for what it's worth, raw(event.description) does the same as event.description.html_safe, but will not blow up on a nil value (not sure what your validation rules are), so it is generally preferred.
Edit:
Sanitize example usage (from http://apidock.com/rails/v3.2.8/ActionView/Helpers/SanitizeHelper/sanitize ):
truncate( raw( sanitize(event.description, :tags => %w(table tr td), :attributes => %w(id class style) ) ), :length => 110, :omission => "...")
Note: Truncating HTML like that can lead to some weird and hard-to-track-down errors, by creating invalid HTML because of cut-off end tags.

haml select_tag with constants

I'm new to Ruby and Haml, so I"m going around in circles on this. Googling isn't giving me any sample code I can use.
I can use the select_tag and populate the list from a table. But I can't figure out how to use a simple static list of items. Can someone change this to be proper Haml? Note: the source table is 'email' and the field is 'status'.
= select_tag(:email, :status, {"canceled", "pending", "success"})
I'm looking to get a dropdown list that just has the items "canceled, pending, success" in it.
The error I get is odd number list for Hash._hamlout.format_script...
Update: I found some sample code that seemed to be what I need, and it doesn't give any errors, but the dropdown box is empty:
= select_tag(:email, :status,{ "canceled" => "1", "pending" => "2", "success"=>"3"})
Here is the HTML it produces:
<select female="2" male="1" id="email" name="email">status </select >
You are using the tag helper rather than the object-oriented helper. Use select
I'd also recommend using options_for_select. Like so:
= select(:email, :status, options_for_select([["canceled", "1"], ["pending", "2"], ["success", "3"]]))
See:
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/select
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select
Got it working! I need to use "Select" instead of "Select_tag". :-)
= select(:email, :status,{ "canceled" => "canceled", "pending" => "pending", "success"=>"success"})

Resources