Use another I18n key in an I18n interpolation - ruby-on-rails

This might be I18n-ception but lets say I have an en.yml file as below
en:
my_var: Foo
my_message: "This is a message where I'd like to interpolate I18n's %{my_var}"
Is there a way to indicate to I18n that %{my_var} should be the my_var key in en.yml?
I know I could accomplish it by doing something like
I18n.t 'my_message', :my_var => I18n.t('my_var')
but I was hoping I18n has a way to self reference keys.

This is actually a pretty common question, but the short answer is no, this isn't possible unfortunately :(

Currently I struggle for it... And finally I create a custom yaml type.
in init section.
Psych.add_builtin_type('i18n') { |_type, value|
->(_key, _options) do
value.gsub(/%\{([\w.]+)\}/) do |match|
key = $1.to_sym
if I18n.exists?(key)
I18n.t(key)
else
match
end
end
end
}
I18n.reload!
in en.yml
en:
my_var: Foo
my_message: !!i18n "This is a message where I'd like to interpolate I18n's %{my_var}"
!!i18n apply custom builtin type.

As you said, maybe it is not a so straight solution to call twice from the view to the translation
<%= t("my_message", my_var: t("my_var") ) %>
but gives you the flexibility to call with a variable
<%= t("my_message", my_var: t("my_#{$item[:slug]}") ) %>

Related

HAML conditional tags

I'm currently building a ruby on rails app using HAML as the template language. I'm looking to create a condition that defines a tag dependent on whether it is met or not, otherwise it defines a different tag. I know I could write this like:
- if ordered
%ol
- else
%ul
But this isn't particularly DRY, and would require me to duplicate the bulk of the code. Is there a very straightforward way to get around this problem? Should I be looking into ruby logic to find it?
Thanks
Define a helper. We'll introduce the ordered option to pick the tag, the rest are passed on to the tag.
# app/helpers/application_helper.rb
module ApplicationHelper
def list_tag(ordered: false, **opts)
kind = ordered ? :ol : :ul
haml_tag kind, **opts do
yield
end
end
end
And then,
-# some_view.html.haml
%p
Here's a list:
- list_tag ordered: false, class: 'some_class' do
- #list.each do |item|
%li
= item
If you need to do this logic in different views I think there are two approaches you can follow:
1. Make a partial and render it where you need this. If you need to pass variables use local_assigns
_my_list.html.haml
- if ordered
%ol
- else
%ul
use it
render 'partials/my_list', ordered: ordered
2. Make your own helper
def my_list(ordered)
if ordered
content_tag(:ol, class: 'my-class') do
# more logic here
# use concat if you need to use more html blocks
end else
content_tag(:ul, class: 'my-class') do
# more logic here
# use concat if you need to use more html blocks
end
end
end
use it
= my_list(ordered)
You can keep your ordered variable outside of the view and deal with the whole logic inside the helper.
If you ask yourself what to use, well the first answer from here is pretty good.
You can use the content_tag method as follows. content_tag documentation
= content_tag(ordered ? "ol" : "ul")
If you need it in several occasions you can put that in a helper method
module Listhelper
def list(ordered, html_options = {})
= content_tag(ordered ? "ol" : "ul")
end
end
Call it from the view with = list(your_variables)

Best way to handle data attributes in Slim

I was evaluating Slim as a replacement for HAML in a personal project, and it doesn't appear to handle HTML5 data attributes as gracefully as HAML. I was hoping someone may have also run into this, or may have known about an option/syntax I haven't yet found in their docs.
HAML allows you to define HTML 5 data attributes simply by using nested hashes like so:
%a{data: {key1: 'val', key2: 'val'}}
resulting in
<a data-key1='val' data-key2='val'></a>
There are multiple ways in Slim
As Hash
Attributes which will be hyphenated if a Hash is given (e.g. data={a:1,b:2} will render as data-a="1" data-b="2")
Use it directly as "mu is too short" mentioned, quite intuitive.
a data-title="help" data-content="foo"
Use Ruby code. I often do this and rarely above.
= link_to 'foo', bar_path, data: {a: 'a', b: 'b'}
Use the splat operator:
h1#section-title*{'data-url'=>'test', 'data-id'=>'test'} = #project.name
.your-class*{data: {first_attribute: 'first value', second_attribute: 'second value'} }
Will produce
<div class="your-class" data-first_attribute="first value" data-second_attribute="second value"></div>
I prefer this kind to fix...
#products.each do |product|
.module data-id=product.id
It is working for me

Rails HAML helper to display a list item

