updater 0.9.4 → 0.10.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.
@@ -2,16 +2,19 @@ require File.join( File.dirname(__FILE__), "spec_helper" )
2
2
 
3
3
  include Updater
4
4
 
5
- require File.join( File.dirname(__FILE__), "fooclass" )
6
-
7
5
  describe "adding an immidiate update request" do
6
+ before :all do
7
+ @orm = Update.orm
8
+ end
9
+
8
10
  before(:each) do
9
- Foo.all.destroy!
11
+ Foo.reset
10
12
  end
11
- it "with a class target" do
13
+
14
+ specify "with a class target" do
12
15
  u = Update.immidiate(Foo,:bar,[])
13
16
  u.target.should == Foo
14
- Update.current.get(u.id).should_not be_nil
17
+ @orm.current.should include(u.orm)
15
18
  Update.delayed.should == 0
16
19
  end
17
20
 
@@ -19,21 +22,26 @@ describe "adding an immidiate update request" do
19
22
  f = Foo.create
20
23
  u = Update.immidiate(f,:bar,[])
21
24
  u.target.should == f
22
- Update.current.get(u.id).should_not be_nil
25
+ @orm.current.should include(u.orm)
23
26
  Update.delayed.should == 0
24
27
  end
25
28
 
26
29
  it "with an custome finder" do
27
30
  f = Foo.create(:name=>'baz')
31
+ Foo.should_receive(:first).with(:name=>'baz').and_return f
28
32
  u = Update.immidiate(Foo,:bar,[],:finder=>:first, :finder_args=>[{:name=>'baz'}])
29
33
  u.target.should == f
30
- Update.current.get(u.id).should_not be_nil
34
+ @orm.current.should include(u.orm)
31
35
  Update.delayed.should == 0
32
36
  end
33
37
 
34
38
  end
35
39
 
36
40
  describe "chained request" do
41
+ before :all do
42
+ @orm = Update.orm
43
+ end
44
+
37
45
  before :each do
38
46
  Update.clear_all
39
47
  end
@@ -41,7 +49,7 @@ describe "chained request" do
41
49
  it "should not be in current or delayed queue" do
42
50
  u = Update.chain(Foo,:bar,[:error])
43
51
  u.time.should be_nil
44
- Update.current.should_not include(u)
52
+ @orm.current.should_not include(u.orm)
45
53
  Update.delayed.should == 0
46
54
  end
47
55
 
@@ -55,7 +63,7 @@ end
55
63
  describe "adding an delayed update request" do
56
64
  before :each do
57
65
  Update.clear_all
58
- Foo.all.destroy
66
+ Foo.reset
59
67
  end
60
68
 
61
69
 
@@ -76,6 +84,7 @@ describe "adding an delayed update request" do
76
84
 
77
85
  it "with an custome finder" do
78
86
  f = Foo.create(:name=>'baz')
87
+ Foo.should_receive(:first).with(:name=>'baz').and_return f
79
88
  u = Update.at(Chronic.parse('tomorrow'),Foo,:bar,[],:finder=>:first, :finder_args=>[{:name=>'baz'}])
80
89
  u.target.should == f
81
90
  Update.current.should_not include(u)
data/spec/spec_helper.rb CHANGED
@@ -4,20 +4,19 @@ ROOT = File.join(File.dirname(__FILE__), '..')
4
4
  $LOAD_PATH << File.join(File.dirname(__FILE__), '../lib')
5
5
 
6
6
  require "rspec" # Satisfies Autotest and anyone else not using the Rake tasks
7
- require "dm-core"
8
- require 'dm-migrations'
7
+ #~ require "dm-core"
8
+ #~ require 'dm-migrations'
9
9
 
10
10
  require 'updater'
11
11
  require 'updater/thread_worker'
12
12
  require 'updater/fork_worker'
13
- require 'updater/orm/datamapper'
13
+ # require 'updater/orm/datamapper'
14
14
 
