mongoid_atomic_votes 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 474842edee2815d2d65750077a75894ac7f67dc0
4
- data.tar.gz: 414e249a6a8bd8c9bfb4852381f7d47f6131bdf7
3
+ metadata.gz: 18279cb81ff240c02a926b21066c461c80dfa500
4
+ data.tar.gz: 2234359382750bfb52027fff45933ae589df3897
5
5
  SHA512:
6
- metadata.gz: d7e410e7eb861576dde400586ee450bf7514ce6202a4c14b5aa01b4c2b9b97339cfd6d06cde5006ca21ab6d62f2d9a616bcb76b7a916904cdaf509fa6582b6ad
7
- data.tar.gz: 2bbbc2ff81776a89cef0f4878d6bd3ff6f72c7cc6338d97c96c2b8c71cabda3cfb653fe01cf0abc33a049814e0126f2427da34515ebad156ab7aa82b2ad8e875
6
+ metadata.gz: 5c135a92ae45d11af9af5fcea96d6469c614b982ef7aa5573ef364c21ea8807ff1875556b91880231265e5eb7ebe4bdda5286a9affb4586b8adec514a93bda65
7
+ data.tar.gz: c8c59df6c14596b8a2efa96041cf68f5bed0071efdc44f051f29b7ed55a40959e99fe1617a4a8560bd88b9e287f1b88be8376dd4a6e58282436c88ba70b9010a
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  .idea
19
+ .gems
@@ -1,7 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.3"
4
3
  - "2.0.0"
4
+ - "2.3.0"
5
5
  services:
6
6
  - mongodb
7
7
  script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -8,5 +8,4 @@ group :test do
8
8
  gem 'factory_girl'
9
9
  gem 'simplecov', require: false
10
10
  gem 'database_cleaner'
11
- gem 'pry'
12
11
  end
@@ -2,100 +2,160 @@ module Mongoid
2
2
  module AtomicVotes
3
3
  class << self
4
4
  def included(base)
5
+ define_fields(base)
5
6
  define_relations(base)
6
7
  define_scopes(base)
8
+
7
9
  base.extend ClassMethods
8
10
  end
9
11
 
10
12
  private
11
- def define_relations(base)
13
+ def define_fields(base)
12
14
  base.field :vote_count, type: Integer, default: 0
13
15
  base.field :vote_value, type: Float, default: nil
16
+ end
17
+
18
+ def define_relations(base)
14
19
  base.embeds_many :votes, class_name: 'Mongoid::AtomicVotes::Vote', as: :atomic_voteable
15
20
  end
16
21
 
17
22
  def define_scopes(base)
18
- base.scope :not_voted, -> { base.where(:vote_value.exists => false) }
19
- base.scope :voted, -> { base.where(:vote_value.exists => true) }
20
- base.scope :voted_by, ->(resource) do
21
- base.where('votes.voted_by_id' => resource.id, 'votes.voter_type' => resource.class.name)
22
- end
23
- base.scope :vote_value_in, ->(range) do
24
- base.where(:vote_value.gte => range.begin, :vote_value.lte => range.end)
25
- end
26
- base.scope :highest_voted, ->(limit=10) { base.order_by(:vote_value.desc).limit(limit) }
23
+ scopes(base).each { |name, block| base.scope name, block }
24
+ end
25
+
26
+ def scopes(base)
27
+ {
28
+ not_voted: -> { base.where(:vote_value.exists => false) },
29
+ voted: -> { base.where(:vote_value.exists => true) },
30
+ voted_by: ->(resource) {
31
+ base.where(
32
+ 'votes.voted_by_id' => resource.id,
33
+ 'votes.voter_type' => resource.class.name
34
+ )
35
+ },
36
+ vote_value_in: ->(range) {
37
+ base.where(
38
+ :vote_value.gte => range.begin,
39
+ :vote_value.lte => range.end
40
+ )
41
+ },
42
+ highest_voted: ->(limit=10) { base.order_by(:vote_value.desc).limit(limit) }
43
+ }
27
44
  end
28
45
  end
29
46
 
