norton 0.0.26 → 0.0.28

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dcc25c436cc01399c68e17739b2181d7298e5a05
4
- data.tar.gz: 9add33b7a73eb52e1cceaceecee1728db377ae15
3
+ metadata.gz: 6b7c3999eddbe0fe7c432929c4a64337f7d13582
4
+ data.tar.gz: ff3c8b2858a68fb58992cf20007c9240fd0669a6
5
5
  SHA512:
6
- metadata.gz: e8dd9328148d9ec90be45726443cdadabbf99de363c11a9680897f53886f6cf5aabe8b6de429c9c92e28986b8ee4d03f95c1a63e0543116f3c7146e3725ac8ee
7
- data.tar.gz: 3e7b992f5406d3c3876f1e065f108a5d3c0bc690bb90429d85f6709980c9d14a02abd1f88252cce62ab9d39f01ef482b94a950a8dc01fb2d8513ddbfb9df9c05
6
+ metadata.gz: dab5a67c19f6ed9c13b7c10e52399fb3b5e230333deee5ccf74ee50b030e8534edcfa143038723bef88db5ae2fa583221c48b5dea9f9a32e97e951bae183859c
7
+ data.tar.gz: 1404ed9e20b18ab3f66c49cf648fb17bd91dece915fc572b3b652a554db27c94aed79809f1f9446836d614b25fafc9fcf47eb54841d14db45af7e64aea598210
data/.travis.yml CHANGED
@@ -4,7 +4,7 @@ services:
4
4
  - redis-server
5
5
 
6
6
  rvm:
7
- - 2.3.0
7
+ - 2.3.4
8
8
 
9
9
  script:
10
10
  - bundle exec rake
data/CHANGLOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ### 0.0.28
2
+
3
+ - Fix `remove_#{norton_attribute}`
4
+
5
+ ### 0.0.27
6
+
7
+ - Save loaded values in instance variable, now calling get method will not contact redis if already loaded.
@@ -15,75 +15,91 @@ module Norton
15
15
  #
16
16
  # @return [type] [description]
17
17
  def counter(name, options={}, &blk)
18
- self.register_norton_value(name, :counter)
18
+ register_norton_value(name, :counter)
19
19
 
20
+ # Redis: GET
20
21
  define_method(name) do
21
- Norton.redis.with do |conn|
22
- conn.get(self.norton_value_key(name)).try(:to_i) || send("#{name}_default_value".to_sym)
22
+ instance_variable_get("@#{name}") || begin
23
+ value = Norton.redis.with do |conn|
24
+ conn.get(norton_value_key(name))
25
+ end || send("#{name}_default_value")
26
+ instance_variable_set("@#{name}", value.to_i)
27
+ end
28
+ end
29
+
30
+ # Redis: SET
31
+ define_method("#{name}=") do |value|
32
+ if Norton.redis.with { |conn| conn.set(norton_value_key(name), value) }
33
+ instance_variable_set("@#{name}", value.to_i)
23
34
  end
24
35
  end
25
36
 
26
37
  define_method("#{name}_default_value") do
27
38
  0
28
39
  end
29
- send(:private, "#{name}_default_value".to_sym)
30
40
 
41
+ # Redis: INCR
31
42
  define_method("incr_#{name}") do
32
- Norton.redis.with do |conn|
33
- conn.incr(self.norton_value_key(name))
43
+ value = Norton.redis.with do |conn|
44
+ conn.incr(norton_value_key(name))
34
45
  end
46
+ instance_variable_set("@#{name}", value.to_i)
35
47
  end
36
48
 
49
+ # Redis: DECR
37
50
  define_method("decr_#{name}") do
38
- Norton.redis.with do |conn|
39
- conn.decr(self.norton_value_key(name))
51
+ value = Norton.redis.with do |conn|
52
+ conn.decr(norton_value_key(name))
40
53
  end
54
+ instance_variable_set("@#{name}", value.to_i)
41
55
  end
42
56
 
57
+ # Redis: INCRBY
43
58
  define_method("incr_#{name}_by") do |increment|
44
- Norton.redis.with do |conn|
45
- conn.incrby(self.norton_value_key(name), increment)
59
+ value = Norton.redis.with do |conn|
60
+ conn.incrby(norton_value_key(name), increment)
46
61
  end
62
+ instance_variable_set("@#{name}", value.to_i)
47
63
  end
48
64
 
65
+ # Redis: DECRBY
49
66
  define_method("decr_#{name}_by") do |decrement|
50
- Norton.redis.with do |conn|
51
- conn.decrby(self.norton_value_key(name), decrement)
52
- end
53
- end
54
-
55
- define_method("#{name}=") do |v|
56
- Norton.redis.with do |conn|
57
- conn.set(self.norton_value_key(name), v)
67
+ value = Norton.redis.with do |conn|
68
+ conn.decrby(norton_value_key(name), decrement)
58
69
  end
70
+ instance_variable_set("@#{name}", value.to_i)
59
71
  end
60
72
 
73
+ # Redis: SET
61
74
  define_method("reset_#{name}") do
62
- count = instance_eval(&blk)
75
+ value = instance_eval(&blk)
63
76
 
64
77
  Norton.redis.with do |conn|
65
- conn.set(self.norton_value_key(name), count)
78
+ conn.set(norton_value_key(name), value)
66
79
  end
80
+ instance_variable_set("@#{name}", value)
67
81
  end
68
82
 
83
+ # Redis: DEL
69
84
  define_method("remove_#{name}") do
70
85
  Norton.redis.with do |conn|
71
- conn.del(self.norton_value_key(name))
86
+ conn.del(norton_value_key(name))
72
87
  end
88
+ remove_instance_variable("@#{name}") if instance_variable_defined?("@#{name}")
73
89
  end
74
90
  send(:after_destroy, "remove_#{name}".to_sym) if respond_to? :after_destroy
75
91
 
76
92
  # Add Increment callback
77
93
  unless options[:incr].nil?
78
94
  options[:incr].each do |callback|
79
- self.send callback, proc{ instance_eval("incr_#{name}") }
95
+ send callback, proc{ instance_eval("incr_#{name}") }
80
96
  end
81
97
  end
