<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>The Dynamic Programmer</title>
 <link href="http://blog.dynamicprogrammer.com/atom.xml" rel="self"/>
 <link href="http://blog.dynamicprogrammer.com/"/>
 <updated>2015-03-23T10:18:32-04:00</updated>
 <id>http://blog.dynamicprogrammer.com/</id>
 <author>
   <name>Hernan Garcia</name>
   <email>hernan@dynamicprogrammer.com</email>
 </author>

 
   <entry>
     <title>Adding a logo to ReactBootstrap NavBar.</title>
     <link href="http://blog.dynamicprogrammer.com/2015/03/10/adding-a-logo-to-react-bootstrap-navbar.html"/>
     <updated>2015-03-10T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2015/03/10/adding-a-logo-to-react-bootstrap-navbar</id>
     <content type="html">&lt;p&gt;This is very simple, but for whatever reason I struggle for a bit. So I&amp;#8217;m posting it here in case somebody else finds it helpful.&lt;/p&gt;
&lt;p&gt;I wanted to add an icon as the logo to the navigation bar while using &lt;a href=&quot;http://react-bootstrap.github.io/&quot;&gt;ReactBootstrap&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The examples on the ReactBootstrap site use a string as the brand attribute.&lt;/p&gt;
&lt;p&gt;I took me a bit of trying different things until I decided to take a look at the code of the library.&lt;/p&gt;
&lt;p&gt;After a quick look, the solution to this was obvious. The `brand` attribute is of type `React.PropTypes.node`&lt;/p&gt;
&lt;p&gt;In other words you can do this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

  var icon = (
    &amp;lt;span class=&quot;logo&quot;&amp;gt;
      &amp;lt;a href=&quot;/&quot;&amp;gt;
        &amp;lt;img src=&quot;/awesome-logo.png&quot; height=&quot;33&quot; width=&quot;120&quot; alt=&quot;text here&quot; /&amp;gt;&amp;lt;/a&amp;gt;
    &amp;lt;/span&amp;gt;
  );

  ...
    &amp;lt;Navbar brand={icon} toggleNavKey={0}&amp;gt;
  ...

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voila! It works!&lt;/p&gt;
&lt;p&gt;Not sure if this idiomatic React, so if you know of a better way, please leave a comment below.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>The DevOps trap, we should all embrace DevOps.</title>
     <link href="http://blog.dynamicprogrammer.com/2015/03/06/the-devops-trap.html"/>
     <updated>2015-03-06T00:00:00-05:00</updated>
     <id>http://blog.dynamicprogrammer.com/2015/03/06/the-devops-trap</id>
     <content type="html">&lt;p&gt;It all starts with the undeniable need and benefits of bringing the different practices and areas of software development together. Agile practices allow us to move faster and adapt to change.&lt;/p&gt;
