redis_model 0.1.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
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