82
98
 
83
99
  # Add Decrement callback
84
100
  unless options[:decr].nil?
85
101
  options[:decr].each do |callback|
86
- self.send callback, proc{ instance_eval("decr_#{name}") }
102
+ send callback, proc{ instance_eval("decr_#{name}") }
87
103
  end
88
104
  end
89
105
  end
data/lib/norton/helper.rb CHANGED
@@ -9,6 +9,19 @@ module Norton
9
9
  module ClassMethods
10
10
  attr_reader :norton_values
11
11
 
12
+ #
13
+ # 当定义一个 Norton Value 的时候,将这个 Norton Value 记录在 Class Variable `@norton_values` 中
14
+ #
15
+ #
16
+ # @return [void]
17
+ #
18
+ def register_norton_value(name, norton_type)
19
+ if !Norton::SUPPORTED_TYPES.include?(norton_type.to_sym)
20
+ raise Norton::InvalidType.new("Norton Type: #{norton_type} invalid!")
21
+ end
22
+
23
+ @norton_values[name.to_sym] = norton_type.to_sym
24
+ end
12
25
 
13
26
  #
14
27
  # 当前类是否定义了某个 Norton Value
@@ -18,7 +31,7 @@ module Norton
18
31
  # @return [Boolean]
19
32
  #
20
33
  def norton_value_defined?(name)
21
- self.norton_values.has_key?(name.to_sym)
34
+ norton_values.has_key?(name.to_sym)
22
35
  end
23
36
 
24
37
  #
@@ -29,21 +42,7 @@ module Norton
29
42
  # @return [Symbol]
30
43
  #
31
44
  def norton_value_type(name)
32
- self.norton_values[name.to_sym].try(:to_sym)
33
- end
34
-
35
- #
36
- # 当定义一个 Norton Value 的时候,将这个 Norton Value 记录在 Class Variable `@norton_values` 中
37
- #
38
- #
39
- # @return [void]
40
- #
41
- def register_norton_value(name, norton_type)
42
- if !Norton::SUPPORTED_TYPES.include?(norton_type.to_sym)
43
- raise Norton::InvalidType.new("Norton Type: #{norton_type} invalid!")
44
- end
45
-
46
- @norton_values[name.to_sym] = norton_type.to_sym
45
+ norton_values[name.to_sym]
47
46
  end
48
47
  end
49
48
 
@@ -63,7 +62,7 @@ module Norton
63
62
  id = self.id
64
63
  raise Norton::NilObjectId if id.nil?
65
64
  klass = self.class.to_s.pluralize.underscore
66
- "#{klass}:#{self.id}"
65
+ "#{klass}:#{id}"
67
66
  end
68
67
 
69
68
  #
@@ -80,46 +79,48 @@ module Norton
80
79
  # @return [String]
81
80
  #
82
81
  def norton_value_key(name)
83
- "#{self.norton_prefix}:#{name}"
82
+ "#{norton_prefix}:#{name}"
84
83
  end
85
84
 
85
+ def cast_value(type, value)
86
+ case type.to_sym
87
+ when :counter then value.to_i
88
+ when :timestamp then value.to_i
89
+ end
90
+ end
91
+
92
+ # 批量取出当前对象的多个 Norton 字段, 仅仅支持 counter / timestamp
86
93
  #
87
- # 批量取出当前对象的多个 Norton value, 仅仅支持 counter / timestamp
88
- #
89
- # @param [Array] *value_keys 直接传入需要的值的 key,例如: :key1, :key2, :key3
94
+ # @param [Array] names 需要检索的字段, 例如: :field1, :field2
90
95
  #
91
- # @return [Hash]
96
+ # @return [Model] 当前对象
92
97
  #
93
98
  def norton_mget(*names)
94
- ret = {}
99
+ values = Norton.redis.with do |conn|
100
+ conn.mget(names.map { |name| norton_value_key(name) })
101
+ end
95
102
 
96
- redis_keys = names.map { |n| self.norton_value_key(n) }
103
+ assign_values(names.zip(values).to_h)
97
104
 
98
- Norton.redis.with do |conn|
99
- # mget 一次取出所有的值
100
- redis_values = conn.mget(redis_keys)
105
+ self
106
+ end
101
107
 
102
- # 组装结果 Hash
103
- names.each_with_index do |n, index|
104
- val = redis_values[index].try(:to_i)
108
+ # :nodoc
109
+ def assign_values(new_values)
110
+ new_values.each do |field, val|
111
+ type = self.class.norton_value_type(field)
112
+ next unless %i[counter timestamp].include?(type)
105
113
 
106
- # 如果返回值为 nil 并且定义了这个 norton value
107
- # 那么获取返回值
108
- if val.nil? && self.class.norton_value_defined?(n)
109
- val = send("#{n}_default_value".to_sym)
114
+ value = cast_value(type, val || try("#{field}_default_value"))
115
+ instance_variable_set("@#{field}", value)
110
116
 
111
- # 如果返回值不为空,将默认值存入 SSDB
112
- # 并且当前 Value 是 `:timestamp`
113
- if !val.nil? && self.class.norton_value_type(n) == :timestamp
114
- conn.set(self.norton_value_key(n), val)
115
- end
117
+ if type == :timestamp && val.nil? && !try("#{field}_default_value").nil?
118
+ Norton.redis.with do |conn|
119
+ conn.set(norton_value_key(field), value)
116
120
  end
117
-
118
- ret[n] = val
119
121
  end
120
122
  end
121
-
122
- ret
123
123
  end
124
+ send(:private, :assign_values)
124
125
  end
125
126
  end
@@ -14,52 +14,44 @@ module Norton
14
14
  #
15
15
  # @return [type] [description]
16
16
  def timestamp(name, options={})
17
- self.register_norton_value(name, :timestamp)
17
+ register_norton_value(name, :timestamp)
18
18
 
19
+ # Redis: GET
19
20
  define_method(name) do
20
- ts = nil
21
+ value = Norton.redis.with do |conn|
22
+ raw_value = conn.get(norton_value_key(name))
23
+ break raw_value if raw_value.present?
21
24
 
