Monday, June 1, 2015

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?

No comments:

Post a Comment