mongoid-publishable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rvmrc +12 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +178 -0
- data/Rakefile +12 -0
- data/lib/mongoid/.DS_Store +0 -0
- data/lib/mongoid/publishable.rb +98 -0
- data/lib/mongoid/publishable/callback.rb +23 -0
- data/lib/mongoid/publishable/callback_collection.rb +11 -0
- data/lib/mongoid/publishable/callbacks.rb +50 -0
- data/lib/mongoid/publishable/queue.rb +51 -0
- data/lib/mongoid/publishable/queuing.rb +27 -0
- data/lib/mongoid/publishable/unpublished_error.rb +7 -0
- data/lib/mongoid/publishable/unpublished_object.rb +88 -0
- data/lib/mongoid/publishable/version.rb +5 -0
- data/lib/mongoid_publishable.rb +2 -0
- data/mongoid_publishable.gemspec +26 -0
- data/spec/.DS_Store +0 -0
- data/spec/mongoid/publishable/callback_collection_spec.rb +17 -0
- data/spec/mongoid/publishable/callback_spec.rb +46 -0
- data/spec/mongoid/publishable/callbacks_spec.rb +72 -0
- data/spec/mongoid/publishable/queue_spec.rb +106 -0
- data/spec/mongoid/publishable/queuing_spec.rb +25 -0
- data/spec/mongoid/publishable/unpublished_object_spec.rb +133 -0
- data/spec/mongoid/publishable_spec.rb +215 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/env.rb +8 -0
- data/spec/support/models/callbackable_object.rb +6 -0
- data/spec/support/models/publishable_object.rb +8 -0
- data/spec/support/models/publisher.rb +3 -0
- data/spec/support/models/queuing_controller.rb +8 -0
- metadata +173 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
require "mongoid/publishable/unpublished_object"
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Publishable
|
5
|
+
class Queue < Array
|
6
|
+
|
7
|
+
# loads the queue from the session
|
8
|
+
def self.load(session = nil)
|
9
|
+
# create a new queue
|
10
|
+
queue = new
|
11
|
+
# if there was no existing queue, return new
|
12
|
+
return queue unless session
|
13
|
+
# create our contents
|
14
|
+
contents = session.split("\n").map do |data|
|
15
|
+
UnpublishedObject.deserialize_from_session(data)
|
16
|
+
end
|
17
|
+
# add into the queue
|
18
|
+
queue.replace(contents)
|
19
|
+
end
|
20
|
+
|
21
|
+
# publishes all the objects on the queue to this user
|
22
|
+
def publish_via(publisher)
|
23
|
+
remaining = delete_if do |model|
|
24
|
+
model.publish_via!(publisher)
|
25
|
+
model.published?
|
26
|
+
end
|
27
|
+
# replaces the contents with the remaining models
|
28
|
+
replace(remaining)
|
29
|
+
end
|
30
|
+
|
31
|
+
# creates a string containing the data of the queue
|
32
|
+
def dump
|
33
|
+
map do |model|
|
34
|
+
model.serialize_for_session
|
35
|
+
end.join("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
# adds a new object to the queue
|
39
|
+
def push(*models)
|
40
|
+
# map each item to an unpublished object
|
41
|
+
models = Array(models).map do |model|
|
42
|
+
UnpublishedObject.new(model: model)
|
43
|
+
end
|
44
|
+
# add them to the array
|
45
|
+
super(*models)
|
46
|
+
end
|
47
|
+
alias_method :<<, :push
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "mongoid/publishable/queue"
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Publishable
|
5
|
+
module Queuing
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
before_filter :deserialize_publishing_queue
|
9
|
+
after_filter :serialize_publishing_queue
|
10
|
+
attr_reader :publishing_queue
|
11
|
+
end
|
12
|
+
base.send(:include, InstanceMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods
|
16
|
+
protected
|
17
|
+
def deserialize_publishing_queue
|
18
|
+
@publishing_queue = Queue.load(session[:publishing_queue])
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize_publishing_queue
|
22
|
+
session[:publishing_queue] = @publishing_queue.dump
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "multi_json"
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Publishable
|
5
|
+
class UnpublishedObject
|
6
|
+
def self.deserialize_from_session(data)
|
7
|
+
new(data: data)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
if options[:model]
|
12
|
+
@source_object = options[:model]
|
13
|
+
elsif options[:data]
|
14
|
+
@serialized_data = options[:data]
|
15
|
+
else
|
16
|
+
raise ArgumentError, "No :model or :data provided"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def serialize_for_session
|
21
|
+
@serialized_data ||= serialize_object_for_session
|
22
|
+
end
|
23
|
+
|
24
|
+
def params
|
25
|
+
MultiJson.load(@serialized_data)
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to_missing?(method)
|
29
|
+
source_object.respond_to?(method)
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(method, *args, &block)
|
33
|
+
if respond_to_missing?(method)
|
34
|
+
source_object.send(method, *args, &block)
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def source_object
|
41
|
+
@source_object ||= load_source_object_from_params
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def load_source_object_from_params
|
46
|
+
data = params
|
47
|
+
# load the top level object
|
48
|
+
object = data["class_name"].constantize.find(data["id"])
|
49
|
+
# if we have embedded stuff
|
50
|
+
while data["embedded"]
|
51
|
+
# work on the next level down
|
52
|
+
data = data["embedded"]
|
53
|
+
# find the nested object
|
54
|
+
object = object.send(data["association"]).find(data["id"])
|
55
|
+
end
|
56
|
+
# once at the bottom, return the object
|
57
|
+
object
|
58
|
+
end
|
59
|
+
|
60
|
+
def serialize_object_for_session
|
61
|
+
# start at the bottom
|
62
|
+
object = source_object
|
63
|
+
result = nil
|
64
|
+
# work the way up the embeds
|
65
|
+
while object.embedded?
|
66
|
+
# select the relation for the parent object
|
67
|
+
parent_relation = relations.select do |k,v|
|
68
|
+
v.macro == :embedded_in && v.class_name == object._parent.class.name
|
69
|
+
end.values.first
|
70
|
+
# wrap the the result
|
71
|
+
result = {
|
72
|
+
embedded: {
|
73
|
+
association: parent_relation.inverse_of,
|
74
|
+
embedded: result,
|
75
|
+
id: object.id
|
76
|
+
}
|
77
|
+
}
|
78
|
+
# now act on the parent
|
79
|
+
object = object._parent
|
80
|
+
end
|
81
|
+
# when at the top level, store the class/id/result
|
82
|
+
result = { class_name: object.class.name, id: object.id, embedded: result }
|
83
|
+
# convert the result to JSON
|
84
|
+
MultiJson.dump(result)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require "mongoid/publishable/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "mongoid-publishable"
|
9
|
+
s.version = Mongoid::Publishable::VERSION
|
10
|
+
s.authors = ["Ryan Townsend"]
|
11
|
+
s.email = ["ryan@ryantownsend.co.uk"]
|
12
|
+
s.description = %q{A mixin for Mongoid document models allowing for publishing them after authentication}
|
13
|
+
s.summary = s.description
|
14
|
+
s.homepage = "https://github.com/ryantownsend/mongoid-publishable"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split($/)
|
17
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "mongoid"
|
22
|
+
s.add_dependency "multi_json"
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
s.add_development_dependency "rspec"
|
25
|
+
s.add_development_dependency "simplecov"
|
26
|
+
end
|
data/spec/.DS_Store
ADDED
Binary file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "mongoid/publishable/callback_collection"
|
3
|
+
|
4
|
+
describe Mongoid::Publishable::CallbackCollection do
|
5
|
+
let(:callback) { mock("callback") }
|
6
|
+
let(:contents) { [callback] }
|
7
|
+
subject { Mongoid::Publishable::CallbackCollection.new(contents) }
|
8
|
+
|
9
|
+
describe "#process" do
|
10
|
+
it "should call #process on each of it's items" do
|
11
|
+
object = mock("instance")
|
12
|
+
callback.should_receive(:process).with(object).and_return(true)
|
13
|
+
subject.process(object)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "mongoid/publishable/callback"
|
3
|
+
|
4
|
+
describe Mongoid::Publishable::Callback do
|
5
|
+
subject { Mongoid::Publishable::Callback }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
it "should accept a block" do
|
9
|
+
callback = subject.new do |object|
|
10
|
+
puts "Done something"
|
11
|
+
end
|
12
|
+
expect(callback).to respond_to :process
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should accept a symbol" do
|
16
|
+
callback = subject.new(:method_name)
|
17
|
+
expect(callback).to respond_to :process
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should not accept two arguments" do
|
21
|
+
expect {
|
22
|
+
subject.new(:one, :two)
|
23
|
+
}.to raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#process" do
|
28
|
+
let(:object) { mock("instance", test: "123") }
|
29
|
+
|
30
|
+
context "when given a method name" do
|
31
|
+
it "should call the method on the given object" do
|
32
|
+
callback = subject.new(:test)
|
33
|
+
expect(callback.process(object)).to eq "123"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when given a block" do
|
38
|
+
it "should yield the block with the given object" do
|
39
|
+
callback = subject.new do |object|
|
40
|
+
object.test
|
41
|
+
end
|
42
|
+
expect(callback.process(object)).to eq "123"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "mongoid/publishable/callbacks"
|
3
|
+
|
4
|
+
describe Mongoid::Publishable::Callbacks do
|
5
|
+
|
6
|
+
context "a class that's included Mongoid::Publishable::Callbacks" do
|
7
|
+
|
8
|
+
subject do
|
9
|
+
CallbackableObject
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "::after_publish_callbacks" do
|
13
|
+
it "should return an object that responds to #process" do
|
14
|
+
expect(subject.after_publish_callbacks).to respond_to :process
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "::after_publish" do
|
19
|
+
it "should increment the callbacks by 1" do
|
20
|
+
subject.after_publish_callbacks.replace []
|
21
|
+
expect(subject.after_publish_callbacks.size).to eq 0
|
22
|
+
subject.after_publish(:method_name)
|
23
|
+
expect(subject.after_publish_callbacks.size).to eq 1
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should accept a block" do
|
27
|
+
callback = subject.after_publish do |object|
|
28
|
+
puts "Done something"
|
29
|
+
end
|
30
|
+
expect(callback).to respond_to :process
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should accept a symbol" do
|
34
|
+
callback = subject.after_publish(:method_name)
|
35
|
+
expect(callback).to respond_to :process
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end # class
|
40
|
+
|
41
|
+
context "an instance class that's included Mongoid::Publishable::Callbacks" do
|
42
|
+
|
43
|
+
subject do
|
44
|
+
CallbackableObject.new
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#process_after_publish_callbacks" do
|
48
|
+
it "should process the callbacks" do
|
49
|
+
collection = subject.class.after_publish_callbacks
|
50
|
+
collection.should_receive(:process).with(subject).and_return(true)
|
51
|
+
expect(subject.process_after_publish_callbacks).to be_true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#run_after_publish_callbacks?" do
|
56
|
+
context "by default" do
|
57
|
+
it "should return false" do
|
58
|
+
expect(subject.run_after_publish_callbacks?).to be_false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "after calling #run_after_publish_callbacks" do
|
63
|
+
it "should return true" do
|
64
|
+
subject.run_after_publish_callbacks
|
65
|
+
expect(subject.run_after_publish_callbacks?).to be_true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end # instance
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "mongoid/publishable/queue"
|
3
|
+
|
4
|
+
describe Mongoid::Publishable::Queue do
|
5
|
+
|
6
|
+
describe "the class" do
|
7
|
+
subject do
|
8
|
+
Mongoid::Publishable::Queue
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "::load" do
|
12
|
+
context "given session data" do
|
13
|
+
it "should create an unpublished object for each line" do
|
14
|
+
data = %Q({ class_name: "Item", id: 123 }\n{ class_name: "Item", id: 123 })
|
15
|
+
expect(subject.load(data).size).to eq 2
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "given no session data" do
|
20
|
+
it "should return an empty queue" do
|
21
|
+
expect(subject.load.size).to eq 0
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "an instance of the queue" do
|
28
|
+
let(:items) { [] }
|
29
|
+
|
30
|
+
subject do
|
31
|
+
Mongoid::Publishable::Queue.new(items)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#publish_via" do
|
35
|
+
let(:item) do
|
36
|
+
mock("unpublished_item").tap do |item|
|
37
|
+
item.stub(:publish_via!).and_return(true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
let(:items) { [item] }
|
41
|
+
|
42
|
+
context "when all contents are published" do
|
43
|
+
before(:each) do
|
44
|
+
item.stub(:published?).and_return(true)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should empty the queue" do
|
48
|
+
expect(subject.size).to eq 1
|
49
|
+
subject.publish_via(mock("user"))
|
50
|
+
expect(subject.size).to eq 0
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when one of the items cannot be published" do
|
55
|
+
before(:each) do
|
56
|
+
item.stub(:published?).and_return(false)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should leave one item in the queue" do
|
60
|
+
expect(subject.size).to eq 1
|
61
|
+
subject.publish_via(mock("user"))
|
62
|
+
expect(subject.size).to eq 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#dump" do
|
68
|
+
let(:item) do
|
69
|
+
mock("unpublished_item", serialize_for_session: "123")
|
70
|
+
end
|
71
|
+
let(:items) { [item, item] }
|
72
|
+
|
73
|
+
it "should serialize each object and join into a multi-line string" do
|
74
|
+
expect(subject.dump).to eq "123\n123"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#push" do
|
79
|
+
context "when given a single item" do
|
80
|
+
it "the number of objects should be incremented by 1" do
|
81
|
+
count = subject.size
|
82
|
+
subject.push(mock("model"))
|
83
|
+
expect(subject.size).to eq count + 1
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when given 2 items" do
|
88
|
+
it "the number of objects should be incremented by 2" do
|
89
|
+
count = subject.size
|
90
|
+
subject.push(mock("model"), mock("model"))
|
91
|
+
expect(subject.size).to eq count + 2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "when given no items" do
|
96
|
+
it "the number of objects should not change" do
|
97
|
+
count = subject.size
|
98
|
+
subject.push
|
99
|
+
expect(subject.size).to eq count
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end # instance
|
105
|
+
|
106
|
+
end
|