22
- Norton.redis.with do |conn|
23
- ts = conn.get(self.norton_value_key(name)).try(:to_i)
24
-
25
- if ts.nil?
26
- ts = send("#{name}_default_value".to_sym)
27
- conn.set(self.norton_value_key(name), ts) if !ts.nil?
25
+ send("#{name}_default_value").tap do |default_value|
26
+ conn.set(norton_value_key(name), default_value)
28
27
  end
29
-
30
- ts
31
28
  end
29
+ instance_variable_set("@#{name}", value.to_i)
32
30
  end
33
31
 
34
32
  define_method("#{name}_default_value") do
35
- if !options[:allow_nil]
36
- if options[:digits].present? && options[:digits] == 13
37
- ts = (Time.now.to_f * 1000).to_i
38
- else
39
- ts = Time.now.to_i
40
- end
33
+ return nil if options[:allow_nil]
34
+ return (Time.current.to_f * 1000).to_i if options[:digits] == 13
41
35
 
42
- ts
43
- else
44
- nil
45
- end
36
+ Time.current.to_i
46
37
  end
47
- send(:private, "#{name}_default_value".to_sym)
48
38
 
39
+ # Redis: SET
49
40
  define_method("touch_#{name}") do
41
+ value = options[:digits] == 13 ? (Time.current.to_f * 1000).to_i : Time.current.to_i
42
+
50
43
  Norton.redis.with do |conn|
51
- if options[:digits].present? && options[:digits] == 13
52
- conn.set(self.norton_value_key(name), (Time.now.to_f * 1000).to_i)
53
- else
54
- conn.set(self.norton_value_key(name), Time.now.to_i)
55
- end
44
+ conn.set(norton_value_key(name), value)
56
45
  end
46
+ instance_variable_set("@#{name}", value)
57
47
  end
58
48
 
49
+ # Redis: DEL
59
50
  define_method("remove_#{name}") do
60
51
  Norton.redis.with do |conn|
61
- conn.del("#{self.class.to_s.pluralize.underscore}:#{self.id}:#{name}")
52
+ conn.del(norton_value_key(name))
62
53
  end
54
+ remove_instance_variable("@#{name}") if instance_variable_defined?("@#{name}")
63
55
  end
64
56
  send(:after_destroy, "remove_#{name}".to_sym) if respond_to? :after_destroy
65
57
 
@@ -1,3 +1,3 @@
1
1
  module Norton
2
- VERSION = "0.0.26"
2
+ VERSION = "0.0.28"
3
3
  end
data/lib/norton.rb CHANGED
@@ -37,82 +37,28 @@ module Norton
37
37
  end
38
38
  end
39
39
 
40
+ # 批量获取多个对象的多个 Norton 字段, 仅仅支持 counter / timestamp
40
41
  #
41
- # 为多个 Norton 对象一次性取出多个值
42
+ # @example
43
+ # Norton.mget([a_user, another_user], [:followers_count, :profile_updated_at])
42
44
  #
43
- # 从一组相同的对象中,一次性取出多个 Norton 值。
45
+ # @param [Array] names 需要检索的字段
44
46
  #
45
- # 例如:
47
+ # @return [Array] 一组对象
46
48
  #
47
- # vals = Norton.mget([user1, user2, user3], [followers_count, profile_updated_at])
48
- #
49
- # 将会返回:
50
- #
51
- # ```
52
- # {
53
- # "users:1:followers_count": 2,
54
- # "users:2:followers_count": 3,
55
- # "users:3:followers_count": 4,
56
- # "users:1:profile_updated_at": 1498315792,
57
- # "users:2:profile_updated_at": 1499315792,
58
- # "users:3:profile_updated_at": 1409315792,
59
- # }
60
- # ```
61
- #
62
- # * 返回的 Field 之间的顺序无法保证
63
- #
64
- # @param [Array] objects
65
- # @param [Array] names
66
- #
67
- # @return [Hash]
68
- #
69
- def mget(objects, names)
70
- ret = {}
71
-
72
- mapping = {}
73
- redis_keys = []
74
-
75
- objects.each_with_index do |obj, index|
76
- next if obj.nil?
77
-
78
- names.each do |n|
79
- redis_key = obj.norton_value_key(n)
80
-
81
- redis_keys << redis_key
82
- mapping[redis_key] = [n, index]
83
- end
49
+ def mget(objects, fields)
50
+ keys = objects.flat_map do |object|
51
+ fields.map { |f| object.norton_value_key(f) }
84
52
  end
53
+ nested_values = Norton.redis.with do |conn|
54
+ conn.mget(keys)
55
+ end.each_slice(fields.size)
85
56
 
86
- Norton.redis.with do |conn|
87
- values = conn.mget(redis_keys)
88
-
89
- redis_keys.each_with_index do |k, i|
90
- val = values[i].try(:to_i)
91
-
92
- # 如果返回值为 nil 并且定义了这个 norton value
93
- # 那么获取返回值
94
- if val.nil?
95
- # 从 mapping 中取出 value name 和对应的 object
96
- value_name, obj_index = mapping[k]
97
- obj = objects[obj_index]
98
-
99
- # 如果 object 定义了这个 value
100
- if obj.class.norton_value_defined?(value_name)
101
- # 获取默认值
102
- val = obj.send("#{value_name}_default_value".to_sym)
103
-
104
- # 如果返回值不为空,并且当前 value 的类型是 `timestamp` 将默认值存入 Redis
105
- if !val.nil? && obj.class.norton_value_type(value_name) == :timestamp
106
- conn.set(k, val)
107
- end
108
- end
109
- end
110
-
111
- ret[k] = val
112
- end
57
+ objects.zip(nested_values).each do |object, values|
58
+ object.send(:assign_values, fields.zip(values).to_h)
113
59
  end
114
60
 
115
- ret
61
+ objects
116
62
  end
117
63
  end
118
64
  end
@@ -1,10 +1,9 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  class Dummy
4
4
  include Norton::Counter
5
5
 
6
6
  counter :candies_count, {} do
7
- # puts 'hahaha'
8
7
  candies
9
8
  end
10
9
 
@@ -18,40 +17,127 @@ class Dummy
18
17
  end
19
18
 
