i18n rails lazy look-up from a helper? - ruby-on-rails

I'm using I18n lazy look-up e.g. t('.field') with Rails 4 and from both controllers and views, I18n does the appropriate look-up. But it doesn't from helpers and I was wondering whether there is a reason why or a solution?
It seems that if I create a helper function, say 'help()', and call it from multiple different views, I have to define the same I18n strings twice (yes, I use aliases ;-) ). So I need to define both
(views), view1.help.field ...and...
(views), view2.help.field
Any nice ways to avoid this?

I18n.t uses the current request as a context. So when you call:
<%= t('.hello') %>
From apps/views/users/show.html.erb it will use the key users.show.hello. However on apps/views/pets/show.html.erb the translation is missing as you have noticed.
It does not matter if you are calling it in your view or in a helper since both use the view context.
If you want to avoid this you would simply state the translation key explicitly:
<%= t('users.show.hello') %>
If you want to use dynamic lookup and fall back to a fixed key you could do it like this:
<%= t('.hello', default: t('users.show.hello')) %>

Related

Is there a way to store a variable while in a view in Rails?

As stated above. Is there a way to store a variable in a view in rails for later use without setting it in the controller function for that view?
ex.
<% bagel = #bagels.active_bagel %>
...
<%= bagel.delicious? %>
This can be achieve by this
#app/views/xyz/
#1. make a hidden html input field
<input type ="hidden" id = "you_id" value = "<%= yourvariable %>"
#if u want to access that variable in every view write your code in app/helper/application_helper
#otherwise write in the specific helper something this kind of code
def yourvariable
#your code
return somthing which you want in view
end
and from that html tag access the value
If you are inside of the same view template (instead of being in another template) then sure - go ahead and set variables just as in your example. We do this all the time.
...but if you go to another template to use this variable... then it should be set in the calling scope, not be a magic variable that is used in multiple templates, if you see what I mean :)
it's possible to do it, but it's not considered good practice. If you can do it in the controller, then you probably should.
In your case... I don't think there should be a problem with you calling active_bagel more than once on the #bagels variable.
If you are trying to do this because you think it might plausibly be slow someday.... I'd recommend waiting until it is actually slow... then figuring out how to optimise for speed then. If it's for some other purpose... then tell us what that is and we'll figure out a better way.
You can use data-attributes.
tag("div", class: :bagel, :data => {bagel_id: 3})
Then use jQuery.data() to retrieve the value.

Different text i18n in Rails

