acts_as_event_owner 1.0.0

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 (29) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +24 -0
  3. data/README.textile +205 -0
  4. data/Rakefile +28 -0
  5. data/acts_as_event_owner.gemspec +76 -0
  6. data/generators/acts_as_event_owner_migration/USAGE +6 -0
  7. data/generators/acts_as_event_owner_migration/acts_as_event_owner_migration_generator.rb +7 -0
  8. data/generators/acts_as_event_owner_migration/templates/acts_as_event_owner_migration.rb +38 -0
  9. data/lib/acts_as_event_owner/core.rb +38 -0
  10. data/lib/acts_as_event_owner/event_occurrence.rb +6 -0
  11. data/lib/acts_as_event_owner/event_specification.rb +152 -0
  12. data/lib/acts_as_event_owner/exception.rb +4 -0
  13. data/lib/acts_as_event_owner/railtie.rb +13 -0
  14. data/lib/acts_as_event_owner/version.rb +3 -0
  15. data/lib/acts_as_event_owner.rb +17 -0
  16. data/lib/generators/acts_as_event_owner/migration/migration_generator.rb +31 -0
  17. data/lib/generators/acts_as_event_owner/migration/templates/active_record/acts_as_event_owner_migration.rb +38 -0
  18. data/lib/generators/acts_as_event_owner_migration/USAGE +6 -0
  19. data/lib/generators/acts_as_event_owner_migration/acts_as_event_owner_migration_generator.rb +7 -0
  20. data/lib/generators/acts_as_event_owner_migration/templates/acts_as_event_owner_migration.rb +38 -0
  21. data/lib/tasks/acts_as_event_owner_tasks.rake +14 -0
  22. data/rails/init.rb +1 -0
  23. data/spec/acts_as_event_owner/core_spec.rb +62 -0
  24. data/spec/acts_as_event_owner/event_specification_spec.rb +365 -0
  25. data/spec/schema.rb +43 -0
  26. data/spec/spec_helper.rb +26 -0
  27. data/spec/support/model_builders.rb +13 -0
  28. data/spec/support/user.rb +3 -0
  29. metadata +106 -0
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ .idea
2
+ *.tmproj
3
+ *.db
4
+ *.swp
5
+ .DS_Store
6
+ *.gem
7
+ *.rvmrc
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ LICENSE
2
+
3
+ The MIT License
4
+
5
+ Copyright (c) 2010 Danny Burkes
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+
data/README.textile ADDED
@@ -0,0 +1,205 @@
1
+ h1. ActsAsEventOwner
2
+
3
+ ActsAsEventOwner is an ActiveRecord extension that adds calendar event management to any ActiveRecord model. Models that declare themselves as @acts_as_event_owner@ gain two @has_many@ associations- one for the event specifications, and one for the event occurrences.
4
+
5
+ ActsAsEventOwner supports recurring events, with roughly the same recurrence rule capabilities as Apple's iCal application. Under the hood, ActsAsEventOwner uses "ri_cal":http://github.com/rubyredrick/ri_cal to provide recurring event support.
6
+
7
+ h1. Installation
8
+
9
+ h2. Rails 2.3.x
10
+
11
+ ActsAsEventOwner is available both as a gem and as a plugin.
12
+
13
+ h3. Installing as a Rails 2.3.x plugin
14
+
15
+ To install as a plugin, just do
16
+
17
+ <pre>
18
+ script/plugin install git://github.com/dburkes/acts_as_event_owner_.git
19
+ </pre>
20
+
21
+ You'll also need to install the @ri_cal@ gem by the method of your choice (bundler, system gems, etc).
22
+
23
+ h3. Installing as a Rails 2.3.x gem
24
+
25
+ To install as a gem, use your preferred method of gem installation, _e.g._ Bundler, @config.gem@, etc.
26
+
27
+ h3. Rails 2.3.x post-installation steps
28
+
29
+ After installation, generate a migration to add ActsAsEventOwner tables to your database:
30
+
31
+ <pre>
32
+ script/generate acts_as_event_owner_migration
33
+ rake db:migrate
34
+ </pre>
35
+
36
+ If you want to use the acts_as_event_owner rake tasks, put the following in your Rakefile:
37
+
38
+ <pre>
39
+ if Gem.searcher.find('acts_as_event_owner')
40
+ Dir["#{Gem.searcher.find('acts_as_event_owner').full_gem_path}/**/tasks/*.rake"].each { |ext| load ext }
41
+ end
42
+ </pre>
43
+
44
+ h2. Rails 3
45
+
46
+ Just add it to your Gemfile, like so:
47
+
48
+ <pre>
49
+ gem 'acts_as_event_owner'
50
+ </pre>
51
+
52
+ Then do:
53
+
54
+ <pre>
55
+ bundle install
56
+ ~</pre>
57
+
58
+ h3. Rails 3 post-installation steps
59
+
60
+ After installation, generate a migration to add ActsAsEventOwner tables to your database:
61
+
62
+ <pre>
63
+ rails generate acts_as_event_owner:migration
64
+ rake db:migrate
65
+ </pre>
66
+
67
+ h1. Usage
68
+
69
+ <pre>
70
+ class User < ActiveRecord::Base
71
+ acts_as_event_owner
72
+ end
73
+
74
+ @user = User.create :name => 'Alvin Seville'
75
+ @user.event_specifications.create :description => 'acquire cheese balls',
76
+ :start_at => Date.today.to_time.utc,
77
+ :repeat => :daily,
78
+ :generate => false
79
+
80
+ @user.events # => []
81
+
82
+ @user.events.generate :from => Date.today.to_time.utc, :to => Date.today.to_time.utc + 1.week
83
+
84
+ # override the description on a per-generate basis
85
+ @user.events.generate :from => Date.today.to_time.utc, :to => Date.today.to_time.utc + 1.week,
86
+ :attributes => { :description => 'acquire cheese balls, like, right away!' }
87
+
88
+ @user.events # => (7 ActsAsEventOwner::EventOccurrence objects)
89
+ </pre>
90
+
91
+ h2. Adding custom fields
92
+
93
+ You can create your own migrations to add custom fields to the event objects- just make sure that you add the same fields to both the @event_specifications@ and @event_occurrences@ tables.
94
+
95
+ When you create an @EventSpecification@, set the value of your custom fields, then, later, when you call @generate@, the values of those fields in the @EventSpecification@ will be copied over to any generated @EventOcurrence@ records.
96
+
97
+ Just like you can do with the standard @:description@ attribute, you can override the default value of your custom fields with the @:attributes@ parameter when you call @generate@.
98
+
99
+ h2. Recurrence rules
100
+
101
+ ActsAsEventOwner supports recurrence rules roughly equivalent to those supported by Apple's iCal application. Examples are:
102
+
103
+ h3. One-time event
104
+
105
+ <pre>
106
+ EventSpecification.create :description => 'pick up laundry',
107
+ :start_at => Time.parse("4:00pm")
108
+ </pre>
109
+
110
+ h3. Every day
111
+
112
+ <pre>
113
+ EventSpecification.create :description => 'eat breakfast',
114
+ :start_at => Time.parse("7:30am"),
115
+ :repeat => :daily
116
+ </pre>
117
+
118
+ h3. Every three days
119
+
120
+ <pre>
121
+ EventSpecification.create :description => 'call mom',
122
+ :start_at => Time.parse("10:30am"),
123
+ :repeat => :daily,
124
+ :frequency => 3
125
+ </pre>
126
+
127
+ h3. On Monday, Wednesday, and Friday of each week
128
+
129
+ <pre>
130
+ EventSpecification.create :description => 'go to the gym',
131
+ :start_at => Time.parse("6:30am"),
132
+ :repeat => :weekly,
133
+ :on => [ :mo, :we, :fr ]
134
+ </pre>
135
+
136
+ h3. On Thursday, every other week
137
+
138
+ <pre>
139
+ EventSpecification.create :description => 'clean the bathroom',
140
+ :start_at => Time.parse("8:00pm"),
141
+ :repeat => :weekly,
142
+ :frequency => 2, :on => [ :th ]
143
+ </pre>
144
+
145
+ h3. On the 10th and 25th of each month
146
+
147
+ <pre>
148
+ EventSpecification.create :description => 'pick up paycheck',
149
+ :start_at => Time.parse("9:30am"),
150
+ :repeat => :monthly,
151
+ :on => [ 10, 25 ]
152
+ </pre>
153
+
154
+ h3. On the last Saturday of each month
155
+
156
+ <pre>
157
+ EventSpecification.create :description => 'run a marathon',
158
+ :start_at => Time.parse("6:30am"),
159
+ :repeat => :monthly,
160
+ :on_the => :last,
161
+ :target => [ :sa ]
162
+ </pre>
163
+
164
+ h3. On the last weekday of each month
165
+
166
+ <pre>
167
+ EventSpecification.create :description => 'wine tasting',
168
+ :start_at => Time.parse("6:30pm"),
169
+ :repeat => :monthly,
170
+ :on_the => :last,
171
+ :target => :wkday
172
+ </pre>
173
+
174
+ h3. Every April 15th
175
+
176
+ <pre>
177
+ EventSpecification.create :description => 'pay taxes',
178
+ :start_at => Time.parse("4/15/2010 5:00pm"),
179
+ :repeat => :yearly
180
+ </pre>
181
+
182
+ h3. On the second Thursday in May, every other year, until Dec 31, 2012
183
+
184
+ <pre>
185
+ EventSpecification.create :description => 'freak out',
186
+ :start_at => Time.now.utc,
187
+ :repeat => :yearly,
188
+ :frequency => 2,
189
+ :on => [ 5 ],
190
+ :on_the => :second,
191
+ :target => [ :th ],
192
+ :until => Time.parse("12/31/2012")
193
+ </pre>
194
+
195
+ h2. Using the Rake task
196
+
197
+ A rake task is included to generate occurrences of recurring events. For example, you might run this out of a cron job each day to generate any recurring events for the next 30 days, or whatever.
198
+
199
+ <pre>
200
+ rake acts_as_event_owner:generate_events FROM=9/1/2010 TO=10/1/2010
201
+ </pre>
202
+
203
+ h1. Credits
204
+
205
+ ActsAsEventOwner was developed for Josh Pigford and "Sabotage Media LLC":http://madebysabotage.com, for use in their excellent "Critterly":http://critterly.com product. Big thanks to Josh and Sabotage for agreeing to make ActsAsEventOwner available by open source!
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+
5
+ desc "Run all specs"
6
+ Spec::Rake::SpecTask.new('specs') do |t|
7
+ t.libs << 'lib'
8
+ t.spec_files = FileList['spec/**/*.rb']
9
+ end
10
+
11
+ task :default => [:specs]
12
+
13
+ begin
14
+ require 'jeweler'
15
+ require 'lib/acts_as_event_owner'
16
+ Jeweler::Tasks.new do |gemspec|
17
+ gemspec.name = "acts_as_event_owner"
18
+ gemspec.version = ActsAsEventOwner::VERSION
19
+ gemspec.summary = "Simple calendar events for any ActiveRecord model"
20
+ gemspec.email = "dburkes@netable.com"
21
+ gemspec.homepage = "http://github.com/dburkes/acts_as_event_owner"
22
+ gemspec.description = "Simple calendar events for any ActiveRecord model"
23
+ gemspec.authors = ["Danny Burkes"]
24
+ gemspec.add_dependency('ri_cal')
25
+ end
26
+ rescue LoadError
27
+ puts "Jeweler not available. Install it with: gem install jeweler"
28
+ end
@@ -0,0 +1,76 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{acts_as_event_owner}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Danny Burkes"]
12
+ s.date = %q{2010-10-17}
13
+ s.description = %q{Simple calendar events for any ActiveRecord model}
14
+ s.email = %q{dburkes@netable.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README.textile",
23
+ "Rakefile",
24
+ "acts_as_event_owner.gemspec",
25
+ "generators/acts_as_event_owner_migration/USAGE",
26
+ "generators/acts_as_event_owner_migration/acts_as_event_owner_migration_generator.rb",
27
+ "generators/acts_as_event_owner_migration/templates/acts_as_event_owner_migration.rb",
28
+ "lib/acts_as_event_owner.rb",
29
+ "lib/acts_as_event_owner/core.rb",
30
+ "lib/acts_as_event_owner/event_occurrence.rb",
31
+ "lib/acts_as_event_owner/event_specification.rb",
32
+ "lib/acts_as_event_owner/exception.rb",
33
+ "lib/acts_as_event_owner/railtie.rb",
34
+ "lib/acts_as_event_owner/version.rb",
35
+ "lib/generators/acts_as_event_owner/migration/migration_generator.rb",
36
+ "lib/generators/acts_as_event_owner/migration/templates/active_record/acts_as_event_owner_migration.rb",
37
+ "lib/generators/acts_as_event_owner_migration/USAGE",
38
+ "lib/generators/acts_as_event_owner_migration/acts_as_event_owner_migration_generator.rb",
39
+ "lib/generators/acts_as_event_owner_migration/templates/acts_as_event_owner_migration.rb",
40
+ "lib/tasks/acts_as_event_owner_tasks.rake",
41
+ "rails/init.rb",
42
+ "spec/acts_as_event_owner/core_spec.rb",
43
+ "spec/acts_as_event_owner/event_specification_spec.rb",
44
+ "spec/schema.rb",
45
+ "spec/spec_helper.rb",
46
+ "spec/support/model_builders.rb",
47
+ "spec/support/user.rb"
48
+ ]
49
+ s.homepage = %q{http://github.com/dburkes/acts_as_event_owner}
50
+ s.rdoc_options = ["--charset=UTF-8"]
51
+ s.require_paths = ["lib"]
52
+ s.rubygems_version = %q{1.3.6}
53
+ s.summary = %q{Simple calendar events for any ActiveRecord model}
54
+ s.test_files = [
55
+ "spec/acts_as_event_owner/core_spec.rb",
56
+ "spec/acts_as_event_owner/event_specification_spec.rb",
57
+ "spec/schema.rb",
58
+ "spec/spec_helper.rb",
59
+ "spec/support/model_builders.rb",
60
+ "spec/support/user.rb"
61
+ ]
62
+
63
+ if s.respond_to? :specification_version then
64
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
+ s.specification_version = 3
66
+
67
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
68
+ s.add_runtime_dependency(%q<ri_cal>, [">= 0"])
69
+ else
70
+ s.add_dependency(%q<ri_cal>, [">= 0"])
71
+ end
72
+ else
73
+ s.add_dependency(%q<ri_cal>, [">= 0"])
74
+ end
75
+ end
76
+
@@ -0,0 +1,6 @@
1
+ Usage:
2
+
3
+ script/generate acts_as_event_owner_migration
4
+
5
+ This will create a migration that will add the acts_as_event_owner tables to your database
6
+
@@ -0,0 +1,7 @@
1
+ class ActsAsEventOwnerMigrationGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template 'acts_as_event_owner_migration.rb', File.join('db', 'migrate'), :migration_file_name => "acts_as_event_owner_migration"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ class ActsAsEventOwnerMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :event_specifications do |t|
4
+ t.integer :owner_id
5
+ t.string :owner_type
6
+ t.string :description
7
+ t.datetime :start_at
8
+ t.datetime :end_at
9
+ t.string :repeat # daily, weekly, monthly, yearly
10
+ t.integer :frequency, :default => 1 # every 'n' days, weeks, months, or years
11
+ t.string :on # su, mo, tu, we, th, fr, sa, 1-31, jan-dec
12
+ t.string :on_the # first, second, third, fourth, last
13
+ t.string :target # su, mo, tu, we, th, fr, sa, day, wkday, wkend
14
+ t.datetime :until
15
+ t.timestamps
16
+ end
17
+
18
+ add_index :event_specifications, [:owner_id, :owner_type]
19
+
20
+ create_table :event_occurrences do |t|
21
+ t.integer :owner_id
22
+ t.string :owner_type
23
+ t.integer :event_specification_id
24
+ t.datetime :start_at
25
+ t.datetime :end_at
26
+ t.string :description
27
+ t.timestamps
28
+ end
29
+
30
+ add_index :event_occurrences, [:owner_id, :owner_type]
31
+ add_index :event_occurrences, :event_specification_id
32
+ end
33
+
34
+ def self.down
35
+ drop_table :event_specifications
36
+ drop_table :event_occurrences
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ module ActsAsEventOwner
2
+ module Core
3
+ def self.included(base)
4
+ base.send :extend, ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def acts_as_event_owner options = {}
9
+ include InstanceMethods
10
+
11
+ class_eval do
12
+ has_many :event_specifications, :class_name => ActsAsEventOwner::EventSpecification.name, :as => :owner, :dependent => :destroy
13
+ has_many :events, :class_name => ActsAsEventOwner::EventOccurrence.name, :as => :owner, :readonly => true do
14
+ def generate(options={})
15
+ proxy_owner.event_specifications.find(:all, :conditions => "until IS NULL OR until >= '#{Time.now.utc.to_s(:db)}'").each {|spec| spec.generate_events(options)}
16
+ self.reload
17
+ end
18
+
19
+ def <<(obj)
20
+ raise ActsAsEventOwner::Exception.new("Do not add events directly- add event specifications, then call events.generate")
21
+ end
22
+
23
+ def build(attributes={})
24
+ raise ActsAsEventOwner::Exception.new("Do not build events directly- build event specifications, then call events.generate")
25
+ end
26
+
27
+ def create(attributes={})
28
+ raise ActsAsEventOwner::Exception.new("Do not create events directly- build event specifications, then call events.generate")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ module InstanceMethods
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ module ActsAsEventOwner
2
+ class EventOccurrence < ::ActiveRecord::Base
3
+ belongs_to :owner, :polymorphic => true
4
+ belongs_to :event_specification
5
+ end
6
+ end
@@ -0,0 +1,152 @@
1
+ require 'ri_cal'
2
+
3
+ module ActsAsEventOwner
4
+ class EventSpecification < ::ActiveRecord::Base
5
+ belongs_to :owner, :polymorphic => true
6
+ has_many :event_occurrences, :dependent => :destroy
7
+
8
+ serialize :repeat
9
+ serialize :on
10
+ serialize :on_the
11
+ serialize :target
12
+
13
+ ON_THE = { :first => '1', :second => '2', :third => '3', :fourth => '4', :last => '-1' }
14
+ BYDAYS = { :day => 'SU,MO,TU,WE,TH,FR,SA', :wkday => 'MO,TU,WE,TH,FR', :wkend => 'SU,SA'}
15
+
16
+ before_validation :set_defaults
17
+ validates_presence_of :description
18
+ validates_inclusion_of :repeat, :in => [:daily,:weekly,:monthly,:yearly], :allow_nil => true
19
+ validates_inclusion_of :on_the, :in => ON_THE.keys, :allow_nil => true
20
+ validates_numericality_of :frequency, :allow_nil => true
21
+ validates_presence_of :start_at
22
+ validate :validate_recurrence_rules
23
+
24
+ attr_accessor :generate
25
+ after_create :auto_generate_events
26
+
27
+ def validate_recurrence_rules
28
+ case self.repeat
29
+ when :daily
30
+ [:on, :on_the, :target].each {|v| errors.add(v, :present) if self.send(v)}
31
+
32
+ when :weekly
33
+ errors.add(:on, "must be an array") if self.on.present? && !self.on.is_a?(Array)
34
+ [:on_the, :target].each {|v| errors.add(v, :present) if self.send(v)}
35
+
36
+ when :monthly
37
+ if self.on_the
38
+ errors.add(:target, "must be an array, :day, :wkday, or :wkend") if self.target.nil? || !(self.target.is_a?(Array) || BYDAYS.keys.include?(self.target))
39
+ errors.add(:on, :present) if self.on.present?
40
+ elsif self.on
41
+ errors.add(:on, "must be an array") if !self.on.is_a?(Array)
42
+ [:on_the, :target].each {|v| errors.add(v, :present) if self.send(v)}
43
+ end
44
+
45
+ when :yearly
46
+ if self.on_the
47
+ errors.add(:on, "must be an array") if !self.on.present? || !self.on.is_a?(Array)
48
+ errors.add(:target, "must be an array, :day, :wkday, or :wkend") if self.target.nil? || !(self.target.is_a?(Array) || BYDAYS.keys.include?(self.target))
49
+ elsif self.on
50
+ errors.add(:on, "must be an array") if !self.on.present? || !self.on.is_a?(Array)
51
+ else
52
+ errors.add(:on, :present)
53
+ end
54
+ end
55
+ end
56
+
57
+ def to_rrule
58
+ return nil if !self.valid? || self.repeat.nil?
59
+
60
+ components = []
61
+
62
+ case self.repeat
63
+ when :daily
64
+
65
+ when :weekly
66
+ components << "BYDAY=#{self.on.join(',').upcase}" if self.on
67
+
68
+ when :monthly
69
+ if self.on_the
70
+ components << "BYSETPOS=#{ON_THE[self.on_the]}"
71
+ components << "BYDAY=#{byday}"
72
+ end
73
+ components << "BYMONTHDAY=#{self.on.join(',').upcase}" if self.on
74
+
75
+ when :yearly
76
+ components << "BYMONTH=#{self.on.join(',').upcase}" if self.on
77
+ components << "BYSETPOS=#{ON_THE[self.on_the]};BYDAY=#{byday}" if self.on_the
78
+ end
79
+
80
+ components.unshift "INTERVAL=#{self.frequency}" if self.frequency
81
+ components.unshift "FREQ=#{self.repeat.to_s.upcase}"
82
+ components << "UNTIL=#{self.until.strftime("%Y%m%dT%H%M%SZ")}" if self.until
83
+ components.join(';')
84
+ end
85
+
86
+ def generate_events options={}
87
+ raise ActsAsEventOwner::Exception.new("Invalid Event Specification") if !valid?
88
+
89
+ opts = options.clone
90
+ opts[:from] ||= self.start_at
91
+ opts[:to] ||= (opts[:from] + 30.days) if opts[:from]
92
+ opts[:from] -= 1.second
93
+ opts[:to] -= 1.second
94
+ opts[:from] = opts[:to] = nil if opts[:count]
95
+ attribute_overrides = opts[:attributes] || {}
96
+
97
+ # puts "generate #{self.attributes.inspect} from #{opts[:from]} to #{opts[:to]}, extended_attributes = #{extended_attributes.inspect}"
98
+
99
+ cal = RiCal.Calendar do |cal|
100
+ cal.event do |event|
101
+ event.description self.description
102
+ event.dtstart(self.start_at) if self.start_at
103
+ event.dtend(self.end_at) if self.end_at
104
+ event.rrule = self.to_rrule if self.to_rrule
105
+ end
106
+ end
107
+ event = cal.events.first
108
+ occurrences = event.occurrences(:starting => opts[:from], :before => opts[:to], :count => opts[:count])
109
+ occurrences.collect do |occurrence|
110
+ @@OCCURRENCE_COLUMNS ||= (EventOccurrence.columns.collect(&:name) - EXCLUDED_COLUMNS)
111
+ @@SPECIFICATION_COLUMNS ||= (EventSpecification.columns.collect(&:name) - EXCLUDED_COLUMNS)
112
+ additional_columns = (@@SPECIFICATION_COLUMNS).inject({}) do |additional, column|
113
+ additional[column] = self.attributes[column] if @@OCCURRENCE_COLUMNS.include?(column)
114
+ additional
115
+ end
116
+
117
+ EventOccurrence.find_or_create_by_owner_id_and_owner_type_and_event_specification_id_and_start_at_and_end_at({
118
+ :owner_id => self.owner_id, :owner_type => self.owner_type, :event_specification_id => self.id,
119
+ :start_at => occurrence.start_time, :end_at => occurrence.finish_time}.merge(additional_columns).merge(attribute_overrides.stringify_keys))
120
+ end
121
+ end
122
+
123
+ def self.generate_events options={}
124
+ self.all(:conditions => "until IS NULL OR until >= '#{Time.now.utc.to_s(:db)}'").each {|spec|
125
+ spec.generate_events(options)
126
+ }
127
+ end
128
+
129
+ def repeat
130
+ self.attributes["repeat"].try(:to_sym)
131
+ end
132
+
133
+ protected
134
+
135
+ def set_defaults
136
+ self.start_at ||= Time.now.utc
137
+ self.end_at ||= self.start_at + 1.hour
138
+ self.generate = { :from => self.start_at, :to => self.start_at + 30.days } if self.generate.nil?
139
+ end
140
+
141
+ def byday
142
+ self.target.is_a?(Array) ? self.target.join(',').upcase : BYDAYS[self.target]
143
+ end
144
+
145
+ def auto_generate_events
146
+ self.generate_events(self.generate) if self.generate
147
+ self.generate = nil
148
+ end
149
+
150
+ EXCLUDED_COLUMNS = [ "id", "owner_id", "owner_type", "description", "start_at", "end_at", "repeat", "frequency", "on", "on_the", "target", "until", "created_at", "updated_at" ]
151
+ end
152
+ end
@@ -0,0 +1,4 @@
1
+ module ActsAsEventOwner
2
+ class Exception < ::Exception
3
+ end
4
+ end
@@ -0,0 +1,13 @@
1
+ require 'acts_as_event_owner'
2
+
3
+ module ActsAsEventOwner
4
+ if defined? Rails::Railtie
5
+ require 'rails'
6
+ class Railtie < Rails::Railtie
7
+ rake_tasks do
8
+ puts Dir.pwd
9
+ load "tasks/acts_as_event_owner_tasks.rake"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module ActsAsEventOwner
2
+ VERSION = "1.0.0" unless defined? ActsAsEventOwner::VERSION
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+
6
+ require 'acts_as_event_owner/core'
7
+ require 'acts_as_event_owner/event_specification'
8
+ require 'acts_as_event_owner/event_occurrence'
9
+ require 'acts_as_event_owner/exception'
10
+ require 'acts_as_event_owner/railtie'
11
+ require 'acts_as_event_owner/version'
12
+
13
+ $LOAD_PATH.shift
14
+
15
+ if defined?(ActiveRecord::Base)
16
+ ActiveRecord::Base.send :include, ActsAsEventOwner::Core
17
+ end
@@ -0,0 +1,31 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module ActsAsEventOwner
4
+ class MigrationGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+
7
+ desc "Generates migration for EventSpecification and EventOcurrence models"
8
+
9
+ def self.orm
10
+ Rails::Generators.options[:rails][:orm]
11
+ end
12
+
13
+ def self.source_root
14
+ File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)) )
15
+ end
16
+
17
+ def self.orm_has_migration?
18
+ [:active_record].include? orm
19
+ end
20
+
21
+ def self.next_migration_number(path)
22
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
23
+ end
24
+
25
+ def create_migration_file
26
+ if self.class.orm_has_migration?
27
+ migration_template 'acts_as_event_owner_migration.rb', File.join('db', 'migrate', 'acts_as_event_owner_migration')
28
+ end
29
+ end
30
+ end
31
+ end