20
19
  describe Norton::Counter do
21
- describe "reset counter" do
22
- it 'should set candies_count' do
23
- dummy = Dummy.new
24
- dummy.reset_candies_count
20
+ let(:dummy) { Dummy.new }
21
+
22
+ it "responds to methods" do
23
+ expect(dummy.respond_to?(:candies_count)).to be(true)
24
+ expect(dummy.respond_to?(:candies_count=)).to be(true)
25
+ expect(dummy.respond_to?(:candies_count_default_value)).to be(true)
26
+ expect(dummy.respond_to?(:incr_candies_count)).to be(true)
27
+ expect(dummy.respond_to?(:decr_candies_count)).to be(true)
28
+ expect(dummy.respond_to?(:incr_candies_count_by)).to be(true)
29
+ expect(dummy.respond_to?(:decr_candies_count_by)).to be(true)
30
+ expect(dummy.respond_to?(:reset_candies_count)).to be(true)
31
+ end
32
+
33
+ describe "#candies_count" do
34
+ it "returns the value correctly" do
35
+ allow(dummy).to receive(:candies_count_default_value) { 123 }
36
+
37
+ expect(dummy.candies_count).to eq(123)
38
+ expect(dummy.instance_variable_get(:@candies_count)).to eq(123)
39
+ end
40
+ end
41
+
42
+ describe "#candies_count=" do
43
+ it "assigns a value to the counter" do
44
+ dummy.candies_count = "200"
45
+
46
+ value_in_redis = Norton.redis.with do |conn|
47
+ conn.get(dummy.norton_value_key(:candies_count))
48
+ end
49
+ expect(value_in_redis.to_i).to eq(200)
50
+ expect(dummy.instance_variable_get(:@candies_count)).to eq(200)
51
+ end
52
+ end
53
+
54
+ describe "#candies_count_default_value" do
55
+ it "returns correct default value" do
56
+ expect(dummy.candies_count_default_value).to eq(0)
57
+ end
58
+ end
25
59
 
26
- expect(dummy.candies_count).to eq(15)
60
+ describe "#incr_candies_count" do
61
+ it "increases the value by one" do
62
+ dummy.incr_candies_count
63
+
64
+ value_in_redis = Norton.redis.with do |conn|
65
+ conn.get(dummy.norton_value_key(:candies_count))
66
+ end
67
+ expect(value_in_redis.to_i).to eq(1)
68
+ expect(dummy.instance_variable_get(:@candies_count)).to eq(1)
27
69
  end
28
70
  end
29
71
 
30
- describe "assign value to counter" do
31
- it 'should be able to assign a value to the counter' do
32
- dummy = Dummy.new
33
- dummy.candies_count = 200
72
+ describe "#decr_candies_count" do
73
+ it "decreases the value by one" do
74
+ dummy.candies_count = 15
75
+ dummy.decr_candies_count
34
76
 
35
- expect(dummy.candies_count).to eq(200)
77
+ value_in_redis = Norton.redis.with do |conn|
78
+ conn.get(dummy.norton_value_key(:candies_count))
79
+ end
80
+ expect(value_in_redis.to_i).to eq(14)
81
+ expect(dummy.instance_variable_get(:@candies_count)).to eq(14)
36
82
  end
37
83
  end
38
84
 
39
- describe ".incr_value_by" do
40
- it "should increase the value of the given amount" do
41
- dummy = Dummy.new
85
+ describe "#incr_candies_count_by" do
86
+ it "increases the value of the given amount" do
42
87
  dummy.incr_candies_count_by(3)
43
88
 
44
- expect(dummy.candies_count).to eq(3)
89
+ value_in_redis = Norton.redis.with do |conn|
90
+ conn.get(dummy.norton_value_key(:candies_count))
91
+ end
92
+ expect(value_in_redis.to_i).to eq(3)
93
+ expect(dummy.instance_variable_get(:@candies_count)).to eq(3)
45
94
  end
46
95
  end
47
96
 
48
- describe ".decr_value_by" do
49
- it "should decrease the value of the given amount" do
50
- dummy = Dummy.new
97
+ describe "#decr_candies_count_by" do
98
+ it "decreases the value of the given amount" do
51
99
  dummy.candies_count = 15
52
100
  dummy.decr_candies_count_by(5)
53
101
 
54
- expect(dummy.candies_count).to eq(10)
102
+ value_in_redis = Norton.redis.with do |conn|
103
+ conn.get(dummy.norton_value_key(:candies_count))
104
+ end
105
+ expect(value_in_redis.to_i).to eq(10)
106
+ expect(dummy.instance_variable_get(:@candies_count)).to eq(10)
107
+ end
108
+ end
109
+
110
+ describe "#reset_candies_count" do
111
+ it "resets candies_count" do
112
+ dummy.reset_candies_count
113
+
114
+ value_in_redis = Norton.redis.with do |conn|
115
+ conn.get(dummy.norton_value_key(:candies_count))
116
+ end
117
+ expect(value_in_redis.to_i).to eq(15)
118
+ expect(dummy.instance_variable_get(:@candies_count)).to eq(15)
119
+ end
120
+ end
121
+
122
+ describe "#remove_candies_count" do
123
+ it "deletes candies_count in redis" do
124
+ dummy.incr_candies_count_by(5)
125
+ expect(
126
+ Norton.redis.with { |conn| conn.exists(dummy.norton_value_key(:candies_count)) }
127
+ ).to be(true)
128
+
129
+ dummy.remove_candies_count
130
+ expect(
131
+ Norton.redis.with { |conn| conn.exists(dummy.norton_value_key(:candies_count)) }
132
+ ).to be(false)
133
+ end
134
+
135
+ it "removes the instance variable named by counter" do
136
+ dummy.incr_candies_count_by(5)
137
+ expect(dummy.instance_variable_defined?(:@candies_count)).to be(true)
138
+
139
+ dummy.remove_candies_count
140
+ expect(dummy.instance_variable_defined?(:@candies_count)).to be(false)
55
141
  end
56
142
  end
57
143
  end
@@ -5,14 +5,10 @@ class Dummy
5
5
  include Norton::Timestamp
6
6
  include Norton::HashMap
