From 3b9b0745de75b3f76aa2331c02332d4e60b1cac4 Mon Sep 17 00:00:00 2001 From: Michael Dvorkin Date: Sat, 17 Aug 2013 12:37:18 -0700 Subject: [PATCH] More refactoring and docs as suggested by golint --- column_editor.go | 26 ++++---- layout.go | 153 ++++++++++++++++++++++++++--------------------- profile.go | 8 +-- 3 files changed, 102 insertions(+), 85 deletions(-) diff --git a/column_editor.go b/column_editor.go index 56f74f2..dfef0d5 100644 --- a/column_editor.go +++ b/column_editor.go @@ -10,9 +10,10 @@ import `github.com/michaeldv/termbox-go` // current column name in the header, then waits for arrow keys (choose // another column), Enter (reverse sort order), or Esc (exit). type ColumnEditor struct { - screen *Screen // Pointer to Screen so we could use screen.Draw(). - quotes *Quotes // Pointer to Quotes to redraw them when the sort order changes. - profile *Profile // Pointer to Profile where we save newly selected sort order. + screen *Screen // Pointer to Screen so we could use screen.Draw(). + quotes *Quotes // Pointer to Quotes to redraw them when the sort order changes. + layout *Layout // Pointer to Layout to redraw stock quotes header. + profile *Profile // Pointer to Profile where we save newly selected sort order. } // Initialize sets internal variables and highlights current column name @@ -20,6 +21,7 @@ type ColumnEditor struct { func (editor *ColumnEditor) Initialize(screen *Screen, quotes *Quotes) *ColumnEditor { editor.screen = screen editor.quotes = quotes + editor.layout = screen.layout editor.profile = quotes.profile editor.selectCurrentColumn() @@ -51,25 +53,25 @@ func (editor *ColumnEditor) Handle(event termbox.Event) bool { //----------------------------------------------------------------------------- func (editor *ColumnEditor) selectCurrentColumn() *ColumnEditor { - editor.profile.selected_column = editor.profile.SortColumn + editor.profile.selectedColumn = editor.profile.SortColumn editor.redrawHeader() return editor } //----------------------------------------------------------------------------- func (editor *ColumnEditor) selectLeftColumn() *ColumnEditor { - editor.profile.selected_column-- - if editor.profile.selected_column < 0 { - editor.profile.selected_column = TotalColumns - 1 + editor.profile.selectedColumn-- + if editor.profile.selectedColumn < 0 { + editor.profile.selectedColumn = editor.layout.TotalColumns() - 1 } return editor } //----------------------------------------------------------------------------- func (editor *ColumnEditor) selectRightColumn() *ColumnEditor { - editor.profile.selected_column++ - if editor.profile.selected_column > TotalColumns - 1 { - editor.profile.selected_column = 0 + editor.profile.selectedColumn++ + if editor.profile.selectedColumn > editor.layout.TotalColumns() - 1 { + editor.profile.selectedColumn = 0 } return editor } @@ -85,13 +87,13 @@ func (editor *ColumnEditor) execute() *ColumnEditor { //----------------------------------------------------------------------------- func (editor *ColumnEditor) done() bool { - editor.profile.selected_column = -1 + editor.profile.selectedColumn = -1 return true } //----------------------------------------------------------------------------- func (editor *ColumnEditor) redrawHeader() { - editor.screen.DrawLine(0, 4, editor.screen.layout.Header(editor.profile)) + editor.screen.DrawLine(0, 4, editor.layout.Header(editor.profile)) termbox.Flush() } diff --git a/layout.go b/layout.go index f20ac3b..dee9858 100644 --- a/layout.go +++ b/layout.go @@ -1,6 +1,6 @@ // Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. +// Use of this source code is governed by a MIT-style license that can +// be found in the LICENSE file. package mop @@ -14,91 +14,100 @@ import ( `time` ) -const TotalColumns = 15 - +// Column describes formatting rules for individual column within the list +// of stock quotes. type Column struct { - width int - name string - title string - formatter func(string)string + width int // Column width. + name string // The name of the field in the Stock struct. + title string // Column title to display in the header. + formatter func(string)string // Optional function to format the contents of the column. } +// Layout is used to format and display all the collected data, i.e. market +// updates and the list of stock quotes. type Layout struct { - columns []Column - sorter *Sorter - regex *regexp.Regexp - market_template *template.Template - quotes_template *template.Template + columns []Column // List of stock quotes columns. + sorter *Sorter // Pointer to sorting receiver. + regex *regexp.Regexp // Pointer to regular expression to align decimal points. + marketTemplate *template.Template // Pointer to template to format market data. + quotesTemplate *template.Template // Pointer to template to format the list of stock quotes. } -//----------------------------------------------------------------------------- -func (self *Layout) Initialize() *Layout { - self.columns = []Column{ - { -7, `Ticker`, `Ticker`, nil }, - { 10, `LastTrade`, `Last`, currency }, - { 10, `Change`, `Change`, currency }, - { 10, `ChangePct`, `Change%`, last }, - { 10, `Open`, `Open`, currency }, - { 10, `Low`, `Low`, currency }, - { 10, `High`, `High`, currency }, - { 10, `Low52`, `52w Low`, currency }, - { 10, `High52`, `52w High`, currency }, - { 11, `Volume`, `Volume`, nil }, - { 11, `AvgVolume`, `AvgVolume`, nil }, - { 9, `PeRatio`, `P/E`, blank }, - { 9, `Dividend`, `Dividend`, blank_currency }, - { 9, `Yield`, `Yield`, percent }, - { 11, `MarketCap`, `MktCap`, currency }, +// Initialize assigns the default values that stay unchanged for the life of +// allocated Layout struct. +func (layout *Layout) Initialize() *Layout { + layout.columns = []Column{ + { -7, `Ticker`, `Ticker`, nil }, + { 10, `LastTrade`, `Last`, currency }, + { 10, `Change`, `Change`, currency }, + { 10, `ChangePct`, `Change%`, last }, + { 10, `Open`, `Open`, currency }, + { 10, `Low`, `Low`, currency }, + { 10, `High`, `High`, currency }, + { 10, `Low52`, `52w Low`, currency }, + { 10, `High52`, `52w High`, currency }, + { 11, `Volume`, `Volume`, nil }, + { 11, `AvgVolume`, `AvgVolume`, nil }, + { 9, `PeRatio`, `P/E`, blank }, + { 9, `Dividend`, `Dividend`, zero }, + { 9, `Yield`, `Yield`, percent }, + { 11, `MarketCap`, `MktCap`, currency }, } - self.regex = regexp.MustCompile(`(\.\d+)[BMK]?$`) - self.market_template = build_market_template() - self.quotes_template = build_quotes_template() + layout.regex = regexp.MustCompile(`(\.\d+)[BMK]?$`) + layout.marketTemplate = buildMarketTemplate() + layout.quotesTemplate = buildQuotesTemplate() - return self + return layout } -//----------------------------------------------------------------------------- -func (self *Layout) Market(market *Market) string { - if ok, err := market.Ok(); !ok { - return err +// Market merges given market data structure with the market template and +// returns formatted string that includes highlighting markup. +func (layout *Layout) Market(market *Market) string { + if ok, err := market.Ok(); !ok { // If there was an error fetching market data... + return err // then simply return the error string. } highlight(market.Dow, market.Sp500, market.Nasdaq) buffer := new(bytes.Buffer) - self.market_template.Execute(buffer, market) + layout.marketTemplate.Execute(buffer, market) return buffer.String() } -//----------------------------------------------------------------------------- -func (self *Layout) Quotes(quotes *Quotes) string { - if ok, err := quotes.Ok(); !ok { - return err +// Quotes uses quotes template to format timestamp, stock quotes header, +// and the list of given stock quotes. It returns formatted string with +// all the necessary markup. +func (layout *Layout) Quotes(quotes *Quotes) string { + if ok, err := quotes.Ok(); !ok { // If there was an error fetching stock quotes... + return err // then simply return the error string. } vars := struct { - Now string - Header string - Stocks []Stock + Now string // Current timestamp. + Header string // Formatted header line. + Stocks []Stock // List of formatted stock quotes. }{ time.Now().Format(`3:04:05pm PST`), - self.Header(quotes.profile), - self.prettify(quotes), + layout.Header(quotes.profile), + layout.prettify(quotes), } buffer := new(bytes.Buffer) - self.quotes_template.Execute(buffer, vars) + layout.quotesTemplate.Execute(buffer, vars) return buffer.String() } -//----------------------------------------------------------------------------- -func (self *Layout) Header(profile *Profile) string { - str, selected_column := ``, profile.selected_column +// Header iterates over column titles and formats the header line. The +// formatting includes placing an arrow next to the sorted column title. +// When the column editor is active it knows how to highlight currently +// selected column title. +func (layout *Layout) Header(profile *Profile) string { + str, selectedColumn := ``, profile.selectedColumn - for i,col := range self.columns { - arrow := arrow_for(i, profile) - if i != selected_column { + for i,col := range layout.columns { + arrow := arrowFor(i, profile) + if i != selectedColumn { str += fmt.Sprintf(`%*s`, col.width, arrow + col.title) } else { str += fmt.Sprintf(`%*s`, col.width, arrow + col.title) @@ -108,8 +117,14 @@ func (self *Layout) Header(profile *Profile) string { return `` + str + `` } +// TotalColumns is the utility method for the column editor that returns +// total number of columns. +func (layout *Layout) TotalColumns() int { + return len(layout.columns) +} + //----------------------------------------------------------------------------- -func (self *Layout) prettify(quotes *Quotes) []Stock { +func (layout *Layout) prettify(quotes *Quotes) []Stock { pretty := make([]Stock, len(quotes.stocks)) // // Iterate over the list of stocks and properly format all its columns. @@ -122,23 +137,23 @@ func (self *Layout) prettify(quotes *Quotes) []Stock { // - If the column has the formatter method then call it. // - Set the column value padding it to the given width. // - for _,column := range self.columns { + for _,column := range layout.columns { // ex. value = stock.Change value := reflect.ValueOf(&stock).Elem().FieldByName(column.name).String() if column.formatter != nil { // ex. value = currency(value) value = column.formatter(value) } - // ex. pretty[i].Change = self.pad(value, 10) - reflect.ValueOf(&pretty[i]).Elem().FieldByName(column.name).SetString(self.pad(value, column.width)) + // ex. pretty[i].Change = layout.pad(value, 10) + reflect.ValueOf(&pretty[i]).Elem().FieldByName(column.name).SetString(layout.pad(value, column.width)) } } profile := quotes.profile - if self.sorter == nil { // Initialize sorter on first invocation. - self.sorter = new(Sorter).Initialize(profile) + if layout.sorter == nil { // Initialize sorter on first invocation. + layout.sorter = new(Sorter).Initialize(profile) } - self.sorter.SortByCurrentColumn(pretty) + layout.sorter.SortByCurrentColumn(pretty) // // Group stocks by advancing/declining unless sorted by Chanage or Change% // in which case the grouping has been done already. @@ -151,8 +166,8 @@ func (self *Layout) prettify(quotes *Quotes) []Stock { } //----------------------------------------------------------------------------- -func (self *Layout) pad(str string, width int) string { - match := self.regex.FindStringSubmatch(str) +func (layout *Layout) pad(str string, width int) string { + match := layout.regex.FindStringSubmatch(str) if len(match) > 0 { switch len(match[1]) { case 2: @@ -166,7 +181,7 @@ func (self *Layout) pad(str string, width int) string { } //----------------------------------------------------------------------------- -func build_market_template() *template.Template { +func buildMarketTemplate() *template.Template { markup := `{{.Dow.name}}: {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}}, {{.Sp500.name}}: {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}}, {{.Nasdaq.name}}: {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}} {{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. {{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq {{if .IsClosed}}U.S. markets closed{{end}} New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. New lows: {{.Lows.nyse}} on NYSE and {{.Lows.nasdaq}} on Nasdaq.` @@ -175,7 +190,7 @@ New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. New lows: {{ } //----------------------------------------------------------------------------- -func build_quotes_template() *template.Template { +func buildQuotesTemplate() *template.Template { markup := `{{.Now}} @@ -218,7 +233,7 @@ func group(stocks []Stock) []Stock { } //----------------------------------------------------------------------------- -func arrow_for(column int, profile *Profile) string { +func arrowFor(column int, profile *Profile) string { if column == profile.SortColumn { if profile.Ascending { return string('\U00002191') @@ -238,7 +253,7 @@ func blank(str string) string { } //----------------------------------------------------------------------------- -func blank_currency(str string) string { +func zero(str string) string { if str == `0.00` { return `-` } diff --git a/profile.go b/profile.go index 269b6b3..0f67623 100644 --- a/profile.go +++ b/profile.go @@ -21,7 +21,7 @@ type Profile struct { Tickers []string SortColumn int Ascending bool - selected_column int + selectedColumn int } //----------------------------------------------------------------------------- @@ -39,7 +39,7 @@ func (self *Profile) Initialize() *Profile { } else { json.Unmarshal(data, self) } - self.selected_column = -1 + self.selectedColumn = -1 return self } @@ -95,10 +95,10 @@ func (self *Profile) RemoveTickers(tickers []string) (removed int, err error) { //----------------------------------------------------------------------------- func (self *Profile) Reorder() error { - if self.selected_column == self.SortColumn { + if self.selectedColumn == self.SortColumn { self.Ascending = !self.Ascending } else { - self.SortColumn = self.selected_column + self.SortColumn = self.selectedColumn } return self.Save() }