15
- Updater::Update.orm = Updater::ORM::DataMapper
15
+ # Updater::Setup.test_setup(:database=>{:adapter=>'sqlite3', :database=>'./default.db', :auto_migrate=>true})
16
+ Updater::Setup.test_setup
16
17
 
17
- DataMapper.setup(:default, 'sqlite3::memory:')
18
- DataMapper.auto_migrate!
18
+ require File.join( File.dirname(__FILE__), "fooclass" )
19
+ require File.join( File.dirname(__FILE__), "orm_lint" )
19
20
 
20
21
  require 'timecop'
21
22
  require 'chronic'
22
-
23
-
@@ -17,43 +17,6 @@ describe ThreadWorker do
17
17
  ThreadWorker.new.name.should be_a String
18
18
  end
19
19
 
20
- describe "loop thread control:" do
21
-
22
- class Foo
23
- include DataMapper::Resource
24
-
25
- property :id, Serial
26
- property :name, String
27
-
28
- def bar(*args)
29
- Foo.bar(:instance,*args)
30
- end
31
-
32
- end
33
-
34
- Foo.auto_migrate!
35
-
36
- specify "The loop should run when the worker is started" do
37
- pending
38
- worker = ThreadWorker.new(:quiet=>true, :name=>"testing")
39
- Update.should_receive(:work_off).with(worker).once.and_return(nil)
40
- t = Thread.new do
41
- worker.start
42
- end
43
- t.run
44
- worker.stop
45
- t.kill unless t.join(0.1) #you've got 0.1 seconds to finish or die
46
- end
47
-
48
- specify "The loop should run with USR1 signal" do
49
- pending
50
- t = Thread.new do
51
- worker.start
52
- end
53
- end
54
-
55
- end
56
-
57
20
  end
58
21
 
59
22
  describe "working off jobs:" do
@@ -2,13 +2,11 @@ require File.join( File.dirname(__FILE__), "spec_helper" )
2
2
 
3
3
  include Updater
4
4
 
5
- require File.join( File.dirname(__FILE__), "fooclass" )
6
-
7
5
  describe "running an update" do
8
6
 
9
7
  before :each do
10
8
  Update.clear_all
11
- Foo.all.destroy!
9
+ Foo.reset
12
10
  end
13
11
 
14
12
  it "should call the named method with a class target" do
@@ -28,21 +26,21 @@ describe "running an update" do
28
26
  u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
29
27
  Foo.should_receive(:bar).with(:arg1,:arg2)
30
28
  u.run
31
- u.should_not be_saved #NOTE: not a theological statment
29
+ Update.orm.get(u.orm.id).should be_nil
32
30
  end
33
31
 
34
32
  it "should delete the record if there is a failure" do
35
33
  u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
36
34
  Foo.should_receive(:bar).with(:arg1,:arg2).and_raise(RuntimeError)
37
35
  u.run
38
- u.should_not be_saved #NOTE: not a theological statment
36
+ Update.orm.get(u.orm.id).should be_nil
39
37
  end
40
38
 
41
39
  it "should NOT delete the record if it is a chain record" do
42
40
  u = Update.chain(Foo,:bar,[:arg1,:arg2])
43
41
  Foo.should_receive(:bar).with(:arg1,:arg2).and_raise(RuntimeError)
44
42
  u.run
45
- u.should be_saved
43
+ Update.orm.get(u.orm.id).should_not be_nil
46
44
  end
47
45
 
48
46
  end
data/spec/update_spec.rb CHANGED
@@ -7,7 +7,7 @@ describe Update do
7
7
  Updater::VERSION.should == File.read(File.join(ROOT,'VERSION')).strip
8
8
  end
9
9
 
10
- it "shuold have its own inspect method" do
10
+ it "should have its own inspect method" do
11
11
  Update.new(Update.orm.new).inspect.should =~ /Updater::Update/
12
12
  end
13
13
 
data/spec/util_spec.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  require File.join( File.dirname(__FILE__), "spec_helper" )
2
2
 
3
- include Updater
4
-
5
3
  describe "Util.tempio" do
