pushpop 0.1.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.
- data/.gitignore +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +72 -0
- data/LICENSE +21 -0
- data/Procfile +1 -0
- data/README.md +582 -0
- data/Rakefile +48 -0
- data/jobs/example_job.rb +20 -0
- data/lib/plugins/keen.rb +78 -0
- data/lib/plugins/sendgrid.rb +94 -0
- data/lib/plugins/twilio.rb +52 -0
- data/lib/pushpop.rb +55 -0
- data/lib/pushpop/job.rb +95 -0
- data/lib/pushpop/step.rb +50 -0
- data/lib/pushpop/version.rb +3 -0
- data/pushpop.gemspec +22 -0
- data/spec/jobs/simple_job.rb +9 -0
- data/spec/plugins/keen_spec.rb +88 -0
- data/spec/plugins/sendgrid_spec.rb +66 -0
- data/spec/pushpop/job_spec.rb +147 -0
- data/spec/pushpop/step_spec.rb +78 -0
- data/spec/pushpop_spec.rb +24 -0
- data/spec/simple_job_spec.rb +9 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/templates/spec.html.erb +6 -0
- data/templates/first_template.html.erb +1 -0
- metadata +113 -0
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
$stdout.sync = true
|
2
|
+
|
3
|
+
$: << File.join(File.dirname(__FILE__), './lib')
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
desc 'Run Rspec unit tests'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
t.pattern = 'spec/**/*_spec.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
task default: :spec
|
13
|
+
rescue LoadError
|
14
|
+
end
|
15
|
+
|
16
|
+
def require_jobfiles(args)
|
17
|
+
require 'pushpop'
|
18
|
+
if jobfile = args[:jobfile]
|
19
|
+
load "#{File.dirname(__FILE__)}/#{jobfile}"
|
20
|
+
else
|
21
|
+
Dir.glob("#{File.dirname(__FILE__)}/jobs/**/*.rb").each { |file|
|
22
|
+
require file
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
namespace :jobs do
|
28
|
+
desc 'Describe jobs'
|
29
|
+
task :describe, :jobfile do |_, args|
|
30
|
+
require_jobfiles(args)
|
31
|
+
Pushpop.jobs.each do |job|
|
32
|
+
puts job.name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Run each job once'
|
37
|
+
task :run_once, :jobfile do |_, args|
|
38
|
+
require_jobfiles(args)
|
39
|
+
Pushpop.run
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'Run jobs ongoing'
|
43
|
+
task :run, :jobfile do |_, args|
|
44
|
+
require_jobfiles(args)
|
45
|
+
Pushpop.schedule
|
46
|
+
Clockwork.manager.run
|
47
|
+
end
|
48
|
+
end
|
data/jobs/example_job.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pushpop'
|
2
|
+
|
3
|
+
job 'Simple Math' do
|
4
|
+
|
5
|
+
every 1.minutes
|
6
|
+
|
7
|
+
step 'return 10' do 10 end
|
8
|
+
|
9
|
+
step 'increase by 20' do |response|
|
10
|
+
20 + response
|
11
|
+
end
|
12
|
+
|
13
|
+
step 'print out via template' do |response|
|
14
|
+
html = template 'first_template.html.erb', response
|
15
|
+
puts 'Hey Pushpop, let\'s do a math!'
|
16
|
+
puts html
|
17
|
+
html
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/plugins/keen.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'keen'
|
2
|
+
|
3
|
+
module Pushpop
|
4
|
+
|
5
|
+
class Keen < Step
|
6
|
+
|
7
|
+
PLUGIN_NAME = 'keen'
|
8
|
+
|
9
|
+
attr_accessor :_event_collection
|
10
|
+
attr_accessor :_analysis_type
|
11
|
+
attr_accessor :_timeframe
|
12
|
+
attr_accessor :_target_property
|
13
|
+
attr_accessor :_group_by
|
14
|
+
attr_accessor :_interval
|
15
|
+
attr_accessor :_filters
|
16
|
+
attr_accessor :_steps
|
17
|
+
attr_accessor :_analyses
|
18
|
+
|
19
|
+
def run(last_response=nil, step_responses=nil)
|
20
|
+
self.configure(last_response, step_responses)
|
21
|
+
::Keen.send(self._analysis_type, self._event_collection, self.to_analysis_options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def configure(last_response=nil, step_responses=nil)
|
25
|
+
self.instance_exec(last_response, step_responses, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_analysis_options
|
29
|
+
{ timeframe: self._timeframe,
|
30
|
+
target_property: self._target_property,
|
31
|
+
group_by: self._group_by,
|
32
|
+
interval: self._interval,
|
33
|
+
filters: self._filters,
|
34
|
+
analyses: self._analyses,
|
35
|
+
steps: self._steps
|
36
|
+
}.delete_if { |_, v| v.nil? }
|
37
|
+
end
|
38
|
+
|
39
|
+
def event_collection(event_collection)
|
40
|
+
self._event_collection = event_collection
|
41
|
+
end
|
42
|
+
|
43
|
+
def analysis_type(analysis_type)
|
44
|
+
self._analysis_type = analysis_type
|
45
|
+
end
|
46
|
+
|
47
|
+
def timeframe(timeframe)
|
48
|
+
self._timeframe = timeframe
|
49
|
+
end
|
50
|
+
|
51
|
+
def target_property(target_property)
|
52
|
+
self._target_property = target_property
|
53
|
+
end
|
54
|
+
|
55
|
+
def group_by(group_by)
|
56
|
+
self._group_by = group_by
|
57
|
+
end
|
58
|
+
|
59
|
+
def interval(interval)
|
60
|
+
self._interval = interval
|
61
|
+
end
|
62
|
+
|
63
|
+
def filters(filters)
|
64
|
+
self._filters = filters
|
65
|
+
end
|
66
|
+
|
67
|
+
def steps(steps)
|
68
|
+
self._steps = steps
|
69
|
+
end
|
70
|
+
|
71
|
+
def analyses(analyses)
|
72
|
+
self._analyses = analyses
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
Pushpop::Job.register_plugin(Keen::PLUGIN_NAME, Keen)
|
78
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'mail'
|
2
|
+
|
3
|
+
Mail.defaults do
|
4
|
+
delivery_method :smtp, { address: 'smtp.sendgrid.net',
|
5
|
+
port: 587,
|
6
|
+
domain: ENV['SENDGRID_DOMAIN'],
|
7
|
+
user_name: ENV['SENDGRID_USERNAME'],
|
8
|
+
password: ENV['SENDGRID_PASSWORD'],
|
9
|
+
authentication: 'plain',
|
10
|
+
enable_starttls_auto: true }
|
11
|
+
end
|
12
|
+
|
13
|
+
module Pushpop
|
14
|
+
|
15
|
+
class Sendgrid < Step
|
16
|
+
|
17
|
+
PLUGIN_NAME = 'sendgrid'
|
18
|
+
|
19
|
+
attr_accessor :_from
|
20
|
+
attr_accessor :_to
|
21
|
+
attr_accessor :_subject
|
22
|
+
attr_accessor :_body
|
23
|
+
attr_accessor :_preview
|
24
|
+
|
25
|
+
def run(last_response=nil, step_responses=nil)
|
26
|
+
|
27
|
+
self.configure(last_response, step_responses)
|
28
|
+
|
29
|
+
# print the message if its just a preview
|
30
|
+
return print_preview if self._preview
|
31
|
+
|
32
|
+
_to = self._to
|
33
|
+
_from = self._from
|
34
|
+
_subject = self._subject
|
35
|
+
_body = self._body
|
36
|
+
|
37
|
+
Mail.deliver do
|
38
|
+
to _to
|
39
|
+
from _from
|
40
|
+
subject _subject
|
41
|
+
text_part do
|
42
|
+
body _body
|
43
|
+
end
|
44
|
+
html_part do
|
45
|
+
content_type 'text/html; charset=UTF-8'
|
46
|
+
body _body
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def configure(last_response=nil, step_responses=nil)
|
52
|
+
self.instance_exec(last_response, step_responses, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def from(from)
|
56
|
+
self._from = from
|
57
|
+
end
|
58
|
+
|
59
|
+
def to(to)
|
60
|
+
self._to = to
|
61
|
+
end
|
62
|
+
|
63
|
+
def subject(subject)
|
64
|
+
self._subject = subject
|
65
|
+
end
|
66
|
+
|
67
|
+
def preview(preview)
|
68
|
+
self._preview = preview
|
69
|
+
end
|
70
|
+
|
71
|
+
def body(*args)
|
72
|
+
if args.length == 1
|
73
|
+
self._body = args.first
|
74
|
+
else
|
75
|
+
self._body = template *args
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def print_preview
|
82
|
+
puts <<MESSAGE
|
83
|
+
To: #{self._to}
|
84
|
+
From: #{self._from}
|
85
|
+
Subject: #{self._subject}
|
86
|
+
|
87
|
+
#{self._body}
|
88
|
+
MESSAGE
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
Pushpop::Job.register_plugin(Sendgrid::PLUGIN_NAME, Sendgrid)
|
94
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'twilio-ruby'
|
2
|
+
|
3
|
+
TWILIO_SID = ENV['TWILIO_SID']
|
4
|
+
TWILIO_AUTH_TOKEN = ENV['TWILIO_AUTH_TOKEN']
|
5
|
+
TWILIO_FROM = ENV['TWILIO_FROM']
|
6
|
+
|
7
|
+
module Pushpop
|
8
|
+
|
9
|
+
class Twilio < Step
|
10
|
+
|
11
|
+
PLUGIN_NAME = 'twilio'
|
12
|
+
|
13
|
+
attr_accessor :_from
|
14
|
+
attr_accessor :_to
|
15
|
+
attr_accessor :_body
|
16
|
+
|
17
|
+
def run(last_response=nil, step_responses=nil)
|
18
|
+
|
19
|
+
self.configure(last_response, step_responses)
|
20
|
+
|
21
|
+
_to = self._to
|
22
|
+
_from = self._from || TWILIO_FROM
|
23
|
+
_body = self._body
|
24
|
+
|
25
|
+
client = ::Twilio::REST::Client.new(TWILIO_SID, TWILIO_AUTH_TOKEN)
|
26
|
+
|
27
|
+
client.account.messages.create(
|
28
|
+
from: _from,
|
29
|
+
to: _to,
|
30
|
+
body: _body )
|
31
|
+
end
|
32
|
+
|
33
|
+
def configure(last_response=nil, step_responses=nil)
|
34
|
+
self.instance_exec(last_response, step_responses, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def from(from)
|
38
|
+
self._from = from
|
39
|
+
end
|
40
|
+
|
41
|
+
def to(to)
|
42
|
+
self._to = to
|
43
|
+
end
|
44
|
+
|
45
|
+
def body(body)
|
46
|
+
self._body = body
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
Pushpop::Job.register_plugin(Twilio::PLUGIN_NAME, Twilio)
|
52
|
+
end
|
data/lib/pushpop.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'clockwork'
|
3
|
+
require 'pushpop/version'
|
4
|
+
require 'pushpop/job'
|
5
|
+
require 'pushpop/step'
|
6
|
+
|
7
|
+
# require all plugins
|
8
|
+
Dir["#{File.expand_path('../plugins/*', __FILE__)}.rb"].each { |file|
|
9
|
+
require file
|
10
|
+
}
|
11
|
+
|
12
|
+
module Pushpop
|
13
|
+
class << self
|
14
|
+
cattr_accessor :logger
|
15
|
+
cattr_accessor :jobs
|
16
|
+
|
17
|
+
# for jobs and steps
|
18
|
+
def random_name
|
19
|
+
(0...8).map { (65 + rand(26)).chr }.join
|
20
|
+
end
|
21
|
+
|
22
|
+
self.jobs = []
|
23
|
+
|
24
|
+
def add_job(name=nil, &block)
|
25
|
+
self.jobs.push(Job.new(name, &block))
|
26
|
+
self.jobs.last
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
self.jobs.map &:run
|
31
|
+
end
|
32
|
+
|
33
|
+
def schedule
|
34
|
+
self.jobs.map &:schedule
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# add into main
|
40
|
+
def job(name=nil, &block)
|
41
|
+
Pushpop.add_job(name, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
Pushpop.logger = lambda {
|
45
|
+
logger = Logger.new($stdout)
|
46
|
+
if ENV['DEBUG']
|
47
|
+
logger.level = Logger::DEBUG
|
48
|
+
elsif ENV['RACK_ENV'] == 'test'
|
49
|
+
logger.level = Logger::FATAL
|
50
|
+
else
|
51
|
+
logger.level = Logger::INFO
|
52
|
+
end
|
53
|
+
logger
|
54
|
+
}.call
|
55
|
+
|
data/lib/pushpop/job.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module Pushpop
|
2
|
+
|
3
|
+
class Job
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
cattr_accessor :plugins
|
8
|
+
self.plugins = {}
|
9
|
+
|
10
|
+
def register_plugin(name, klass)
|
11
|
+
self.plugins ||= {}
|
12
|
+
self.plugins[name.to_s] = klass
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :name
|
18
|
+
attr_accessor :every_duration
|
19
|
+
attr_accessor :every_options
|
20
|
+
attr_accessor :steps
|
21
|
+
|
22
|
+
def initialize(name=nil, &block)
|
23
|
+
self.name = name || Pushpop.random_name
|
24
|
+
self.steps = []
|
25
|
+
self.every_options = {}
|
26
|
+
self.instance_eval(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def every(duration, options={})
|
30
|
+
self.every_duration = duration
|
31
|
+
self.every_options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
def step(name=nil, plugin=nil, &block)
|
35
|
+
if plugin
|
36
|
+
|
37
|
+
plugin_klass = self.class.plugins[plugin]
|
38
|
+
raise "No plugin configured for #{plugin}" unless plugin_klass
|
39
|
+
|
40
|
+
self.add_step(plugin_klass.new(name, plugin, &block))
|
41
|
+
else
|
42
|
+
self.add_step(Step.new(name, plugin, &block))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_step(step)
|
47
|
+
self.steps.push(step)
|
48
|
+
end
|
49
|
+
|
50
|
+
def schedule
|
51
|
+
Clockwork.manager.every(every_duration, name, every_options) do
|
52
|
+
run
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def run
|
57
|
+
|
58
|
+
# track the last response, and all responses
|
59
|
+
last_response = nil
|
60
|
+
step_responses = {}
|
61
|
+
|
62
|
+
self.steps.each do |step|
|
63
|
+
|
64
|
+
# track the last_response and all responses
|
65
|
+
last_response = step.run(last_response, step_responses)
|
66
|
+
step_responses[step.name] = last_response
|
67
|
+
|
68
|
+
# abort unless this step returned truthily
|
69
|
+
return unless last_response
|
70
|
+
end
|
71
|
+
|
72
|
+
# log responses in debug
|
73
|
+
Pushpop.logger.info("#{name}: #{step_responses}")
|
74
|
+
|
75
|
+
# return the last response and all responses
|
76
|
+
[last_response, step_responses]
|
77
|
+
end
|
78
|
+
|
79
|
+
def method_missing(method, *args, &block)
|
80
|
+
plugin_class = self.class.plugins[method.to_s]
|
81
|
+
|
82
|
+
name = args[0]
|
83
|
+
plugin = method.to_s
|
84
|
+
|
85
|
+
if plugin_class
|
86
|
+
step(name, plugin, &block)
|
87
|
+
else
|
88
|
+
super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|