7
7
 
8
- counter :counter1
9
- counter :counter2
10
- counter :counter3
11
-
8
+ counter :counter1
12
9
  timestamp :time1
13
- timestamp :time2
14
-
15
- hash_map :map1
10
+ timestamp :time2, :allow_nil => true
11
+ hash_map :map1
16
12
 
17
13
  def id
18
14
  @id ||= Random.rand(10000)
@@ -30,29 +26,17 @@ module HolyLight
30
26
  end
31
27
 
32
28
  describe Norton::Helper do
33
- describe "@norton_values" do
34
- it "should contain defined values and type" do
35
- expect(Dummy.norton_values[:counter1]).to eq(:counter)
36
- expect(Dummy.norton_values[:counter2]).to eq(:counter)
37
- expect(Dummy.norton_values[:counter3]).to eq(:counter)
38
-
39
- expect(Dummy.norton_values[:time1]).to eq(:timestamp)
40
- expect(Dummy.norton_values[:time2]).to eq(:timestamp)
41
-
42
- expect(Dummy.norton_values[:map1]).to eq(:hash_map)
43
- end
44
-
45
- it "should not contain undefined values" do
46
- expect(Dummy.norton_values[:foobar]).to be_nil
47
- end
48
- end
49
-
50
29
  describe ".register_norton_value" do
51
30
  it "should raise error if type is not supported" do
52
31
  expect {
53
32
  Dummy.register_norton_value("foo", "bar")
54
33
  }.to raise_error(Norton::InvalidType)
55
34
  end
35
+
36
+ it "adds the fields with valid type to `@norton_values`" do
37
+ Dummy.register_norton_value("foo", "counter")
38
+ expect(Dummy.norton_values[:foo]).to eq(:counter)
39
+ end
56
40
  end
57
41
 
58
42
  describe ".norton_value_defined?" do
@@ -60,31 +44,30 @@ describe Norton::Helper do
60
44
  expect(Dummy.norton_value_defined?(:counter1)).to eq(true)
61
45
  end
62
46
 
63
- it "should return false for a defined value" do
47
+ it "should return false for a undefined value" do
64
48
  expect(Dummy.norton_value_defined?(:time3)).to eq(false)
65
49
  end
66
50
  end
67
51
 
68
- describe "#norton_value_key" do
69
- it do
70
- n = SecureRandom.hex(3)
71
-
72
- dummy = Dummy.new
73
- expect(dummy.norton_value_key(n)).to eq("dummies:#{dummy.id}:#{n}")
52
+ describe ".norton_value_type" do
53
+ it "returns the type for a defined value" do
54
+ Dummy.register_norton_value("foo", "counter")
55
+ expect(Dummy.norton_value_type("foo")).to eq(:counter)
74
56
  end
57
+ end
75
58
 
76
- it "should raise `Norton::NilObjectId` if id returns nil" do
77
- dummy = Dummy.new
78
- allow(dummy).to receive(:id) { nil }
59
+ describe "#norton_prefix" do
60
+ let(:dummy) { Dummy.new }
61
+ let(:spammer) { HolyLight::Spammer.new }
79
62
 
63
+ it "raises error if the object's id is nil" do
64
+ allow(dummy).to receive(:id) { nil }
80
65
  expect { dummy.norton_prefix }.to raise_error(Norton::NilObjectId)
81
66
  end
82
- end
83
67
 
84
- describe "#norton_prefix" do
85
- it "should return correctly for `Dummy`" do
86
- dummy = Dummy.new
68
+ it "returns correctly for valid objects" do
87
69
  expect(dummy.norton_prefix).to eq("dummies:#{dummy.id}")
70
+ expect(spammer.norton_prefix).to eq("holy_light/spammers:#{spammer.id}")
88
71
  end
89
72
 
90
73
  it "should return correctly for `HolyLight::Spammer`" do
@@ -93,87 +76,93 @@ describe Norton::Helper do
93
76
  end
94
77
  end
95
78
 
96
- describe "#norton_mget" do
97
- it "should respond to `:norton_mget`" do
98
- dummy = Dummy.new
99
-
100
- expect(dummy).to respond_to(:norton_mget)
101
- end
102
-
103
- it "should return the specific values" do
104
- dummy = Dummy.new
79
+ describe "#norton_value_key" do
80
+ let(:dummy) { Dummy.new }
105
81
 
106
- dummy.counter1 = 10
107
- dummy.counter2 = 15
108
- dummy.counter3 = 100
82
+ it "returns the redis key correctly" do
83
+ allow(dummy).to receive(:norton_prefix) { "foobar" }
109
84
 
110
- dummy.touch_time1
85
+ expect(dummy.norton_value_key("lol")).to eq("foobar:lol")
86
+ end
87
+ end
111
88
 
112
- values = dummy.norton_mget(:counter1, :time1)
89
+ describe "#norton_mget" do
90
+ let(:dummy) { Dummy.new }
113
91
 
114
- expect(values).to include(:counter1, :time1)
115
- expect(values.size).to eq(2)
116
- expect(values[:counter1]).to eq(dummy.counter1)
117
- expect(values[:time1]).to eq(dummy.time1)
92
+ context "when the field isn't defined" do
93
+ it "doesn't set the instance variable" do
94
+ dummy.norton_mget(:undefined_field)
95
+ expect(dummy.instance_variable_defined?(:@undefined_field)).to be(false)
96
+ end
118
97
  end
119
98
 
120
- it "should default value if the value of does not exist" do
121
- dummy = Dummy.new
122
-
123
- dummy.counter1 = 10
124
- dummy.counter2 = 15
99
+ context "when the type isn't in the [:counter, :timestamp]" do
100
+ it "doesn't set the instance variable" do
101
+ dummy.norton_mget(:map1)
102
+ expect(dummy.instance_variable_defined?(:@map1)).to be(false)
103
+ end
104
+ end
125
105
 
126
- dummy.touch_time1
106
+ context "when the type is in the [:counter, :timestamp]" do
107
+ it "returns norton values correctly from redis" do
108
+ Norton.redis.with do |conn|
109
+ conn.set(dummy.norton_value_key(:counter1), 2)
110
+ conn.set(dummy.norton_value_key(:time1), 1234)
111
+ end
127
112
 
