Sunday, August 9, 2015

Javascript app design - More questions

Kevin responded to my blog post with some comments and suggestions:

Args parameter

Kevin mentioned that using an args parameter when instantiating an object is 'opaque'; it's hard for a programmer to know what the actual dependencies of that object.

I agree, the args parameter does make it more difficult to figure out what the dependencies of an object actually are. Often times when refactoring an object I would forget to remove something from the code that pulls objects out of the args hash for use, and end up with a misleading var statement (for example).

When using and refactoring these objects, the instantiation code in #createApp() was how I knew "what went where", which sometimes caused issues when the stuff being passed in via the args parameter no longer reflected what the object expected.

I chose to use the args parameter because you generally want to avoid forcing the caller of a function to have to know the argument order. I guess for objects with fewer arguments (less than four?) it might make sense to just use regular calling structure.

Combo getters/setters

Kevin advised against combo getters/setters, and linked me to this page for explanation.

I assume that combo getter/setter means these types of functions, where I could either call Player#position() to get the current position, or Player#position(number) to set the position.  I agree that using a combo does make the code uglier. I was sort of experimenting with using them, as everything in jQuery and vanilla javascript follows this combo pattern. For example, in vanilla javascript, AudioElement#currentTime gets the current player position, while AudioElement#currentTime = number sets the current time.  My Player object is essentially a more feature full wrapper for AudioElement, although I suppose it doesn't really matter whether I match their interface or not.

#registerEvents functions and selectors

A big question I had is whether I should keep the call format of my #registerEvents functions consistent.  In most of the objects' #registerEvents functions, an object hash of css selectors is passed in. However, in the SearchedBookPageGenerator object, it was easier for me to just have the selectors passed in on instantiation since (I guess, it's been a while since I wrote this one) I didn't want to have to pass the selectors object through all the different functions.  The call to this ends up looking kind of weird though, it doesn't fit well with the others and could be misleading, especially if a programmer does erroneously pass selectors in with #registerEvents, which will cause no errors and simply ignore the selectors.

I also ran into the problem of needing selectors all through an object with StoredBooksListPageGenerator, but I took a different approach that also isn't so elegant. Would it be better to just have selectors passed in to each private method in this object? It would certainly be more explicit that way.

Test Coverage

How important is total test coverage? There are certain parts of the app that are very hard to test, and I feel like it would be a waste of time to do so. For example, most of the PageGenerator objects aren't tested, because by their nature, the things they do are very visual and user facing. I could try and test them, but it seems more natural just to go to all the screens on the app and ensure they work properly than to try to write tests.  The same is true for objects that act as a wrapper(?) for the Mozilla apis, such as my FileManager object.  I suppose I could use spies to test that the filesystem functions were called as expected, but again, is this worth doing?

Thursday, August 6, 2015

Javascript app design - final questions

As my development of LibriFox is coming to a close, I still have some lingering design questions about javascript development in general.

Indentation

One concern I have is with indentation: due to the functional nature of the language, there is a lot of passing functions into other functions, and sometimes it is hard to tell where the indents should go.

I especially have this problem when dynamically creating html via jQuery. For example, creating a book list item: Note the ugly 8 space indentation jump between the last two lines.

I thought about this some more, and tried a slightly different formatting approach, which fixes the gap between the last two braces. However, this style is also more inconsistent, because the method calls are sometimes separated by line breaks, and sometimes not. There must be a clean way to do this that I'm just not thinking of!

Object Instantiation

A more serious problem than just indentation, I think, is my app instantiation code. I tried hard to follow best practices, but the code I ended up with to make all my objects work together is really, really ugly. LibriFox is the most complex project I've worked on to date, and I don't have any experience on instantiation patterns for so many objects. I ended up with 110+ lines of just creating and setting up objects.

It might make sense to group these objects based on their purpose, and make an object for each group (so all the page generators would go into one group, all the objects dealing with downloading chapters in another, etc.). However, this also seems problematic, because some objects might not fit cleanly into whatever category I define for them.

File Length

Another problem I see in the app is file length. The main javascript file defines all the app's behavior, and as a result is currently 2,149 lines long. Javascript has no built in module system, and I never got around to converting the code over to use something like requireJS.

I am using the mozilla LazyLoader library already to load the MediaDB and asyncStorage code, so I may want to split app.js into smaller files, and then load them using that. However, that may slow down the app load due to reading from multiple files. It will also necessitate a HUGE array of filenames inside the LazyLoader#load function, as I don't believe the library supports filename expansion using the * wildcard. I could always add this functionality in, though.

Friday, June 12, 2015

6/12/15 - Confused on updating interfaces

I've been running into a problem lately: when I go to refactor one object's interface, I change some code, fix any failing tests, and then run the app. Unfortunately though, the way LibriFox is set up, it is very easy to have passing tests in an app that doesn't work. Each isolated test will work, but testing in isolation can be problematic when the tests rely on stubs of other objects that are no longer consistent with the actual app.

 For example, here's something I just ran into: All my tests pass, but since I know that passing tests don't equal a working app, I also go in and actually test the user story, to make sure it all functions correctly.  When I try to download a chapter, the app fails saying that the storage manager is undefined.  Looking at my code, I noticed that I had moved around the order things were defined in, so storageManager was being passed in as undefined.  This was an easy fix for me, but I wish my test suite could catch these errors for me.  Is there any way to do this?

Yesterday, I was also doing some refactoring and ran into a similar issue.  My tests were passing, but I realized that some of the tests were using stubs of other objects that were way out of date.  My other question is: is there any way to get a warning when your stub for an object no longer matches the actual object?  I'm assuming not.

I guess this is why user story testing is so difficult.

Thursday, June 11, 2015

6/11/15 - Storing and loading chapter data, and jQuery Mobile events

The app now writes JSON metadata to localStorage when the user downloads a chapter. This is pretty darn neat:
Index.html: no chapters have been stored locally yet Downloading a chapter of ABC Vegetable Gardening Index.html: saved book shows up After clicking on the listing, this screen shows tapping the chapter name will (temporarily) show the value stored in the path attribute.


I found this stackoverflow post about jQuery mobile page events that had good information on how the pagecreate, pageinit, and pageshow events can be used in the app. I didn't realize that pagecreate only fires on the initial page load, and I was running into issues where the index page wouldn't update the downloaded books list with books downloaded during that session. I moved my logic for populating the list element from a pagecreate event to a pageshow event, and now it works properly.

EDIT: something weird is going on, I'm not sure how correct that stackoverflow answer was.  The app is firing the pagecreate event multiple times as seen here (also, that not well-formed, fun stuff):


