Стоян обнови решението на 18.11.2014 16:49 (преди над 3 години)
+package main
+
+import "time"
+import "strconv"
+import "strings"
+
+type ExpireMap struct {
+ items map[string]*Item
+ expireChannel chan string
+}
+
+//Връща указател към нов обект от тип ExpireMap, готов за използване.
+func NewExpireMap() *ExpireMap {
+ em := new(ExpireMap)
+ em.items = make(map[string]*Item)
+ em.expireChannel = make(chan string)
+ return em
+}
+
+//Добавя в хранилището нов ключ key със стойност value за expire време.
+//Duration е дефиниран в пакета time.
+//expire време след добавянето на ключа той вече трябва да спре да бъде в хранилището.
+//Стойността може да е от всякакъв тип, докато ключа е от тип string.
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ item := new(Item)
+ //item.key = key
+ item.value = value
+ item.expires = expire
+ item.timeAdded = time.Now()
+ em.items[key] = item
+
+ go func(key string, value interface{}, expire time.Duration) {
+ time.Sleep(expire)
+
+ if item.expires <= time.Since(item.timeAdded) {
+ delete(em.items, key)
+ em.expireChannel <- key
+ }
+
+ }(key, value, expire)
+
+}
+
+//Връща стойността на ключ и true, когато ключа е намерен. Когато ключа липсва или е изтекъл връща nil и false.
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ if item, ok := em.items[key]; ok {
+ return item.value, true
+ }
+
+ return nil, false
+
+}
+
+//Връща стойността, отговаряща на този ключ и true, когато ключа е намерен и стойността му е от тип int.
+//Когато ключа липсва, изтекъл е или стойността му не е от този тип метода трябва да върне нула и false.
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ if value, ok := em.Get(key); ok {
+ if val, ok := value.(int); ok {
+ return val, true
+ }
+
+ return 0, false
+ }
+
+ return 0, false
+}
+
+//Връща стойността на този ключ и true, когато ключа е намерен и стойността му е от тип float64.
+//Когато ключа липсва, изтекъл е или стойността му не е от този тип метода трябва да върне нула и false.
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ if value, ok := em.Get(key); ok {
+ if val, ok := value.(float64); ok {
+ return val, true
+ }
+
+ return 0, false
+ }
+
+ return 0, false
+}
+
+//Връща стойността на ключ и true, когато ключа е намерен и стойността му е от тип string.
+//Когато ключа липсва, изтекъл е или стойността не е от тип string метода трябва да върне празен стринг и false.
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ if value, ok := em.Get(key); ok {
+ if val, ok := value.(string); ok {
+ return val, true
+ }
+
+ return "", false
+ }
+
+ return "", false
+}
+
+//Връща стойността на ключ и true, когато ключа е намерен и стойността му е от тип bool.
+//Когато ключа липсва, изтекъл е или стойността не е от тип bool метода трябва да върне false и false.
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ if value, ok := em.Get(key); ok {
+ if val, ok := value.(bool); ok {
+ return val, true
+ }
+
+ return false, false
+ }
+
+ return false, false
+
+}
+
+//За определен ключ тази функция трябва да върне времето, когато той ще изтече.
+//Втората върната стойност е true когато ключа е намерен в хранилището. Когато не е намерен функцията трябва да върне нулевото време и false.
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ item, ok := em.items[key]
+ if ok {
+ return item.timeAdded.Add(item.expires), true
+ }
+
+ return time.Time{}, false
+}
+
+//Връща големината на хранилището. Големина е бройката неизтекли ключове, които са в него.
+func (em *ExpireMap) Size() int {
+ //lock ?
+ size := 0
+ for _, item := range em.items {
+ if item.expires >= time.Since(item.timeAdded) {
+ size++
+ }
+ }
+
+ return size
+}
+
+//Казва дали ключ е в хранилището или не. Отново, ключове са в хранилището само ако техния expire не е вече изтекъл.
+func (em *ExpireMap) Contains(key string) bool {
+ //lock
+ item, ok := em.items[key]
+
+ if ok && item.expires >= time.Since(item.timeAdded) {
+ return true
+ }
+
+ return false
+}
+
+//Премахва ключа от хранилището. Когато няма такъв ключ метода не трябав да гърми.
+func (em *ExpireMap) Delete(key string) {
+ delete(em.items, key)
+}
+
+//За ключ увеличава стойността му числово с 1 когато тази стойност е от тип int или стринг, представящ цяло число.
+//Когато стойността е от тип, различен от тези неща функцията трябва да върне грешка, различна от nil.
+//float32 и float64 не са целочислен типове. Времето на изтичане на ключа не трябва да се променя.
+func (em *ExpireMap) Increment(key string) error {
+ item, ok := em.items[key]
+
+ error := new(ExpireMapError)
+ error.ErrorMessage = "Error"
+
+ if ok {
+ if val, ok := item.value.(int); ok {
+ item.value = val + 1
+ return nil
+ }
+
+ if val, ok := item.value.(string); ok {
+ if n, err := strconv.Atoi(val); err == nil {
+ item.value = n + 1
+ return nil
+ }
+
+ return error
+ }
+
+ return error
+ }
+
+ return error
+}
+
+//Същото като Increment, но намалява стойността с 1 вместо да я увеличава.
+func (em *ExpireMap) Decrement(key string) error {
+ item, ok := em.items[key]
+
+ error := new(ExpireMapError)
+ error.ErrorMessage = "Error"
+
+ if ok {
+ if val, ok := item.value.(int); ok {
+ item.value = val - 1
+ return nil
+ }
+
+ if val, ok := item.value.(string); ok {
+ if n, err := strconv.Atoi(val); err == nil {
+ item.value = n - 1
+ return nil
+ }
+
+ return error
+ }
+
+ return error
+ }
+
+ return error
+}
+
+//Ако стойността за ключа key е от тип string, то всички букви в този string трябва да станат главни.
+//При успех метода трябва да върне nil, а при невъзможност да изпълни задачата - грешка.
+func (em *ExpireMap) ToUpper(key string) error {
+ item, ok := em.items[key]
+
+ error := new(ExpireMapError)
+ error.ErrorMessage = "Error"
+
+ if !ok {
+ return error
+ }
+
+ if val, ok := item.value.(string); ok {
+ item.value = strings.ToUpper(val)
+ }
+
+ return error
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ item, ok := em.items[key]
+
+ error := new(ExpireMapError)
+ error.ErrorMessage = "Error"
+
+ if !ok {
+ return error
+ }
+
+ if val, ok := item.value.(string); ok {
+ item.value = strings.ToLower(val)
+ }
+
+ return error
+}
+
+//Тази функция трябва да върне канал, по който да идват ключовете, които са изтекли.
+// Те трябва да се пускат по канала в момента на изтичането си. Ключове, които са изтрити с Delete или Cleanup не трябва да се връщат по този канал.
+// Единствено тези, които са достигнали до времето си на изтичане. Не е задължително през цялото време някой да чете от върнатия канал.
+// Ключове добавени в инстанцията след извикването на ExpireChan също трябва да се пратят по канала, когато тяхното време дойде.
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ return nil
+}
+
+func (em *ExpireMap) Cleanup() {
+ em.items = make(map[string]*Item)
+}
+
+func (em *ExpireMap) Destroy() {
+
+}
+
+type Item struct {
+ //key string
+ value interface{}
+ expires time.Duration
+ timeAdded time.Time
+}
+
+type ExpireMapError struct {
+ ErrorMessage string
+}
+
+func (error *ExpireMapError) Error() string {
+ return error.ErrorMessage
+}