128
- t = Time.now
113
+ dummy.norton_mget(:counter1, :time1)
114
+ expect(dummy.instance_variable_get(:@counter1)).to eq(2)
115
+ expect(dummy.instance_variable_get(:@time1)).to eq(1234)
116
+ end
129
117
 
130
- Timecop.freeze(t) do
131
- values = dummy.norton_mget(:counter1, :counter2, :time2)
118
+ it "returns the default value if no value in redis" do
119
+ allow(dummy).to receive(:counter1_default_value) { 99 }
120
+ allow(dummy).to receive(:time1_default_value) { 1234 }
132
121
 
133
- expect(values).to include(:counter1, :counter2, :time2)
134
- expect(values.size).to eq(3)
135
- expect(values[:counter1]).to eq(dummy.counter1)
136
- expect(values[:counter2]).to eq(dummy.counter2)
137
- expect(values[:time2]).to eq(t.to_i)
122
+ dummy.norton_mget(:counter1, :time1)
123
+ expect(dummy.instance_variable_get(:@counter1)).to eq(99)
124
+ expect(dummy.instance_variable_get(:@time1)).to eq(1234)
138
125
  end
139
126
  end
140
127
 
141
- it "should save the default value for timestamp" do
142
- dummy = Dummy.new
128
+ context "when the type is :counter" do
129
+ it "doesn't save the default value in redis if no value in redis" do
130
+ allow(dummy).to receive(:counter1_default_value) { 99 }
143
131
 
144
- t = Time.now
132
+ dummy.norton_mget(:counter1)
145
133
 
146
- Timecop.freeze(t) do
147
- values = dummy.norton_mget(:time2)
148
- expect(values[:time2]).to eq(t.to_i)
149
-
150
- # Test value directly from Redis
151
- val = Norton.redis.with { |conn| conn.get(dummy.norton_value_key(:time2)) }.to_i
152
- expect(val).to eq(t.to_i)
134
+ value_from_redis = Norton.redis.with do |conn|
135
+ conn.get(dummy.norton_value_key(:counter1))
136
+ end
137
+ expect(value_from_redis).to be(nil)
153
138
  end
154
139
  end
155
140
 
156
- it "should not save the default value for counter" do
157
- dummy = Dummy.new
158
-
159
- t = Time.now
141
+ context "when the type is :timestamp" do
142
+ context "when the attribute doesn't allow nil" do
143
+ it "saves the default value in redis if no value in redis" do
144
+ allow(dummy).to receive(:time1_default_value) { 1234 }
160
145
 
161
- Timecop.freeze(t) do
162
- values = dummy.norton_mget(:counter1)
163
- expect(values[:counter1]).to eq(0)
146
+ dummy.norton_mget(:time1)
164
147
 
165
- # Test value directly from Redis
166
- expect(Norton.redis.with { |conn| conn.get(dummy.norton_value_key(:counter1)) }).to be_nil
148
+ value_from_redis = Norton.redis.with do |conn|
149
+ conn.get(dummy.norton_value_key(:time1))
150
+ end.to_i
151
+ expect(value_from_redis).to eq(1234)
152
+ end
167
153
  end
168
- end
169
154
 
170
- it "should return nil if the norton value is not defined" do
171
- dummy = Dummy.new
155
+ context "when the attribute allow nil" do
156
+ it "doesn't save the default value in redis if no value in redis" do
157
+ allow(dummy).to receive(:time2_default_value) { nil }
172
158
 
173
- values = dummy.norton_mget(:time3)
159
+ dummy.norton_mget(:time2)
174
160
 
175
- expect(values).to include(:time3)
176
- expect(values[:time3]).to be_nil
161
+ expect(
162
+ Norton.redis.with { |conn| conn.exists(dummy.norton_value_key(:time2)) }
163
+ ).to eq(false)
164
+ end
165
+ end
177
166
  end
178
167
  end
179
168
  end
@@ -4,8 +4,8 @@ class Dummy
4
4
  include Norton::Timestamp
5
5
 
6
6
  timestamp :born_at
7
+ timestamp :thirteen_ts, :digits => 13
7
8
  timestamp :first_kissed_at, :allow_nil => true
8
- # timestamp :graduated_at, before_save: -> { title_changed? || content_changed? }
9
9
 
10
10
  def id
11
11
  @id ||= Random.rand(10000)
@@ -13,59 +13,102 @@ class Dummy
13
13
  end
14
14
 
15
15
  describe Norton::Timestamp do
16
- describe ".timestamp" do
17
- it "should add a class method timestamp to the class" do
18
- expect(Dummy).to respond_to(:timestamp)
19
- end
16
+ let(:dummy) { Dummy.new }
17
+
18
+ it "responds to methods" do
19
+ expect(dummy.respond_to?(:born_at)).to be(true)
20
+ expect(dummy.respond_to?(:born_at_default_value)).to be(true)
21
+ expect(dummy.respond_to?(:touch_born_at)).to be(true)
22
+ expect(dummy.respond_to?(:remove_born_at)).to be(true)
23
+ end
20
24
 
21
- it 'should create timestamp accessors and touch method' do
22
- dummy = Dummy.new
25
+ describe "#born_at" do
26
+ it "returns the timestamp correctly" do
27
+ Norton.redis.with do |conn|
28
+ conn.set(dummy.norton_value_key(:born_at), 123)
29
+ end
23
30
 
24
- expect(dummy).to respond_to(:born_at)
25
- expect(dummy).to respond_to(:touch_born_at)
31
+ expect(dummy.born_at).to eq(123)
32
+ expect(dummy.instance_variable_get(:@born_at)).to eq(123)
26
33
  end
27
34
 
28
- it "should create remove method" do
29
- dummy = Dummy.new
30
- expect(dummy).to respond_to(:remove_born_at)
35
+ it "returns default timestamp if no value in norton" do
36
+ allow(dummy).to receive(:born_at_default_value) { 456 }
37
+ dummy.born_at
38
+
39
+ value = Norton.redis.with do |conn|
40
+ conn.get(dummy.norton_value_key(:born_at))
41
+ end
42
+ expect(value.to_i).to eq(456)
31
43
  end