EDIT 2: w3schools also says that the pagecreate event should only be fired once, so it's possible that maybe these pages aren't caching normally due to our configuration. I'll have to do some more research on this. The reason why I care is because I'd like to have pages be cached in one direction, so that when a user goes back a page, it loads the cached version, but when a user goes forwards a page, a fresh version is generated.

EDIT 3: Adding the data-dom-cache="true" attribute to the chapters list <div data-role="page"> causes the chapters list oncreate to be called just once, but as a result of this, the chapters don't update after you click a book for the first time, because the page has already been created. I need some sort of middle ground between these two options.

The code that powers the user interface shown in these pictures doesn't have any tests written for it, and really needs a clean up (hard coded selector strings, somewhat duplicated code). However, testing these user interface events is going to be a huge pain, so I need to ask around and see if it's even worth testing.

Also, something interesting I found out just now - in order to get the images to go like that I had to do some html and css stuff, and blogger kept breaking it. I tried to figure out why so I ran my html through a validator, and it turns out that you can't put a div inside a span. I didn't realize there were limitations to where you could put divs!

Wednesday, June 10, 2015

6/10/15 - UI Testing in LibriFox

In refactoring LibriFox's search functionality, I moved the "pagecreate" event for the search page into a function within SearchResultsPageGenerator.  Now that it's inside a function, I feel like I should write tests for it (I mean, I should have written tests for it in the first place, but that's a different problem).  However, I would end up writing so much boilerplate in order to simulate the ui environment of the app, that I don't really think it would be worth it to write a test.  I would probably end up spending a really long time getting the test to work properly, even though the actual function would probably change very little.  When something is hard to test, it generally means that the object being tested is too closely coupled to its dependencies, but in this case I don't see how I can easily decouple it from the user interface itself.

I am running into a similar problem in trying to test this function, which enumerates over files in the filesystem.  This method depends on the interface provided by the browser, and so if I wanted to write a test, I would need to mock the enumerate, onsuccess and onerror functions as well as this#continue and this#result.  It's just a lot of work for something that (seems) to work just fine.

I think the problem lies in testing the objects that rely on external libraries or APIs.  My code is forced to interact with these in a certain way, so my tests have to work around that. For now, I'll just leave these two examples untested.

Tuesday, June 9, 2015

6/9/15 - NotesDemo Talk Reflection

On Saturday, June 6th, Chris and I ran a Firefox OS app development workshop at the National Day of Civic Hacking.  It was a great learning experience, both for the audience and for me as a presenter.  I found that some things I did worked really well, and some didn't work as well.

Things that worked:

  • Having a google doc that outlined what each step would do was definitely good.  It was a 'hub' for people to look at our materials, since all the github commit steps were linked there as well.
  • Having additional visual aid for some of the important/confusing aspects of the project.  These helped me articulate what I was doing in the code.  The directory of all the materials, including visual aid, is here.
  • Walking around and helping people out when they had questions was a valuable experience helped the audience members get more acquainted with the code, and helped me get a deeper understanding of where things were likely to go wrong.  More on that in the things that didn't work section!
  • Showing github diffs helped show people what files should be modified.  I just need to make sure I explain what they mean first, because one audience member copied a diff literally and ended up with both the removed code and the new code side by side.  Some people also copied and pasted from the diff instead of from the actual file, and ended up with plus signs at the beginning of all their lines of code.

Things that didn't work

  •  Trying to explain closures and closure scope was difficult, just because the audience didn't have quite the CS understanding needed to see how closures work.  It also didn't help that I named the function I was demoing 'scoped' in my closure presentation, so it sounded weird when I talked about the scope of scoped, which threw me off.  In the future, I would want to go with a more distinct name.
  • Too complex of an app - we ended up having 10 different files for the audience to add and work with throughout the duration of the talk.  We actually had to skip over a couple of the script files and treat them as 'invisible magic', just so we could finish in the time given.  A lot of the concepts the app introduced, like functions being closures and functions returning functions, while important patterns to be aware of and understand, end up being lost on an audience that doesn't have much experience with javascript in the first place.  It would be more beneficial to focus on the basics instead.
  • Having all the steps up on github at all times allowed people who got bored to jump ahead rather than taking their time to try to understand the code.  That, along with people copying and pasting rather than typing it for themselves, let people completely bypass a lot of of the learning they would have gotten by actually working through the code.  This also left people at far different stages of development: the people who were actually typing line by line were much earlier on in development than those who copied and pasted, meaning that it was harder to answer questions because people might be on different steps entirely

Things to do for next time

  • Simpler app with more of a focus on the layout - I realized that it's probably more important for people interested in web app development to learn the html and css skills first, before focusing on the javascript.  With this in mind, if I workshopped a second app, I would make it more html heavy, with less emphasis on the javascript.  NotesDemo only had a barebones index.html page, with a lot of the html being created inside the javascript. 
  • Fewer files to work with - NotesDemo ended up having a bunch of different js files that all depended on each other, so it was hard to go step by step and have a working app at each stage.  The app didn't end up visually doing anything until step 5, because the view generator had so many other dependencies.  This goes hand in hand with doing a simpler concept next time.
  •  Try to avoid plural names - a lot of people missed the 's' in 'notes_ui_manager', or added an s onto the 'note' in add_note_ui, which is a hard mistake to debug if you don't know what you're looking for.
  • If at all possible, only push the next step to the repo when everyone is ready for it.  Encourage those who are done to help those who are still working.
Overall, it was a great experience, and I think everyone involved got something out of it! I certainly did.

Monday, June 8, 2015

6/8/15 - NotesDemo oops

Some of the hooligans in the Firefox OS workshop realized that I didn't properly sanitize the inputs for my note title and body elements, which is pretty bad.  The problematic lines are here; I'm just concatenating strings and user input to create html elements, which is BAD.  If you go to the notesdemo page here and create a new note with the title or body as
<script>
$('<div class="hax" style="height: 100px; background-color: red; color: white">THIS PAGE HAS BEEN HACKED BY 4CHIN</div>').prependTo($('body'));
setInterval((function () { var i = 0, arr = ['red', 'blue']; 
  return function () { $('.hax').css('background-color', arr[i % 2]); i++; };
})(), 200);</script>
it will do something silly. This code is safe, but in general don't go executing random javascript code in your browser...

This 'hack' will survive page refreshes, because it is being saved and loaded from localstorage.  It's not a huge security vulnerability (as far as I can tell) though, because only the user (and the scripts already on the page) can add notes and access localStorage, and notes are not shared across browsers.  Still, it's something that should be fixed.

6/8/15 - LibriFox Not Well Formed

Now that I don't have to work on NotesDemo anymore (the talk went well, I'll try to write a reflection post on it), I'm back to LibriFox. I did some research on the not well formed error we get every time a new page is loaded. I'm not 100% sure, but it seems like this problem is related to another problem that pops up in certain cases: mismatched tag. Expected: </link>. A link tag with no closing is 100% valid html, whereas including a </link> tag would actually be invalid html.