6
4
 
7
5
  it "should return an unlinked file" do
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 9
8
- - 4
9
- version: 0.9.4
7
+ - 10
8
+ - 0
9
+ version: 0.10.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - John F. Miller
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-02-14 00:00:00 -08:00
17
+ date: 2011-03-06 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -38,13 +38,13 @@ dependencies:
38
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
- - - "="
41
+ - - ">="
42
42
  - !ruby/object:Gem::Version
43
43
  segments:
44
- - 1
45
- - 3
44
+ - 2
45
+ - 0
46
46
  - 0
47
- version: 1.3.0
47
+ version: 2.0.0
48
48
  type: :development
49
49
  version_requirements: *id002
50
50
  - !ruby/object:Gem::Dependency
@@ -93,29 +93,30 @@ files:
93
93
  - Rakefile
94
94
  - VERSION
95
95
  - lib/updater.rb
96
- - lib/updater/thread_worker.rb~
97
96
  - lib/updater/setup.rb
98
- - lib/updater/setup.rb~
99
97
  - lib/updater/util.rb
100
98
  - lib/updater/update.rb
101
99
  - lib/updater/fork_worker.rb
102
- - lib/updater/update_dm.rb
103
- - lib/updater/simulated.db
104
100
  - lib/updater/tasks.rb
105
101
  - lib/updater/thread_worker.rb
106
- - lib/updater/orm/mongo.rb~
107
102
  - lib/updater/orm/orm.rb
108
103
  - lib/updater/orm/datamapper.rb
104
+ - lib/updater/orm/mock.rb
105
+ - lib/updater/orm/activerocord.rb
109
106
  - lib/updater/orm/mongo.rb
107
+ - spec/results.html
108
+ - spec/datamapper_orm_spec.rb
110
109
  - spec/fork_worker_instance_spec.rb
111
110
  - spec/thread_worker_spec.rb
112
111
  - spec/schedule_spec.rb
113
- - spec/lock_spec.rb
114
- - spec/spec_helper.rb~
112
+ - spec/mock_orm_spec.rb
115
113
  - spec/params_sub_spec.rb
116
114
  - spec/chained_spec.rb
115
+ - spec/fooclass_spec.rb
117
116
  - spec/fooclass.rb
118
117
  - spec/update_spec.rb
118
+ - spec/mongo_orm_spec.rb
119
+ - spec/orm_lint.rb
119
120
  - spec/spec_helper.rb
120
121
  - spec/named_request_spec.rb
121
122
  - spec/update_runner_spec.rb
