"private method `gsub'" error caused by addition of a line

hi all. I just added this line to my config/locals/en.yml file and now
rails is blowing up when i try to start mongrel/console.

old version:

title:
main: Who Wants To Be A Millionaire? For Schools

flashes:
quiz:
saved: Your quiz has been saved

new version:

title:
main: Who Wants To Be A Millionaire? For Schools

taggings:
create_failed: “Could not add this tag because {{message}}”

flashes:
quiz:
saved: Your quiz has been saved

error:
/usr/lib/ruby/1.8/uri/common.rb:289:in escape': private methodgsub’
called for {:create_failed=>“Could not add this tag because”}:Hash
(NoMethodError)

I tried it without the quotes originally and it blew up. It still blows
up if i remove the quotes and the the variable part, ie this still
causes the error:

taggings:
create_failed: Could not add this tag because

I don’t understand what’s gone wrong here. If i delete the line then
it’s fine, but what’s wrong with this line? As far as i can tell
there’s nothing in there that would break the formatting syntax.

any ideas welcomed…thanks, max

Can you show your view, please

2010/4/20 Max W. [email protected]

Andrés Gutiérrez wrote:

Can you show your view, please

2010/4/20 Max W. [email protected]

Hi andres - it’s not my view that’s at fault (and it’s actually called
from a controller action as it happens) - i can’t even start the app.
Rails is blowing up when it tries to parse the en.yml file.

What’s weird is if i nest it inside another block, eg change it to this

title:
main: Who Wants To Be A Millionaire? For Schools

flashes:
taggings:
save_failed: Could not add this tag because {{message}}
quiz:
saved: Your quiz has been saved

then it’s fine. But if i change it back to this:

title:
main: Who Wants To Be A Millionaire? For Schools

taggings:
save_failed: Could not add this tag because {{message}}

flashes:
quiz:
saved: Your quiz has been saved

then i get the error:
/usr/lib/ruby/1.8/uri/common.rb:289:in escape': private method gsub’
called for {:save_failed=>“Could not add this tag because
{{message}}”}:Hash (NoMethodError)

Andrés Gutiérrez wrote:

well, can you show me your controller? :slight_smile:

2010/4/20 Max W. [email protected]

Well, yes, but i promise you that it is irrelevant to the problem since
the controller code is not executed before the problem occurs!

def create_tagging
@taggable =
params[:tagging][:taggable_type].constantize.find(params[:tagging][:taggable_id])
@tagging = @taggable.tag_with(params[:tagging][:tag_name], :user_id
=> params[:tagging][:user_id])
unless @tagging && @tagging.id
add_error t(‘taggings.save_failed’, :message =>
@tagging.errors.full_messages.join(", "))
end
end

Sorry, but what is "add_error "? is a rails method? or what?

2010/4/20 Max W. [email protected]

well, can you show me your controller? :slight_smile:

2010/4/20 Max W. [email protected]

Andrés Gutiérrez wrote:

Sorry, but what is "add_error "? is a rails method? or what?

2010/4/20 Max W. [email protected]

it’s effectively a way of putting something into flash[:error], it’s
from a plugin called “flash-message-conductor”. But, at the risk of
sounding like a broken record, the actually call to I18n.t isn’t the
issue, since the app breaks as soon as i try to start it, NOT when the
translate call is made.

Can you reproduce your error with a fresh new Rails application with the
same en.yml?

If so, could you send the entire en.yml and tell us your Rails version?

I guess you mean “config/locales/en.yml” instead of
“config/locals/en.yml”, right?

Really strange issue…

Rodrigo.

Em 20-04-2010 11:17, Max W. escreveu:

Could you reduce it to an absolutely minimal failing yml file?

Perhaps the issue is something like a non-breaking space instead of a
regular space. No idea what errors that would give, if any.

Rodrigo Rosenfeld R. wrote:

Can you reproduce your error with a fresh new Rails application with the
same en.yml?

Hi Rodrigo

I’ve done as you suggested:

  1. i made a new rails app ‘foo’

  2. In my new app ‘foo’ i uncommented the following in my environment.rb

    config.i18n.load_path += Dir[Rails.root.join(‘config’, ‘locales’,
    ‘*.{rb,yml}’)]
    config.i18n.default_locale = :en

athough as a side note, this line, when commented, said