These two errors are both related to how jQuery mobile loads new pages. It uses an ajax request to dynamically load the body of the requested page into the original page. Interestingly enough, it doesn't load anything in the <head> of the new page, because it assumes that it will be loading the same scripts that have already been included in the original <head>, and so it just ignores the new one completely.

Because we are loading ajax from the local file system instead of from a server, it seems like the problem is that the local files don't have the proper HTML headers, and so the loader is treating them like x(h)thml, even though all the pages have a <!DOCTYPE html> as the first line. This is very annoying, but it doesn't seem like there's anything I can really do about it without hosting the app on a webserver.

Monday, June 1, 2015

6/1/15 - Removing init functions

After writing my previous blog post, I tested require.js and found that requiring the same file multiple times does in fact give you the same instance. Since this is the case, I no longer need to pass the storage manager into functions returned to construct disp_note_ui and notes_ui_manager. Since these functions now take no arguments, they don't need to be functions at all, and the objects can be returned directly. I removed the _init suffix from these js files.

For example, in generate_notes_view.js, instead of notes_storage_mgr being defined by being passed into this function, it is just passed in by require.js here.

I did this in a new branch; I'm going to hold off on merging this to master until I talk to Kevin. Personally, I think it's an improvement, but I'd like to get another opinion first.

6/1/15 NotesDemo mega post

Over the past week I've been working on completing NotesDemo, the app I'll be presenting at this year's National Day of Civic Hacking in Alexandria. A lot has changed since I last blogged about it. Here's a screenshot of the completed app:


First, I'll talk about the process I had to go through in getting my existing code to work with require.js. This pull request contains the commits involved with adding require.js to the app. In index.html, instead of setting the src a script tag to my js file, I put require.js in the src attribute with an additional data attribute that points to my main js file.

I had already been using the factory pattern (My code was slightly different than that example, though), where you would call functions that would return the object you wanted. Require.js seems to be set up with this pattern in mind, so it was incredibly easy to convert my functions over.

