Rails I18n _html suffix rule and translate helper called from a controller - ruby-on-rails

According to ruby docs, the translate (or t) helper delegates to I18n#translate but also performs several additional functions, among which is: "it’ll mark the translation as safe HTML if the key has the suffix _html".
I would expect that it should work equally in both views and in controllers, but in my experience it doesn't: t(:hello_html) does work as expected in views (marks the translation as html_safe), but it does not mark the result as safe html when invoked from a controller.
To reproduce the problem you could add hello_html: '<i>Hello world</i>' to your locales/en.yml and flash.now[:notice] = t(:hello_html) to any convenient action of any controller. For me that resulted in an escaped html markup in a flash messages area which was an unexpected outcome for me.
My questions are:
is there anyone else who experienced or is able to reproduce this problem?
what is it: a rails bug, a feature, or just my project's unique "oddity"?
is there any easy way to work this around?
(Tested in rails 3.2.11 and 3.2.13)

You are correct about this functionality not being available to controllers given that the overloaded .t method is defined in ActionView::Helpers::TranslationHelper. I think this is probably an oversight as opposed to an actual bug.
Off the top of my head there are 2 ways you can get around this in your project :
Call .html_safe in your controller (this worked for me in a quick test).
flash[:notice] = t(:hello_html).html_safe
Send the translation key as the flash message as opposed to the actual message :
Controller :
flash[:translate_notice] = :hello_html
View :
%div= t flash[:translate_notice]
Granted, the latter option might get a bit messy if you need to pass interpolations, YMMV.

Related

Ruby On Rails - Use "Format" As A URL GET Parameter?

I have a search page where I update the URL params on the page as filters are added or removed by the user. This allows me to deep link into the page (ie. going to /search?location=new+york&time=afternoon will set the location and afternoon filters).
I also have a filter named format. I noticed that passing in ?format=whatevervalue to the URL and then reloading the page with that param causes Rails to return a Completed 406 Not Acceptable error. It seems that format is a reserved Rails URL parameter.
Is there anyway to unreserve this parameter name for a particular endpoint?
In the context of an URL in Ruby on Rails there are at least four reserved parameter names: controller, method, id, format.
You cannot use these keys for anything else than for their intended purpose.
If you try to you will override the value internally set by Rails. In your example by setting ?format=whatevervalue you override the default format (html) and your application will try to find and render a whatevervalue template instead of the html formatted template. This will obviously not work.
Fun fact: Instead of using the default Rails path format like /users/123/edit you could use query parameters instead like this: /?controller=users&id=123&method=edit&format&html.
My suggestion is: Do not try to fight Rails conventions. Whenever you try to work around basic Rails conventions it will hurt you later on because it makes updates more difficult, common gems might break, unexpected side-effects will happen. Just use another name for that parameter.

Are There Any Rails Modules or Classes Which Provide Frozen HTML Content Type Strings?

Ive been searching through source for a while, and it appears to me that there are no given Rails tools for retrieving the String representation of various HTML content types. Ive also found this to be a very difficult concept to search for in general.
What I want is something like this:
Mime::Mimes::CONTENT_TYPE_JSON = 'application/json'.freeze
or, Mime::Mimes::CONTENT_TYPES[:json] etc.
...because I want to do a lot of things like some_value == 'application/json' or some_value = 'application/json' etc.
I want to use the expression "application/json" often, and I dont want to create new String instances for something that is pretty well within the domain of web application development. Ive thought of creating my own app consts or vars so I dont have to allocate HTML Content Type strings more than once, but also feel this should just be available for me in any web application framework (at least, those written in languages where every string is a new memory allocation).
Is there a better tool or resource within the Rails 5 source that I am missing that allows easy retrieval of content type strings? Do I have to get a gem / create my own for this?
Note: Im away of how heavy of an "optimization" this may appear to be. Let's then entertain this query from a position of being pragmatic about organizational style, for a project that requires elimination of any duplication of domain-specific string literals, and to keep them symbolized or as some frozen const. Let's pretend its a personal project for the sheer joy of experimenting with such style!
There is a shorthand for it:
Mime[:json]
Mime#[] -
https://github.com/rails/rails/blob/e2efc667dea886e71c33e3837048e34b7a1fe470/actionpack/lib/action_dispatch/http/mime_type.rb#L41
which uses
Mime::Type#lookup_by_extension -
https://github.com/rails/rails/blob/e2efc667dea886e71c33e3837048e34b7a1fe470/actionpack/lib/action_dispatch/http/mime_type.rb#L149
If you want to get the actual content type you might need to call a #to_s on it:
Mime[:json].to_s
Creating a new module to facilitate simple storage and retrieval using the ActionPack Mime::Type system would work as follows:
# Build hash of type name to value, e.g., { xml: "application/xml" }
CONTENT_TYPES = {}.tap do |simple_content_types_hash|
# Get each registered Mime Type
Mime::EXTENSION_LOOKUP.each do |mime|
simple_content_type_hash[mime.first.to_sym] = mime.last.instance_variable_get("#string").freeze
end
end.freeze
Note: the above is untested, its just a generalization of what I am looking for. Thanks to #Fire-Dragon-DoL for the tip.
This could be added via an initializer, patched into an existing module, or into a new helper module.