47
+ # Creates an embedded vote record and updates number of votes and vote value.
48
+ #
49
+ # @param [Int,Float] value vote value
50
+ # @param [Mongoid::Document] voted_by object from which the vote is done
51
+ # @return [Boolean] success flag
30
52
  def vote(value, voted_by)
31
53
  mark = Vote.new(value: value, voted_by_id: voted_by.id, voter_type: voted_by.class.name)
32
- return false unless mark.valid?
33
54
  add_vote_mark(mark)
34
55
  end
35
56
 
57
+ # Removes previously added vote.
58
+ #
59
+ # @param [Mongoid::Document] voted_by object from which the vote was done
60
+ # @return [Boolean] success flag
36
61
  def retract(voted_by)
37
62
  mark = self.votes.find_by(voted_by_id: voted_by.id)
38
- return false unless mark
39
- remove_vote_mark(mark)
63
+ mark && remove_vote_mark(mark)
40
64
  end
41
65
 
66
+ # Indicates whether the document has votes or not.
67
+ #
68
+ # @return [Boolean]
42
69
  def has_votes?
43
70
  self.vote_count > 0
44
71
  end
45
72
 
73
+ # Indicates whether the document has a vote from particular voter object
74
+ #
75
+ # @param [Mongoid::Document] voted_by object from which the vote was done
76
+ # @return [Boolean]
46
77
  def voted_by?(voted_by)
47
78
  !!self.votes.find_by(voted_by_id: voted_by.id)
48
- rescue
79
+ rescue NoMethodError, Mongoid::Errors::DocumentNotFound
49
80
  false
50
81
  end
51
82
 
52
83
  module ClassMethods
84
+ attr_reader :vote_range
85
+
86
+ # Specifies possible vote range which is used vote mark validation later.
87
+ #
88
+ # @param [Range] val new vote range, for example: 1..5
89
+ # @return [Range] vote range, previously passed to a method as a parameter
53
90
  def set_vote_range(val)
54
- raise 'argument should be a Range' unless val.is_a?(Range)
55
- Vote.send(__method__, val)
91
+ fail ArgumentError.new('argument should be a Range') unless val.is_a?(Range)
92
+ @vote_range = val
93
+ end
94
+
95
+ # Sets vote range to nil
96
+ # @return [NilClass] nil
97
+ def reset_vote_range
98
+ @vote_range = nil
56
99
  end
57
100
  end
58
101
 
59
102
  private
60
- def update_votes(mark, retract=false)
61
- opts = {
62
- '$inc' => { vote_count: retract ? -1 : 1 },
63
- '$set' => { vote_value: self.vote_value }
64
- }
65
103
 
66
- if retract
67
- opts['$pull'] = { votes: { _id: mark.id } }
104
+ def update_vote_counters(mark, increment_count_by)
105
+ self.vote_value = calculate_vote_value(mark.value, increment_count_by)
106
+ self.vote_count += increment_count_by
107
+ end
108
+
109
+ def calculate_vote_value(value, count)
110
+ current_vote_count = self.vote_count
111
+ new_vote_count = current_vote_count + count
112
+
113
+ if new_vote_count > 0
114
+ (current_vote_count * self.vote_value.to_f + value * count) / new_vote_count
68
115
  else
69
- opts['$push'] = { votes: mark.as_json }
116
+ nil
70
117
  end
118
+ end
71
119
 
72
- self.collection.find(_id: self.id).update_one(opts).modified_count > 0
120
+ def update_votes(query_opts)
121
+ self.collection.find(_id: self.id).update_one(query_opts).modified_count > 0
73
122
  end
74
123
 
75
- def update_vote_value(mark, retract=false)
76
- value, vote_count_diff = [mark.value, 1].map { |v| v * (retract ? -1 : 1) }
77
- self.vote_value = if self.vote_count == 1 && retract
78
- nil
79
- else
80
- (self.vote_count * self.vote_value.to_f + value) / (self.vote_count + vote_count_diff)
81
- end
82
- self.vote_count += vote_count_diff
124
+ def vote_options(mark)
125
+ {
126
+ '$inc' => { vote_count: 1 },
127
+ '$set' => { vote_value: self.vote_value },
128
+ '$push' => { votes: mark.as_json }
129
+ }
130
+ end
131
+
132
+ def retract_options(mark)
133
+ {
134
+ '$inc' => { vote_count: -1 },
135
+ '$set' => { vote_value: self.vote_value },
136
+ '$pull' => { votes: { _id: mark.id } }
137
+ }
83
138
  end
