Example #1
0
// parseConf parses a requested file and returns
// it in a form of conf structure.
func parseConf(file string) *conf {
	var err error
	c := &conf{
		watch: map[string]func(){},
	}

	// Parse the configuration file..
	c.m, err = parseFile(file)
	log.AssertNil(err)

	// Extract init tasks.
	init, err := parseSlice(c.m, initSection)
	log.AssertNil(err)
	c.init, err = c.processTasksFn(init, initSection)
	log.AssertNil(err)

	// Extract patterns and tasks from watch section of config file.
	watch, err := parseMap(c.m, watchSection)
	log.AssertNil(err)
	for pattern, tasks := range watch {
		section := watchSection + ":" + pattern // It is used for debug messages.
		c.watch[pattern], err = c.processTasksFn(tasks, section)
		log.AssertNil(err)
	}

	log.Trace.Printf(`Config file "%s" has been parsed.`, file)
	return c
}
Example #2
0
File: main.go Project: gocore/goal
// Main is an entry point of the subcommand (tool).
func main(hs []tool.Handler, i int, args tool.Data) {
	// The first argument in the list is a path.
	// If it's missing use an empty string instead.
	p := args.GetDefault(0, "")

	// Prepare source and destination directory paths.
	src, err := path.ImportToAbsolute("github.com/colegion/goal/internal/skeleton")
	log.AssertNil(err)
	destImp, err := path.CleanImport(p)
	log.AssertNil(err)
	dest, err := path.ImportToAbsolute(destImp)
	log.AssertNil(err)

	// Make sure the requested import path (dest) does not exist yet.
	if _, err := os.Stat(dest); !os.IsNotExist(err) {
		log.Error.Panicf(`Cannot use "%s", such import path already exists.`, destImp)
	}

	// Scan the skeleton directory and get a list of directories / files
	// to be copied / processed.
	res, err := walk(src)
	log.AssertNil(err)

	// Create the directories in destination path.
	for i := 0; i < len(res.dirs); i++ {
		err = os.MkdirAll(filepath.Join(dest, res.dirs[i]), 0755)
		log.AssertNil(err)
	}

	// Copy static files to the destination directories.
	for i := 0; i < len(res.files); i++ {
		copyFile(res.files[i].absolute, filepath.Join(dest, res.files[i].relative))
	}

	// Process source files and copy to the destination directories.
	for i := 0; i < len(res.srcs); i++ {
		copyModifiedFile(
			res.srcs[i].absolute, filepath.Join(dest, res.srcs[i].relative), [][][]byte{
				{
					[]byte("github.com/colegion/goal/internal/skeleton"),
					[]byte(destImp),
				},
			},
		)
	}

	log.Info.Printf(info, destImp)
}
Example #3
0
File: main.go Project: gocore/goal
// main is an entry point of the "run" subcommand (tool).
func main(hs []tool.Handler, i int, args tool.Data) {
	// The first argument in the list is a path.
	// If it's missing use an empty string instead.
	p := args.GetDefault(0, "")

	// Determine import path and absolute path of the project to run.
	imp, err := path.CleanImport(p)
	log.AssertNil(err)
	dir, err := path.ImportToAbsolute(imp)
	log.AssertNil(err)

	// Prepare a path of configuration file.
	cf := filepath.Join(dir, ConfigFile)

	// Start a user tasks runner and instances controller.
	go instanceController()

	// Start a configuration file watcher.
	go configDaemon(imp, cf)

	// Show user friendly errors and terminate subprograms
	// in case of panics.
	defer func() {
		channel <- message{
			action: "exit",
		}
		<-stopped
		log.Trace.Panicln("Application has been terminated.")
	}()

	// Execute all commands from the requested directory.
	curr, _ := os.Getwd()
	os.Chdir(dir) // pushd
	defer func() {
		// Going back to the initial directory.
		os.Chdir(curr) // popd
	}()

	// Load the configuration.
	reloadConfig()

	// Cleaning up after we are done.
	signal.Notify(notify, os.Interrupt, syscall.SIGTERM)
	<-notify
}
Example #4
0
// NewType reads the requested template and returns an output.Type
// with initialized Template field. << and >> are used as delimiters.
// "\" + "\n" sequences are removed from the template so newline
// elision is supported. Moreover, ":" + "\t" are removed too for
// a possibility of a better code formatting.
func NewType(pkg, templatePath string) Type {
	// Read the template file, cut all "\" + line break.
	f, err := ioutil.ReadFile(templatePath)
	log.AssertNil(err)
	s := strings.Replace(string(f), "\\\r\n", "", -1)
	s = strings.Replace(s, "\\\n", "", -1)
	s = strings.Replace(s, "\\\r", "", -1)
	s = strings.Replace(s, ":\t", "", -1)

	// Allocate a new type, initialize template, then return.
	// Use <@ and > as delimiters, add template helper functions.
	n := filepath.Base(templatePath)
	t, err := template.New(n).Delims("<@", ">").Funcs(funcs).Parse(s)
	log.AssertNil(err)
	return Type{
		Package:      pkg,
		TemplateName: n,
		Template:     t,
	}
}
Example #5
0
// processPackage gets an import path of a package, processes it, and
// extracts controllers + actions.
func (ps packages) processPackage(importPath string) {
	log.Trace.Printf(`Parsing "%s"...`, importPath)
	dir, err := path.ImportToAbsolute(importPath)
	log.AssertNil(err)
	p := reflect.ParseDir(dir, false)
	cs := ps.extractControllers(p)
	if len(cs.data) > 0 {
		ps[importPath] = controllers{
			data: cs.data,
			init: ps.extractInitFunc(p),
		}
	}
}
Example #6
0
File: copy.go Project: gocore/goal
// copyFile reads a source file and copies it to the destination.
// It doesn't check whether input source parameter is a regular file
// rather than a directory.
func copyFile(src, dst string) {
	// Open source file.
	sf, err := os.Open(src)
	log.AssertNil(err)
	defer sf.Close() // Make sure the file will be closed.

	// Get the meta info of the source file.
	info, err := sf.Stat()
	log.AssertNil(err)

	// Create a destination file.
	df, err := os.Create(dst)
	log.AssertNil(err)
	defer df.Close() // Make sure it will be closed at the end.

	// Copy the content of source to destination.
	_, err = io.Copy(df, sf)
	log.AssertNil(err)

	// Set the chmod of source to destination file.
	err = os.Chmod(dst, info.Mode())
	log.AssertNil(err)
}
Example #7
0
File: copy.go Project: gocore/goal
// copyModifiedFile is similar to copyFile except it takes changes of type
// [][][]byte as the third argument.
// Example:
//	[][][]byte{
//		[][]byte{
//			[]byte("key"), []byte("value"),
//		}
//	}
// Keys are what should be replaced and their values are the replacements.
func copyModifiedFile(src, dst string, changes [][][]byte) {
	// Open source file.
	sf, err := os.Open(src)
	log.AssertNil(err)
	defer sf.Close() // Make sure the file will be closed.

	// Get the meta info of the source file.
	info, err := sf.Stat()
	log.AssertNil(err)

	// Read its content.
	d := make([]byte, info.Size())
	_, err = sf.Read(d)
	log.AssertNil(err)

	// Make the required changes.
	for i := 0; i < len(changes); i++ {
		d = bytes.Replace(d, changes[i][0], changes[i][1], -1)
	}

	// Write the content to the destination file.
	err = ioutil.WriteFile(dst, d, info.Mode())
	log.AssertNil(err)
}
Example #8
0
// ListenFile is equivalent of Listen but for files.
// If file is added using ListenFile and the same file
// is within a pattern of Listen, only the first one
// will trigger restarts.
// I.e. we have the following calls:
//	w.Listen("./", fn1)
//	w.ListenFile("./goal.yml", fn2)
// If "goal.yml" file is modified fn2 will be triggered.
// fn1 may be triggered by changes in any file inside
// "./" directory except "goal.yml".
func (t *Type) ListenFile(path string, fn func()) *fsnotify.Watcher {
	// Create a new watcher.
	w, err := fsnotify.NewWatcher()
	log.AssertNil(err)

	// Watch a directory instead of file.
	// See issue #17 of fsnotify to find out more
	// why we do this.
	dir := filepath.Dir(path)
	w.Add(dir)

	// Clean path and replace back slashes
	// to the normal ones.
	path = filepath.ToSlash(path)

	// Start watching process.
	t.files[path] = true
	go t.NotifyOnUpdate(path, w, fn)
	return w
}
Example #9
0
// Listen gets a pattern and a function. The function will be executed
// when files matching the pattern will be modified.
func (t *Type) Listen(pattern string, fn func()) *fsnotify.Watcher {
	// Create a new watcher.
	w, err := fsnotify.NewWatcher()
	log.AssertNil(err)

	// Find directories matching the pattern.
	ds := glob(pattern)

	// Add the files to the watcher.
	for i := range ds {
		log.Trace.Printf(`Adding "%s" to the list of watched directories...`, ds[i])
		err := w.Add(ds[i])
		if err != nil {
			log.Warn.Println(err)
		}
	}

	// Start watching process.
	go t.NotifyOnUpdate(filepath.ToSlash(pattern), w, fn)
	return w
}
Example #10
0
// start is an entry point of the generate handlers command.
func start() {
	// Clean the out directory.
	log.Trace.Printf(`Removing "%s" directory if already exists...`, *output)
	err := os.RemoveAll(*output)
	log.AssertNil(err)

	// Start processing of controllers.
	ps := packages{}
	absInput, err := path.ImportToAbsolute(*input)
	log.AssertNil(err)
	absImport, err := path.AbsoluteToImport(absInput)
	log.AssertNil(err)
	absOutput, err := path.ImportToAbsolute(*output)
	log.AssertNil(err)
	absImportOut, err := path.AbsoluteToImport(absOutput)
	log.AssertNil(err)
	log.Trace.Printf(`Processing "%s" package...`, absImport)
	ps.processPackage(absImport)

	// Start generation of handler packages.
	tpl, err := path.ImportToAbsolute("github.com/colegion/goal/tools/generate/handlers/handlers.go.template")
	log.AssertNil(err)
	t := generation.NewType("", tpl)
	t.Extension = ".go" // Save generated files as a .go source.

	// Iterate through all available packages and generate handlers for them.
	log.Trace.Printf(`Starting generation of "%s" package...`, *pkg)
	for imp := range ps {
		// Check whether current package is the main one
		// and should be stored at the root directory or it is a subpackage.
		//
		// I.e. if --input is "./controllers" and --output is "./assets/handlers",
		// we are saving processed "./controllers" package to "./assets/handlers"
		// and some "github.com/colegion/smth" to "./assets/handlers/github.com/colegion/smth".
		out := *output
		if imp != absImport {
			out = filepath.Join(out, imp)
		}
		t.CreateDir(out)

		// Iterate over all available controllers, generate handlers package on
		// every of them.
		n := 0
		for name := range ps[imp].data {
			// Find parent controllers of this controller.
			cs := []parent{}
			for i, p := range ps[imp].data[name].Parents {
				// Make sure it is a controller rather than just some embedded struct.
				check := p.Import
				if check == "" { // Embedded parent is a local structure.
					check = absImport
				}
				if _, ok := ps[check]; !ok { // Such package is not in the list of scanned ones.
					continue
				}
				if _, ok := ps[check].data[p.Name]; !ok { // There is no such controller.
					continue
				}

				// It is a valid parent controller, add it to the list.
				cs = append(cs, parent{
					ID:     i,
					Import: p.Import,
					Name:   p.Name,
				})
			}

			// Initialize parameters and generate a package.
			t.Package = strings.ToLower(name)
			t.Context = map[string]interface{}{
				"after":     action.MethodAfter,
				"before":    action.MethodBefore,
				"initially": method.InitiallyName,
				"finally":   method.FinallyName,

				"controller":   ps[imp].data[name],
				"controllers":  ps[imp].data,
				"import":       imp,
				"input":        input,
				"name":         name,
				"outputImport": absImportOut,
				"output":       output,
				"package":      pkg,
				"parents":      cs,
				"initFunc":     ps[imp].init,
				"num":          n,

				"actionImport":    action.InterfaceImport,
				"actionInterface": action.Interface,
				"strconv":         action.StrconvContext,
			}
			t.Generate()
			n++
		}
	}
}