&lt;p&gt;XP practices promote code quality and better (or just more reliable) products for the end users.&lt;/p&gt;
&lt;p&gt;The idea of cross-functional teams help us to identify problems in our approach to solve issues and pinpoint bad architectural decisions.&lt;/p&gt;
&lt;p&gt;DevOps is one of the newest practices on the Agile arsenal. In reality is not that new, but we have been hearing about it more and more in the last three to four years and now, even the enterprise is starting to notice.&lt;/p&gt;
&lt;p&gt;According to Wikipedia DevOps is:&lt;/p&gt;
&lt;p&gt;&lt;i&gt;DevOps (a portmanteau of &amp;#8220;development&amp;#8221; and &amp;#8220;operations&amp;#8221;) is a concept dealing, among other things with software development, operations and services. It emphasizes communication, collaboration and integration between software developers and information technology (IT) operations personnel. DevOps is a response to the interdependence of software development and IT operations.&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;That is a definition that I can agree with.&lt;/p&gt;
&lt;p&gt;Notice that there are no mentions in the &lt;a href=&quot;http://en.wikipedia.org/wiki/DevOps&quot;&gt;wikipedia article&lt;/a&gt; to &amp;#8220;developers&amp;#8221; doing &amp;#8220;operations&amp;#8221;. That is not a requisite of DevOps.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s all about communication and collaboration. In time this usually means that some or all the developers in the team will have access to and help with whatever scripts, recipes, modules or method of deployment and configuration is used.&lt;/p&gt;
&lt;p&gt;The focus is on transparency and predictability. Automation and the introduction of better management tools to reduce the amount of manual, repetitive and (let&amp;#8217;s face it) boring work is a big focus of the practice.&lt;/p&gt;
&lt;p&gt;This collaboration will naturally bring some of the developers more interested with the operations, network and hardware side of things into a tighter collaboration with the &amp;#8220;operations team&amp;#8221;. This results in a &amp;#8220;good&amp;#8221; vicious circle and big gains for the company that adopt the practice.&lt;/p&gt;
&lt;h3&gt;The trap&lt;/h3&gt;
&lt;p&gt;As with all social practices, the trap is trying to force DevOps into people. This can take many forms but the one that I keep seeing popping around is the idea of having developers double up as operation people and thus not having a proper operations team.&lt;/p&gt;
&lt;p&gt;Don&amp;#8217;t get me wrong. I think that the goal should be not having &amp;#8220;teams&amp;#8221; and having polyskills people that can perform in different areas as needed or as they (as individuals) want to grow into.&lt;/p&gt;
&lt;p&gt;This is the same trap we got into with other Agile practices, like &amp;#8220;architecture&amp;#8221; or much rather &amp;#8220;no architecture&amp;#8221;. Since we don&amp;#8217;t have glorify architects in an agile team, sometimes this is portrayed as &amp;#8220;we don&amp;#8217;t need no stinking architecture&amp;#8221;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://globalnerdy.com/wordpress/wp-content/uploads/2007/11/dilbert-agile_programming.gif&quot; alt=&quot;classic dilbert cartoon about Agile presented to a team&quot;&gt;&lt;/p&gt;
&lt;h3&gt;We should all embrace DevOps&lt;/h3&gt;
&lt;p&gt;We should certainly embrace DevOps and QaDev and DevProd and whatever new practice comes along that help us remove the barriers for collaboration. That encourage open and high bandwidth communication between all the members of the team and between &lt;a href=&quot;http://en.wikipedia.org/wiki/The_Chicken_and_the_Pig&quot;&gt;chickens and pigs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;Specially, between chikens and pigs!&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;But we should also recognize the need for strong operation people as part of the team tacking charge and helping the team move forward faster and safer. Enabling many deployments a day and achieving the somehow elusive holly grail of Continuous Delivery.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Distro transports plug-ins.</title>
     <link href="http://blog.dynamicprogrammer.com/2014/06/04/distro-plug-ins.html"/>
     <updated>2014-06-04T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/06/04/distro-plug-ins</id>
     <content type="html">&lt;h3&gt;distro-redis&lt;/h3&gt;
&lt;p&gt;I yanked the support for Redis from the Distro module and moved into distro-redis. This makes distro lightweight and remove dependencies on third party modules on the code (for now).&lt;/p&gt;
&lt;p&gt;If you want to use redis you will need to add dependencies no only on distro but also on &lt;span class=&quot;code&quot;&gt;distro-redis&lt;/span&gt; and when creating the distro factory pass the module to the &lt;span class=&quot;code&quot;&gt;create&lt;/span&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	var distro = require(&#39;distro&#39;);
	var distroRedis = require(&#39;distro-redis&#39;)

	var server = distro.create(distroRedis).server(options);

	//Or

	var client = distro.create(distroRedis).client(options);

&lt;/code&gt;&lt;/pre&gt;</content>
   </entry>
 
   <entry>
     <title>A complete overhaul and tones of improvements on Distro.</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/28/complete-overhaul-and-improvements-for-distro.html"/>
     <updated>2014-05-28T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/28/complete-overhaul-and-improvements-for-distro</id>
     <content type="html">&lt;h3&gt;What&amp;#8217;s distro?&lt;/h3&gt;
&lt;p&gt;The distro module is intended to simplify the way services communicate with each other. It abstracts several different transports while providing a consistent interface. It also specifies a message format and comes with some helper factories to create and parse those messages.&lt;/p&gt;
&lt;p&gt;It comes with some prepackaged transports for udp4 and udp6, tcp and redis (using pub-sub).&lt;br /&gt;
You can easily create your own transports as plug-ins (more details below).&lt;br /&gt;
Note that there is not interoperability between transports. Both clients and servers have to use the same transport to be able to talk to each other.&lt;/p&gt;
&lt;h3&gt;Updates on version 0.3.1&lt;/h3&gt;
&lt;h4&gt;Message goes mainstream.&lt;/h4&gt;
&lt;p&gt;Before you needed to call the &lt;span class=&quot;code&quot;&gt;create()&lt;/span&gt; method of the distro module before creating a new message.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	var distro = require(&#39;distro&#39;);
	distro.create().message(headers, payload);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That makes no sense, specially with the new semantics of the create method (see below). The &lt;span class=&quot;code&quot;&gt;message&lt;/span&gt; method can be called directly from the module now.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	var distro = require(&#39;distro&#39;);
	distro.message(headers, payload);

&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Added support for Redis (pub-sub) as a transport.&lt;/h4&gt;
&lt;p&gt;If you don&amp;#8217;t want to use udp or tcp to pass messages around and you prefer using a queue, distro have you covered now.&lt;br /&gt;
Just use the redis transport and use pub-sub underneath.&lt;/p&gt;
&lt;h4&gt;Fixed big oversight from previous versions.&lt;/h4&gt;
&lt;p&gt;Before v 0.3.0 you registered callbacks directly on the verbs and the Uri filtering was done by the sever. This is sub-optimal to say the least.&lt;/p&gt;
&lt;p&gt;Now, all the registration methods (except &lt;span class=&quot;code&quot;&gt;receive&lt;/span&gt;) take the Uri as the first parameter.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	server.receive(callback);
	server.get(&quot;/path/&quot;, callbackGet);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps the same functionality as before for all the methods, even the receive method. All callbacks registered with &lt;span class=&quot;code&quot;&gt;receive&lt;/span&gt; will be called each time a message is processed by the server regarding verb or Uri.&lt;/p&gt;
&lt;h4&gt;New transports and you can implement your own.&lt;/h4&gt;
&lt;p&gt;Transports are implemented as plug-ins. You can implement your own transport that respond the same &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; and inject the module as the first argument of the &lt;span class=&quot;code&quot;&gt;create()&lt;/span&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	var distro = require(&#39;distro&#39;);
	var myTransport = require(&#39;smoke-signals-transport&#39;);

	var server = distro.create(myTransport).sever();

&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Better error messages.&lt;/h4&gt;
&lt;p&gt;Not just better error messages but a few more error checking in place to try to avoid the most common problems.&lt;/p&gt;
&lt;h4&gt;Much better test coverage due to the new design.&lt;/h4&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;h3&gt;Plans for the future.&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;m planning on changing a few more things fairly soon in the way message headers are constructed and how back and forth communication takes place.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Calculating distance and speed with the GeoLocation API - PhoneGap (Part 9).</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/23/calculate-distance-with-geolocation-and-PhoneGap-part-9.html"/>
     <updated>2014-05-23T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/23/calculate-distance-with-geolocation-and-PhoneGap-part-9</id>
     <content type="html">&lt;p&gt;&lt;i&gt;Code for this series is available on &lt;a href=&quot;https://github.com/hgarcia/dynamic-sports&quot;&gt;Github&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;
&lt;h3&gt;Home screen changes.&lt;/h3&gt;
&lt;p&gt;I decided to change the data displayed on Home screen to provide information regarding distance as well.&lt;/p&gt;
&lt;p&gt;I also made a few changes on the way we are using the Geo location plug-in. I decided to stop calling &lt;span class=&quot;code&quot;&gt;watchPosition()&lt;/span&gt; and instead use the angular &lt;span class=&quot;code&quot;&gt;$interval&lt;/span&gt; service and call &lt;span class=&quot;code&quot;&gt;getCurrentPosition()&lt;/span&gt; once every second.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not sure if this will work for sports where speed is hight (ski, biking) and I may have to either short the time or revert this changes.&lt;/p&gt;
&lt;p&gt;The changes where fairly easy to do and didn&amp;#8217;t required many changes on the consumers.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	/* globals angular */
	angular.module(&#39;dynamic-sports.services&#39;)
	  .factory(&#39;geoLocationService&#39;, [&#39;$interval&#39;, function ($interval) {
	    &#39;use strict&#39;;
	    var watchId;

	    return {
	      start: function (success, error) {
	        watchId = $interval(function () {
	          navigator.geolocation.getCurrentPosition(success, error, {enableHighAccuracy: true});
	        }, 1000);
	      },
	      stop: function () {
	        if (watchId) {
	          $interval.cancel(watchId);
	        }
	      }
	    };
	  }]);

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Calculating distances between coordinates.&lt;/h3&gt;
&lt;p&gt;This is a solved problem, so a quick &lt;a href=&quot;https://www.google.com/search?q=distance+between+two+coordinates&quot;&gt;Google search&lt;/a&gt; and a quick visit to &lt;a href=&quot;http://www.movable-type.co.uk/scripts/latlong.html&quot;&gt;this website&lt;/a&gt; provides the answer.&lt;/p&gt;
&lt;p&gt;It translates to two functions. The meat of the calculation is done inside the &lt;span class=&quot;code&quot;&gt;calculateDistance&lt;/span&gt; function. As a convention, I return -1 in case there is an error due to invalid data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	function toRad(value) {
	  var RADIANT_CONSTANT = 0.0174532925199433;
	  return (value * RADIANT_CONSTANT);
	}

	function calculateDistance(starting, ending) {
	  var KM_RATIO = 6371;
	  try {      
	    var dLat = toRad(ending.latitude - starting.latitude);
	    var dLon = toRad(ending.longitude - starting.longitude);
	    var lat1Rad = toRad(starting.latitude);
	    var lat2Rad = toRad(ending.latitude);
	    
	    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
	            Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1Rad) * Math.cos(lat2Rad);
	    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
	    var d = KM_RATIO * c;
	    return d;
	  } catch(e) {
	    return -1;
	  }
	}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The trick to display distance traveled is to use an accumulator for the distance traveled each time the interval function is called.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	function setSpeed(coords) {
	  var KMS_TO_KMH = 3600;
	  if (!prevCoord) {
	    prevCoord = coords;
	  }
	  $scope.session.distance += calculateDistance(prevCoord, coords);
	  if (duration.asSeconds() &amp;gt; 0) {
	    $scope.session.avgSpeed = ($scope.session.distance / duration.asSeconds()) * KMS_TO_KMH;
	  }
	  prevCoord = coords;
	}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;span class=&quot;code&quot;&gt;setSpeed&lt;/span&gt; function takes the coordinates received from &lt;span class=&quot;code&quot;&gt;getCurrentPosition()&lt;/span&gt;. I think the code is pretty self explanatory.&lt;/p&gt;
&lt;p&gt;Some things that may not be obvious are that &lt;span class=&quot;code&quot;&gt;prevCoord&lt;/span&gt; is an interval variable of the controller and keeps track of the previous position.&lt;/p&gt;
&lt;p&gt;You may be wandering where the &lt;span class=&quot;code&quot;&gt;duration&lt;/span&gt; object is coming from. We create it when we click the start button. It&amp;#8217;s a moment.js duration object, and the &lt;span class=&quot;code&quot;&gt;asSeconds&lt;/span&gt; method returns the total number of seconds in the interval.&lt;/p&gt;
&lt;p&gt;The distance is calculated in kilometers so we need to convert kilometers per second to kilometer per hour. It&amp;#8217;s easily done by multiplying by 3600.&lt;/p&gt;
&lt;p&gt;We did change the view to reflect this changes as well, and the session object now looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	$scope.session = {avgSpeed: 0, distance: 0, elapsed: &quot;00:00&quot;};

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Accuracy.&lt;/h3&gt;
&lt;p&gt;The Geo location &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; accuracy is just not there yet. It&amp;#8217;s understandable and it will depend on many factors, like location, satellite coverage, interference from buildings and more. In any case I did enable high accuracy and so far the results have been mixed.&lt;/p&gt;
&lt;p&gt;I need to do some more testing in real conditions to see how things work. May be this week-end.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Deleting files with PhoneGap (Part 8).</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/22/deleting-files-with-PhoneGap-part-8.html"/>
     <updated>2014-05-22T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/22/deleting-files-with-PhoneGap-part-8</id>
     <content type="html">&lt;p&gt;&lt;i&gt;Code for this series is available on &lt;a href=&quot;https://github.com/hgarcia/dynamic-sports&quot;&gt;Github&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;
&lt;h3&gt;Cleaning up&lt;/h3&gt;
&lt;p&gt;In our application we are uploading the files with the data from a session into our server. The problem is that we are not removing the files from the device.&lt;br /&gt;
So the next time that you want to upload files, you will be uploading all the files again.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s not that much that we forgot about this, but we decided to move on to other things at the time. It&amp;#8217;s time to come back and clean up this part of the code.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s surprisingly simple.&lt;/p&gt;
&lt;p&gt;Our code is given list of &lt;span class=&quot;code&quot;&gt;fileEntry&lt;/span&gt; objects to the upload method. Each &lt;span class=&quot;code&quot;&gt;fileEntry&lt;/span&gt; have a &lt;span class=&quot;code&quot;&gt;remove&lt;/span&gt; method that will delete the entry from the device.&lt;/p&gt;
&lt;p&gt;We need to call this method after the upload succeed. We could add the code into the &lt;span class=&quot;code&quot;&gt;serverService.upload&lt;/span&gt; method, but I don&amp;#8217;t think  it belongs there.&lt;/p&gt;
&lt;p&gt;I think the consumer of this method should be responsible from deciding what to do with the file.&lt;/p&gt;
&lt;p&gt;We need to modify the &lt;span class=&quot;code&quot;&gt;upload&lt;/span&gt; method a bit to return the fileEntry to the consumer on success.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	...

	function savedFile(file, cb) {
	  return function () {
	    cb(file);
	  };
	}

	return {
	  upload: function (files, onSuccess, onError) {
	    var ft =  new FileTransfer();
	    for (var i = 0; i &amp;lt; files.length; i++) {
	      var file = files[i];
	      ft.upload(file.toURL(), encodeURI(&quot;http://server.com/uploads&quot;), savedFile(file, onSuccess), onError, getFileUploadOptions(file.fullPath));
	    }
	  }
	};

	...

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the consumer can call the remove method directly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	function filesSaved(file) {
	  if (file.remove) {
	    file.remove();
	  }
	  $timeout(function () {
	    $scope.totalFiles -= 1;
	    checkUploadFinished();
	  }, 100);
	}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just in case we do a quick check to verify the argument respond to the &lt;span class=&quot;code&quot;&gt;remove&lt;/span&gt; method, before calling it.&lt;/p&gt;
&lt;p&gt;You can add some callbacks to the remove method to react on success or error if you want.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>progeny</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/21/progeny.html"/>
     <updated>2014-05-21T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/21/progeny</id>
     <content type="html"></content>
   </entry>
 
   <entry>
     <title>Displaying current and max speed with PhoneGap and Ionic (Part 7).</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/21/displaying-speed-and-duration-PhoneGap-with-Ionic-part-7.html"/>
     <updated>2014-05-21T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/21/displaying-speed-and-duration-PhoneGap-with-Ionic-part-7</id>
     <content type="html">&lt;p&gt;&lt;i&gt;Code for this series is available on &lt;a href=&quot;https://github.com/hgarcia/dynamic-sports&quot;&gt;Github&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;
&lt;h3&gt;Max and current speed.&lt;/h3&gt;
&lt;p&gt;We want to start displaying the maximum and current speed in the home page. The numbers need to be big enough to read easily. we also want to show the  duration for the active session.&lt;/p&gt;
&lt;h3&gt;UI&lt;/h3&gt;
&lt;p&gt;We will change the home page once more. We will remove the &lt;strong&gt;Uploading files&lt;/strong&gt; message since the badge numbers on the upload icon provide enough feedback.&lt;/p&gt;
&lt;p&gt;We will accommodate the three boxes for the counters near the top of the application and we will move the start/stop button a tiny bit closer to the bottom.&lt;br /&gt;
The &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; for the &lt;span class=&quot;code&quot;&gt;home.html&lt;/span&gt; page now look like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	&amp;lt;ion-view title=&quot;New session&quot;&amp;gt;
	  &amp;lt;ion-nav-buttons side=&quot;right&quot;&amp;gt;
	      &amp;lt;button class=&quot;button button-clear icon ion-ios7-cloud-upload&quot; ng-click=&quot;upload()&quot; ng-disabled=&quot;uploadDisabled&quot;&amp;gt;&amp;lt;/button&amp;gt;
	      &amp;lt;span class=&quot;count-badge&quot; ng-show=&quot;totalFiles&quot;&amp;gt;&amp;lt;/span&amp;gt;
	  &amp;lt;/ion-nav-buttons&amp;gt;
	  &amp;lt;div class=&quot;status slide&quot; ng-class=&#39;{&quot;status-success-visible&quot;: uploadSucceded, &quot;status-error-visible&quot;: uploadErrored}&#39;&amp;gt;&amp;lt;/div&amp;gt;
	  &amp;lt;ion-content class=&quot;center-child&quot; has-header=&quot;true&quot; padding=&quot;true&quot;&amp;gt;
	    &amp;lt;div class=&quot;speedometer-container&quot;&amp;gt;
	        &amp;lt;div class=&quot;instrument&quot;&amp;gt;
	            &amp;lt;small class=&quot;icon-left ion-ios7-speedometer&quot;&amp;gt; max speed&amp;lt;/small&amp;gt;&amp;lt;br/&amp;gt;
	            &amp;lt;div class=&quot;instrument-figure&quot;&amp;gt; &amp;lt;span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;small&amp;gt;km/h&amp;lt;/small&amp;gt;&amp;lt;/div&amp;gt;
	        &amp;lt;/div&amp;gt;
	        &amp;lt;div class=&quot;instrument&quot;&amp;gt;
	            &amp;lt;small class=&quot;icon-left ion-ios7-speedometer-outline&quot;&amp;gt; curr. speed&amp;lt;/small&amp;gt;&amp;lt;br/&amp;gt;
	            &amp;lt;div class=&quot;instrument-figure&quot;&amp;gt; &amp;lt;span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;small&amp;gt;km/h&amp;lt;/small&amp;gt;&amp;lt;/div&amp;gt;
	        &amp;lt;/div&amp;gt;
	        &amp;lt;div class=&quot;instrument&quot;&amp;gt;
	            &amp;lt;small class=&quot;icon-left ion-ios7-stopwatch-outline&quot;&amp;gt; duration&amp;lt;/small&amp;gt;&amp;lt;br/&amp;gt;
	            &amp;lt;div class=&quot;instrument-figure no-units&quot;&amp;gt; &amp;lt;span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;small&amp;gt;&amp;lt;/small&amp;gt;&amp;lt;/div&amp;gt;
	        &amp;lt;/div&amp;gt;
	    &amp;lt;/div&amp;gt;
	    &amp;lt;div class=&quot;duration-container&quot;&amp;gt;
	    &amp;lt;/div&amp;gt;
	    &amp;lt;play-stop-button class=&quot;play-button&quot; click-handler=&quot;recording&quot;&amp;gt;&amp;lt;/play-stop-button&amp;gt;
	  &amp;lt;/ion-content&amp;gt;
	&amp;lt;/ion-view&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are going to clean up the &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; significantly. Mostly re-structuring the &lt;span class=&quot;code&quot;&gt;sass&lt;/span&gt; file and removing the center button hack. We will be using absolute positioning for now.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	.center-child {
	  .scroll {
	    .speedometer-container {
	      margin-bottom: 24px;
	      margin-top: 12px;
	      position: relative;
	      text-align:center;
	      .instrument {
	        border: solid 2px $balanced;
	        display: inline-block;
	        min-width: 90px;
	        padding: 3px 6px;
	        .instrument-figure {
	          font-size: 24px;
	          &amp;amp;.no-units {
	            height: 25px;
	          }
	          small {
	            font-size: 12px;
	          }
	        }
	      }
	    }
	    .play-button {
	      position: relative;
	      top: 70px;
	    }

	....

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Controller abuse.&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;m abusing the &lt;span class=&quot;code&quot;&gt;HomeCtrl&lt;/span&gt; here and it&amp;#8217;s pretty obvious that a service wants to spawn out from this code. &lt;br /&gt;
we are adding a new object on the &lt;span class=&quot;code&quot;&gt;$scope&lt;/span&gt;, the &lt;span class=&quot;code&quot;&gt;session&lt;/span&gt; object, that contains three properties; maximum speed, current speed and duration.&lt;/p&gt;
&lt;p&gt;We injected the &lt;span class=&quot;code&quot;&gt;$interval&lt;/span&gt; service to the controller to calculate duration and display it every second. We are also adding the great &lt;a href=&quot;http://momentjs.com&quot;&gt;moment.js&lt;/a&gt; library to calculate and display duration easily.&lt;/p&gt;
&lt;p&gt;Take a look at the &lt;span class=&quot;code&quot;&gt;stopTimer&lt;/span&gt; method. We need to make sure we de-register the interval and we destroy the times to avoid memory leaks.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	/* globals angular, console */
	angular.module(&#39;dynamic-sports.controllers&#39;)
	  .controller(&#39;HomeCtrl&#39;, [&#39;$scope&#39;, &#39;$timeout&#39;, &#39;$interval&#39;, &#39;$ionicPlatform&#39;, &#39;geoLocationService&#39;, &#39;fileService&#39;, &#39;serverService&#39;,
	    function ($scope, $timeout, $interval, $ionicPlatform, geoLocationService, fileService, serverService) {
	    &#39;use strict&#39;;
	    var fileName;
	    var elapsedTimer;
	    var duration;
	    $scope.uploading = false;
	    $scope.uploadDisabled = false;
	    $scope.uploadErrored = false;
	    $scope.uploadSucceded = false;
	    $scope.uploadMessage = &quot;&quot;;
	    $scope.totalFiles = 0;

	    function resetSession() {
	      $scope.session = {maxSpeed: 0, curSpeed: 0, elapsed: &quot;00:00&quot;};
	    }

	    function toKmPerHour(meterPerSecond) {
	      return String(meterPerSecond * 3.6).substring(0, 3);
	    }

	    function setSpeed(speed) {
	      if (speed &amp;gt; 0) {
	        $scope.session.curSpeed = toKmPerHour(speed);
	      }
	      if (speed &amp;gt; $scope.session.maxSpeed) {
	        $scope.session.maxSpeed = toKmPerHour(speed);
	      }
	    }

	    function onChange(newPosition) {
	      var data = newPosition.coords;
	      data.timestamp = newPosition.timestamp;
	      setSpeed(data.speed);
	      fileService.save(fileName, data, function () {}, function (error) { alert(&quot;Error file save&quot;);});
	    }

	    function toolTip() {
	      if (!$scope.uploading) {
	        $scope.uploadErrored = $scope.erroredCount &amp;gt; 0;
	        $scope.uploadSucceded = $scope.erroredCount === 0;
	        $scope.uploadMessage = ($scope.uploadSucceded) ? &quot;Upload completed&quot; : &quot;Failed to upload &quot; + $scope.erroredCount + &quot; files&quot;;
	        $timeout(function () {
	          $scope.uploadErrored = false;
	          $scope.uploadSucceded = false;
	        }, 3000);
	      }
	    }

	    function checkUploadFinished() {
	      $scope.uploading = ($scope.totalFiles &amp;gt; $scope.erroredCount);
	      $scope.uploadDisabled = $scope.totalFiles === 0 || $scope.uploading;
	      toolTip();
	    }

	    function errHandler(error) {
	      $scope.erroredCount += 1;
	      checkUploadFinished();
	    }

	    function filesSaved() {
	      $timeout(function () {
	        $scope.totalFiles -= 1;
	        checkUploadFinished();
	      }, 100);
	    }

	    function uploadFiles(files) {
	      $scope.uploading = true;
	      $scope.uploadDisabled = true;
	      filesToUpload(files);
	      $scope.erroredCount = 0;
	      checkUploadFinished();
	      $timeout(function () {
	        serverService.upload(files, filesSaved, errHandler);
	      }, 100);
	    }

	    function checkUploadDisabledStatus() {
	      $scope.uploadDisabled = $scope.totalFiles === 0;
	    }

	    function setTotalFilesTo(qty) {
	      $scope.totalFiles = qty;
	      checkUploadDisabledStatus();
	    }

	    function filesToUpload(files) {
	      $timeout(function () {
	        setTotalFilesTo(files.length);
	      }, 10);
	    }

	    function padTime(val) {
	      if (val &amp;lt; 10) {
	        return &quot;0&quot; + val;
	      }
	      return val;
	    }

	    function displayDuration() {
	      var hours = padTime(duration.get(&#39;hours&#39;));
	      var minutes = padTime(duration.get(&#39;minutes&#39;));
	      var seconds = padTime(duration.get(&#39;seconds&#39;));
	      if (hours === &quot;00&quot;) {
	        $scope.session.elapsed = minutes + &quot;:&quot; + seconds;
	      } else {
	        $scope.session.elapsed = hours + &quot;:&quot; + minutes + &quot;:&quot; + seconds;
	      }
	    }

	    function startTimer() {
	      duration = moment.duration(0);
	      elapsedTimer = $interval(function () {
	        duration.add(1, &#39;s&#39;);
	        displayDuration();
	      }, 1000);
	    }

	    function stopTimer() {
	      if (angular.isDefined(elapsedTimer)) {
	        $interval.cancel(elapsedTimer);
	        elapsedTimer = undefined;
	      }
	      resetSession();
	    }

	    $scope.upload = function () {
	      fileService.list(uploadFiles, errHandler);
	    };

	    $scope.recording = function (on) {
	      if (on) {
	        fileName = geoLocationService.start(onChange, function (err) { alert(&quot;Error geolocation service&quot;); });
	        startTimer();
	      } else {
	        geoLocationService.stop();
	        setTotalFilesTo($scope.totalFiles + 1);
	        stopTimer();
	      }
	    };

	    $ionicPlatform.ready(function () {
	      fileService.list(filesToUpload, errHandler);
	    });

	    resetSession();
	  }]);

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Speed conversion.&lt;/h3&gt;
&lt;p&gt;The Geo-location service reports speed in meters per second and we want to display kilometers per hour. The conversion is easily done multiplying the speed by 3.6&lt;br /&gt;
We also make sure that we only display 3 characters for speed, so we use the &lt;span class=&quot;code&quot;&gt;substring&lt;/span&gt; method of the &lt;span class=&quot;code&quot;&gt;String&lt;/span&gt; object.&lt;/p&gt;
&lt;p&gt;We encapsulates all the logic in the &lt;span class=&quot;code&quot;&gt;toKmPerHour&lt;/span&gt; function.&lt;/p&gt;
&lt;h3&gt;How it looks.&lt;/h3&gt;
&lt;p&gt;This video was taken directly from my iPhone.&lt;/p&gt;
&lt;p&gt;&lt;iframe width=&quot;420&quot; height=&quot;315&quot; src=&quot;//www.youtube.com/embed/QfTCLIe951s?rel=0&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>water planter</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/20/water-planter-slide.html"/>
     <updated>2014-05-20T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/20/water-planter-slide</id>
     <content type="html"></content>
   </entry>
 
   <entry>
     <title>blooming</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/20/cherry-tree.html"/>
     <updated>2014-05-20T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/20/cherry-tree</id>
     <content type="html"></content>
   </entry>
 
   <entry>
     <title>A few different ways to improve user feedback in our Ionic application (Part 6).</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/16/user-feedback-on-PhoneGap-with-Ionic-part-6.html"/>
     <updated>2014-05-16T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/16/user-feedback-on-PhoneGap-with-Ionic-part-6</id>
     <content type="html">&lt;p&gt;&lt;i&gt;Code for this series is available on &lt;a href=&quot;https://github.com/hgarcia/dynamic-sports&quot;&gt;Github&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;
&lt;h3&gt;Showing upload progress.&lt;/h3&gt;
&lt;p&gt;We want to report the progress of uploaded files with a simple message and a counter indicating how many have been uploaded. In the case there is an error with a file we want to show all errors at the end of the process.&lt;/p&gt;
&lt;p&gt;Since the name of the file is not important (the user have no control on the file and there is not much he can do about the files itself), we will just indicate the number of files that erred uploading.&lt;/p&gt;
&lt;p&gt;Once the process finishes we want to show a message that slides down from the top that indicates the result.&lt;/p&gt;
&lt;p&gt;&lt;iframe width=&quot;420&quot; height=&quot;315&quot; src=&quot;//www.youtube.com/embed/mwn__C6zitM?rel=0&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;h3&gt;The code&lt;/h3&gt;
&lt;p&gt;We can start with a very simple UI element in the home page to indicate that the upload process started.&lt;/p&gt;
&lt;p&gt;We are also moving the upload button into the navbar and adding a small badge icon to display the number of files.&lt;br /&gt;
If we have no files we want to disable the button and hide the badge.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	&amp;lt;ion-view title=&quot;New session&quot;&amp;gt;
	  &amp;lt;ion-nav-buttons side=&quot;right&quot;&amp;gt;
	      &amp;lt;button class=&quot;button button-clear icon ion-ios7-cloud-upload&quot; ng-click=&quot;upload()&quot; ng-disabled=&quot;uploadDisabled&quot;&amp;gt;&amp;lt;/button&amp;gt;
	      &amp;lt;span class=&quot;count-badge&quot; ng-show=&quot;totalFiles&quot;&amp;gt;&amp;lt;/span&amp;gt;
	  &amp;lt;/ion-nav-buttons&amp;gt;
	  &amp;lt;ion-content class=&quot;center-child&quot; has-header=&quot;true&quot; padding=&quot;true&quot;&amp;gt;
	    &amp;lt;div class=&quot;info-message&quot; ng-show=&quot;uploading&quot;&amp;gt;Uploading files...&amp;lt;/div&amp;gt;
	    &amp;lt;play-stop-button click-handler=&quot;recording&quot;&amp;gt;&amp;lt;/play-stop-button&amp;gt;
	  &amp;lt;/ion-content&amp;gt;
	&amp;lt;/ion-view&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we need to do a few changes to the &lt;span class=&quot;code&quot;&gt;HomeCtrl&lt;/span&gt; as well to keep track of success and failures.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	/* globals angular, console */
	angular.module(&#39;dynamic-sports.controllers&#39;)
	  .controller(&#39;HomeCtrl&#39;, [&#39;$scope&#39;, &#39;$timeout&#39;, &#39;$ionicPlatform&#39;, &#39;geoLocationService&#39;, &#39;fileService&#39;, &#39;serverService&#39;,
	    function ($scope, $timeout, $ionicPlatform, geoLocationService, fileService, serverService) {
	    &#39;use strict&#39;;
	    var fileName;
	    $scope.uploading = false;
	    $scope.uploadDisabled = false;

	    function onChange(newPosition) {
	      var data = newPosition.coords;
	      data.timestamp = newPosition.timestamp;
	      fileService.save(fileName, data, function () {}, function (error) {});
	    }

	    function checkUploadFinished() {
	      $scope.uploading = ($scope.totalFiles &amp;gt; $scope.erroredCount);
	      $scope.uploadDisabled = $scope.totalFiles === 0 || $scope.uploading;
	    }

	    function errHandler(error) {
	      $scope.erroredCount += 1;
	      checkUploadFinished();
	    }

	    function filesSaved() {
	      $timeout(function () {
	        $scope.totalFiles -= 1;
	        checkUploadFinished();
	      }, 100);
	    }

	    function uploadFiles(files) {
	      $scope.uploading = true;
	      $scope.uploadDisabled = true;
	      filesToUpload(files);
	      $scope.erroredCount = 0;
	      checkUploadFinished();
	      $timeout(function () {
	        serverService.upload(files, filesSaved, errHandler);
	      }, 100);
	    }

	    function filesToUpload(files) {
	      $timeout(function () {
	        $scope.totalFiles = files.length;
	        $scope.uploadDisabled = $scope.totalFiles === 0;
	      }, 10);
	    }

	    $scope.upload = function () {
	      fileService.list(uploadFiles, errHandler);
	    };

	    $scope.recording = function (on) {
	      if (on) {
	        fileName = geoLocationService.start(onChange, errHandler);
	      } else {
	        geoLocationService.stop();
	        $scope.totalFiles += 1;
	        $scope.uploadDisabled = false;
	      }
	    };

	    $ionicPlatform.ready(function () {
	      fileService.list(filesToUpload, errHandler);
	    });
	  }]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We check for the number of files once we load the home page and we keep track of the number of created and uploaded files.&lt;/p&gt;
&lt;h3&gt;Changes on the file service.&lt;/h3&gt;
&lt;p&gt;During testing I found that the emulator reads not only the files we created but a few others from the root of the file sytem. We will create an specific folder for the tracking files to avoid problems.&lt;/p&gt;
&lt;p&gt;We do that adding a private method on our &lt;span class=&quot;code&quot;&gt;fileService&lt;/span&gt; that creates a Directory if it doesn&amp;#8217;t exist.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	function getCreateDir(entry, successCb, errorCb) { 
	  entry.getDirectory(&quot;dynsports&quot;, {create: true, exclusive: false}, successCb, errorCb); 
	}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We replace all calls to &lt;span class=&quot;code&quot;&gt;fileSystem.root.getFile()&lt;/span&gt; with the result of calling this new function. For example our &lt;span class=&quot;code&quot;&gt;write&lt;/span&gt; function instead of using the &lt;span class=&quot;code&quot;&gt;root&lt;/span&gt; system directly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	function write(fileName, data, successCb, errorCb) {
	  return function (fileSystem) {
	    fileSystem.root.getFile(fileName, {create: true, exclusive: false}, gotFileEntry(data, successCb, errorCb), errorCb);
	  };
	}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It now looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	function write(fileName, data, successCb, errorCb) {
	  return function (fileSystem) {
	    getCreateDir(fileSystem.root, function (dir) {
	      dir.getFile(fileName, {create: true, exclusive: false}, gotFileEntry(data, successCb, errorCb), errorCb);
	    }, errorCb);
	  };
	}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need to add a method to delete the files once upload finishes successfully.&lt;/p&gt;
&lt;p&gt;The new method looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will call it from the &lt;span class=&quot;code&quot;&gt;HomeCtrl&lt;/span&gt; inside the success call back for the upload method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tool-tip&lt;/h3&gt;
&lt;p&gt;Adding the tool-tip is easy. We only need a few lines of html and this css.&lt;br /&gt;
In the &lt;span class=&quot;code&quot;&gt;home.html&lt;/span&gt; file we add the div that will be our sliding tool-tip.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	&amp;lt;div class=&quot;status slide&quot; ng-class=&#39;{&quot;status-success-visible&quot;: uploadSucceded, &quot;status-error-visible&quot;: uploadErrored}&#39;&amp;gt;&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will use &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; transitions to slide it down and up again. We are using two different classes for success and error to show the tool-tip in different colours.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	.slide {
	  color: #fff;
	  height: 22px;
	  position: absolute;
	  width: 100%;
	  text-align: center;
	  top: 0;
	  z-index: -1;
	  -webkit-transition: all 1s;
	  -moz-transition: all 1s;
	}
	.status-success-visible {
	  background-color: $balanced;
	  top: 64px;
	}
	.status-error-visible {
	  background-color: $energized;
	  top: 64px;
	}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to set the message and change those scope variables to true or false in the controller. We add a new private function that we will call when we check for upload completion.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	

	function toolTip() {
	  if (!$scope.uploading) {
	    $scope.uploadErrored = $scope.erroredCount &amp;gt; 0;
	    $scope.uploadSucceded = $scope.erroredCount === 0;
	    $scope.uploadMessage = ($scope.uploadSucceded) ? &quot;Upload completed&quot; : &quot;Failed to upload &quot; + $scope.erroredCount + &quot; files&quot;;
	    $timeout(function () {
	      $scope.uploadErrored = false;
	      $scope.uploadSucceded = false;
	    }, 3000);
	  }
	}

&lt;/code&gt;&lt;/pre&gt;</content>
   </entry>
 
   <entry>
     <title>Adding TCP support to Distro.</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/15/adding-tcp-support-to-distro.html"/>
     <updated>2014-05-15T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/15/adding-tcp-support-to-distro</id>
     <content type="html">&lt;p&gt;I took a bit of time to add &lt;span class=&quot;caps&quot;&gt;TCP&lt;/span&gt; support to &lt;a href=&quot;https://www.npmjs.org/package/distro&quot;&gt;distro&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now you can create a network of client servers with &lt;span class=&quot;caps&quot;&gt;TCP&lt;/span&gt;, UDP4 and UDP6.&lt;/p&gt;
&lt;p&gt;The &lt;span class=&quot;caps&quot;&gt;TCP&lt;/span&gt; servers use the same Message types.&lt;/p&gt;
&lt;p&gt;I took the time to improve the documentation (I think is completed now).&lt;/p&gt;
&lt;p&gt;I also added two more examples (server and client) for &lt;span class=&quot;caps&quot;&gt;TCP&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;As part of the effort the code was refactored a bit allowing for easier extensibility.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>tree waiting</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/12/tree-waiting.html"/>
     <updated>2014-05-12T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/12/tree-waiting</id>
     <content type="html"></content>
   </entry>
 
   <entry>
     <title>red wing</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/12/red-wing.html"/>
     <updated>2014-05-12T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/12/red-wing</id>
     <content type="html"></content>
   </entry>
 
   <entry>
     <title>butterflies</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/12/butterflies.html"/>
     <updated>2014-05-12T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/12/butterflies</id>
     <content type="html"></content>
   </entry>
 
   <entry>
     <title>bon voyage</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/10/bon-voyage.html"/>
     <updated>2014-05-10T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/10/bon-voyage</id>
     <content type="html"></content>
   </entry>
 
   <entry>
     <title>Ionic new serve task.</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/10/Ionic-new-task-on-beta-4.html"/>
     <updated>2014-05-10T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/10/Ionic-new-task-on-beta-4</id>
     <content type="html">&lt;p&gt;I&amp;#8217;m not sure when these tasks have been introduced but I just installed Ionic 1.0.0 beta 4 and after running the &lt;span class=&quot;code&quot;&gt;ionic&lt;/span&gt; command I saw three new tasks that you can run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

    serve - Start a local development server for easy app development and testing
    login - Login to the Ionic Platform
    upload - Upload an Ionic project to the Ionic Platform (requires login)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I really appreciate the &lt;span class=&quot;code&quot;&gt;serve&lt;/span&gt; task that make running a development server a bit easier than running &lt;span class=&quot;code&quot;&gt;python -m SimpleHTTPServer 8000&lt;/span&gt; from inside the &lt;span class=&quot;code&quot;&gt;www&lt;/span&gt; folder of the project.&lt;/p&gt;
&lt;p&gt;Now you just run &lt;span class=&quot;code&quot;&gt;ionic serve&lt;/span&gt; from the root of the project and your default browser will open with the application loaded in it. It will also run a a live reload server, so when you change the files in your project the browser window will reload with the changes.&lt;/p&gt;
&lt;p&gt;This should improve your development cycle without having to set up any of this on your own.&lt;/p&gt;
&lt;p&gt;The other two task are related to the upcoming Ionic Platform.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Uploading files with PhoneGap and Angular (Part 5).</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/09/uploading-files-with-PhoneGap-Angular-part-5.html"/>
     <updated>2014-05-09T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/09/uploading-files-with-PhoneGap-Angular-part-5</id>
     <content type="html">&lt;p&gt;&lt;i&gt;Code for this series is available on &lt;a href=&quot;https://github.com/hgarcia/dynamic-sports&quot;&gt;Github&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;In the previous to last post we created a series of files, each one with data from a recorded session. We want to upload that data into a server for further processing.&lt;/p&gt;
&lt;p&gt;In this post we will add the upload mechanism and we will build a simple web server that will receive the data.&lt;/p&gt;
&lt;p&gt;From this post one we will only show the code for the implementation and left the test code out of the posts unless there is something specially interesting on it.&lt;br /&gt;
You can always get the code with all the tests in the &lt;a href=&quot;https://github.com/hgarcia/dynamic-sports&quot;&gt;github repository&lt;/a&gt; for this project.&lt;/p&gt;
&lt;h3&gt;Getting the saved files&lt;/h3&gt;
&lt;p&gt;Before uploading any of the files we need to get a list of files available for upload.&lt;br /&gt;
We use the cordova file plugin that we already added previously to the project. &lt;br /&gt;
We will add a list method to our &lt;span class=&quot;code&quot;&gt;fileService&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We expose a public &lt;span class=&quot;code&quot;&gt;list&lt;/span&gt; function and we move the implementation into a private function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
    
    function list(successCb, errorCb) {
      return function (fileSystem) {
        var reader = fileSystem.root.createReader();
        reader.readEntries(successCb, errorCb);
      };
    }

    ... 

    list: function (successCb, errorCb) {
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, list(successCb, errorCb), errorCb);
    },

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The server&lt;/h3&gt;
&lt;p&gt;We need to upload the files to a server, for this exercise we created a simple web server using &lt;a href=&quot;http://expressjs.com&quot;&gt;Express&lt;/a&gt; and we deploy it to Heroku. I&amp;#8217;m not implementing any type of authentication or security at this point. We will do that later.&lt;/p&gt;
&lt;p&gt;&lt;i&gt;The code for the web server can be found on &lt;a href=&quot;https://github.com/hgarcia/dynamic-sports-api&quot;&gt;Github.&lt;/a&gt; You can clone and use it as a starting point if you wish.&lt;/i&gt;&lt;/p&gt;
&lt;h3&gt;And the server service&lt;/h3&gt;
&lt;p&gt;We start by installing the cordova &lt;span class=&quot;code&quot;&gt;file-transfer&lt;/span&gt; plug-in.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
    
    cordova plugin add org.apache.cordova.file-transfer

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the plug-in is installed we can write our upload method. We want to pass an array of &lt;span class=&quot;code&quot;&gt;FileEntry&lt;/span&gt; objects and send each one to the server.&lt;/p&gt;
&lt;p&gt;We also want to report back success or error for each entry individually what makes our implementation very simple.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

    /* globals angular */
    angular.module(&#39;dynamic-sports.services&#39;)
      .factory(&#39;serverService&#39;, function () {
        &#39;use strict&#39;;

        function getFileUploadOptions(fileURI) {
          var options = new FileUploadOptions();
          options.fileName = fileURI.substr(fileURI.lastIndexOf(&#39;/&#39;)+1);
          options.mimeType = &quot;text/plain&quot;;
          return options;
        }
        return {
          upload: function (files, onSuccess, onError) {
            var ft =  new FileTransfer();
            for (var i = 0; i &amp;lt; files.length; i++) {
              var file = files[i];
              ft.upload(file.toURL(), encodeURI(&quot;http://app.herokuapp.com/uploads&quot;), onSuccess, onError, getFileUploadOptions(file.fullPath));
            }
          }
        };
      });

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We hook everything together on the &lt;span class=&quot;code&quot;&gt;HomeCtrl&lt;/span&gt; after injecting the new service&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

    .controller(&#39;HomeCtrl&#39;, [&#39;$scope&#39;, &#39;geoLocationService&#39;, &#39;fileService&#39;, &#39;serverService&#39;,
      function ($scope, geoLocationService, fileService, serverService) {

    ....

    function filesSaved() {
      alert(&quot;Saved&quot;);
    }

    function uploadFiles(files) {
      serverService.upload(files, filesSaved, errHandler);
    }

    $scope.upload = function () {
      fileService.list(uploadFiles, errHandler);
    };
    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we add a button to the home page that calls the &lt;span class=&quot;code&quot;&gt;upload()&lt;/span&gt; function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
    
    &amp;lt;button class=&quot;button button-block&quot; ng-click=&quot;upload()&quot;&amp;gt;Upload&amp;lt;/button&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Next Steps&lt;/h3&gt;
&lt;p&gt;We will add some feedback to the user regarding the upload process and we will improve the UI for the Home page.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>PhoneGap errorCode 1 on FileTransfer upload.</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/07/PhoneGap-filetransfer-upload-errors.html"/>
     <updated>2014-05-07T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/07/PhoneGap-filetransfer-upload-errors</id>
     <content type="html">&lt;h3&gt;FileTransferError.code = 1&lt;/h3&gt;
&lt;p&gt;This seems to be a common error (at least for all the questions about it in the Internet). &lt;br /&gt;
I hope this short post will save some time to others.&lt;/p&gt;
&lt;p&gt;When you follow the documentation for the &lt;span class=&quot;code&quot;&gt;FileTransfer&lt;/span&gt; plug-in you end up with code that looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	var ft =  new FileTransfer();
    ft.upload(fileEntry.fullPath, encodeURI(&quot;http://domain.com/uploads&quot;), onSuccess, onError, getFileUploadOptions(fileEntry.fullPath));

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you do this you will get an error with code 1.&lt;/p&gt;
&lt;p&gt;The solution can actually be found in the documentation of the &lt;span class=&quot;code&quot;&gt;file&lt;/span&gt; plug-in. They introduced a new method to the &lt;span class=&quot;code&quot;&gt;FileEntry&lt;/span&gt; object to help with this problem.&lt;/p&gt;
&lt;p&gt;The new revised code looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	var ft =  new FileTransfer();
    ft.upload(fileEntry.toURL(), encodeURI(&quot;http://domain.com/uploads&quot;), onSuccess, onError, getFileUploadOptions(fileEntry.fullPath));	

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will notice that for the first argument to the upload method we now use the &lt;span class=&quot;code&quot;&gt;toURL()&lt;/span&gt; method of the &lt;span class=&quot;code&quot;&gt;FileEntry&lt;/span&gt;, everything else stays the same.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Refactoring the js code structure PhoneGap and Angular (Part 4).</title>
     <link href="http://blog.dynamicprogrammer.com/2014/05/06/refactoring-the-js-code-structure-part-4.html"/>
     <updated>2014-05-06T00:00:00-04:00</updated>
     <id>http://blog.dynamicprogrammer.com/2014/05/06/refactoring-the-js-code-structure-part-4</id>
     <content type="html">&lt;p&gt;&lt;i&gt;Code for this series is available on &lt;a href=&quot;https://github.com/hgarcia/dynamic-sports&quot;&gt;Github&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;The last time we created our first services, controllers and directives. We can see that if we keep adding controllers to the &lt;span class=&quot;code&quot;&gt;controller.js&lt;/span&gt; file things are going to get out of control soon.&lt;/p&gt;
&lt;p&gt;What we want is to create each entity on it&amp;#8217;s own file and concatenate all the js files together as part of the build process.&lt;br /&gt;
We are going to create a &lt;span class=&quot;code&quot;&gt;js&lt;/span&gt; folder under the root of the project and inside it three subfolders for each type of entity.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	js
	|-controllers
	|-directives
	|-services

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are going to move the files from &lt;span class=&quot;code&quot;&gt;www/js&lt;/span&gt; inside the new folders. The &lt;span class=&quot;code&quot;&gt;app.js&lt;/span&gt; file will be at the root of the new structure and the other files inside each folder.&lt;/p&gt;
&lt;p&gt;We will create a new &lt;span class=&quot;code&quot;&gt;home_controller.js&lt;/span&gt; file and copy the code for the &lt;span class=&quot;code&quot;&gt;HomeController&lt;/span&gt; to it.&lt;/p&gt;
&lt;p&gt;We will do the same for the service and diretcives.&lt;/p&gt;
&lt;h3&gt;Concatenation&lt;/h3&gt;
&lt;p&gt;We open the &lt;span class=&quot;code&quot;&gt;gulpfile.js&lt;/span&gt; file and we add a new task.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	gulp.task(&#39;scripts&#39;, function() {
	  gulp.src([&#39;./js/*.js&#39;, &#39;./js/**/*.js&#39;])
	    .pipe(concat(&#39;all.js&#39;))
	    .pipe(gulp.dest(&#39;./www/js/&#39;));
	});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will need to modify the paths object as well since the sass property doesn&amp;#8217;t make a lot of sense anymore.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	var paths = {
  		all: [&#39;./scss/**/*.scss&#39;, &#39;./js/*.js&#39;, &#39;./js/**/*.js&#39;]
	};

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we will add the new scripts task to the watch and default tasks.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	gulp.task(&#39;watch&#39;, function() {
	  gulp.watch(paths.all, [&#39;sass&#39;, &#39;scripts&#39;]);
	});

	gulp.task(&#39;default&#39;, [&#39;sass&#39;, &#39;scripts&#39;]);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will also need to modify the &lt;span class=&quot;code&quot;&gt;package.json&lt;/span&gt; file to call the default gulp task.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	&quot;ios&quot;: &quot;gulp &amp;amp;&amp;amp; ionic emulate ios&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modify our &lt;span class=&quot;code&quot;&gt;karma.config.js&lt;/span&gt; file for our test to load the new files.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

	files: [
    	&#39;lib/ionic/js/ionic.bundle.js&#39;,
      	&#39;lib/ionic/js/angular/angular-mocks.js&#39;,
      	&#39;../js/*.js&#39;,
      	&#39;../js/**/*.js&#39;,
      	&#39;../tests/**/*_spec.js&#39;
    ],

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we need to use the new concatenate file on our &lt;span class=&quot;code&quot;&gt;index.html&lt;/span&gt; and remove the script tags for the all files.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	
	&amp;lt;script src=&quot;js/all.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Next steps.&lt;/h3&gt;
&lt;p&gt;We want to send the information we recorded to a server for saving and processing, but we can only do that while connected to a network. We will also want to give the user the option to only upload files when the device is using a Wi-Fi connection.&lt;/p&gt;</content>
   </entry>
 

</feed>