I am having difficulty getting my helper to display a list item. The markup looks like the following:
- #bars.each do |bar|
<% display_bar(bar) %>
The actual helper looks like the following:
module MyHelper
def display_bar(bar)
type = bar.type
concat(%li.type)
concat(%b some text)
concat(%i some more text)
end
end
What am I doing wrong here?
Such things has to be implemented via partials. Or see 5.
<% won't show you anyting. You're in Haml. It's ERb stuff (but even there it wouldn't have shown anything: you'd forgotten the = sign, it should have been <%=).
About concat(%li.type): you cant put your markup inside your Ruby code. Ruby knows nothing about your %li "code".
Amokrane Chentir already mentioned.
You're trying to reinvent the wheel. Rails already provides magnificent helper for such simple cases.
Take a look:
= content_tag_for(:li, #bars) do |bar|
%b= bar.title
%i= bar.id
UPD: content_tag_for sets styles/ids for each li tag based on the current model instance that makes it easy to implement styling/scripting in the future.
The name of your helper is display_bar not display_event.
You should use = instead of <% %>
- #bars.each do |bar|
= display_event(bar)
EDIT
Oops didn't read carefully the content of display_bar method, as #jdoe mentioned you can't use Haml markup syntax in your Ruby code.

What's happening when Ruby shows something like this: #<Role:0x11157b630>?

I remember coming across this when first watching some Ruby videos, but I can't find it again. When Ruby shows something like this:
#<Role:0x11157b630>
,what is going on?
I have three roles (admin/staff/client) and I would like to show one of these, not
#<Role:0x11157b630>.
Any idea how I could do that?
Cheers!
What you're seeing is just a representation of the instance you've got. Say you have a title attribute on the class Role, you could, instead of logger.debug #role do something like logger.debug #role.title. If you want just doing logger.debug #role to print out something more useful, define a to_s method on Role.
Appending an inspect method should show some more details.
#role.inspect
That's what the default implementation of to_s looks like; class name followed by memory location. You can define your own version if you like:
def to_s
"My name is #{#name}"
end
When I started with rails I had strange bugs sometimes when I made something simple like:
<% #posts.each do |post| %>
....
<% end %>
I would get those strange outputs under the list of posts.
For example:
#<Post:0x11157b630>#<Post:0x11157b630>#<Post:0x11157b630>
Turns out I accidentally put a "=" in there before the loop.
<%= #posts.each do |post| %>
....
<% end %>
According to The Secret of Object#to_s, the number in #<Role:0x11157b630> is double the object's object_id in hexidecimal.

What is the best way to return multiple tags from a Rails Helper?

I want to create a hidden field and create a link in one helper and then output both to my erb.
<%= my_cool_helper "something", form %>
Should out put the results of
link_to "something", a_path
form.hidden_field "something".tableize, :value => "something"
What would the definition of the helper look like? The details of what link_to and the form.hidden_field don't really matter. What matters is, how do I return the output from two different calls.
There are several ways to do this.
Remember that the existing rails helpers like link_to, etc, just output strings. You can concatenate the strings together and return that (which is what I do most of the time, if things are simple).
EG:
link_to( "something", something_path ) + #NOTE THE PLUS FOR STRING CONCAT
form.hidden_field('something'.tableize, :value=>'something')
If you're doing things which are more complicated, you could just put that code in a partial, and have your helper call render :partial.
If you're doing more complicated stuff than even that, then you may want to look at errtheblog's block_to_partial helper, which is pretty cool
So far the best I have come up with is:
def my_cool_helper(name, form)
out = capture { link_to name, a_path }
out << capture { form.hidden_field name.tableize, value => 'something' }
end
Is there a better way?
Using safe_join.
I typically prefer just concatenating with +, as shown in Orion Edwards's Answer, but here's another option I recently discovered.
safe_join( [
link_to( "something", something_path ),
form.hidden_field( "something".tableize, value: "something" )
] )
It has the advantage of explicitly listing all of the elements and the joining of those elements.
I find that with long elements, the + symbol can get lost at the end of the line. Additionally, if you're concatenating more than a few elements, I find listing them in an Array like this to be more obvious to the next reader.
If you want to buffer other output which apart from string then you can use concat instead of +.
see this - http://thepugautomatic.com/2013/06/helpers/
def output_siblings
div1 = tag.div 'some content'
div2 = tag.div 'other content'
div1 + div2
end
just simplifying some other answers in here.
This worked for me.
def format_paragraphs(text)
text.split(/\r?\n/).sum do |paragraph|
tag.p(paragraph)
end
end

Resources