From 51b6250fcf8ebda0f2b7abbb50361c4a1e985c71 Mon Sep 17 00:00:00 2001 From: Michael Dvorkin Date: Fri, 29 May 2015 19:30:24 -0700 Subject: [PATCH] Idiomatic Go --- cmd/mop.go | 10 +++++----- column_editor.go | 16 +++++++++------- layout.go | 36 +++++++++++++++++++++++++----------- line_editor.go | 14 +++++++------- markup.go | 7 ++++--- sorter.go | 10 +++++----- yahoo_quotes.go | 21 ++++++++------------- 7 files changed, 63 insertions(+), 51 deletions(-) diff --git a/cmd/mop.go b/cmd/mop.go index 7726410..53442df 100644 --- a/cmd/mop.go +++ b/cmd/mop.go @@ -10,7 +10,7 @@ import ( `time` ) -const help = `Mop v0.1.0 -- Copyright (c) 2013 Michael Dvorkin. All Rights Reserved. +const help = `Mop v0.2.0 -- Copyright (c) 2013-15 Michael Dvorkin. All Rights Reserved. NO WARRANTIES OF ANY KIND WHATSOEVER. SEE THE LICENSE FILE FOR DETAILS. Command Description @@ -46,8 +46,8 @@ func mainLoop(screen *mop.Screen, profile *mop.Profile) { } }() - market := new(mop.Market).Initialize() - quotes := new(mop.Quotes).Initialize(market, profile) + market := mop.NewMarket() + quotes := mop.NewQuotes(market, profile) screen.Draw(market, quotes) loop: @@ -60,10 +60,10 @@ loop: if event.Key == termbox.KeyEsc || event.Ch == 'q' || event.Ch == 'Q' { break loop } else if event.Ch == '+' || event.Ch == '-' { - lineEditor = new(mop.LineEditor).Initialize(screen, quotes) + lineEditor = mop.NewLineEditor(screen, quotes) lineEditor.Prompt(event.Ch) } else if event.Ch == 'o' || event.Ch == 'O' { - columnEditor = new(mop.ColumnEditor).Initialize(screen, quotes) + columnEditor = mop.NewColumnEditor(screen, quotes) } else if event.Ch == 'g' || event.Ch == 'G' { if profile.Regroup() == nil { screen.Draw(quotes) diff --git a/column_editor.go b/column_editor.go index dfef0d5..699bcde 100644 --- a/column_editor.go +++ b/column_editor.go @@ -16,13 +16,15 @@ type ColumnEditor struct { profile *Profile // Pointer to Profile where we save newly selected sort order. } -// Initialize sets internal variables and highlights current column name -// (as stored in Profile). -func (editor *ColumnEditor) Initialize(screen *Screen, quotes *Quotes) *ColumnEditor { - editor.screen = screen - editor.quotes = quotes - editor.layout = screen.layout - editor.profile = quotes.profile +// Returns new initialized ColumnEditor struct. As part of initialization it +// highlights current column name (as stored in Profile). +func NewColumnEditor(screen *Screen, quotes *Quotes) *ColumnEditor { + editor := &ColumnEditor{ + screen: screen, + quotes: quotes, + layout: screen.layout, + profile: quotes.profile, + } editor.selectCurrentColumn() diff --git a/layout.go b/layout.go index fff1b80..d6ff386 100644 --- a/layout.go +++ b/layout.go @@ -33,9 +33,9 @@ type Layout struct { quotesTemplate *template.Template // Pointer to template to format the list of stock quotes. } -// Initialize assigns the default values that stay unchanged for the life of -// allocated Layout struct. -func (layout *Layout) Initialize() *Layout { +// Creates the layout and assigns the default values that stay unchanged. +func NewLayout() *Layout { + layout := &Layout{} layout.columns = []Column{ { -7, `Ticker`, `Ticker`, nil }, { 10, `LastTrade`, `Last`, currency }, @@ -67,8 +67,9 @@ func (layout *Layout) Market(market *Market) string { return err // then simply return the error string. } - highlight(market.Dow, market.Sp500, market.Nasdaq, market.London, market.Frankfurt, - market.Paris, market.Tokyo, market.HongKong, market.Shanghai) + highlight(market.Dow, market.Sp500, market.Nasdaq, + market.Tokyo, market.HongKong, market.London, market.Frankfurt, + market.Yield, market.Oil, market.Euro, market.Gold) buffer := new(bytes.Buffer) layout.marketTemplate.Execute(buffer, market) @@ -152,7 +153,7 @@ func (layout *Layout) prettify(quotes *Quotes) []Stock { profile := quotes.profile if layout.sorter == nil { // Initialize sorter on first invocation. - layout.sorter = new(Sorter).Initialize(profile) + layout.sorter = NewSorter(profile) } layout.sorter.SortByCurrentColumn(pretty) // @@ -183,9 +184,9 @@ func (layout *Layout) pad(str string, width int) string { //----------------------------------------------------------------------------- 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}} -{{.London.name}} {{.London.change}} ({{.London.percent}}) at {{.London.latest}} {{.Frankfurt.name}} {{.Frankfurt.change}} ({{.Frankfurt.percent}}) at {{.Frankfurt.latest}} {{.Paris.name}} {{.Paris.change}} ({{.Paris.percent}}) at {{.Paris.latest}} {{if .IsClosed}}U.S. markets closed{{end}} -{{.Tokyo.name}} {{.Tokyo.change}} ({{.Tokyo.percent}}) at {{.Tokyo.latest}} {{.HongKong.name}} {{.HongKong.change}} ({{.HongKong.percent}}) at {{.HongKong.latest}} {{.Shanghai.name}} {{.Shanghai.change}} ({{.Shanghai.percent}}) at {{.Shanghai.latest}}` + markup := `Dow {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}} S&P 500 {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}} NASDAQ {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}} +Tokyo {{.Tokyo.change}} ({{.Tokyo.percent}}) at {{.Tokyo.latest}} HK {{.HongKong.change}} ({{.HongKong.percent}}) at {{.HongKong.latest}} London {{.London.change}} ({{.London.percent}}) at {{.London.latest}} Frankfurt {{.Frankfurt.change}} ({{.Frankfurt.percent}}) at {{.Frankfurt.latest}} {{if .IsClosed}}U.S. markets closed{{end}} +10-Year Yield {{.Yield.latest}}% ({{.Yield.change}}) Euro ${{.Euro.latest}} ({{.Euro.change}}%) Yen ¥{{.Yen.latest}} ({{.Yen.change}}%) Oil ${{.Oil.latest}} ({{.Oil.change}}%) Gold ${{.Gold.latest}} ({{.Gold.change}}%)` return template.Must(template.New(`market`).Parse(markup)) } @@ -267,7 +268,8 @@ func last(str string) string { if len(str) >= 6 && str[0:6] == `N/A - ` { return str[6:] } - return str + + return percent(str) } //----------------------------------------------------------------------------- @@ -282,11 +284,23 @@ func currency(str string) string { return `$` + str } +// Returns percent value truncated at 2 decimal points. //----------------------------------------------------------------------------- func percent(str string) string { if str == `N/A` { return `-` } - return str + `%` + split := strings.Split(str, ".") + if len(split) == 2 { + digits := len(split[1]) + if digits > 2 { + digits = 2 + } + str = split[0] + "." + split[1][0:digits] + } + if str[len(str)-1] != '%' { + str += `%` + } + return str } diff --git a/line_editor.go b/line_editor.go index d2d1779..03cab55 100644 --- a/line_editor.go +++ b/line_editor.go @@ -24,13 +24,13 @@ type LineEditor struct { regex *regexp.Regexp // Regex to split comma-delimited input string. } -// Initialize sets internal pointers and compiles the regular expression. -func (editor *LineEditor) Initialize(screen *Screen, quotes *Quotes) *LineEditor { - editor.screen = screen - editor.quotes = quotes - editor.regex = regexp.MustCompile(`[,\s]+`) - - return editor +// Returns new initialized LineEditor struct. +func NewLineEditor(screen *Screen, quotes *Quotes) *LineEditor { + return &LineEditor{ + screen: screen, + quotes: quotes, + regex: regexp.MustCompile(`[,\s]+`), + } } // Prompt displays a prompt in response to '+' or '-' commands. Unknown commands diff --git a/markup.go b/markup.go index 6675c13..fae6b1e 100644 --- a/markup.go +++ b/markup.go @@ -31,9 +31,10 @@ type Markup struct { regex *regexp.Regexp // Regex to identify the supported tag names. } -// Initialize creates tags to Termbox translation hash and sets default -// colors and string alignment. -func (markup *Markup) Initialize() *Markup { +// Creates markup to define tag to Termbox translation rules and store default +// colors and column alignments. +func NewMarkup() *Markup { + markup := &Markup{} markup.Foreground = termbox.ColorDefault markup.Background = termbox.ColorDefault markup.RightAligned = false diff --git a/sorter.go b/sorter.go index 87057b9..f02e178 100644 --- a/sorter.go +++ b/sorter.go @@ -85,11 +85,11 @@ func (list byDividendDesc) Less(i, j int) bool { return list.sortable[j].Divi func (list byYieldDesc) Less(i, j int) bool { return list.sortable[j].Yield < list.sortable[i].Yield } func (list byMarketCapDesc) Less(i, j int) bool { return m(list.sortable[j].MarketCap) < m(list.sortable[i].MarketCap) } -// Initialize simply saves the pointer to Profile for later use. -func (sorter *Sorter) Initialize(profile *Profile) *Sorter { - sorter.profile = profile - - return sorter +// Returns new Sorter struct. +func NewSorter(profile *Profile) *Sorter { + return &Sorter{ + profile: profile, + } } // SortByCurrentColumn builds a list of sort interface based on current sort diff --git a/yahoo_quotes.go b/yahoo_quotes.go index 7dd6e8e..a20fcb0 100644 --- a/yahoo_quotes.go +++ b/yahoo_quotes.go @@ -15,11 +15,7 @@ import ( // See http://www.gummy-stuff.org/Yahoo-stocks.htm // -// Also http://query.yahooapis.com/v1/public/yql -// ?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in(%22ALU%22,%22AAPL%22) -// &env=http%3A%2F%2Fstockstables.org%2Falltables.env&format=json' - -//const quotesURL = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=sl1c6k2oghjkva2r2rdyj3j1` +// const quotesURL = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=sl1c6k2oghjkva2r2rdyj3j1` // c2: realtime change vs c1: change // k2: realtime change vs p2: change // @@ -57,14 +53,13 @@ type Quotes struct { errors string // Error string if any. } -// Initialize sets the initial values for the Quotes struct. It returns "self" -// so that the next function call could be chained. -func (quotes *Quotes) Initialize(market *Market, profile *Profile) *Quotes { - quotes.market = market - quotes.profile = profile - quotes.errors = `` - - return quotes +// Sets the initial values and returns new Quotes struct. +func NewQuotes(market *Market, profile *Profile) *Quotes { + return &Quotes{ + market: market, + profile: profile, + errors: ``, + } } // Fetch the latest stock quotes and parse raw fetched data into array of