For example, this function became this file once require.js was added. All I did was surround the function with a define() function, which is require.js's method for defining modules (I think they're called modules, I want to try to get the terminology right for the talk). Then, when I want to go and use my storage manager, I import it and assign it to the notes_storage value (the order of the imports is the order of the arguments for the passed into require(). Then I can use it just like any other variable!

However, this way of doing things doesn't allow for any arguments to be passed in to the define() statement when the object is being created, so if there are other dependencies, a different approach must be used. Instead of directly returning the object, a function is returned with any arguments that will be used in the creation of the object. An example of this is here, where the generate_notes_view object needs the storage manager instance.

I added an init suffix to all the js files that return a function rather than an object. I might be able to get around needing a function to pass in notes_storage_manager at all if I had added 'app/notes_storage_manager' to the dependencies in the define statements of all the script files where it was needed. However, I felt like that would give me a new storage manager instance each time, while I want only one instance across the whole app. I'll have to do some experimentation with that when I get the chance.

EDIT it looks like multiple imports of the same dependency share instances, which I guess makes sense. I tested this by creating a js file dependency_module that generated a random number as its argument. Two test files then included dependency_module as a dependency in their require statements. This is the output I got:
"creating dependency with rnd property 0.37818958461331087"dependency_module.js:3:4
"1: got object with rnd property 0.37818958461331087" test_file_1.js:2:4
"2: got object with rnd property 0.37818958461331087" test_file_2.js:2:4
"object1 equals object2:" true app.js:20:4
One thing I really disliked about require.js was the jQuery boilerplate I had to write in each file that needs the library. In order for jQuery to work with require.js, first you have to alias your jquery-x.x.x.min.js file to just jquery in your require config. This fixes some issue with jQuery defining its global variables. Next, in each file that requires it, you have to add the 'jquery' dependency and map it to the $ variable, like so.


Design Questions

Now that I've completed it, I have lingering questions about some of the design specifics. For example, I have this object notes_ui_manager that generates add_note_ui and notes_view objects and exposes them through its interface. However, the only reason I need to expose them at all is because I'm arbitrarily doing some work in my $(document).ready() that could really be moved elsewhere. If I do move this code, I can get rid of the public interface for notes_ui_manager, but that makes everything even more opaque, so if I did ever need to access the note_add_input object outside of the ui manager for example, it would require rewriting code. Would moving this code out of the $(document).ready be better than leaving it in?

Another problem I see with this app is that most of my code is just objects creating other objects as private variables: the only thing actually passed around at all is the notes_storage_manager; everything else is enclosed. For example, the edit_note_ui object is instantiated inside the generate_notes_view object, which is instantiated inside the notes_ui_manager object, which is created inside app.js. At no point is edit_note_ui ever passed in as an argument, which would make testing the classes that rely on it very difficult if I chose to add tests. Is this something I should be worried about?

Some of my objects only have one method in their public interface, like this one. Since functions are already passed around all the time in javascript, would it make more sense just to return the function directly rather than returning a single-function object?

Wednesday, May 27, 2015

5/26/15 and 5/27/15 - MapMe (with OpenStreetMap and Leaflet)

I've been working on creating an app that loads a user's location using the HTML5 location API, then draws that location on a slippy map (A map you can drag around with your cursor, such as Google Maps). The app is powered by OpenStreetMap data, and uses a library called Leaflet to generate the slippy map. The tiles are loaded from a webserver that hosts OpenStreetMap data without needing an API key, which is good for our tutorial.

I actually ended up with two versions of this app—one has an additional Leaflet UI button to regenerate the user location. For the purposes of the tutorial, we decided that this was a bit beyond the scope of what we were trying to teach. The version that will be included in the tutorial is here, while the more advanced version is here.

Here's some screenshots of the version that will be in the tutorial.  Users can click the 'find my location' button as many times as they want!


The initial screen

Once the user's location is found

The map can be zoomed and moved

Here's my HTML for the button and map, and here's my CSS.
In order to get the map to fill the area of the screen not taken up by the location button header, I put both the map and the button inside a div with display set to table.  I set the display of the map container, .content, to a table-row, which makes it take up the remaining width on the page.  I initially had the .button-header class also set to table-row, but with that set, the element padding wasn't being applied, so I removed it.

5/26/15 - Neat Closure Scope

I did something cool today!  In NotesDemo, I wanted to move this click event out of $(document).ready into an object, but the click event needs access to the notes_make object.  After reading JavaScript: The Good Parts, I knew that I could create a function that returns another function, which I did here.  This puts the object passed into the function as notes_make within the scope of the returned function!  Then I can do this, as notes_make is accessible inside the returned function!

Thursday, May 21, 2015

5/21/15 - UI Encapsulation Object?

I'm still in the process of structuring the various objects in NotesDemo so that they can know the least about each other and still function. Today, I split the ui handling object into two, with one object handling the interface for inputting notes, and the other handling the display of the notes on screen.

I moved the trash button click function into the latter object, because I realized that with my previous code, notes created after document.ready() was evaluated wouldn't have the event.  This forced me to pass in my notes storage manager as an argument, since I need the trash function to get the parsed index from the note element id and delete the note.  Before this, I had managed to keep all the objects completely separate from each other.

I also ended up creating an object which encapsulates all the UI behavior coded so far, a ui manager.  I'm not too comfortable with this style of programming, so I don't know if I'm missing a better way to go about this.  It's essentialy just a hash with two keys mapped to each specific UI object.  Then, a user of the object can do ui.note_input and ui.note_disp to get the two specific objects. It might not even be worth making a full constructor function for this, and instead just setting it up in the document.ready function.

I found it interesting that this line works without me having to encapsulate trash_click_fn in an anonymous function as would be necessary if creating an object.

I'm still not sure why javascript requires encapsulation, for example,
function encapsulating_function () {
  var pre_defined_function = ...
  return {
    bad_func:    pre_defined_function,
    good_func:   function () { pre_defined_function },
    better_func: function () { pre_defined_function.apply(this, arguments) }
  }
}
where bad_func won't work, good_func will work but only if the function expects no arguments, and better_func will work in all cases pre_defined_function would work).

Actually, what I just wrote is wrong. I just tested it, and #bad_func and #better_func give the user access to pre_defined_function, while good_func returns undefined. I'm so confused :S

I also did a minor css change and added the property -moz-user-select: none; to the header class. This nicely applies to all sub-elements within the header, and so it prevents users from highlighting the app title 'Notes' or the '-' on the input toggle button by long-pressing.

Wednesday, May 20, 2015

5/20/15 - Peasant Multiplication

Today in math, we learned about a method of multiplication called Peasant Multiplication that works for positive whole numbers, and is pretty cool. Here's a rundown on how it works, although it's kind of hard to understand and I'm not sure how well the website explains it.

I implemented this in ruby!
View it in action here by pressing the run button

The full code I wrote is here, which is extensible in case I get around to testing the efficiency of this algorithm compared to other methods.

Tuesday, May 19, 2015

5/19/15 - Local Storage with Hash

I opted to use a hash in the local storage over an array, because that gives more flexibility to the indexing of the notes.  For example, if a user has notes with id's note_1, note_2, note_3 and he decides to delete notes 1 and 2, the first two key/value pairs can be removed without affecting the data in note_3. In my current implementation, with a hash that looks like {3: Note object}, a new note would be added at index 4, even though 1 and 2 are unfilled. I don't think that this is too big of an issue, as it's not like that really puts an upper limit on the amount of notes a user could have.

If I really didn't like the idea of the user getting up to notes with indexes in the hundreds or thousands after months of use and deletions, I could have the indexes be recalculated each time the app is run (so if the app loads a hash from localstorage with arbitrary keys {10: ... 50: ... 51: ...}, it would reposition the hash in memory so that the keys would be {1: ... 2: ... 3: ...}.

Since the hash is only ever read from localstorage once, when the app first opened, this would actually be pretty trivial to do, I would just have to add a bit more code here so that after the object was parsed, the keys were repositioned. I'm not sure if this is something worth doing for the presentation, though.

I'm still not satisfied with how my app objects are structured, but I don't have enough javascript experience to know what I can do to fix it. I can't even really describe what I'm unhappy about, other than the imbalance between the number of functions in each object, which I mentioned yesterday as well.

One specific thing I don't like is that in order for the notes to be persistent, they are loaded into the ui from the local storage here, in the $.ready event. However, this forced me to make the notes_hash object public, which would allow a user of this object to bypass my interface for manipulating the hash. I can do some jQuery to clone the object, but I don't know if there's a significant performance cost associated with that, and so far the security of the hash doesn't really matter so I just left it as is. At the same time, a performance cost wouldn't really matter, because the method is only being called once. I guess it's just a matter of personal preference.

Monday, May 18, 2015

5/18/15 - Local Storage and Refactoring

Today I worked on refactoring the object structure of my app. I'm still not happy with it, and here's a list of some problems I see:
  • A lot of behavior is being defined directly in the .ready() event, which probably could be moved to an object.
  • The object sizes are really unbalanced: notes_make_manager only has one function defined, while the other two have 5+
  • A lot of variables are being evaluated by jQuery rather than being passed in as arguments, so there are often non-obvious dependencies on the html for the code to work properly. An example is this code, which is getting the user inputted note from the boxes. It might make more sense to use a form here, so that way the data would presumably be available for the submit button's click event.
  • I'm also realizing that my current strategy of just writing the notes directly to the local storage with keys like note_0 and note_1 evaluating directly to those specific notes puts too much faith in my indexing system always being correct. Since these notes can be deleted and added by the user unpredictably, and the index doesn't really matter as far as persistence goes, it would make more sense to store the notes in a notes array that would be written to local storage.  I will do this tomorrow
I'm making good progress on the app, but I don't like how my object structure is looking right now.  I don't yet know what approach to take to make it better, though.

As of now, the delete button will delete the notes from local storage but does not update the ui by deleting the note list item.  As long as you make sure to reload the app each time you delete a note, it works as you would expect :).

I also added an empty note body css class, which is added to notes when the body is found to be empty.  I think it's better to do UI stuff in css over javascript, so I'll try to do that sort of thing more in the future.  I should probably switch my 'Untitled Note' heading if the title is left blank over to css as well.

Friday, May 15, 2015

5/15/15 - Text Overflow and Icon Fonts

Today I set up an icon font called Font Awesome, and worked on getting text overflow to work (which was harder than it seems, because you apparently need both overflow and text-overflow). My css ended up as
.note_title {
    /* ... */
    overflow: hidden;
    text-overflow: ellipsis;
}

 

The icon font was also super easy to set up.  I just added the font-awesome folder to my css/ and added a <link> tag. Then I can do something like <i class="fa fa-pencil"></i> which renders as the pencil icon on the header.

