redpear 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/redpear/connection.rb +110 -17
- data/lib/redpear/model/expiration.rb +18 -0
- data/lib/redpear/model/factory_girl.rb +27 -0
- data/lib/redpear/model/finders.rb +37 -0
- data/lib/redpear/model/machinist.rb +54 -0
- data/lib/redpear/model.rb +186 -63
- data/lib/redpear/schema/collection.rb +7 -7
- data/lib/redpear/{column.rb → schema/column.rb} +12 -6
- data/lib/redpear/schema/index.rb +22 -0
- data/lib/redpear/schema/score.rb +13 -0
- data/lib/redpear/schema.rb +9 -6
- data/lib/redpear/store/base.rb +120 -0
- data/lib/redpear/store/counter.rb +44 -0
- data/lib/redpear/store/enumerable.rb +8 -0
- data/lib/redpear/store/hash.rb +120 -0
- data/lib/redpear/store/list.rb +144 -0
- data/lib/redpear/store/lock.rb +108 -0
- data/lib/redpear/store/set.rb +147 -0
- data/lib/redpear/store/sorted_set.rb +239 -0
- data/lib/redpear/store/value.rb +66 -0
- data/lib/redpear/store.rb +11 -0
- data/lib/redpear.rb +7 -22
- metadata +45 -30
- data/lib/redpear/core_ext/stringify_keys.rb +0 -17
- data/lib/redpear/counters.rb +0 -26
- data/lib/redpear/expiration.rb +0 -27
- data/lib/redpear/finders.rb +0 -59
- data/lib/redpear/index.rb +0 -31
- data/lib/redpear/machinist.rb +0 -51
- data/lib/redpear/members.rb +0 -83
- data/lib/redpear/namespace.rb +0 -79
- data/lib/redpear/nest.rb +0 -100
- data/lib/redpear/persistence.rb +0 -147
- data/lib/redpear/zindex.rb +0 -26
- data/lib/redpear/zmembers.rb +0 -68
data/lib/redpear/schema.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
module Redpear::Schema
|
2
|
-
extend Redpear::Concern
|
3
2
|
autoload :Collection, 'redpear/schema/collection'
|
3
|
+
autoload :Column, 'redpear/schema/column'
|
4
|
+
autoload :Index, 'redpear/schema/index'
|
5
|
+
autoload :Score, 'redpear/schema/score'
|
6
|
+
extend Redpear::Concern
|
4
7
|
|
5
8
|
module ClassMethods
|
6
9
|
|
@@ -9,23 +12,23 @@ module Redpear::Schema
|
|
9
12
|
@columns ||= Redpear::Schema::Collection.new
|
10
13
|
end
|
11
14
|
|
12
|
-
# @param [multiple] the column definition. Please see Redpear::Column#initialize
|
15
|
+
# @param [multiple] the column definition. Please see Redpear::Schema::Column#initialize
|
13
16
|
def column(*args)
|
14
|
-
columns.store(Redpear::Column, self, *args).tap do |col|
|
17
|
+
columns.store(Redpear::Schema::Column, self, *args).tap do |col|
|
15
18
|
__define_attribute_accessors__(col)
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
19
22
|
# @param [multiple] the index definition. Please see Redpear::Index#initialize
|
20
23
|
def index(*args)
|
21
|
-
columns.store(Redpear::Index, self, *args).tap do |col|
|
24
|
+
columns.store(Redpear::Schema::Index, self, *args).tap do |col|
|
22
25
|
__define_attribute_accessors__(col)
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
29
|
# @param [multiple] the sorted index definition. Please see Redpear::ZIndex#initialize
|
27
|
-
def
|
28
|
-
columns.store(Redpear::
|
30
|
+
def score(*args)
|
31
|
+
columns.store(Redpear::Schema::Score, self, *args).tap do |col|
|
29
32
|
__define_attribute_accessors__(col)
|
30
33
|
end
|
31
34
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
class Redpear::Store::Base
|
4
|
+
|
5
|
+
attr_reader :key, :conn
|
6
|
+
|
7
|
+
# Creates and yields over a temporary key.
|
8
|
+
# Useful in combination with e.g. `interstore`, `unionstore`, etc.
|
9
|
+
#
|
10
|
+
# @param [Redpear::Connection] conn
|
11
|
+
# The connection
|
12
|
+
# @param [Hash] options
|
13
|
+
# The options hash
|
14
|
+
# @option [String] prefix
|
15
|
+
# Specify a key prefix. Example:
|
16
|
+
# Base.temporary conn, :prefix => "temp:" do |c|
|
17
|
+
# store.key # => temp:55ee0c1ec9530cf545bc25040beb4f292fd448af
|
18
|
+
# end
|
19
|
+
# @yield [Redpear::Store::Base]
|
20
|
+
# The temporary key
|
21
|
+
def self.temporary(conn, options = {})
|
22
|
+
store = nil
|
23
|
+
while !store || store.exists?
|
24
|
+
key = "#{options[:prefix]}#{SecureRandom.hex(20)}"
|
25
|
+
store = new(key, conn)
|
26
|
+
end
|
27
|
+
yield store
|
28
|
+
ensure
|
29
|
+
store.clear if store
|
30
|
+
end
|
31
|
+
|
32
|
+
# Constructor
|
33
|
+
# @param [String] key
|
34
|
+
# The storage key
|
35
|
+
# @param [Redpear::Connection] conn
|
36
|
+
# The connection
|
37
|
+
def initialize(key, conn)
|
38
|
+
@key, @conn = key, conn
|
39
|
+
end
|
40
|
+
|
41
|
+
alias to_s key
|
42
|
+
|
43
|
+
# @return [String] custom inspect
|
44
|
+
def inspect
|
45
|
+
"#<#{self.class.name} #{key}: #{value.inspect}>"
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Boolean] true if the record exists
|
49
|
+
def exists?
|
50
|
+
!!conn.exists(key)
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Integer] remaining time-to-live in seconds (if set)
|
54
|
+
def ttl
|
55
|
+
value = conn.ttl(key).to_i
|
56
|
+
value if value > -1
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [String] type information for this record
|
60
|
+
def type
|
61
|
+
conn.type(key).to_sym
|
62
|
+
end
|
63
|
+
|
64
|
+
# Expires the record
|
65
|
+
# @overload expire(time)
|
66
|
+
# @param [Time] time The time to expire the record at
|
67
|
+
# @overload expire(seconds)
|
68
|
+
# @param [Integer] seconds Expire in `seconds` from now
|
69
|
+
def expire(expiration)
|
70
|
+
case expiration
|
71
|
+
when Time
|
72
|
+
expire_at(expiration)
|
73
|
+
when Integer
|
74
|
+
expire_in(expiration)
|
75
|
+
when String
|
76
|
+
expiration = Kernel::Integer(expiration) rescue nil
|
77
|
+
expire(expiration)
|
78
|
+
else
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Deletes the whole record
|
84
|
+
def purge!
|
85
|
+
conn.del(key) == 1
|
86
|
+
end
|
87
|
+
|
88
|
+
# Deletes the record and returns the value
|
89
|
+
def clear
|
90
|
+
purge!
|
91
|
+
value
|
92
|
+
end
|
93
|
+
|
94
|
+
# Expires the record
|
95
|
+
# @param [Time] time The time to expire the record at
|
96
|
+
def expire_at(time)
|
97
|
+
conn.expireat key, time.to_i
|
98
|
+
end
|
99
|
+
|
100
|
+
# Expires the record
|
101
|
+
# @param [Integer] seconds Expire in `seconds` from now
|
102
|
+
def expire_in(seconds)
|
103
|
+
conn.expire key, seconds.to_i
|
104
|
+
end
|
105
|
+
|
106
|
+
# @abstract, override in subclasses
|
107
|
+
def value
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def range_pair(range)
|
114
|
+
first = range.first.to_i
|
115
|
+
last = range.last.to_i
|
116
|
+
last -= 1 if range.exclude_end?
|
117
|
+
[first, last]
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Redpear::Store::Counter < Redpear::Store::Value
|
2
|
+
|
3
|
+
# @return [Integer] the value
|
4
|
+
def get
|
5
|
+
super.to_i
|
6
|
+
end
|
7
|
+
|
8
|
+
# Sets the value
|
9
|
+
# @param [Integer] the value to set
|
10
|
+
def set(value)
|
11
|
+
super Kernel::Integer(value)
|
12
|
+
end
|
13
|
+
|
14
|
+
undef_method :append
|
15
|
+
undef_method :<<
|
16
|
+
|
17
|
+
# Increments the value
|
18
|
+
# @param [Integer] by
|
19
|
+
# The increment, defaults to 1
|
20
|
+
def increment(by = 1)
|
21
|
+
case by
|
22
|
+
when 1
|
23
|
+
conn.incr(key)
|
24
|
+
else
|
25
|
+
conn.incrby(key, by)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
alias_method :next, :increment
|
29
|
+
|
30
|
+
# Decrements the value
|
31
|
+
# @param [Integer] by
|
32
|
+
# The decrement, defaults to 1
|
33
|
+
def decrement(by = 1)
|
34
|
+
case by
|
35
|
+
when 1
|
36
|
+
conn.decr(key)
|
37
|
+
else
|
38
|
+
conn.decrby(key, by)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
alias_method :previous, :decrement
|
42
|
+
alias_method :prev, :decrement
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
class Redpear::Store::Hash < Redpear::Store::Enumerable
|
2
|
+
|
3
|
+
# @yield over a field-value pair
|
4
|
+
# @yieldparam [String] field
|
5
|
+
# @yieldparam [String] value
|
6
|
+
def each(&block)
|
7
|
+
all.each(&block)
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [Hash] all pairs
|
11
|
+
def all
|
12
|
+
conn.hgetall(key) || {}
|
13
|
+
end
|
14
|
+
alias_method :to_hash, :all
|
15
|
+
alias_method :value, :all
|
16
|
+
|
17
|
+
# @param [String] field
|
18
|
+
# The field to delete
|
19
|
+
def delete(field)
|
20
|
+
conn.hdel key, field
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean] true, if field exists
|
24
|
+
def key?(field)
|
25
|
+
!!conn.hexists(key, field)
|
26
|
+
end
|
27
|
+
alias_method :has_key?, :key?
|
28
|
+
alias_method :member?, :key?
|
29
|
+
alias_method :include?, :key?
|
30
|
+
|
31
|
+
# @return [Boolean] true, if empty
|
32
|
+
def empty?
|
33
|
+
length.zero?
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [String] field
|
37
|
+
# The field to fetch
|
38
|
+
# @return [String] value stored in +field+
|
39
|
+
def fetch(field)
|
40
|
+
conn.hget key, field
|
41
|
+
end
|
42
|
+
alias_method :[], :fetch
|
43
|
+
|
44
|
+
# @param [String] field
|
45
|
+
# The field to store at
|
46
|
+
# @param [String] value
|
47
|
+
# The value to store
|
48
|
+
# @param [Hash] options
|
49
|
+
# The value to store
|
50
|
+
def store(field, value, options = {})
|
51
|
+
if value.nil?
|
52
|
+
delete field
|
53
|
+
else
|
54
|
+
conn.hset key, field, value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
alias_method :[]=, :store
|
58
|
+
|
59
|
+
# @return [Array] all keys
|
60
|
+
def keys
|
61
|
+
conn.hkeys key
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Array] all values
|
65
|
+
def values
|
66
|
+
conn.hvals key
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [Intege] the number of pairs in the hash
|
70
|
+
def length
|
71
|
+
conn.hlen key
|
72
|
+
end
|
73
|
+
alias_method :size, :length
|
74
|
+
|
75
|
+
# @param [String] field
|
76
|
+
# The field to increment
|
77
|
+
# @param [Integer] value
|
78
|
+
# The increment value, defaults to 1
|
79
|
+
def increment(field, value = 1)
|
80
|
+
conn.hincrby key, field, value
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param [String] field
|
84
|
+
# The field to decrement
|
85
|
+
# @param [Integer] value
|
86
|
+
# The decrement value, defaults to 1
|
87
|
+
def decrement(field, value = 1)
|
88
|
+
increment(field, -value)
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param [multiple] fields
|
92
|
+
# The field to return
|
93
|
+
# @return [Array] values
|
94
|
+
def values_at(*fields)
|
95
|
+
conn.hmget key, *fields
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param [Hash] hash
|
99
|
+
# The pairs to update
|
100
|
+
def update(hash)
|
101
|
+
merge!(hash)
|
102
|
+
to_hash
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param [Hash] hash
|
106
|
+
# The pairs to merge
|
107
|
+
def merge!(hash)
|
108
|
+
hash = hash.reject do |field, value|
|
109
|
+
delete(field) if value.nil?
|
110
|
+
end
|
111
|
+
conn.hmset key, *hash.flatten
|
112
|
+
end
|
113
|
+
|
114
|
+
# Comparator
|
115
|
+
# @return [Boolean] true if same as `other`
|
116
|
+
def ==(other)
|
117
|
+
other.respond_to?(:to_hash) && other.to_hash == to_hash
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
class Redpear::Store::List < Redpear::Store::Enumerable
|
2
|
+
|
3
|
+
# @yield over each item in the list
|
4
|
+
# @yieldparam [String] item
|
5
|
+
def each(&block)
|
6
|
+
all.each(&block)
|
7
|
+
end
|
8
|
+
|
9
|
+
# @return [Array] all items
|
10
|
+
def all
|
11
|
+
slice(0..-1)
|
12
|
+
end
|
13
|
+
alias_method :to_a, :all
|
14
|
+
|
15
|
+
# Returns a slice of the list
|
16
|
+
# @overload slice(index)
|
17
|
+
# Returns the item at `index`
|
18
|
+
# @param [Integer] index
|
19
|
+
# @return [String] item
|
20
|
+
# @overload slice(start, length)
|
21
|
+
# Returns from `length` items from `start`
|
22
|
+
# @param [Integer] start
|
23
|
+
# @param [Integer] length
|
24
|
+
# @return [Array] items
|
25
|
+
# @overload slice(range)
|
26
|
+
# Returns items from range
|
27
|
+
# @param [Range] range
|
28
|
+
# @return [Array] items
|
29
|
+
def slice(start, length = nil)
|
30
|
+
case start
|
31
|
+
when Integer
|
32
|
+
if length
|
33
|
+
range(start, start + length - 1)
|
34
|
+
else
|
35
|
+
conn.lindex(key, start) rescue nil
|
36
|
+
end
|
37
|
+
when Range
|
38
|
+
range *range_pair(start)
|
39
|
+
else
|
40
|
+
[]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
alias_method :[], :slice
|
44
|
+
|
45
|
+
# Destructive slice. Returns specified range and removes other items.
|
46
|
+
# @overload slice(start, length)
|
47
|
+
def slice!(start, length = nil)
|
48
|
+
case start
|
49
|
+
when Range
|
50
|
+
trim *range_pair(start)
|
51
|
+
else
|
52
|
+
trim(start, start + length - 1)
|
53
|
+
end
|
54
|
+
to_a
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param [Integer] start
|
58
|
+
# @param [Integer] finish
|
59
|
+
# @return [Array] items
|
60
|
+
def range(start, finish)
|
61
|
+
conn.lrange(key, start, finish) || []
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param [Integer] start
|
65
|
+
# @param [Integer] finish
|
66
|
+
def trim(start, finish)
|
67
|
+
conn.ltrim(key, start, finish)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Integer] the number of items in the set
|
71
|
+
def length
|
72
|
+
conn.llen key
|
73
|
+
end
|
74
|
+
alias_method :size, :length
|
75
|
+
|
76
|
+
# Appends a single item. Chainable example:
|
77
|
+
# list << 'a' << 'b'
|
78
|
+
# @param [String] item
|
79
|
+
# A item to add
|
80
|
+
def push(item)
|
81
|
+
conn.rpush key, item
|
82
|
+
self
|
83
|
+
end
|
84
|
+
alias_method :<<, :push
|
85
|
+
|
86
|
+
# Removes the last item
|
87
|
+
# @return [String] the removed item
|
88
|
+
def pop
|
89
|
+
conn.rpop key
|
90
|
+
end
|
91
|
+
|
92
|
+
# Prepends a single item.
|
93
|
+
# @param [String] item
|
94
|
+
# A item to add
|
95
|
+
def unshift(item)
|
96
|
+
conn.lpush key, item
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
# Removes the first item
|
101
|
+
# @return [String] the removed item
|
102
|
+
def shift
|
103
|
+
conn.lpop key
|
104
|
+
end
|
105
|
+
|
106
|
+
# Removes the last item and prepends it to `target`
|
107
|
+
# @param [Redpear::Store::List] target
|
108
|
+
def pop_unshift(target)
|
109
|
+
conn.rpoplpush key, target.to_s
|
110
|
+
end
|
111
|
+
|
112
|
+
# Comparator
|
113
|
+
# @param [#to_a] other
|
114
|
+
# @return [Boolean] true if items match
|
115
|
+
def ==(other)
|
116
|
+
other.respond_to?(:to_a) && other.to_a == to_a
|
117
|
+
end
|
118
|
+
|
119
|
+
# Remove `item` from list
|
120
|
+
# @param [String] item
|
121
|
+
# @param [Integer] count
|
122
|
+
# The number of items to remove.
|
123
|
+
# When =0 - remove all occurences
|
124
|
+
# When >0 - remove the first `count` items
|
125
|
+
# When <0 - remove the last `count` items
|
126
|
+
def delete(item, count = 0)
|
127
|
+
conn.lrem key, count, item
|
128
|
+
end
|
129
|
+
|
130
|
+
# Insert `item` before `pivot`
|
131
|
+
# @param [String] pivot
|
132
|
+
# @param [String] item
|
133
|
+
def insert_before(pivot, item)
|
134
|
+
conn.linsert key, :before, pivot, item
|
135
|
+
end
|
136
|
+
|
137
|
+
# Insert `item` after `pivot`
|
138
|
+
# @param [String] pivot
|
139
|
+
# @param [String] item
|
140
|
+
def insert_after(pivot, item)
|
141
|
+
conn.linsert key, :after, pivot, item
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
class Redpear::Store::Lock < Redpear::Store::Base
|
2
|
+
class LockTimeout < ::StandardError; end
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Default lock options. Override via e.g.:
|
7
|
+
#
|
8
|
+
# Redpear::Store::Lock.default_options[:lock_timeout] = 5
|
9
|
+
#
|
10
|
+
# @return [Hash] default options
|
11
|
+
def default_options
|
12
|
+
@default_options ||= { :lock_timeout => 2, :wait_timeout => 2 }
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Float] the current lock timestamp
|
18
|
+
def current
|
19
|
+
value.to_f
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String] the current lock value
|
23
|
+
def value
|
24
|
+
conn.get key
|
25
|
+
end
|
26
|
+
|
27
|
+
# Creates a lock and yields a transaction. Example:
|
28
|
+
#
|
29
|
+
# sender = Redpear::Store::Hash.new "accounts:sender", connection
|
30
|
+
# recipient = Redpear::Store::Hash.new "accounts:recipient", connection
|
31
|
+
# lock = Redpear::Store::Lock.new "locks:transfer", connection
|
32
|
+
#
|
33
|
+
# lock.lock do
|
34
|
+
# sender.decrement 'balance', 100
|
35
|
+
# recipient.increment 'balance', 100
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @param [Hash] options
|
39
|
+
# @option [Integer] lock_timeout
|
40
|
+
# Hold the lock for a maximum of `lock_timeout` seconds. Defaults to 2.
|
41
|
+
# @option [Integer] wait_timeout
|
42
|
+
# Wait for `wait_timeout` seconds to obtain a lock, before timing out. Defaults to 2.
|
43
|
+
# @yield [] processes the block within the lock
|
44
|
+
def lock(options = {})
|
45
|
+
options = self.class.default_options.merge(options)
|
46
|
+
result = nil
|
47
|
+
timestamp = nil
|
48
|
+
timeout = to_time(options[:wait_timeout])
|
49
|
+
|
50
|
+
while !timestamp && timeout > Time.now
|
51
|
+
timestamp = to_time(options[:lock_timeout]).to_f
|
52
|
+
|
53
|
+
if lock_obtained?(timestamp) || expired_lock_obtained?(timestamp)
|
54
|
+
result = yield
|
55
|
+
else
|
56
|
+
timestamp = nil # Unset
|
57
|
+
sleep 0.1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
unless timestamp
|
62
|
+
raise LockTimeout, "Could not acquire lock on '#{key}'. Timed out."
|
63
|
+
end
|
64
|
+
|
65
|
+
result
|
66
|
+
ensure
|
67
|
+
purge! if timestamp && timestamp > Time.now.to_f
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
# @param [Float] timestamp
|
73
|
+
# The timestamp to set for the lock
|
74
|
+
# @return [Boolean]
|
75
|
+
# True, if timestamp could be set and the lock was obtained
|
76
|
+
def lock_obtained?(timestamp)
|
77
|
+
conn.setnx key, timestamp
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param [Float] timestamp
|
81
|
+
# The timestamp to set for the lock
|
82
|
+
# @return [Boolean]
|
83
|
+
# True, if key is set to an expired value and we can replace it
|
84
|
+
# with our `timestamp`
|
85
|
+
def expired_lock_obtained?(timestamp)
|
86
|
+
exists? && past?(value) && past?(conn.getset(key, timestamp))
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Boolean] true, if timestamp has expired and is in the past
|
90
|
+
def past?(timestamp)
|
91
|
+
timestamp = timestamp.to_f if timestamp.is_a?(String)
|
92
|
+
timestamp < Time.now.to_f
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def to_time(value)
|
98
|
+
case value
|
99
|
+
when Time
|
100
|
+
value
|
101
|
+
when Numeric
|
102
|
+
Time.now + value
|
103
|
+
else
|
104
|
+
Time.now + 5
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|