activity_hub 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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +260 -0
  5. data/Rakefile +20 -0
  6. data/lib/generators/public_activity.rb +16 -0
  7. data/lib/generators/public_activity/migration/migration_generator.rb +19 -0
  8. data/lib/generators/public_activity/migration_upgrade/migration_upgrade_generator.rb +19 -0
  9. data/lib/generators/public_activity/migration_upgrade/templates/upgrade.rb +11 -0
  10. data/lib/public_activity.rb +70 -0
  11. data/lib/public_activity/actions/creation.rb +19 -0
  12. data/lib/public_activity/actions/destruction.rb +19 -0
  13. data/lib/public_activity/actions/update.rb +20 -0
  14. data/lib/public_activity/activity.rb +8 -0
  15. data/lib/public_activity/common.rb +363 -0
  16. data/lib/public_activity/config.rb +102 -0
  17. data/lib/public_activity/models/activist.rb +11 -0
  18. data/lib/public_activity/models/activity.rb +6 -0
  19. data/lib/public_activity/models/adapter.rb +7 -0
  20. data/lib/public_activity/models/trackable.rb +11 -0
  21. data/lib/public_activity/orm/active_record.rb +7 -0
  22. data/lib/public_activity/orm/active_record/activist.rb +35 -0
  23. data/lib/public_activity/orm/active_record/activity.rb +66 -0
  24. data/lib/public_activity/orm/active_record/adapter.rb +23 -0
  25. data/lib/public_activity/orm/active_record/trackable.rb +17 -0
  26. data/lib/public_activity/orm/mongo_mapper.rb +6 -0
  27. data/lib/public_activity/orm/mongo_mapper/activist.rb +36 -0
  28. data/lib/public_activity/orm/mongo_mapper/activity.rb +35 -0
  29. data/lib/public_activity/orm/mongo_mapper/adapter.rb +19 -0
  30. data/lib/public_activity/orm/mongo_mapper/trackable.rb +13 -0
  31. data/lib/public_activity/orm/mongoid.rb +6 -0
  32. data/lib/public_activity/orm/mongoid/activist.rb +36 -0
  33. data/lib/public_activity/orm/mongoid/activity.rb +34 -0
  34. data/lib/public_activity/orm/mongoid/adapter.rb +19 -0
  35. data/lib/public_activity/orm/mongoid/trackable.rb +13 -0
  36. data/lib/public_activity/renderable.rb +166 -0
  37. data/lib/public_activity/roles/deactivatable.rb +44 -0
  38. data/lib/public_activity/roles/tracked.rb +196 -0
  39. data/lib/public_activity/testing.rb +37 -0
  40. data/lib/public_activity/utility/store_controller.rb +32 -0
  41. data/lib/public_activity/utility/view_helpers.rb +30 -0
  42. data/lib/public_activity/version.rb +6 -0
  43. data/test/migrations/001_create_activities.rb +25 -0
  44. data/test/migrations/002_create_articles.rb +15 -0
  45. data/test/migrations/003_create_users.rb +12 -0
  46. data/test/migrations/004_add_nonstandard_to_activities.rb +11 -0
  47. data/test/migrations_base.rb +7 -0
  48. data/test/mongo_mapper.yml +4 -0
  49. data/test/mongoid.yml +6 -0
  50. data/test/test_activist.rb +58 -0
  51. data/test/test_activity.rb +89 -0
  52. data/test/test_common.rb +194 -0
  53. data/test/test_controller_integration.rb +42 -0
  54. data/test/test_generators.rb +31 -0
  55. data/test/test_helper.rb +144 -0
  56. data/test/test_testing.rb +37 -0
  57. data/test/test_tracking.rb +385 -0
  58. data/test/test_view_helpers.rb +38 -0
  59. data/test/views/custom/_layout.erb +1 -0
  60. data/test/views/custom/_test.erb +1 -0
  61. data/test/views/layouts/_activity.erb +1 -0
  62. data/test/views/public_activity/_test.erb +8 -0
  63. metadata +295 -0
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ describe PublicActivity::Activist do
6
+ it 'adds owner association' do
7
+ klass = article
8
+ klass.must_respond_to :activist
9
+ klass.activist
10
+ klass.new.must_respond_to :activities
11
+ case ENV["PA_ORM"]
12
+ when "active_record"
13
+ klass.reflect_on_association(:activities_as_owner).options[:as].must_equal :owner
14
+ when "mongoid"
15
+ klass.reflect_on_association(:activities_as_owner).options[:inverse_of].must_equal :owner
16
+ when "mongo_mapper"
17
+ klass.associations[:activities_as_owner].options[:as].must_equal :owner
18
+ end
19
+
20
+ if ENV["PA_ORM"] == "mongo_mapper"
21
+ klass.associations[:activities_as_owner].options[:class_name].must_equal "::PublicActivity::Activity"
22
+ else
23
+ klass.reflect_on_association(:activities_as_owner).options[:class_name].must_equal "::PublicActivity::Activity"
24
+ end
25
+ end
26
+
27
+ it 'returns activities from association' do
28
+ case PublicActivity::Config.orm
29
+ when :active_record
30
+ class ActivistUser < ActiveRecord::Base
31
+ include PublicActivity::Model
32
+ self.table_name = 'users'
33
+ activist
34
+ end
35
+ when :mongoid
36
+ class ActivistUser
37
+ include Mongoid::Document
38
+ include PublicActivity::Model
39
+ activist
40
+
41
+ field :name, type: String
42
+ end
43
+ when :mongo_mapper
44
+ class ActivistUser
45
+ include MongoMapper::Document
46
+ include PublicActivity::Model
47
+ activist
48
+
49
+ key :name, String
50
+ end
51
+ end
52
+ owner = ActivistUser.create(:name => "Peter Pan")
53
+ a = article(owner: owner).new
54
+ a.save
55
+
56
+ owner.activities_as_owner.length.must_equal 1
57
+ end
58
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ describe 'PublicActivity::Activity Rendering' do
6
+ describe '#text' do
7
+ subject { PublicActivity::Activity.new(:key => 'activity.test', :parameters => {:one => 1}) }
8
+
9
+ specify '#text uses translations' do
10
+ subject.save
11
+ I18n.config.backend.store_translations(:en,
12
+ {:activity => {:test => '%{one} %{two}'}}
13
+ )
14
+ subject.text(:two => 2).must_equal('1 2')
15
+ subject.parameters.must_equal({:one => 1})
16
+ end
17
+ end
18
+
19
+ describe '#render' do
20
+ subject do
21
+ s = PublicActivity::Activity.new(:key => 'activity.test', :parameters => {:one => 1})
22
+ s.save && s
23
+ end
24
+
25
+ let(:template_output) { "<strong>1, 2</strong>\n<em>activity.test, #{subject.id}</em>\n" }
26
+ before { @controller.view_paths << File.expand_path('../views', __FILE__) }
27
+
28
+ it 'uses view partials when available' do
29
+ PublicActivity.set_controller(Struct.new(:current_user).new('fake'))
30
+ subject.render(self, :two => 2)
31
+ rendered.must_equal template_output + "fake\n"
32
+ end
33
+
34
+ it 'uses requested partial'
35
+
36
+ it 'uses view partials without controller' do
37
+ PublicActivity.set_controller(nil)
38
+ subject.render(self, :two => 2)
39
+ rendered.must_equal template_output + "\n"
40
+ end
41
+
42
+ it 'provides local variables' do
43
+ PublicActivity.set_controller(nil)
44
+ subject.render(self, locals: {two: 2})
45
+ rendered.chomp.must_equal "2"
46
+ end
47
+
48
+ it 'uses translations only when requested' do
49
+ I18n.config.backend.store_translations(:en,
50
+ {:activity => {:test => '%{one} %{two}'}}
51
+ )
52
+ @controller.view_paths.paths.clear
53
+ subject.render(self, two: 2, display: :i18n)
54
+ rendered.must_equal '1 2'
55
+ end
56
+
57
+ it "pass all params to view context" do
58
+ view_context = mock('ViewContext')
59
+ PublicActivity.set_controller(nil)
60
+ view_context.expects(:render).with() {|params| params[:formats] == ['json']}
61
+ subject.render(view_context, :formats => ['json'])
62
+ end
63
+
64
+ it "uses specified layout" do
65
+ PublicActivity.set_controller(nil)
66
+ subject.render(self, :layout => "activity")
67
+ rendered.must_include "Here be the layouts"
68
+
69
+ subject.render(self, :layout => "layouts/activity")
70
+ rendered.must_include "Here be the layouts"
71
+
72
+ subject.render(self, :layout => :activity)
73
+ rendered.must_include "Here be the layouts"
74
+ end
75
+
76
+ it "accepts a custom layout root" do
77
+ subject.render(self, :layout => :layout, :layout_root => "custom")
78
+ rendered.must_include "Here be the custom layouts"
79
+ end
80
+ it "accepts an absolute layout path" do
81
+ subject.render(self, :layout => '/custom/layout')
82
+ rendered.must_include "Here be the custom layouts"
83
+ end
84
+ it "accepts a template root" do
85
+ subject.render(self, :root => "custom")
86
+ rendered.must_include "Custom Template Root"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ describe PublicActivity::Common do
6
+ before do
7
+ @owner = User.create(:name => "Peter Pan")
8
+ @recipient = User.create(:name => "Bruce Wayne")
9
+ @options = {:params => {:author_name => "Peter",
10
+ :summary => "Default summary goes here..."},
11
+ :owner => @owner, :recipient => @recipient}
12
+ end
13
+ subject { article(@options).new }
14
+
15
+ it 'prioritizes parameters passed to #create_activity' do
16
+ subject.save
17
+ subject.create_activity(:test, params: {author_name: 'Pan'}).parameters[:author_name].must_equal 'Pan'
18
+ subject.create_activity(:test, parameters: {author_name: 'Pan'}).parameters[:author_name].must_equal 'Pan'
19
+ subject.create_activity(:test, params: {author_name: nil}).parameters[:author_name].must_be_nil
20
+ subject.create_activity(:test, parameters: {author_name: nil}).parameters[:author_name].must_be_nil
21
+ end
22
+
23
+ it 'prioritizes owner passed to #create_activity' do
24
+ subject.save
25
+ subject.create_activity(:test, owner: @recipient).owner.must_equal @recipient
26
+ subject.create_activity(:test, owner: nil).owner.must_be_nil
27
+ end
28
+
29
+ it 'prioritizes recipient passed to #create_activity' do
30
+ subject.save
31
+ subject.create_activity(:test, recipient: @owner).recipient.must_equal @owner
32
+ subject.create_activity(:test, recipient: nil).recipient.must_be_nil
33
+ end
34
+
35
+ it 'uses global fields' do
36
+ subject.save
37
+ activity = subject.activities.last
38
+ activity.parameters.must_equal @options[:params]
39
+ activity.owner.must_equal @owner
40
+ end
41
+
42
+ it 'allows custom fields' do
43
+ subject.save
44
+ subject.create_activity :with_custom_fields, nonstandard: "Custom allowed"
45
+ subject.activities.last.nonstandard.must_equal "Custom allowed"
46
+ end
47
+
48
+ it '#create_activity returns a new activity object' do
49
+ subject.save
50
+ subject.create_activity("some.key").wont_be_nil
51
+ end
52
+
53
+ it '#create_activity! returns a new activity object' do
54
+ subject.save
55
+ activity = subject.create_activity!("some.key")
56
+ assert activity.persisted?
57
+ assert_equal 'article.some.key', activity.key
58
+ end
59
+
60
+ it 'update action should not create activity on save unless model changed' do
61
+ subject.save
62
+ before_count = subject.activities.count
63
+ subject.save
64
+ subject.save
65
+ after_count = subject.activities.count
66
+ before_count.must_equal after_count
67
+ end
68
+
69
+
70
+ it 'allows passing owner through #create_activity' do
71
+ article = article().new
72
+ article.save
73
+ activity = article.create_activity("some.key", :owner => @owner)
74
+ activity.owner.must_equal @owner
75
+ end
76
+
77
+ it 'allows resolving custom fields' do
78
+ subject.name = "Resolving is great"
79
+ subject.published = true
80
+ subject.save
81
+ subject.create_activity :with_custom_fields, nonstandard: :name
82
+ subject.activities.last.nonstandard.must_equal "Resolving is great"
83
+ subject.create_activity :with_custom_fields_2, nonstandard: proc {|_, model| model.published.to_s}
84
+ subject.activities.last.nonstandard.must_equal "true"
85
+ end
86
+
87
+ it 'inherits instance parameters' do
88
+ subject.activity :params => {:author_name => "Michael"}
89
+ subject.save
90
+ activity = subject.activities.last
91
+
92
+ activity.parameters[:author_name].must_equal "Michael"
93
+ end
94
+
95
+ it 'accepts instance recipient' do
96
+ subject.activity :recipient => @recipient
97
+ subject.save
98
+ subject.activities.last.recipient.must_equal @recipient
99
+ end
100
+
101
+ it 'accepts instance owner' do
102
+ subject.activity :owner => @owner
103
+ subject.save
104
+ subject.activities.last.owner.must_equal @owner
105
+ end
106
+
107
+ it 'accepts owner as a symbol' do
108
+ klass = article(:owner => :user)
109
+ @article = klass.new(:user => @owner)
110
+ @article.save
111
+ activity = @article.activities.last
112
+
113
+ activity.owner.must_equal @owner
114
+ end
115
+
116
+ it 'reports PublicActivity::Activity as the base class' do
117
+ if ENV["PA_ORM"] == "active_record" # Only relevant for ActiveRecord
118
+ subject.save
119
+ subject.activities.last.class.base_class.must_equal PublicActivity::Activity
120
+ end
121
+ end
122
+
123
+ describe '#prepare_key' do
124
+ describe 'for class#activity_key method' do
125
+ before do
126
+ @article = article(:owner => :user).new(:user => @owner)
127
+ end
128
+
129
+ it 'assigns key to value of activity_key if set' do
130
+ def @article.activity_key; "my_custom_key" end
131
+
132
+ @article.prepare_key(:create, {}).must_equal "my_custom_key"
133
+ end
134
+
135
+ it 'assigns key based on class name as fallback' do
136
+ def @article.activity_key; nil end
137
+
138
+ @article.prepare_key(:create).must_equal "article.create"
139
+ end
140
+
141
+ it 'assigns key value from options hash' do
142
+ @article.prepare_key(:create, :key => :my_custom_key).must_equal "my_custom_key"
143
+ end
144
+ end
145
+
146
+ describe 'for camel cased classes' do
147
+ before do
148
+ class CamelCase < article(:owner => :user)
149
+ def self.name; 'CamelCase' end
150
+ end
151
+ @camel_case = CamelCase.new
152
+ end
153
+
154
+ it 'assigns generates key from class name' do
155
+ @camel_case.prepare_key(:create, {}).must_equal "camel_case.create"
156
+ end
157
+ end
158
+
159
+ describe 'for namespaced classes' do
160
+ before do
161
+ module ::MyNamespace;
162
+ class CamelCase < article(:owner => :user)
163
+ def self.name; 'MyNamespace::CamelCase' end
164
+ end
165
+ end
166
+ @namespaced_camel_case = MyNamespace::CamelCase.new
167
+ end
168
+
169
+ it 'assigns key value from options hash' do
170
+ @namespaced_camel_case.prepare_key(:create, {}).must_equal "my_namespace_camel_case.create"
171
+ end
172
+ end
173
+ end
174
+
175
+ # no key implicated or given
176
+ specify { ->{subject.prepare_settings}.must_raise PublicActivity::NoKeyProvided }
177
+
178
+ describe 'resolving values' do
179
+ it 'allows procs with models and controllers' do
180
+ context = mock('context')
181
+ context.expects(:accessor).times(2).returns(5)
182
+ controller = mock('controller')
183
+ controller.expects(:current_user).returns(:cu)
184
+ PublicActivity.set_controller(controller)
185
+ p = proc {|c, m|
186
+ assert_equal :cu, c.current_user
187
+ assert_equal 5, m.accessor
188
+ }
189
+ PublicActivity.resolve_value(context, p)
190
+ PublicActivity.resolve_value(context, :accessor)
191
+ end
192
+ end
193
+
194
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class StoringController < ActionView::TestCase::TestController
6
+ include PublicActivity::StoreController
7
+ include ActionController::Testing
8
+ end
9
+
10
+ describe PublicActivity::StoreController do
11
+ it 'stores controller' do
12
+ controller = StoringController.new
13
+ PublicActivity.set_controller(controller)
14
+ controller.must_be_same_as Thread.current[:public_activity_controller]
15
+ controller.must_be_same_as PublicActivity.get_controller
16
+ end
17
+
18
+ it 'stores controller with a filter in controller' do
19
+ controller = StoringController.new
20
+ controller._process_action_callbacks.select {|c| c.kind == :around}.map(&:filter).must_include :store_controller_for_public_activity
21
+ controller.instance_eval do
22
+ store_controller_for_public_activity do
23
+ controller.must_be_same_as PublicActivity.get_controller
24
+ end
25
+ end
26
+ end
27
+
28
+ it 'stores controller in a threadsafe way' do
29
+ PublicActivity.set_controller(1)
30
+ PublicActivity.get_controller.must_equal 1
31
+
32
+ Thread.new {
33
+ PublicActivity.set_controller(2)
34
+ assert_equal 2, PublicActivity.get_controller
35
+ PublicActivity.set_controller(nil)
36
+ }
37
+
38
+ PublicActivity.get_controller.must_equal 1
39
+
40
+ PublicActivity.set_controller(nil)
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ if ENV["PA_ORM"] == "active_record"
4
+
5
+ require 'test_helper'
6
+ require 'rails/generators/test_case'
7
+ require 'generators/public_activity/migration/migration_generator'
8
+ require 'generators/public_activity/migration_upgrade/migration_upgrade_generator'
9
+
10
+ class TestMigrationGenerator < Rails::Generators::TestCase
11
+ tests PublicActivity::Generators::MigrationGenerator
12
+ destination File.expand_path("../tmp", File.dirname(__FILE__))
13
+ setup :prepare_destination
14
+
15
+ def test_generating_activity_model
16
+ run_generator
17
+ assert_migration "db/migrate/create_activities.rb"
18
+ end
19
+ end
20
+
21
+ class TestMigrationUpgradeGenerator < Rails::Generators::TestCase
22
+ tests PublicActivity::Generators::MigrationUpgradeGenerator
23
+ destination File.expand_path("../tmp", File.dirname(__FILE__))
24
+ setup :prepare_destination
25
+
26
+ def test_generating_activity_model
27
+ run_generator
28
+ assert_migration "db/migrate/upgrade_activities.rb"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems"
4
+ require "bundler"
5
+ Bundler.setup(:default, :test)
6
+
7
+ if ENV['COV']
8
+ require 'simplecov'
9
+ SimpleCov.start do
10
+ add_filter "/test/"
11
+ end
12
+ end
13
+ $:.unshift File.expand_path('../../lib/', __FILE__)
14
+ require 'active_support/testing/setup_and_teardown'
15
+ require 'public_activity'
16
+ require 'public_activity/testing'
17
+ require 'pry'
18
+ require 'minitest/autorun'
19
+ require 'mocha/minitest'
20
+
21
+ PublicActivity::Config.orm = (ENV['PA_ORM'] || :active_record)
22
+
23
+ case PublicActivity::Config.orm
24
+ when :active_record
25
+ require 'active_record'
26
+ require 'active_record/connection_adapters/sqlite3_adapter'
27
+ require 'stringio' # silence the output
28
+ $stdout = StringIO.new # from migrator
29
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
30
+
31
+ migrations_path = File.expand_path('../migrations', __FILE__)
32
+
33
+ if ActiveRecord.version.release() < Gem::Version.new('5.2.0')
34
+ ActiveRecord::Migrator.migrate(migrations_path)
35
+ else
36
+ ActiveRecord::MigrationContext.new(migrations_path).migrate
37
+ end
38
+
39
+ $stdout = STDOUT
40
+
41
+ def article(options = {})
42
+ klass = Class.new(ActiveRecord::Base) do
43
+ self.table_name = 'articles'
44
+ include PublicActivity::Model
45
+ tracked options
46
+ belongs_to :user
47
+
48
+ def self.name
49
+ "Article"
50
+ end
51
+
52
+ if ::ActiveRecord::VERSION::MAJOR < 4
53
+ attr_accessible :name, :published, :user
54
+ end
55
+ end
56
+ klass
57
+ end
58
+ class User < ActiveRecord::Base; end
59
+
60
+ if ::ActiveRecord::VERSION::MAJOR < 4
61
+ PublicActivity::Activity.class_eval do
62
+ attr_accessible :nonstandard
63
+ end
64
+ end
65
+ when :mongoid
66
+ require 'mongoid'
67
+
68
+ Mongoid.load!(File.expand_path("test/mongoid.yml"), :test)
69
+
70
+ class User
71
+ include Mongoid::Document
72
+ include Mongoid::Timestamps
73
+
74
+ has_many :articles
75
+
76
+ field :name, type: String
77
+ end
78
+
79
+ class Article
80
+ include Mongoid::Document
81
+ include Mongoid::Timestamps
82
+ include PublicActivity::Model
83
+
84
+ if ::Mongoid::VERSION.split('.')[0].to_i >= 7
85
+ belongs_to :user, optional: true
86
+ else
87
+ belongs_to :user
88
+ end
89
+
90
+ field :name, type: String
91
+ field :published, type: Boolean
92
+ end
93
+
94
+ def article(options = {})
95
+ Article.class_eval do
96
+ set_public_activity_class_defaults
97
+ tracked options
98
+ end
99
+ Article
100
+ end
101
+
102
+ when :mongo_mapper
103
+ require 'mongo_mapper'
104
+
105
+ config = YAML.load(File.read("test/mongo_mapper.yml"))
106
+ MongoMapper.setup(config, :test)
107
+
108
+ class User
109
+ include MongoMapper::Document
110
+
111
+ has_many :articles
112
+
113
+ key :name, String
114
+ timestamps!
115
+ end
116
+
117
+ class Article
118
+ include MongoMapper::Document
119
+ include PublicActivity::Model
120
+
121
+ belongs_to :user
122
+
123
+ key :name, String
124
+ key :published, Boolean
125
+ end
126
+
127
+ def article(options = {})
128
+ Article.class_eval do
129
+ set_public_activity_class_defaults
130
+ tracked options
131
+ end
132
+ Article
133
+ end
134
+ end
135
+
136
+ class ViewSpec < MiniTest::Spec
137
+ if ActiveSupport.version >= Gem::Version.new('5.2.0')
138
+ prepend ActiveSupport::Testing::SetupAndTeardown
139
+ else
140
+ include ActiveSupport::Testing::SetupAndTeardown
141
+ end
142
+ include ActionView::TestCase::Behavior
143
+ end
144
+ MiniTest::Spec.register_spec_type(/Rendering$/, ViewSpec)