khronos 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  --color
2
+ --format d
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
1
  source :rubygems
2
2
  gemspec
3
3
 
4
+ gem 'rake'
5
+
4
6
  group :test do
5
7
  gem 'thin'
6
8
  gem 'simplecov', '= 0.6.4'
@@ -13,6 +15,7 @@ group :test do
13
15
  gem "factory_girl", "~> 4.0.0"
14
16
  gem "webmock", "~> 1.8.9"
15
17
  gem "database_cleaner"
18
+ gem 'netrc', '= 0.7.7'
16
19
 
17
20
  #
18
21
  # Used for time functions
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- khronos (0.1.3)
4
+ khronos (0.1.4)
5
5
  activerecord (~> 3.2.8)
6
6
  girl_friday (~> 0.10.0)
7
7
  json (~> 1.7.5)
@@ -63,6 +63,7 @@ GEM
63
63
  rack
64
64
  rack-test (0.6.1)
65
65
  rack (>= 1.0)
66
+ rake (0.9.2.2)
66
67
  rest-client (1.6.8)
67
68
  mime-types (>= 1.16)
68
69
  netrc
@@ -107,8 +108,10 @@ DEPENDENCIES
107
108
  mongo
108
109
  mongoid (~> 3.0.5)
109
110
  mysql2 (~> 0.3.11)
111
+ netrc (= 0.7.7)
110
112
  pg
111
113
  rack-test (~> 0.6.1)
114
+ rake
112
115
  rspec (~> 2.11.0)
113
116
  simplecov (= 0.6.4)
114
117
  sqlite3
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- khronos
1
+ khronos [![Build Status](https://secure.travis-ci.org/endel/khronos.png)](http://travis-ci.org/endel/khronos)
2
2
  ===
3
3
 
4
4
  Simple HTTP-based Job scheduling for the cloud.
@@ -10,7 +10,8 @@ Features
10
10
  - Configure recurrency per request
11
11
  - Log HTTP status code for every request made
12
12
  - Query the database via REST API
13
- - Postgresql, MySQL and SQLite supported. (mongodb will be supported soon)
13
+
14
+ Currently only PostgreSQL is supported. Patches for other databases are welcome.
14
15
 
15
16
  How it works
16
17
  ---
@@ -31,24 +32,24 @@ Create a scheduled task:
31
32
  :task_url => "http://myapp.com/do-something-awesome",
32
33
  :recurrency => 12.hours
33
34
  })
34
- # => "{\"active\":true,\"at\":\"2012-09-15T21:24:56-03:00\",\"context\":\"test\",\"id\":1,\"recurrency\":1,\"task_url\":\"http://myapp.com/do-something-awesome\"}"
35
+ # => {"active":true,"at":"2012-09-15T21:24:56-03:00","context":"test","id":1,"recurrency":1,"task_url":"http://myapp.com/do-something-awesome"}
35
36
 
36
37
  Query for a scheduled task:
37
38
 
38
39
  RestClient.get('http://localhost:3000/task', :params => { :context => 'test' })
39
- # => "{\"active\":true,\"at\":\"2012-09-15T21:24:56-03:00\",\"context\":\"test\",\"id\":1,\"recurrency\":1,\"task_url\":\"http://myapp.com/do-something-awesome\"}"
40
+ # => {"active":true,"at":"2012-09-15T21:24:56-03:00","context":"test","id":1,"recurrency":1,"task_url":"http://myapp.com/do-something-awesome"}
40
41
 
41
42
  Delete a scheduled task by query:
42
43
 
43
44
  RestClient.delete('http://localhost:3000/task', :params => { :status_code => 404 })
44
- # => "{\"deleted\":3}"
45
+ # => {"deleted":3}
45
46
  RestClient.delete('http://localhost:3000/task', :params => { :id => 9 })
46
- # => "{\"deleted\":1}"
47
+ # => {"deleted":1}
47
48
 
48
49
  Query for logs for tasks that already ran.
49
50
 
50
51
  RestClient.get('http://localhost:3000/schedule/logs', :params => { :status_code => 500 })
51
- # => "[{\"id\":3,\"schedule_id\":1,\"started_at\":\"2012-09-15T13:38:48-03:00\",\"status_code\":500},{\"id\":5,\"schedule_id\":2,\"started_at\":\"2012-09-15T13:38:48-03:00\",\"status_code\":500}]"
52
+ # => [{"id":3,"schedule_id":1,"started_at":"2012-09-15T13:38:48-03:00","status_code":500},{"id":5,"schedule_id":2,"started_at":"2012-09-15T13:38:48-03:00","status_code":500}]
52
53
 
