Difference between t(:str) and t :str in ROR - ruby-on-rails

i am new to ROR.. i am having a doubt in internationalization commands.
in some case we were using <%=t :str_use%>
and in some cases we were using <%= t(:str_use) %>
what is the difference between these two
when should i have to use 1st and when to use the second one..
Pls give some ideas regarding this.
i am having a view file with in that i am having lot of strings i wanna to internationalization them.
in some cases i am having like <td>Use</td>
and in some cases
<% if use %> Use <br />
<% else %>

This is ruby's syntax, not specifically ror ;)
Both are the same. Ruby can guess the parenthesis even if they're not there.
So it's completely up to you.

There's no difference between t :str and t(:str) — they both call the method t with the symbol :str as an argument. In Ruby, parentheses around arguments are optional.
But these are both different from t: str, which is Ruby 1.9 shorthand for the hash {:t => str}.

When to use t(:str)? When you want to chain further methods.
Try this in Rails:
t 'some.translation'.capitalize
t('some.translation').capitalize
First approach will return:
Translation
..as in the second half the identifier after the period, instead of translating the whole text. This, from what I can tell, is because you are passing the argument 'some.translation'.capitalize, not calling capitalize on the return, like the second example.

Related

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).

Truncate + Sanitize in Rails Views

I ran into a small problem today when I was trying to use sanitize and truncate in conjunction with one another to create an excerpt for my blog. Each of the methods worked by itself, but used together it would only truncate. I tried both of these syntaxes (the former being recommended in a blog post titled "Six Ruby on Rails Tips & Tricks"):
<%= truncate(sanitize(post.content), length: 580) %>
<%= sanitize(truncate(post.content, length: 580, separator: '<p>')) %>
And then I tried to put truncate in the controller, and sanitized that object in the view, but still no.
Finally I got it to work like this:
<%= sanitize(post.content.truncate(580, separator: '</p>')) %>
What I'd like to know is why didn't it work when I wrapped a method in another method? What's the difference with the last way I tried it?
TIA
'bondibox'
This worked for me:
<%= sanitize(article.description[0..100]) %>
Truncate and Sanitize are not included in controllers, they are part of ActionView::Helpers::TextHelper and ActionView::Helpers::SanitizeHelper respectively. These modules are not included per default in a controller, so you cannot use it in there.
However, both are included in the views (templates) and you can therefore use them in there. You can include the modules stated above in a controller class to use them there, but i would not recommend it.
The reason the second statement works is because Rails extends some base objects from Ruby, like String with several methods. so you actually call sanitize on a truncated version of a string truncated by a method of String.
The combination of both is a bit tricky. I cannot really tell you why the combination of the module version of sanitize and truncate does not work without a little more info. What exactly did you try to accomplish here (examples?)
An old question, but landed on it while struggling with this myself. The problem is that sanitize will not strip all HTML tags, but just a few that are defined by default, such as script. So you have to specifiy that no tags are allowed:
truncate sanitize(post.content, tags: [], attributes: []), length: 580
Thanks Ruben,
<%= sanitize(article.description[0..100]) %>
is working perfectly in Rails 6.
Our experience is that all other combinations w/ truncate aren't working.

Why do I get nil objects iterating an array in an ERB template?

