using Rails 3.1.0.rc4, I'm trying to access a route helper in a javascript file (event.js.erb in this case) and it seems like they are not loaded at that moment. When requesting the merged /assets/application.js file, I get:
throw Error("NameError: undefined local variable or method `events_path' for #<#<Class:0x00000001580010>:0x00000003191510>\n (in /<...>/app/assets/javascripts/event.js.erb)")
Any idea how to access the route helper in there?
UPDATE: Now there is a gem that does this for you: js-routes
The problem is that Sprockets is evaluating the ERB outside of the context of your Rails app, and most of the stuff you're expecting isn't there.
You can add things to your Sprockets context like so:
Rails.application.assets.context_class.class_eval do
include Rails.application.routes.url_helpers
end
That's all well and good, but getting the helpers that require an id to work is a little trickier. I'm going to use a technique called a URI Template:
var event_path = "<%= CGI.unescape event_path('{event_id}') %>";
which returns /events/{event_id} which you could render into a url using an object like { event_id: 1 }. See SugarJS's String#assign method as example implementation of this. You could also roll your own like I did.
You could move the file to views where it has access to the proper context, then pull it down to the client from a JS source tag. Referring to MyRailsApp::Application.routes.url_helpers may not be the best if you are writing an engine.
Related
I have redefined this question from the original a bit to make it more fundamental to the question at hand. The relevant parts of my filesystem are as follows.
env
tutorial
tutorial
templates
view.pt
static
myjava.js
views.py
__init__.py
Right now my view.pt template has
<script type="text/javascript" src="/static/myjava.js"></script>
Then in my __init__.py, I have
config.add_static_view(name='static',path='env/tutorial/tutorial/static')
And finally, the myjava.js file itself is very simple:
document.write("hello from the javascript file")
I am trying to follow this document: http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/assets.html
but right now none of the text is showing up. I feel like the problem lies in the paths i am giving it.
Some ideas I have had: in the config.add_static_view, the name='static' is confusing. I want users to be able to visit the url www.domain.com/firstpage, where firstpage is the result of a template that uses a javascript file resource (a file in the static folder). I am worried that these static assets are only for urls that start with www.domain.com/static/... Is this a valid concern? How can I tell the config.add_static_view function to serve the static resources for any views rendered from the view.pt template?
Edit: here is what worked:
in the template, use src="${request.static_url('tutorial:static/myjava.js')}"
then in the init.py use config.add_static_view(name='static',path='tutorial:static/')
Your javascript link, in the template, should be something like src="${request.static_url('tutorial:static/myjava.js')}"
This allows your application to be more easily relocated.
This also uses the appropriate asset specification, using the name of the package,
"tutorial", a colon, then a path relative to the location of the "tutorial" package, which in your case the package is at env/tutorial/tutorial.
Edited: I forgot about the Configurator object.
Here, you want to use a similar asset specification such as config.add_static_view('static', 'tutorial:static/').
You can make different static views for different directories as well, like: config.add_static_view('images', 'tutorial:images/')
When you do things like this, you can move the root of your application to another location, allowing you to have http://mysite.com/stable/ and http://mysite.com/devel/ having accesses to / be rewritten to /stable/.
The static views can be called from any template with code like ${request.static_url('tutorial:images/icons/favicon.ico')}
Was reading the docs here and it looks like when you call add_static_view it changes the path of the file? To quote the docs:
this means that you wish to serve the files that live in /var/www/static as sub-URLs of the /static URL prefix. Therefore, the file /var/www/static/foo.css will be returned when the user visits your application’s URL /static/foo.css.
In your case, since you're calling env/tutorial/tutorial/static "static", you might want to try src="static/Three.js"> instead
I am not very experienced with rails, but I have used it for several apps before. This app is different because I want to port over a ruby script to a rails app and I am running into several problems, I'm fairly certain I'm going about it the wrong way so wanted to ask S.O. some advice.
The script generates some equations, and I want the controller to just call a helper (or something) to generate the equations list with my script, then pass them on as JSON or something that I can play around with using JS in the view. I tried simply copying the script files over to the helpers folder, but when I try to initialize them via a new route I made in the controller, it gives this error: NameError (uninitialized constant UsersController::EquationSettings). I think it may be expecting my model to include all of these objects.
Code snippet:
The new controller action is in the users_controller (it has its own route now - wanted it to just pass the equation_list and the user to the view where I will handle all the display):
def list_equations
#user = User.find(params[:id])
equation_settings = EquationSettings.new 10
equation_list_generator = EquationList.new equation_settings
#equation_list = equation_list_generator.generate
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user, json: #equation_list }
end
end
Folder structure:
app/controllers/users_controller.rb (the controller noted above)
app/helpers/equation_helpers/(EquationSettings, and EquationList rb files are here)
Once this #equation_list gets passed to the view, I just want to be able to access it like any other JSON hash. Wide open to any suggestions, if it seems I'm going about this completely the wrong way please try to point me in the right direction. Appreciate the time of all the readers,
--Anthony
Guessing from your code snippet, you may need to change your approach. A few suggestions/pointers:
It looks like you're trying to use a helper class, but in Rails, helpers should be modules, not to instantiate but to simply use their methods in the view. For example, in app/helpers/stark_helper.rb you would want to have:
module StarkHelper
def use_honor
# winter is coming
end
end
That would make use_honor available in your view.
Helper methods automatically become available in the view, but not the controller. To use helper methods in a controller (not recommended; doing so would violate the MVC pattern) you need to explicitly include the helper in the controller class, e.g.:
class BaratheonController < ApplicationController
include BaratheonHelper
# stuff
end
When auto-loading project files, such as helpers, Rails expects folder structure and file names to reflect the names of modules and classes defined therein. For example, Rails would not load your file if in app/helpers/lannister.rb, you had defined module LannisterHelper; you would need to change that file name to lannister_helper.rb. (lannister_helper.rb -> LannisterHelper)
Another important facet of the auto-loading I mentioned is that sub-folders are expected to reflect sub-classes and sub-modules. So if you had app/helpers/greyjoy_helper/greyjoy_boat_names.rb, that file would need to have this definition:
module GreyjoyHelper
module GreyjoyBoatNames
# stuff
end
end
In the code we use something like this:
$('#wrapper').html('//app/views/content.ejs', {foo:"bar"});
And when we build the app, this still stays the same, although the content.ejs file is built into production.js.
So my question is, what should we do so that when we build the app, these references point to ejs files inside of production.js?
We are using JMVC 3.2.2
We've also tried using this way:
$('#wrapper').html( $.View('//app/views/content.ejs', {foo:"bar"}) );
Your views are not getting added to production.js; you need to steal each one of them:
steal('//app/views/content.ejs');
JMVC 3.1:
steal.views('//app/views/content.ejs');
Got the answer in JMVC forum: https://forum.javascriptmvc.com/topic/#Topic/32525000000958049
Credit to: Curtis Cummings
Answer:
The paths to the views do not need to change.
When the production.js file is created, your views are included and
get preloaded when the script runs. When you reference:
'//app/views/content.ejs', view first checks if the view file you are
requesting has been preloaded and if it has, will use that instead of
making a request for the .ejs file.
I was under the impression that you could put javascript in a view template in Rails 3. For example, if I had this html in views/public/home.html.erb
<div id="block">click</div>
then in views/public/home.js.erb, I thought I could put the following javascript, and then click on html to trigger the javascript. However, when I tested it, I got no results. But if I put the javascript in assets/javascript/application.js, then everything worked fine...Shouldn't it also work if it was in a js template with the same name as the html view?
$(document).ready(function(){
test();
});
function test() {
$("#block").click(function() {
$('#block').hide();
});
}
Ummm. no. It just doesn't work that way. Only one of views/public/home.* will be rendered, depending on the responds type.
Javascript shouldn't be added as a view file (bla.js.erb). They must be put in assets/javascripts or at least in lib or vendor directory.
You must also require them in your application.js file, if you already don't use require_tree.
In this way you won't need to reference the javascript in any way in your view (the application.js will include it for you). Otherwise, you need to specify a layout to insert javascript files in block, because views are rendered after tag.
There are a lot of reason not to put javascript directly in html (except for tests obviusly), read the rails asset pipeline for more information.
When you create a view with a different extension from html.erb that will be used only if your url specify a format with that extension, for example mywebsite/users.json will return eventually a index.json.erb.
For AJAX you would like to return a JSON object, not javascript which is definitely not a correct approach. Remember that you are using a framework and you should follow it's guidelines. If you want to live it's rails, it will be hard to work with it.
You can use javascript_include_tag
If you have the js files source1.js, source2.js in public/javascript then you can include them using
javascript_include_tag 'source1', 'source2'
For a Rails 3.1 app, some of my site wide JavaScript is only included when certain real time, instance specific conditions are met. This means I can't put it in the new asset pipeline's application.js because that isn't parsed by erb for embedded Ruby within the current context. Basically, I'm including keyboard shortcuts, based on the current_user that is logged in.
My question: where should this JavaScript with embedded code go, so that the embedded Ruby is still parsed for each page access with the proper context (i.e. current, logged in user)?
The answer seems to just be to put it in the application.html.erb layout view at the bottom, but this seams like I'm hiding away javascript code in a non intuitive location.
I've tried creating an application2.js.erb file, but then I got errors about undefined variables, which I think might be because the asset engine only parses this file once before the output is cached and the scope isn't correct yet for things like current_user.
So, using application.html.erb works just fine here, and this isn't so much a question of how to get it to work functionally. Instead, I'm wondering if there's a more elegant way to incorporate the asset pipeline model here with my requirements and still keep most of my JavaScript in the assets/javascripts directory.
You should try to create app/assets/javascripts/application2.js.erb (or whatever better name you come up with)
And then put something like this in your app/assets/javascripts/application.js:
//= require application2
And then you can have
<%= javascript_include_tag 'application2' %>
wherever you want - for example in your application.html.erb.
Btw, if you want to customize what's included on a per-view basis you might find content_for useful. Check out this screencast
Ok, about unobtrusive js. It will be just a cocept (HAML):
In your view somewhere
# hotkeys are "Ctrl+C", "Ctrl+A"
-current_user.hotkeys.each do |hotkey|
%hotkey{ "data-key" => hotkey.key, "data-behavior" => hotkey.fn }
Then in your application.js
$(document).ready(function(){
if($("hotkey").length > 0){
$("hotkey").each{function(this){
key = $(this).data("key");
fn = $(this).data("behavior");
$(document).bind('keydown', key, fn);
}}
}
})
So just the same JS will extract from HTML hotkeys data and then bind it.
As some people have pointed out, the two options are:
Put your javascript inside the view (and as you say, this doesn't feel quite right).
Put it in a javascript file. Make a conditional inside your view that includes this javascript file if certain conditions are met.
If you need to pass more instance variables from the controller to your javascript, this gem called gon can make your life easier.
This allows you to use the default asset pipeline using the following javascript:
if(gon.conditional){
//your embedded js code here
}
If you want to know more about this gem, checkout this railcast where everything gets explained.