netphase-acts_as_eventable 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
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 ADDED
@@ -0,0 +1,184 @@
1
+ = ActsAsEventable
2
+
3
+ This "acts_as" module makes it extremely simple to add events to a model that will
4
+ be stored in a table for later querying or reporting on. The events have an event
5
+ type, which also corresponds to how the event is triggered.
6
+
7
+ An example of acts_as_eventable's use would be if you needed to track every time a
8
+ particular object was viewed on your site. You could setup and event type of 'view',
9
+ then simply call my_object.view from the the controller's show method (assuming a
10
+ restful design is in play, of course). This would trigger an entry in the event table,
11
+ noting the object that was viewed.
12
+
13
+ The events can be anything you want, and you can call the methods from anywhere. It
14
+ works whether the object is a first-class Active Record object, or an associated
15
+ object (via has_xxx or belongs_to).
16
+
17
+ This is the initial commit, and I have not written any tests or comments in the code
18
+ as of yet. Soon come, mon!
19
+
20
+ == Install
21
+
22
+ Acts As Eventable is hosted on github. It may be installed as a gem or a plugin. To
23
+ install as a gem, do the following:
24
+
25
+ sudo gem install netphase-acts_as_eventable
26
+
27
+ To install as a plugin, do this:
28
+
29
+ script/plugin install git://github.com/netphase/acts_as_eventable.git
30
+
31
+ You may also get the latest from github.com by cloning from git://github.com/netphase/acts_as_eventable.git
32
+
33
+ == Setup
34
+
35
+ This code does not require changing any current database tables. It only requires
36
+ adding one migration for two new tables which contain the event types and track the
37
+ events.
38
+
39
+ If you are using acts_as_eventable as a plugin, you can run this to create the migration:
40
+
41
+ script/generate acts_as_eventable_migration
42
+
43
+ If you are using the gem, create a migration with the following:
44
+
45
+ def self.up
46
+ create_table :event_types do |t|
47
+ t.string :name
48
+ end
49
+
50
+ create_table :events do |t|
51
+ t.integer :eventable_id, :default => 0, :null => false
52
+ t.string :eventable_type, :limit => 25, :default => "", :null => false
53
+ t.integer :event_type_id
54
+
55
+ t.timestamps
56
+ end
57
+ add_index :events, [:eventable_id, :eventable_type]
58
+ add_index :events, :event_type_id
59
+ end
60
+
61
+ def self.down
62
+ drop_table :events
63
+ drop_table :event_types
64
+ end
65
+
66
+ IMPORTANT! Acts as Eventable relies on acts_as_enumerated to cache the event types.
67
+ You need to install the enumerations_mixin plugin.
68
+
69
+ To get it, do this:
70
+
71
+ script/plugin install http://svn.protocool.com/public/plugins/enumerations_mixin
72
+
73
+ == Using acts_as_eventable
74
+
75
+ First off, after you run the migration above, you need to populate your event_types
76
+ table with whatever kind of events you are going to track. Let's look at the example
77
+ of a coupon website.
78
+
79
+ For our coupon site, we want to know every time someone views, prints or clips a coupon.
80
+ In that case, I would put the following items in a database population migration or
81
+ fixture:
82
+
83
+ EventType.enumeration_model_updates_permitted = true
84
+ EventType.create :name => "print"
85
+ EventType.create :name => "clip"
86
+ EventType.create :name => "view"
87
+
88
+ Please note that the enumeration_model_updates_permitted = true line is required
89
+ whenever you are updating the event_types table, because acts_as_eventable uses
90
+ acts_as_enumerated for the EventType model (please make sure you have the
91
+ enumerations_mixin plugin to support this as I have not yet added it).
92
+
93
+ Now with your event types setup, you are ready to log events on your objects. Let's
94
+ suppose we have a Coupon object that is living in a lovely RESTfully architected
95
+ application.
96
+
97
+ First, in the Coupon model, you add this:
98
+
99
+ class Coupon < ActiveRecord::Base
100
+ acts_as_eventable
101
+ end
102
+
103
+ In the CouponController's show method, you can do this:
104
+
105
+ def show
106
+ @coupon = Coupon.find(params[:id])
107
+ @coupon.view
108
+ end
109
+
110
+ Now, whenever someone views that individual coupon, an entry will be logged in the
111
+ events table that looks like this:
112
+
113
+ +----+--------------+----------------+---------------+---------------------+---------------------+
114
+ | id | eventable_id | eventable_type | event_type_id | created_at | updated_at |
115
+ +----+--------------+----------------+---------------+---------------------+---------------------+
116
+ | 1 | 6 | Coupon | 3 | 2009-06-15 17:37:47 | 2009-06-15 17:37:47 |
117
+ +----+--------------+----------------+---------------+---------------------+---------------------+
118
+
119
+ Pretty cool! Notice, we did not add any other methods to Coupon object. The code automatically
120
+ gives the coupon object the methods that correspond to the names of the event types!
121
+
122
+ Now, you can actually call the event type items anything you want. Because these items are added
123
+ as methods to whatever object you are making eventable, you may have to come up with some
124
+ interesting names to prevent name collisions. But, they are purely arbitrary. Just know that the
125
+ event type name is the same as the method name you will call on your eventable object. One more
126
+ thing: make sure the name can be a valid :symbol. It is important for the rails internals in this
127
+ case.
128
+
129
+ So, does it work with associated objects, you ask? As a matter of fact, it does! Let's say you
130
+ had a ClippedCoupon model, and it was defined as such:
131
+
132
+ class ClippedCoupon < ActiveRecord::Base
133
+ belongs_to :coupon
134
+ belongs_to :user # just makes our example make more sense - need to know who clipped it!
135
+ end
136
+
137
+ Notice, I did not make this object acts_as_eventable. Just an ordinary association, because we are
138
+ tracking Coupons, not necessarily ClippedCoupons.
139
+
140
+ Now, if I want to know if the coupon gets clipped, I would put this in my create method in the
141
+ ClippedCouponController (again, assuming your app is RESTful):
142
+
143
+ def create
144
+ @clipped_coupon = ClippedCoupon.create(:coupon_id => params[:coupon_id], :user_id => current_user.id)
145
+ @clipped_coupon.coupon.clip
146
+ end
147
+
148
+ Bam! You have just made the following entry into the events table:
149
+ +----+--------------+----------------+---------------+---------------------+---------------------+
150
+ | id | eventable_id | eventable_type | event_type_id | created_at | updated_at |
151
+ +----+--------------+----------------+---------------+---------------------+---------------------+
152
+ | 2 | 7 | Coupon | 2 | 2009-06-15 18:01:40 | 2009-06-15 18:01:40 |
153
+ +----+--------------+----------------+---------------+---------------------+---------------------+
154
+
155
+ And voila! You now have the basis for reporting events on your objects. I will leave that part up to
156
+ your imagination for now.
157
+
158
+ There are a couple of other minor methods in the code, and I am sure I will add a bunch more. I will
159
+ update this README once I have tests and more methods for you to play with.
160
+
161
+ For now, enjoy!
162
+
163
+ = Credits
164
+ acts_as_eventable was created by Netphase, LLC.
165
+
166
+ Netphase is Chris Beck and Scott Nedderman.
167
+
168
+ We'd love to here how you like acts_as_eventable. If you use it in one of your projects, please
169
+ drop us a line.
170
+
171
+ For more information or updates on this plugin, visit http://github.com/netphase/acts_as_eventable/tree/master
172
+
173
+ Also, check out our other projects at http://netphase.com. We are available for consulting work. Contact us
174
+ for your next project.
175
+
176
+ Copyright (c) 2009 Chris Beck, released under the MIT license
177
+
178
+
179
+
180
+
181
+
182
+
183
+
184
+
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the acts_as_eventable plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the acts_as_eventable plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'ActsAsEventable'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'jeweler'
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = "netphase-acts_as_eventable"
29
+ gemspec.summary = "This acts_as module makes it extremely simple to add events to a model that will be stored in a table for later querying or reporting on."
30
+ gemspec.email = "chris@netphase.com"
31
+ gemspec.homepage = "http://github.com/netphase/acts_as_eventable"
32
+ gemspec.description = "This acts_as module makes it extremely simple to add events to a model that will be stored in a table for later querying or reporting on. The events have an event type, which also corresponds to how the event is triggered. View the README for examples and usage instructions."
33
+ gemspec.authors = ["Chris Beck"]
34
+ end
35
+ rescue LoadError
36
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
37
+ end
38
+
39
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,11 @@
1
+ class ActsAsEventableMigrationGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template 'migration.rb', 'db/migrate'
5
+ end
6
+ end
7
+
8
+ def file_name
9
+ "acts_as_eventable_migration"
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ class ActsAsEventableMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :event_types do |t|
4
+ t.string :name
5
+ end
6
+
7
+ create_table :events do |t|
8
+ t.integer :eventable_id, :default => 0, :null => false
9
+ t.string :eventable_type, :limit => 25, :default => "", :null => false
10
+ t.integer :event_type_id
11
+
12
+ t.timestamps
13
+ end
14
+ add_index :events, [:eventable_id, :eventable_type]
15
+ add_index :events, :event_type_id
16
+ end
17
+
18
+ def self.down
19
+ drop_table :events
20
+ drop_table :event_types
21
+ end
22
+ end
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ # Include hook code here
2
+ require 'acts_as_eventable'
3
+ ActiveRecord::Base.send(:include, Netphase::Acts::Eventable::ActiveRecordExtensions)
4
+ # ActionView::Base.send(:include, Netphase::Acts::Eventable::ActionViewExtensions)
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,86 @@
1
+ module Netphase
2
+ module Acts
3
+ module Eventable
4
+
5
+ module ActiveRecordExtensions
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def acts_as_eventable
12
+ has_many :events, :as => :eventable, :dependent => :destroy
13
+ extend SingletonMethods
14
+ include InstanceMethods
15
+
16
+ # any class methods here - like validations and callbacks
17
+ end
18
+ end
19
+
20
+ module SingletonMethods
21
+ # Helper method to lookup for events for a given object.
22
+ # This method is equivalent to obj.issues.
23
+ def find_events_for(obj)
24
+ eventable = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
25
+
26
+ Event.find(:all,
27
+ :conditions => ["eventable_id = ? and eventable_type = ?", obj.id, eventable],
28
+ :order => "created_at DESC"
29
+ )
30
+ end
31
+
32
+ end
33
+
34
+ module InstanceMethods
35
+ # Helper method to sort events by date
36
+ def events_by_most_recent
37
+ Event.find(:all,
38
+ :conditions => ["eventable_id = ? and eventable_type = ?", id, self.type.name],
39
+ :order => "created_at DESC"
40
+ )
41
+ end
42
+
43
+ def events_by_event_type(symbol)
44
+ event_type = EventType[symbol]
45
+ Event.find(:all,
46
+ :conditions => ["eventable_id = ? and eventable_type = ? and event_type_id = ?", id, self.type.name, event_type.id],
47
+ :order => "created_at DESC"
48
+ )
49
+ end
50
+
51
+ def call(symbol, *args)
52
+ event_type = EventType[symbol]
53
+ Event.create(:eventable_id => id, :eventable_type => self.type.name, :event_type_id => event_type.id) unless event_type.blank?
54
+ end
55
+
56
+ def method_missing(symbol, *args)
57
+ begin
58
+ super(symbol, *args)
59
+ rescue NoMethodError
60
+ call(symbol, *args)
61
+ end
62
+ end
63
+
64
+ def respond_to?(method_id, include_private = false)
65
+ if EventType.to_a.include? method_id.to_s
66
+ return true
67
+ else
68
+ super
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
76
+ module ActionControllerExtensions
77
+ # not sure i will need this yet - placeholder so i don't forget
78
+ end
79
+
80
+ module ActionViewExtensions
81
+ # not sure i will need this yet - placeholder so i don't forget
82
+ end
83
+
84
+ end
85
+ end
86
+ end
data/lib/event.rb ADDED
@@ -0,0 +1,24 @@
1
+ class Event < ActiveRecord::Base
2
+ belongs_to :eventable, :polymorphic => true
3
+ has_enumerated :event_type
4
+
5
+ def self.find_events_for_eventable(eventable_str, eventable_id)
6
+ find(:all,
7
+ :conditions => ["eventable_type = ? and eventable_id = ?", eventable_str, eventable_id],
8
+ :order => "updated_at DESC"
9
+ )
10
+ end
11
+
12
+ def self.find_events_for_eventable_by_event_type(eventable_str, event_type_id)
13
+ find(:all,
14
+ :conditions => ["eventable_type = ? and event_type_id = ?", eventable_str, event_type_id],
15
+ :order => "created_at ASC"
16
+ )
17
+ end
18
+
19
+ def self.find_eventable(eventable_str, eventable_id)
20
+ eventable_str.constantize.find(eventable_id)
21
+ end
22
+
23
+
24
+ end
data/lib/event_type.rb ADDED
@@ -0,0 +1,8 @@
1
+ class EventType < ActiveRecord::Base
2
+ has_many :events
3
+ acts_as_enumerated
4
+
5
+ def self.to_a
6
+ a = EventType.all.collect {|et| et.name } || []
7
+ end
8
+ end
@@ -0,0 +1,55 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{acts_as_eventable}
5
+ s.version = "0.2.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Chris Beck"]
9
+ s.date = %q{2009-06-18}
10
+ s.description = %q{This acts_as module makes it extremely simple to add events to a model that will be stored in a table for later querying or reporting on. The events have an event type, which also corresponds to how the event is triggered. View the README for examples and usage instructions.}
11
+ s.email = %q{chris@netphase.com}
12
+ s.extra_rdoc_files = [
13
+ "README"
14
+ ]
15
+ s.files = [
16
+ "MIT-LICENSE",
17
+ "README",
18
+ "Rakefile",
19
+ "VERSION",
20
+ "generators/acts_as_eventable_migration/acts_as_eventable_migration_generator.rb",
21
+ "generators/acts_as_eventable_migration/templates/migration.rb",
22
+ "init.rb",
23
+ "install.rb",
24
+ "lib/acts_as_eventable.rb",
25
+ "lib/event.rb",
26
+ "lib/event_type.rb",
27
+ "netphase-acts_as_eventable.gemspec",
28
+ "pkg/netphase-acts_as_eventable-0.0.0.gem",
29
+ "pkg/netphase-acts_as_eventable-0.2.0.gem",
30
+ "tasks/acts_as_eventable_tasks.rake",
31
+ "test/acts_as_eventable_test.rb",
32
+ "test/test_helper.rb",
33
+ "uninstall.rb"
34
+ ]
35
+ s.has_rdoc = true
36
+ s.homepage = %q{http://github.com/netphase/acts_as_eventable}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.1}
40
+ s.summary = %q{This acts_as module makes it extremely simple to add events to a model that will be stored in a table for later querying or reporting on.}
41
+ s.test_files = [
42
+ "test/acts_as_eventable_test.rb",
43
+ "test/test_helper.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 2
49
+
50
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
+ else
52
+ end
53
+ else
54
+ end
55
+ end
File without changes
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class ActsAsEventableTest < ActiveSupport::TestCase
4
+ # Replace this with your real tests.
5
+ test "the truth" do
6
+ assert true
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'active_support/test_case'
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: netphase-acts_as_eventable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Beck
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-18 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: This acts_as module makes it extremely simple to add events to a model that will be stored in a table for later querying or reporting on. The events have an event type, which also corresponds to how the event is triggered. View the README for examples and usage instructions.
17
+ email: chris@netphase.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - MIT-LICENSE
26
+ - README
27
+ - Rakefile
28
+ - VERSION
29
+ - generators/acts_as_eventable_migration/acts_as_eventable_migration_generator.rb
30
+ - generators/acts_as_eventable_migration/templates/migration.rb
31
+ - init.rb
32
+ - install.rb
33
+ - lib/acts_as_eventable.rb
34
+ - lib/event.rb
35
+ - lib/event_type.rb
36
+ - netphase-acts_as_eventable.gemspec
37
+ - pkg/netphase-acts_as_eventable-0.0.0.gem
38
+ - pkg/netphase-acts_as_eventable-0.2.0.gem
39
+ - tasks/acts_as_eventable_tasks.rake
40
+ - test/acts_as_eventable_test.rb
41
+ - test/test_helper.rb
42
+ - uninstall.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/netphase/acts_as_eventable
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --charset=UTF-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.2.0
66
+ signing_key:
67
+ specification_version: 2
68
+ summary: This acts_as module makes it extremely simple to add events to a model that will be stored in a table for later querying or reporting on.
69
+ test_files:
70
+ - test/acts_as_eventable_test.rb
71
+ - test/test_helper.rb