redis_model 0.1.0.pre3

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +187 -0
  6. data/Rakefile +15 -0
  7. data/lib/redis_model/adapters/paperclip.rb +51 -0
  8. data/lib/redis_model/attribute.rb +124 -0
  9. data/lib/redis_model/base.rb +67 -0
  10. data/lib/redis_model/belonged_to.rb +27 -0
  11. data/lib/redis_model/class_attribute.rb +50 -0
  12. data/lib/redis_model/configurations.rb +15 -0
  13. data/lib/redis_model/helpers/sorted_set_paginator.rb +80 -0
  14. data/lib/redis_model/intersected.rb +17 -0
  15. data/lib/redis_model/schema.rb +114 -0
  16. data/lib/redis_model/types/base.rb +32 -0
  17. data/lib/redis_model/types/base_value.rb +26 -0
  18. data/lib/redis_model/types/counter.rb +25 -0
  19. data/lib/redis_model/types/float.rb +17 -0
  20. data/lib/redis_model/types/hash.rb +53 -0
  21. data/lib/redis_model/types/integer.rb +17 -0
  22. data/lib/redis_model/types/list.rb +40 -0
  23. data/lib/redis_model/types/set.rb +59 -0
  24. data/lib/redis_model/types/sorted_set.rb +184 -0
  25. data/lib/redis_model/types/string.rb +11 -0
  26. data/lib/redis_model/types/timestamp.rb +26 -0
  27. data/lib/redis_model/version.rb +3 -0
  28. data/lib/redis_model.rb +37 -0
  29. data/redis_model.gemspec +28 -0
  30. data/spec/redis_model/attribute_spec.rb +77 -0
  31. data/spec/redis_model/base_spec.rb +34 -0
  32. data/spec/redis_model/class_attribute_spec.rb +16 -0
  33. data/spec/redis_model/helpers/sorted_set_paginator_spec.rb +33 -0
  34. data/spec/redis_model/schema_spec.rb +118 -0
  35. data/spec/redis_model/types/base_spec.rb +28 -0
  36. data/spec/redis_model/types/counter_spec.rb +32 -0
  37. data/spec/redis_model/types/float_spec.rb +20 -0
  38. data/spec/redis_model/types/hash_spec.rb +55 -0
  39. data/spec/redis_model/types/integer_spec.rb +22 -0
  40. data/spec/redis_model/types/list_spec.rb +55 -0
  41. data/spec/redis_model/types/set_spec.rb +62 -0
  42. data/spec/redis_model/types/sorted_set_spec.rb +303 -0
  43. data/spec/redis_model/types/string_spec.rb +28 -0
  44. data/spec/redis_model/types/timestamp_spec.rb +22 -0
  45. data/spec/spec_helper.rb +13 -0
  46. data/spec/support/dynamic_class.rb +5 -0
  47. metadata +190 -0
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ class TestModel
4
+ attr_accessor :id, :custom
5
+
6
+ def initialize(id, custom = nil)
7
+ @id = id
8
+ @custom = custom
9
+ end
10
+ end
11
+
12
+ describe RedisModel::Attribute do
13
+ let(:parent_klass) { dynamic_class(TestModel) }
14
+
15
+ before { parent_klass.send(:include, RedisModel::Attribute) }
16
+
17
+ describe '.redis_model_attribute' do
18
+ let(:attribute_name) { :my_counter }
19
+ let(:attribute_type) { :counter }
20
+ let(:test_id) { 'test' }
21
+
22
+ it { expect { parent_klass.redis_model_attribute attribute_name, attribute_type }.to change { parent_klass.new(test_id).respond_to?(attribute_name) } }
23
+ it { expect { parent_klass.redis_model_attribute attribute_name, attribute_type }.to change { parent_klass.new(test_id).respond_to?(:"#{attribute_name}=") } }
24
+
25
+ context 'when belonged_to klass is defined' do
26
+ let(:key_label) { parent_klass.new(test_id).send(attribute_name).key_label }
27
+
28
+ before { parent_klass.redis_model_attribute attribute_name, attribute_type }
29
+
30
+ it { expect(parent_klass.new(test_id).send(attribute_name)).to be_kind_of(RedisModel::BelongedTo) }
31
+ it { expect(parent_klass.new(test_id).send(attribute_name).class.included_modules).to include(RedisModel::Types::Counter) }
32
+ it { expect(key_label).to end_with(test_id) }
33
+ it { expect(key_label).to include(parent_klass.to_s.underscore) }
34
+ end
35
+
36
+ context 'when parent klass is inherited' do
37
+ let(:child_klass) { dynamic_class(parent_klass) }
38
+
39
+ it { expect { parent_klass.redis_model_attribute attribute_name, attribute_type }.to change { child_klass.new(test_id).respond_to?(attribute_name) } }
40
+ it { expect { parent_klass.redis_model_attribute attribute_name, attribute_type }.to change { child_klass.new(test_id).respond_to?(:"#{attribute_name}=") } }
41
+
42
+ context 'when belonged_to klass is defined' do
43
+ before { parent_klass.redis_model_attribute attribute_name, attribute_type }
44
+
45
+ it { expect(parent_klass.new(test_id).send(attribute_name).class).to eq(child_klass.new(test_id).send(attribute_name).class) }
46
+ it { expect(parent_klass.new(test_id).send(attribute_name).key_label).to eq(child_klass.new(test_id).send(attribute_name).key_label) }
47
+ end
48
+ end
49
+
50
+ context 'when foreign_key option is specified' do
51
+ let(:custom_id) { 'customid' }
52
+ let(:key_label) { parent_klass.new(test_id, custom_id).send(attribute_name).key_label }
53
+
54
+ before { parent_klass.redis_model_attribute attribute_name, attribute_type, foreign_key: :custom }
55
+
56
+ it { expect(key_label).to end_with(custom_id) }
57
+ it { expect(key_label).to include(parent_klass.to_s.underscore) }
58
+ it { expect(key_label).to end_with(custom_id) }
59
+ it { expect(key_label).not_to end_with(test_id) }
60
+ end
61
+
62
+ describe '.redis_model_attributes' do
63
+ it { expect(parent_klass.redis_model_attributes).to be_kind_of(RedisModel::Attribute::DefinitionHelper) }
64
+ it { expect { parent_klass.redis_model_attributes { counter :my_counter } }.to change { parent_klass.new(test_id).methods } }
65
+ end
66
+ end
67
+
68
+ describe RedisModel::Attribute::DefinitionHelper do
69
+ let(:parent_klass) { dynamic_class(TestModel) }
70
+ let(:definition_helper) { RedisModel::Attribute::DefinitionHelper.new parent_klass, {} }
71
+
72
+ it { expect(definition_helper).to respond_to(:counter) }
73
+ it { expect(definition_helper).to respond_to(:set) }
74
+ it { expect(definition_helper).to respond_to(:sorted_set) }
75
+ it { expect { definition_helper.counter :my_counter }.to change { parent_klass.new('test').methods } }
76
+ end
77
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel::Base do
4
+ let(:klass) { dynamic_class(RedisModel::Base) }
5
+ let(:another_klass) { dynamic_class(RedisModel::Base, 'Another') }
6
+
7
+ describe '.data_type' do
8
+ it { expect { klass.data_type(:value) }.to change { RedisModel::Schema.collection.keys }.by([klass]) }
9
+ it { expect { klass.data_type(:string) }.to change { RedisModel::Schema.collection[klass] }.from(nil) }
10
+ it { expect { klass.data_type(:string) }.to change { klass.included_modules.include?(RedisModel::Types::String) }.from(false).to(true) }
11
+
12
+ context 'when child class tries to redefine data type' do
13
+ let(:child_klass) { dynamic_class(klass, 'Child') }
14
+
15
+ before { klass.data_type(:value) }
16
+
17
+ it { expect { child_klass.data_type(:value) }.to raise_error(RedisModel::Schema::DuplicateDefinition) }
18
+ end
19
+
20
+ context 'when another class defines its data type' do
21
+ before { klass.data_type(:value) }
22
+
23
+ it { expect { another_klass.data_type(:string) }.not_to change { RedisModel::Schema.find(klass) } }
24
+ end
25
+ end
26
+
27
+ describe '.connection' do
28
+ before { klass.data_type :counter }
29
+ before { another_klass.data_type :string }
30
+
31
+ it { expect(klass.connection).to be_kind_of(Redis) }
32
+ it { expect(klass.connection).to eq(another_klass.connection) }
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'redis_model/class_attribute'
3
+
4
+ class TestClass
5
+ include RedisModel::ClassAttribute
6
+
7
+ redis_class_attribute :something, :set
8
+ end
9
+
10
+ describe RedisModel::ClassAttribute do
11
+ context '.redis_class_attribue' do
12
+ it { expect(TestClass).to be_respond_to(:something) }
13
+ it { expect(TestClass).to be_respond_to(:something=) }
14
+ it { expect(TestClass.something).to be_kind_of(RedisModel::Base) }
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'redis_model/helpers/sorted_set_paginator'
3
+
4
+ describe RedisModel::Helpers::SortedSetPaginator do
5
+ let(:klass) { dynamic_class(RedisModel::Base) }
6
+ let(:sorted_set) { klass.new }
7
+ let(:paginator) { RedisModel::Helpers::SortedSetPaginator.new(sorted_set) }
8
+
9
+ before { klass.data_type :sorted_set }
10
+ before { (1..10).each { |number| sorted_set.put(number, number) } }
11
+
12
+ describe '#page' do
13
+ it { expect(paginator.per(1).page(1).map(&:to_i)).to eq([10]) }
14
+ it { expect(paginator.per(1).page(2).map(&:to_i)).to eq([9]) }
15
+ it { expect(paginator.per(1).page(10).map(&:to_i)).to eq([1]) }
16
+ end
17
+
18
+ describe '#per' do
19
+ it { expect(paginator.per(2).page(1).map(&:to_i)).to eq([10, 9]) }
20
+ it { expect(paginator.per(3).page(1).map(&:to_i)).to eq([10, 9, 8]) }
21
+ it { expect(paginator.per(4).page(1).map(&:to_i)).to eq([10, 9, 8, 7]) }
22
+ end
23
+
24
+ describe '#max_id' do
25
+ it { expect(paginator.max_id(3).map(&:to_i)).to eq([2, 1]) }
26
+ it { expect(paginator.max_id(4).map(&:to_i)).to eq([3, 2, 1]) }
27
+ end
28
+
29
+ describe '#since_id' do
30
+ it { expect(paginator.since_id(8).map(&:to_i)).to eq([10, 9]) }
31
+ it { expect(paginator.since_id(9).map(&:to_i)).to eq([10]) }
32
+ end
33
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+ require 'redis_model/schema'
3
+
4
+ describe RedisModel::Schema do
5
+ let(:random_string) { SecureRandom.base64(4).tr('+/=lIO0', 'pqrsxyz') }
6
+
7
+ describe '.collection' do
8
+ it { expect(RedisModel::Schema.collection).to be_kind_of(Hash) }
9
+ end
10
+
11
+ describe '.find' do
12
+ let(:parent_klass) { Class.new }
13
+ let(:child_klass) { Class.new parent_klass }
14
+
15
+ before { RedisModel::Schema.collection.clear }
16
+
17
+ context 'when there is no entry of direct ancestor' do
18
+ it { expect(RedisModel::Schema.find(parent_klass)).to be_nil }
19
+ it { expect(RedisModel::Schema.find(child_klass)).to be_nil }
20
+ end
21
+
22
+ context 'when collection is populated' do
23
+ let(:value) { 1234 }
24
+
25
+ before { RedisModel::Schema.collection[child_klass] = value }
26
+
27
+ it { expect(RedisModel::Schema.find(child_klass)).to eq(value) }
28
+ it { expect(RedisModel::Schema.find(parent_klass)).to be_nil }
29
+ end
30
+
31
+ context 'when collection is populated by both' do
32
+ let(:parent_value) { 1234 }
33
+ let(:child_value) { 4321 }
34
+
35
+ before { RedisModel::Schema.collection[parent_klass] = parent_value }
36
+ before { RedisModel::Schema.collection[child_klass] = child_value }
37
+
38
+ it { expect(RedisModel::Schema.find(parent_klass)).to eq(parent_value) }
39
+ it { expect(RedisModel::Schema.find(child_klass)).to eq(child_value) }
40
+ end
41
+ end
42
+
43
+ describe '.register' do
44
+ let(:parent_klass) { Class.new.tap { |k| Object.const_set("Parent#{random_string}", k) } }
45
+ let(:child_klass) { Class.new parent_klass.tap { |k| Object.const_set("Child#{random_string}", k) } }
46
+
47
+ before { RedisModel::Schema.collection.clear }
48
+
49
+ context 'when data_type is invalid' do
50
+ it { expect { RedisModel::Schema.register(parent_klass, data_type: :nothing) }.to raise_error(RedisModel::Schema::UnknownType) }
51
+ end
52
+
53
+ context 'when data_type is valid' do
54
+ it { expect { RedisModel::Schema.register(parent_klass, data_type: :string) }.not_to raise_error }
55
+ it { expect(RedisModel::Schema.register(parent_klass, data_type: :string)).to eq(RedisModel::Types::String) }
56
+ end
57
+ end
58
+
59
+ describe '#initialize' do
60
+ context 'when invalid data_type key is given' do
61
+ it { expect { RedisModel::Schema.new(data_type: :something_i_dont_know) }.to raise_error(RedisModel::Schema::UnknownType) }
62
+ end
63
+
64
+ context 'when valid data_type key is given' do
65
+ let(:klass) { Class.new.tap { |k| Object.const_set("Parent#{random_string}", k) } }
66
+ let(:data_type) { :string }
67
+ let(:schema) { RedisModel::Schema.new(data_type: data_type, klass: klass) }
68
+
69
+ it { expect { schema }.not_to raise_error }
70
+ it { expect(schema.klass).to eq(klass) }
71
+ it { expect(schema.data_type).to eq(data_type) }
72
+ end
73
+ end
74
+
75
+ describe '#custom_key_label' do
76
+ let(:string_child_klass) { Class.new(String).tap { |k| Object.const_set("String#{random_string}", k) } }
77
+ let(:schema) { RedisModel::Schema.new data_type: :string, klass: string_child_klass }
78
+ let(:object) { string_child_klass.new('test') }
79
+
80
+ context 'when block is given' do
81
+ it { expect { schema.custom_key_label { |o| o.reverse } }.not_to raise_error }
82
+ it { expect { schema.custom_key_label { |o| o.reverse } }.to change { schema.key_label(object).end_with?(':tset') } }
83
+ end
84
+
85
+ context 'when method is given' do
86
+ it { expect { schema.custom_key_label(&:reverse) }.not_to raise_error }
87
+ it { expect { schema.custom_key_label(&:reverse) }.to change { schema.key_label(object).end_with?(':tset') } }
88
+ end
89
+ end
90
+
91
+ describe '#key_label' do
92
+ let(:klass) { Class.new.tap { |k| Object.const_set("Klass#{random_string}", k) } }
93
+ let(:schema) { RedisModel::Schema.new klass: klass, data_type: :string }
94
+ let(:object) { klass.new }
95
+ let(:app_name) { nil }
96
+
97
+ before { RedisModel::Configurations.instance.app_name = app_name }
98
+
99
+ context 'when no custom key labels and app name present' do
100
+ it { expect(schema.key_label(object)).to include(klass.name.underscore) }
101
+ end
102
+
103
+ context 'when custom key label presents' do
104
+ let(:label_text) { 'test' }
105
+ before { schema.custom_key_label { |o| label_text } }
106
+
107
+ it { expect(schema.key_label(object)).to include(klass.name.underscore) }
108
+ it { expect(schema.key_label(object)).to include(label_text) }
109
+ end
110
+
111
+ context 'when app name presents' do
112
+ let(:app_name) { 'app' }
113
+
114
+ it { expect(schema.key_label(object)).to include(klass.name.underscore) }
115
+ it { expect(schema.key_label(object)).to include(app_name) }
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel::Types::Base do
4
+ let(:klass) { dynamic_class(RedisModel::Base) }
5
+ let(:object) { klass.new }
6
+
7
+ before { RedisModel::Schema.register(klass, data_type: :string) }
8
+ before { klass.send(:include, RedisModel::Types::Base) }
9
+
10
+ describe '#exists?' do
11
+ it { expect(object.exists?).to be_false }
12
+ it { expect { RedisModel::Base.connection.set(object.key_label, 'value') }.to change { object.exists? } }
13
+ end
14
+
15
+ describe '#del' do
16
+ context 'when object exists' do
17
+ before { RedisModel::Base.connection.set(object.key_label, 'value') }
18
+
19
+ it { expect { object.del }.to change { object.exists? } }
20
+ it { expect(object.del).to eq(1) }
21
+ end
22
+
23
+ context 'when object does not exist' do
24
+ it { expect { object.del }.not_to change { object.exists? } }
25
+ it { expect(object.del).to eq(0) }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel::Types::Counter do
4
+ let(:klass) { dynamic_class(RedisModel::Base) }
5
+ let(:object) { klass.new }
6
+
7
+ before { klass.data_type :counter }
8
+
9
+ describe '#incr' do
10
+ it { expect { object.incr }.to change { object.to_i }.from(0).to(1) }
11
+ it { expect(object.incr).to eq(1) }
12
+
13
+ context 'when argument is given' do
14
+ let(:by) { 3 }
15
+
16
+ it { expect { object.incr(by) }.to change { object.to_i }.from(0).to(by) }
17
+ it { expect(object.incr(by)).to eq(by) }
18
+ end
19
+ end
20
+
21
+ describe '#to_i' do
22
+ it { expect(object.to_i).to eq(0) }
23
+
24
+ context 'when value was set previously' do
25
+ let(:value) { 3 }
26
+
27
+ before { RedisModel::Base.connection.set(object.key_label, value) }
28
+
29
+ it { expect(object.to_i).to eq(value) }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel::Types::Float do
4
+ let(:klass) { dynamic_class(RedisModel::Base) }
5
+ let(:object) { klass.new }
6
+
7
+ before { klass.data_type :float }
8
+
9
+ describe '#to_f' do
10
+ it { expect(object.to_f).to be_nil }
11
+
12
+ context 'when value was set before' do
13
+ let(:value) { 1.23 }
14
+
15
+ before { object.set 1.23 }
16
+
17
+ it { expect(object.to_f).to eq(1.23) }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel::Types::Hash do
4
+ let(:klass) { dynamic_class(RedisModel::Base) }
5
+ let(:object) { klass.new }
6
+
7
+ before { klass.data_type :hash }
8
+
9
+ describe '#[]=' do
10
+ let(:key) { :some_key }
11
+ let(:value) { 'hello world' }
12
+
13
+ it { expect(object.[]=(key, value)).to eq(value) }
14
+ end
15
+
16
+ describe '#[]' do
17
+ let(:key) { :some_key }
18
+
19
+ context 'when value was not set before' do
20
+ it { expect(object[key]).to be_nil }
21
+ end
22
+
23
+ context 'when value was set before' do
24
+ let(:value) { 'hello world' }
25
+
26
+ before { object[key] = value }
27
+
28
+ it { expect(object[key]).to eq(value) }
29
+ end
30
+ end
31
+
32
+ describe '#incr' do
33
+ let(:key) { :some_key }
34
+
35
+ context 'when key does not exist before' do
36
+ it { expect(object.incr(key)).to eq(1) }
37
+ it { expect { object.incr(key) }.to change { object[key] }.from(nil).to(1.to_s) }
38
+ end
39
+
40
+ context 'when amount is given' do
41
+ let(:by) { 3 }
42
+
43
+ it { expect(object.incr(key, by)).to eq(by) }
44
+ it { expect { object.incr(key, by) }.to change { object[key] }.from(nil).to(by.to_s) }
45
+ end
46
+
47
+ context 'when key was set before' do
48
+ let(:initial) { 2 }
49
+
50
+ before { object[key] = initial }
51
+
52
+ it { expect { object.incr(key) }.to change { object[key] }.from(initial.to_s).to((initial + 1).to_s) }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel::Types::Integer do
4
+ let(:klass) { dynamic_class(RedisModel::Base) }
5
+ let(:object) { klass.new }
6
+
7
+ before { klass.data_type :integer }
8
+
9
+ describe '#to_i' do
10
+ context 'when no value was set before' do
11
+ it { expect(object.to_i).to be_nil }
12
+ end
13
+
14
+ context 'when value was set before' do
15
+ let(:value) { 2 }
16
+
17
+ before { RedisModel::Base.connection.set(object.key_label, value) }
18
+
19
+ it { expect(object.to_i).to eq(value) }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel::Types::List do
4
+ let(:klass) { dynamic_class(RedisModel::Base) }
5
+ let(:object) { klass.new }
6
+
7
+ before { klass.data_type :list }
8
+
9
+ describe '#to_a' do
10
+ context 'when list does not exist' do
11
+ it { expect(object.to_a).to eq([]) }
12
+ end
13
+
14
+ context 'when list has been populated before' do
15
+ let(:elements) { ['testelement1', 'testelement2'] }
16
+
17
+ before { elements.each { |element| RedisModel::Base.connection.rpush(object.key_label, element) } }
18
+
19
+ it { expect(object.to_a).to eq(elements) }
20
+ end
21
+ end
22
+
23
+ describe '#count' do
24
+ context 'when list does not exist' do
25
+ it { expect(object.count).to eq(0) }
26
+ end
27
+
28
+ context 'when list has been populated before' do
29
+ let(:elements) { ['testelement1', 'testelement2'] }
30
+
31
+ before { elements.each { |element| RedisModel::Base.connection.rpush object.key_label, element } }
32
+
33
+ it { expect(object.count).to eq(elements.length) }
34
+ end
35
+ end
36
+
37
+ describe '#[]' do
38
+ context 'when list does not exist' do
39
+ it { expect(object[1]).to be_nil }
40
+ end
41
+
42
+ context 'when list has been populated before' do
43
+ let(:elements) { ['testelement1', 'testelement2'] }
44
+
45
+ before { elements.each { |element| RedisModel::Base.connection.rpush object.key_label, element } }
46
+
47
+ it { expect(object[0]).to eq(elements[0]) }
48
+ it { expect(object[1]).to eq(elements[1]) }
49
+ end
50
+ end
51
+
52
+ describe '#<<' do
53
+ it { expect { object << 1 }.to change { object.count }.by(1) }
54
+ end
55
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel::Types::Set do
4
+ let(:klass) { dynamic_class(RedisModel::Base) }
5
+ let(:object) { klass.new }
6
+
7
+ before { klass.data_type :set }
8
+
9
+ describe '#to_a' do
10
+ context 'when set does not exist' do
11
+ it { expect(object.to_a).to eq([]) }
12
+ end
13
+
14
+ context 'when set has been populated before' do
15
+ let(:elements) { ['testelement1', 'testelement2'] }
16
+
17
+ before { elements.each { |element| RedisModel::Base.connection.sadd(object.key_label, element) } }
18
+
19
+ it { expect(object.to_a.sort).to eq(elements.sort) }
20
+ end
21
+ end
22
+
23
+ describe '#count' do
24
+ context 'when set does not exist' do
25
+ it { expect(object.count).to eq(0) }
26
+ end
27
+
28
+ context 'when set has been populated before' do
29
+ let(:elements) { ['testelement1', 'testelement2'] }
30
+
31
+ before { elements.each { |element| RedisModel::Base.connection.sadd(object.key_label, element) } }
32
+
33
+ it { expect(object.count).to eq(elements.count) }
34
+ end
35
+ end
36
+
37
+ describe '#<<' do
38
+ it { expect { object << 1 }.to change { object.count }.by(1) }
39
+ end
40
+
41
+ describe '#remove' do
42
+ let(:elements) { ['testelement1', 'testelement2'] }
43
+
44
+ before { elements.each { |element| RedisModel::Base.connection.sadd(object.key_label, element) } }
45
+
46
+ context 'when removing existing element' do
47
+ it { expect { object.remove(elements.first) }.to change { object.count }.by(-1) }
48
+ end
49
+
50
+ context 'when removing non-existent element' do
51
+ it { expect { object.remove('nonexistent') }.not_to change { object.count } }
52
+ end
53
+ end
54
+
55
+ describe '#pick' do
56
+ let(:elements) { ['testelement1', 'testelement2'] }
57
+
58
+ before { elements.each { |element| RedisModel::Base.connection.sadd(object.key_label, element) } }
59
+
60
+ it { expect(object.pick(5).all? { |e| elements.include?(e) }).to be_true }
61
+ end
62
+ end