mongoid-publishable 0.0.1
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.
- 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
|