public_activity 1.1.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -20
  3. data/README.md +50 -62
  4. data/lib/public_activity/activity.rb +2 -0
  5. data/lib/public_activity/common.rb +9 -7
  6. data/lib/public_activity/config.rb +15 -0
  7. data/lib/public_activity/models/activist.rb +2 -0
  8. data/lib/public_activity/models/adapter.rb +1 -0
  9. data/lib/public_activity/models/trackable.rb +2 -0
  10. data/lib/public_activity/orm/active_record/activist.rb +9 -6
  11. data/lib/public_activity/orm/active_record/activity.rb +4 -3
  12. data/lib/public_activity/orm/active_record/adapter.rb +4 -0
  13. data/lib/public_activity/orm/active_record/trackable.rb +4 -0
  14. data/lib/public_activity/orm/mongo_mapper/activist.rb +46 -0
  15. data/lib/public_activity/orm/mongo_mapper/activity.rb +33 -0
  16. data/lib/public_activity/orm/mongo_mapper/adapter.rb +12 -0
  17. data/lib/public_activity/orm/mongo_mapper/trackable.rb +11 -0
  18. data/lib/public_activity/orm/mongo_mapper.rb +4 -0
  19. data/lib/public_activity/orm/mongoid/activity.rb +1 -0
  20. data/lib/public_activity/renderable.rb +12 -8
  21. data/lib/public_activity/roles/deactivatable.rb +3 -0
  22. data/lib/public_activity/roles/tracked.rb +6 -6
  23. data/lib/public_activity/utility/view_helpers.rb +11 -3
  24. data/lib/public_activity/version.rb +1 -1
  25. data/test/migrations/001_create_activities.rb +23 -0
  26. data/test/migrations/002_create_articles.rb +11 -0
  27. data/test/migrations/003_create_users.rb +8 -0
  28. data/test/migrations/004_add_nonstandard_to_activities.rb +7 -0
  29. data/test/mongo_mapper.yml +4 -0
  30. data/test/mongoid.yml +6 -0
  31. data/test/test_activist.rb +56 -0
  32. data/test/test_activity.rb +67 -0
  33. data/test/test_common.rb +168 -0
  34. data/test/test_controller_integration.rb +41 -0
  35. data/test/test_generators.rb +41 -0
  36. data/test/test_helper.rb +124 -0
  37. data/test/test_tracking.rb +378 -0
  38. data/test/test_view_helpers.rb +36 -0
  39. data/test/views/layouts/_activity.erb +1 -0
  40. data/test/views/public_activity/_test.erb +8 -0
  41. metadata +139 -22
  42. data/UPGRADING +0 -8
@@ -3,10 +3,18 @@ module PublicActivity
3
3
  # Module extending ActionView::Base and adding `render_activity` helper.
4
4
  module ViewHelpers
5
5
  # View helper for rendering an activity, calls {PublicActivity::Activity#render} internally.
6
- def render_activity activity, options = {}
7
- activity.render self, options
6
+ def render_activity activities, options = {}
7
+ if activities.is_a? PublicActivity::Activity
8
+ activities.render self, options
9
+ elsif activities.respond_to?(:map)
10
+ # depend on ORMs to fetch as needed
11
+ # maybe we can support Postgres streaming with this?
12
+ activities.map {|activity| activity.render self, options.dup }.join.html_safe
13
+ end
8
14
  end
9
- # Helper for setting content_for in activity partial, needed to
15
+ alias_method :render_activities, :render_activity
16
+
17
+ # Helper for setting content_for in activity partial, needed to
10
18
  # flush remains in between partial renders.
11
19
  def single_content_for(name, content = nil, &block)
12
20
  @view_flow.set(name, ActiveSupport::SafeBuffer.new)
@@ -1,4 +1,4 @@
1
1
  module PublicActivity
2
2
  # A constant with gem's version
3
- VERSION = '1.1.0'
3
+ VERSION = '1.4.0'
4
4
  end