84
139
 
85
140
  def add_vote_mark(mark)
141
+ mark_is_valid = false
142
+
86
143
  _assigning do
87
144
  self.votes << mark
88
- update_vote_value(mark)
145
+ mark_is_valid = mark.valid?
146
+ mark_is_valid && update_vote_counters(mark, 1)
89
147
  end
90
- update_votes(mark)
148
+
149
+ mark_is_valid && update_votes(vote_options(mark))
91
150
  end
92
151
 
93
152
  def remove_vote_mark(mark)
94
153
  _assigning do
95
- self.votes.reject! { |v| v.id == mark.id }
96
- update_vote_value(mark, true)
154
+ self.votes.reject! { |vote| vote.id == mark.id }
155
+ update_vote_counters(mark, -1)
97
156
  end
98
- update_votes(mark, true)
157
+
158
+ update_votes(retract_options(mark))
99
159
  end
100
160
  end
101
161
  end
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module AtomicVotes
3
- VERSION = '0.2.1'
3
+ VERSION = '1.0.0'
4
4
  end
5
5
  end
@@ -9,19 +9,9 @@ class Mongoid::AtomicVotes::Vote
9
9
  field :voter_type, type: String
10
10
 
11
11
  validates_presence_of :value, :voted_by_id, :voter_type
12
- validates_inclusion_of :value, in: ->(vote) { vote.class.vote_range }, if: ->(vote) { vote.class.vote_range }
12
+ validates_inclusion_of :value,
13
+ in: ->(vote) { vote.atomic_voteable.class.vote_range },
14
+ if: ->(vote) { vote.atomic_voteable.class.vote_range }
13
15
 
14
16
  index({ voted_by_id: 1, mark: 1 }, unique: true, background: true)
15
-
16
- class << self
17
- @@vote_range = nil
18
-
19
- def set_vote_range(val)
20
- @@vote_range ||= val
21
- end
22
-
23
- def vote_range
24
- @@vote_range
25
- end
26
- end
27
17
  end
@@ -1,9 +1,15 @@
1
1
  FactoryGirl.define do
2
2
  factory :user do
3
- sequence(:login) {|n| "user_#{n}"}
3
+ sequence(:login) { |n| "user_#{n}" }
4
4
  end
5
5
 
6
6
  factory :post do
7
- sequence(:title) {|n| "post_#{n}"}
7
+ sequence(:title) { |n| "post_#{n}" }
8
+ end
9
+
10
+ factory :vote, class: 'Mongoid::AtomicVotes::Vote' do
11
+ value { rand(0..10) }
12
+ sequence(:voted_by_id) { |n| n }
13
+ voter_type 'User'
8
14
  end
9
15
  end