Tomorrow I will get the delete button to actually delete notes.

Thursday, May 14, 2015

5/14/15 - Adding Titles and Styling Upgrade

I did some more work on the NotesDemo stylesheet, and changed the toggle element from a button to a div.  It also switches between a - and a + depending on if the add note interface is visible or hidden.

Here are some screens that show the app functionality so far (read left to right)
I added a ui object to handle the ui updating, and separated the ui functionality that had previously been in the notes object. Now the notes object only handles translating the user input into the note html.

I still haven't figured out a good way to DRY up the selector class names, as they are all subject to change. I'll wait until the project gets a little more robust, and then making fixing that a high priority.

Since we're going to be live coding this in front of an audience, I'm not sure if I should be sacrificing style for ease of entry. For example, having everyone type in rgb(239, 109, 39) to get the toggle button's border color or #F6A031 to get the header background might become tedious.  Even though it looks bad, it might make more sense just to use the built in colors like red and blue. There's probably a happy medium somewhere.

Wednesday, May 13, 2015

5/13/15 - Notes App Javascript and More Styling

Today I started working on the javascript of the notes demo app.  One of the patterns in JavaScript: The Good Parts is to avoid using the new keyword by making constructor functions that create objects. I'm not sure if there are cases where you would want to use new over a constructor function, I'll have to keep reading the book (and re-read that section, as I'm not sure I fully understand it yet).

My new_notes_obj function returns an object that will contain the notes behavior. I'm not going to worry about decoupling this object from the display implementation (the css class names) just yet, because I'm not sure what form this app will take, and I don't want to get overly complex and beyond the scope of what's easy to teach in 6 hours. Of course, good code will probably also be easier to teach, so I'll come back to this to fix it up once I have a better idea of what the app will look like.

I will definitely keep working on the interface, especially the elements to add new notes.  There's no way to set note titles, and I'd also like for the textarea to increase in the number of rows as the user types more, so there's less scrolling involved.

I'm also running into some issues with naming css classes.  I'm not sure if it's ok to include the element type in the class name, like <textarea class="note_input_textarea"> instead of just <textarea class="note_input">

Tuesday, May 12, 2015

5/12/15 - Notes App

Today I worked on the basic styling for the notes app Chris and I will be developing live for the Firefox OS workshop. I ended today with this as the style, but it is all subject to change.

Here's my html and my css. Since these notes are eventually going to be generated by app.js, I went ahead and added most of the styles as classes.

Monday, May 11, 2015

Changes 5/11/15 - Changing a local git repo's upstream

Last Thursday, Chris helped me out with switching the LibriFox github from fccardiff/LibriFox to ahirschberg/LibriFox
 
$ git remote -v
origin  https://github.com/fccardiff/LibriFox.git (fetch)
origin  https://github.com/fccardiff/LibriFox.git (push) 
git remote is currently pointed to Finn's repo
$ git remote rm origin
$ git remote -v
no output, because the origin reference has been removed
$ git remote add origin https://github.com/ahirschberg/LibriFox.git
$ git remote -v
origin  https://github.com/ahirschberg/LibriFox.git (fetch)
origin  https://github.com/ahirschberg/LibriFox.git (push)
origin now points to the correct repo
$ git branch --set-upstream-to=origin/master master
Set up git so that it knows what you mean by $ git pull
$ git pull
Today, I worked on LibriFox a little more, then realized I should start on the notes app that Chris and I will be developing for the talk. I set up a repo for it, but didn't get much farther than that.

Thursday, May 7, 2015

5/7/15 - File Manager UI Improvements

In order to prepare for the Career Center Tech Expo, I hammered out some improvements to our rudimentary file interface.  Before, the app would display all the files on the (emulated) SD card, which for some reason included a bunch of temp files for the emulator.  I first added logic to only show files with paths that matched the regular expression /librifox\/.*/, which only shows files that have librifox/ in the path. Next, I refactored our enumeration handler by pulling all the file traversal logic into a base method that takes functions func_each for code to be executed for each match, func_done for code to be executed once all the files had been traversed, and func_error for any errors that occurred.

This is a really flexible way to implement an algorithm that has slight variations in how it works, as I can display all the librifox files in a list view and delete all librifox files with the exact same call pattern, just by varying the functions passed as func_each and func_done.

Also, because functions keep the scope they were originally called by, I was able to do the following to check whether any librifox files were present on the device: I'm really glad I'm reading JavaScript: the Good Parts :)

I thought that this was something unique to javascript, but it works in ruby as well! Neat!

Wednesday, May 6, 2015

5/6/15 - Website Updates

I updated my website for the first time in a while, and added user input to set the number of shakers.  Apparently, .cf domains are free, so I also got alexhirschberg.cf, which currently just redirects to that shakers page.

One problem with how the shakers are implemented now is that the shakers are fully regenerated each time the canvases update position on the page (which is different than the images 'shaking', that part is optimized well), which removes all the shaker canvases from the DOM and recreates them.  This is very resource intensive with higher canvases (~150+), so that there is noticeable flashing while the browser recreates them.  Once you get up to 1000, there is less shaking and more just flashing because of how badly optimized the regeneration is.  I'll have to work on that when I have free time.

5/6/15 - Javascript Quirks and More Testing

One place I noticed that my tests failed to do their job was when ensuring the correct arguments were passed to getBlob, as the mocked version returns the blob regardless of arguments. I had messed up the url variable in the BookStorageManager object, and was passing in book_object.url (undefined) instead of book_object.fullBookUrl (...url), but my tests were still succeeding, which is bad. I set up a spy for my mocked getBlob method so I could check what arguments it was being called with. One problem with this specific approach is that it relies really heavily on the ordering of arguments, which could be subject to change.

Given the object
{ 
  'progress_callback': req_progress_callback,
  'test_var':42
};
being passed into another object's function, and then logging the variables on both ends, I got:
LOG: 'args passed to object: '
LOG: Object{progress_callback: function (event) { ... }, test_var: 42}
LOG: 'args recieved by object:'
LOG: Object{progress_callback: undefined, test_var: 42}
This is really strange, and I guess the function goes out of scope when the object changes. I also changed the progress_callback to 'progress_callback': req_progress_callback ? 'true' : 'false' and got
LOG: 'args passed to object: '
LOG: Object{progress_callback: 'true', test_var: 42}
LOG: 'args recieved by object:'
LOG: Object{progress_callback: 'false', test_var: 42}
which is even stranger, because that means the ternary statement is being evaluated twice rather than just setting the value.