32
44
  end
33
45
 
34
- describe "#touch_born_at" do
35
- it 'should set timestamp in redis' do
36
- dummy = Dummy.new
37
- dummy.touch_born_at
46
+ describe "#born_at_default_value" do
47
+ it "returns nil if the timestamp allow nil" do
48
+ expect(dummy.first_kissed_at_default_value).to eq(nil)
49
+ end
38
50
 
39
- Norton.redis.with do |conn|
40
- born_at = conn.get("#{Dummy.to_s.pluralize.downcase}:#{dummy.id}:born_at")
41
- expect(born_at.to_i).to be > 0
51
+ it "returns the current time as timestamp" do
52
+ Timecop.freeze(Time.current) do
53
+ expect(dummy.born_at_default_value).to eq(Time.current.to_i)
54
+ end
55
+ end
56
+
57
+ it "returns the current time as 13 digit timestamp" do
58
+ Timecop.freeze(Time.current) do
59
+ expect(dummy.thirteen_ts_default_value).to eq((Time.current.to_f * 1000).to_i)
42
60
  end
43
61
  end
44
62
  end
45
63
 
46
- describe "#born_at" do
47
- it 'should get the timestamp' do
48
- dummy = Dummy.new
49
- dummy.touch_born_at
50
- expect(dummy.born_at).not_to be_nil
51
- expect(dummy.born_at).to be_a(Fixnum)
64
+ describe "#touch_born_at" do
65
+ it "sets the timestamp to the current time" do
66
+ Timecop.freeze(Time.current) do
67
+ dummy.touch_born_at
68
+
69
+ expect(
70
+ Norton.redis.with { |conn| conn.get(dummy.norton_value_key(:born_at)) }.to_i
71
+ ).to eq(Time.current.to_i)
72
+ end
52
73
  end
53
74
 
54
- it "should get current time as a default if timestamp is not touched" do
55
- dummy = Dummy.new
56
- t = Time.now
75
+ it "sets the timestamp to the current time as 13 digit timestamp" do
76
+ Timecop.freeze(Time.current) do
77
+ dummy.touch_thirteen_ts
78
+
79
+ expect(
80
+ Norton.redis.with { |conn| conn.get(dummy.norton_value_key(:thirteen_ts)) }.to_i
81
+ ).to eq((Time.current.to_f * 1000).to_i)
82
+ end
83
+ end
57
84
 
58
- Timecop.freeze(t) do
59
- expect(dummy.born_at).to eq(t.to_i)
60
- expect(dummy.born_at).to eq(t.to_i)
85
+ it "sets the instance variable named by timestamp" do
86
+ Timecop.freeze(Time.current) do
87
+ dummy.touch_born_at
88
+ expect(dummy.instance_variable_get(:@born_at)).to eq(Time.current.to_i)
61
89
  end
62
90
  end
63
91
  end
64
92
 
65
- describe "#first_kissed_at" do
66
- it "should return nil if not set before because it allows nil" do
67
- dummy = Dummy.new
68
- expect(dummy.first_kissed_at).to be_nil
93
+ describe "#remove_born_at" do
94
+ it "deletes the timestamp in the redis" do
95
+ dummy.touch_born_at
96
+ expect(
97
+ Norton.redis.with { |conn| conn.exists(dummy.norton_value_key(:born_at)) }
98
+ ).to be(true)
99
+
100
+ dummy.remove_born_at
101
+ expect(
102
+ Norton.redis.with { |conn| conn.exists(dummy.norton_value_key(:born_at)) }
103
+ ).to be(false)
104
+ end
105
+
106
+ it "removes the instance variable named by timestamp" do
107
+ dummy.touch_born_at
108
+ expect(dummy.instance_variable_defined?(:@born_at)).to be(true)
109
+
110
+ dummy.remove_born_at
111
+ expect(dummy.instance_variable_defined?(:@born_at)).to be(false)
69
112
  end
70
113
  end
71
114
  end
data/spec/norton_spec.rb CHANGED
@@ -8,102 +8,97 @@ describe Norton do
8
8
  end
9
9
  end
10
10
 
11
- describe "mget" do
12
- class Foobar
11
+ describe ".mget" do
12
+ class Dummy
13
13
  include Norton::Counter
14
14
  include Norton::Timestamp
15
+ include Norton::HashMap
15
16
 
16
- counter :test_counter
17
- timestamp :test_timestamp
18
-
19
- def initialize(id)
20
- @id = id
21
- end
17
+ counter :counter1
18
+ timestamp :time1
19
+ hash_map :map1
22
20
 
23
21
  def id
24
- @id
22
+ @id ||= Random.rand(10000)
25
23
  end
26
24
  end
27
25
 
28
- it "should get the values correctly" do
29
- foobar1 = Foobar.new(SecureRandom.hex(2))
30
- foobar2 = Foobar.new(SecureRandom.hex(2))
31
-
32
- Random.rand(100).times { foobar1.incr_test_counter }
33
- foobar1.touch_test_timestamp
34
-
35
- sleep(2)
36
-
37
- Random.rand(100).times { foobar2.incr_test_counter }
38
- foobar2.touch_test_timestamp
26
+ let(:dummy) { Dummy.new }
39
27
 
40
- vals = Norton.mget([foobar1, foobar2], [:test_counter, :test_timestamp])
41
-
42
- expect(vals).to include("foobars:#{foobar1.id}:test_counter" => foobar1.test_counter)
43
- expect(vals).to include("foobars:#{foobar1.id}:test_timestamp" => foobar1.test_timestamp)
44
- expect(vals).to include("foobars:#{foobar2.id}:test_counter" => foobar2.test_counter)
45
- expect(vals).to include("foobars:#{foobar2.id}:test_timestamp" => foobar2.test_timestamp)
28
+ context "when the field isn't defined" do
29
+ it "doesn't set the instance variable" do
30
+ Norton.mget([dummy], %i[undefined_field])
31
+ expect(dummy.instance_variable_defined?(:@undefined_field)).to be(false)
32
+ end
46
33
  end
47
34
 
