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.

Code contributed

  • RepoSense

  • Initial RSS support in #33, Architectural changes #80 #107. Feeds subscription #114 #173 #183, Add counter to status bar #15, Staging usage of GitHub pages for network tests #64.

Other contributions:

  • Project Management:

    • managed release v1.1

  • Community

    • PRs reviewed (with non-trivial review comments): #37 #77 #106

    • Contributions to forum discussions: #5 #47 #51 #72. Highlights include

      • Linking to exact line of relevant code in #47

      • Linking to exact section of relevant docs in #51

      • Bisecting the exact commit which broke my peer’s code in #72

    • Tools:

      • Integrated third party library (ROME) to the project. #33

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 Title and Description fields are automatically filled up if you do not provide them. If no tags were specified, the feed entry will automatically be tagged with the URL’s (stripped) hostname, if possible.

A feed can have any number of tags (including 0).
Imported entries from the feed will also be tagged with the same tags as the feed.

Examples:

  • Adds a feed whose name is “Kattis”.
    subscribe l/https://open.kattis.com/rss/new-problems t/programming

ug subx 1
Figure 1. Subscribing to a feed will add it to the feeds list.
ug subx 2
Figure 2. The links present in the feed are automatically imported to the reading list.

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 Trump keyword

    1. bing Trump

ug contextx bing
Figure 3. Bing search for the phrase Trump

Contributions 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.

FeedUtilActivityDiagram
Figure 4. Activity diagram for feed fetching.
  • 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.

GeneralisedEntryBookSerializer
Figure 5. Class diagram illustrating more general 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

MultipleEntryBooksChosenApproach
Figure 6. Class diagram illustrating a model supporting 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

MultipleEntryBooksAlternativeApproach
Figure 7. Class diagram of alternative approach for supporting multiple EntryBooks.
  • Alternative: Each EntryBook belongs to a ModelContext, modifiers overloaded to be context-sensitive

    • Pros: less code duplication due to polymorphism

    • Cons: writing to an EntryBook that does not belong the current state will either involve an extra accessor (basically Alternative 1), or changing state (specifically Model#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 the EntryBook provided in the user interface.

Given below is an example usage scenario and how this mechanism behaves.

DisplayEntryBookSequenceDiagram
Figure 8. Sequence diagram illustrating how displayEntryBook is used.
  1. The user launches the application. The ModelManager will default to displaying the EntryBook which corresponds to the default (list) context.

  2. The user executes the feed [valid_feed_url] command to view a web feed. The feed command fetches and parses the web feed into an EntryBook which is used for ModelManager#setSearchEntryBook to replace the search context EntryBook.

  3. It then sets the ModelContext to CONTEXT_SEARCH. In the search context the EntryBook is read-only, and you can import links from the currently displayed EntryBook into the list context EntryBook.

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 displayEntryBook private, it only gets called when ModelContext changes.

    • Pros: more defensive — a rogue command cannot easily display junk.

    • Cons: In the context of the feed command, where the EntryBook used is in fact ephemeral. When the ModelContext switches away from search, the ephemeral EntryBook still lives in ModelManager, using extra memory.

  • Alternative: Promote displayEntryBook to Model interface so that other classes (such as various commands) could use it.

    • Pros: easier implementation for some commands, slightly reduced memory usage.

    • Cons: decoupling displayed EntryBook from ModelContext could allow buggy commands to place ModelManager in 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.