@@ -0,0 +1,194 @@
1
+ require 'spec_helper'
2
+
3
+ describe Post do
4
+ let!(:users) { FactoryGirl.create_list(:user, 2) }
5
+ let!(:post) { FactoryGirl.create(:post) }
6
+
7
+ it 'has Mongoid::AtomicVotes module' do
8
+ expect(subject.class.ancestors).to include(Mongoid::AtomicVotes)
9
+ end
10
+
11
+ it { expect(subject).to respond_to(:vote) }
12
+ it { expect(subject).to respond_to(:retract) }
13
+ it { expect(subject).to respond_to(:has_votes?) }
14
+ it { expect(subject).to respond_to(:vote_count) }
15
+ it { expect(subject).to respond_to(:vote_value) }
16
+ it { expect(subject).to respond_to(:votes) }
17
+
18
+ it { expect(subject.class).to respond_to(:set_vote_range) }
19
+ it { expect { subject.class.set_vote_range(1) }.to raise_error('argument should be a Range') }
20
+
21
+ describe '#votes' do
22
+ it { expect(post.votes).to be_an_instance_of(Array) }
23
+ end
24
+
25
+ describe 'when does not have votes' do
26
+ it 'is in not_voted scope' do
27
+ expect(subject.class.not_voted).to include(post)
28
+ end
29
+
30
+ it 'is not in voted scope' do
31
+ expect(subject.class.voted).not_to include(post)
32
+ end
33
+
34
+ it 'is not in voted_by scope' do
35
+ expect(users.all? { |user| subject.class.voted_by(user).include?(post) == false }).to eq(true)
36
+ end
37
+
38
+ it '#vote_count returns 0' do
39
+ expect(post.vote_count).to eq(0)
40
+ end
41
+
42
+ it '#vote_value returns nil' do
43
+ expect(post.vote_value).to be_nil
44
+ end
45
+
46
+ it '#votes returns empty array' do
47
+ expect(post.votes).to eq([])
48
+ end
49
+
50
+ it '#has_votes? returns false' do
51
+ expect(post.has_votes?).to eq(false)
52
+ end
53
+
54
+ it '#voted_by? returns false for any voter' do
55
+ expect(users.all? { |user| post.voted_by?(user) == false }).to eq(true)
56
+ end
57
+
58
+ describe '#vote' do
59
+ let(:votes) { [rand(1..10), rand(1..10)] }
60
+ let(:expected_vote_value) { votes.sum.to_f / users.size }
61
+
62
+ it 'returns true on successfull vote' do
63
+ expect(users.all? { |user| post.vote(rand(1..10), user) == true }).to eq(true)
64
+ end
65
+
66
+ it 'properly calculates vote value' do
67
+ users.each_with_index { |user, index| post.vote(votes[index], user) }
68
+ expect(post.vote_value).to eq(expected_vote_value)
69
+ end
70
+
71
+ it 'properly adds vote' do
72
+ expect { post.vote(rand(1..10), users.sample) }.to change { post.vote_count }.by(1)
73
+ end
74
+
75
+ it 'persists changes to database' do
76
+ users.each_with_index { |user, index| post.vote(votes[index], user) }
77
+
78
+ db_post = Post.find(post.id)
79
+ votes_info = [
80
+ db_post.vote_value,
81
+ db_post.vote_count,
82
+ db_post.votes.size
83
+ ]
84
+ expect(votes_info).to eq([expected_vote_value, users.size, users.size])
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'when has votes' do
90
+ before(:each) do
91
+ users.each { |user| post.vote(rand(1..10), user) }
92
+ end
93
+
94
+ it 'is not in the not_voted scope' do
95
+ expect(subject.class.not_voted).not_to include(post)
96
+ end
97
+
98
+ it 'is in voted scope' do
99
+ expect(subject.class.voted).to include(post)
100
+ end
101
+
102
+ it 'is in voted_by scope for each voted user' do
103
+ expect(users.all? { |user| subject.class.voted_by(user).include?(post) == true }).to eq(true)
104
+ end
105
+
106
+ it '#vote_count returns count of votes' do
107
+ expect(post.vote_count).to eq(post.votes.size)
108
+ end
109
+
110
+ it '#vote_value returns actual vote value' do
111
+ expected_vote_value = post.votes.map(&:value).sum.to_f / post.votes.size
112
+ expect(post.vote_value).to eq(expected_vote_value)
113
+ end
114
+
115
+ describe '#votes' do
116
+ let(:votes) { post.votes }
117
+
118
+ it { expect(votes.class).to eq(Array) }
119
+ it { expect(votes).not_to be_empty }
120
+ it { expect(votes.size).to eq(users.size) }
121
+ it { expect(votes.map(&:voted_by_id)).to match_array(users.map(&:id)) }
122
+ end
123
+
124
+ it '#has_votes? returns true' do
125
+ expect(post.has_votes?).to eq(true)
126
+ end
127
+
128
+ it '#voted_by? returns true for all voted resource' do
129
+ expect(users.all? { |user| post.voted_by?(user) == true }).to eq(true)
130
+ end
131
+
132
+ describe '#retract' do
133
+ it 'returns true on successfull vote retract' do
134
+ expect(users.all? { |user| post.retract(user) == true }).to eq(true)
135
+ end
136
+
137
+ it 'properly calculates vote value' do
138
+ user = users.sample
139
+ retracted_vote_value = post.votes.find_by(voted_by_id: user.id).value
140
+ expected_vote_value = (post.votes.map(&:value).sum.to_f - retracted_vote_value) / (users.size - 1)
141
+
142
+ post.retract(user)
143
+ expect(post.vote_value).to eq(expected_vote_value)
144
+ end
145
+
146
+ it 'properly decreases vote count' do
147
+ expect { post.retract(users.sample) }.to change { post.vote_count }.by(-1)
148
+ end
149
+
150
+ it 'persists changes to database' do
151
+ user = users.sample
152
+ retracted_vote_value = post.votes.find_by(voted_by_id: user.id).value
153
+ expected_vote_value = (post.votes.map(&:value).sum.to_f - retracted_vote_value) / (users.size - 1)
154
+
155
+ post.retract(user)
156
+
157
+ db_post = Post.find(post.id)
158
+ votes_info = [
159
+ db_post.vote_value,
160
+ db_post.vote_count,
161
+ db_post.votes.size
162
+ ]
163
+ expect(votes_info).to eq([expected_vote_value, users.size - 1, users.size - 1])
164
+ end
165
+ end
166
+ end
167
+
168
+ describe 'when model has vote_range' do
169
+ before { Post.set_vote_range(1..5) }
170
+
171
+ it 'marks vote as invalid if value is out of range' do
172
+ post.vote(rand(6..10), users.sample)
173
+ expect(post.votes.last).not_to be_valid
174
+ end
175
+
176
+ it 'marks post as invalid' do
177
+ post.vote(rand(6..10), users.sample)
178
+ expect(post).not_to be_valid
179
+ end
180
+
181
+ it 'has properly filled error messages for invalid vote' do
182
+ post.vote(rand(6..10), users.sample)
183
+ expect(post.votes.last.errors.messages[:value].first).to match(/included in the list/)
184
+ end
185
+
186
+ describe '#reset_vote_range' do
187
+ let(:klass) { post.class }
188
+
189
+ it 'sets vote_range to nil' do
190
+ expect { klass.reset_vote_range }.to change { klass.vote_range }.to(nil)
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Mongoid::AtomicVotes::Vote do
4
+ let(:vote) { FactoryGirl.build(:vote, atomic_voteable: post) }
5
+ let(:post) { FactoryGirl.create(:post) }
6
+
7
+ it { expect(vote).to be_valid }
8
+
9
+ it 'is not valid without vote value' do
10
+ vote.value = nil
11
+ expect(vote).not_to be_valid
12
+ end
13
+
14
+ it 'is not valid without voted_by_id' do
15
+ vote.voted_by_id = nil
16
+ expect(vote).not_to be_valid
17
+ end
18
+
19
+ it 'is not valid without voter type' do
20
+ vote.voter_type = nil
21
+ expect(vote).not_to be_valid
22
+ end
23
+
24
+ context 'with vote range' do
25
+ before do
26
+ vote.atomic_voteable = post
27
+ post.class.set_vote_range(2..5)
28
+ end
29
+
30
+ after { post.class.reset_vote_range }
31
+
32
+ it 'is valid if value is in range specified in vote_range' do
33
+ vote.value = rand(post.class.vote_range)
34
+ expect(vote).to be_valid
35
+ end
36
+
37
+ it 'is not valid if value is out of range specified in vote_range' do
38
+ vote.value = [1, 6].sample
39
+ expect(vote).not_to be_valid
40
+ end
41
+ end
42
+ end
@@ -3,6 +3,8 @@ MODELS = File.join(File.dirname(__FILE__), 'models')
3
3
  require 'rubygems'
