Use Rails Sprockets directives when rendering js from controller - ruby-on-rails

I want, from a controller, to render a .js.coffee view that includes another js file from the lib/assets/javascripts directory:
#= require doc_ready
Why a view rendered by a controller instead of a static asset?
Because I want to refer to the file through an absolute url, that doesn't changes. Rails 4.0 only compiles assets with a digest like embed-dc589fbef3832d9c38a4fbbc4b021f59.js and I want to use the same url (and possibly expire the cache file based on time), even if I make changes to the script.
Why an absolute url?
Because I want to use the script externally on another website, and the code I give to the webmaster of that site mustn't change.
Why do I want to include another js from the assets?
To keep the code DRY
To require a simple library that simulates the jquery ready event, used to create widgets on the page that included the script.
Can I achieve that by making a controller action that renders a .js.coffee view, which compiles and includes other needed js files from the library, just like sprocket does when compiling assets?

Use redirection like so:
def show
redirect_to view_context.javascript_path('embed.js.coffee')
end
There is a way to render whole js file:
def show
render text: Rails.application.assets.find_asset('embed.js.coffee').body
end

I managed to find a way to do it, by using this answer.
The controller is left untouched:
class Widgets::EmbedJsController < ActionController::Base
def embedded_script
end
end
In the coffeescript view, I have "required" the other file like this:
`<%= raw Rails.application.assets['doc_ready'].body %>`
Seems to work locally, I'll test in production soon.
This can also be refactored by just serving Rails.application.assets['widgets/embed'].body directly from the controller, which should compile coffeescript but have not tested it.

Another approach is to symlink or copy the digest version of the asset to some constant path (and give that to the 3rd party). This has the advantage that the requests shouldn't hit rails at all (since these should be served directly by the web server.
It is relatively simple to automate this - two libraries that I am aware of that do this are
non stupid assets
asset_symlink (I wrote this one)

Related

Using Asset Pipeline in Rails View

I have a Coffeescript view, something like widget.js.coffee which needs to include jQuery, as I can't be sure that jQuery is available. This idea is for other people to use the JS file, e.g.
<script src="http://my.rails.app/40/widget.js"></script>
My app already has jQuery via the asset pipeline, so I want to do the equivalent of the application.coffee manifest, where I can simply say something like:
#= require jquery
So far, it looks like I can output jQuery via so:
<%= Rails.application.assets["jquery"].source %>
but this seems to break the Coffeescript code (looks like there are backticks in the jQuery source).
I'm not sure the best way to proceed. Any thoughts on the best way to do this?
Make a .coffee file in the asset pipeline that includes all the libraries you require. Let's call it "widget.js.coffee" and it lives in assets/javascripts
In your controller, pull the generated source like so:
code = Rails.application.assets['widget'].source
Compress it
#js_libraries = Uglifier.compile code
Use it in your view. If your view is coffeescrtipt, make sure it is enclosed in backticks
<%= raw #js_libraries %>

Rails - Assets loaded on all pages

So I'm kinda lost here. All my CSS/SCSS files are loaded everywhere on my app. But I have two different design (front and back) that I want to separate. How can I achieve that ?
Plus it's kinda useless that all js/css are loaded, even where they are not used. How can I control that ?
What you're wanting to do is control your layouts.
As your question is currently it's too broad for someone to give you a decent specific answer, it's like saying 'tell me about astrophysics, I don't understand how to launch a rocket right now'.
I would suggest to start with the rails guides relating to layouts and then come back with a more specific question once you have a better understanding.
http://guides.rubyonrails.org/layouts_and_rendering.html
There is also a great 11 minute video on RailsCasts which will help you understand and control the assets pipeline: http://railscasts.com/episodes/279-understanding-the-asset-pipeline
Where you are heading is say your app was about managing projects.
Make a copy of the application.css file called say project-manifest.css and use the same structure as that application.css for loading just the stylesheets you want.
Make a copy of views/layouts/application.html.erb to say projects-layout.html.erb
In the new projecs-layout file, update the reference to the css to point to project-manifest.css
Point your controller code to use your new layout
say you have:
# app/controllers/ProjectsController.rb
def show
# code here
# rails does a default render layout: 'application', its overwritten by adding an explict render
render layout: 'project-layout'
end
In your application.js and application.css there is a directive by default: require_tree. It will load all your js and css files to be precompiled later. This is done to make the clients to download the assets packet only once (as it will be cached by the browser) and make the app faster.
If you want to load specific javascript or stylesheet files for each controller, remove the require_tree directive and include them in their respective controller:
<%= javascript_include_tag params[:controller] %> or <%= stylesheet_link_tag params[:controller] %>
Check this out: http://guides.rubyonrails.org/asset_pipeline.html#controller-specific-assets

Why isn't this method getting called from my Rails unobtrusive js template