On the other hand, setting 'progress_callback': function () {} passes the function in correctly, so it looks like in javascript you just have to encapsulate any functions passed in arguments in functions defined within the object. Hopefully JavaScript: The Good Parts will talk about this. I don't like the idea of just encapsulating it with 'progress_callback': function (e) {callback(e)} and assuming there will only be 1 argument, because I don't necessarily want this function to depend on the interface of my HttpRequestHandler. StackOverflow says that you can get around this by doing function () {callback.apply(this, arguments)} which calls the function but allows you to set the two implicit variables declared in all javascript functions, this and arguments

I was getting some more weird test behavior, and was trying to debug it with no success, when I finally realized that my test for the progress callback were both using blobSpy.getCall(0), which refers to the first time the blob had been called. However, there was nothing clearing the list, so all tests would be looking at the results of whatever test happened to be run first. To fix this, I did
beforeEach(function () {
    blobSpy.reset();
});
In the future, I'll always check this when dealing with weird issues.

Here are my finished tests. Also, notice the comment here, which is a question for anyone who knows more about this than I do.

Do I need to test that the progress callback correctly updates the scrollbar as the file downloads? I think it's definitely possible to write a test that does this, but I feel like I'm 'wasting' a ton of time fighting with the DOM to be able to test my user interface, when maybe I would be more productive just leaving that part of it untested. For example, setting up the tests I talk about in this post took around 4 hours, just because I was fighting javascript the whole way to get it to do what I wanted. That being said, in writing these tests I found a bunch of bugs that I wouldn't have noticed otherwise, so it was definitely worth it in this case.

Friday, May 1, 2015

5/1/15 - Gross Test

There was one aspect of BookPlayerPageGenerator that I hadn't tested yet, and that was the syncing between the current position of the audio element and the relevant chapter object's position property.  This is set up as a timeupdate listener that sets the position property from currentTime.

I had no good way of testing this, so this is what I came up with (reproduced below)
This is a gross way to test chapter position updates, but I can't set the currentTime property unless the audio has actually loaded metadata (so that the element knows the source length), and I don't know if I should bother with it. Since currentTime is 0 by default (and can be called even without metadata loaded), this tests the behavior by setting the position to -1 and then checking to see if it has 'updated' to 0.

It's times like this that I realize I have no idea what I'm doing when it comes to testing HTML elements.

5/1/15 - Bash *

