mongoid_timeline_fu 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 ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2-p180@mongoid_timeline_fu
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mongoid_timeline_fu.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Teng Siong Ong
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ MongoidTimelineFu
2
+ =================
3
+
4
+ Easily build timelines, much like GitHub's news feed. But, on Mongoid tho. This
5
+ project is a port of [TimelineFu](https://github.com/jamesgolick/timeline_fu) on [Mongoid](http://mongoid.org/).
6
+
7
+ Usage
8
+ =====
9
+
10
+ MongoidTimelineFu requires you to have a TimelineEvent model.
11
+ The simplest way is to use the generator.
12
+
13
+ $ rails generate mongoid_timeline_fu
14
+ create app/models/timeline_event.rb
15
+
16
+ Next step is to determine what generates an event in your various models.
17
+
18
+ class Post
19
+ #...
20
+ belongs_to :author, :class_name => 'Person'
21
+ fires :new_post, :on => :create,
22
+ :actor => :author
23
+ end
24
+
25
+ You can add `fires` statements to as many models as you want on as many models
26
+ as you want.
27
+
28
+ They are hooked for you after standard Mongoid events. In
29
+ the previous example, it's an after_create on Posts.
30
+
31
+ Parameters for #fires
32
+ =====================
33
+
34
+ You can supply a few parameters to fires, two of them are mandatory. The first param is a custom name for the event type. It'll be your way of figuring out what events your reading back from the timeline_events table later. `new_post` in the example above.
35
+
36
+ The rest all fit neatly in an options hash.
37
+
38
+ - :on => [Mongoid event]
39
+ - mandatory. You use it to specify whether you want the event created after a create, update or destroy. You can also supply an array of events, e.g. [:create, :update].
40
+ - :actor is your way of specifying who took this action.
41
+ - In the example, post.author is going to be this person.
42
+ - :subject is automatically set to self, which is good most of the time. You can however override it if you need to, using :subject.
43
+ - :secondary_subject can let you specify something else that's related to the event. A comment to a blog post would be a good example.
44
+ - :if => symbol or proc/lambda lets you put conditions on when a TimelineEvent is created. It's passed right to the after_xxx Mongoid event hook, so it's has the same behavior.
45
+
46
+ Here's another example:
47
+
48
+ class Comment
49
+ #...
50
+ belongs_to :commenter, :class_name => 'Person'
51
+ belongs_to :post
52
+ fires :new_comment, :on => :create,
53
+ :actor => :commenter,
54
+ #implicit :subject => self,
55
+ :secondary_subject => 'post',
56
+ :if => lambda { |comment| comment.commenter != comment.post.author }
57
+ end
58
+
59
+ TimelineEvent instantiation
60
+ ===========================
61
+
62
+ The Mongoid event hook will automatically instantiate a
63
+ TimelineEvent instance for you.
64
+ It will receive the following parameters in #create!
65
+
66
+ - event_type
67
+ - "new_comment" in the comment example
68
+ - actor
69
+ - the commenter
70
+ - subject
71
+ - the comment instance
72
+ - secondary_subject
73
+ - the post instance
74
+
75
+ The generated model stores most of its info as polymorphic relationships.
76
+
77
+ class TimelineEvent
78
+ include Mongoid::Document
79
+ field :event_type, :type => String
80
+
81
+ belongs_to :actor, :polymorphic => true
82
+ belongs_to :subject, :polymorphic => true
83
+ belongs_to :secondary_subject, :polymorphic => true
84
+ end
85
+
86
+ How you actually get your timeline
87
+ ==================================
88
+
89
+ To get your timeline you'll probably have to create your own finder or scopes
90
+ (if your situation is extremely simple).
91
+
92
+ MongoidTimelineFu is not currently providing anything to generate your timeline because
93
+ different situations will have wildly different requirements. Like access control
94
+ issues and actually just what crazy stuff you're cramming in that timeline.
95
+
96
+ We're not saying it can't be done, just that we haven't done it yet.
97
+ Contributions are welcome :-)
98
+
99
+ Get it
100
+ ======
101
+
102
+ # Gemfile
103
+ gem "mongoid_timeline_fu"
104
+
105
+ License
106
+ =======
107
+
108
+ Copyright (c) 2011 Teng Siong Ong, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the timeline_fu plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'rails/generators'
2
+
3
+ class MongoidTimelineFuGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ def create_migration_file
7
+ copy_file 'model.rb', 'app/models/timeline_event.rb'
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ class TimelineEvent
2
+ include Mongoid::Document
3
+ field :event_type, :type => String
4
+
5
+ belongs_to :actor, :polymorphic => true
6
+ belongs_to :subject, :polymorphic => true
7
+ belongs_to :secondary_subject, :polymorphic => true
8
+ end
@@ -0,0 +1,36 @@
1
+ module MongoidTimelineFu
2
+ module Fires
3
+ def fires(event_type, opts)
4
+ raise ArgumentError, "Argument :on is mandatory" unless opts.has_key?(:on)
5
+
6
+ # Array provided, set multiple callbacks
7
+ if opts[:on].kind_of?(Array)
8
+ opts[:on].each { |on| fires(event_type, opts.merge({:on => on})) }
9
+ return
10
+ end
11
+
12
+ opts[:subject] = :self unless opts.has_key?(:subject)
13
+
14
+ method_name = :"fire_#{event_type}_after_#{opts[:on]}"
15
+ define_method(method_name) do
16
+ create_options = [:actor, :subject, :secondary_subject].inject({}) do |memo, sym|
17
+ if opts[sym]
18
+ if opts[sym].respond_to?(:call)
19
+ memo[sym] = opts[sym].call(self)
20
+ elsif opts[sym] == :self
21
+ memo[sym] = self
22
+ else
23
+ memo[sym] = send(opts[sym])
24
+ end
25
+ end
26
+ memo
27
+ end
28
+ create_options[:event_type] = event_type.to_s
29
+
30
+ TimelineEvent.create!(create_options)
31
+ end
32
+
33
+ send(:"after_#{opts[:on]}", method_name, :if => opts[:if])
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module MongoidTimelineFu
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ require "mongoid_timeline_fu/version"
2
+ require "mongoid_timeline_fu/fires"
3
+
4
+ module MongoidTimelineFu
5
+ end
6
+
7
+ Mongoid::Document::ClassMethods.class_eval do
8
+ include MongoidTimelineFu::Fires
9
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mongoid_timeline_fu/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mongoid_timeline_fu"
7
+ s.version = MongoidTimelineFu::VERSION
8
+ s.authors = ["Teng Siong Ong"]
9
+ s.email = ["siong1987@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Easily build timelines, much like GitHub's news feed. But, on Mongoid tho.}
12
+ s.description = %q{Easily build timelines, much like GitHub's news feed. But, on Mongoid tho.}
13
+
14
+ s.rubyforge_project = "mongoid_timeline_fu"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "mocha"
22
+ s.add_dependency("mongoid", "~> 2.0.0")
23
+ s.add_dependency("bson_ext", "~> 1.4.0")
24
+ end
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__)+'/test_helper'
2
+
3
+ class FiresTest < Test::Unit::TestCase
4
+ def setup
5
+ @james = create_person(:email => 'james@giraffesoft.ca')
6
+ @mat = create_person(:email => 'mat@giraffesoft.ca')
7
+ end
8
+
9
+ def test_should_fire_the_appropriate_callback
10
+ @list = List.new(hash_for_list(:author => @james));
11
+ TimelineEvent.expects(:create!).with(:actor => @james, :subject => @list, :event_type => 'list_created_or_updated')
12
+ @list.save
13
+ TimelineEvent.expects(:create!).with(:actor => @mat, :subject => @list, :event_type => 'list_created_or_updated')
14
+ @list.author = @mat
15
+ @list.save
16
+ end
17
+
18
+ def test_should_fire_event_with_secondary_subject
19
+ @list = List.new(hash_for_list(:author => @james));
20
+ TimelineEvent.stubs(:create!)
21
+ @list.save
22
+ @comment = Comment.new(:body => 'cool list!', :author => @mat, :list => @list)
23
+ TimelineEvent.expects(:create!).with(:actor => @mat,
24
+ :subject => @comment,
25
+ :secondary_subject => @list,
26
+ :event_type => 'comment_created')
27
+ @comment.save
28
+ end
29
+
30
+ def test_exception_raised_if_on_missing
31
+ # This needs to be tested with should_raise, to check out the msg content
32
+ assert_raise(ArgumentError) do
33
+ some_class = Class.new
34
+ some_class.class_eval do
35
+ include Mongoid::Document
36
+ attr_accessor :someone
37
+ fires :some_event, :actor => :someone
38
+ end
39
+ end
40
+ end
41
+
42
+ def test_should_only_fire_if_the_condition_evaluates_to_true
43
+ TimelineEvent.expects(:create!).with(:actor => @mat, :subject => @james, :event_type => 'follow_created')
44
+ @james.new_watcher = @mat
45
+ @james.save
46
+ end
47
+
48
+ def test_should_not_fire_if_the_if_condition_evaluates_to_false
49
+ TimelineEvent.expects(:create!).never
50
+ @james.new_watcher = nil
51
+ @james.save
52
+ end
53
+
54
+ def test_should_fire_event_with_symbol_based_if_condition_that_is_true
55
+ @james.fire = true
56
+ TimelineEvent.expects(:create!).with(:subject => @james, :event_type => 'person_updated')
57
+ @james.save
58
+ end
59
+
60
+ def test_should_fire_event_with_symbol_based_if_condition
61
+ @james.fire = false
62
+ TimelineEvent.expects(:create!).never
63
+ @james.save
64
+ end
65
+
66
+ def test_should_set_secondary_subject_to_self_when_requested
67
+ @list = List.new(hash_for_list(:author => @james));
68
+ TimelineEvent.stubs(:create!).with(has_entry(:event_type, "list_created_or_updated"))
69
+ @list.save
70
+ @comment = Comment.new(:body => 'cool list!', :author => @mat, :list => @list)
71
+ TimelineEvent.stubs(:create!).with(has_entry(:event_type, "comment_created"))
72
+ @comment.save
73
+ TimelineEvent.expects(:create!).with(:actor => @mat,
74
+ :subject => @list,
75
+ :secondary_subject => @comment,
76
+ :event_type => 'comment_deleted')
77
+ @comment.destroy
78
+ end
79
+ end
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ require 'mongoid'
3
+ require 'test/unit'
4
+ require 'mocha'
5
+
6
+ require File.dirname(__FILE__)+'/../lib/mongoid_timeline_fu'
7
+
8
+ Mongoid.configure do |config|
9
+ config.master = Mongo::Connection.new('127.0.0.1', 27017).db("timeline-fu-test-suite")
10
+ config.use_utc = true
11
+ config.include_root_in_json = true
12
+ end
13
+
14
+ class Person
15
+ include Mongoid::Document
16
+ field :email, :type => String
17
+ field :password, :type => String
18
+ attr_accessor :new_watcher, :fire
19
+
20
+ fires :follow_created, :on => :update,
21
+ :actor => lambda { |person| person.new_watcher },
22
+ :if => lambda { |person| !person.new_watcher.nil? }
23
+ fires :person_updated, :on => :update,
24
+ :if => :fire?
25
+
26
+ def fire?
27
+ new_watcher.nil? && fire
28
+ end
29
+ end
30
+
31
+ class List
32
+ include Mongoid::Document
33
+ field :title, :type => String
34
+
35
+ belongs_to :author, :class_name => "Person"
36
+ has_many :comments
37
+
38
+ fires :list_created_or_updated, :actor => :author,
39
+ :on => [:create, :update]
40
+ end
41
+
42
+ class Comment
43
+ include Mongoid::Document
44
+ field :body, :type => String
45
+
46
+ belongs_to :list
47
+ belongs_to :author, :class_name => "Person"
48
+
49
+ fires :comment_created, :actor => :author,
50
+ :on => :create,
51
+ :secondary_subject => :list
52
+ fires :comment_deleted, :actor => :author,
53
+ :on => :destroy,
54
+ :subject => :list,
55
+ :secondary_subject => :self
56
+ end
57
+
58
+ TimelineEvent = Class.new
59
+
60
+ class Test::Unit::TestCase
61
+ protected
62
+ def hash_for_list(opts = {})
63
+ {:title => 'whatever'}.merge(opts)
64
+ end
65
+
66
+ def create_list(opts = {})
67
+ List.create!(hash_for_list(opts))
68
+ end
69
+
70
+ def hash_for_person(opts = {})
71
+ {:email => 'james'}.merge(opts)
72
+ end
73
+
74
+ def create_person(opts = {})
75
+ Person.create!(hash_for_person(opts))
76
+ end
77
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid_timeline_fu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Teng Siong Ong
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-24 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mocha
16
+ requirement: &70235157266800 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70235157266800
25
+ - !ruby/object:Gem::Dependency
26
+ name: mongoid
27
+ requirement: &70235157266300 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70235157266300
36
+ - !ruby/object:Gem::Dependency
37
+ name: bson_ext
38
+ requirement: &70235157265740 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.4.0
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70235157265740
47
+ description: Easily build timelines, much like GitHub's news feed. But, on Mongoid
48
+ tho.
49
+ email:
50
+ - siong1987@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - .rvmrc
57
+ - Gemfile
58
+ - MIT-LICENSE
59
+ - README.md
60
+ - Rakefile
61
+ - lib/generators/mongoid_timeline_fu_generator.rb
62
+ - lib/generators/templates/model.rb
63
+ - lib/mongoid_timeline_fu.rb
64
+ - lib/mongoid_timeline_fu/fires.rb
65
+ - lib/mongoid_timeline_fu/version.rb
66
+ - mongoid_timeline_fu.gemspec
67
+ - test/fires_test.rb
68
+ - test/test_helper.rb
69
+ homepage: ''
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project: mongoid_timeline_fu
89
+ rubygems_version: 1.8.10
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Easily build timelines, much like GitHub's news feed. But, on Mongoid tho.
93
+ test_files:
94
+ - test/fires_test.rb
95
+ - test/test_helper.rb