48
- it "should get default value correctly if no value in norton redis database" do
49
- foobar1 = Foobar.new(SecureRandom.hex(2))
50
- foobar2 = Foobar.new(SecureRandom.hex(2))
35
+ context "when the type isn't in the [:counter, :timestamp]" do
36
+ it "doesn't set the instance variable" do
37
+ Norton.mget([dummy], %i[map1])
38
+ expect(dummy.instance_variable_defined?(:@map1)).to be(false)
39
+ end
40
+ end
51
41
 
52
- t = Time.now
42
+ context "when the type is in the [:counter, :timestamp]" do
43
+ it "returns norton values correctly from redis" do
44
+ Norton.redis.with do |conn|
45
+ conn.set(dummy.norton_value_key(:counter1), 2)
46
+ conn.set(dummy.norton_value_key(:time1), 1234)
47
+ end
53
48
 
54
- Timecop.freeze(t) do
55
- foobar1.incr_test_counter
56
- foobar2.touch_test_timestamp
49
+ Norton.mget([dummy], %i[counter1 time1])
50
+ expect(dummy.instance_variable_get(:@counter1)).to eq(2)
51
+ expect(dummy.instance_variable_get(:@time1)).to eq(1234)
57
52
  end
58
53
 
59
- sleep(1)
54
+ it "returns the default value if no value in redis" do
55
+ allow(dummy).to receive(:counter1_default_value) { 99 }
56
+ allow(dummy).to receive(:time1_default_value) { 1234 }
60
57
 
61
- t2 = Time.now
62
-
63
- vals = Timecop.freeze(t2) do
64
- Norton.mget([foobar1, foobar2], [:test_counter, :test_timestamp])
58
+ Norton.mget([dummy], %i[counter1 time1])
59
+ expect(dummy.instance_variable_get(:@counter1)).to eq(99)
60
+ expect(dummy.instance_variable_get(:@time1)).to eq(1234)
65
61
  end
66
-
67
- expect(vals).to include("foobars:#{foobar1.id}:test_counter" => 1)
68
- expect(vals).to include("foobars:#{foobar1.id}:test_timestamp" => t2.to_i)
69
- expect(vals).to include("foobars:#{foobar2.id}:test_counter" => 0)
70
- expect(vals).to include("foobars:#{foobar2.id}:test_timestamp" => t.to_i)
71
62
  end
72
63
 
73
- it "should return nil for undefined norton values" do
74
- foobar1 = Foobar.new(SecureRandom.hex(2))
75
- foobar2 = Foobar.new(SecureRandom.hex(2))
64
+ context "when the type is :counter" do
65
+ it "doesn't save the default value in redis if no value in redis" do
66
+ allow(dummy).to receive(:counter1_default_value) { 99 }
76
67
 
77
- t = Time.now
68
+ Norton.mget([dummy], %i[counter1])
78
69
 
79
- Timecop.freeze(t) do
80
- foobar1.incr_test_counter
81
- foobar2.touch_test_timestamp
70
+ value_from_redis = Norton.redis.with do |conn|
71
+ conn.get(dummy.norton_value_key(:counter1))
72
+ end
73
+ expect(value_from_redis).to be(nil)
82
74
  end
83
-
84
- vals = Norton.mget([foobar1, foobar2], [:test_counter, :test_timestamp, :test_foobar])
85
-
86
- expect(vals).to include("foobars:#{foobar1.id}:test_counter" => 1)
87
- expect(vals).to include("foobars:#{foobar2.id}:test_timestamp" => t.to_i)
88
- expect(vals).to include("foobars:#{foobar1.id}:test_foobar" => nil)
89
- expect(vals).to include("foobars:#{foobar2.id}:test_foobar" => nil)
90
75
  end
91
76
 
92
- it "returns the default value when the field value is nil" do
93
- object = Foobar.new(99)
94
- allow(object).to receive(:test_timestamp_default_value) { 22 }
77
+ context "when the type is :timestamp" do
78
+ context "when the attribute doesn't allow nil" do
79
+ it "saves the default value in redis if no value in redis" do
80
+ allow(dummy).to receive(:time1_default_value) { 1234 }
95
81
 
96
- ret = Norton.mget([object], %i[test_timestamp])
97
- expect(ret["foobars:99:test_timestamp"]).to eq(22)
98
- end
82
+ dummy.norton_mget(:time1)
83
+
84
+ value_from_redis = Norton.redis.with do |conn|
85
+ conn.get(dummy.norton_value_key(:time1))
86
+ end.to_i
87
+ expect(value_from_redis).to eq(1234)
88
+ end
89
+ end
99
90
 
100
- it "sets the default value in redis if the `Timestamp` field value is nil" do
101
- object = Foobar.new(99)
102
- allow(object).to receive(:test_timestamp_default_value) { 22 }
91
+ context "when the attribute allow nil" do
92
+ it "doesn't save the default value in redis if no value in redis" do
93
+ allow(dummy).to receive(:time2_default_value) { nil }
103
94
 
104
- Norton.mget([object], %i[test_timestamp])
95
+ dummy.norton_mget(:time2)
105
96
 
106
- expect(Norton.redis.with { |conn| conn.get("foobars:99:test_timestamp") }.to_i).to eq(22)
97
+ expect(
98
+ Norton.redis.with { |conn| conn.exists(dummy.norton_value_key(:time2)) }
99
+ ).to eq(false)
100
+ end
101
+ end
107
102
  end
108
103
  end
109
104
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: norton
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.26
4
+ version: 0.0.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Larry Zhao
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-29 00:00:00.000000000 Z
11
+ date: 2017-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -189,6 +189,7 @@ files:
189
189
  - ".gitignore"
190
190
  - ".rspec"
191
191
  - ".travis.yml"
192
+ - CHANGLOG.md
192
193
  - Gemfile
193
194
  - LICENSE
194
195
  - LICENSE.txt
@@ -230,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
231
  version: '0'
231
232
  requirements: []
232
233
  rubyforge_project:
233
- rubygems_version: 2.6.11
234
+ rubygems_version: 2.6.12
234
235
  signing_key:
235
236
  specification_version: 4
236
237
  summary: Provide simple helpers on persist values in redis for performance. Works