MVC Routing - Generate the Route URL With Good Coding Standards

I'm learning how to do routing in MVC. It seems to me that the routing API only solves half the problem. I can easily see how to map incoming URLs to controller actions and params. However, it is not obvious to me how to generate these routed URLs in the source code of my pages.
For example, in one of my views, I use this code to get the route URL:
<a class="listingResult" href="#Url.RouteUrl("ListingSEO", new { id = Model.Listing.ID, seoName = ListingController.SeoName(Model.Listing.Title) })">
This seems like poor coding practice to me for several reasons:
If the route changes in the future, I may have many places in my View code that will need updating.
The View now requires knowledge of the ListingController (maybe this is not a big deal?)
I've lost strong typing on my input params, and if I misspell the param names, my code is broken, but this doesn't generate compile warnings.
How do I observe good coding standards when I am generating route URLs? The alternative seems to be putting static functions in the controller to generate routes, which would at least address concerns #1 and #3. If I worked with you and you saw the code above, how unhappy would you be?
My recommendations:
Generate URLs in the ViewModel, not the View: This will keep your views cleaner and logic free. You can pass the UrlHelper instance from the controller to the ViewModel, which will also help for my next point...
Use a strongly-typed URL generation technique: Such as delegate-based, expression-based or code generation.
One of the purposes of using named routes is to abstract the controller/action. Your named routes shouldn't really change. At the most, you'd just change the controller/action they hit, but that happens seamlessly behind the scenes because you're using named routes.
Your view requires knowledge of the controller because you've added a dependency on it. This is bad for a number of reasons. There's many different ways you could handle this that wouldn't require a dependency on the controller, depending on what it is you're actually doing here, but at the very least, you should simply use a utility class, so at least it wouldn't be controller-specific.
The route params are intentionally not strongly-typed, because routes are flexible by design. You can pass anything you want to the action, with or without a parameter to catch it (you can use something like Request to get at it without a param).

Execute arbitrary rails 4 controller action, render to string

In Rails 4, how does one execute an arbitrary controller action and render the response to a string?
This is obviously a bad practice, but there are circumstances when it becomes very difficult to avoid:
You are making an offline copy or e-mail attachment of a dynamically rendered pdf (or any self-contained response).
Aforementioned response involves views and controllers not under your control, or in external gems.
Aforementioned views involve layouts and dozens of partials using relative paths and custom template rendering engines.
In some circumstances (when calling from another controller), it is possible to eliminate the dependency on the controller by replacing any data needed by the view. However, this typically still breaks the view rendering, as relative paths can no longer be passed to the render function within partials (among other issues).
So I haven't actually done this. Or anything like it.
But I remembered you can get a Rack app object for any arbitrary rails action. So it seemed like you could use that rack-compat interface to all an arbitrary action and get a response internally, without actually having to make an http request.
Googled around things I vaguely remembered to put the pieces together, and got this, which I have tried out in a very simple dummy app I made, and it seems to work:
rack_env = Rack::MockRequest.env_for("/some/url?foo=bar")
rack_app = SomeController.action(:index)
(status, headers, rack_body) = rack_app.call(rack_env)
str = rack_body.body
I was surprised to need to call #body on the thing I already thought was the body, not sure exactly what's going on I guess I don't entirely understand the rack api. Not sure if MockRequest is the right way to build a request. Not sure if there's a better way (but heck 3-4 lines ain't bad). But it does seem to work.
(There's probably a way to get the 'right' way to work too, with enough work -- there are for instance ways to change or add to the view template lookup paths, to put the original controller's views on the view lookup path, even when you're rendering the template from a new view. But I believe you that it gets painful, and am honestly not sure in this case what the 'right' way to do it is, I think the Rack method seems reasonable)

Render different template based on where request was called from?

I'm calling a controller action to do a search with an AJAX request from 2 different pages and want to render a different rjs file based on which page requested the action. I could just make 2 actions to do this but it doesn't seem very DRY when it's the same code in the action just need different rjs as it's displaying the search results differently in the view.
Using Rails 2.3.4 and Ruby 1.8.7
If I understand your question correctly, three ways come to mind to solve this:
In your action, check the current request's http_referrer and try to figure out what page initiated the request. Depending on how you've got your routing set up, this may or may not work, but it does have the advantage of being pretty simple to do.
Have your AJAX request include an extra GET parameter to identify which page the request is from. Then, have the Rails action test for that parameter, and render RJS accordingly.
Do something clever with Routes and have page A hit the action from one distinct URL, and page B hit the action from another, and include the page identification parameter in the route configuration.
My preference would be for approach #2, as it seems way less likely to break randomly when your routing changes, and #3 strikes me as being overly complicated. There's probably a million other ways to do this, but those are the three that came to mind right off the bat. Hope that helps...
How much code is in the action? You could just factor that out into a common subroutine and call that from each action. It would keep the code simple and easy to understand, without resorting to clever tricks.
I usually do like #2 from Steven's answer, but with a twist. A filter in my ApplicationController attributes a custom mime type corresponding to the extra parameter.
That way, the names of my view files are clearer (i.e.: "show.employees-autocomplete.rjs", "show.quotation-autofill.rjs").

Resources