director 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
19
+ .rspec
20
+ tags
21
+ readme.html
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in director.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+
6
+ watch(%r{^lib/director/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
8
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
9
+ end
10
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jacob Atzen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # Director
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'director'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install director
18
+
19
+ ## Usage
20
+
21
+ The first thing we need to do to start using the scheduler is to get our hands
22
+ on a schedule object:
23
+
24
+ schedule = Director::Schedule.new(event_repository)
25
+
26
+ Notice that you need to pass an `event_repository` object to the Schedule. See
27
+ the section on Event Repository for details.
28
+
29
+ Then we need to add a trigger to the schedule:
30
+
31
+ schedule.add_trigger(:invitation,
32
+ :event => :participant_join,
33
+ :action => :send_invite)
34
+
35
+ Then we need to tell the schedule which handlers are to be used for handling
36
+ which actions. Say we have an InviteSender module like this:
37
+
38
+ module InviteSender
39
+ extend self
40
+ def call(participant_id)
41
+ # Do something
42
+ end
43
+ end
44
+
45
+ Now let's tell the scheduler to use the InviteSender to handle send\_invite
46
+ actions:
47
+
48
+ schedule.add_handler(:send_invite, InviteSender)
49
+
50
+ And finally we'll notify the scheduler that a participant is to join the
51
+ schedule:
52
+
53
+ participant_id = 42
54
+ schedule.notify(participant_id, :participant_join)
55
+
56
+ Now we're ready to execute the schedule:
57
+
58
+ schedule.execute
59
+
60
+ Which in turn will call:
61
+
62
+ InviteSender.call(42)
63
+
64
+ ## Delayed triggers
65
+
66
+ Sometimes you want to schedule a trigger sometime after an event. This can be
67
+ done with:
68
+
69
+ schedule.add_trigger(:reminder,
70
+ :event => 'participant_join'
71
+ :offset => 3600,
72
+ :action => :send_reminder)
73
+
74
+ Where the offset is some number of seconds that must pass after the event
75
+ before the trigger is run.
76
+
77
+ ## Event Repository
78
+
79
+ To use the scheduler you need to provide an event repository object. This
80
+ object needs to respond to `notify(participant_id, event_name)` and
81
+ `search(criteria)`.
82
+
83
+ `participant_id` will most likely be an integer and `event_name` must be a
84
+ string.
85
+
86
+ For the search the criteria is an array of hashes that look like:
87
+
88
+ { :name => 'event_name', :includes => true }
89
+
90
+ This means that the event `event_name` must have happened to the participant.
91
+ The inverse is also possible:
92
+
93
+ { :name => 'event_name', :excludes => true }
94
+
95
+ Meaning that the event `event_name` must not have happened to the participant.
96
+ The search method must return an array of participant ids that match the
97
+ criteria.
98
+
99
+ Optionally the hash might include a `before` key if the event must have
100
+ happened before a given time.
101
+
102
+ { :name => 'event_name', :includes => true, :before => [time object] }
103
+
104
+ ## Config files
105
+
106
+ The schedule can be instantiated from a YAML file. The most simple
107
+ example is a schedule that basically does nothing but run an action when it's
108
+ told to:
109
+
110
+ triggers:
111
+ invitation:
112
+ event: participant_join
113
+ action: send_invite
114
+ offset: 1 day
115
+
116
+ This schedule can be loaded with:
117
+
118
+ schedule = Director::ConfigLoader.load_schedule(YAML::load_file('path/to/file'))
119
+
120
+ ## Contributing
121
+
122
+ 1. Fork it
123
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
124
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
125
+ 4. Push to the branch (`git push origin my-new-feature`)
126
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/director ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'director'
data/director.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/director/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Jacob Atzen"]
6
+ gem.email = ["jacob@incremental.dk"]
7
+ gem.description = %q{Scheduler}
8
+ gem.summary = %q{Scheduler gem}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\).reject{|f| f.match(/\Aschedule_demo/) }
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "director"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Director::VERSION
17
+
18
+ gem.add_runtime_dependency 'activesupport'
19
+
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec', "~> 2.8.0"
22
+ gem.add_development_dependency 'guard-rspec'
23
+ gem.add_development_dependency 'growl'
24
+ end
data/lib/director.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "director/config_loader"
2
+ require "director/event"
3
+ require "director/event_repository"
4
+ require "director/schedule"
5
+ require "director/trigger"
6
+ require "director/query"
7
+ require "director/version"
8
+
9
+ module Director
10
+ # Your code goes here...
11
+ end
@@ -0,0 +1,42 @@
1
+ require 'yaml'
2
+
3
+ module Director
4
+ class ConfigLoader
5
+ def self.load_schedule(schedule, filename)
6
+ new(schedule).load_config(YAML::load_file(filename))
7
+ end
8
+
9
+ def initialize(schedule)
10
+ @schedule = schedule
11
+ end
12
+
13
+ def load_config(config)
14
+ config['triggers'].each do |name, trigger_config|
15
+ if trigger_config['offset']
16
+ trigger_config['offset'] = parse_time(trigger_config['offset'])
17
+ end
18
+ @schedule.add_trigger(name, trigger_config)
19
+ end
20
+ end
21
+
22
+ def parse_time(time_string)
23
+ md = time_string.match(/([0-9]+)\s+([a-z]+)/)
24
+ count = md[1]
25
+ unit = md[2]
26
+ unit_to_i(unit) * count.to_i
27
+ end
28
+
29
+ def unit_to_i(unit)
30
+ case unit
31
+ when /month/
32
+ 30 * 24 * 60 * 60
33
+ when /day/
34
+ 24 * 60 * 60
35
+ when /hour/
36
+ 60 * 60
37
+ when /minute/
38
+ 60
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,5 @@
1
+ module Director
2
+ class Event
3
+ attr_accessor :name, :created_at
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ module Director
2
+ class EventRepository
3
+ def initialize
4
+ @participants = Hash.new{|hash, key| hash[key] = []}
5
+ end
6
+
7
+ def notify(participant_id, event_name, created_at = Time.now)
8
+ event = Event.new
9
+ event.name = event_name
10
+ event.created_at = created_at
11
+ @participants[participant_id] << event
12
+ end
13
+
14
+ def search(criteria)
15
+ resolver = Director::QueryResolver.new(criteria)
16
+ @participants.select do |participant, events|
17
+ resolver.match?(events)
18
+ end.map{|array| array[0]}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module Director
2
+ class Query
3
+ attr_reader :criteria
4
+ def initialize
5
+ @criteria = []
6
+ end
7
+
8
+ def includes_event(event, opts = {})
9
+ @criteria << { :name => event, :includes => true }.merge(opts)
10
+ end
11
+
12
+ def excludes_event(event, opts = {})
13
+ @criteria << { :name => event, :excludes => true }.merge(opts)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ module Director
2
+ class QueryResolver
3
+ attr_reader :criteria
4
+ def initialize(criteria)
5
+ @criteria = criteria
6
+ end
7
+
8
+ def match?(events)
9
+ criteria.all?{|criterion| passes?(events, criterion) }
10
+ end
11
+
12
+ def passes?(events, criterion)
13
+ event = find_event(events, criterion)
14
+ result = !event.nil? if(criterion[:includes])
15
+ result = event.nil? if(criterion[:excludes])
16
+ result
17
+ end
18
+
19
+ def find_event(events, criterion)
20
+ events.detect do |event|
21
+ if(criterion[:before])
22
+ event.name == criterion[:name] &&
23
+ event.created_at < criterion[:before]
24
+ else
25
+ event.name == criterion[:name]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ module Director
2
+ class Schedule
3
+ def initialize(event_repository)
4
+ @event_repository = event_repository
5
+ @triggers = []
6
+ @handlers = {}
7
+ end
8
+
9
+ def add_trigger(name, opts)
10
+ @triggers << Trigger.new(name, opts)
11
+ end
12
+
13
+ def add_handler(action, handler)
14
+ @handlers[action.to_s] = handler
15
+ end
16
+
17
+ def notify(participant_id, event_name)
18
+ @event_repository.notify(participant_id, event_name)
19
+ end
20
+
21
+ def execute
22
+ @triggers.each do |trigger|
23
+ trigger.run(@event_repository, @handlers[trigger.action])
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ require 'active_support/core_ext/object'
2
+
3
+ module Director
4
+ class Trigger
5
+ attr_reader :action
6
+ def initialize(name, opts)
7
+ opts.symbolize_keys!
8
+ @name, @event, @action = name, opts[:event].to_s, opts[:action].to_s
9
+ @offset = opts[:offset]
10
+ end
11
+
12
+ def run(event_repository, handler)
13
+ event_repository.search(query.criteria).each do |participant_id|
14
+ handler.call(participant_id)
15
+ event_repository.notify(participant_id, triggered_event_name)
16
+ end
17
+ end
18
+
19
+ def query
20
+ query = Query.new
21
+ opts = {}
22
+ if @offset
23
+ opts = { :before => Time.now - @offset }
24
+ end
25
+ query.includes_event(@event, opts)
26
+ query.excludes_event(triggered_event_name)
27
+ query
28
+ end
29
+
30
+ def triggered_event_name
31
+ "__handled_#{@name}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module Director
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,57 @@
1
+ require 'director/config_loader'
2
+
3
+ describe Director::ConfigLoader do
4
+ let(:schedule) { stub('Schedule') }
5
+
6
+ it "loads a simple config" do
7
+ schedule.should_receive(:add_trigger).with(
8
+ 'invitation',
9
+ {
10
+ 'event' => 'participant_join',
11
+ 'action' => 'send_invite'
12
+ }
13
+ )
14
+ Director::ConfigLoader.load_schedule(schedule, 'spec/fixtures/simple_config.yaml')
15
+ end
16
+
17
+ context "with a timed config" do
18
+ before(:each) { schedule.stub(:add_trigger) }
19
+
20
+ it "loads the invitation" do
21
+ schedule.should_receive(:add_trigger).with(
22
+ 'invitation',
23
+ {
24
+ 'event' => 'participant_join',
25
+ 'action' => 'send_invite',
26
+ 'offset' => 3600
27
+ }
28
+ )
29
+ Director::ConfigLoader.load_schedule(schedule, 'spec/fixtures/timed_config.yaml')
30
+ end
31
+
32
+ it "loads the reminder" do
33
+ schedule.should_receive(:add_trigger).with(
34
+ 'reminder',
35
+ {
36
+ 'event' => 'participant_join',
37
+ 'action' => 'send_invite',
38
+ 'offset' => 2 * 24 * 60 * 60
39
+ }
40
+ )
41
+ Director::ConfigLoader.load_schedule(schedule, 'spec/fixtures/timed_config.yaml')
42
+ end
43
+
44
+ it "loads the final" do
45
+ schedule.should_receive(:add_trigger).with(
46
+ 'final',
47
+ {
48
+ 'event' => 'participant_join',
49
+ 'action' => 'send_invite',
50
+ 'offset' => 2 * 30 * 24 * 60 * 60
51
+ }
52
+ )
53
+ Director::ConfigLoader.load_schedule(schedule, 'spec/fixtures/timed_config.yaml')
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,64 @@
1
+ require 'director/query'
2
+ require 'director/query_resolver'
3
+ require 'director/event_repository'
4
+
5
+ describe Director::EventRepository do
6
+ let(:repos) { Director::EventRepository.new }
7
+ let(:participant_1) { stub('Participant 1') }
8
+ let(:participant_2) { stub('Participant 2') }
9
+ let(:event1) { 'event1' }
10
+ let(:event2) { 'event2' }
11
+ let(:event_yesterday) { 'event_yesterday' }
12
+ let(:now) { Time.now }
13
+ let(:yesterday) { now - (24 * 60 * 60) }
14
+ let(:two_days_ago) { now - (2 * 24 * 60 * 60) }
15
+
16
+ context "two participants with event1 where one also has event2" do
17
+ before(:each) do
18
+ repos.notify(participant_1, event1)
19
+ repos.notify(participant_1, event2)
20
+ repos.notify(participant_2, event1)
21
+ end
22
+
23
+ it "finds both participants when asked for participants with the event" do
24
+ query = Director::Query.new
25
+ query.includes_event(event1)
26
+ repos.search(query.criteria).should have(2).items
27
+ end
28
+
29
+ it "finds only the participant without event2" do
30
+ query = Director::Query.new
31
+ query.includes_event(event1)
32
+ query.excludes_event(event2)
33
+ repos.search(query.criteria).should == [participant_2]
34
+ end
35
+ end
36
+
37
+ context "one participant with an event and one without" do
38
+ before(:each) do
39
+ repos.notify(participant_1, event2)
40
+ repos.notify(participant_2, event1)
41
+ end
42
+ it "finds only the participant with the event" do
43
+ query = Director::Query.new
44
+ query.includes_event(event1)
45
+ repos.search(query.criteria).should == [participant_2]
46
+ end
47
+ end
48
+
49
+ context "a participant with an event yesterday" do
50
+ before(:each) do
51
+ repos.notify(participant_1, event_yesterday, yesterday)
52
+ end
53
+ it "finds the events from yesterday" do
54
+ query = Director::Query.new
55
+ query.includes_event(event_yesterday, :before => now)
56
+ repos.search(query.criteria).should == [participant_1]
57
+ end
58
+ it "skips the event when asked for 2 days ago" do
59
+ query = Director::Query.new
60
+ query.includes_event(event_yesterday, :before => two_days_ago)
61
+ repos.search(query.criteria).should be_empty
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,4 @@
1
+ triggers:
2
+ invitation:
3
+ event: participant_join
4
+ action: send_invite
@@ -0,0 +1,13 @@
1
+ triggers:
2
+ invitation:
3
+ event: participant_join
4
+ action: send_invite
5
+ offset: 1 hour
6
+ reminder:
7
+ event: participant_join
8
+ action: send_invite
9
+ offset: 2 days
10
+ final:
11
+ event: participant_join
12
+ action: send_invite
13
+ offset: 2 months
@@ -0,0 +1,63 @@
1
+ require 'director/query'
2
+ require 'director/query_resolver'
3
+
4
+ describe Director::QueryResolver do
5
+ let(:query) { Director::Query.new }
6
+ let(:query_resolver) { Director::QueryResolver.new(query.criteria) }
7
+ let(:time) { Time.now }
8
+ let(:event1) { stub(:name => 'event1', :created_at => time) }
9
+ let(:event2) { stub(:name => 'event2', :created_at => time) }
10
+ let(:event3) { stub(:name => 'event3', :created_at => time) }
11
+
12
+ it "finds an included event" do
13
+ query.includes_event('event1')
14
+ query_resolver.should be_match([event1])
15
+ end
16
+
17
+ it "skips an excluded event" do
18
+ query.excludes_event('event1')
19
+ query_resolver.should_not be_match([event1])
20
+ end
21
+
22
+ context "a query with two includes" do
23
+ before(:each) do
24
+ query.includes_event('event1')
25
+ query.includes_event('event2')
26
+ end
27
+ it "should match a set of events" do
28
+ query_resolver.should be_match([event1, event3, event2])
29
+ end
30
+ it "does not match when an event is missing" do
31
+ query_resolver.should_not be_match([event1, event3])
32
+ end
33
+ end
34
+ context "a query with an includes with time before now" do
35
+ before(:each) do
36
+ query.includes_event('event1', :before => time - 1)
37
+ end
38
+ it "does not match when an event is missing" do
39
+ query_resolver.should_not be_match([event1])
40
+ end
41
+ end
42
+ context "a query with an includes with time after now" do
43
+ before(:each) do
44
+ query.includes_event('event1', :before => time + 1)
45
+ end
46
+ it "does not match when an event is missing" do
47
+ query_resolver.should be_match([event1])
48
+ end
49
+ end
50
+ context "a query with an include and an exclude" do
51
+ before(:each) do
52
+ query.includes_event('event1')
53
+ query.excludes_event('event2')
54
+ end
55
+ it "matches a set of events" do
56
+ query_resolver.should be_match([event1, event3])
57
+ end
58
+ it "does not match a set of events" do
59
+ query_resolver.should_not be_match([event1, event2, event3])
60
+ end
61
+ end
62
+ end
63
+
@@ -0,0 +1,13 @@
1
+ require 'director/query'
2
+
3
+ describe Director::Query do
4
+ subject { Director::Query.new }
5
+ before(:each) do
6
+ subject.includes_event('event1')
7
+ subject.includes_event('event2')
8
+ subject.excludes_event('event3')
9
+ end
10
+ its(:criteria) { should include({:name => 'event1', :includes => true}) }
11
+ its(:criteria) { should include({:name => 'event2', :includes => true}) }
12
+ its(:criteria) { should include({:name => 'event3', :excludes => true}) }
13
+ end
@@ -0,0 +1,88 @@
1
+ require 'director'
2
+
3
+ describe Director::Schedule do
4
+ subject { Director::Schedule.new(Director::EventRepository.new) }
5
+ let(:some_event) { 'some_event' }
6
+ let(:some_other_event) { 'some_other_event' }
7
+ context 'with one trigger' do
8
+ let(:handler) { stub }
9
+
10
+ before(:each) do
11
+ subject.add_trigger(:now, 'event' => 'some_event', :action => :some_action)
12
+ subject.add_handler(:some_action, handler)
13
+ end
14
+
15
+ it "can execute an action for two participants" do
16
+ handler.should_receive(:call).with(42)
17
+ handler.should_receive(:call).with(43)
18
+ subject.notify(42, some_event)
19
+ subject.notify(43, some_event)
20
+ subject.execute
21
+ end
22
+
23
+ it "only executes an action once" do
24
+ handler.should_receive(:call).with(42)
25
+ subject.notify(42, some_event)
26
+ subject.execute
27
+ subject.execute
28
+ end
29
+ end
30
+
31
+ context 'with two triggers' do
32
+ let(:handler1) { stub }
33
+ let(:handler2) { stub }
34
+
35
+ before(:each) do
36
+ subject.add_trigger(:now,
37
+ :event => 'some_event',
38
+ :action => :some_action)
39
+ subject.add_handler(:some_action, handler1)
40
+ subject.add_trigger(:again,
41
+ :event => 'some_other_event',
42
+ :action => :some_other_action)
43
+ subject.add_handler(:some_other_action, handler2)
44
+ end
45
+
46
+ it "can execute an action for two participants" do
47
+ handler1.should_receive(:call).with(42)
48
+ handler1.should_receive(:call).with(43)
49
+ subject.notify(42, some_event)
50
+ subject.notify(43, some_event)
51
+ subject.execute
52
+ end
53
+
54
+ it "only executes an action once" do
55
+ handler1.should_receive(:call).with(42)
56
+ subject.notify(42, some_event)
57
+ subject.execute
58
+ subject.execute
59
+ end
60
+
61
+ it "executes two actions" do
62
+ handler1.should_receive(:call).with(42)
63
+ handler2.should_receive(:call).with(42)
64
+ subject.notify(42, some_event)
65
+ subject.notify(42, some_other_event)
66
+ subject.execute
67
+ end
68
+ end
69
+
70
+ context 'with a timed trigger' do
71
+ let(:handler1) { stub }
72
+ let(:handler2) { stub }
73
+
74
+ before(:each) do
75
+ subject.add_trigger(:now,
76
+ :event => 'some_event',
77
+ :action => :some_action,
78
+ :offset => 60)
79
+ subject.add_handler(:some_action, handler1)
80
+ end
81
+
82
+ it "can execute an action for two participants" do
83
+ handler1.should_not_receive(:call).with(42)
84
+ subject.notify(42, some_event)
85
+ subject.execute
86
+ end
87
+ end
88
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: director
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jacob Atzen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &2151800720 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2151800720
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &2151800140 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2151800140
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &2151799160 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 2.8.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2151799160
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard-rspec
49
+ requirement: &2151798240 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2151798240
58
+ - !ruby/object:Gem::Dependency
59
+ name: growl
60
+ requirement: &2151797260 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2151797260
69
+ description: Scheduler
70
+ email:
71
+ - jacob@incremental.dk
72
+ executables:
73
+ - !binary |-
74
+ ZGlyZWN0b3I=
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - !binary |-
79
+ LmdpdGlnbm9yZQ==
80
+ - !binary |-
81
+ R2VtZmlsZQ==
82
+ - !binary |-
83
+ R3VhcmRmaWxl
84
+ - !binary |-
85
+ TElDRU5TRQ==
86
+ - !binary |-
87
+ UkVBRE1FLm1k
88
+ - !binary |-
89
+ UmFrZWZpbGU=
90
+ - !binary |-
91
+ YmluL2RpcmVjdG9y
92
+ - !binary |-
93
+ ZGlyZWN0b3IuZ2Vtc3BlYw==
94
+ - !binary |-
95
+ bGliL2RpcmVjdG9yLnJi
96
+ - !binary |-
97
+ bGliL2RpcmVjdG9yL2NvbmZpZ19sb2FkZXIucmI=
98
+ - !binary |-
99
+ bGliL2RpcmVjdG9yL2V2ZW50LnJi
100
+ - !binary |-
101
+ bGliL2RpcmVjdG9yL2V2ZW50X3JlcG9zaXRvcnkucmI=
102
+ - !binary |-
103
+ bGliL2RpcmVjdG9yL3F1ZXJ5LnJi
104
+ - !binary |-
105
+ bGliL2RpcmVjdG9yL3F1ZXJ5X3Jlc29sdmVyLnJi
106
+ - !binary |-
107
+ bGliL2RpcmVjdG9yL3NjaGVkdWxlLnJi
108
+ - !binary |-
109
+ bGliL2RpcmVjdG9yL3RyaWdnZXIucmI=
110
+ - !binary |-
111
+ bGliL2RpcmVjdG9yL3ZlcnNpb24ucmI=
112
+ - !binary |-
113
+ c3BlYy9jb25maWdfbG9hZGVyX3NwZWMucmI=
114
+ - !binary |-
115
+ c3BlYy9ldmVudF9yZXBvc2l0b3J5X3NwZWMucmI=
116
+ - !binary |-
117
+ c3BlYy9maXh0dXJlcy9zaW1wbGVfY29uZmlnLnlhbWw=
118
+ - !binary |-
119
+ c3BlYy9maXh0dXJlcy90aW1lZF9jb25maWcueWFtbA==
120
+ - !binary |-
121
+ c3BlYy9xdWVyeV9yZXNvbHZlcl9zcGVjLnJi
122
+ - !binary |-
123
+ c3BlYy9xdWVyeV9zcGVjLnJi
124
+ - !binary |-
125
+ c3BlYy9zY2hlZHVsZXJfc3BlYy5yYg==
126
+ homepage: ''
127
+ licenses: []
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ! '>='
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubyforge_project:
146
+ rubygems_version: 1.8.17
147
+ signing_key:
148
+ specification_version: 3
149
+ summary: Scheduler gem
150
+ test_files:
151
+ - !binary |-
152
+ c3BlYy9jb25maWdfbG9hZGVyX3NwZWMucmI=
153
+ - !binary |-
154
+ c3BlYy9ldmVudF9yZXBvc2l0b3J5X3NwZWMucmI=
155
+ - !binary |-
156
+ c3BlYy9maXh0dXJlcy9zaW1wbGVfY29uZmlnLnlhbWw=
157
+ - !binary |-
158
+ c3BlYy9maXh0dXJlcy90aW1lZF9jb25maWcueWFtbA==
159
+ - !binary |-
160
+ c3BlYy9xdWVyeV9yZXNvbHZlcl9zcGVjLnJi
161
+ - !binary |-
162
+ c3BlYy9xdWVyeV9zcGVjLnJi
163
+ - !binary |-
164
+ c3BlYy9zY2hlZHVsZXJfc3BlYy5yYg==