DPS911 Release 1

Introduction

This is my first release for David Humphrey’s DPS911 open source class. I am currently working on three projects: Popcorn Maker, Popcorn Maker FCP (the Mac version of the web-based Popcorn Maker), and TestSwarm for Popcorn. Below is a description of the bugs I am working on for each project. Although the patches submitted for Popcorn Maker are small, it took time to find the right area in the code to actually make the fix.

Here is a table quickly summarizing the work done for the first release. The blog posts for bug 67 and 157 explain the problem and the solution; this blog post talks about how I went about solving them, and what issues I ran into. This post also talks about the progress of the work being done for Popcorn Maker FCP and TestSwarm for iOS.

Summary of First Release for DPS911
Popcorn Maker
Bug Blog Post Tweet
Bug 67 Popcorn Maker – Bug 67 Tweet
Bug 157 Popcorn Maker – Bug 157 Tweet
Popcorn Maker FCP
Bug Blog Post Tweet
Custom DMGs for release builds Still working on it
TestSwarm for iOS
Play videos automatically Still working on it

Popcorn Maker

Bug 67

Bug 67 is on Lighthouse; the issue is that users are able to change a media source to nothing, which leaves the application in a broken state. The fix to this problem is explained in my blog post titled “Popcorn Maker – Bug 67.” That, however, was not my first solution before posting the patch up for review. My first solution involved editing two files, butter/src/core/media.js, and js/menu.js.

butter/src/core/media.js

  Object.defineProperty( this, "url", {
	get: function() {
	  return url;
	},
	set: function( val ) {
	  if ( val && val.length > 0 && !/^\s*$/.test( val ) && val !== url ) {
		url = val;
		em.dispatch( "mediacontentchanged", that );
	  }
	}
  });

js/menu.js

  buttonManager.add( "change-url", $( ".change-url-btn" ), {
	click: function() {
	  var newUrl = $('#url').val();
	  popupManager.hidePopups();
	  if ( !newUrl || newUrl.length === 0 || /^\s*$/.test( newUrl ) ) {
		popupManager.showPopup( "load-failed" );
		return;
	  }
	  if ( newUrl !== butter.currentMedia.url ) {
		butter.currentMedia.url = newUrl;
		$(".media-title-div").html( newUrl );
		pm.toggleLoadingScreen( true );
		function changeComplete( media ) {
		  pm.toggleLoadingScreen( false );
		  butter.unlisten( "mediacontentchangecomplete", changeComplete );
		}
		butter.listen( "mediacontentchangecomplete", changeComplete );
	  } //if
	} //click
  }); //change-url-btn

This code is also up on pastebin.

There is the same check going on here in two different places:

