webapp_worker 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,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in highcharts.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,37 @@
1
+ ## Information
2
+
3
+ Provides a way to have workers on your webapp servers, espeically useful for webapps that tend to scale up and down with X amount of servers in the load balancer. Also good way to not use another dependent resource like a job scheduler/queue. Keeps your application all packaged up nicely, no cron jobs to set, nothing else to think about setting up and nothing else to maintain.
4
+
5
+ ## Installation
6
+
7
+ `gem install webapp_worker`
8
+
9
+ or use it in your Gemfile
10
+
11
+ `gem 'webapp_worker'`
12
+
13
+ ## Quick Test
14
+
15
+ ``
16
+
17
+ ## Using in your webapp
18
+
19
+
20
+
21
+ ## Contributing
22
+
23
+ - Fork the project and do your work in a topic branch.
24
+ - Rebase your branch to make sure everything is up to date.
25
+ - Commit your changes and send a pull request.
26
+
27
+ ## Copyright
28
+
29
+ (The MIT License)
30
+
31
+ Copyright © 2011 nictrix (Nick Willever)
32
+
33
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
36
+
37
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/waw ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'webapp_worker'
4
+ require 'trollop'
5
+
6
+ opts = Trollop::options do
7
+ banner <<-EOS
8
+ Usage:
9
+ #{File.basename($0)} [options]
10
+
11
+ where [options] are:
12
+ EOS
13
+
14
+ opt :environment, "Enviroment (i.e. local, development..)", :type => String, :short => "-e"
15
+ opt :jobfile, "A YAML config file", :type => String, :short => "-f"
16
+ opt :run, "Run the jobs", :default => false, :short => "-r"
17
+ opt :jobs, "Show the jobs", :default => false, :short => "-j"
18
+ opt :nextrun, "Find the next possible command(s) to run (i.e. 1,2...)", :type => Integer, :short => "-n"
19
+ opt :debug, "Local Debug", :short => "-d"
20
+ opt :verbose, "Verbose Output", :short => "-v"
21
+ end
22
+
23
+ Trollop::die :jobfile, "must specify jobfile" unless File.exist?(opts[:jobfile]) if opts[:jobfile]
24
+ Trollop::die :environment, "must specify environment" unless opts[:environment]
25
+
26
+ job_file = File.absolute_path(opts[:jobfile])
27
+
28
+ a = WebappWorker::Application.new(environment:opts[:environment])
29
+ a.parse_yaml(job_file)
30
+
31
+ if opts[:nextrun] != nil
32
+ a.next_command_run?(opts[:nextrun]).each do |command,time|
33
+ puts "Next Command Run: #{command}"
34
+ puts " Next Run: #{time}"
35
+ end
36
+ elsif opts[:run] == false && opts[:jobs] == false
37
+ puts
38
+ puts "Host: #{a.hostname}"
39
+ puts "Mailto: #{a.mailto}"
40
+ puts "Environment: #{a.environment}"
41
+ puts "Amount of Jobs: #{a.jobs.length}"
42
+ elsif opts[:run] == false && opts[:jobs] == true
43
+ puts "Job File: #{job_file}"
44
+ puts
45
+ puts "Host: #{a.hostname}"
46
+ puts "Mailto: #{a.mailto}"
47
+ puts "Environment: #{a.environment}"
48
+ puts "Amount of Jobs: #{a.jobs.length}"
49
+ puts
50
+ a.jobs.each do |job|
51
+ j = WebappWorker::Job.new(job)
52
+ puts "Command to Run: #{j.command}"
53
+ puts " Next Run: #{j.next_run?}"
54
+ end
55
+ else
56
+ puts "Running Jobs"
57
+ a.run
58
+ end
@@ -0,0 +1,88 @@
1
+ require 'socket'
2
+
3
+ module WebappWorker
4
+ class Application
5
+ attr_accessor :hostname, :mailto, :environment, :jobs
6
+
7
+ def initialize(user_supplied_hash={})
8
+ standard_hash = { hostname:"#{self.hostname}", mailto:"", environment:"local", jobs:"" }
9
+
10
+ user_supplied_hash = {} unless user_supplied_hash
11
+ user_supplied_hash = standard_hash.merge(user_supplied_hash)
12
+
13
+ user_supplied_hash.each do |key,value|
14
+ self.instance_variable_set("@#{key}", value)
15
+ self.class.send(:define_method, key, proc{self.instance_variable_get("@#{key}")})
16
+ self.class.send(:define_method, "#{key}=", proc{|x| self.instance_variable_set("@#{key}", x)})
17
+ end
18
+ end
19
+
20
+ def parse_yaml(yaml)
21
+ @mailto = (YAML.load_file(yaml))[@environment]["mailto"] unless @mailto
22
+ @jobs = (YAML.load_file(yaml))[@environment][@hostname] unless @hostname.nil?
23
+ end
24
+
25
+ def hostname
26
+ return Socket.gethostname.downcase
27
+ end
28
+
29
+ def next_command_run?(til)
30
+ commands = {}
31
+ c = {}
32
+ next_commands = {}
33
+
34
+ @jobs.each do |job|
35
+ j = WebappWorker::Job.new(job)
36
+ commands.store(j.command,j.next_run?)
37
+ end
38
+ (commands.sort_by { |key,value| value }).collect { |key,value| c.store(key,value) }
39
+
40
+ counter = 0
41
+ c.each do |key,value|
42
+ next_commands.store(key,value)
43
+ counter = counter + 1
44
+ break if counter >= til
45
+ end
46
+
47
+ return next_commands
48
+ end
49
+
50
+ def run
51
+ #Going to need to do memory/process management, or fork processes not threads...
52
+ p = Process.fork do
53
+ Signal.trap('HUP', 'IGNORE')
54
+
55
+ @threads = {}
56
+
57
+ loop do
58
+ @threads.each do |thread,command|
59
+ if thread.status == false
60
+ @threads.delete(thread)
61
+ end
62
+ end
63
+
64
+ data = self.next_command_run?(1)
65
+
66
+ data.each do |command,time|
67
+ time = time[0]
68
+ now = Time.now.utc
69
+ range = (time - now).to_i
70
+
71
+ if @threads.detect { |thr,com| com == command }
72
+ sleep(range) unless range <= 0
73
+ else
74
+ t = Thread.new do
75
+ sleep(range) unless range <= 0
76
+ `#{command}`
77
+ end
78
+ @threads.store(t,command)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ Process.detach(p)
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,204 @@
1
+ require 'yaml'
2
+ require 'time'
3
+
4
+ module WebappWorker
5
+ class Job
6
+ attr_accessor :command, :minute, :hour, :day, :month, :weekday
7
+
8
+ DIVISION = /\d+-\d+[\/]\d+/
9
+ DIGIT = /^\d+$/
10
+
11
+ def initialize(user_supplied_hash={})
12
+ standard_hash = { command:"", minute:"*", hour:"*", day:"*", month:"*", weekday:"*" }
13
+
14
+ user_supplied_hash = {} unless user_supplied_hash
15
+ user_supplied_hash = standard_hash.merge(user_supplied_hash)
16
+
17
+ user_supplied_hash.each do |key,value|
18
+ self.instance_variable_set("@#{key}", value)
19
+ self.class.send(:define_method, key, proc{self.instance_variable_get("@#{key}")})
20
+ self.class.send(:define_method, "#{key}=", proc{|x| self.instance_variable_set("@#{key}", x)})
21
+ end
22
+
23
+ self.parse_datetime
24
+ end
25
+
26
+ def self.new_from_yaml(yaml,environment)
27
+ job = self.new((YAML.load_file(yaml))[environment])
28
+ end
29
+
30
+ def current_time
31
+ return "#{Time.now.strftime("%w %m-%d %H:%M")}"
32
+ end
33
+
34
+ def make_string(n)
35
+ test_n = n.to_s.length
36
+
37
+ if test_n < 2 && test_n > 0
38
+ return "0#{n}"
39
+ else
40
+ return n.to_s
41
+ end
42
+ end
43
+
44
+ def fix_every(value)
45
+ divider = /\d+$/
46
+ range = /^\d+-\d+/
47
+ first_range = /^\d+/
48
+ second_range = /\d+$/
49
+
50
+ every = value.match(divider).to_s.to_i
51
+ number_range = value.match(range).to_s
52
+ first = number_range.match(first_range).to_s.to_i
53
+ second = number_range.match(second_range).to_s.to_i
54
+
55
+ range = []
56
+ until first >= second do
57
+ first = first + every
58
+ break if first > second
59
+ range << self.make_string(first)
60
+ end
61
+
62
+ return range
63
+ end
64
+
65
+ def parse_datetime
66
+ self.fix_minute
67
+ self.fix_hour
68
+ self.fix_day
69
+ self.fix_month
70
+ self.fix_weekday
71
+ end
72
+
73
+ def fix_minute
74
+ case @minute.to_s
75
+ when "*", nil, ""
76
+ @minute = []
77
+ (0..59).each do |m|
78
+ @minute << self.make_string(m)
79
+ end
80
+ when DIVISION
81
+ @minute = self.fix_every(@minute)
82
+ when DIGIT
83
+ @minute = [self.make_string(@minute)]
84
+ end
85
+ end
86
+
87
+ def fix_hour
88
+ case @hour.to_s
89
+ when "*", nil, ""
90
+ @hour = []
91
+ (0..23).each do |h|
92
+ @hour << self.make_string(h)
93
+ end
94
+ when DIVISION
95
+ @hour = self.fix_every(@hour)
96
+ when DIGIT
97
+ @hour = [self.make_string(@hour)]
98
+ end
99
+ end
100
+
101
+ def fix_day
102
+ case @day.to_s
103
+ when "*", nil, ""
104
+ @day = []
105
+ (1..31).each do |d|
106
+ @day << self.make_string(d)
107
+ end
108
+ when DIVISION
109
+ @day = self.fix_every(@day)
110
+ when DIGIT
111
+ @day = [self.make_string(@day)]
112
+ end
113
+ end
114
+
115
+ def fix_month
116
+ case @month.to_s
117
+ when "*", nil, ""
118
+ @month = []
119
+ (1..12).each do |m|
120
+ @month << self.make_string(m)
121
+ end
122
+ when DIVISION
123
+ @month = self.fix_every(@month)
124
+ when DIGIT
125
+ @month = [self.make_string(@month)]
126
+ end
127
+ end
128
+
129
+ def fix_weekday
130
+ case @weekday.to_s
131
+ when "*", nil, ""
132
+ @weekday = []
133
+ (0..6).each do |w|
134
+ @weekday << w.to_s
135
+ end
136
+ when DIVISION
137
+ @weekday = self.fix_every(@weekday)
138
+ when DIGIT
139
+ @weekday = [@weekday.to_s]
140
+ end
141
+ end
142
+
143
+ def next_times(numbers,amount,time)
144
+ future = {}
145
+
146
+ numbers.each do |number|
147
+ calculated = number.to_i - time
148
+ calculated = (calculated + amount) unless calculated >= 0
149
+ future.store(number,calculated)
150
+ end
151
+
152
+ sub = {}
153
+ (future.sort_by { |key,value| value }).collect { |key,value| sub.store(key,value) }
154
+ fn = sub.collect { |key,value| key }
155
+
156
+ return fn
157
+ end
158
+
159
+ def next_run?
160
+ self.next_runs?(1)
161
+ end
162
+
163
+ def next_runs?(til)
164
+ self.parse_datetime
165
+
166
+ next_runs = []
167
+
168
+ now = Time.now
169
+ @weekday = self.next_times(@weekday,6,now.strftime("%w").to_i)
170
+ @year = now.strftime("%Y")
171
+ @month = self.next_times(@month,12,now.strftime("%m").to_i)
172
+ @day = self.next_times(@day,31,now.strftime("%d").to_i)
173
+ @hour = self.next_times(@hour,23,now.strftime("%H").to_i)
174
+ @minute = self.next_times(@minute,60,now.strftime("%M").to_i)
175
+
176
+ counter = 0
177
+ catch :done do
178
+ @month.each do |month|
179
+ @day.each do |day|
180
+ @weekday.each do |weekday|
181
+ @hour.each do |hour|
182
+ @minute.each do |minute|
183
+ begin
184
+ next_time = (DateTime.strptime("#{weekday} #{@year}-#{month}-#{day} #{hour}:#{minute}","%w %Y-%m-%d %H:%M")).to_time + 25200
185
+
186
+ next unless next_time >= now
187
+ next_runs << next_time
188
+ counter = counter + 1
189
+ rescue ArgumentError
190
+ next
191
+ end
192
+
193
+ throw :done, next_runs if counter >= til
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ return next_runs
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,3 @@
1
+ module WebappWorker
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "webapp_worker/version"
2
+
3
+ require "webapp_worker/application"
4
+ require "webapp_worker/job"
5
+
6
+ module WebappWorker
7
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "webapp_worker/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "webapp_worker"
7
+ s.version = WebappWorker::VERSION
8
+ s.authors = ["Nick Willever"]
9
+ s.email = ["nickwillever@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Provides a worker for your webapp}
12
+ s.description = %q{Allow the webapp to handle your workers, no need to use a job scheduler}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_runtime_dependency 'trollop'
20
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webapp_worker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nick Willever
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-15 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: trollop
16
+ requirement: &81315550 !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: *81315550
25
+ description: Allow the webapp to handle your workers, no need to use a job scheduler
26
+ email:
27
+ - nickwillever@gmail.com
28
+ executables:
29
+ - waw
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .gitignore
34
+ - Gemfile
35
+ - README.markdown
36
+ - Rakefile
37
+ - bin/waw
38
+ - lib/webapp_worker.rb
39
+ - lib/webapp_worker/application.rb
40
+ - lib/webapp_worker/job.rb
41
+ - lib/webapp_worker/version.rb
42
+ - webapp_worker.gemspec
43
+ homepage: ''
44
+ licenses: []
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project:
63
+ rubygems_version: 1.8.11
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Provides a worker for your webapp
67
+ test_files: []