<commit_before>package qr

// In process queue with disk based overflow. Element order is not strictly
// preserved.
//
// When everything is fine elements flow over Qr.q. This is a simple channel
// directly connecting the producer(s) and the consumer(s).
// If that channel is full elements are written to the Qr.planb channel.
// swapout() will write all elements from Qr.planb to disk. That file is closed
// after `timeout`. At the same time swapin() will try to process old files: if
// there is at least a single completed file, swapin() will open that file and
// try to write the elements to Qr.q.
//
//   ---> Enqueue()    ---------   .q  --------->     Dequeue() --->
//             \                                           ^
//            .planb                                     .q
//               \--> swapout() --> fs() --> swapin() --/
//
//
// Usage:
//    q := New("/mnt/queues/", "demo", OptionBuffer(100))
//    defer q.Close()
//    go func() {
//        for e := range q.Dequeue() {
//           fmt.Printf("We got: %v\n", e)
//        }
//    }()
//
//    // elsewhere:
//    q.Enqueue("aap")
//    q.Enqueue("noot")

import (
	"encoding/gob"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

const (
	// DefaultTimeout can be changed with OptionTimeout.
	DefaultTimeout = 10 * time.Second
	// DefaultBuffer can be changed with OptionBuffer.
	DefaultBuffer = 1000

	fileExtension = ".qr"
)

// Qr is a disk-based queue.
type Qr struct {
	q          chan interface{} // the main channel.
	planb      chan interface{} // to disk, used when q is full.
	dir        string
	prefix     string
	timeout    time.Duration
	bufferSize int
	log        Logger
}

// Option is an option to New(), which can change some settings.
type Option func(qr *Qr)

// OptionTimeout is an option for New(). It specifies the time after which a queue
// file is closed. Smaller means more files.
func OptionTimeout(t time.Duration) Option {
	return func(qr *Qr) {
		qr.timeout = t
	}
}

// OptionBuffer is an option for New(). It specifies the in-memory size of the
// queue. Smaller means the disk will be used sooner, larger means more memory.
func OptionBuffer(n int) Option {
	return func(qr *Qr) {
		qr.bufferSize = n
	}
}

// OptionLogger is an option for New(). Is sets the logger, the default is the
// log module.
func OptionLogger(l Logger) Option {
	return func(qr *Qr) {
		qr.log = l
	}
}

// New starts a Queue which stores files in <dir>/<prefix>-.<timestamp>.qr
func New(dir, prefix string, options ...Option) *Qr {
	qr := Qr{
		planb:      make(chan interface{}),
		dir:        dir,
		prefix:     prefix,
		timeout:    DefaultTimeout,
		bufferSize: DefaultBuffer,
		log:        StdLog{},
	}
	for _, cb := range options {
		cb(&qr)
	}
	qr.q = make(chan interface{}, qr.bufferSize)

	var (
		filesToDisk   = make(chan string)
		filesFromDisk = make(chan string)
	)
	go qr.swapout(filesToDisk)
	go qr.fs(filesToDisk, filesFromDisk)
	go qr.swapin(filesFromDisk)
	for _, f := range qr.findOld() {
		filesFromDisk <- f
	}
	return &qr
}

// Enqueue adds something in the queue. This never blocks, and is safe to be
// called by different goroutines.
func (qr *Qr) Enqueue(e interface{}) {
	select {
	case qr.q <- e:
		return
	default:
	}
	qr.planb <- e
}

// Dequeue is the channel where elements come from. It'll be closed when we
// shut down.
func (qr *Qr) Dequeue() <-chan interface{} {
	return qr.q
}

// Close shuts down all Go routines and closes the Dequeue() channel. It'll
// write all in-flight entries to disk. Calling Enqueue() after Close will
// panic.
func (qr *Qr) Close() {
	// Closing planb triggers a cascade closing of all go-s and channels.
	close(qr.planb)

	// Store the in-flight entries for next time.
	filename := qr.batchFilename(time.Time{}) // special filename
	fh, err := os.Create(filename)
	if err != nil {
		qr.log.Printf("create err: %v", err)
		return
	}
	enc := gob.NewEncoder(fh)
	count := 0
	for e := range qr.q {
		count++
		if err = enc.Encode(&e); err != nil {
			qr.log.Printf("Encode error: %v", err)
		}
	}
	fh.Close()

	if count == 0 {
		// All this work, and there was nothing to queue...
		os.Remove(filename)
	}
}

func (qr *Qr) swapout(files chan<- string) {
	var (
		enc      *gob.Encoder
		filename string
		fh       io.WriteCloser
		tc       <-chan time.Time
		t        = time.NewTimer(0)
		n        int
		err      error
	)
	defer func() {
		if enc != nil {
			fh.Close()
			files <- filename
		}
		close(files)
	}()
	for {
		select {
		case e, ok := <-qr.planb:
			if !ok {
				return
			}
			if enc == nil {
				// open file
				filename = qr.batchFilename(time.Now().UTC())
				fh, err = os.Create(filename)
				if err != nil {
					// TODO: sure we return?
					qr.log.Printf("create err: %v\n", err)
					return
				}
				enc = gob.NewEncoder(fh)
				if n == 0 {
					t.Reset(qr.timeout)
					tc = t.C
				}
			}
			if err = enc.Encode(&e); err != nil {
				qr.log.Printf("Encode error: %v\n", err)
			}
			n++
		case <-tc:
			// time to close our file.
			fh.Close()
			enc = nil
			n = 0
			tc = nil
			files <- filename
		}
	}
}

func (qr *Qr) swapin(files <-chan string) {
	defer close(qr.q)
	for filename := range files {
		fh, err := os.Open(filename)
		if err != nil {
			qr.log.Printf("open err: %v\n", err)
			continue
		}
		os.Remove(filename)
		dec := gob.NewDecoder(fh)
		for {
			var next interface{}
			if err = dec.Decode(&next); err != nil {
				if err != io.EOF {
					qr.log.Printf("decode err: %v\n", err)
				}
				fh.Close()
				fh = nil
				break
			}
			qr.q <- next
		}
	}
}

func (qr *Qr) fs(in <-chan string, out chan<- string) {
	defer close(out)
	var (
		filenames []string
		checkOut  chan<- string
		next      string
	)
	for {
		select {
		case f, ok := <-in:
			if !ok {
				return
			}
			if checkOut == nil {
				checkOut = out
				next = f
			} else {
				filenames = append(filenames, f)
			}
		case checkOut <- next:
			if len(filenames) > 0 {
				next, filenames = filenames[0], filenames[1:]
			} else {
				// case disabled since there is no file
				checkOut = nil
			}
		}
	}
}

func (qr *Qr) batchFilename(t time.Time) string {
	format := "20060102T150405.999999999" // time.RFC3339Nano
	return fmt.Sprintf("%s/%s-%s%s",
		qr.dir,
		qr.prefix,
		t.Format(format),
		fileExtension,
	)
}

// findOld finds .qr files from a previous run.
func (qr *Qr) findOld() []string {
	f, err := os.Open(qr.dir)
	if err != nil {
		return nil
	}
	defer f.Close()

	names, err := f.Readdirnames(-1)
	if err != nil {
		return nil
	}

	var existing []string
	for _, n := range names {
		if !strings.HasPrefix(n, qr.prefix+"-") || !strings.HasSuffix(n, fileExtension) {
			continue
		}
		existing = append(existing, filepath.Join(qr.dir, n))
	}

	sort.Strings(existing)

	return existing
}
<commit_msg>minor simplifications<commit_after>package qr

// In process queue with disk based overflow. Element order is not strictly
// preserved.
//
// When everything is fine elements flow over Qr.q. This is a simple channel
// directly connecting the producer(s) and the consumer(s).
// If that channel is full elements are written to the Qr.planb channel.
// swapout() will write all elements from Qr.planb to disk. That file is closed
// after `timeout`. At the same time swapin() will try to process old files: if
// there is at least a single completed file, swapin() will open that file and
// try to write the elements to Qr.q.
//
//   ---> Enqueue()    ---------   .q  --------->     Dequeue() --->
//             \                                           ^
//            .planb                                     .q
//               \--> swapout() --> fs() --> swapin() --/
//
//
// Usage:
//    q := New("/mnt/queues/", "demo", OptionBuffer(100))
//    defer q.Close()
//    go func() {
//        for e := range q.Dequeue() {
//           fmt.Printf("We got: %v\n", e)
//        }
//    }()
//
//    // elsewhere:
//    q.Enqueue("aap")
//    q.Enqueue("noot")

import (
	"encoding/gob"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

const (
	// DefaultTimeout can be changed with OptionTimeout.
	DefaultTimeout = 10 * time.Second
	// DefaultBuffer can be changed with OptionBuffer.
	DefaultBuffer = 1000

	fileExtension = ".qr"
)

// Qr is a disk-based queue.
type Qr struct {
	q          chan interface{} // the main channel.
	planb      chan interface{} // to disk, used when q is full.
	dir        string
	prefix     string
	timeout    time.Duration
	bufferSize int
	log        Logger
}

// Option is an option to New(), which can change some settings.
type Option func(qr *Qr)

// OptionTimeout is an option for New(). It specifies the time after which a queue
// file is closed. Smaller means more files.
func OptionTimeout(t time.Duration) Option {
	return func(qr *Qr) {
		qr.timeout = t
	}
}

// OptionBuffer is an option for New(). It specifies the in-memory size of the
// queue. Smaller means the disk will be used sooner, larger means more memory.
func OptionBuffer(n int) Option {
	return func(qr *Qr) {
		qr.bufferSize = n
	}
}

// OptionLogger is an option for New(). Is sets the logger, the default is the
// log module.
func OptionLogger(l Logger) Option {
	return func(qr *Qr) {
		qr.log = l
	}
}

// New starts a Queue which stores files in <dir>/<prefix>-.<timestamp>.qr
func New(dir, prefix string, options ...Option) *Qr {
	qr := Qr{
		planb:      make(chan interface{}),
		dir:        dir,
		prefix:     prefix,
		timeout:    DefaultTimeout,
		bufferSize: DefaultBuffer,
		log:        StdLog{},
	}
	for _, cb := range options {
		cb(&qr)
	}
	qr.q = make(chan interface{}, qr.bufferSize)

	var (
		filesToDisk   = make(chan string)
		filesFromDisk = make(chan string)
	)
	go qr.swapout(filesToDisk)
	go qr.fs(filesToDisk, filesFromDisk)
	go qr.swapin(filesFromDisk)
	for _, f := range qr.findOld() {
		filesFromDisk <- f
	}
	return &qr
}

// Enqueue adds something in the queue. This never blocks, and is safe to be
// called by different goroutines.
func (qr *Qr) Enqueue(e interface{}) {
	select {
	case qr.q <- e:
	default:
		qr.planb <- e
	}
}

// Dequeue is the channel where elements come out the queue. It'll be closed
// on Close().
func (qr *Qr) Dequeue() <-chan interface{} {
	return qr.q
}

// Close shuts down all Go routines and closes the Dequeue() channel. It'll
// write all in-flight entries to disk. Calling Enqueue() after Close will
// panic.
func (qr *Qr) Close() {
	// Closing planb triggers a cascade closing of all go-s and channels.
	close(qr.planb)

	// Store the in-flight entries for next time.
	filename := qr.batchFilename(time.Time{}) // special filename
	fh, err := os.Create(filename)
	if err != nil {
		qr.log.Printf("create err: %v", err)
		return
	}
	enc := gob.NewEncoder(fh)
	count := 0
	for e := range qr.q {
		count++
		if err = enc.Encode(&e); err != nil {
			qr.log.Printf("Encode error: %v", err)
		}
	}
	fh.Close()
	if count == 0 {
		// there was nothing to queue
		os.Remove(filename)
	}
}

func (qr *Qr) swapout(files chan<- string) {
	var (
		enc      *gob.Encoder
		filename string
		fh       io.WriteCloser
		tc       <-chan time.Time
		t        = time.NewTimer(0)
		err      error
	)
	defer func() {
		if enc != nil {
			fh.Close()
			files <- filename
		}
		close(files)
	}()
	for {
		select {
		case e, ok := <-qr.planb:
			if !ok {
				return
			}
			if enc == nil {
				filename = qr.batchFilename(time.Now().UTC())
				fh, err = os.Create(filename)
				if err != nil {
					// TODO: sure we return?
					qr.log.Printf("create err: %v\n", err)
					return
				}
				enc = gob.NewEncoder(fh)
				t.Reset(qr.timeout)
				tc = t.C
			}
			if err = enc.Encode(&e); err != nil {
				qr.log.Printf("Encode error: %v\n", err)
			}
		case <-tc:
			fh.Close()
			files <- filename
			enc = nil
			tc = nil
		}
	}
}

func (qr *Qr) swapin(files <-chan string) {
	defer close(qr.q)
	for filename := range files {
		fh, err := os.Open(filename)
		if err != nil {
			qr.log.Printf("open err: %v\n", err)
			continue
		}
		os.Remove(filename)
		dec := gob.NewDecoder(fh)
		for {
			var next interface{}
			if err = dec.Decode(&next); err != nil {
				if err != io.EOF {
					qr.log.Printf("decode err: %v\n", err)
				}
				fh.Close()
				fh = nil
				break
			}
			qr.q <- next
		}
	}
}

func (qr *Qr) fs(in <-chan string, out chan<- string) {
	defer close(out)
	var (
		filenames []string
		checkOut  chan<- string
		next      string
	)
	for {
		select {
		case f, ok := <-in:
			if !ok {
				return
			}
			if checkOut == nil {
				checkOut = out
				next = f
			} else {
				filenames = append(filenames, f)
			}
		case checkOut <- next:
			if len(filenames) > 0 {
				next, filenames = filenames[0], filenames[1:]
			} else {
				// case disabled since there is no file
				checkOut = nil
			}
		}
	}
}

func (qr *Qr) batchFilename(t time.Time) string {
	format := "20060102T150405.999999999"
	return fmt.Sprintf("%s/%s-%s%s",
		qr.dir,
		qr.prefix,
		t.Format(format),
		fileExtension,
	)
}

// findOld finds .qr files from a previous run.
func (qr *Qr) findOld() []string {
	f, err := os.Open(qr.dir)
	if err != nil {
		return nil
	}
	defer f.Close()

	names, err := f.Readdirnames(-1)
	if err != nil {
		return nil
	}

	var existing []string
	for _, n := range names {
		if !strings.HasPrefix(n, qr.prefix+"-") || !strings.HasSuffix(n, fileExtension) {
			continue
		}
		existing = append(existing, filepath.Join(qr.dir, n))
	}

	sort.Strings(existing)

	return existing
}
<|endoftext|>


<commit_before>package api

import "net/http"

var (
	handler http.Handler
)

func init() {
	Initialize()
	err := ConnectDb()
	if err != nil {
		panic(err)
	}
	handler, _ = Handler()
}
<commit_msg>Fail earlier if tests can't connect to the db<commit_after>package api

import "net/http"

var (
	handler http.Handler
)

func init() {
	err := ConnectDb()
	if err != nil {
		panic(err)
	}
	Initialize()
	handler, _ = Handler()
}
<|endoftext|>


<commit_before>// Copyright 2014 Ben-Kuang. All rights reserved.
// Use of this source code is governed by The MIT License
// license that can be found in the LICENSE file.

/*
	Package provide method alloc task to Node server(base the Node execution speed)
*/
package scheduler

import (
	"distributed-task/gocommand"
	"distributed-task/gonet"
	"log"
	"strconv"
)

var nodeChan chan *Node

/*
	allocate data task with node performance
	//define the user collect data
*/
func AllocateData(method string, commandType string, data map[string]string, userCollect func(msg string)){
	loadNodeChan()
	switch commandType {
	case gocommand.TypeSequence:
		//TO-DO performance
		count := 0
		avg := len(data) / len(NodeConfig)
		tempMap := make(map[string]string)
		for key,value := range data {
			tempMap[key]=value
			count ++
			// log.Printf("datalength: %s \n", len(tempMap))
			if len(tempMap)>=avg{
				command := &gocommand.Command{method, commandType, tempMap}
				commandString := command.GetCommandString()
				content := gocommand.EnCode(commandString)
				sendNode(content, userCollect)
				// log.Printf("datalength1: %s \n", len(tempMapOther))
				tempMap = make(map[string]string)
				// log.Printf("datalength2: %s \n", len(tempMap))
			}else{
				if count >= len(data){
					//at the end
					command := &gocommand.Command{method, commandType, tempMap}
					commandString := command.GetCommandString()
					content := gocommand.EnCode(commandString)
					sendNode(content, userCollect)
				}
			}
		}

	case gocommand.TypeStartEnd:
		//TO-DO performance
		avg := len(data) / len(NodeConfig)
		start,err := strconv.ParseInt(data["start"], 10, 64)
		if err !=nil {
			log.Printf("scheduler, execute start end at start type wrong: %v\n", err)
		}
		end,err1 := strconv.ParseInt(data["end"], 10, 64)
		if err1 !=nil {
			log.Printf("scheduler, execute start end at end type wrong: %v\n", err)
		}

		tempMap := make(map[string]string)

		for current := int64(start); current<= end; current= current + int64(avg) {
			tempMap["start"] = strconv.FormatInt(current, 10)
			tempEnd := current + int64(avg)
			if tempEnd > end {
				tempEnd = end
			}
			tempMap["end"] = strconv.FormatInt(tempEnd, 10)
			command := &gocommand.Command{method, commandType, tempMap}
			commandString := command.GetCommandString()
			content := gocommand.EnCode(commandString)
			sendNode(content, userCollect)
			tempMap = make(map[string]string)
		}
	}
}

/*
	load the nodeconfig in the nodechan sequence
*/
func loadNodeChan(){
	nodeChan = make(chan *Node, len(NodeConfig))
	for _,config := range NodeConfig{
		nodeChan <- config
	}
}

func sendNode(content string, userCollect func(msg string)){
	node := <- nodeChan
	log.Printf("send msg to node:%s with message: %s\n", node.Config["NodeAddr"], content)
	msg := gonet.Message{node.Config["NodeAddr"], content}
	gonet.Send(msg, userCollect)
	nodeChan <- node
}

/*
	user define the task data and task function name
*/
// func Scheduler(){
// 	var taskData[string]string
// 	var taskType string
// 	var methodName string
// 	//user define data

// 	//
// 	allocateData(methodName, taskType, taskData)
// }

<commit_msg>fix allocate type start end<commit_after>// Copyright 2014 Ben-Kuang. All rights reserved.
// Use of this source code is governed by The MIT License
// license that can be found in the LICENSE file.

/*
	Package provide method alloc task to Node server(base the Node execution speed)
*/
package scheduler

import (
	"distributed-task/gocommand"
	"distributed-task/gonet"
	"log"
	"strconv"
)

var nodeChan chan *Node

/*
	allocate data task with node performance
	//define the user collect data
*/
func AllocateData(method string, commandType string, data map[string]string, userCollect func(msg string)) {
	loadNodeChan()
	switch commandType {
	case gocommand.TypeSequence:
		//TO-DO performance
		count := 0
		avg := len(data) / len(NodeConfig)
		tempMap := make(map[string]string)
		for key, value := range data {
			tempMap[key] = value
			count++
			// log.Printf("datalength: %s \n", len(tempMap))
			if len(tempMap) >= avg {
				command := &gocommand.Command{method, commandType, tempMap}
				commandString := command.GetCommandString()
				content := gocommand.EnCode(commandString)
				sendNode(content, userCollect)
				// log.Printf("datalength1: %s \n", len(tempMapOther))
				tempMap = make(map[string]string)
				// log.Printf("datalength2: %s \n", len(tempMap))
			} else {
				if count >= len(data) {
					//at the end
					command := &gocommand.Command{method, commandType, tempMap}
					commandString := command.GetCommandString()
					content := gocommand.EnCode(commandString)
					sendNode(content, userCollect)
				}
			}
		}

	case gocommand.TypeStartEnd:
		//TO-DO performance

		start, err := strconv.ParseInt(data["start"], 10, 64)
		if err != nil {
			log.Printf("scheduler, execute start end at start type wrong: %v\n", err)
		}
		end, err1 := strconv.ParseInt(data["end"], 10, 64)
		if err1 != nil {
			log.Printf("scheduler, execute start end at end type wrong: %v\n", err)
		}

		avg := (end - start) / int64(len(NodeConfig))
		tempMap := make(map[string]string)

		for current := int64(start); current <= end; current = current + int64(avg) {
			tempMap["start"] = strconv.FormatInt(current, 10)
			tempEnd := current + int64(avg) - 1
			if tempEnd > end {
				tempEnd = end
			}
			tempMap["end"] = strconv.FormatInt(tempEnd, 10)
			command := &gocommand.Command{method, commandType, tempMap}
			commandString := command.GetCommandString()
			content := gocommand.EnCode(commandString)
			sendNode(content, userCollect)
			tempMap = make(map[string]string)
		}
	}
}

/*
	load the nodeconfig in the nodechan sequence
*/
func loadNodeChan() {
	nodeChan = make(chan *Node, len(NodeConfig))
	for _, config := range NodeConfig {
		nodeChan <- config
	}
}

func sendNode(content string, userCollect func(msg string)) {
	node := <-nodeChan
	log.Printf("send msg to node:%s with message: %s\n", node.Config["NodeAddr"], content)
	msg := gonet.Message{node.Config["NodeAddr"], content}
	gonet.Send(msg, userCollect)
	nodeChan <- node
}

/*
	user define the task data and task function name
*/
// func Scheduler(){
// 	var taskData[string]string
// 	var taskType string
// 	var methodName string
// 	//user define data

// 	//
// 	allocateData(methodName, taskType, taskData)
// }
<|endoftext|>


<commit_before>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/jinzhu/now"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}
				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := now.Parse(str); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Related(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<commit_msg>Use utils ParseTime API<commit_after>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}
				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Related(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<|endoftext|>


<commit_before>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					if utils.ToString(value) == "" {
						field.Set(reflect.Zero(field.Type()))
						return
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Association(field).Find(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<commit_msg>Don't find association if current value is new record<commit_after>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					if utils.ToString(value) == "" {
						field.Set(reflect.Zero(field.Type()))
						return
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if context.GetDB().NewRecord(submodel.Interface()) && !context.GetDB().NewRecord(model.Addr().Interface()) {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Association(field).Find(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<|endoftext|>


<commit_before>package uniter

import (
	"fmt"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/log"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/watcher"
	"launchpad.net/juju-core/worker"
	"launchpad.net/tomb"
	"sort"
)

// filter collects unit, service, and service config information from separate
// state watchers, and presents it as events on channels designed specifically
// for the convenience of the uniter.
type filter struct {
	st   *state.State
	tomb tomb.Tomb

	// outUnitDying is closed when the unit's life becomes Dying.
	outUnitDying chan struct{}

	// The out*On chans are used to deliver events to clients.
	// The out* chans, when set to the corresponding out*On chan (rather than
	// nil) indicate that an event of the appropriate type is ready to send
	// to the client.
	outConfig      chan struct{}
	outConfigOn    chan struct{}
	outUpgrade     chan *charm.URL
	outUpgradeOn   chan *charm.URL
	outResolved    chan state.ResolvedMode
	outResolvedOn  chan state.ResolvedMode
	outRelations   chan []int
	outRelationsOn chan []int

	// The want* chans are used to indicate that the filter should send
	// events if it has them available.
	setCharm          chan *charm.URL // Request to upgrade to a charm URL
	wantForcedUpgrade chan bool
	wantResolved      chan struct{}

	// discardConfig is used to indicate that any pending config event
	// should be discarded.
	discardConfig chan struct{}

	// charmChanged is used to report back the error when setting a charm URL.
	charmChanged chan error

	// The following fields hold state that is collected while running,
	// and used to detect interesting changes to express as events.
	unit             *state.Unit
	life             state.Life
	resolved         state.ResolvedMode
	service          *state.Service
	upgradeRequested serviceCharm
	upgradeAvailable serviceCharm
	upgrade          *charm.URL
	relations        []int
}

// newFilter returns a filter that handles state changes pertaining to the
// supplied unit.
func newFilter(st *state.State, unitName string) (*filter, error) {
	f := &filter{
		st:                st,
		outUnitDying:      make(chan struct{}),
		outConfig:         make(chan struct{}),
		outConfigOn:       make(chan struct{}),
		outUpgrade:        make(chan *charm.URL),
		outUpgradeOn:      make(chan *charm.URL),
		outResolved:       make(chan state.ResolvedMode),
		outResolvedOn:     make(chan state.ResolvedMode),
		outRelations:      make(chan []int),
		outRelationsOn:    make(chan []int),
		wantForcedUpgrade: make(chan bool),
		setCharm:          make(chan *charm.URL),
		wantResolved:      make(chan struct{}),
		discardConfig:     make(chan struct{}),
		charmChanged:      make(chan error),
	}
	go func() {
		defer f.tomb.Done()
		err := f.loop(unitName)
		log.Printf("worker/uniter/filter: error: %v", err)
		f.tomb.Kill(err)
	}()
	return f, nil
}

func (f *filter) Stop() error {
	f.tomb.Kill(nil)
	return f.tomb.Wait()
}

func (f *filter) Dead() <-chan struct{} {
	return f.tomb.Dead()
}

func (f *filter) Wait() error {
	return f.tomb.Wait()
}

// UnitDying returns a channel which is closed when the Unit enters a Dying state.
func (f *filter) UnitDying() <-chan struct{} {
	return f.outUnitDying
}

// UpgradeEvents returns a channel that will receive a new charm URL whenever an
// upgrade is indicated. Events should not be read until the baseline state
// has been specified by calling WantUpgradeEvent.
func (f *filter) UpgradeEvents() <-chan *charm.URL {
	return f.outUpgradeOn
}

// ResolvedEvents returns a channel that may receive a ResolvedMode when the
// unit's Resolved value changes, or when an event is explicitly requested.
// A ResolvedNone state will never generate events, but ResolvedRetryHooks and
// ResolvedNoHooks will always be delivered as described.
func (f *filter) ResolvedEvents() <-chan state.ResolvedMode {
	return f.outResolvedOn
}

// ConfigEvents returns a channel that will receive a signal whenever the service's
// configuration changes, or when an event is explicitly requested.
func (f *filter) ConfigEvents() <-chan struct{} {
	return f.outConfigOn
}

// RelationsEvents returns a channel that will receive the ids of all the service's
// relations whose Life status has changed.
func (f *filter) RelationsEvents() <-chan []int {
	return f.outRelationsOn
}

// WantUpgradeEvent controls whether the filter will generate upgrade
// events for unforced service charm changes.
func (f *filter) WantUpgradeEvent(mustForce bool) {
	select {
	case <-f.tomb.Dying():
	case f.wantForcedUpgrade <- mustForce:
	}
}

// SetCharm notifies the filter that the unit is running a new
// charm. It causes the unit's charm URL to be set in state, and the
// following changes to the filter's behaviour:
//
// * Upgrade events will only be generated for charms different to
//   that supplied;
// * A fresh event will be generated for every relation
//   known to the service;
// * A fresh configuration event will be generated, and subsequent
//   events will only be sent in response to changes in the version
//   of the service's settings that is specific to that charm.
//
// SetCharm blocks until the charm URL is set in state, returning any
// error that occurred.
//
// TODO(dimitern): Once we have per-charm service settings in a coming
// follow-up the described behavior will match.
func (f *filter) SetCharm(curl *charm.URL) error {
	for {
		select {
		case <-f.tomb.Dying():
			return tomb.ErrDying
		case f.setCharm <- curl:
		}
		select {
		case <-f.tomb.Dying():
			return tomb.ErrDying
		case err := <-f.charmChanged:
			return err
		}
	}
	panic("unreachable")
}

// WantResolvedEvent indicates that the filter should send a resolved event
// if one is available.
func (f *filter) WantResolvedEvent() {
	select {
	case <-f.tomb.Dying():
	case f.wantResolved <- nothing:
	}
}

// DiscardConfigEvent indicates that the filter should discard any pending
// config event.
func (f *filter) DiscardConfigEvent() {
	select {
	case <-f.tomb.Dying():
	case f.discardConfig <- nothing:
	}
}

func (f *filter) loop(unitName string) (err error) {
	f.unit, err = f.st.Unit(unitName)
	if err != nil {
		return err
	}
	if err = f.unitChanged(); err != nil {
		return err
	}
	f.service, err = f.unit.Service()
	if err != nil {
		return err
	}
	f.upgradeRequested.url, _ = f.service.CharmURL()
	if err = f.serviceChanged(); err != nil {
		return err
	}
	unitw := f.unit.Watch()
	defer watcher.Stop(unitw, &f.tomb)
	servicew := f.service.Watch()
	defer watcher.Stop(servicew, &f.tomb)
	// configw and relationsw can get restarted, so we need to bind
	// their current values in the defer calls.
	var configw *state.ConfigWatcher
	var configChanges <-chan *state.Settings
	if _, ok := f.unit.CharmURL(); ok {
		configChanges = configw.Changes()
	}
	defer func() {
		if configw != nil {
			watcher.Stop(configw, &f.tomb)
		}
	}()
	relationsw := f.service.WatchRelations()
	defer func() { watcher.Stop(relationsw, &f.tomb) }()

	// Config events cannot be meaningfully discarded until one is available;
	// once we receive the initial change, we unblock discard requests by
	// setting this channel to its namesake on f.
	var discardConfig chan struct{}
	for {
		var ok bool
		select {
		case <-f.tomb.Dying():
			return tomb.ErrDying

		// Handle watcher changes.
		case _, ok = <-unitw.Changes():
			log.Debugf("worker/uniter/filter: got unit change")
			if !ok {
				return watcher.MustErr(unitw)
			}
			if err = f.unitChanged(); err != nil {
				return err
			}
		case _, ok = <-servicew.Changes():
			log.Debugf("worker/uniter/filter: got service change")
			if !ok {
				return watcher.MustErr(servicew)
			}
			if err = f.serviceChanged(); err != nil {
				return err
			}
		case _, ok = <-configChanges:
			log.Debugf("worker/uniter/filter: got config change")
			if !ok {
				return watcher.MustErr(configw)
			}
			log.Debugf("worker/uniter/filter: preparing new config event")
			f.outConfig = f.outConfigOn
			discardConfig = f.discardConfig
		case ids, ok := <-relationsw.Changes():
			log.Debugf("worker/uniter/filter: got relations change")
			if !ok {
				return watcher.MustErr(relationsw)
			}
			f.relationsChanged(ids)

		// Send events on active out chans.
		case f.outUpgrade <- f.upgrade:
			log.Debugf("worker/uniter/filter: sent upgrade event")
			f.upgradeRequested.url = f.upgrade
			f.outUpgrade = nil
		case f.outResolved <- f.resolved:
			log.Debugf("worker/uniter/filter: sent resolved event")
			f.outResolved = nil
		case f.outConfig <- nothing:
			log.Debugf("worker/uniter/filter: sent config event")
			f.outConfig = nil
		case f.outRelations <- f.relations:
			log.Debugf("worker/uniter/filter: sent relations event")
			f.outRelations = nil
			f.relations = nil

		// Handle explicit requests.
		case curl := <-f.setCharm:
			log.Debugf("worker/uniter/filter: changing charm to %q", curl)
			// We need to restart the config watcher after setting the
			// charm, because service config settings are distinct for
			// different service charms.
			if configw != nil {
				watcher.Stop(configw, &f.tomb)
			}
			configw = f.unit.WatchServiceConfig()
			if err := f.unit.SetCharmURL(curl); err != nil {
				f.charmChanged <- err
				return err
			} else {
				f.charmChanged <- nil
			}
			configChanges = configw.Changes()

			// Restart the relations watcher, in order to generate
			// fresh events for every relation, so that
			// previously-ignored events (i.e. those not applicable to
			// the previous charm) can be handled.
			watcher.Stop(relationsw, &f.tomb)
			relationsw = f.service.WatchRelations()

			f.upgradeRequested.url = curl
			if err = f.upgradeChanged(); err != nil {
				return err
			}
		case force := <-f.wantForcedUpgrade:
			log.Debugf("worker/uniter/filter: want forced upgrade %v", force)
			f.upgradeRequested.force = force
			if err = f.upgradeChanged(); err != nil {
				return err
			}
		case <-f.wantResolved:
			log.Debugf("worker/uniter/filter: want resolved event")
			if f.resolved != state.ResolvedNone {
				f.outResolved = f.outResolvedOn
			}
		case <-discardConfig:
			log.Debugf("worker/uniter/filter: discarded config event")
			f.outConfig = nil
		}
	}
	panic("unreachable")
}

// unitChanged responds to changes in the unit.
func (f *filter) unitChanged() error {
	if err := f.unit.Refresh(); err != nil {
		if state.IsNotFound(err) {
			return worker.ErrDead
		}
		return err
	}
	if f.life != f.unit.Life() {
		switch f.life = f.unit.Life(); f.life {
		case state.Dying:
			log.Printf("worker/uniter/filter: unit is dying")
			close(f.outUnitDying)
			f.outUpgrade = nil
		case state.Dead:
			log.Printf("worker/uniter/filter: unit is dead")
			return worker.ErrDead
		}
	}
	if resolved := f.unit.Resolved(); resolved != f.resolved {
		f.resolved = resolved
		if f.resolved != state.ResolvedNone {
			f.outResolved = f.outResolvedOn
		}
	}
	return nil
}

// serviceChanged responds to changes in the service.
func (f *filter) serviceChanged() error {
	if err := f.service.Refresh(); err != nil {
		if state.IsNotFound(err) {
			return fmt.Errorf("service unexpectedly removed")
		}
		return err
	}
	url, force := f.service.CharmURL()
	f.upgradeAvailable = serviceCharm{url, force}
	switch f.service.Life() {
	case state.Dying:
		if err := f.unit.Destroy(); err != nil {
			return err
		}
	case state.Dead:
		return fmt.Errorf("service unexpectedly dead")
	}
	return f.upgradeChanged()
}

// upgradeChanged responds to changes in the service or in the
// upgrade requests that defines which charm changes should be
// delivered as upgrades.
func (f *filter) upgradeChanged() (err error) {
	if f.life != state.Alive {
		log.Debugf("worker/uniter/filter: charm check skipped, unit is dying")
		f.outUpgrade = nil
		return nil
	}
	if *f.upgradeAvailable.url != *f.upgradeRequested.url {
		if f.upgradeAvailable.force || !f.upgradeRequested.force {
			log.Debugf("worker/uniter/filter: preparing new upgrade event")
			if f.upgrade == nil || *f.upgrade != *f.upgradeAvailable.url {
				f.upgrade = f.upgradeAvailable.url
			}
			f.outUpgrade = f.outUpgradeOn
			return nil
		}
	}
	log.Debugf("worker/uniter/filter: no new charm event")
	return nil
}

// relationsChanged responds to service relation changes.
func (f *filter) relationsChanged(ids []int) {
outer:
	for _, id := range ids {
		for _, existing := range f.relations {
			if id == existing {
				continue outer
			}
		}
		f.relations = append(f.relations, id)
	}
	if len(f.relations) != 0 {
		sort.Ints(f.relations)
		f.outRelations = f.outRelationsOn
	}
}

// serviceCharm holds information about a charm.
type serviceCharm struct {
	url   *charm.URL
	force bool
}

// nothing is marginally more pleasant to read than "struct{}{}".
var nothing = struct{}{}
<commit_msg>Fixed config watcher<commit_after>package uniter

import (
	"fmt"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/log"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/watcher"
	"launchpad.net/juju-core/worker"
	"launchpad.net/tomb"
	"sort"
)

// filter collects unit, service, and service config information from separate
// state watchers, and presents it as events on channels designed specifically
// for the convenience of the uniter.
type filter struct {
	st   *state.State
	tomb tomb.Tomb

	// outUnitDying is closed when the unit's life becomes Dying.
	outUnitDying chan struct{}

	// The out*On chans are used to deliver events to clients.
	// The out* chans, when set to the corresponding out*On chan (rather than
	// nil) indicate that an event of the appropriate type is ready to send
	// to the client.
	outConfig      chan struct{}
	outConfigOn    chan struct{}
	outUpgrade     chan *charm.URL
	outUpgradeOn   chan *charm.URL
	outResolved    chan state.ResolvedMode
	outResolvedOn  chan state.ResolvedMode
	outRelations   chan []int
	outRelationsOn chan []int

	// The want* chans are used to indicate that the filter should send
	// events if it has them available.
	setCharm          chan *charm.URL // Request to upgrade to a charm URL
	wantForcedUpgrade chan bool
	wantResolved      chan struct{}

	// discardConfig is used to indicate that any pending config event
	// should be discarded.
	discardConfig chan struct{}

	// charmChanged is used to report back the error when setting a charm URL.
	charmChanged chan error

	// The following fields hold state that is collected while running,
	// and used to detect interesting changes to express as events.
	unit             *state.Unit
	life             state.Life
	resolved         state.ResolvedMode
	service          *state.Service
	upgradeRequested serviceCharm
	upgradeAvailable serviceCharm
	upgrade          *charm.URL
	relations        []int
}

// newFilter returns a filter that handles state changes pertaining to the
// supplied unit.
func newFilter(st *state.State, unitName string) (*filter, error) {
	f := &filter{
		st:                st,
		outUnitDying:      make(chan struct{}),
		outConfig:         make(chan struct{}),
		outConfigOn:       make(chan struct{}),
		outUpgrade:        make(chan *charm.URL),
		outUpgradeOn:      make(chan *charm.URL),
		outResolved:       make(chan state.ResolvedMode),
		outResolvedOn:     make(chan state.ResolvedMode),
		outRelations:      make(chan []int),
		outRelationsOn:    make(chan []int),
		wantForcedUpgrade: make(chan bool),
		setCharm:          make(chan *charm.URL),
		wantResolved:      make(chan struct{}),
		discardConfig:     make(chan struct{}),
		charmChanged:      make(chan error),
	}
	go func() {
		defer f.tomb.Done()
		err := f.loop(unitName)
		log.Printf("worker/uniter/filter: error: %v", err)
		f.tomb.Kill(err)
	}()
	return f, nil
}

func (f *filter) Stop() error {
	f.tomb.Kill(nil)
	return f.tomb.Wait()
}

func (f *filter) Dead() <-chan struct{} {
	return f.tomb.Dead()
}

func (f *filter) Wait() error {
	return f.tomb.Wait()
}

// UnitDying returns a channel which is closed when the Unit enters a Dying state.
func (f *filter) UnitDying() <-chan struct{} {
	return f.outUnitDying
}

// UpgradeEvents returns a channel that will receive a new charm URL whenever an
// upgrade is indicated. Events should not be read until the baseline state
// has been specified by calling WantUpgradeEvent.
func (f *filter) UpgradeEvents() <-chan *charm.URL {
	return f.outUpgradeOn
}

// ResolvedEvents returns a channel that may receive a ResolvedMode when the
// unit's Resolved value changes, or when an event is explicitly requested.
// A ResolvedNone state will never generate events, but ResolvedRetryHooks and
// ResolvedNoHooks will always be delivered as described.
func (f *filter) ResolvedEvents() <-chan state.ResolvedMode {
	return f.outResolvedOn
}

// ConfigEvents returns a channel that will receive a signal whenever the service's
// configuration changes, or when an event is explicitly requested.
func (f *filter) ConfigEvents() <-chan struct{} {
	return f.outConfigOn
}

// RelationsEvents returns a channel that will receive the ids of all the service's
// relations whose Life status has changed.
func (f *filter) RelationsEvents() <-chan []int {
	return f.outRelationsOn
}

// WantUpgradeEvent controls whether the filter will generate upgrade
// events for unforced service charm changes.
func (f *filter) WantUpgradeEvent(mustForce bool) {
	select {
	case <-f.tomb.Dying():
	case f.wantForcedUpgrade <- mustForce:
	}
}

// SetCharm notifies the filter that the unit is running a new
// charm. It causes the unit's charm URL to be set in state, and the
// following changes to the filter's behaviour:
//
// * Upgrade events will only be generated for charms different to
//   that supplied;
// * A fresh event will be generated for every relation
//   known to the service;
// * A fresh configuration event will be generated, and subsequent
//   events will only be sent in response to changes in the version
//   of the service's settings that is specific to that charm.
//
// SetCharm blocks until the charm URL is set in state, returning any
// error that occurred.
//
// TODO(dimitern): Once we have per-charm service settings in a coming
// follow-up the described behavior will match.
func (f *filter) SetCharm(curl *charm.URL) error {
	for {
		select {
		case <-f.tomb.Dying():
			return tomb.ErrDying
		case f.setCharm <- curl:
		}
		select {
		case <-f.tomb.Dying():
			return tomb.ErrDying
		case err := <-f.charmChanged:
			return err
		}
	}
	panic("unreachable")
}

// WantResolvedEvent indicates that the filter should send a resolved event
// if one is available.
func (f *filter) WantResolvedEvent() {
	select {
	case <-f.tomb.Dying():
	case f.wantResolved <- nothing:
	}
}

// DiscardConfigEvent indicates that the filter should discard any pending
// config event.
func (f *filter) DiscardConfigEvent() {
	select {
	case <-f.tomb.Dying():
	case f.discardConfig <- nothing:
	}
}

func (f *filter) loop(unitName string) (err error) {
	f.unit, err = f.st.Unit(unitName)
	if err != nil {
		return err
	}
	if err = f.unitChanged(); err != nil {
		return err
	}
	f.service, err = f.unit.Service()
	if err != nil {
		return err
	}
	f.upgradeRequested.url, _ = f.service.CharmURL()
	if err = f.serviceChanged(); err != nil {
		return err
	}
	unitw := f.unit.Watch()
	defer watcher.Stop(unitw, &f.tomb)
	servicew := f.service.Watch()
	defer watcher.Stop(servicew, &f.tomb)
	// configw and relationsw can get restarted, so we need to bind
	// their current values in the defer calls.
	var configw *state.ConfigWatcher
	var configChanges <-chan *state.Settings
	if _, ok := f.unit.CharmURL(); ok {
		configw = f.unit.WatchServiceConfig()
		configChanges = configw.Changes()
	}
	defer func() {
		if configw != nil {
			watcher.Stop(configw, &f.tomb)
		}
	}()
	relationsw := f.service.WatchRelations()
	defer func() { watcher.Stop(relationsw, &f.tomb) }()

	// Config events cannot be meaningfully discarded until one is available;
	// once we receive the initial change, we unblock discard requests by
	// setting this channel to its namesake on f.
	var discardConfig chan struct{}
	for {
		var ok bool
		select {
		case <-f.tomb.Dying():
			return tomb.ErrDying

		// Handle watcher changes.
		case _, ok = <-unitw.Changes():
			log.Debugf("worker/uniter/filter: got unit change")
			if !ok {
				return watcher.MustErr(unitw)
			}
			if err = f.unitChanged(); err != nil {
				return err
			}
		case _, ok = <-servicew.Changes():
			log.Debugf("worker/uniter/filter: got service change")
			if !ok {
				return watcher.MustErr(servicew)
			}
			if err = f.serviceChanged(); err != nil {
				return err
			}
		case _, ok = <-configChanges:
			log.Debugf("worker/uniter/filter: got config change")
			if !ok {
				return watcher.MustErr(configw)
			}
			log.Debugf("worker/uniter/filter: preparing new config event")
			f.outConfig = f.outConfigOn
			discardConfig = f.discardConfig
		case ids, ok := <-relationsw.Changes():
			log.Debugf("worker/uniter/filter: got relations change")
			if !ok {
				return watcher.MustErr(relationsw)
			}
			f.relationsChanged(ids)

		// Send events on active out chans.
		case f.outUpgrade <- f.upgrade:
			log.Debugf("worker/uniter/filter: sent upgrade event")
			f.upgradeRequested.url = f.upgrade
			f.outUpgrade = nil
		case f.outResolved <- f.resolved:
			log.Debugf("worker/uniter/filter: sent resolved event")
			f.outResolved = nil
		case f.outConfig <- nothing:
			log.Debugf("worker/uniter/filter: sent config event")
			f.outConfig = nil
		case f.outRelations <- f.relations:
			log.Debugf("worker/uniter/filter: sent relations event")
			f.outRelations = nil
			f.relations = nil

		// Handle explicit requests.
		case curl := <-f.setCharm:
			log.Debugf("worker/uniter/filter: changing charm to %q", curl)
			// We need to restart the config watcher after setting the
			// charm, because service config settings are distinct for
			// different service charms.
			if configw != nil {
				watcher.Stop(configw, &f.tomb)
			}
			configw = f.unit.WatchServiceConfig()
			if err := f.unit.SetCharmURL(curl); err != nil {
				log.Debugf("worker/uniter/filter: failed setting charm url %q: %v", curl, err)
				f.charmChanged <- err
				return err
			} else {
				f.charmChanged <- nil
			}
			configChanges = configw.Changes()

			// Restart the relations watcher, in order to generate
			// fresh events for every relation, so that
			// previously-ignored events (i.e. those not applicable to
			// the previous charm) can be handled.
			watcher.Stop(relationsw, &f.tomb)
			relationsw = f.service.WatchRelations()

			f.upgradeRequested.url = curl
			if err = f.upgradeChanged(); err != nil {
				return err
			}
		case force := <-f.wantForcedUpgrade:
			log.Debugf("worker/uniter/filter: want forced upgrade %v", force)
			f.upgradeRequested.force = force
			if err = f.upgradeChanged(); err != nil {
				return err
			}
		case <-f.wantResolved:
			log.Debugf("worker/uniter/filter: want resolved event")
			if f.resolved != state.ResolvedNone {
				f.outResolved = f.outResolvedOn
			}
		case <-discardConfig:
			log.Debugf("worker/uniter/filter: discarded config event")
			f.outConfig = nil
		}
	}
	panic("unreachable")
}

// unitChanged responds to changes in the unit.
func (f *filter) unitChanged() error {
	if err := f.unit.Refresh(); err != nil {
		if state.IsNotFound(err) {
			return worker.ErrDead
		}
		return err
	}
	if f.life != f.unit.Life() {
		switch f.life = f.unit.Life(); f.life {
		case state.Dying:
			log.Printf("worker/uniter/filter: unit is dying")
			close(f.outUnitDying)
			f.outUpgrade = nil
		case state.Dead:
			log.Printf("worker/uniter/filter: unit is dead")
			return worker.ErrDead
		}
	}
	if resolved := f.unit.Resolved(); resolved != f.resolved {
		f.resolved = resolved
		if f.resolved != state.ResolvedNone {
			f.outResolved = f.outResolvedOn
		}
	}
	return nil
}

// serviceChanged responds to changes in the service.
func (f *filter) serviceChanged() error {
	if err := f.service.Refresh(); err != nil {
		if state.IsNotFound(err) {
			return fmt.Errorf("service unexpectedly removed")
		}
		return err
	}
	url, force := f.service.CharmURL()
	f.upgradeAvailable = serviceCharm{url, force}
	switch f.service.Life() {
	case state.Dying:
		if err := f.unit.Destroy(); err != nil {
			return err
		}
	case state.Dead:
		return fmt.Errorf("service unexpectedly dead")
	}
	return f.upgradeChanged()
}

// upgradeChanged responds to changes in the service or in the
// upgrade requests that defines which charm changes should be
// delivered as upgrades.
func (f *filter) upgradeChanged() (err error) {
	if f.life != state.Alive {
		log.Debugf("worker/uniter/filter: charm check skipped, unit is dying")
		f.outUpgrade = nil
		return nil
	}
	if *f.upgradeAvailable.url != *f.upgradeRequested.url {
		if f.upgradeAvailable.force || !f.upgradeRequested.force {
			log.Debugf("worker/uniter/filter: preparing new upgrade event")
			if f.upgrade == nil || *f.upgrade != *f.upgradeAvailable.url {
				f.upgrade = f.upgradeAvailable.url
			}
			f.outUpgrade = f.outUpgradeOn
			return nil
		}
	}
	log.Debugf("worker/uniter/filter: no new charm event")
	return nil
}

// relationsChanged responds to service relation changes.
func (f *filter) relationsChanged(ids []int) {
outer:
	for _, id := range ids {
		for _, existing := range f.relations {
			if id == existing {
				continue outer
			}
		}
		f.relations = append(f.relations, id)
	}
	if len(f.relations) != 0 {
		sort.Ints(f.relations)
		f.outRelations = f.outRelationsOn
	}
}

// serviceCharm holds information about a charm.
type serviceCharm struct {
	url   *charm.URL
	force bool
}

// nothing is marginally more pleasant to read than "struct{}{}".
var nothing = struct{}{}
<|endoftext|>