4
4
  require 'database_cleaner'
5
5
  require 'factory_girl'
6
+ require 'mongoid'
7
+ require 'mongoid_atomic_votes'
6
8
  require 'simplecov'
7
9
 
8
10
  SimpleCov.start do
@@ -10,9 +12,6 @@ SimpleCov.start do
10
12
  add_filter '/.bundle/'
11
13
  end
12
14
 
13
- require 'mongoid'
14
- require 'mongoid_atomic_votes'
15
-
16
15
  Dir["#{MODELS}/*.rb"].each { |f| require f }
17
16
 
18
17
  Mongoid.configure do |config|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_atomic_votes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hck
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-24 00:00:00.000000000 Z
11
+ date: 2016-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongoid
@@ -42,8 +42,9 @@ files:
42
42
  - lib/mongoid_atomic_votes/version.rb
43
43
  - lib/mongoid_atomic_votes/vote.rb
44
44
  - mongoid_atomic_votes.gemspec
45
- - spec/atomic_votes_spec.rb
46
45
  - spec/factories/factories.rb
46
+ - spec/lib/atomic_votes_spec.rb
47
+ - spec/lib/vote_spec.rb
47
48
  - spec/models/post.rb
48
49
  - spec/models/user.rb
49
50
  - spec/spec_helper.rb