@@ -1,188 +0,0 @@
1
- require 'mongo'
2
- require 'active_support/inflector' #to get classes from strings
3
- require 'active_support/core_ext/object/try'
4
-
5
- module Updater
6
- module ORM
7
- class Mongo
8
-
9
- FINDER= :get
10
- ID=:_id
11
-
12
- def initialize(hash = {})
13
- @hash = {}
14
- hash.each do |key, val|
15
- if respond_to? "#{key}="
16
- send("#{key}=", val)
17
- else
18
- @hash[key] = val
19
- end
20
- end
21
- end
22
-
23
- %w{time finder finder_args method method_args name persistance lock_name}.each do |field|
24
- eval(<<-EOF) #,__LINE__+1,__FILE__)
25
- def #{field};@hash['#{field}'];end
26
- def #{field}=(value);@hash['#{field}'] = value;end
27
- EOF
28
- end
29
-
30
- def _id
31
- @hash['_id'] || @hash[:_id]
32
- end
33
-
34
- def _id=(val)
35
- val = BSON::ObjectID.from_string(val.to_s) unless val.kind_of? BSON::ObjectID
36
- @hash[:_id] = val
37
- end
38
-
39
- alias :id :_id
40
- alias :'id=' :'_id='
41
-
42
- def target
43
- @hash['target'].try :constantize
44
- end
45
-
46
- def target=(value)
47
- @hash['target'] = value.to_s
48
- end
49
-
50
- def save
51
- #todo validation
52
- self.class.collection.save @hash
53
- end
54
-
55
- def destroy
56
- @collection.remove({:_id=>id})
57
- end
58
-
59
- def [](arg) #this allows easy mapping for time when a value coud either be U::ORM::Mongo or an ordered hash
60
- @hash[arg]
61
- end
62
-
63
- #key :time, Integer, :numeric=>true
64
- #key :target, String, :required => true
65
- #key :finder, String
66
- # key :finder_args, Array
67
- #key :method, String :required => true
68
- #key :method_args, String :required => true
69
- #key :name
70
- # key :persistant
71
- #key :lock_name
72
-
73
- %w{failure success ensure}.each do |mode|
74
- eval(<<-EOF) #,__FILE__,__LINE__+1)
75
- def #{mode}
76
- @#{mode} ||= init_chain(:#{mode})
77
- end
78
- EOF
79
- end
80
-
81
- def init_chain(mode)
82
- ret = [@hash[mode.to_s] || []].flatten
83
- unless ret.empty?
84
- ret = @collection.find(:_id=>{'$in'=>ret}).map {|i| self.class.new(i)}
85
- end
86
- ret.define_singleton_method '<<' do |val|
87
- val = BSON::ObjectID.fron_string(val) if val.kind_of? String
88
- aval,hval = case val
89
- when self.class
90
- [val,val.id]
91
- when Hash
92
- [self.class.new(val),val['_id']]
93
- when BSON::ObjectID
94
- [@collection.find_one(val),val]
95
- end
96
- @hash[mode] ||= []
97
- @hash[mode] << hval
98
- super aval
99
- end
100
- end
101
-
102
- def lock(worker)
103
- raise NotImplimentedError, "Use lock_next"
104
- end
105
-
106
- class << self
107
- attr_accessor :db, :collection, :logger
108
-
109
- # Availible options:
110
- # * :database - the name of the database *required*
111
- # * :host - the host to connect to. Default: "localhost"
112
- # * :port - the port to connect to. Default: 27017
113
- # * :collection - which collection to store jobs in. Default: "Updater"
114
- # * :username/:password - if these are present, they will be used to authenticate against the database
115
- def setup(options)
116
- logger = options[:logger]
117
- raise ArgumentError, "Must spesify the name of a databas when setting up Mongo driver" unless options[:database]
118
- logger.info "Attempting to connect to mongodb at #{[options[:host] || "localhost", options[:port] || 27017}
119
- @db = ::Mongo::Connection.new(options[:host] || "localhost", options[:port] || 27017).db(options[:database].to_s)
120
- if options[:username] && options[:password]
121
- success = db.authenticate(options[:username] , options[:password])
122
- raise RunTimeError, "Could not Authenticate with MongoDb \"#{options[:database]}\" Please check the username and password."
123
- end
124
- collection_name = options[:collection] || 'updater'
125
- unless db.collection_names.include collection_name
126
- logger.warn "Updater MongoDB Driver is creating a new collection, \"#{collection_name}\" in \"#{options[:database]}\""
127
- end
128
- @collection = db.collection(collection_name)
129
- end
130
-
131
- def lock_next(worker)
132
- hash = OrderedHash.new
133
- hash['findandmodify'] =@collection.name
134
- hash['query'] = {:time=>{'$lte'=>tnow},:lock_name=>nil}
135
- hash['sort'] =[[:time,'ascending']] #oldest first
136
- hash['update'] = {'$set'=>{:lock_name=>worker.name}}
137
- hash['new'] = true
138
-
139
- ret = @db.command hash
140
- return nil unless ret['ok'] == 1
141
- return new(ret['value'])
142
- end
143
-
144
- def get(id)
145
- id = BSON::ObjectID.from_string(id) if id.kind_of? String
146
- new(@collection.find_one(id))
147
- end
148
-
149
- def current
150
- raise NotImplementedError, "Mongo does not support lazy evaluation"
151
- end
152
-
153
- def current_load
154
- @collection.find(:time=>{'$lte'=>tnow}).count
155
- end
156
-
157
- def delayed
158
- @collection.find(:time=>{'$gt'=>tnow}).count
159
- end
160
-
161
- def future(start, finish)
162
- @collection.find(:time=>{'$gt'=>start+tnow,'$lt'=>finish+tnow}).count
163
- end
164
-
165
- def queue_time
166
- nxt = @collection.find_one({:time=>{'$gt'=>3,'$lt'=>4}, :lock_name=>'foobar'}, :sort=>[[:time, :asc]], :fields=>[:time])
167
- return nil unless nxt
168
- return 0 if nxt['time'] <= tnow
169
- return nxt['time'] - tnow
170
- end
171
-
172
- def create(hash)
173
- new(hash).save
174
- end
175
-
176
- def clear_all
177
- @collection.remove
178
- end
179
-
180
- private
181
- def tnow
182
- Updater::Update.time.now.to_i
183
- end
184
- end
185
-
186
- end
187
- end
188
- end
@@ -1,175 +0,0 @@
1
- require 'logger'
2
- require 'yaml'
3
- require 'socket'
4
- require 'erb'
5
-
6
- module Updater
7
- class Setup
8
- class << self
9
- def start
10
- new(config_file).start
11
- end
12
-
13
- def stop
14
- new(config_file).stop
15
- end
16
-
17
- def client_setup(options = {})
18
- new(config_file, options).client_setup
19
- end
20
-
21
- def monitor
22
-
23
- end
24
-
25
- def config_file(options = {})
26
- if options[:config_file] && File.exists(options[:config_file])
27
- option[:config_file]
28
- elsif ENV['UPDATE_CONFIG'] && File.exists(ENV['UPDATE_CONFIG'])
29
- ENV['UPDATE_CONFIG']
30
- else
31
- (Dir.glob('{config,.}/updater.config') + Dir.glob('.updater')).first
32
- end
33
- end
34
- end
35
-
36
- ROOT = File.dirname(self.config_file || Dir.pwd)
37
-
38
- #extended used for clients who wnat to override parameters
39
- def initialize(file_or_hash, extended = {})
40
- @options = file_or_hash.kind_of?(Hash) ? file_or_hash : load_file(file_or_hash)
41
- @options.merge!(extended)
42
- @options[:pid_file] ||= File.join(ROOT,'updater.pid')
43
- @options[:host] ||= "localhost"
44
- @logger = @options[:logger] || Logger.new(@options[:log_file] || STDOUT)
45
- level = Logger::SEV_LABEL.index(@options[:log_level].upcase) if @options[:log_level]
46
- @logger.level = level || Logger::WARN unless @options[:logger] #only set this if we were not handed a logger
47
- end
48
-
49
- def start
50
- pid = Process.fork do
51
- _start
52
- end
53
- @logger.warn "Successfully started Master Loop at pid #{pid}"
54
- puts "Job Queue Processor Started at PID: #{pid}"
55
- end
56
-
57
- def stop
58
- Process.kill("TERM",File.read(@options[:pid_file]).to_i)
59
- end
60
-
61
- # The client is responcible for loading classes and making connections. We will simply setup the Updater spesifics
62
- def client_setup
63
- @logger.info "Updater Client is being initialized..."
64
- set_orm
65
-
66
- if @options[:socket] && File.exists?(@options[:socket])
67
- @logger.debug "Using UNIX Socket \"#{@options[:socket]}\""
68
- Updater::Update.socket = UNIXSocket.new(@options[:socket])
69
- elsif @options[:udp]
70
- socket = UDPSocket.new()
71
- socket.connect(@options[:host],@options[:udp])
72
- Updater::Update.socket = socket
73
- elsif @options[:tcp]
74
- Updater::Update.socket = TCPSocket.new(@options[:host],@options[:tcp])
75
- elsif @options[:remote]
76
- raise NotImplimentedError #For future Authenticated Http Rest Server
77
- end
78
-
79
- #set PID
80
- if File.exists? @options[:pid_file]
81
- Updater::Update.pid = File.read(@options[:pid_file]).strip
82
- end
83
-
84
-
85
- end
86
-
87
- private
88
-
89
- def set_orm
90
- #don't setup twice. Client setup might call this as part of server setup in which case it is already done
91
- return false if Updater::Update.orm
92
- orm = @options[:orm] || "datamapper"
93
- @logger.info "Updater setting ORM to \"#{orm}\""
94
- case orm.to_s.downcase
95
- when "datamapper"
96
- require 'updater/orm/datamapper'
97
- Updater::Update.orm = ORM::DataMapper
98
- when "mongodb"
99
- require 'updater/orm/mongo'
100
- Updater::Update.orm = ORM::Mongo
101
- when "activerecord"
102
- require 'updater/orm/activerecord'
103
- Updater::Update.orm = ORM::ActiveRecord
104
- else
105
- require "update/orm/#{orm}"
106
- Updater::Update.orm = Object.const_get("ORM").const_get(orm.capitalize)
107
- end
108
- @logger.info "Data store '#{orm}' selected"
109
- end
110
-
111
- def _start
112
- #set ORM
113
- set_orm
114
- #init DataStore
115
- default_options = {:adapter=>'sqlite3', :database=>'./default.db'}
116
- Updater::Update.orm.setup((@options[:database] || @options[:orm_setup] || default_options).merge(:logger=>@logger))
117
- #load Models
118
-
119
- models = @options[:models] || Dir.glob('./app/models/**/*.rb')
120
- models.each do |file|
121
- require file
122
- end
123
-
124
- #establish Connections
125
- @options[:host] ||= 'localhost'
126
- #Unix Socket -- name at @options[:socket]
127
- if @options[:socket]
128
- File.unlink @options[:socket] if File.exists? @options[:socket]
129
- @options[:sockets] ||= []
130
- @options[:sockets] << UNIXServer.new(@options[:socket])
131
- @logger.info "Now listening on UNIX Socket: #{@options[:socket]}"
132
- end
133
-
134
- #UDP potentially unsafe user monitor server for Authenticated Connections (TODO)
135
- if @options[:udp]
136
- @options[:sockets] ||= []
137
- udp = UDPSocket.new
138
- udp.bind(@options[:host],@options[:udp])
139
- @options[:sockets] << udp
140
- @logger.info "Now listening for UDP: #{@options[:host]}:#{@options[:udp]}"
141
- end
142
-
143
- #TCP Unsafe user monitor server for Authenticated Connections (TODO)
144
- if @options[:tcp]
145
- @options[:sockets] ||= []
146
- @options[:sockets] << TCPServer.new(@options[:host],@options[:tcp])
147
- @logger.info "Now listening for TCP: #{@options[:host]}:#{@options[:tcp]}"
148
- end
149
-
150
- #Log PID
151
- File.open(@options[:pid_file],'w') { |f| f.write(Process.pid.to_s)}
152
-
153
- client_setup
154
-
155
- #start Worker
156
- worker = @options[:worker] || 'fork' #todo make this line windows safe
157
- require "updater/#{worker}_worker"
158
- worker_class = Updater.const_get("#{worker.capitalize}Worker")
159
- worker_class.logger = @logger
160
- @logger.info "Using #{worker_class.to_s} to run jobs:"
161
- worker_class.start(@options)
162
- File.unlink(@options[:pid_file])
163
- File.unlink @options[:socket] if @options[:socket] && File.exists?(@options[:socket])
164
- end
165
-
166
- def load_file(file)
167
- return {} if file.nil?
168
- file = File.open(file) if file.kind_of?(String)
169
- @config_file = File.expand_path(file.path)
170
- YAML.load(ERB.new(file.read).result(binding)) || {}
171
- ensure
172
- file.close if file.kind_of?(IO) && !file.closed?
173
- end
174
- end
175
- end
Binary file