In my rails application I have a partial(file1/_partial.html.erb) that must be rendered in Two form file2/form1.html.erb and file3/_partial.html.erb, I'am wondering what is the best solution to i18n a text in the partial
actually what I'm doing is
<%= (t :to_ranslate, :scope => 'file2.form1' ) %>
file2:
form1:
to_ranslate: "translated"
and this is working but I'm wondering if there is a solution where can I have different translation based on template so for the same partial i will have different text based on where it is rendered
If you do not need different translations when included in different templates you can just use the "Lazy Lookup" as described here in 4.1.4: http://guides.rubyonrails.org/i18n.html#looking-up-translations
It also applies to rendering partials, for example:
in file1/_partial.html.erb
<%= (t '.to_translate') %>
in the translations.yml:
en:
file1:
_partial:
to_translate: "translated"
For using different translations depending on where you render from, like so (untested but the principle should work):
in file1/_partial.html.erb
<%= (t "#{translation_scope.to_s}.to_translate') %>
in file2/form1.html.erb
<%= render '/file1/partial', translation_scope: 'file2._partial' %>
in file3/_partial.html.erb
<%= render '/file1/partial', translation_scope: 'file3._partial' %>
in the translations.yml
en:
file1:
_partial:
to_translate: "translated"
file2:
_partial:
to_translate: "translated"
file2:
_partial:
to_translate: "translated"
This would have the advantage that it still works when you do not specify the translation_scope (falling back to its default) and also when you pass it.
This is the reason why i would prefer it over using the scope: 'xx' like you did.
And the whole interface seems a little bit like inheritance in the views with the same structure everywhere.
In a more abstract and complicated way it would also be possible to use fallbacks for this, but in the example here this would be overkill. On larger projects it might make sense to implement something like this.
as far as i know rails is internationalized by default out of the box
just place the text as you would normally and it should work no problem.
reference: http://guides.rubyonrails.org/i18n.html

HTML "class" and "id" attributes using hyphens instead of undescores in Rails 4

Rails fills the id and class of an element generated with various input tag helpers (text_field for example) by itself. For example, in a form_for(#athlete), adding a text_field :height generates
<input name="athlete_height" ... />
I am a strong advocate of the use of hyphens in HTML ids and classes (and in CSS selectors consequently) (by reading MDN, because I like to write using the conventions dictated by the language - like in data-stuff - and because it's just better looking).
I read here and there that in Rails you can't fix this. This would be quite disappointing.
Do you know a way around this problem?
I'm afraid, underscores are hardcoded. From https://github.com/rails/rails/blob/master/actionview/lib/action_view/helpers/form_helper.rb, lines 446-450:
options[:html].reverse_merge!(
class: as ? "#{action}_#{as}" : dom_class(object, action),
id: (as ? [namespace, action, as] : [namespace, dom_id(object, action)]).compact.join("_").presence,
method: method
)
you can always provide your own id and class, or patch code of ActionView, which may be hard, tedious and error-prone
Recently ran into this very same situation and #lobanovadik has a good solution: just write your own helper method.
# app/helpers/dom_helper.rb
module DomHelper
dom_id_helper(model)
dom_id(model).dasherize
end
end
# view.html.erb
<%= link_to "Text", path, id: dom_id_helper(#model) %>
#=> Text
This has the benefit of not monkey-patching Rails or messing with any default methods/configuration. Thus, you won't "break" any updates to Rails.
It also gives you greater flexibility because you can now use both dashes or underscores depending on the situation. For instance, let's say there's a gem that expects IDs to have underscores...you won't break it.
As a personal preference, I always append _helper to all my own helper methods, so that I know it's a helper method and that it came from me and not from Rails (easier to debug).

Should Rails helpers assume an instance variable exists or should they receive them as parameters?

I'm wondering if there's a specific programming principle (Demeter?) that supports the idea that Rails helpers should never use controller instance variables, rather, they should receive such variables as function parameters. For example, assume my ChickensController#squawk action creates an instance variable called #egg. Furthermore, assume the squawk view contains a call to a helper called cockadoodledoo, implemented like so:
def cockadoodledoo
#egg.to_s
end
Would it be better or unnecessarily verbose to pass #egg as a parameter, such that the view calls cockadoodledoo(#egg) and for the helper to resemble:
def cockadoodledoo(egg)
egg.to_s
end
I hope one of you happy hackers is bored enough on a Friday afternoon to assert an answer. Cockadoodledoo!
This question here is similar, but was never accurately answered.
Receive them as a param. Otherwise, as the app grows, it gets very difficult to trace where the instance vars are being set when refactoring, troubleshooting, etc.
Also, I believe there's a general best practice to only use instance vars in views within the initial template...and from there you should pass the var into helpers and other partials.
I'd say you should always pass the variables explicitly to your helper for 2 reasons:
you control exactly what you do
above all, you can test your helper
I don't know if there is any named principle governing this sort of thing but I would pass an argument. Not only will the argument make your helper easier to test and your application's data flow easier to follow but it will also let you use one helper for a single instance as well as a list; if you pass an argument then both:
<%= cockadoodledoo #egg %>
and:
<% #eggs.each do |egg| %>
<%= cockadoodledoo egg %>
<% end %>
will work as expected without introducing a special cockadoodledoo that handles a list in #eggs rather than a single #egg.
Since helper messages are mixed in to all controllers, hence available to all views (including partials and layouts), it's always wise to establish a clear contract - the parameters.
The only exception I could think of is when a instance variable is also available to all views and controllers, like a menu or something similar.

Rails 3: Where should a helper that uses h (i.e. html_escape) live?

I'm writing a webapp in Ruby on Rails 3. Rails 3 automatically escapes any potentially-bad strings, which is generally a good thing, but means if you assemble HTML yourself, you have to call html_safe on it.
I have a Card model, which has several text fields, the contents of which are not trusted (may contain evil HTML or script). I have a function which performs a few transforms on one of these text fields, using other knowledge about the specific Card, to produce HTML output. I want to embed the HTML produced by this function in several places throughout several parts of my app.
Conceptually, this helper is to do with the View. However, I can't find any way to write functions in my View files; it seems they have to go in Helpers or the Controller/Model.
Since this function is very much specific to a Card object, the next best option would be to have a function inside my Card model card.rb:
class Card < ActiveRecord::Base
[...]
def format(unsafe_text)
initial_text = h unsafe_text # aka html_escape unsafe_text
# assembles HTML output based on initial_text and fields of self
output_text.html_safe!
end
Then I'd like to call this in assorted views by doing things like:
Rules text: <%= format(#card.rulestext) %>
However, there's a big problem here as well. In the Card model card.rb, I am able to use the html_safe! function, but I'm not able to use h or html_escape. It seems that the h and html_escape functions are only available in ERB views, not in the helpers or controllers!
There are a few workarounds. I can make format not sanitize its input, and go
Rules text: <%= format(h(#card.rulestext)) %>
But that's both prone to dangerous slipups (one missing h() and we've got problems) and is very non-DRY. At the moment I'm using a partial to gain access to the h() function:
(in a normal view)
Rules text: <%= render 'formattext', :text=> #card.rulestext %>
(app/views/shared/_formattext.html.erb)
<%= #card.format(html_escape(text)) %>
But this still feels dangerous. All I have to do is make single forgetful call to format(sometext) in a view, rather than calling render 'formattext', :text=> sometext, and I've got unescaped text running around.
Is there any better way to do this? Is there a way to write helper functions to live in the View rather than the Model or the Controller?
Place the logic that does your view assembly into a CardHelper:
app/helpers/card_helper.rb
class CardHelper
def rules(card)
initial_text = h card.rules_text
# assembles HTML output based on initial_text and fields of card
output_text.html_safe
end
end
It's not clear from your example whether you want to format several fields via the format method. If that's the case, then you might be able to do:
class CardHelper
def format(card, attribute)
initial_text = h card[attribute]
# assembles HTML output based on initial_text and fields of card
output_text.html_safe
end
end
You can use this helper like any other:
class CardsController
helper CardHelper
end
and in your views:
<%= rules(#card) %>
or
<%= format(#card, :rules) %>
Escaping the content for view is a View responsibility, this is the reason why the h helper is not available in controllers or models.
Still, I don't understand why can't you simply sanitize the content in the view.
Also note that, in Rails 3, you don't need to call the h helper.
Content is sanitized automatically by default unless you flag it as html_safe!.
The main reason why is not logically true to use the h helper in the model is because the model should work view-independently. In other words, the model should not care whether the content is going to be embedded in a HTML document or JSON file (which requires a different escaping approach compared to HTML).

Resources