acts_as_event_owner 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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