acts_as_caesar 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +4 -2
- data/lib/acts_as_caesar/modules/act_as_candidate.rb +25 -10
- data/lib/acts_as_caesar/services/active_record_persistence.rb +23 -0
- data/lib/acts_as_caesar/services/mongoid_persistence.rb +52 -0
- data/lib/acts_as_caesar/version.rb +1 -1
- data/spec/modules/act_as_candidate_spec.rb +95 -10
- metadata +4 -2
data/README.textile
CHANGED
@@ -6,17 +6,19 @@ h1. Installation
|
|
6
6
|
|
7
7
|
Simply add acts_as_caesar to your Gemfile
|
8
8
|
|
9
|
-
<pre><code>gem
|
9
|
+
<pre><code>gem "acts_as_caesar"</code></pre>
|
10
10
|
|
11
11
|
h2. Requirements
|
12
12
|
|
13
13
|
You must have Redis server installed. We will seek to make the persistence layer database agnostic over time.
|
14
14
|
|
15
|
+
Candidate objects (things that are being up/downvoted, e.g. comments) must be persisted using ActiveRecord or Mongoid
|
16
|
+
|
15
17
|
You must have jQuery installed and JavaScript on the client-side is required.
|
16
18
|
|
17
19
|
h2. Configuration
|
18
20
|
|
19
|
-
Acts As Caesar will connect to Redis on localhost, port 6379 (default for Redis server)
|
21
|
+
Acts As Caesar expects a Redis connection to exist on the @$redis@ constant, if that's not set it will connect to Redis on localhost, port 6379 (default for Redis server). You can easily create your own Redis connection using the code below (for Rails place this in @config/initializers/redis.rb@):
|
20
22
|
|
21
23
|
<pre><code>$redis = Redis.new(:host => 'localhost', :port => 6379)</code></pre>
|
22
24
|
|
@@ -1,26 +1,41 @@
|
|
1
1
|
require 'acts_as_caesar/objects/candidate'
|
2
2
|
require 'acts_as_caesar/objects/notification'
|
3
|
+
require 'acts_as_caesar/services/active_record_persistence'
|
4
|
+
require 'acts_as_caesar/services/mongoid_persistence'
|
3
5
|
|
4
6
|
module ActsAsCaesar
|
5
7
|
module ActAsCandidate
|
6
8
|
def self.included(base)
|
7
9
|
Notification.subscribe(key: /^#{base.name.downcase.gsub('::', '_')}\_[\da-f]+$/) do |candidate|
|
8
|
-
candidate
|
9
|
-
base.find($1).update_total_candidate_score
|
10
|
+
handle_candidate_update_notification(base, candidate)
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def self.handle_candidate_update_notification(base, candidate)
|
15
|
+
# ensure we have an id
|
16
|
+
unless candidate.key =~ /\_([\da-f]+)$/ && id = $1
|
17
|
+
raise ArgumentError, "#{candidate.key} does not contain a valid key"
|
18
|
+
end
|
19
|
+
|
20
|
+
if ActiveRecordPersistence.can_handle?(base)
|
21
|
+
ActiveRecordPersistence.new(base, id).update_score
|
22
|
+
elsif MongoidPersistence.can_handle?(base)
|
23
|
+
MongoidPersistence.new(base, id).update_score
|
24
|
+
elsif base.respond_to?(:acts_as_caesar_score_persistence)
|
25
|
+
base.acts_as_caesar_score_persistence.new(base, id).update_score
|
26
|
+
else
|
27
|
+
raise StandardError, %Q(
|
28
|
+
Incompatible database adapter, try implementing the following method:
|
29
|
+
def self.acts_as_caesar_score_persistence
|
30
|
+
# returns a class name like lib/acts_as_caesar/services/mongoid_persistence.rb
|
31
|
+
# e.g. MongoidPersistence
|
32
|
+
end
|
33
|
+
)
|
34
|
+
end
|
20
35
|
end
|
21
36
|
|
22
37
|
def total_candidate_score
|
23
|
-
|
38
|
+
ActsAsCaesar::Candidate.new(self).total
|
24
39
|
end
|
25
40
|
end
|
26
41
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActsAsCaesar
|
2
|
+
class ActiveRecordPersistence
|
3
|
+
class << self
|
4
|
+
def can_handle?(klass)
|
5
|
+
defined?(ActiveRecord) && defined?(ActiveRecord::Base) &&
|
6
|
+
(klass < ActiveRecord::Base)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(klass, id)
|
11
|
+
@klass = klass
|
12
|
+
@id = id
|
13
|
+
end
|
14
|
+
|
15
|
+
def update_score
|
16
|
+
candidate.update_attribute(:score, candidate.total_candidate_score)
|
17
|
+
end
|
18
|
+
|
19
|
+
def candidate
|
20
|
+
@candidate ||= @klass.find(@id)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ActsAsCaesar
|
2
|
+
class MongoidPersistence
|
3
|
+
class << self
|
4
|
+
def can_handle?(klass)
|
5
|
+
defined?(Mongoid) && defined?(Mongoid::Document) &&
|
6
|
+
klass.included_modules.include?(Mongoid::Document)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(klass, id)
|
11
|
+
@klass = klass
|
12
|
+
@id = id
|
13
|
+
end
|
14
|
+
|
15
|
+
def update_score
|
16
|
+
puts "Updating score of #{candidate}"
|
17
|
+
candidate.update_attribute(:score, candidate.total_candidate_score)
|
18
|
+
end
|
19
|
+
|
20
|
+
def candidate
|
21
|
+
return @candidate if defined?(@candidate)
|
22
|
+
|
23
|
+
relations = []
|
24
|
+
klass = @klass
|
25
|
+
|
26
|
+
# loop back through nesting, appending relations
|
27
|
+
while (embedded_relations = klass.relations.select { |k,v| v.macro == :embedded_in }) && embedded_relations.size > 0
|
28
|
+
relation = embedded_relations.values.first
|
29
|
+
klass = relation.class_name.constantize
|
30
|
+
relations << relation.inverse_of
|
31
|
+
end
|
32
|
+
|
33
|
+
# start at the root document
|
34
|
+
scope = klass
|
35
|
+
|
36
|
+
# for each nested relation
|
37
|
+
while relation = relations.pop
|
38
|
+
key = relation.to_s
|
39
|
+
if relations.size > 0
|
40
|
+
key += "."
|
41
|
+
key += relations.reverse.join(".")
|
42
|
+
end
|
43
|
+
key += "._id"
|
44
|
+
# append the condition
|
45
|
+
scope = scope.where(key => BSON::ObjectId(@id)).first.send(relation)
|
46
|
+
end
|
47
|
+
|
48
|
+
# find the final object
|
49
|
+
@candidate = scope.find(@id)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -1,9 +1,44 @@
|
|
1
|
-
|
2
|
-
require_relative
|
1
|
+
require "mongoid"
|
2
|
+
require_relative "../../lib/acts_as_caesar/modules/act_as_candidate"
|
3
|
+
require_relative "../../lib/acts_as_caesar/objects/notification"
|
3
4
|
|
4
5
|
class ActsAsCaesar::TestActAsCandidate
|
5
6
|
attr_accessor :score
|
6
7
|
include ActsAsCaesar::ActAsCandidate
|
8
|
+
|
9
|
+
def key
|
10
|
+
"#{self.class.name}_abcdef1234567890"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ActsAsCaesar::MongoTestActAsCandidate
|
15
|
+
include Mongoid::Document
|
16
|
+
include ActsAsCaesar::ActAsCandidate
|
17
|
+
|
18
|
+
field :score, type: Integer
|
19
|
+
|
20
|
+
embeds_many :nested_mongo_tests,
|
21
|
+
class_name: "ActsAsCaesar::NestedMongoTestActAsCandidate",
|
22
|
+
inverse_of: :mongo_test
|
23
|
+
|
24
|
+
def key
|
25
|
+
"#{self.class.name}_#{id}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class ActsAsCaesar::NestedMongoTestActAsCandidate
|
30
|
+
include Mongoid::Document
|
31
|
+
include ActsAsCaesar::ActAsCandidate
|
32
|
+
|
33
|
+
field :score, type: Integer
|
34
|
+
|
35
|
+
embedded_in :mongo_test,
|
36
|
+
class_name: "ActsAsCaesar::MongoTestActAsCandidate",
|
37
|
+
inverse_of: :nested_mongo_tests
|
38
|
+
|
39
|
+
def key
|
40
|
+
"#{self.class.name}_#{id}"
|
41
|
+
end
|
7
42
|
end
|
8
43
|
|
9
44
|
describe ActsAsCaesar::ActAsCandidate do
|
@@ -26,18 +61,68 @@ describe ActsAsCaesar::ActAsCandidate do
|
|
26
61
|
end
|
27
62
|
|
28
63
|
context "when updating the score" do
|
29
|
-
|
30
|
-
|
31
|
-
|
64
|
+
context "of an ActiveRecord object" do
|
65
|
+
before(:each) do
|
66
|
+
ActsAsCaesar::ActiveRecordPersistence.should_receive(:can_handle?).and_return(true)
|
67
|
+
ActsAsCaesar::ActiveRecordPersistence.any_instance.should_receive(:candidate).any_number_of_times.and_return(subject)
|
68
|
+
subject.should_receive(:total_candidate_score).and_return(987)
|
69
|
+
subject.should_receive(:update_attribute).with(:score, 987).and_return(true)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should persist the score" do
|
73
|
+
ActsAsCaesar::ActAsCandidate.handle_candidate_update_notification(subject.class, subject)
|
74
|
+
end
|
32
75
|
end
|
33
76
|
|
34
|
-
|
35
|
-
|
36
|
-
|
77
|
+
context "of a Mongoid object" do
|
78
|
+
before(:each) do
|
79
|
+
ActsAsCaesar::MongoidPersistence.should_receive(:can_handle?).and_return(true)
|
80
|
+
ActsAsCaesar::MongoidPersistence.any_instance.should_receive(:candidate).any_number_of_times.and_return(subject)
|
81
|
+
subject.should_receive(:total_candidate_score).and_return(987)
|
82
|
+
subject.should_receive(:update_attribute).with(:score, 987).and_return(true)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should persist the score" do
|
86
|
+
ActsAsCaesar::ActAsCandidate.handle_candidate_update_notification(subject.class, subject)
|
87
|
+
end
|
37
88
|
end
|
89
|
+
|
90
|
+
context "of a nested Mongoid object" do
|
91
|
+
subject do
|
92
|
+
parent = ActsAsCaesar::MongoTestActAsCandidate.create!
|
93
|
+
parent.nested_mongo_tests.create!
|
94
|
+
end
|
38
95
|
|
39
|
-
|
40
|
-
|
96
|
+
before(:each) do
|
97
|
+
ActsAsCaesar::NestedMongoTestActAsCandidate.any_instance.should_receive(:total_candidate_score).and_return(987)
|
98
|
+
ActsAsCaesar::NestedMongoTestActAsCandidate.any_instance.should_receive(:update_attribute).once.with(:score, 987).and_return(true)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should persist the score" do
|
102
|
+
ActsAsCaesar::ActAsCandidate.handle_candidate_update_notification(subject.class, subject)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "of an object with custom AAC support" do
|
107
|
+
class CustomPersister < Struct.new(:klass, :id); end
|
108
|
+
|
109
|
+
before(:each) do
|
110
|
+
subject.class.should_receive(:respond_to?).with(:acts_as_caesar_score_persistence).and_return(true)
|
111
|
+
subject.class.should_receive(:acts_as_caesar_score_persistence).and_return(CustomPersister)
|
112
|
+
CustomPersister.any_instance.should_receive(:update_score).and_return(true)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should call the custom class" do
|
116
|
+
ActsAsCaesar::ActAsCandidate.handle_candidate_update_notification(subject.class, subject).should be_true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "of an object without custom AAC support" do
|
121
|
+
it "should raise an error" do
|
122
|
+
lambda do
|
123
|
+
ActsAsCaesar::ActAsCandidate.handle_candidate_update_notification(subject.class, subject)
|
124
|
+
end.should raise_error
|
125
|
+
end
|
41
126
|
end
|
42
127
|
end
|
43
128
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_caesar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-06-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
@@ -244,6 +244,8 @@ files:
|
|
244
244
|
- lib/acts_as_caesar/rack.rb
|
245
245
|
- lib/acts_as_caesar/rails/engine.rb
|
246
246
|
- lib/acts_as_caesar/rails/view_helpers.rb
|
247
|
+
- lib/acts_as_caesar/services/active_record_persistence.rb
|
248
|
+
- lib/acts_as_caesar/services/mongoid_persistence.rb
|
247
249
|
- lib/acts_as_caesar/version.rb
|
248
250
|
- spec/assets/jasmine_helper.js.coffee
|
249
251
|
- spec/assets/jquery_helper.js.coffee
|