Repository: go-co-op/gocron
Pull request #15 (2020-03-29 04:00:15)

Update all exported comments and start example_test.go for godoc
This is explains godocs pretty helpfully: https://godoc.org/github.com/fluhus/godoc-tricks

What the examples look like:
![image](https://user-images.githubusercontent.com/19351306/77840146-8409af80-7149-11ea-99db-3cb073461414.png)



Streppel: @arjunmahishi made some good points but I think this looks good now 
JohnRoesler: Agreed - great comments - one will be addressed in another issue / PR

Patch:
diff --git a/gocron.go b/gocron.go
index 6ec3d2c..0140c61 100644
--- a/gocron.go
+++ b/gocron.go
@@ -4,14 +4,6 @@
 // for configuration. Schedule lets you run Golang functions periodically
 // at pre-determined intervals using a simple, human-friendly syntax.
 //
-// Inspired by the Ruby module clockwork <https://github.com/tomykaira/clockwork>
-// and
-// Python package schedule <https://github.com/dbader/schedule>
-//
-// See also
-// http://adam.heroku.com/past/2010/4/13/rethinking_cron/
-// http://adam.heroku.com/past/2010/6/30/replace_cron_with_clockwork/
-//
 // Copyright 2014 Jason Lyu. jasonlvhit@gmail.com .
 // All rights reserved.
 // Use of this source code is governed by a BSD-style .
@@ -20,6 +12,7 @@ package gocron
 
 import (
 	"crypto/sha256"
+	"errors"
 	"fmt"
 	"reflect"
 	"runtime"
@@ -27,9 +20,18 @@ import (
 	"strings"
 )
 
-// Locker provides a method to lock jobs from running
-// at the same time on multiple instances of gocron.
-// You can provide any locker implementation you wish.
+// Error declarations for gocron related errors
+var (
+	ErrTimeFormat           = errors.New("time format error")
+	ErrParamsNotAdapted     = errors.New("the number of params is not adapted")
+	ErrNotAFunction         = errors.New("only functions can be schedule into the job queue")
+	ErrPeriodNotSpecified   = errors.New("unspecified job period")
+	ErrNotScheduledWeekday  = errors.New("job not scheduled weekly on a weekday")
+)
+
+// Locker provides an interface for implementing job locking
+// to prevent jobs from running at the same time on multiple
+// instances of gocron
 type Locker interface {
 	Lock(key string) (bool, error)
 	Unlock(key string) error
@@ -49,7 +51,8 @@ var (
 	locker Locker
 )
 
-// SetLocker sets a locker implementation
+// SetLocker sets a locker implementation to be used by
+// the scheduler for locking jobs
 func SetLocker(l Locker) {
 	locker = l
 }
@@ -66,7 +69,6 @@ func callJobFuncWithParams(jobFunc interface{}, params []interface{}) ([]reflect
 	return f.Call(in), nil
 }
 
-// for given function fn, get the name of function.
 func getFunctionName(fn interface{}) string {
 	return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
 }
diff --git a/job.go b/job.go
index 3296709..4427bbe 100644
--- a/job.go
+++ b/job.go
@@ -1,38 +1,29 @@
 package gocron
 
 import (
-	"errors"
+	"fmt"
 	"time"
 )
 
-// Error declarations for gocron
-var (
-	ErrTimeFormat           = errors.New("time format error")
-	ErrParamsNotAdapted     = errors.New("the number of params is not adapted")
-	ErrNotAFunction         = errors.New("only functions can be schedule into the job queue")
-	ErrPeriodNotSpecified   = errors.New("unspecified job period")
-	ErrParameterCannotBeNil = errors.New("nil paramaters cannot be used with reflection")
-)
-
-// Job struct keeping information about job
+// Job struct stores the information necessary to run a Job
 type Job struct {
 	interval          uint64                   // pause interval * unit between runs
-	startsImmediately bool                     // if the job should run upon scheduler start
-	jobFunc           string                   // the job jobFunc to run, func[jobFunc]
+	startsImmediately bool                     // if the Job should run upon scheduler start
+	jobFunc           string                   // the Job jobFunc to run, func[jobFunc]
 	unit              timeUnit                 // time units, ,e.g. 'minutes', 'hours'...
-	atTime            time.Duration            // optional time at which this job runs
-	err               error                    // error related to job
+	atTime            time.Duration            // optional time at which this Job runs
+	err               error                    // error related to Job
 	lastRun           time.Time                // datetime of last run
 	nextRun           time.Time                // datetime of next run
 	startDay          time.Weekday             // Specific day of the week to start on
 	funcs             map[string]interface{}   // Map for the function task store
 	fparams           map[string][]interface{} // Map for function and  params of function
-	lock              bool                     // lock the job from running at same time form multiple instances
-	tags              []string                 // allow the user to tag jobs with certain labels
+	lock              bool                     // lock the Job from running at same time form multiple instances
+	tags              []string                 // allow the user to tag Jobs with certain labels
 	time              timeHelper               // an instance of timeHelper to interact with the time package
 }
 
-// NewJob creates a new job with the time interval.
+// NewJob creates a new Job with the provided interval
 func NewJob(interval uint64) *Job {
 	th := newTimeHelper()
 	return &Job{
@@ -47,24 +38,24 @@ func NewJob(interval uint64) *Job {
 	}
 }
 
-// True if the job should be run now
+// shouldRun returns true if the Job should be run now
 func (j *Job) shouldRun() bool {
 	return j.time.Now().Unix() >= j.nextRun.Unix()
 }
 
-//Run the job and immediately reschedule it
+// Run the Job and immediately reschedule it
 func (j *Job) run() {
 	j.lastRun = j.time.Now()
 	go callJobFuncWithParams(j.funcs[j.jobFunc], j.fparams[j.jobFunc])
 }
 
-// Err should be checked to ensure an error didn't occur creating the job
+// Err returns an error if one ocurred while creating the Job
 func (j *Job) Err() error {
 	return j.err
 }
 
-// Tag allows you to add labels to a job
-// they don't impact the functionality of the job.
+// Tag allows you to add arbitrary labels to a Job that do not
+// impact the functionality of the Job
 func (j *Job) Tag(t string, others ...string) {
 	j.tags = append(j.tags, t)
 	for _, tag := range others {
@@ -72,7 +63,7 @@ func (j *Job) Tag(t string, others ...string) {
 	}
 }
 
-// Untag removes a tag from a job
+// Untag removes a tag from a Job
 func (j *Job) Untag(t string) {
 	newTags := []string{}
 	for _, tag := range j.tags {
@@ -84,7 +75,7 @@ func (j *Job) Untag(t string) {
 	j.tags = newTags
 }
 
-// Tags returns the tags attached to the job
+// Tags returns the tags attached to the Job
 func (j *Job) Tags() []string {
 	return j.tags
 }
@@ -110,13 +101,21 @@ func (j *Job) periodDuration() (time.Duration, error) {
 	return periodDuration, nil
 }
 
-// NextScheduledTime returns the time of when this job is to run next
+// NextScheduledTime returns the time of the Job's next scheduled run
 func (j *Job) NextScheduledTime() time.Time {
 	return j.nextRun
 }
 
-// GetWeekday returns which day of the week the job will run on
-// This should only be used when .Weekday(...) was called on the job.
-func (j *Job) GetWeekday() time.Weekday {
-	return j.startDay
+// GetScheduledTime returns the specific time of day the Job will run at
+func (j *Job) GetScheduledTime() string {
+	return fmt.Sprintf("%d:%d", j.atTime/time.Hour, (j.atTime%time.Hour)/time.Minute)
+}
+
+// GetWeekday returns which day of the week the Job will run on and
+// will return an error if the Job is not scheduled weekly
+func (j *Job) GetWeekday() (time.Weekday, error) {
+	if j.unit == weeks {
+		return j.startDay, nil
+	}
+	return time.Sunday, ErrNotScheduledWeekday
 }
diff --git a/scheduler.go b/scheduler.go
index 116fe5a..4c93c30 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -7,15 +7,15 @@ import (
 	"time"
 )
 
-// Scheduler struct, the only data member is the list of jobs.
-// - implements the sort.Interface{} for sorting jobs, by the time nextRun
+// Scheduler struct stores a list of Jobs and the location of time Scheduler
+// Scheduler implements the sort.Interface{} for sorting Jobs, by the time of nextRun
 type Scheduler struct {
 	jobs []*Job
 	loc  *time.Location
 	time timeHelper // an instance of timeHelper to interact with the time package
 }
 
-// NewScheduler creates a new scheduler
+// NewScheduler creates a new Scheduler
 func NewScheduler(loc *time.Location) *Scheduler {
 	return &Scheduler{
 		jobs: newEmptyJobSlice(),
@@ -24,8 +24,7 @@ func NewScheduler(loc *time.Location) *Scheduler {
 	}
 }
 
-// Start all the pending jobs
-// Add seconds ticker
+// Start all the pending Jobs using a per second ticker
 func (s *Scheduler) Start() chan struct{} {
 	stopped := make(chan struct{})
 	ticker := s.time.NewTicker(1 * time.Second)
@@ -50,10 +49,12 @@ func (s *Scheduler) Jobs() []*Job {
 	return s.jobs
 }
 
+// Len returns the number of Jobs in the Scheduler
 func (s *Scheduler) Len() int {
 	return len(s.jobs)
 }
 
+// Swap
 func (s *Scheduler) Swap(i, j int) {
 	s.jobs[i], s.jobs[j] = s.jobs[j], s.jobs[i]
 }
@@ -67,7 +68,7 @@ func (s *Scheduler) SetLocation(newLocation *time.Location) {
 	s.loc = newLocation
 }
 
-// scheduleNextRun Compute the instant when this job should run next
+// scheduleNextRun Compute the instant when this Job should run next
 func (s *Scheduler) scheduleNextRun(j *Job) error {
 	now := s.time.Now().In(s.loc)
 	if j.lastRun == s.time.Unix(0, 0) {
@@ -99,7 +100,7 @@ func (s *Scheduler) scheduleNextRun(j *Job) error {
 		j.nextRun = j.nextRun.Add(j.atTime)
 	}
 
-	// advance to next possible schedule
+	// advance to next possible Schedule
 	for j.nextRun.Before(now) || j.nextRun.Before(j.lastRun) {
 		j.nextRun = j.nextRun.Add(periodDuration)
 	}
@@ -112,7 +113,7 @@ func (s *Scheduler) roundToMidnight(t time.Time) time.Time {
 	return s.time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, s.loc)
 }
 
-// Get the current runnable jobs, which shouldRun is True
+// Get the current runnable Jobs, which shouldRun is True
 func (s *Scheduler) getRunnableJobs() []*Job {
 	var runnableJobs []*Job
 	sort.Sort(s)
@@ -126,7 +127,7 @@ func (s *Scheduler) getRunnableJobs() []*Job {
 	return runnableJobs
 }
 
-// NextRun datetime when the next job should run.
+// NextRun datetime when the next Job should run.
 func (s *Scheduler) NextRun() (*Job, time.Time) {
 	if len(s.jobs) <= 0 {
 		return nil, s.time.Now()
@@ -135,14 +136,14 @@ func (s *Scheduler) NextRun() (*Job, time.Time) {
 	return s.jobs[0], s.jobs[0].nextRun
 }
 
-// Every schedule a new periodic job with interval
+// Every schedules a new periodic Job with interval
 func (s *Scheduler) Every(interval uint64) *Scheduler {
 	job := NewJob(interval)
 	s.jobs = append(s.jobs, job)
 	return s
 }
 
-// RunPending runs all the jobs that are scheduled to run.
+// RunPending runs all the Jobs that are scheduled to run.
 func (s *Scheduler) RunPending() {
 	runnableJobs := s.getRunnableJobs()
 	for _, job := range runnableJobs {
@@ -168,12 +169,12 @@ func (s *Scheduler) runJob(job *Job) error {
 	return nil
 }
 
-// RunAll run all jobs regardless if they are scheduled to run or not
+// RunAll run all Jobs regardless if they are scheduled to run or not
 func (s *Scheduler) RunAll() {
 	s.RunAllWithDelay(0)
 }
 
-// RunAllWithDelay runs all jobs with delay seconds
+// RunAllWithDelay runs all Jobs with delay seconds
 func (s *Scheduler) RunAllWithDelay(d int) {
 	for _, job := range s.jobs {
 		err := s.runJob(job)
@@ -184,14 +185,14 @@ func (s *Scheduler) RunAllWithDelay(d int) {
 	}
 }
 
-// Remove specific job j by function
+// Remove specific Job j by function
 func (s *Scheduler) Remove(j interface{}) {
 	s.removeByCondition(func(someJob *Job) bool {
 		return someJob.jobFunc == getFunctionName(j)
 	})
 }
 
-// RemoveByRef removes specific job j by reference
+// RemoveByRef removes specific Job j by reference
 func (s *Scheduler) RemoveByRef(j *Job) {
 	s.removeByCondition(func(someJob *Job) bool {
 		return someJob == j
@@ -214,7 +215,7 @@ func removeAtIndex(jobs []*Job, i int) []*Job {
 	return jobs
 }
 
-// Scheduled checks if specific job j was already added
+// Scheduled checks if specific Job j was already added
 func (s *Scheduler) Scheduled(j interface{}) bool {
 	for _, job := range s.jobs {
 		if job.jobFunc == getFunctionName(j) {
@@ -224,7 +225,7 @@ func (s *Scheduler) Scheduled(j interface{}) bool {
 	return false
 }
 
-// Clear delete all scheduled jobs
+// Clear delete all scheduled Jobs
 func (s *Scheduler) Clear() {
 	s.jobs = newEmptyJobSlice()
 }
@@ -234,7 +235,7 @@ func newEmptyJobSlice() []*Job {
 	return make([]*Job, 0, initialCapacity)
 }
 
-// Do specifies the jobFunc that should be called every time the job runs
+// Do specifies the jobFunc that should be called every time the Job runs
 func (s *Scheduler) Do(jobFun interface{}, params ...interface{}) (*Job, error) {
 	j := s.getCurrentJob()
 	if j.err != nil {
@@ -260,9 +261,7 @@ func (s *Scheduler) Do(jobFun interface{}, params ...interface{}) (*Job, error)
 	return j, nil
 }
 
-// At schedules job at specific time of day
-// s.Every(1).Day().At("10:30:01").Do(task)
-// s.Every(1).Monday().At("10:30:01").Do(task)
+// At schedules the Job at a specific time of day in the form "HH:MM:SS" or "HH:MM"
 func (s *Scheduler) At(t string) *Scheduler {
 	j := s.getCurrentJob()
 	hour, min, sec, err := formatTime(t)
@@ -275,19 +274,13 @@ func (s *Scheduler) At(t string) *Scheduler {
 	return s
 }
 
-// GetAt returns the specific time of day the job will run at
-//	s.Every(1).Day().At("10:30").GetAt() == "10:30"
-func (j *Job) GetAt() string {
-	return fmt.Sprintf("%d:%d", j.atTime/time.Hour, (j.atTime%time.Hour)/time.Minute)
-}
-
-// StartAt schedules the next run of the job
+// StartAt schedules the next run of the Job
 func (s *Scheduler) StartAt(t time.Time) *Scheduler {
 	s.getCurrentJob().nextRun = t
 	return s
 }
 
-// StartImmediately sets the jobs next run as soon as the scheduler starts
+// StartImmediately sets the Jobs next run as soon as the scheduler starts
 func (s *Scheduler) StartImmediately() *Scheduler {
 	job := s.getCurrentJob()
 	job.nextRun = s.time.Now().In(s.loc)
@@ -295,108 +288,107 @@ func (s *Scheduler) StartImmediately() *Scheduler {
 	return s
 }
 
-// setUnit sets unit type
+// setUnit sets the unit type
 func (s *Scheduler) setUnit(unit timeUnit) {
 	currentJob := s.getCurrentJob()
 	currentJob.unit = unit
 }
 
-// Seconds set the unit with seconds
+// Second sets the unit with seconds
+func (s *Scheduler) Second() *Scheduler {
+	return s.Seconds()
+}
+
+// Seconds sets the unit with seconds
 func (s *Scheduler) Seconds() *Scheduler {
 	s.setUnit(seconds)
 	return s
 }
 
-// Minutes set the unit with minute
+// Minute sets the unit with minutes
+func (s *Scheduler) Minute() *Scheduler {
+	return s.Minutes()
+}
+
+// Minutes sets the unit with minutes
 func (s *Scheduler) Minutes() *Scheduler {
 	s.setUnit(minutes)
 	return s
 }
 
-// Hours set the unit with hours
+// Hour sets the unit with hours
+func (s *Scheduler) Hour() *Scheduler {
+	return s.Hours()
+}
+
+// Hours sets the unit with hours
 func (s *Scheduler) Hours() *Scheduler {
 	s.setUnit(hours)
 	return s
 }
 
-// Second sets the unit with second
-func (s *Scheduler) Second() *Scheduler {
-	return s.Seconds()
-}
-
-// Minute sets the unit  with minute, which interval is 1
-func (s *Scheduler) Minute() *Scheduler {
-	return s.Minutes()
-}
-
-// Hour sets the unit with hour, which interval is 1
-func (s *Scheduler) Hour() *Scheduler {
-	return s.Hours()
-}
-
-// Day sets the job's unit with day, which interval is 1
+// Day sets the unit with days
 func (s *Scheduler) Day() *Scheduler {
 	s.setUnit(days)
 	return s
 }
 
-// Days set the job's unit with days
+// Days set the unit with days
 func (s *Scheduler) Days() *Scheduler {
 	s.setUnit(days)
 	return s
 }
 
-// Week sets the job's unit with week, which interval is 1
+// Week sets the unit with weeks
 func (s *Scheduler) Week() *Scheduler {
 	s.setUnit(weeks)
 	return s
 }
 
-// Weeks sets the job's unit with weeks
+// Weeks sets the unit with weeks
 func (s *Scheduler) Weeks() *Scheduler {
 	s.setUnit(weeks)
 	return s
 }
 
-// Weekday start job on specific Weekday
+// Weekday sets the start with a specific weekday weekday
 func (s *Scheduler) Weekday(startDay time.Weekday) *Scheduler {
 	s.getCurrentJob().startDay = startDay
 	s.setUnit(weeks)
 	return s
 }
 
-// Monday set the start day with Monday
-// - s.Every(1).Monday().Do(task)
+// Monday sets the start day as Monday
 func (s *Scheduler) Monday() *Scheduler {
 	return s.Weekday(time.Monday)
 }
 
-// Tuesday sets the job start day Tuesday
+// Tuesday sets the start day as Tuesday
 func (s *Scheduler) Tuesday() *Scheduler {
 	return s.Weekday(time.Tuesday)
 }
 
-// Wednesday sets the job start day Wednesday
+// Wednesday sets the start day as Wednesday
 func (s *Scheduler) Wednesday() *Scheduler {
 	return s.Weekday(time.Wednesday)
 }
 
-// Thursday sets the job start day Thursday
+// Thursday sets the start day as Thursday
 func (s *Scheduler) Thursday() *Scheduler {
 	return s.Weekday(time.Thursday)
 }
 
-// Friday sets the job start day Friday
+// Friday sets the start day as Friday
 func (s *Scheduler) Friday() *Scheduler {
 	return s.Weekday(time.Friday)
 }
 
-// Saturday sets the job start day Saturday
+// Saturday sets the start day as Saturday
 func (s *Scheduler) Saturday() *Scheduler {
 	return s.Weekday(time.Saturday)
 }
 
-// Sunday sets the job start day Sunday
+// Sunday sets the start day as Sunday
 func (s *Scheduler) Sunday() *Scheduler {
 	return s.Weekday(time.Sunday)
 }
@@ -405,7 +397,7 @@ func (s *Scheduler) getCurrentJob() *Job {
 	return s.jobs[len(s.jobs)-1]
 }
 
-// Lock prevents job to run from multiple instances of gocron
+// Lock prevents Job to run from multiple instances of gocron
 func (s *Scheduler) Lock() *Scheduler {
 	s.getCurrentJob().lock = true
 	return s


Test patch:
diff --git a/example_test.go b/example_test.go
new file mode 100644
index 0000000..272c2e9
--- /dev/null
+++ b/example_test.go
@@ -0,0 +1,27 @@
+package gocron_test
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/go-co-op/gocron"
+)
+
+func ExampleScheduler_Start() {
+	s := gocron.NewScheduler(time.UTC)
+	s.Every(3).Seconds().Do(func() { fmt.Println("I am a task") })
+	<-s.Start()
+}
+
+func ExampleScheduler_At() {
+	s := gocron.NewScheduler(time.UTC)
+	s.Every(1).Day().At("10:30").Do(func() { fmt.Println("I am a task") })
+	s.Every(1).Monday().At("10:30:01").Do(func() { fmt.Println("I am a task") })
+}
+
+func ExampleJob_GetScheduledTime() {
+	s := gocron.NewScheduler(time.UTC)
+	job, _ := s.Every(1).Day().At("10:30").Do(func() { fmt.Println("I am a task") })
+	fmt.Println(job.GetScheduledTime())
+	// Output: 10:30
+}
diff --git a/job_test.go b/job_test.go
index adcf883..613479d 100644
--- a/job_test.go
+++ b/job_test.go
@@ -20,12 +20,36 @@ func TestTags(t *testing.T) {
 	assert.ElementsMatch(t, j.Tags(), []string{"tags", "tag", "some"})
 }
 
-func TestGetAt(t *testing.T) {
+func TestGetScheduledTime(t *testing.T) {
 	j, _ := NewScheduler(time.UTC).Every(1).Minute().At("10:30").Do(task)
-	assert.Equal(t, "10:30", j.GetAt())
+	assert.Equal(t, "10:30", j.GetScheduledTime())
 }
 
 func TestGetWeekday(t *testing.T) {
-	j, _ := NewScheduler(time.UTC).Every(1).Weekday(time.Wednesday).Do(task)
-	assert.Equal(t, time.Wednesday, j.GetWeekday())
+	s := NewScheduler(time.UTC)
+	weedayJob, _ := s.Every(1).Weekday(time.Wednesday).Do(task)
+	nonWeekdayJob, _ := s.Every(1).Minute().Do(task)
+
+	testCases := []struct {
+		desc            string
+		job             *Job
+		expectedWeekday time.Weekday
+		expectedError   error
+	}{
+		{"success", weedayJob, time.Wednesday, nil},
+		{"fail - not set for weekday", nonWeekdayJob, time.Sunday, ErrNotScheduledWeekday},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			weekday, err := tc.job.GetWeekday()
+			if tc.expectedError != nil {
+				assert.Error(t, tc.expectedError, err)
+			} else {
+				assert.Nil(t, err)
+			}
+
+			assert.Equal(t, tc.expectedWeekday, weekday)
+		})
+	}
 }