@@ -0,0 +1,23 @@
1
+ # Migration responsible for creating a table with activities
2
+ class CreateActivities < ActiveRecord::Migration
3
+ # Create table
4
+ def self.up
5
+ create_table :activities do |t|
6
+ t.belongs_to :trackable, :polymorphic => true
7
+ t.belongs_to :owner, :polymorphic => true
8
+ t.string :key
9
+ t.text :parameters
10
+ t.belongs_to :recipient, :polymorphic => true
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :activities, [:trackable_id, :trackable_type]
16
+ add_index :activities, [:owner_id, :owner_type]
17
+ add_index :activities, [:recipient_id, :recipient_type]
18
+ end
19
+ # Drop table
20
+ def self.down
21
+ drop_table :activities
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ class CreateArticles < ActiveRecord::Migration
2
+ def self.up
3
+ puts "creating"
4
+ create_table :articles do |t|
5
+ t.string :name
6
+ t.boolean :published
7
+ t.belongs_to :user
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ class AddNonstandardToActivities < ActiveRecord::Migration
2
+ def change
3
+ change_table :activities do |t|
4
+ t.string :nonstandard
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ test:
2
+ host: 127.0.0.1
3
+ port: 27017
4
+ database: public_activity_test
data/test/mongoid.yml ADDED
@@ -0,0 +1,6 @@
1
+ test:
2
+ sessions:
3
+ default:
4
+ hosts:
5
+ - 127.0.0.1:27017
6
+ database: public_activity_test
@@ -0,0 +1,56 @@
1
+ require 'test_helper'
2
+
3
+ describe PublicActivity::Activist do
4
+ it 'adds owner association' do
5
+ klass = article
6
+ klass.must_respond_to :activist
7
+ klass.activist
8
+ klass.new.must_respond_to :activities
9
+ case ENV["PA_ORM"]
10
+ when "active_record"
11
+ klass.reflect_on_association(:activities_as_owner).options[:as].must_equal :owner
12
+ when "mongoid"
13
+ klass.reflect_on_association(:activities_as_owner).options[:inverse_of].must_equal :owner
14
+ when "mongo_mapper"
15
+ klass.associations[:activities_as_owner].options[:as].must_equal :owner
16
+ end
17
+
18
+ if ENV["PA_ORM"] == "mongo_mapper"
19
+ klass.associations[:activities_as_owner].options[:class_name].must_equal "::PublicActivity::Activity"
20
+ else
21
+ klass.reflect_on_association(:activities_as_owner).options[:class_name].must_equal "::PublicActivity::Activity"
22
+ end
23
+ end
24
+
25
+ it 'returns activities from association' do
26
+ case PublicActivity::Config.orm
27
+ when :active_record
28
+ class ActivistUser < ActiveRecord::Base
29
+ include PublicActivity::Model
30
+ self.table_name = 'users'
31
+ activist
32
+ end
33
+ when :mongoid
34
+ class ActivistUser
35
+ include Mongoid::Document
36
+ include PublicActivity::Model
37
+ activist
38
+
39
+ field :name, type: String
40
+ end
41
+ when :mongo_mapper
42
+ class ActivistUser
43
+ include MongoMapper::Document
44
+ include PublicActivity::Model
45
+ activist
46
+
47
+ key :name, String
48
+ end
49
+ end
50
+ owner = ActivistUser.create(:name => "Peter Pan")
51
+ a = article(owner: owner).new
52
+ a.save
53
+
54
+ owner.activities_as_owner.length.must_equal 1
55
+ end
56
+ end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ describe 'PublicActivity::Activity Rendering' do
4
+ describe '#text' do
5
+ subject { PublicActivity::Activity.new(:key => 'activity.test', :parameters => {:one => 1}) }
6
+
7
+ specify '#text uses translations' do
8
+ subject.save
9
+ I18n.config.backend.store_translations(:en,
10
+ {:activity => {:test => '%{one} %{two}'}}
11
+ )
12
+ subject.text(:two => 2).must_equal('1 2')
13
+ subject.parameters.must_equal({:one => 1})
14
+ end
15
+ end
16
+
17
+ describe '#render' do
18
+ subject do
19
+ s = PublicActivity::Activity.new(:key => 'activity.test', :parameters => {:one => 1})
20
+ s.save && s
21
+ end
22
+
23
+ let(:template_output) { "<strong>1, 2</strong>\n<em>activity.test, #{subject.id}</em>\n" }
24
+ before { @controller.view_paths << File.expand_path('../views', __FILE__) }
25
+
26
+ it 'uses view partials when available' do
27
+ PublicActivity.set_controller(Struct.new(:current_user).new('fake'))
28
+ subject.render(self, :two => 2)
29
+ rendered.must_equal template_output + "fake\n"
30
+ end
31
+
32
+ it 'uses requested partial'
33
+
34
+ it 'uses view partials without controller' do
35
+ PublicActivity.set_controller(nil)
36
+ subject.render(self, :two => 2)
37
+ rendered.must_equal template_output + "\n"
38
+ end
39
+
40
+ it 'provides local variables' do
41
+ PublicActivity.set_controller(nil)
42
+ subject.render(self, locals: {two: 2})
43
+ rendered.chomp.must_equal "2"
44
+ end
45
+
46
+ it 'uses translations only when requested' do
47
+ I18n.config.backend.store_translations(:en,
48
+ {:activity => {:test => '%{one} %{two}'}}
49
+ )
50
+ @controller.view_paths.paths.clear
51
+ subject.render(self, two: 2, display: :i18n)
52
+ rendered.must_equal '1 2'
53
+ end
54
+
55
+ it "uses specified layout" do
56
+ PublicActivity.set_controller(nil)
57
+ subject.render(self, :layout => "activity")
58
+ rendered.must_include "Here be the layouts"
59
+
60
+ subject.render(self, :layout => "layouts/activity")
61
+ rendered.must_include "Here be the layouts"
62
+
63
+ subject.render(self, :layout => :activity)
64
+ rendered.must_include "Here be the layouts"
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,168 @@
1
+ require 'test_helper'
2
+
3
+ describe PublicActivity::Common do
4
+ before do
5
+ @owner = User.create(:name => "Peter Pan")
6
+ @recipient = User.create(:name => "Bruce Wayne")
7
+ @options = {:params => {:author_name => "Peter",
8
+ :summary => "Default summary goes here..."},
9
+ :owner => @owner, :recipient => @recipient}
10
+ end
11
+ subject { article(@options).new }
12
+
13
+ it 'prioritizes parameters passed to #create_activity' do
14
+ subject.save
15
+ subject.create_activity(:test, params: {author_name: 'Pan'}).parameters[:author_name].must_equal 'Pan'
16
+ subject.create_activity(:test, parameters: {author_name: 'Pan'}).parameters[:author_name].must_equal 'Pan'
17
+ subject.create_activity(:test, params: {author_name: nil}).parameters[:author_name].must_be_nil
18
+ subject.create_activity(:test, parameters: {author_name: nil}).parameters[:author_name].must_be_nil
19
+ end
20
+
21
+ it 'prioritizes owner passed to #create_activity' do
22
+ subject.save
23
+ subject.create_activity(:test, owner: @recipient).owner.must_equal @recipient
24
+ subject.create_activity(:test, owner: nil).owner.must_be_nil
25
+ end
26
+
27
+ it 'prioritizes recipient passed to #create_activity' do
28
+ subject.save
29
+ subject.create_activity(:test, recipient: @owner).recipient.must_equal @owner
30
+ subject.create_activity(:test, recipient: nil).recipient.must_be_nil
31
+ end
32
+
33
+ it 'uses global fields' do
34
+ subject.save
35
+ activity = subject.activities.last
36
+ activity.parameters.must_equal @options[:params]
37
+ activity.owner.must_equal @owner
38
+ end
39
+
40
+ it 'allows custom fields' do
41
+ subject.save
42
+ subject.create_activity :with_custom_fields, nonstandard: "Custom allowed"
43
+ subject.activities.last.nonstandard.must_equal "Custom allowed"
44
+ end
45
+
46
+ it '#create_activity returns a new activity object' do
47
+ subject.save
48
+ subject.create_activity("some.key").wont_be_nil
49
+ end
50
+
51
+ it 'allows passing owner through #create_activity' do
52
+ article = article().new
53
+ article.save
54
+ activity = article.create_activity("some.key", :owner => @owner)
55
+ activity.owner.must_equal @owner
56
+ end
57
+
58
+ it 'allows resolving custom fields' do
59
+ subject.name = "Resolving is great"
60
+ subject.published = true
61
+ subject.save
62
+ subject.create_activity :with_custom_fields, nonstandard: :name
63
+ subject.activities.last.nonstandard.must_equal "Resolving is great"
64
+ subject.create_activity :with_custom_fields_2, nonstandard: proc {|_, model| model.published.to_s}
65
+ subject.activities.last.nonstandard.must_equal "true"
66
+ end
67
+
68
+ it 'inherits instance parameters' do
69
+ subject.activity :params => {:author_name => "Michael"}
70
+ subject.save
71
+ activity = subject.activities.last
72
+
73
+ activity.parameters[:author_name].must_equal "Michael"
74
+ end
75
+
76
+ it 'accepts instance recipient' do
77
+ subject.activity :recipient => @recipient
78
+ subject.save
79
+ subject.activities.last.recipient.must_equal @recipient
80
+ end
81
+
82
+ it 'accepts instance owner' do
83
+ subject.activity :owner => @owner
84
+ subject.save
85
+ subject.activities.last.owner.must_equal @owner
86
+ end
87
+
88
+ it 'accepts owner as a symbol' do
89
+ klass = article(:owner => :user)
90
+ @article = klass.new(:user => @owner)
91
+ @article.save
92
+ activity = @article.activities.last
93
+
94
+ activity.owner.must_equal @owner
95
+ end
96
+
97
+ describe '#extract_key' do
98
+ describe 'for class#activity_key method' do
99
+ before do
100
+ @article = article(:owner => :user).new(:user => @owner)
101
+ end
102
+
103
+ it 'assigns key to value of activity_key if set' do
104
+ def @article.activity_key; "my_custom_key" end
105
+
106
+ @article.extract_key(:create, {}).must_equal "my_custom_key"
107
+ end
108
+
109
+ it 'assigns key based on class name as fallback' do
110
+ def @article.activity_key; nil end
111
+
112
+ @article.extract_key(:create).must_equal "article.create"
113
+ end
114
+
115
+ it 'assigns key value from options hash' do
116
+ @article.extract_key(:create, :key => :my_custom_key).must_equal "my_custom_key"
117
+ end
118
+ end
119
+
120
+ describe 'for camel cased classes' do
121
+ before do
122
+ class CamelCase < article(:owner => :user)
123
+ def self.name; 'CamelCase' end
124
+ end
125
+ @camel_case = CamelCase.new
126
+ end
127
+
128
+ it 'assigns generates key from class name' do
129
+ @camel_case.extract_key(:create, {}).must_equal "camel_case.create"
130
+ end
131
+ end
132
+
133
+ describe 'for namespaced classes' do
134
+ before do
135
+ module ::MyNamespace;
136
+ class CamelCase < article(:owner => :user)
137
+ def self.name; 'MyNamespace::CamelCase' end
138
+ end
139
+ end
140
+ @namespaced_camel_case = MyNamespace::CamelCase.new
141
+ end
142
+
143
+ it 'assigns key value from options hash' do
144
+ @namespaced_camel_case.extract_key(:create, {}).must_equal "my_namespace_camel_case.create"
145
+ end
146
+ end
147
+ end
148
+
149
+ # no key implicated or given
150
+ specify { ->{subject.prepare_settings}.must_raise PublicActivity::NoKeyProvided }
151
+
152
+ describe 'resolving values' do
153
+ it 'allows procs with models and controllers' do
154
+ context = mock('context')
155
+ context.expects(:accessor).times(2).returns(5)
156
+ controller = mock('controller')
157
+ controller.expects(:current_user).returns(:cu)
158
+ PublicActivity.set_controller(controller)
159
+ p = proc {|controller, model|
160
+ assert_equal :cu, controller.current_user
161
+ assert_equal 5, model.accessor
162
+ }
163
+ PublicActivity.resolve_value(context, p)
164
+ PublicActivity.resolve_value(context, :accessor)
165
+ end
166
+ end
167
+
168
+ end
@@ -0,0 +1,41 @@
1
+ require 'test_helper'
2
+
3
+ class StoringController < ActionView::TestCase::TestController
4
+ include PublicActivity::StoreController
5
+ include ActionController::Testing::ClassMethods
6
+ end
7
+
8
+ describe PublicActivity::StoreController do
9
+ it 'stores controller' do
10
+ controller = StoringController.new
11
+ PublicActivity.set_controller(controller)
12
+ controller.must_be_same_as PublicActivity.instance_eval { class_variable_get(:@@controllers)[Thread.current.object_id] }
13
+ controller.must_be_same_as PublicActivity.get_controller
14
+ end
15
+
16
+ it 'stores controller with a filter in controller' do
17
+ controller = StoringController.new
18
+ controller._process_action_callbacks.select {|c| c.kind == :before}.map(&:filter).must_include :store_controller_for_public_activity
19
+ controller.instance_eval { store_controller_for_public_activity }
20
+ controller.must_be_same_as PublicActivity.class_eval { class_variable_get(:@@controllers)[Thread.current.object_id] }
21
+ end
22
+
23
+ it 'stores controller in a threadsafe way' do
24
+ reset_controllers
25
+ PublicActivity.set_controller(1)
26
+ PublicActivity.get_controller.must_equal 1
27
+
28
+ a = Thread.new {
29
+ PublicActivity.set_controller(2)
30
+ PublicActivity.get_controller.must_equal 2
31
+ }
32
+
33
+ PublicActivity.get_controller.must_equal 1
34
+ # cant really test finalizers though
35
+ end
36
+
37
+ private
38
+ def reset_controllers
39
+ PublicActivity.class_eval { class_variable_set(:@@controllers, {}) }
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ if ENV["PA_ORM"] == "active_record"
2
+
3
+ require 'test_helper'
4
+ require 'rails/generators/test_case'
5
+ require 'generators/public_activity/activity/activity_generator'
6
+ require 'generators/public_activity/migration/migration_generator'
7
+ require 'generators/public_activity/migration_upgrade/migration_upgrade_generator'
8
+
9
+ class TestActivityGenerator < Rails::Generators::TestCase
10
+ tests PublicActivity::Generators::ActivityGenerator
11
+ destination File.expand_path("../tmp", File.dirname(__FILE__))
12
+ setup :prepare_destination
13
+
14
+ def test_generating_activity_model
15
+ run_generator
16
+ assert_file "app/models/activity.rb"
17
+ end
18
+ end
19
+
20
+ class TestMigrationGenerator < Rails::Generators::TestCase
21
+ tests PublicActivity::Generators::MigrationGenerator
22
+ destination File.expand_path("../tmp", File.dirname(__FILE__))
23
+ setup :prepare_destination
24
+
25
+ def test_generating_activity_model
26
+ run_generator
27
+ assert_migration "db/migrate/create_activities.rb"
28
+ end
29
+ end
30
+
31
+ class TestMigrationUpgradeGenerator < Rails::Generators::TestCase
32
+ tests PublicActivity::Generators::MigrationUpgradeGenerator
33
+ destination File.expand_path("../tmp", File.dirname(__FILE__))
34
+ setup :prepare_destination
35
+
36
+ def test_generating_activity_model
37
+ run_generator
38
+ assert_migration "db/migrate/upgrade_activities.rb"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,124 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup(:default, :test)
4
+
5
+ unless ENV['NOCOV']
6
+ require 'simplecov'
7
+ SimpleCov.start do
8
+ add_filter "/test/"
9
+ end
10
+ end
11
+ $:.unshift File.expand_path('../../lib/', __FILE__)
12
+ require 'active_support/testing/setup_and_teardown'
13
+ require 'public_activity'
14
+ require 'minitest/autorun'
15
+ require 'minitest/pride' if ENV['WITH_PRIDE'] or ENV['PRIDE']
16
+
17
+ PublicActivity::Config.orm = (ENV['PA_ORM'] || :active_record)
18
+
19
+ case PublicActivity::Config.orm
20
+ when :active_record
21
+ require 'active_record'
22
+ require 'active_record/connection_adapters/sqlite3_adapter'
23
+ require 'stringio' # silence the output
24
+ $stdout = StringIO.new # from migrator
25
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
26
+ ActiveRecord::Migrator.migrate(File.expand_path('../migrations', __FILE__))
27
+ $stdout = STDOUT
28
+
29
+ def article(options = {})
30
+ klass = Class.new(ActiveRecord::Base) do
31
+ self.table_name = 'articles'
32
+ include PublicActivity::Model
33
+ tracked options
34
+ belongs_to :user
35
+
36
+ def self.name
37
+ "Article"
38
+ end
39
+
40
+ if ::ActiveRecord::VERSION::MAJOR < 4
41
+ attr_accessible :name, :published, :user
42
+ end
43
+ end
44
+ klass
45
+ end
46
+ class User < ActiveRecord::Base; end
47
+
48
+ if ::ActiveRecord::VERSION::MAJOR < 4
49
+ PublicActivity::Activity.class_eval do
50
+ attr_accessible :nonstandard
51
+ end
52
+ end
53
+ when :mongoid
54
+ require 'mongoid'
55
+
56
+ Mongoid.load!(File.expand_path("test/mongoid.yml"), :test)
57
+
58
+ class User
59
+ include Mongoid::Document
60
+ include Mongoid::Timestamps
61
+
62
+ has_many :articles
63
+
64
+ field :name, type: String
65
+ end
66
+
67
+ class Article
68
+ include Mongoid::Document
69
+ include Mongoid::Timestamps
70
+ include PublicActivity::Model
71
+
72
+ belongs_to :user
73
+
74
+ field :name, type: String
75
+ field :published, type: Boolean
76
+ end
77
+
78
+ def article(options = {})
79
+ Article.class_eval do
80
+ set_public_activity_class_defaults
81
+ tracked options
82
+ end
83
+ Article
84
+ end
85
+
86
+ when :mongo_mapper
87
+ require 'mongo_mapper'
88
+
89
+ config = YAML.load(File.read("test/mongo_mapper.yml"))
90
+ MongoMapper.setup(config, :test)
91
+
92
+ class User
93
+ include MongoMapper::Document
94
+
95
+ has_many :articles
96
+
97
+ key :name, String
98
+ timestamps!
99
+ end
100
+
101
+ class Article
102
+ include MongoMapper::Document
103
+ include PublicActivity::Model
104
+
105
+ belongs_to :user
106
+
107
+ key :name, String
108
+ key :published, Boolean
109
+ end
110
+
111
+ def article(options = {})
112
+ Article.class_eval do
113
+ set_public_activity_class_defaults
114
+ tracked options
115
+ end
116
+ Article
117
+ end
118
+ end
119
+
120
+ class ViewSpec < MiniTest::Spec
121
+ include ActiveSupport::Testing::SetupAndTeardown
122
+ include ActionView::TestCase::Behavior
123
+ end
124
+ MiniTest::Spec.register_spec_type(/Rendering$/, ViewSpec)