@@ -66,13 +67,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
67
  version: '0'
67
68
  requirements: []
68
69
  rubyforge_project:
69
- rubygems_version: 2.4.5
70
+ rubygems_version: 2.5.1
70
71
  signing_key:
71
72
  specification_version: 4
72
73
  summary: Atomic votes implementation for mongoid
73
74
  test_files:
74
- - spec/atomic_votes_spec.rb
75
75
  - spec/factories/factories.rb
76
+ - spec/lib/atomic_votes_spec.rb
77
+ - spec/lib/vote_spec.rb
76
78
  - spec/models/post.rb
77
79
  - spec/models/user.rb
78
80
  - spec/spec_helper.rb
@@ -1,171 +0,0 @@
1
- require 'pry'
2
- require 'spec_helper'
3
-
4
- describe Post do
5
- before(:each) do
6
- @users = FactoryGirl.create_list(:user, 2)
7
- @post = FactoryGirl.create(:post)
8
- end
9
-
10
- it 'has Mongoid::AtomicVotes module' do
11
- expect(subject.class.ancestors).to include(Mongoid::AtomicVotes)
12
- end
13
-
14
- it { expect(subject).to respond_to(:vote) }
15
- it { expect(subject).to respond_to(:retract) }
16
- it { expect(subject).to respond_to(:has_votes?) }
17
- it { expect(subject).to respond_to(:vote_count) }
18
- it { expect(subject).to respond_to(:vote_value) }
19
- it { expect(subject).to respond_to(:votes) }
20
-
21
- it { expect(subject.class).to respond_to(:set_vote_range) }
22
- it { expect { subject.class.set_vote_range(1) }.to raise_error('argument should be a Range') }
23
-
24
- describe '#votes' do
25
- it 'is array of votes' do
26
- expect(@post.votes).to be_an_instance_of(Array)
27
- end
28
- end
29
-
30
- describe 'when does not have votes' do
31
- it 'is in not_voted scope' do
32
- expect(subject.class.not_voted).to include(@post)
33
- end
34
-
35
- it 'is not in voted scope' do
36
- expect(subject.class.voted).not_to include(@post)
37
- end
38
-
39
- it 'is not in voted_by scope' do
40
- @users.each do |u|
41
- expect(subject.class.voted_by(u)).not_to include(@post)
42
- end
43
- end
44
-
45
- it '#vote_count returns 0' do
46
- expect(@post.vote_count).to eq(0)
47
- end
48
-
49
- it '#vote_value returns nil' do
50
- expect(@post.vote_value).to be_nil
51
- end
52
-
53
- it '#votes returns empty array' do
54
- expect(@post.votes).to eq([])
55
- end
56
-
57
- it '#has_votes? returns false' do
58
- expect(@post.vote_count).to eq(0)
59
- expect(@post.has_votes?).to be_falsey
60
- end
61
-
62
- it '#voted_by? returns false with any resource' do
63
- @users.each do |u|
64
- expect(@post.voted_by?(u)).to be_falsey
65
- end
66
- end
67
-
68
- it '#vote returns true on successful vote' do
69
- @users.each do |u|
70
- expect(@post.vote(rand(1..10), u)).to be_truthy
71
- end
72
-
73
- vote_value = @post.votes.map(&:value).sum.to_f/@post.votes.size
74
-
75
- expect(@post.vote_value).to eq(vote_value)
76
- expect(@post.vote_count).to eq(@users.size)
77
- expect(@post.votes.size).to eq(@users.size)
78
-
79
- Post.find(@post.id).tap do |post|
80
- expect(post.vote_value).to eq(vote_value)
81
- expect(post.vote_count).to eq(@users.size)
82
- expect(post.votes.size).to eq(@users.size)
83
- end
84
- end
85
- end
86
-
87
- describe 'when has votes' do
88
- before(:each) do
89
- @users.each{|u| @post.vote(rand(1..10), u)}
90
- end
91
-
92
- it 'is not in the not_voted scope' do
93
- expect(subject.class.not_voted).not_to include(@post)
94
- end
95
-
96
- it 'is in voted scope' do
97
- expect(subject.class.voted).to include(@post)
98
- end
99
-
100
- it 'is in voted_by scope for each voted user' do
101
- @users.each do |u|
102
- expect(subject.class.voted_by(u)).to include(@post)
103
- end
104
- end
105
-
106
- it '#vote_count returns count of votes' do
107
- expect(@post.vote_count).to eq(@post.votes.size)
108
- end
109
-
110
- it '#vote_value returns actual vote value' do
111
- vote_value = @post.votes.map(&:value).sum.to_f/@post.votes.size
112
- expect(@post.vote_value).to eq(vote_value)
113
- end
114
-
115
- it '#votes returns array of vote marks' do
116
- @post.votes.tap do |votes|
117
- expect(votes.class).to eq(Array)
118
- expect(votes).not_to be_empty
119
- expect(votes.size).to eq(@users.size)
120
- expect(votes.map(&:voted_by_id).sort).to eq(@users.map(&:id).sort)
121
- end
122
- end
123
-
124
- it '#has_votes? returns true' do
125
- expect(@post.vote_count).to eq(@post.votes.size)
126
- expect(@post.has_votes?).to be_truthy
127
- end
128
-
129
- it '#voted_by? returns true for all voted resource' do
130
- @users.each do |u|
131
- expect(@post.voted_by?(u)).to be_truthy
132
- end
133
- end
134
-
135
- it '#retract returns true on successful vote retract' do
136
- cnt = @users.size
137
-
138
- @users.each do |u|
139
- expect(@post.retract(u)).to be_truthy
140
- cnt -= 1
141
-
142
- vote_value = @post.votes.size == 0 ? nil : @post.votes.map(&:value).sum.to_f/@post.votes.size
143
-
144
- expect(@post.vote_value).to eq(vote_value)
145
- expect(@post.vote_count).to eq(@post.votes.size)
146
- expect(@post.vote_count).to eq(cnt)
147
-
148
- expect(Post.find(@post.id).vote_value).to eq(vote_value)
149
- expect(Post.find(@post.id).vote_count).to eq(cnt)
150
- Post.find(@post.id).votes.size == cnt
151
- end
152
- end
153
- end
154
-
155
- describe 'when model has vote_range' do
156
- before(:each) do
157
- Post.send(:set_vote_range, 1..5)
158
- @post = FactoryGirl.create(:post)
159
- end
160
-
161
- it 'Vote has vote_range set up' do
162
- expect(Mongoid::AtomicVotes::Vote.vote_range).to eq((1..5))
163
- end
164
-
165
- it 'Vote is not valid if its value is not in specified vote_range' do
166
- @vote = Mongoid::AtomicVotes::Vote.new(value: rand(6..10), voted_by_id: @users.first.id, voter_type: @users.first.class.name)
167
- expect(@vote).not_to be_valid
168
- expect(@vote.errors.messages[:value].first).to match(/included in the list/)
169
- end
170
- end
171
- end