Dir[Rails.root.join(‘my’, ‘locales’

… which seems weird - does locales live in a ‘my’ folder by default,
rather than ‘config’??

  1. Anyway, i set up the new app, with the above line, copied my en.yml
    file into locales, started the app, fine.

  2. Set up a basic route / to the welcome controller, made the welcome
    controller and views/welcome/index.html.erb, which calls t, deleted
    public/index.html and started the app. Fine.

  3. The page loads fine and the translation works, with the problematic
    line back in the position that was making it break before.

What’s weird is if i now deliberately break the syntax of en.yml, eg by
shoving a : in on it’s own line, i only get the complaint when i make
the t call, NOT when the app starts, as is the case in my other app. In
other words, if i add a spurious : to the en.yml in my ‘proper’ rails
app it blows up as soon as i start mongrel.

I don’t know what the difference would be between them. One possible
thing is that in my ‘proper’ rails app i have frozen rails and all of
the gems i use into the vendor folder. Would this have anything to do
with it do you think?

err “hi henrik” that should have read. Finger problem rather than eye
problem :slight_smile:

Hi Henri, thanks.

Yes, i’ve took out everything else and it still breaks, which should
make diagnosis easier. I too suspected it might be some kind of rogue
invisible character breaking it so rather than copy the text into my
post, here’s a download link for the actual file (i copied it into my
public dropbox)

http://dl.dropbox.com/u/846812/en.yml

Is there a nice way in bash/linux to view the contents of a file showing
all of the formatting characters?

thanks again, max

Hi Max.

Please note this comment in a new fresh Rails app:

The default locale is :en and all translations from

config/locales/*.rb,yml are auto loaded.

It means you don’t need to uncomment the following lines if you will be
setting default_locale to :en and put your translations into
config/locales, following Rails conventions.

About your problem, as I suspected before, it is not related to the
locale file itself, but some other code being loaded on Rails startup.
Could you send the stacktrace so we can help you better?

You could try disable your plugins and moving your scripts in
config/initializers to other place to see if the error disappears.

I would start from the stacktrace, then would probably debug the
relevant part where it is breaking to see what is happening.

You can also turn your plugins back one by one if the errors disappear
when you disable all your plugins. Same for the initializers scripts.

I don’t know what the difference would be between them. One possible
thing is that in my ‘proper’ rails app i have frozen rails and all of
the gems i use into the vendor folder. Would this have anything to do
with it do you think?

I don’t think this make any differences here, but you can freeze your
new Rails app as well if you think this might be a problem…

Good luck,

Rodrigo.

Em 21-04-2010 05:40, Max W. escreveu:

Em 21-04-2010 11:44, Max W. escreveu:

into routes to give you translated urls? I should have guessed this

flashes:
(i’m guessing “taggings” is the default). So, it calls gsub on it,
it’s a hash rather than a string, and you get the error because you
can’t call gsub on a hash (it’s a private method).

That’s also why indenting it to be under flashes was fine: it doesn’t
fight with the default top level “taggings” key any more and there’s no
problem.

Does that make sense? I’m not sure if i explained it well.

Completely, you stated it very clear.

Maybe you should add this note to Rails I18n API and as a comment in the
en.yml template, besides stating this behavior in the Rails guides. All
through the railsdoc project. What do you say? This breaks the least
surprise principle, I guess… :slight_smile:

Best regards,

Rodrigo.

I think i found what is causing it, and if i’m right it’s a gotcha of
the highest order. The rule seems to be

“In your .yml file you cannot have a top-level key which has the
same name as one of your controllers and which points to a further
nested set of key-value pairs”

It seems that the controller names (or maybe resource names from
routes.rb) are pushed into the hash of translated terms automatically,
as part of the general mechanism of I18n - i guess this is what hooks
into routes to give you translated urls? I should have guessed this
before because i noticed that if i had these as top-level pairs in my
en.yml file:

en:
#url translations
users: users
account: account
quizzes: quizzes
questions: questions
user_session: user_session

these are all controllers in my app, and i automatically get translated
urls without having to do anything, if the urls contain these words. I
also have a taggings controller, so when i had this:

title:
main: Who Wants To Be A Millionaire? For Schools

taggings:
save_failed: Could not add this tag because {{message}}

flashes:
quiz:
saved: Your quiz has been saved

…the part that was breaking was that i added a top level key,
“taggings”, which points to another hash {“save_failed” => “Could not
add this tag because {{message}}”}. The actual bug was caused because
rails was expecting this to be a string, probably because of the
automatically added entry for “taggings” which DOES point to a string
(i’m guessing “taggings” is the default). So, it calls gsub on it,
it’s a hash rather than a string, and you get the error because you
can’t call gsub on a hash (it’s a private method).

That’s also why indenting it to be under flashes was fine: it doesn’t
fight with the default top level “taggings” key any more and there’s no
problem.

Does that make sense? I’m not sure if i explained it well.

So, another way to state my rule is:

“Controller names CAN be used as top level keys, and must point to a
simple value rather than another hash. If present, these will be
translated in urls.”

or

“If you have a top level key which is the same name as one of your
controllers/resources, and it has further nested translations below it,
kiss your app goodbye”

So to put it in other words, your keys were doubly defined, like this:

en:
foo: bar
foo:
baz: bang

And YAML being as it is (a hash), it allows for only one unique key,
so one of them wins.
When I18n receives a string instead of a hash for nested translation
keys (or the other way round) it gives strange errors.

A problem can arise with default translations, provided by gems or
rails itself, which you know nothing about, that get added to the
I18n.load_paths.
You cannot see the duplication because it’s spread around different
files, sometimes tucked away inside a gem.

A solution might be something that checks the translations for doubled
keys, although I think this is tricky to make.
Another solution might be that I18n gives a more sensible error (or
doesn’t try to interpolate when it’s not a string)

Am I formulating this right?

Iain

Yeah, ultimately the problem was that the ‘tagging’ key was double
defined - one pointed to a string and the other pointed to a hash. The
tricky part was that i only defined the pointing-to-a-hash version in my
yaml file, and the pointing-to-a-string one was invisibly added by
rails. This is the gotcha part.

And yes, it would be good if I18n didn’t blow up but instead just
‘chose’ one of them. To me the most sensible approach would be that
keys explicitly defined in the yaml override anything invisbly added by
rails/I18n/plugins/gems etc.

Hi Rodrigo, i will, it’d be nice to give something back for a change.

thanks a lot
max

Iain H. wrote:

The problem is that I18n can’t choose anything, because when the file
is read, the second one overwrites the first one. This is done outside
I18n.
It’s logical too, because you’re actually saying something like this:
{ :foo => 1, :foo => 2 } which will always result in { :foo => 2 }.

Gem authors should put their locales in front of the load_paths, so
that the locales from gems are overwritten by your own locales.

right, yeah. Is there any way i can make that happen in my
environment.rb do you know? In this case it’s not a problem, i’m just
wondering for the future.