I coded small web app that runs ant (batch file). The processing time of the batch file could take up to minutes.
index.haml list all available ant files and run.haml runs them. The flow how it works now is that when I click a link from index.haml the ant script is run and after it finishes the whole run.haml page is sent to the browser. So after clicking link from index.haml I still can see index.haml and nothing from run.haml
After I click a link from index.haml I want to
show what script is going to be run and then
run the ant script and then
display the results of it.
I was recommended in my other question to use
an Ajax call or
a separate worker thread. Like BackgrounDRb, run_later or delayed_job
I didn't understand how a separate worker thread could help me. Would the delayed job's results that are captured by ruby's call be sent to the browser once the job finishes?
I also didn't get how I can use Ajax in sinatra.
Could somebody point me out what a solution for that could be like? Please note that I know bit of ruby, learned bit of sinatra and haml yesterday. No nothing about Ajax :-) I learn by examples ... and happy to learn anything.
index.haml gives me html like
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Available test suits</title>
</head>
<body>
<h1>Available test suits</h1>
<br/><br/>
<a href='run?run=login_build'>login_build</a>
<br />
<a href='run?run=login_cycle_build'>login_cycle_build</a>
<br />
<a href='run?run=login_cycle_academicyear_build'>login_cycle_academicyear_build</a>
</body>
</html>
run.haml looks like
!!!
%html
%head
%title Running
%body
= "Starting test suite: #{params['run']}"
- output = %x[cd C:\\Program Files\\TestPro\\TestPro Automation Framework410 && ant -lib lib -f "C:\\Program Files\\TestPro\\TestPro Automation Framework410\\Output Files\\builds\\#{params['run']}.xml"]
-#The result is
%br
= output.split("\n")[-2,2].join("<BR>")
= "<br/>"*2
%a(href="/")back to suits list
If you want to use Ajax must, choose a javascript library Dojo, Prototype, Mootools, jQuery... Each of them has specific tools for handling ajax requests.
In javascript you make an xhr(Ajax request), which may be asynchronous and still Sinatra serves you the wanted content, you can display in webpage what you want.
var xhrLoadClientMenu = function(param){
var result_node = dojo.byId('div_menu')
var xhrArgs = {
// the Sinatra get "/run" ... end handler
url: '/run',
load: function(data){
// When response is rendered come's here
result_node.innerHTML = data;
},
error: function(error){
msg = "<p>Ooops some error ...<br><br>" + error + "</p>";
}
}
// Here you can put what you want to display durring loading
result_node.innerHtml = "Loading..."
var defered = dojo.xhrGet(xhrArgs)
}
a xhrGet example using dojo. See inline comments.
xhrLoadClientMenu(param) is triggered by an event;
a HTML element with id #div_menu from page is stored in result_node object;
xhrArgs object sets the Ajax request properties like:
url: the Sinatra handler which renders the content;
load: function gets the response from sinatra and replaces result_node content when load is completed
error if something goes wrong this message will be shown in result_node;
meanwhile you may set the result_node content to anything while the real content is loading;
last line executes the xhr request.
All of this is happening without a page reload.
Delayed_job results are indeed sent "back to the browser." Please take another look at this live example of using Sinatra and DJ to process a task asynchronously and push the result to the browser once the task is complete.
The call runs asynchronously, and the rest of the page loads, and when the calls are complete the data is posted back to the page. Now, since it's on the cloud, it may seem VERY delayed; you'll want to download the source and run it on your own machine to see what kind of speed DJ offers on a local network. The results are indeed processed and sent back; scroll down the page and you'll see the results of earlier processing -- you can even enter your own text block to add to the processing queue.
So perhaps you could utilize delayed_job to create a queue for asynchronous processing of your test suites -- 'out of sync' from the rest of your page, but also with the option of passing data back 'incrementally' as well as once the task completes.
Now, Ajax offers another way to provide this same functionality. I absolutely and unequivocally recommend getting one of the Javascript frameworks and learning how to do this sort of asynchronous processing that way as well. However, based on your earlier question, I would still suggest working through the source code of the example above using Sinatra and delayed_job, as it's already extremely close to what you are trying to do, not to mention that it keeps you entirely in Ruby.
Related
I am having issues with translating client-side text in CKAN. My site is in french, so here is the problem:
- if I click on the green button "S'abonner" from the left, it should change the button to a red one and show the text "Se désabonner", but instead, it shows me "Unfollow", like in the photos:
And if I click it again it shows me "Follow" (instead of "S'abonner"). So this is a problem on the client side. Normally, when you generate text on the server side you write in the templates _('msgid present in pofile'), but, on the client side, it uses the result from the AJAX call to "/api/i18n/{language}".
I did some digging and it seems that the client-side translation uses the result from the AJAX call to "/api/i18n/fr", but all I get is a json object:
{
"": {
"domain": "ckan",
"lang": "fr",
"plural-forms": "nplurals=2; plural=(n > 1)"
}
}
But, if I look at the ckan demo website, (http://demo.ckan.org/api/i18n/fr), I get a long JSON file, that contains all the translations.
I managed to discover from where this Ajax call is done, apparently it comes from : "src/ckan/ckan/public/base/javascript/client.js", at the function:
getLocaleData: function (locale, success, error) {
var url = this.url('/api/i18n/' + (locale || ''));
return jQuery.getJSON(url).then(success, error);
}
If I replace
var url = this.url('/api/i18n/' + (locale || ''));
with
var url = this.url('http://demo.ckan.org/api/i18n/fr')
the problem is solved, because I get the translations in the json object.
My question is how I can get the right data to be generated, is there a parameter to put in production.ini? Unfortunately, the documentation in CKAN is really poor so I have no leads on this. Does anybody have a clue?
Thanks!
I suspect that you might be running an unreleased branch of CKAN (eg master), so the Javascript translations have not been built.
This is done with the following command:
paster front-end-build -c config_file.ini
Just run this command and the translations should come up fine.
On the main repository this is only done just before releases to avoid complicating the git history.
Check for instance this site running the latest master branch, but on which the front end is built every night:
http://master.ckan.org/fr/dataset/world-countries
I'm using Capybara to fill in a form and download the results.
It's a bit slow when filling in the form, and I want to check if JavaScript is the culprit.
How do I turn off JavaScript?
The Ruby code was something similar to, but not the same as, the following (the following won't reproduce the error message, but it is somewhat slow).
require "capybara"
url = "http://www.hiv.lanl.gov/content/sequence/HIGHLIGHT/highlighter.html"
fasta_text = [">seq1", "gattaca" * 1000, ">seq2", "aattaca" * 1000].join("\n")
session = Capybara::Session.new(:selenium)
# Code similar to this was run several times
session.visit(url)
session.fill_in('sample', :with => fasta_text)
session.click_on('Submit')
And the error I was getting (with my real code, but not the code I have above) was
Warning: Unresponsive script
A script on this page may be busy, or it may have stopped responding.
You can stop the script now, open the script in the debugger, or let
the script continue.
Script: chrome://browser/content/tabbrowser.xml:2884
I wasn't running Capybara as part of a test or as part of a spec.
To confirm that the code I wrote currently has JavaScript enabled (which is something I want to disable), doing
url = "http://www.isjavascriptenabled.com"
session = Capybara::Session.new(:selenium)
session.visit(url)
indicates that JavaScript is enabled.
Capybara only uses JavaScript if you've specified a javascript_browser:
Capybara.javascript_driver = :poltergeist
And if you've specified js: true as metadata in your spec:
context "this is a test", js: true do
Check for both of those things. If they're not there and the test is not running in a browser or using Poltergeist, then it's probably not using JavaScript.
For my single page application, the page is delivered by node-express-ejs on the server side to Maironette-Backbone based client on the browser. Unfortunately, both EJS and Marionette use the syntax "<%= var %>" for plugging in computed values. My page has some values that need to be plugged in by the server and some that will be computed by the client. So I need something like the following snippet to work.
<html> .....
Server Computed = $ <%=serverComputed%>
Client Computed = $ <%=clientComputed%>
....</html>
And my routes.js on the server would have something like,
res.render('test', { serverComputed: '5000' });
But, EJS throws
ReferenceError: .../test.ejs:17
.....
clientComputed is not defined
The following does not help either
res.render('test', { serverComputed: '5000', clientComputed: '<%= clientComputed %>' });
since it as expected, ends up as
$lt:%= clientComputed %>
Is there a way (a market, a processor directive etc) to tell EJS processor to not process a part of the file?
Is there any other way to work around this conflict?
Thanks,
Hemant
Found the solution.
EJS does allow defining your own tags by setting them in the options. So far as I can tell, this is not documented. I only found it by going through the source code. Anyway, the following option setting would avoid conflict with Marionette open-close tags.
On server side, routes.js can specify options like this.
res.render('test', { open: '{{', close: '}}', serverComputed: '5000' });
In the ejs file, now you can have different markups for server-computed and client-computed variables, like so.
Server Computed = $ {{=serverComputed}}
Client Computed = $ <%=clientComputed%>
EJS processor will not process the "<%=clientComputed%>" part.
Hemant
My code:
breeze.config.initializeAdapterInstance("ajax", "angular", true);
...
var ajaxAdapter = breeze.config.getAdapterInstance('ajax');
ajaxAdapter.defaultSettings = {
method: 'POST',
data: {
CompanyName: 'Hilo Hattie',
ContactName: 'Donald',
City: 'Duck',
Country: 'USA',
Phone: '808-234-5678'
}
};
in line 14813 of breeze.debug.js:
ngConfig = core.extend(compositeConfig, ngConfig);
compositeConfig.method has a value of 'POST' until it is overwritten, because ngConfig.method has a value of 'GET'.
I imagine this question is relevant for any ajax setting, but of course I'm mainly struggling with how to post with the angular ajax adapter, so maybe there's an entirely better way to do that? this approach feels dirty anyway, but breeze.ajaxPost.js only works with the jQuery ajax adapter, right?
6 Oct 2014 update
We recently had reason to revisit ajaxpost and have updated both the code and the documentation.
The original recommendation works. We're merely clarifying and updating the happy path.
A few points:
The ajaxpost plug-in works for both jQuery and Angular ajax adapters.
Those adapters long ago became aware of adapter.defaultSettings.headers and have blended them into your Breeze ajaxpost http calls (take heed, PW Kad).
You must call breeze.ajaxPost() explicitly after replacing the default ajax adapter as you do when you use the 'breeze.angular' service.
You can wrap a particular ajax adapter explicitly: breeze.ajaxPost(myAjaxAdapter); We just don't do that ourselves because, in our experience, it is sufficient to omit the params (breeze.ajaxPost()) and let the method find-and-wrap whatever is the active ajax adapter of the moment.
The documentation explains the Angular use case which I repeat here to spare you some time:
// app module definition
var app = angular.module('app', ['breeze.angular']); // add other dependencies
// this data service abstraction definition function injects the 'breeze.angular' service
// which configures breeze for angular use including choosing $http as the ajax adapter
app.factory('datacontext', ['breeze', function (breeze) { // probably inject other stuff too
breeze.ajaxPost(); // wraps the now-current $http adapter
//... your service logic
}]);
Original reply
AHA! Thanks for the plunker (and the clever use of the Todo-Angular sample's reset method!).
In brief, the actual problem is that breeze.ajaxpost.js extends the jQuery ajax adapter, not the angular ajax adapter. There is a timing problem.
The root cause is that you can't tell breeze.ajaxpost.js which adapter to wrap. It always wraps the default adapter at the time that it runs. As you've got your script loading now, the jQuery adapter is the current one when breeze.ajaxpost.js runs.
The workaround is to set the default adapter to the angular adapter before breeze.ajaxpost.js runs.
One way to do that is to load the scripts as follows ... adding an inline script to set the default ajax adapter to the "angular" version.
<script src="breeze.debug.js"></script>
<script>
<!-- Establish that we will use the angular ajax adapter BEFORE breeze.ajaxpost.js wraps it! -->
breeze.config.initializeAdapterInstance("ajax", "angular", true);
</script>
<script src="breeze.ajaxpost.js"></script>
Clearly this is a hack. We'll look into how we can change breeze.ajaxpost.js so you can wrap any ajax adapter at the time of your convenience.
Thanks for finding this issue, and thanks Ward for bringing it to my attention. I've updated the breeze.ajaxpost.js code to use .data as you described, and added a function you can call after adapter initialization. So now you can do:
var ajaxAdapter = breeze.config.initializeAdapterInstance("ajax", "angular");
ajaxAdapter.setHttp($http);
breeze.ajaxpost.configAjaxAdapter(ajaxAdapter); // now we can use POST
So, it's slightly less hacky.
This answer is applicable to the following plugins and version:
Breeze.Angular v.0.8.7
Breeze.AjaxPost v.1.0.6
I just saw a new plugin called Breeze.Angular.js that can work in conjunction with Breeze.AjaxPost.js
You have to modify the Breeze.AjaxPost.js in order for it to work. Breeze.Angular.js initializes your adapter when your page loads, and Breeze.AjaxPost.js will take in the adapter initialized from Breeze.Angular.js
You can learn more about the Breeze.Angular server here: breeze-angular-service
You can learn more about Breeze.Ajaxpost here: breeze-ajaxpost
Now to set this up wasn't exactly apparent, because I took both files exactly as they were in git.
1.) Reference the files in this order:
<script src="Scripts/q.min.js"></script>
<script src="Scripts/breeze.angular.js"></script>
<script src="Scripts/breeze.ajaxpost.js"></script>
2.) Go into your breeze.ajaxpost.js
Remove this line (or comment this line):
breeze.ajaxpost(); // run it immediately on whatever is the current ajax adapter
The reason why is because, this will cause your ajaxpost method to run before your breeze.angular service. When you go into the ajaxpost method, the ajaxAdapter parameter that is supposed to be passed in will be null. Removing this line appends the functionality for later in your code, so you can call it from the breeze.angular service.
3.) In breeze.angular.js go to your useNgHttp method. It should look like this:
// configure breeze to use Angular's $http ajax adapter
var ajaxAdapter = breeze.config.initializeAdapterInstance("ajax", "angular");
ajaxAdapter.setHttp($http);
breeze.ajaxpost(ajaxAdapter); // now we can use POST
When you run your program, both plugins should be able to set up the environment for you without having to include it in every javascript that makes your calls to the webapi.
Thanks guys for the plugins.
I'm trying to use iChemLabs cloud services from a html5 web worker. Normally the cloudservices requires jQuery but I can't import that into a web worker so I'm using Pollen instead with a ChemDoodle Web Components library with which I have stripped out the document-related things.
jQuery.Hive.Pollen provides a nice ajax function very similar to jQuery, but I can't seem to get it to work at all. I know this problem will be tricky to solve considering that Access-control-headers need to be set to allow any of you to actually find the solution. However, I'm a beginning javascript programmer and I was wondering if my two weeks of frustration is actually a small difference. I am trying to invoke the following function:
var cloudmolecule;
ChemDoodle.iChemLabs.readSMILES('N1(C)C(=O)N(C)C(C(=C1N1)N(C=1)C)=O', function(mol){
cloudmolecule = mol;
});
Here is a link to the library code I am using, see the 'q.ajax' call and substitute jQuery = q for p = q (p is for pollen) in that block of code.
Right now I'm just trying to get the ajax call to work in an ordinary block of javascript with the plan to migrate to a web worker later.
If anybody could point out the problem to me I would be extremely grateful.
solved! turns out iChemLabs rejects these two extra headers that pollen creates:
_xhr.setRequestHeader("X-Requested-With", "Worker-XMLHttpRequest");
_xhr.setRequestHeader("X-Worker-Hive", "Pollen-JS" );
Simply comment them out
Also, Pollen ajax seems to return a JSON object containing the data in JSON format AND as a string, so do
o = JSON.parse(data.string)//data is the parameter to the callback function
The reduced ChemDoodle library (without document-related methods) will work like a charm with pollen ajax.