Skip to content

Commit

Permalink
Massive refactoring
Browse files Browse the repository at this point in the history
- Move watch functionality inot a new "watch" module
- Move variable substitution into varcmd
  • Loading branch information
cortesi committed Feb 2, 2016
1 parent 23e3d84 commit ddb9858
Show file tree
Hide file tree
Showing 13 changed files with 127 additions and 110 deletions.
15 changes: 8 additions & 7 deletions cmd/modd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/cortesi/modd"
"github.com/cortesi/modd/conf"
"github.com/cortesi/modd/notify"
"github.com/cortesi/modd/watch"
"github.com/cortesi/termlog"
"gopkg.in/alecthomas/kingpin.v2"
)
Expand Down Expand Up @@ -53,7 +54,7 @@ var debug = kingpin.Flag("debug", "Debugging for modd development").
// Returns a (continue, error) tuple. If continue is true, execution of the
// remainder of the block should proceed. If error is not nil, modd should
// exit.
func prepsAndNotify(b conf.Block, vars map[string]string, lmod *modd.Mod, log termlog.TermLog) (bool, error) {
func prepsAndNotify(b conf.Block, vars map[string]string, lmod *watch.Mod, log termlog.TermLog) (bool, error) {
err := modd.RunPreps(b, vars, lmod, log)
if pe, ok := err.(modd.ProcError); ok {
if *beep {
Expand All @@ -75,9 +76,9 @@ func prepsAndNotify(b conf.Block, vars map[string]string, lmod *modd.Mod, log te
}

func run(log termlog.TermLog, cnf *conf.Config, watchconf string) *conf.Config {
modchan := make(chan *modd.Mod, 1024)
modchan := make(chan *watch.Mod, 1024)
if *ignores {
for _, patt := range modd.CommonExcludes {
for _, patt := range watch.CommonExcludes {
fmt.Println(patt)
}
os.Exit(0)
Expand All @@ -86,7 +87,7 @@ func run(log termlog.TermLog, cnf *conf.Config, watchconf string) *conf.Config {
daemonPens := make([]*modd.DaemonPen, len(cnf.Blocks))
for i, b := range cnf.Blocks {
if !b.NoCommonFilter {
b.Exclude = append(b.Exclude, modd.CommonExcludes...)
b.Exclude = append(b.Exclude, watch.CommonExcludes...)
}
cnf.Blocks[i] = b

Expand Down Expand Up @@ -119,7 +120,7 @@ func run(log termlog.TermLog, cnf *conf.Config, watchconf string) *conf.Config {

// FIXME: This takes a long time. We could start it in parallel with the
// first process run in a goroutine
watcher, err := modd.Watch(watchpaths, lullTime, modchan)
watcher, err := watch.Watch(watchpaths, lullTime, modchan)
defer watcher.Stop()
if err != nil {
kingpin.Fatalf("Fatal error: %s", err)
Expand Down Expand Up @@ -169,13 +170,13 @@ func run(log termlog.TermLog, cnf *conf.Config, watchconf string) *conf.Config {
}

func main() {
kingpin.Version(modd.Version)
kingpin.Version(watch.Version)
kingpin.Parse()

log := termlog.NewLog()
if *debug {
log.Enable("debug")
modd.Logger = log
watch.Logger = log
}

ret, err := ioutil.ReadFile(*file)
Expand Down
14 changes: 7 additions & 7 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (b *Block) addPrep(command string, options []string) error {
// Config represents a complete configuration
type Config struct {
Blocks []Block
Variables map[string]string
variables map[string]string
}

// Equals checks if this Config equals another
Expand All @@ -94,8 +94,8 @@ func (c *Config) Equals(other *Config) bool {
return false
}
}
if (c.Variables != nil || len(c.Variables) != 0) || (other.Variables != nil || len(other.Variables) != 0) {
if !reflect.DeepEqual(c.Variables, other.Variables) {
if (c.variables != nil || len(c.variables) != 0) || (other.variables != nil || len(other.variables) != 0) {
if !reflect.DeepEqual(c.variables, other.variables) {
return false
}
}
Expand All @@ -121,17 +121,17 @@ func (c *Config) addBlock(b Block) {
}

func (c *Config) addVariable(key string, value string) error {
if c.Variables == nil {
c.Variables = map[string]string{}
if c.variables == nil {
c.variables = map[string]string{}
}
c.Variables[key] = value
c.variables[key] = value
return nil
}

// GetVariables returns a copy of the Variables map
func (c *Config) GetVariables() map[string]string {
n := map[string]string{}
for k, v := range c.Variables {
for k, v := range c.variables {
n[k] = v
}
return n
Expand Down
8 changes: 4 additions & 4 deletions conf/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ var parseTests = []struct {
Include: []string{"foo"},
},
},
Variables: map[string]string{
variables: map[string]string{
"@var": "bar",
},
},
Expand All @@ -202,7 +202,7 @@ var parseTests = []struct {
Include: []string{"foo"},
},
},
Variables: map[string]string{
variables: map[string]string{
"@var": "bar\nvoing",
},
},
Expand All @@ -215,7 +215,7 @@ var parseTests = []struct {
Include: []string{"foo"},
},
},
Variables: map[string]string{
variables: map[string]string{
"@var": "bar",
},
},
Expand All @@ -228,7 +228,7 @@ var parseTests = []struct {
Include: []string{"foo"},
},
},
Variables: map[string]string{
variables: map[string]string{
"@var": "bar",
"@oink": "foo",
},
Expand Down
2 changes: 1 addition & 1 deletion notify/notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "os/exec"
const prog = "modd"

func hasExecutable(name string) bool {
_, err := exec.LookPath("growlnotify")
_, err := exec.LookPath(name)
if err != nil {
return false
}
Expand Down
40 changes: 6 additions & 34 deletions proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,18 @@ import (
"time"

"github.com/cortesi/modd/conf"
"github.com/cortesi/modd/filter"
"github.com/cortesi/modd/varcmd"
"github.com/cortesi/modd/watch"
"github.com/cortesi/termlog"
)

const moddVar = "@mods"
const moddMarker = "|MODD|"

// MinRestart is the minimum amount of time between daemon restarts
const MinRestart = 1 * time.Second

const lineLimit = 80

// quotePath quotes a path for use on the command-line
func quotePath(path string) string {
path = strings.Replace(path, "\"", "\\\"", -1)
return "\"" + path + "\""
}

// mkArgs prepares a list of paths for the command line
func mkArgs(paths []string) string {
escaped := make([]string, len(paths))
for i, s := range paths {
escaped[i] = quotePath(s)
}
return strings.Join(escaped, " ")
}

// shortCommand shortens a command to a name we can use in a notification
// header.
func shortCommand(command string) string {
Expand Down Expand Up @@ -134,23 +118,10 @@ func RunProc(cmd string, log termlog.Stream) error {
}

// RunPreps runs all commands in sequence. Stops if any command returns an error.
func RunPreps(b conf.Block, vars map[string]string, mod *Mod, log termlog.TermLog) error {
func RunPreps(b conf.Block, vars map[string]string, mod *watch.Mod, log termlog.TermLog) error {
vcmd := varcmd.VarCmd{&b, mod, vars}
for _, p := range b.Preps {
if varcmd.HasVar(p.Command, moddVar) {
if mod == nil {
// First run - only do the expensive find once
if _, ok := vars[moddVar]; !ok {
modified, err := filter.Find(".", b.Include, b.Exclude)
if err != nil {
return err
}
vars[moddVar] = mkArgs(modified)
}
} else {
vars[moddVar] = mkArgs(mod.All())
}
}
cmd, err := varcmd.Render(p.Command, vars)
cmd, err := vcmd.Render(p.Command)
if err != nil {
return err
}
Expand Down Expand Up @@ -181,7 +152,8 @@ func (d *daemon) Run() {
lastStart = time.Now()
sh := getShell()

finalcmd, err := varcmd.Render(d.conf.Command, d.vars)
vcmd := varcmd.VarCmd{nil, nil, d.vars}
finalcmd, err := vcmd.Render(d.conf.Command)
if err != nil {
d.log.Shout("%s", err)
continue
Expand Down
18 changes: 0 additions & 18 deletions proc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,3 @@ func TestShortCommand(t *testing.T) {
}
}
}

var quotePathTests = []struct {
path string
expected string
}{
{`one`, `"one"`},
{` one`, `" one"`},
{`one `, `"one "`},
}

func TestQuotePath(t *testing.T) {
for i, tst := range quotePathTests {
result := quotePath(tst.path)
if result != tst.expected {
t.Errorf("Test %d: expected\n%q\ngot\n%q", i, tst.expected, result)
}
}
}
6 changes: 3 additions & 3 deletions test/escaping.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

** {
prep: ls |MODD|
prep: echo |MODD|
prep: ls |MODD| |MODD|
prep: ls @mods
prep: echo @mods
prep: ls @mods @mods
}
Empty file added varcmd/tdir/tfile
Empty file.
59 changes: 47 additions & 12 deletions varcmd/varcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,70 @@ package varcmd
import (
"fmt"
"regexp"
"strings"

"github.com/cortesi/modd/conf"
"github.com/cortesi/modd/filter"
"github.com/cortesi/modd/watch"
)

var name = regexp.MustCompile(`@\w+`)

// Vars returns a list of all variables in the string
func Vars(cmd string) []string {
return name.FindAllString(cmd, -1)
// quotePath quotes a path for use on the command-line
func quotePath(path string) string {
path = strings.Replace(path, "\"", "\\\"", -1)
return "\"" + path + "\""
}

// mkArgs prepares a list of paths for the command line
func mkArgs(paths []string) string {
escaped := make([]string, len(paths))
for i, s := range paths {
escaped[i] = quotePath(s)
}
return strings.Join(escaped, " ")
}

// HasVar checks if the command has a given variable
func HasVar(cmd string, name string) bool {
for _, v := range Vars(cmd) {
if v == name {
return true
// VarCmd represents a set of variables for a specific block and mod set. It
// should be re-created anew each time the block is executed.
type VarCmd struct {
Block *conf.Block
Mod *watch.Mod
Vars map[string]string
}

// Get a variable by name
func (v *VarCmd) get(name string) (string, error) {
if val, ok := v.Vars[name]; ok {
return val, nil
}
if name == "@mods" && v.Block != nil {
var modified []string
if v.Mod == nil {
var err error
modified, err = filter.Find(".", v.Block.Include, v.Block.Exclude)
if err != nil {
return "", err
}
} else {
modified = v.Mod.All()
}
v.Vars[name] = mkArgs(modified)
return v.Vars[name], nil
}
return false
return "", fmt.Errorf("No such variable: %s", name)
}

// Render renders the command with a map of variables
func Render(cmd string, vars map[string]string) (string, error) {
func (v *VarCmd) Render(cmd string) (string, error) {
var err error
cmd = string(
name.ReplaceAllFunc(
[]byte(cmd),
func(key []byte) []byte {
ks := string(key)
val, ok := vars[ks]
if !ok {
val, errv := v.get(ks)
if errv != nil {
err = fmt.Errorf("No such variable: %s", ks)
return nil
}
Expand Down
Loading

0 comments on commit ddb9858

Please sign in to comment.