mongoid-publishable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,7 @@
1
+ module Mongoid
2
+ module Publishable
3
+ class UnpublishedError < StandardError
4
+ attr_accessor :model
5
+ end
6
+ end
7
+ 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,5 @@
1
+ module Mongoid
2
+ module Publishable
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require "mongoid/publishable"
2
+ require "mongoid/publishable/version"
@@ -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