norton 0.0.26 → 0.0.28

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.
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