I am new to Ruby and currently trying a few experiments.
I am confused about these scripts:
<%=#myworlds[2].topic%>
and
<% id = 1 %>
<%=#myworlds[id+1].topic%>
#mywodrld is an instance of a model and topic is the field. When executing the first one, the program runs correctly. When I run the second script, I get the following error:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.topic
What causes the nil object?
When I try your approach, I can't replicate your problem. It works fine for me. My guess is that you might use the variable id somewhere else also and that when you call #myworlds[id+1].topic id has some other value. But as I said, only a guess.
However, I recommend that you use another syntax when looping through collections of models in Ruby. Try something like this:
<% #myworlds.each do |myworld| %>
<h1><%= myworld.topic %></h1>
<% end %>
And if you really need the value of the iterator, you could always go with:
<% #myworlds.each_with_index do|myworld, i| %>
Where i keeps track of the current index in the array. Another good thing with this is that id no longer exists in memory after the block ended.
Are you sure that you have no other differences between these two code snippets?
In your comment you say that you have #myworlds[#id+1], in the original question you say #myworlds[id+1] (local variable versus instance variable). Can you show the exact code?
Both scripts are OK. You can create variables in one <% %> block, and you can use them in another one (if they are in the same .erb file, of course).
The error message says that your array has no element with index #id+1 or id+1. You have to debug the value of the expression used for the index. I guess that there is somewhere some small mistake, like a typo.
What is the output of your debug(#myworlds[#id+1]) statement when #myworlds[#id+1].topic raises the error?
Also try to debug the value of id:
<pre>The id = <%= debug(id) %> (<%= id.inspect %>)</pre>
(Depending on your version of Rails you may want to use h( id.inspect ))
I'm guessing but for some reason id+1 is probably not equal to 2.
To check the value of id+1 you can do that :
raise (id+1).inspect
Inspect is very useful is you want to see what is in an object :)
I think I know how to solve the problem is. You are trying to each an array data from a model, but u use the parameter [#id+1]. No matter the "id" is global or local variable, but the problem is in the end of array, there are no array with index "id+1". You should add another parameter to prevent the unrecognized parameter.
Try this
if((#myworlds.length-1) > #id)
#id = #id+1
end
:D
It looks like you're looping over an array, but possibly using a for or while loop to accomplish it, rather than use an [].each. Your sample code doesn't give us enough information to work from so we're shooting in the dark attempting to help you.
Manually creating your index then trying to walk the array tends to run into problems where you either miss the first or last item, or you go too far and get the error you are seeing. Because each returns only the items in the array it can't do that.
Something like this might work better:
<% #myworlds.each do |world| %>
...
<%= world.topic %>
<% end %>
I didn't see the answer #DanneManne gave before I wrote my response. I think he's got the right solution.

raw vs. html_safe vs. h to unescape html

Suppose I have the following string
#x = "<a href='#'>Turn me into a link</a>"
In my view, I want a link to be displayed. That is, I don't want everything in #x to be unescaped and displayed as a string. What's the difference between using
<%= raw #x %>
<%= h #x %>
<%= #x.html_safe %>
?
Considering Rails 3:
html_safe actually "sets the string" as HTML Safe (it's a little more complicated than that, but it's basically it). This way, you can return HTML Safe strings from helpers or models at will.
h can only be used from within a controller or view, since it's from a helper. It will force the output to be escaped. It's not really deprecated, but you most likely won't use it anymore: the only usage is to "revert" an html_safe declaration, pretty unusual.
Prepending your expression with raw is actually equivalent to calling to_s chained with html_safe on it, but is declared on a helper, just like h, so it can only be used on controllers and views.
"SafeBuffers and Rails 3.0" is a nice explanation on how the SafeBuffers (the class that does the html_safe magic) work.
I think it bears repeating: html_safe does not HTML-escape your string. In fact, it will prevent your string from being escaped.
<%= "<script>alert('Hello!')</script>" %>
will put:
<script>alert('Hello!')</script>
into your HTML source (yay, so safe!), while:
<%= "<script>alert('Hello!')</script>".html_safe %>
will pop up the alert dialog (are you sure that's what you want?). So you probably don't want to call html_safe on any user-entered strings.
The difference is between Rails’ html_safe() and raw(). There is an excellent post by Yehuda Katz on this, and it really boils down to this:
def raw(stringish)
stringish.to_s.html_safe
end
Yes, raw() is a wrapper around html_safe() that forces the input to String and then calls html_safe() on it. It’s also the case that raw() is a helper in a module whereas html_safe() is a method on the String class which makes a new ActiveSupport::SafeBuffer instance — that has a #dirty flag in it.
Refer to "Rails’ html_safe vs. raw".
html_safe :
Marks a string as trusted safe. It will be inserted into HTML with no additional escaping performed.
"<a>Hello</a>".html_safe
#=> "<a>Hello</a>"
nil.html_safe
#=> NoMethodError: undefined method `html_safe' for nil:NilClass
raw :
raw is just a wrapper around html_safe. Use raw if there are chances that the string will be nil.
raw("<a>Hello</a>")
#=> "<a>Hello</a>"
raw(nil)
#=> ""
h alias for html_escape :
A utility method for escaping HTML tag characters. Use this method to escape any unsafe content.
In Rails 3 and above it is used by default so you don't need to use this method explicitly
The best safe way is: <%= sanitize #x %>
It will avoid XSS!
In Simple Rails terms:
h remove html tags into number characters so that rendering won't break your html
html_safe sets a boolean in string so that the string is considered as html save
raw It converts to html_safe to string
Short and Simple
Let's assume we can't trust user input.
Bad:
user_input.html_safe # asking for trouble
Good:
user_input.html_escape # or
h(user_input) # in some view
Inputs we control:
trusted_input_only.html_safe
that should be fine. but be careful what your trusted inputs are. They must only be generated from your app.

Rails: Flatten array in parameter

I'm trying to flatten an array for my form.
def update
#tour = Tour.find(params[:id])
params[:tour][:hotel_ids][0] = params[:tour][:hotel_ids][0].split(',')
...
This results in:
"hotel_ids"=>[["1","2"]]
Naturally I want it to be
"hotel_ids"=>["1","2"]
My Form:
<%= text_field_tag 'tour[hotel_ids][]', nil %>
Hope anyone can help with this.
EDIT
I've gotten it to work, somehow. This might be a bad way to do it though:
I changed the text_field that get's the array from jquery to:
<%= text_field_tag 'tour[h_ids][]', nil %>
then in my controller I did:
params[:tour][:hotel_ids] = params[:tour][:h_ids][0].split(',')
And this works, I had to add h_ids to attr_accessor though. And it will probably be a big WTF for anyone reading the coder later... but is this acceptable?
This is ruby!
params[:tour][:hotel_ids][0].flatten!
should do the trick!
ps: the '!' is important here, as it causes the 'flatten' to be saved to the calling object.
pps: for those ruby-related questions I strongly suggest experimenting with the irb or script/console. You can take your object and ask for
object.inspect
object.methods
object.class
This is really useful when debugging and discovering what ruby can do for you.
Simply use <%= text_field_tag 'tour[hotel_ids]', nil %> here, and then split like you do in example.
What really happens in your example is that Rails get param(-s) tour[hotel_ids][] in request and it thinks: "ok, so params[:tour][:hotel_ids] is an array, so I'll just push every value with this name as next values to this array", and you get exactly this behavior, you have one element in params[:tour][:hotel_ids] array, which is your value ("1,2"). If you don't need (or don't want) to assign multiple values to same param then don't create array (don't add [] at the end of the name)
Edit:
You can also go easy way (if you only want answer to posted question, not solution to problem why you have now what you expect) and just change your line in controller to:
params[:tour][:hotel_ids] = params[:tour][:hotel_ids][0].split(',')
#split returns array and in your example you assigned this new array to first position of another array. That's why you had array-in-array.

Resources