PROJECT: README
Hi, I’m a second-year computer science undergraduate in NUS.
Overview
README is an integrated link manager and web feed aggregator application. My main contribution involved adding support for web feeds to README. RSS and Atom are common technologies of allowing an user to access updates to online content. When supported by websites, subscribing to the feed removes the need for users to manually check the website in question, offering greater convenience to the user.
Summary of contributions
Primary enhancement: I added web syndication(RSS/Atom) support in README.
-
Justification: With support for web syndication the user can subscribe to sources of content that they care about, and have the content delivered to them.
-
Highlights: This enhancement requires significant architectural changes in the base link management application. It required an in-depth analysis of design considerations.
-
Credits: The ROME library is used to the heavy lifting in parsing web feeds.
Minor enhancements
-
I added a counter in status bar showing how many entries are listed, which is a quality-of-life improvement.
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Subscribing to a feed: subscribe
Adds a feed and subscribes to its updates.
All entries in the subscribed feed will be added to the reading list.
Format: subscribe l/URL [ti/TITLE_OVERRIDE] [d/DESCRIPTION_OVERRIDE] [t/TAG]..
|
The |
|
A feed can have any number of tags (including 0). |
Examples:
-
Adds a feed whose name is “Kattis”.
subscribe l/https://open.kattis.com/rss/new-problems t/programming
|
The application may be unresponsive for a short while when adding entries from a large feed. |
Searching the web: bing
Searches Bing for entries that you can subsequently add.
This command also enters the Search Results context. Refer to [Results-Context] for available commands in this context.
Format: bing [KEYWORD]…
Examples:
-
Bing search the web for entries containing the
Trumpkeyword-
bing Trump
-
TrumpContributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Web Feed support
To support the reading and subscription of web feeds, we decided to centralise various feed-related operations in a static class FeedUtil.
Static class (chosen approach)
We define a new static class FeedUtil in commons.util.
FeedUtil is primarily in charge of fetching a web feed from a URL and serializing its contents into an EntryBook.
The advantage of this approach, which is why we decided to use it, is because keeping all the feed handling code in a single class restricts the dependency of ROME at the program boundary, which is more maintainable.
-
Pros: Relatively easy to write, and we minimise the number of classes that depend on the external library.
-
Cons: Increased coupling with the components that handle RSS feeds.
Ad-hoc
Another approach we can consider is to directly invoke ROME whenever we deal with web feeds.
-
Pros: Even easier to write, almost no code needed.
-
Cons: Bad cohesion, if we were to change the library for RSS parsing it requires changes in multiple components.
Extending and generalising Storage component.
Reading Entries from a web feed shares a major similarity with reading a stored EntryBook from disk. Both actions serialize an EntryBook from a source of external data.
To apply this general observation we can consider the following design.
-
Pros: Expresses the intention of the code better with a stronger separation of concerns. Also applies interface segregation principle.
-
Cons: Large amounts of refactors and changes needed.
Multiple EntryBooks
ModelManager used to have only a single EntryBook. However, managing feeds and archives subscription system means that we need to deal with more than a single EntryBook.
Since we should only show a single EntryBook at a time, we have to make model contain multiple entrybooks.
Multiple EntryBooks
To support multiple EntryBooks, we decided to have multiple differently named EntryBooks each with their own accessors.
Design considerations
-
Current choice: multiple differently named
EntryBook’s with differently-named accessors each-
Pros: easy to implement
-
Cons: accessors are basically duplicated code
-
-
Alternative: Each
EntryBookbelongs to aModelContext, modifiers overloaded to be context-sensitive-
Pros: less code duplication due to polymorphism
-
Cons: writing to an
EntryBookthat does not belong the current state will either involve an extra accessor (basically Alternative 1), or changing state (specificallyModel#setContext), which is not robust.
-
We chose to implement multiple EntryBooks in parallel as it turned out that the different EntryBooks demanded slightly different behaviour, and a more polymorphic approach like that given in the alternative might overcomplicate the implementation.
Decoupling displayed EntryBook
Another task is to decouple the EntryBook that is displayed from the ones that we added to ModelManager.
Decoupling the displayed EntryBook means that the following additional methods are implemented
-
ModelManager#displayEntryBook(EntryBook)— shows theEntryBookprovided in the user interface.
Given below is an example usage scenario and how this mechanism behaves.
displayEntryBook is used.-
The user launches the application. The
ModelManagerwill default to displaying theEntryBookwhich corresponds to the default (list) context. -
The user executes the
feed [valid_feed_url]command to view a web feed. Thefeedcommand fetches and parses the web feed into anEntryBookwhich is used forModelManager#setSearchEntryBookto replace the search contextEntryBook. -
It then sets the
ModelContexttoCONTEXT_SEARCH. In the search context theEntryBookis read-only, and you can import links from the currently displayedEntryBookinto thelistcontextEntryBook.
Design Considerations
With reference to Class diagram illustrating a model supporting multiple EntryBooks., we decided to have a SimpleListProperty<Entry> called displayedEntryList which is used to provide the base list for filteredEntries.
The implementation of ModelManager#displayEntryBook will then just be a displayedEntryList.setValue. However we decided to keep displayEntryBook a private method and not expose it via the Model interface.
-
Current choice: Keep
displayEntryBookprivate, it only gets called whenModelContextchanges.-
Pros: more defensive — a rogue command cannot easily display junk.
-
Cons: In the context of the
feedcommand, where theEntryBookused is in fact ephemeral. When theModelContextswitches away fromsearch, the ephemeralEntryBookstill lives inModelManager, using extra memory.
-
-
Alternative: Promote
displayEntryBooktoModelinterface so that other classes (such as various commands) could use it.-
Pros: easier implementation for some commands, slightly reduced memory usage.
-
Cons: decoupling displayed
EntryBookfromModelContextcould allow buggy commands to placeModelManagerin an inconsistent state.
-
We chose to keep displayEntryBook private and only invoke it on model context change since doing so is more robust and only entails a slight additional cognitive load.