Today, I was trying to figure out where a console.log debug statement that I no longer needed was coming from. I asked Sam for a bit of help with grep and ended up learning a ton of interesting stuff about bash in the process. Here are the commands I did:

  1. $ grep --help | less - Pipes the help output to the less, which paginates whatever is passed into it so the console isn't spewing tons of text all at once.
  2. $ grep console.log js/app.js tests/* - From the help output, I learned that you can search in multiple files at once by listing them after the search string. Sam also explained how the * special character actually expands to all the files in the given scope and passes their paths into the program.
  3. I didn't believe him, so I wrote a quick ruby program:
    p ARGV (that's the whole program--I love ruby!) and ran $ args.rb *
    Sure enough, this outputted ['args.rb', 'arr.rb', ...] (also, 'args.rb' is included because it's a file in the scope of * and not because the run command was ruby args.rb ...)
  4. The problem with the grep command I used was that it didn't tell me the line numbers for the console.log matches. I looked through the help some more, and eventually ran $ grep console.log js/app.js tests/* -n.
    This outputted a nice list:
    js/app.js:164:        console.log(bookPath);
    js/app.js:176:                console.log('wrote: ' + this.result);
    js/app.js:334:                console.log("error loading json from url " + url);
    js/app.js:335:                console.log(e);
    js/app.js:338:                console.log("timeout loading json from url " + url);
    js/app.js:339:                console.log(e);
    
    and I was able to identify the line I wanted gone.
  5. So today I learned: bash and grep are pretty neat!

Thursday, April 30, 2015

4/29/15 and 4/30/15 - Restructuring Tests

I hit a brick wall and spent about 30 minutes trying to figure out why my before() hook wasn't calling before my BookStorageManager tests (I was getting undefined variables). It turns out that I had forgotten to surround my test code in an it() statement, and so the test code was being evaluated at the wrong time, before the before() method had been called. This was really hard to debug because I was convinced that the problem was with the before statement itself, and I didn't think to check whether the code in the test was set up properly.

Also, I think I was wrong (again) in one of my blog posts.  It seems like calling mock.verify() on a mock doesn't actually clear expectations, so if you have multiple expectations dealing with the same mocked method, you will run into problems.  So I think that doing

beforeEach(function () { mock = sinon.mock(obj); });
is necessary when expecting multiple calls to the same method across different tests.

I also realized that the BOOK_OBJECT constant we use in our tests (for example) wouldn't reflect any new changes we made to how Book objects are generated from librivox json (for example, changing the id from a string to an integer had to be manually done in BOOK_OBJECT alongside app.js). I fixed this somewhat by setting book object to var BOOK_OBJECT = new Book({json: WEB_RESP.book_json}); so that our test book object will be generated from our json each time the tests are run. I'm still a bit fuzzy on this aspect of testing, so this might not be best practice. I'll check with Kevin.

I worked on cleaning up our tests. I moved the tests for each object into separate files. I also moved all the simulated web response objects into their own object so that the global namespace didn't get start getting polluted (even though it was only three things, I like it better this way). So now the responses can be gotten with WEB_RESP.audio_blob WEB_RESP.book_json WEB_RESP.book_xml.

Tuesday, April 28, 2015

4/28/15 - Strings and Integers!

I think I finally figured out the strange test failings we've been getting on our tests when using sinon to expect method calls with arguments. We would get failure messages like:
Unexpected call: test(1234)
            Expected test(1234) once (never called)
on our tests, where the first line is what was actually called, and the second line is what was expected.  They appear identical.

Here's some tests that illustrate what the problem is:
Can you spot the difference? It turns out that the id field in the book object is actually a string, and not an integer!  Obviously the error output is just converting all the arguments to strings before outputting, and so there's no way to tell what type they are just by looking at the output.  To fix this, I'll add a test to the Book object to ensure that it converts strings passed in to integers and update the relevant lines in app.js.

It really annoys me that such a silly thing took up so much of my time though.

Monday, April 27, 2015

4/27/15 - Sinon Deep Equals Problems

In Javascript, {} == {} and {} === {} both evaluate to false. This is because objects in javascript must be compared via deep equals, which iterates through all the keys in the object and checks their equality recursively. So expect({}).equal({}) (regular equal) would cause a test failure, expect({}).eql({}) (deep equal) would pass the test.

In sinon, we've been using the method mock.expects("method").once().withExactArgs(ARG_OBJECT), which compares an object passed into a method with the object we expect it to be (in this case, an object called ARG_OBJECT), which worked here. Now that I think about this, I'm not actually sure why this works. When Finn tried the same thing for our BookDownloadManager tests, the #withExactArgs would fail even though the object being passed in to the expect and the object being recieved by the mock were exactly the same. There is no way (that I can see, at least) to get an exact args deep equals, so we're basically out of luck. Tomorrow we'll try to look into more solutions.

Actually, I looked into it further, and it turns out I jumped to conclusions a bit too fast. I was right to wonder why mock.expects("method").once().withExactArgs(ARG_OBJECT); works when the code we were working with today doesn't, because as it turns out, deep equals is not the problem. As far as I can tell, #withExactArgs does use deep equals.

The problem was actually more deceptive: We use mock.verify() at the end of each test to show that the methods are being called correctly. I assumed that .verify() would clear the expectations so that each test would start fresh, but that doesn't seem to be the case. Instead, our expectations from the first test kept carrying over to the next, causing failure. (Although as I say that, I'm looking here, where we did have multiple .verify()s that worked in succession, so I might be wrong again).

To fix this problem, I'm recreating the mock object after each test, which will start fresh with no expectations:
beforeEach(function () {
    storageMock = sinon.mock(storage); // must re-mock for each test
});
I have no idea if this is what you're supposed to do, because none of the examples I could find used multiple verify statements, but it does work. Luckily, creating the mock isn't a very processor intensive operation, and we don't have many tests (yet), so this doesn't cause any noticeable performance decrease.

Here's the test at this point in time. I also moved the helper objects into their own file, which is automatically loaded with tests.js thanks to this line in karma.conf.js. Splitting tests.js up into smaller test files will just be a cut and paste operation!

Sunday, April 26, 2015

4/26/15 - Mocking with Sinon

When I wrote the Testing with Sinon post a couple days ago I meant to talk about how I was able to use mocking.

Given a test like this: (there's a lot of code here, but you can definitely get through it—read the comments)
Sinon's mocking is interesting, because it keeps the behavior of the object being mocked intact while still allowing you to test how the methods have been called. In the case of this test, I don't care about what the BookDownloadManager#downloadBook method is actually doing, so I created a dummy object with stubbed methods (line 8), and used sinon to mock that (line 9).

At first, I was confused about whether to use the original dlManager object or the dlManagerMock I had created with sinon, but I found that the mock you get from sinon.mock() is an object for you as the tester to verify that methods are being called, while the original object passed into .mock() continues to be what you actually use to interact with the objects being tested. Sam pointed out that this was done to preserve the namespace of the original object. For example, what if dlManager had a function called #verify()?

Saturday, April 25, 2015

4/25/15 - More Unit Tests

Today, I let Finn drive and he started writing the BookDownloadManager tests, although we didn't get to a point where the tests actually do anything.

I also noticed that somewhere along the way I had broken the BookDownloadManager in our app, even though it passed all the tests. This was because I had forgotten to pass an HttpRequestHandler into the object in app.js on this line that I was passing in correctly in the tests. Mr. Elkner suggested standardizing the way the objects are generated between the app and the tests. Another solution to this problem would be to throw an exception if something vital is missing from the arguments object. Unfortunately, neither var b = vital_arg || throw Error or var b = vital_arg ? vital_arg : throw Error work, so we would need an if statement for each variable we want to ensure is defined.

I emailed Kevin and linked him to yesterday's blog post, where I had complained about how long this it statement was. He said that I was right in being suspicious about the length, and that the test is testing more than one thing. He suggested splitting this test into two, one to test chapter, and one to test book. In hindsight, this is really obvious and I should have figured it out on my own. Also, it made me realize that the wording on the test is wrong. It says that the BookPlayerPageGenerator actually 'generates download book and download chapter buttons', which isn't true; it only adds onclick events to the existing elements.

Thursday, April 23, 2015

4/23/15 - Testing with Sinon

Today I started testing the BookPlayerPageGenerator, and wanted to test that the BookDownloadManager was getting the proper messages. User interface is really hard to test, and one aspect of what I ended up with really bothered me.
I really dislike how long the description inside the it() is. I vaguely recall that when I was starting out in rspec, I learned you don't want to go over a certain character limit with your test description, otherwise it suggests that something is wrong (line 27) with the objects you are working with.  If you are interested, the unabridged test code is here and the object being tested is here.

Also, the test file is getting pretty long (225 lines), and there's a lot of sample constructs defined at the top of the file (like the BOOK_OBJECT and CHAPTER_OBJECT variables shown in this gist) that should be moved into their own file, and maybe even encapsulated in some sort of globals object.  Each object's test should also be moved into its own test file.

Wednesday, April 22, 2015

4/22/15 - LibriFox Audio Downloading

Today, Finn and I worked on setting up a way to download the audiobook mp3s. It was hard because there is very sparse documentation for the Firefox OS file system API. For example, on this page, only one example of a "selector" for enumerate is given. We wanted to find all the files in the librifox/ directory, and eventually Finn guessed that you could do #enumerate('path/'), which turned out to work. There also isn't really a good way to access the filesystem short of using their API, so when we ran into trouble we weren't sure if our problem was in the file write code or the code we were inputting to read the filenames back to see if they were written.  There is no native file browser in Firefox OS.

Two new objects were added to app.js, a BookDownloadManager, which handles the downloading of chapter and book audio files, and a BookPlayerPageGenerator, which sets up the book.html user interface.

One thing I don't like about the code in this stage is how much object generation is done just in the body of app.js. For example, BookPlayerPageGenerator needs to know about three elements on the page, and right now we are creating the instance and setting up the selectors right in the body of the file, which adds clutter. App.js is getting really big, so we really need to look into splitting it into smaller files. I'm not sure how this works with jQuery Mobile though.

Here's a picture that shows today's progress.

You can see on the console there (under the not well formed errors, which are the most vague and unhelpful error messages ever), that the downloaded chapter gets written to 'librifox/book-id/chapter-index.mp3'.

jQuery Mobile's built in sliders were terrible to use as progress bars because they are designed to be interactive, and we don't want the user sliding them around while a file is downloading.  I ended up making my own progress bar that's just a div nested inside another div, which requires way less setup code than what we would need to do for jQuery mobile sliders.

None of this code has tests written yet, so that's something we will work on tomorrow.

One thing I was unsure of when writing the 'generator' objects is how to handle the args hash being undefined.  Right now I have the line var args = args || {};, which sets the args to an empty object if it's excluded. I'm thinking that it might be better not to do that at all, because the object won't function correctly without those arguments, and it's probably better to fail explicitly right at the point it tries to set variables from the args, rather than later on when the code tries to use them and finds them to be undefined.

Tuesday, April 21, 2015

4/21/15 - Gitignore

I haven't blogged in a while because I was preparing for the UMD programming contest last week, but today Finn and I worked on refactoring the book page of LibriFox.

I also wanted to blog in order to write this down in case I needed to do it again:
I put all my Learn C the Hard Way exercises into a git repository so that I would have access to their revision history, but I forgot about all the compiled files and ended up with this as the repo folder.  I don't have make set up to use the .out suffix, so I couldn't just add the compiled files to the gitignore.  Instead I found this guide online and set up my .gitignore following it:
File: .gitignore

# ignore everything
*
# Don't ignore directories, so we can recurse into them
!*/
# Don't ignore other important files
!.gitignore
!*.c
!*.java
!*.py
!*.rb
Then, to remove all the files I didn't want that had already been committed, I did git rm -rf . --cached, which removed all the files from my git path, while keeping them on the local machine. Then I did git add . to add all the files allowed by the gitignore back into the path.
This isn't the perfect solution, because every time I add a new file type I'll have to add it manually, but it works for now. I ended up with a much cleaner directory.

