director 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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==