53
54
  Note: these examples are using [rest-client](https://github.com/archiloque/rest-client/) and [activesupport](https://github.com/rails/rails/tree/master/activesupport).
54
55
 
data/Rakefile CHANGED
@@ -1,36 +1,20 @@
1
1
  $: << 'lib'
2
2
 
3
+ require 'rake'
3
4
  require 'bundler/setup'
4
5
 
5
6
  ENV['RACK_ENV'] = ENV['ENV'] || 'test'
6
7
  require 'khronos'
7
8
 
8
- def load_migrations!
9
- require 'khronos/storage/adapter/activerecord/migrations/schedule'
10
- require 'khronos/storage/adapter/activerecord/migrations/schedule_log'
11
- end
12
-
13
- namespace :db do
14
-
15
- desc 'Create the database.'
16
- task :create do
17
- adapter = Khronos::Storage::Adapter.get(ENV['KHRONOS_STORAGE'])
18
- if adapter.name =~ /ActiveRecord/
19
- load_migrations!
20
- CreateSchedule.up
21
- CreateScheduleLog.up
22
- end
23
- end
9
+ require "rspec/core/rake_task"
24
10
 
25
- desc 'Destroy entire database.'
26
- task :drop do
27
- adapter = Khronos::Storage::Adapter.get(ENV['KHRONOS_STORAGE'])
28
- if adapter.name =~ /ActiveRecord/
29
- load_migrations!
30
- CreateSchedule.down
31
- CreateScheduleLog.down
32
- end
33
- end
11
+ desc "Run all specs"
12
+ task :spec => ["spec:all"]
34
13
 
14
+ desc "Run unit specs"
15
+ RSpec::Core::RakeTask.new(:'spec:all') do |t|
16
+ t.rspec_opts = ['--colour']
17
+ t.pattern = 'spec/**/*_spec.rb'
35
18
  end
36
19
 
20
+ task :default => :spec
@@ -7,7 +7,6 @@ require 'logger'
7
7
  module Khronos
8
8
  autoload :Config, 'khronos/config'
9
9
  autoload :Logger, 'khronos/logger'
10
- autoload :Scheduler, 'khronos/scheduler'
11
10
 
12
11
  autoload :Storage, 'khronos/storage'
13
12
  autoload :Server, 'khronos/server'
@@ -15,10 +15,12 @@ module Khronos
15
15
  def check_schedule!
16
16
  puts "Checking... #{Time.now}"
17
17
  count = 0
18
- Khronos::Scheduler.fetch(Time.now).each do |schedule|
19
- Khronos::Scheduler.run(schedule, @runner)
18
+
19
+ Storage::Schedule.fetch(Time.now).each do |schedule|
20
+ @runner.enqueue(schedule)
20
21
  count += 1
21
22
  end
23
+
22
24
  puts "Tick. #{count} jobs to run."
23
25
  end
24
26
 
@@ -111,9 +111,8 @@ module Khronos
111
111
  #
112
112
  # @return [Hash] queued
113
113
  post '/task/run' do
114
- schedule = Storage::Schedule.where(:id => params[:id]).first
115
- Khronos::Scheduler.run(schedule) if schedule
116
- {:queued => !schedule.nil?}.to_json
114
+ schedule = Storage::Schedule.where(:id => params[:id]).update_all(:at => Time.now)
115
+ {:queued => schedule}.to_json
117
116
  end
118
117
 
119
118
  # Schedule logs querying interface
@@ -8,7 +8,6 @@ module Khronos
8
8
  raise RuntimeError.new("Please configure 'KHRONOS_STORAGE' on your environment variables.") if uri.nil?
9
9
 
10
10
  @adapter = Adapter.get(uri)
11
- self.migrate! if @adapter.name =~ /ActiveRecord/
12
11
  self.class.send(:include, @adapter)
13
12
  end
14
13
 
@@ -24,24 +23,5 @@ module Khronos
24
23
  ScheduleLog.delete_all
25
24
  end
26
25
 
27
- protected
28
-
29
- def migrate!
30
- require 'khronos/storage/adapter/activerecord/migrations/schedule'
31
- require 'khronos/storage/adapter/activerecord/migrations/schedule_log'
32
-
33
- unless ActiveRecord::Base.connection.table_exists?(:schedules)
34
- Adapter::ActiveRecord::CreateSchedule.up
35
- else
36
- Logger.debug "Schedules table already exists."
37
- end
38
-
39
- unless ActiveRecord::Base.connection.table_exists?(:schedule_logs)
40
- Adapter::ActiveRecord::CreateScheduleLog.up
41
- else
42
- Logger.debug "ScheduleLog table already exists."
43
- end
44
- end
45
-
46
26
  end
47
27
  end
@@ -29,14 +29,10 @@ module Khronos
29
29
  def self.get(url)
30
30
  uri = parse_uri(url)
31
31
  framework = @frameworks[uri[:scheme]]
32
-
33
32
  require "khronos/storage/adapter/#{framework}"
34
33
 
35
34
  # Get and connect with the adapter class.
36
- adapter = const_get(@classes[framework])
37
-
38
- # uri.merge(:adapter => @adapters[uri[:scheme]] || uri[:scheme])
39
- adapter.connect!(url)
35
+ const_get(@classes[framework]).connect!(url)
40
36
  end
41
37
 
42
38
  end
@@ -10,25 +10,33 @@ module Khronos
10
10
  def self.connect!(url)
11
11
  require 'active_record'
12
12
 
13
- #if File.exists?("config/database.yml")
14
- #::ActiveRecord::Base.establish_connection(YAML.load_file("config/database.yml")[ENV['RACK_ENV']])
15
- #else
16
- ::ActiveRecord::Base.establish_connection(url)
17
- ::ActiveRecord::Base.include_root_in_json = false
18
- #end
19
-
20
- #
21
- # ::ActiveRecord::Base.logger = ::Logger.new(STDOUT)
22
- #
13
+ ::ActiveRecord::Base.establish_connection(url)
14
+ ::ActiveRecord::Base.include_root_in_json = false
23
15
  self
24
16
  end
25
17
 
26
18
  def self.included(base)
27
- #puts "included in #{base}"
19
+ self.migrate!
28
20
  end
29
21
 
30
- def self.extended(base)
31
- #puts "extended in #{base.inspect}"
22
+
23
+ protected
24
+
25
+ def self.migrate!
26
+ require 'khronos/storage/adapter/activerecord/migrations/schedule'
27
+ require 'khronos/storage/adapter/activerecord/migrations/schedule_log'
28
+
29
+ unless ::ActiveRecord::Base.connection.table_exists?(:schedules)
30
+ ActiveRecord::CreateSchedule.up
31
+ else
32
+ Logger.debug "Schedules table already exists."
33
+ end
34
+
35
+ unless ::ActiveRecord::Base.connection.table_exists?(:schedule_logs)
36
+ ActiveRecord::CreateScheduleLog.up
37
+ else
38
+ Logger.debug "ScheduleLog table already exists."
39
+ end
32
40
  end
33
41
 
34
42
  end
@@ -9,6 +9,14 @@ module Khronos
9
9
  attr_accessible :context, :at, :recurrency, :task_url, :callbacks, :active
10
10
  has_many :logs, :class_name => ScheduleLog
11
11
 
12
+ class << self
13
+ def fetch(time)
14
+ self.find_by_sql([
15
+ "UPDATE #{self.table_name} SET active = false WHERE at <= ? AND active = true RETURNING *", time
16
+ ])
17
+ end
18
+ end
19
+
12
20
  def callbacks=(options)
13
21
  write_attribute(:callbacks, options.to_json)
14
22
  end
@@ -1,3 +1,3 @@
1
1
  module Khronos
2
- VERSION = '0.1.3'
2
+ VERSION = '0.1.4'
3
3
  end
@@ -31,7 +31,7 @@ describe Khronos::Storage do
31
31
  Khronos::Storage::Schedule.last.task_url.should == "http://some-service.com/task"
32
32
  end
33
33
 
34
- it "should identify activerecord adapter for sqlite3" do
34
+ xit "should identify activerecord adapter for sqlite3" do
35
35
  client = subject.new('sqlite3://localhost/spec/tmp/sqlite3.db')
36
36
  Khronos::Storage::Schedule.should == Khronos::Storage::Adapter::ActiveRecord::Schedule
37
37
  Khronos::Storage::ScheduleLog.should == Khronos::Storage::Adapter::ActiveRecord::ScheduleLog
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Khronos::Storage do
4
+ subject { Khronos::Storage::Schedule }
5
+ let(:schedule) { subject }
6
+
7
+ before(:all) do
8
+ ENV['KHRONOS_STORAGE'] = 'postgresql://localhost:5432/khronos'
9
+
10
+ @storage = Khronos::Storage.new
11
+ @storage.truncate!
12
+
13
+ load_factory_girl!
14
+ end
15
+
16
+ it "fetch schedules by time" do
17
+ FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
18
+ schedule.fetch(1.minutes.from_now).length.should == 1
19
+
20
+ FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
21
+ FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
22
+ schedule.fetch(5.minutes.from_now).length.should == 2
23
+
24
+ FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
25
+ FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
26
+ FactoryGirl.create(:schedule, :at => 10.minutes.from_now)
27
+ schedule.fetch(10.minutes.from_now).length.should == 3
28
+
29
+ FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
30
+ FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
31
+ FactoryGirl.create(:schedule, :at => 10.minutes.from_now)
32
+ FactoryGirl.create(:schedule, :at => 30.minutes.from_now)
33
+ schedule.fetch(30.minutes.from_now).length.should == 4
34
+
35
+ FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
36
+ FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
37
+ FactoryGirl.create(:schedule, :at => 10.minutes.from_now)
38
+ FactoryGirl.create(:schedule, :at => 30.minutes.from_now)
39
+ FactoryGirl.create(:schedule, :at => 60.minutes.from_now)
40
+ schedule.fetch(60.minutes.from_now).length.should == 5
41
+
42
+ schedule.fetch(60.minutes.from_now).length.should == 0
43
+ end
44
+
45
+ end
46
+
@@ -44,7 +44,7 @@ describe Khronos::Server::Scheduler do
44
44
  dummy_schedule = FactoryGirl.create(:schedule)
45
45
  post('/task/run', :id => dummy_schedule.id)
46
46
  last_response.status.should == 200
47
- JSON.parse(last_response.body).should == {'queued' => true}
47
+ JSON.parse(last_response.body).should == {'queued' => 1}
48
48
  end
49
49
 
50
50
  it "should update schedule data" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: khronos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-16 00:00:00.000000000 Z
12
+ date: 2012-09-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
@@ -165,7 +165,6 @@ files:
165
165
  - lib/khronos.rb
166
166
  - lib/khronos/config.rb
167
167
  - lib/khronos/logger.rb
168
- - lib/khronos/scheduler.rb
169
168
  - lib/khronos/server.rb
170
169
  - lib/khronos/server/controller.rb
171
170
  - lib/khronos/server/runner.rb
@@ -189,7 +188,7 @@ files:
189
188
  - spec/functional/adapters_spec.rb
190
189
  - spec/functional/controller_spec.rb
191
190
  - spec/functional/misc_spec.rb
192
- - spec/functional/scheduler_spec.rb
191
+ - spec/functional/storage_spec.rb
193
192
  - spec/integration/runner_server_spec.rb
194
193
  - spec/integration/scheduler_server_spec.rb
195
194
  - spec/spec_helper.rb
@@ -224,7 +223,7 @@ test_files:
224
223
  - spec/functional/adapters_spec.rb
225
224
  - spec/functional/controller_spec.rb
226
225
  - spec/functional/misc_spec.rb
227
- - spec/functional/scheduler_spec.rb
226
+ - spec/functional/storage_spec.rb
228
227
  - spec/integration/runner_server_spec.rb
229
228
  - spec/integration/scheduler_server_spec.rb
230
229
  - spec/spec_helper.rb
@@ -1,19 +0,0 @@
1
- require 'socket'
2
- require 'json'
3
-
4
- module Khronos
5
- class Scheduler
6
-
7
- def self.run(schedule, runner=nil)
8
- schedule.update_attributes(:active => false)
9
- schedule.save
10
- runner.enqueue(schedule) if runner
11
- end
12
-
13
- def self.fetch(target_time=Time.now)
14
- Storage::Schedule.where(['at <= ?', target_time]).where(:active => true)
15
- end
16
-
17
- end
18
- end
19
-
@@ -1,30 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Khronos::Scheduler do
4
- subject { Khronos::Scheduler }
5
- let(:scheduler) { subject }
6
-
7
- before(:all) do
8
- ENV['KHRONOS_STORAGE'] = 'sqlite3://localhost/spec/tmp/scheduler.db'
9
-
10
- @storage = Khronos::Storage.new
11
- @storage.truncate!
12
-
13
- load_factory_girl!
14
- FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
15
- FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
16
- FactoryGirl.create(:schedule, :at => 10.minutes.from_now)
17
- FactoryGirl.create(:schedule, :at => 30.minutes.from_now)
18
- FactoryGirl.create(:schedule, :at => 60.minutes.from_now)
19
- end
20
-
21
- it "fetch schedules by time" do
22
- scheduler.fetch(1.minutes.from_now).length.should == 1
23
- scheduler.fetch(5.minutes.from_now).length.should == 2
24
- scheduler.fetch(10.minutes.from_now).length.should == 3
25
- scheduler.fetch(30.minutes.from_now).length.should == 4
26
- scheduler.fetch(60.minutes.from_now).length.should == 5
27
- end
28
-
29
- end
30
-