Also I found a javascript web mocking library called Nock, so hopefully that will allow us to test the HttpRequestHandler object easily.

Wednesday, April 8, 2015

4/8/15 - File Name Too Long

Today I kept working on the SearchResultsPageGenerator, but I'm having trouble getting it to work because I'm getting this error: NS_ERROR_FILE_NAME_TOO_LONG.  Apparently this is a problem with windows, and I couldn't find any info on how to fix it.  I tried it with one of the flame devices, and that didn't work either (same error). I tried a different Firefox OS app I had, and it worked fine, so this problem is specific to the librifox app.

Tuesday, April 7, 2015

4/7/15 - Windows Shell


Since I use windows on my laptop, I've had to use git through cmd.exe, which is very feature-lacking. It doesn't have tab support and copying/pasting is buried in the menus with no keyboard shortcut. I followed this tutorial in order to get Console2 running with git. I installed Console2 to my Program Files (x86) directory, and figured out that when you change your settings through the menu, they will silently revert back unless Console2 is running in administrator mode, since it doesn't have permission to write to the program files directory otherwise.

I also started refactoring the book search results page into a BookSearchPageGenerator just like I did with ChapterListPageGenerator. I also started writing tests for it, but I'm not sure the best method for removing the http dependencies. Right now I'm making a mock of the http parser, and adding the methods. It's also tough to figure out how much is really necessary to test for, since UI is pretty complex. For example, is it important to test that the appended elements are <li>s? I don't think it is.

4/4/15 - UMD Contest Problems

Today I did two 2013 UMD contest problems: 3 - Guessing Game I and 4 - Tree Isomorphism. Guessing Game I was really easy, and I had already done Tree Isomorphism with Sam on Wednesday and Thursday, but I decided to do it again because Sam did the typing (and most of the thinking) on the last one, and I remembered our approach to the problem well enough to re-solve it. I've noticed that a lot of times I'll read one of these UMD problems and just have no idea what to do, and before talking through it with Sam, Tree Isomorphism was definitely one of those cases where I just had no clue how to approach it.  After seeing the solution once, though, I was able to come up with it again on my own.  I didn't keep track of time, but it probably took about an hour and a half, which isn't very good considering I had already seen the solution once.

One thing about our approach to this problem that confused me and slowed me down was the List<List<TreeNode>> type returned by #permutations(). I couldn't remember our previous solution exactly, so I had to play around with the recursion to get the method to work correctly with the return type. Another thing that threw me off was the choice between List<TreeNode> children and rootNode.children in method arguments. I found that it made the most sense to do the former, because a root node might not always be present, such as when the #permutations() List<List<TreeNode>> was in use. For example, I originally had #treeEquals() take two root nodes and compare children (rather than taking two lists of nodes), but I realized that this would require me to add additional root nodes when all I had was a list of children, such as when checking the permutations of one tree against the other.

I also looked at 5 - Guessing Game II, and it's the same problem—I don't have enough experience to be able to come up with a strategy. I did work through their solution, and I think understand it pretty well, although I wouldn't have been able to come up with that on my own.

Monday, April 6, 2015

4/6/15 - Permutations in Ruby

I rewrote my ruby permutations algorithm to use yield instead of return. This is a lazy algorithm: rather than generating all permutations at once, it generates them as they are needed. So the line permutations(%w[a b c d]).first(5) only generates the first 5 permutations of the array. Here is the implementation: As it turns out, this function is already built into ruby! This statement does the exact same thing.

Sunday, April 5, 2015

4/5/15 ChaptersListPageGenerator code and tests

Repo diff
ChaptersListPageGenerator code
ChaptersListPageGenerator tests

For some reason, there are two ways to test equality in our tests, assert.equal() and expect(). I think the former is the mocha expression, and the latter is the chai expression. I tried to convert all our tests over to the expect() syntax. I also found this page, which was helpful for writing the expect statements.

In testing the chapters list page generator, I made my own stub for HttpRequestHandler, which is used to fetch the RSS chapter data that populates the chapters list. There's a stubbing/mocking library we might want to look into as well.

I wasn't quite sure how to test the output of #generatePage since it's all within the page elements, and this is what I ended up with. I also found that I had to explicitly instantiate the <ul> targeted by #pageGenerate as a jQuery Mobile listview, otherwise this line in app.js would cause an error saying that the listview had not been initialized.

I added a property to karma.conf.js to allow console.log statements to be outputted to the server console, which is helpful for debugging the tests

I don't think we need to test the private methods of ChapterListPageGenerator; they aren't part of any public interface, so there is no risk of cascading changes causing problems beyond #generatePage. if something is changed.

I looked at how to make public and private methods in an object, and how inheritance works in JavaScript:

With a Foo() object set as Bar's prototype, all new Bar objects to send anything that they don't know how to handle up the message chain to Foo. So a method defined in Bar would override an identical method in Foo, but a method defined in Foo and not in Bar will work as though it is being called on Foo directly. This source was helpful in figuring stuff out.

Saturday, March 28, 2015

3/28/15 - Permutations in Ruby and Python

Right at the end of class on Friday I finished my ruby permutations implementation, but didn't get a chance to blog it.
Sam also wrote a python version and explained the yield statement to me. I even wrote about it in Thursday's post, but I still don't really understand what is happening here. I'll try to get around to looking a bit more closely at the code and figure out how it works.