Rails angular coffeescript error - ruby-on-rails

I'm using angularjs-rails gem.
I've created angular_app folder in assets, and have angular_app/controllers/phoneListController.js.coffee, and angular_app/modules/phoneCatApp.js.coffee (*yeah you're right I'm doing angular's phone tutorial ). So angular_app/controllers/phoneListController.js.coffee has:
phonecatApp.controller 'PhoneListController', ($scope) ->
$scope.phones = [
{ 'name': 'Nexus S'
'snippet': 'Fast just got faster with Nexus S.'
}
{
'name': 'Motorola XOOMâ„¢ with Wi-Fi'
'snippet': 'The Next, Next Generation tablet.'
}
{
'name': 'MOTOROLA XOOMâ„¢'
'snippet': 'The Next, Next Generation tablet.'
}
]
return
angular_app/modules/phoneCatApp.js.coffee has:
phonecatApp = angular.module('phonecatApp', [])
Every thing works fine if I use vanila js in angular_app/modules/phoneCatApp.js.coffee using `phonecatApp = angular.module('phonecatApp', [])`` (with backsticks).
So problem is that coffee covers all in anonymous function with ().call.this. What should I do to make it work in coffee?

The problem, as you've stated, is the
(function() { ... }).call(this)
that the coffeescript compiler generates. To make phonecatApp a global simply do
this.phonecatApp = phonecatApp
in your angular_app/modules/phoneCatApp.js.coffee file after the angular.module() call.
However, MUCH better is to use:
angular.module('phonecatApp').controller( ... )
to define your controllers. This version gets angular to provide the module singleton on which to define the controller.

Related

Konacha, Mocha, BackboneJS and Ruby on Rails configuration

I am using Konacha to test a BackboneJS application in my Ruby on Rails application. I have read about every tutorial on the web and it shows how easy it is to set up and get working. Unfortunately, I am not having this level of success. Here is what I have:
app/assets/javascripts/app_specific/itapp/models/post.js
Itapp.Models.Post = Backbone.Model.extend({
isFirstPost: function() {
return (this.get('id') === Itapp.bootstrap.posts[0].get('id'));
},
});
spec/javascripts/app_specific/itapp/models/post_spec.js
//= require spec_helper
var expect = chai.expect;
describe("Posts", function() {
it("should have a first post", function() {
//just trying anything to get some kind of valid error/issue
//I could put anything here and it wouldn't matter, just FYI
expect(typeof this.isFirstPost).to.equal('function');
});
});
spec/javascripts/spec_helper.js file:
// Require the appropriate asset-pipeline files:
//= require application
//Any other testing specific code here...
//Custom matchers, etc....
Konacha.mochaOptions.ignoreLeaks = true
beforeEach(function() {
return window.page = $("#konacha");
});
// set the Mocha test interface
// see http://mochajs.org/#interfaces
mocha.ui('bdd');
//
// ignore the following globals during leak detection
mocha.globals(['YUI']);
//
// or, ignore all leaks
mocha.ignoreLeaks();
//
// set slow test timeout in ms
mocha.timeout(5);
//
// Show stack trace on failing assertion.
chai.config.includeStack = true;
I do have a config/initializers/konacha.rb file:
Konacha.configure do |config|
config.spec_dir = "spec/javascripts"
config.spec_matcher = /_spec\.|_test\./
config.stylesheets = %w(application)
config.driver = :selenium
end if defined?(Konacha)
The error I am getting:
Error: Failed to load app_specific/itapp/collections/posts_spec.js.
Perhaps it failed to compile? Check the rake output for errors.
Checking the rake output:
ActionView::Template::Error: couldn't find file 'application' which I am requiring in my spec_helper.js
So for some reason even though in my spec_helper I am trying to load the BackboneJS application for the testing environment it is not able to find it.
Any thoughts/ideas that I should try to get this communicating/working?
--Mike Riley
I figured this out. The problem was that it was not finding the file under app/assets/javascripts/ correctly. I needed to do this at the top of the spec/javascripts/app_specific/itapp/models/post_spec.js file:
//= require app_specific/itapp/models/post
Once I did that it was able to find the associated code that I am testing against. I will need to work a bit more to clean up the path in a spec_helper.js file, but I am no longer blocked on this.

Can't get Dart asset_pack package example code to run correctly

So I am trying to make a game in Dart and I decided to check out this asset_pack package. I tried to test the example code (below) but asset.imported prints null..why is this?
I did create /web/test/foo.txt in my project folder and put a little bit of text in it but still I get null printed.
main() {
// Construct a new AssetManager.
AssetManager assets = new AssetManager();
// Register the 'test' pack. No url is needed so the empty string suffices.
AssetPack testPack = assets.registerPack('test', '');
// Register asset 'foo' and load it's contents from 'foo.txt'.
// The asset type is 'text' and there are no arguments for the loader
// or importer.
Future<Asset> futureAsset = testPack.loadAndRegisterAsset('foo', 'foo.txt',
'text', {}, {})
futureAsset.then((asset) {
// Print the contents of foo.txt.
print(asset.imported);
});
}
I think your file has to be in the web/ folder or you have to load test/foo.txt. If you run the example you should see an error that the file was not found in the developer console. This code seems to work:
Future<Asset> futureAsset = testPack.loadAndRegisterAsset('foo', 'text',
'test/foo.txt', {}, {})

What's the recommended way to copy multiple dotfiles with yeoman?

I am building a yeoman generator for a fairly typical node app:
/
|--package.json
|--.gitignore
|--.travis.yml
|--README.md
|--app/
|--index.js
|--models
|--views
|--controllers
In the templates folder of my yeoman generator, I have to rename the dotfiles (and the package.json) to prevent them from being processed as part of the generator:
templates/
|--_package.json
|--_gitignore
|--_travis.yml
|--README.md
|--app/
|--index.js
|--models
|--views
|--controllers
I see a lot of generators that copy dotfiles individually manually:
this.copy('_package.json', 'package.json')
this.copy('_gitignore', '.gitignore')
this.copy('_gitattributes', '.gitattributes')
I think it's a pain to manually change my generator code when I add new template files. I would like to automatically copy all files in the /templates folder, and rename the ones that are prefixed with _.
What's the best way to do this?
If I were to describe my intention in imaginary regex, this is what it would look like:
this.copy(/^_(.*)/, '.$1')
ths.copy(/^[^_]/)
EDIT
This is the best I can manage:
this.expandFiles('**', { cwd: this.sourceRoot() }).map(function() {
this.copy file, file.replace(/^_/, '.')
}, this);
I found this question through Google as I was looking for the solution, and then I figured it out myself.
Using the new fs API, you can use globs!
// Copy all non-dotfiles
this.fs.copy(
this.templatePath('static/**/*'),
this.destinationRoot()
);
// Copy all dotfiles
this.fs.copy(
this.templatePath('static/.*'),
this.destinationRoot()
);
Adding to #callumacrae 's answer: you can also define dot: true in the globOptions of copy(). That way a /** glob will include dotfiles. Example:
this.fs.copy(
this.templatePath('files/**'),
this.destinationPath('client'),
{ globOptions: { dot: true } }
);
A list of available Glob options can be found in the README of node-glob.
Just got this working for me: the globOptions needs to be in the fifth argument:
this.fs.copyTpl(
this.templatePath('sometemplate/**/*'),
this.destinationPath(this.destinationRoot()),
null,
null,
{ globOptions: { dot: true } }
);
If you don't want to use templates that starts with a dot, you can use the dive module to achieve something identical:
var templatePath = this.templatePath('static-dotfiles');
var destinationRoot = this.destinationRoot();
dive(templatePath, {all: true}, function (err, file, stat) {
if (err) throw err;
this.fs.copy(
file,
(destinationRoot + path.sep + path.relative(templatePath, file))
.replace(path.sep + '_', path.sep + '.')
);
}.bind(this));
where static-dotfiles is the name of your template folder for dotfiles where _ replaces . in filenames (ex: _gitignore).
Don't forget to add a requirement to dive at the top of your generator with
var dive = require('dive');
Of course, this also works for copyTpl.
Note that all subparts of paths that starts with a _ will be replaced by a . (ex: static-dotfiles/_config/_gitignore will be generated as .config/.gitignore)

AngularJS e2e testing in Rails stack: [object Object] has no method 'resumeBootstrap'

It seems quite hard to get the AngularJS/Karma end-to-end testing environment working with a Rails backend.
I have Karma installed and running with the following config file:
basePath = '../';
files = [
ANGULAR_SCENARIO,
ANGULAR_SCENARIO_ADAPTER,
'test/e2e/**/*.js'
];
autoWatch = false;
browsers = ['Chrome'];
// singleRun = true;
proxies = {
'/': 'http://local.mywebsite.com:3000/'
};
urlRoot = 'e2e';
junitReporter = {
outputFile: 'test_out/e2e.xml',
suite: 'e2e'
};
Where http://local.mywebsite.com:3000/ is my rails application.
I try a very simple test:
it('should redirect to /', function() {
expect(browser().location().url()).toBe("/");
});
But I am stuck with the following error:
http://localhost:9876/base/test/e2e/scenarios.js:8:5
TypeError: Object [object Object] has no method 'resumeBootstrap'
at HTMLIFrameElement.<anonymous> (http://localhost:9876/adapter/lib/angular-scenario.js:26285:27)
at HTMLIFrameElement.jQuery.event.dispatch (http://localhost:9876/adapter/lib/angular-scenario.js:3064:9)
at HTMLIFrameElement.elemData.handle.eventHandle (http://localhost:9876/adapter/lib/angular-scenario.js:2682:28)
at k (http://connect.facebook.net/en_US/all.js:48:745)
Where scenarios.js comes from https://github.com/angular/angular-seed.
Any idea how to make it work?
Try
it('should redirect to /', function() {
browser().navigateTo("/");
expect(browser().window().path()).toBe("/");
});
I would get the most up-to-date version of angular scenario runner. You can find the unstable version at
http://code.angularjs.org/1.1.4/angular-scenario.js
and the stable version at
http://code.angularjs.org/1.0.6/angular-scenario.js
Then, be sure to update your karma configuration to use the newest scenario runner
files = [
'path/to/angular-scenario.js',
ANGULAR_SCENARIO_ADAPTER,
'test/e2e/**/*.js'
];
Hopefully that helps.
The solution above is correct. This link covers the problem: https://github.com/karma-runner/karma/issues/470
I hit the same issue while running the excellent examples on: http://www.yearofmoo.com/2013/01/full-spectrum-testing-with-angularjs-and-testacular.html
That's a great resource if you're getting started, but you will need to update from testacular to karma and update angularjs to 1.0.6

Server-side internationalization for Backbone and Handlebars

I'm working on a Grails / Backbone / Handlebars application that's a front end to a much larger legacy Java system in which (for historical & customizability reasons) internationalization messages are deep in a database hidden behind a couple of SOAP services which are in turn hidden behind various internal Java libraries. Getting at these messages from the Grails layer is easy and works fine.
What I'm wondering, though, is how to get (for instance) internationalized labels into my Handlebars templates.
Right now, I'm using GSP fragments to generate the templates, including a custom tag that gets the message I'm interested in, something like:
<li><myTags:message msgKey="title"/> {{title}}</li>
However, for performance and code layout reasons I want to get away from GSP templates and get them into straight HTML. I've looked a little into client-side internationalization options such as i18n.js, but they seem to depend on the existence of a messages file I haven't got. (I could generate it, possibly, but it would be ginormous and expensive.)
So far the best thing I can think of is to wedge the labels into the Backbone model as well, so I'd end up with something like
<li>{{titleLabel}} {{title}}</li>
However, this really gets away from the ideal of building the Backbone models on top of a nice clean RESTful JSON API -- either the JSON returned by the RESTful service is cluttered up with presentation data (i.e., localized labels), or I have to do additional work to inject the labels into the Backbone model -- and cluttering up the Backbone model with presentation data seems wrong as well.
I think what I'd like to do, in terms of clean data and clean APIs, is write another RESTful service that takes a list of message keys and similar, and returns a JSON data structure containing all the localized messages. However, questions remain:
What's the best way to indicate (probably in the template) what message keys are needed for a given view?
What's the right format for the data?
How do I get the localized messages into the Backbone views?
Are there any existing Javascript libraries that will help, or should I just start making stuff up?
Is there a better / more standard alternative approach?
I think you could create quite an elegant solution by combining Handelbars helpers and some regular expressions.
Here's what I would propose:
Create a service which takes in a JSON array of message keys and returns an JSON object, where keys are the message keys and values are the localized texts.
Define a Handlebars helper which takes in a message key (which matches the message keys on the server) and outputs an translated text. Something like {{localize "messageKey"}}. Use this helper for all template localization.
Write a template preprocessor which greps the message keys from a template and makes a request for your service. The preprocessor caches all message keys it gets, and only requests the ones it doesn't already have.
You can either call this preprocessor on-demand when you need to render your templates, or call it up-front and cache the message keys, so they're ready when you need them.
To optimize further, you can persist the cache to browser local storage.
Here's a little proof of concept. It doesn't yet have local storage persistence or support for fetching the texts of multiple templates at once for caching purposes, but it was easy enough to hack together that I think with some further work it could work nicely.
The client API could look something like this:
var localizer = new HandlebarsLocalizer();
//compile a template
var html = $("#tmpl").html();
localizer.compile(html).done(function(template) {
//..template is now localized and ready to use
});
Here's the source for the lazy reader:
var HandlebarsLocalizer = function() {
var _templateCache = {};
var _localizationCache = {};
//fetches texts, adds them to cache, resolves deferred with template
var _fetch = function(keys, template, deferred) {
$.ajax({
type:'POST',
dataType:'json',
url: '/echo/json',
data: JSON.stringify({
keys: keys
}),
success: function(response) {
//handle response here, this is just dummy
_.each(keys, function(key) { _localizationCache[key] = "(" + key + ") localized by server"; });
console.log(_localizationCache);
deferred.resolve(template);
},
error: function() {
deferred.reject();
}
});
};
//precompiles html into a Handlebars template function and fetches all required
//localization keys. Returns a promise of template.
this.compile = function(html) {
var cacheObject = _templateCache[html],
deferred = new $.Deferred();
//cached -> return
if(cacheObject && cacheObject.ready) {
deferred.resolve(cacheObject.template);
return deferred.promise();
}
//grep all localization keys from template
var regex = /{{\s*?localize\s*['"](.*)['"]\s*?}}/g, required = [], match;
while((match = regex.exec(html))) {
var key = match[1];
//if we don't have this key yet, we need to fetch it
if(!_localizationCache[key]) {
required.push(key);
}
}
//not cached -> create
if(!cacheObject) {
cacheObject = {
template:Handlebars.compile(html),
ready: (required.length === 0)
};
_templateCache[html] = cacheObject;
}
//we have all the localization texts ->
if(cacheObject.ready) {
deferred.resolve(cacheObject.template);
}
//we need some more texts ->
else {
deferred.done(function() { cacheObject.ready = true; });
_fetch(required, cacheObject.template, deferred);
}
return deferred.promise();
};
//translates given key
this.localize = function(key) {
return _localizationCache[key] || "TRANSLATION MISSING:"+key;
};
//make localize function available to templates
Handlebars.registerHelper('localize', this.localize);
}
We use http://i18next.com for internationalization in a Backbone/Handlebars app. (And Require.js which also loads and compiles the templates via plugin.)
i18next can be configured to load resources dynamically. It supports JSON in a gettext format (supporting plural and context variants).
Example from their page on how to load remote resources:
var option = {
resGetPath: 'resources.json?lng=__lng__&ns=__ns__',
dynamicLoad: true
};
i18n.init(option);
(You will of course need more configuration like setting the language, the fallback language etc.)
You can then configure a Handlebars helper that calls i18next on the provided variable (simplest version, no plural, no context):
// namespace: "translation" (default)
Handlebars.registerHelper('_', function (i18n_key) {
i18n_key = Handlebars.compile(i18n_key)(this);
var result = i18n.t(i18n_key);
if (!result) {
console.log("ERROR : Handlebars-Helpers : no translation result for " + i18n_key);
}
return new Handlebars.SafeString(result);
});
And in your template you can either provide a dynamic variable that expands to the key:
<li>{{_ titleLabeli18nKey}} {{title}}</li>
or specify the key directly:
<li>{{_ "page.fancy.title"}} {{title}}</li>
For localization of datetime we use http://momentjs.com (conversion to local time, formatting, translation etc.).

Resources