if ( !newUrl || newUrl.length === 0 || /^\s*$/.test( newUrl )

This is checking to make sure that the new URL is defined, is not empty, and is not a string full of spaces. Based on the title of the ticket, which reads, “Don’t let a user commit a blank field for timeline media change,” it sounds like changing the URL source to nothing should be handled in the core of Butter. That’s why there is a check in the butter/src/core/media.js: if it is not allowed to be blank, then having this check will protect any function that sets the currentMedia.url property of an instance of Butter to an empty string. If it’s handled there, then why do the same check again in js/menu.js? The reason for the check on the UI side is to give an appropriate error message, and to stop from making a call that we know will not work. It’s similar to a client application calling a web service: the web service has its own checks that it does server side. Often, however, these checks are also done on the client side to avoid making round trips and putting a greater load on the server if it’s known ahead of time that what’s being entered is invalid. For example, submitting a blank field when asked to enter your credit card number will not be accepted on the server side. So to save the server some work, and for quicker feedback to the user, a check is made client side and the user is presented with an appropriate error message saying that the credit card field is blank. The same type of logic applies to this first solution: why make Butter do work trying to change the media’s source when it’s known what’s being sent as the new media source is invalid?

After completing this, I decided to talk to Bobby–secretrobotron–on IRC to get his thoughts on having the checks in both places, and perhaps turning this check into a utility method in js/utils.js. He told me that the URL may need to be blank for the base player to work. This is exactly what David Seifried told me when I discussed the patch with him. Bobby also said that Butter should throw the error, and Popcorn Maker should react to it. I then started looking for a way to dispatch this specific error so that when Popcorn Maker reacts to it, it will know that the exact reason changing the media failed was because what was entered was either nothing, an empty string, or a string full of spaces. But then I remembered that I read an error dispatched in the code, previewerfail, when working on bug 157. That’s when I added in the listener for “previewerfail” in js/menu.js, which lead to the patch that is currently in review.

Bug 157

The fix for bug 157 is explained here. Although this fix is even smaller than bug 67, it actually took longer. I ended up spending many hours looking at the wrong areas of the code (which was not a waste of time, it helped me understand the code base better, and made finding the area to fix bug 67 easier). At first I was looking for all areas that could possibly manipulate some sort of id property for a track. After some digging, I finally found a line of code in js/timeline.js that may have been causing the issue:

  layerDiv.id = "layer-" + track.id;

So then I put a breakpoint there, examined the call stack, stepped through the code, and started digging around. After some time, I eventually wound up in butter/external/trackLiner/trackLiner.js. I found a clear function, and noticed that it never said track.id to 0. So I added that in, but when I tested the code it still didn’t work. I found it a bit odd; maybe a track is being passed around to different functions, and one of those is somehow manipulating the id in the wrong way. So I added more breakpoints and spent quite a bit of time pursuing this idea. Once I realized that wasn’t working, I thought maybe something was being done to the JSON blob that represents the project when it’s either imported or exported. I had watch expressions set up for a few variables that I thought could be the issue as I was stepping through the code.

I eventually noticed that this issue doesn’t actually occur when first loading Popcorn Maker. If Popcorn Maker is loaded for the first time in the browser, and a previous project is opened, everything works just fine. This issue only happens if a project is saved first, and the loaded again. So there was a variable somewhere that was sticking around and not being reset back to 0. I finally found butter/src/core/track.js. The id of the track is set by incrementing a static variable, Track.guid. I used grep to find other areas in Popcorn Maker where this was used. It wasn’t used any other place other than when first initialized to 0, and when being using to set the id of the track. I finally found the problem. While looking through the code I knew that Butter had a clearProject function in butter/src/butter-main.js. I set Track.guid to 0 in that function, and everything worked as expected.

Popcorn Maker FCP

For Popcorn Maker FCP, I have been working on creating a custom DMG file that people will be able to download. I want to create a drag-and-drop installation, which is common with Mac programs. The DMG will have two icons: one for Popcorn Maker FCP, and one for the Applications folder on the Mac. Once the DMG is opened, the user can drag Popcorn Maker FCP’s icon into the icon representing the Applications folder, which is a symbolic link to /Applications. I also need this to work with the updating mechanism in place for Popcorn Maker FCP, which uses the popular Sparkle framework.

It turns out creating DMGs with custom images is not straightforward. There are commercial applications out there that allow the developers to use a native Mac application that makes creating these DMGs manually really easy. But that method is too slow; I want to add in a build script for the release builds so that these DMGs are creating automatically. Therefore, I needed a way to create a custom DMG using the command line. I found some information on StackOverflow, which gave me a start. Now I know hdiutil is responsible for creating DMGs, but it seems as though there are many quirks. I then started looking for a tool that wraps hdiutil and simplifies the interface. After searching around, I stumbled on the create-dmg project. I set this up and started playing with the settings, following the example posted here. But I couldn’t get it to work. No matter what image I told it to use as a custom background, it always created a plain DMG. So I started to search again, and found another project, ChocTop (which also supports the Sparkle framework!). I have spent quite a bit of time actually trying to get the tool to work without throwing an error. I have been installing the ruby gems it needs, but I keep getting this error when I try to use the install_choctop command:

/usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/gems/1.9.1/gems/rubigen-1.5.7/lib/rubigen/options.rb:31:in `default_options': undefined method `write_inheritable_attribute' for RubiGen::Base:Class (NoMethodError)
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/gems/1.9.1/gems/rubigen-1.5.7/lib/rubigen/base.rb:84:in `'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/gems/1.9.1/gems/rubigen-1.5.7/lib/rubigen/base.rb:79:in `'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/gems/1.9.1/gems/rubigen-1.5.7/lib/rubigen/base.rb:42:in `'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/gems/1.9.1/gems/rubigen-1.5.7/lib/rubigen.rb:10:in `'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/1.9.1/rubygems/custom_require.rb:59:in `require'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/1.9.1/rubygems/custom_require.rb:59:in `rescue in require'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/1.9.1/rubygems/custom_require.rb:35:in `require'
	from /usr/local/Cellar/ruby/1.9.3-p0/lib/ruby/gems/1.9.1/gems/choctop-0.14.1/bin/install_choctop:4:in `'
	from /usr/local/Cellar/ruby/1.9.3-p0/bin/install_choctop:19:in `load'
	from /usr/local/Cellar/ruby/1.9.3-p0/bin/install_choctop:19:in `'

I hope to get this working as it would make releasing the application much easier, and the resulting custom DMG will look much more professional.

TestSwarm for Popcorn

David Seifried is working on TestSwarm for Popcorn. I am going to work on getting this to work on iOS: we need a way to let the videos needed for Popcorn’s test cases to play automatically, without someone having to sit there and hit the play button. I haven’t been able to do too much on actually figuring that issue out, we are still working on me actually being able to see the web page on the iOS simulator. I keep getting this error:

TestSwarm iOSError

Conclusion

I have learned really interesting things while working on my first release. It’s nice to work on different projects so that I can keep up to date with more than one technology. Working on multiple projects has also come in really useful when I am unable to work on a particular bug. For example, when I was not able to work on TestSwarm for iOS, I went back to trying to get ChocTop to work for Popcorn Maker FCP.

What I Learned for Future Releases

  • I learned that I need to blog about my process and the problems I run into when working on the bugs assigned to me. It helps others know my current progress, and someone who reads it may end up helping me solve the issue. Having these blog posts will also help make my release blog post more organized and concise; it will avoid having to talk about the process of fixing the bugs in a release post. Additionally, it’s better to blog about the process while I am actually in the middle of it, because that’s when it is fresh in my mind; some of it forgotten when I blog about it after the fact.
  • I need to use IRC more. I did make use of IRC, but I may have been able to solve my bugs faster if I asked questions earlier on. There are others who have worked with the code and already understand it; they can help me focus in on the area I need to be looking at.
  • Get more involved with Twitter. I tweet about my blog post once I publish it, but I can tweet about my progress as I am working on the bugs. Sometimes progress is small and isn’t enough for a blog post, that’s when tweeting will come in useful.
  • About these ads
This entry was posted in Open Source. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s