I recently started using CoffeeScript in my Rails 3.2.14 app. Currently all of our javascript code is jumbled into application.js, which also acts as our manifest file. Our plan was to extract out the stuff into controller specific code so it is easier to maintain in the future. In our application_helper.rb file, we have this helper
def css_tag_id
"#{controller.controller_name}-#{controller.action_name}"
end
We use this for page specific CSS and JavaScript. So my first step was taking code related to our PostsController and putting it in a new file posts.js.coffee. I wrap all the code in posts.js.coffee with a check using the id on the body to ensure the code only runs on views rendered by the PostsController. This all gets compiled into one big application.js file and this is fine with me. This all works perfectly.
However, an AJAX submitted form on one of the pages hits an action in the PostsController which renders select_customer.js.erb. In this template, it calls a method that is now defined in posts.js.coffee, and for some reason no longer works.
Here is a small example of all the files involved:
posts.js.coffee:
jQuery ->
if $('#posts-new').length > 0
keywordsAccordion()
keywordsAccordion = ->
$('.accordion').accordion
'active': 0,
'collapsible': true
select_customer.js.erb
keywordsAccordion();
Is CoffeeScript compiling posts.js.coffee so that it is all namespaced or something? And I need to call methods defined in it differently now from other js templates?
I realize this may be terribly confusing, but will be so grateful is someone can help me out.
Each coffeescript file is namespaced so that its functions are only available within the same file. So keywordsAccordion() is only accessible within the posts.js.coffee file.
You can attach these functions to the window object instead, making them available anywhere:
window.keywordsAccordion = ->
...
I believe you can also use #keywordsAccordion = ->, which is shorthand for this.keywordsAccordion = -> (with this referring to the global scope)

How to register rb as a template handler in rails

Well since I am using a lot of helper methods in my view files and I avoid using html in most of my view files.
Example
myview.html.erb
<%=myhelper #myobject%>
so I end up using,the erb processing tags each time for each file.
<%=%>
I want to register .rb as a template handler or any other extention for that matter.
So my templates look like
myview.html.rb
myhelper #myobject
I am clueless on how to go ahead.
I found it,it seems railscasts already covered that part.
Its show notes,worth checking out.
https://github.com/railscasts/379-template-handlers/blob/master/store-after/config/initializers/ruby_template_handler.rb
Things without ruby are easy to read and render without those erb tags.

Rails asset pipeline - how to include asset not in asset path?

I've converted this multi skinned app of mine to make use of the assets pipeline introduced in Rails 3.1. For the most part it's been surprisingly easy and I'm in love with the preprocessing ability which allows you to use inline Ruby in your CSS/JS files.
I have run into a major problem though, which despite the power of Sprockets seems tricky to solve. My app can be run with any number of skins (or "identities" rather) which is chosen at runtime. This "identity" parameter sets up stuff like cache directory, database connection, view paths - and indeed asset paths. While all "identities" can have their own stylesheet there is also a shared one which is used across all instances. So the assets folder structure looks something like this:
In /app/assets/stylesheets/aplication.css.erb:
<% require_asset("shared.css") %>
<% require_asset("overrides.css") %>
This loads two stylesheets, and crucially it uses the configured asset paths to resolve them (that's why i use require_assets instead of the standard require and include directives, as they don't hit the resolver). It returns the first matches found and allows me to very easily override part or whole of the default styling. So
/app/assets/stylesheets/shared.css
can be overridden by putting a file with the same name in the instance assets folder
/app/assets/[identity]/stylesheets/shared.css
and if no such file exists it silently falls back to the default shared.css.
It all works brilliantly - I use the same technique for JavaScripts, images and fonts and everything gets neatly processed and packaged during precompilation. BUT. There is a type of (sideways) inheritance that I'm unable to achieve; sometimes the skin for an identity is so similar to another one that only a few dozen lines differ (e.g. identical layout but with a different color scheme) and I really want to be able to do something like this:
assets/stylesheets/application.css.erb:
<% require_asset("shared.css") %>
<% require_asset("overrides.css") %>
assets/current_identity/stylesheets/overrides.css:
<% require_asset("../../some_other_identity/stylesheets/overrides.css") %>
/* followed by the dozen or so lines that differ for this skin */
...
This FAILS because in the current context "some_other_identity" is not in the asset paths - Rails does not find the file in dev mode, and of course it's not included during precompilation either. And if I do include it in the assets path it loads the wrong overrides.css (there can be only one). So I've been experimenting with putting something like this at the top of overrides.css:
<%= File.read(Rails.root.join("app/assets/some_other_identity/stylesheets/overrides.css")) %>
/* rest of CSS */
...
And indeed that works just as expected. BUT. Because I'm now using the assets pipeline to serve all assets I can no longer reference images in the CSS with a fixed path - I have to use <%= asset_path("some_image.png") %> so that the path resolver can work its magic. This means my overrides.css is really overrides.css.erb, and of course the ERB preprocessing doesn't happen when you do File.read(). So, I'm stuck! Help! Anyone?
Edit: If I use
<%= ERB.new(File.read(Rails.root.join("app/assets/some_other_identity/stylesheets/overrides.css.erb"))).result %>
it does try to parse the ERB, but I get
undefined method `asset_path' for main:Object
which of course is due to me using asset_path("some_image.png") etc in the file I'm trying to include.
Ok, after hours of searching I came upon the list of available helper methods in Sprockets - it would have saved me a lot of time had this been linked to from the Sprockets man page on GitHub (there is a link, but it points to #FIXME). From the Sprockets API docs:
(Object) evaluate(path, options = {})
Reads path and runs processors on the file.
This allows you to capture the result of an asset and include it directly in another.
<%= evaluate "bar.js" %>
Bingo! I changed my include directive to:
<%= evaluate(Rails.root.join("app/assets/some_other_identity/stylesheets/overrides.css.erb")) %>
and the CSS gets processed and the results inserted, just the way I wanted it to work.

Resources