chrono_trigger 0.1.7 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoTrigger
4
+ class Config
5
+ include Singleton
6
+ attr_accessor :interval
7
+
8
+ def initialize
9
+ @interval = 0.1
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoTrigger
4
+ class Event
5
+ class << self
6
+ def schedule(*args)
7
+ id = SecureRandom.uuid
8
+ options = OpenStruct.new(
9
+ id: id,
10
+ scope: @scope,
11
+ repeats: @repeats || 1,
12
+ every: @every || 1.second,
13
+ at: @at,
14
+ before: @before,
15
+ after: @after
16
+ )
17
+ new(options, args)
18
+ end
19
+
20
+ def scope(value)
21
+ return self unless value
22
+ @scope = value if value.is_a?(String)
23
+ @scope = value.to_gid.to_s if value.is_a?(ActiveRecord::Base)
24
+ self
25
+ end
26
+
27
+ def repeats(value)
28
+ return self unless value
29
+ @repeats = value.count if value.is_a?(Enumerator)
30
+ @repeats = value if value.is_a?(Integer) || value == :forever
31
+ self
32
+ end
33
+
34
+ def every(value)
35
+ return self unless value
36
+ @every = value if value.is_a?(ActiveSupport::Duration)
37
+ @every = value.seconds if value.is_a?(Integer)
38
+ self
39
+ end
40
+
41
+ def at(value)
42
+ return self unless value
43
+ value = Time.zone.parse(value) if value.is_a?(String)
44
+ @at = moment_in_the_future(value) if value.is_a?(ActiveSupport::TimeWithZone)
45
+ puts @at.class
46
+ self
47
+ end
48
+
49
+ def before(value)
50
+ return self unless value
51
+ value = Time.zone.parse(value) if value.is_a?(String)
52
+ @before = moment_in_the_future(value) if value.is_a?(ActiveSupport::TimeWithZone)
53
+ self
54
+ end
55
+
56
+ def after(value)
57
+ return self unless value
58
+ value = Time.zone.parse(value) if value.is_a?(String)
59
+ @after = moment_in_the_future(value) if value.is_a?(ActiveSupport::TimeWithZone)
60
+ self
61
+ end
62
+
63
+ private
64
+
65
+ def moment_in_the_future(time_with_zone)
66
+ Time.zone.today + time_with_zone.hour.hours + time_with_zone.min.minutes + time_with_zone.sec.seconds
67
+ end
68
+ end
69
+
70
+ class LostInTimeError < StandardError
71
+ end
72
+
73
+ class TodayIsTomorrowsYesterday < StandardError
74
+ end
75
+
76
+ attr_reader :id, :scope, :args, :every, :before, :after, :purge
77
+ attr_accessor :repeats, :at
78
+
79
+ def initialize(options, args)
80
+ @id = options.id
81
+ @scope = options.scope
82
+ @repeats = options.repeats
83
+ @every = options.every
84
+ @at = options.at
85
+ @before = options.before
86
+ @after = options.after
87
+ @args = args
88
+ @purge = false
89
+ ChronoTrigger.schedule.add(self)
90
+ end
91
+
92
+ def ticks
93
+ ChronoTrigger::Clock.ticks
94
+ end
95
+
96
+ def purge!
97
+ @purge = true
98
+ end
99
+
100
+ def right_now
101
+ now = Time.zone.now
102
+ Time.zone.today + now.hour.hours + now.min.minutes + now.sec.seconds
103
+ end
104
+
105
+ def moment_in_the_future(time_with_zone)
106
+ raise(LostInTimeError, "Argument must be of type ActiveSupport::TimeWithZone eg. right_now + 1.second") unless time_with_zone.is_a?(ActiveSupport::TimeWithZone)
107
+ raise(TodayIsTomorrowsYesterday, "Time must be in the future eg. 1.second.from_now") unless time_with_zone.future?
108
+ Time.zone.today + time_with_zone.hour.hours + time_with_zone.min.minutes + time_with_zone.sec.seconds
109
+ end
110
+
111
+ def to_s
112
+ inspect
113
+ end
114
+
115
+ private
116
+
117
+ def perform(args)
118
+ raise NotImplementedError
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoTrigger
4
+ class Schedule
5
+ include Singleton
6
+ attr_reader :events
7
+
8
+ def initialize
9
+ @events = Concurrent::Array.new
10
+ @pool = Concurrent::Actor::Utils::Pool.spawn! "pool", 5 do |index|
11
+ Rails.logger.info "ChronoTrigger: Spawning worker schedule-#{index}"
12
+ ChronoTrigger::Worker.spawn name: "schedule-#{index}", supervise: true, args: []
13
+ end
14
+ end
15
+
16
+ def add(event)
17
+ @events << event if event&.is_a?(ChronoTrigger::Event)
18
+ self
19
+ end
20
+
21
+ def remove(uuid)
22
+ uuid_regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
23
+ @events.each { |event| event.purge! if event.id == uuid } if uuid && uuid_regex.match?(uuid.to_s.downcase)
24
+ self
25
+ end
26
+
27
+ def clear
28
+ @events.each { |event| event.purge! }
29
+ self
30
+ end
31
+
32
+ def clear_scope(value)
33
+ return self unless value
34
+ scope = value if value.is_a?(String)
35
+ scope = value.to_gid.to_s if value.is_a?(ActiveRecord::Base)
36
+ @events.each { |event| event.purge! if event.scope == scope }
37
+ self
38
+ end
39
+
40
+ def process_events
41
+ now = right_now
42
+ events.each do |event|
43
+ if event.purge || (event.before && now >= event.before) || event.repeats == 0
44
+ @events.delete(event)
45
+ next
46
+ end
47
+ next if event.after && now < event.after
48
+ if event.at.nil? || event.at == now
49
+ @pool << event
50
+ event.at = now + event.every
51
+ event.repeats -= 1 unless event.repeats == :forever
52
+ end
53
+ end
54
+ end
55
+
56
+ def refresh
57
+ now = right_now
58
+ events.each do |event|
59
+ next if event.at.nil?
60
+ event.at = nil if event.at < now
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def right_now
67
+ now = Time.zone.now
68
+ Time.zone.today + now.hour.hours + now.min.minutes + now.sec.seconds
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoTrigger
4
+ module Timeline
5
+ extend ::ActiveSupport::Concern
6
+
7
+ class LostInTimeError < StandardError
8
+ end
9
+
10
+ class TodayIsTomorrowsYesterday < StandardError
11
+ end
12
+
13
+ def chrono_trigger
14
+ ChronoTrigger::Schedule.instance
15
+ end
16
+
17
+ def right_now
18
+ now = Time.zone.now
19
+ Time.zone.today + now.hour.hours + now.min.minutes + now.sec.seconds
20
+ end
21
+
22
+ def moment_in_the_future(time_with_zone)
23
+ raise(LostInTimeError, "Argument must be of type ActiveSupport::TimeWithZone eg. right_now + 1.second") unless time_with_zone.is_a?(ActiveSupport::TimeWithZone)
24
+ raise(TodayIsTomorrowsYesterday, "Time must be in the future eg. 1.second.from_now") unless time_with_zone.future?
25
+ Time.zone.today + time_with_zone.hour.hours + time_with_zone.min.minutes + time_with_zone.sec.seconds
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoTrigger
4
+ VERSION = "1.0.2"
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoTrigger
4
+ class Worker < Concurrent::Actor::RestartingContext
5
+ def on_message(event)
6
+ Rails.logger.debug "ChronoTrigger: #{event.inspect}"
7
+ event.perform(*event.args) if event.at.nil? || (event.at && event.before.nil?) || (event.before && event.at < event.before)
8
+ end
9
+ end
10
+ end
metadata CHANGED
@@ -1,79 +1,193 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chrono_trigger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
5
- prerelease:
4
+ version: 1.0.2
6
5
  platform: ruby
7
6
  authors:
8
- - Jon Ciccone
7
+ - leastbad
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-08-22 00:00:00.000000000 Z
13
- dependencies: []
14
- description: Binding to tag
15
- email: darful@gmail.com
16
- executables:
17
- - chrono_trigger
11
+ date: 2021-04-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: concurrent-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 1.1.8
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.1'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.1.8
47
+ - !ruby/object:Gem::Dependency
48
+ name: concurrent-ruby-edge
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.6'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 0.6.0
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '0.6'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.6.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: magic_frozen_string_literal
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 1.2.0
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: 1.2.0
81
+ - !ruby/object:Gem::Dependency
82
+ name: pry
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.12'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '0.12'
95
+ - !ruby/object:Gem::Dependency
96
+ name: pry-nav
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '0.3'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '0.3'
109
+ - !ruby/object:Gem::Dependency
110
+ name: rake
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '13.0'
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 13.0.3
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '13.0'
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: 13.0.3
129
+ - !ruby/object:Gem::Dependency
130
+ name: standardrb
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '1.0'
136
+ type: :development
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '1.0'
143
+ description:
144
+ email:
145
+ - hello@leastbad.com
146
+ executables: []
18
147
  extensions: []
19
- extra_rdoc_files:
20
- - README.rdoc
148
+ extra_rdoc_files: []
21
149
  files:
22
- - History.txt
23
- - Manifest.txt
24
- - PostInstall.txt
25
- - README.rdoc
150
+ - Gemfile
151
+ - Gemfile.lock
152
+ - LICENSE.txt
153
+ - README.md
26
154
  - Rakefile
27
- - VERSION.yml
28
- - bin/chrono_trigger
29
- - chrono_trigger.gemspec
155
+ - SUMMARY.md
156
+ - bin/console
157
+ - bin/loc
158
+ - bin/setup
159
+ - bin/standardize
30
160
  - lib/chrono_trigger.rb
31
- - lib/chrono_trigger/cron_entry.rb
32
- - lib/chrono_trigger/process.rb
33
- - lib/chrono_trigger/runner.rb
34
- - lib/chrono_trigger/shell.rb
35
- - lib/chrono_trigger/tasks.rb
36
- - lib/chrono_trigger/trigger.rb
37
- - lib/tasks/chrono_trigger.rake
38
- - lib/triggers/test_triggers.rb
39
- - script/console
40
- - script/destroy
41
- - script/generate
42
- - test/test_chrono_trigger.rb
43
- - test/test_cron_entry.rb
44
- - test/test_helper.rb
45
- - test/test_shell.rb
46
- - test/test_trigger.rb
47
- - test/triggers.rb
48
- homepage: ''
49
- licenses: []
161
+ - lib/chrono_trigger/clock.rb
162
+ - lib/chrono_trigger/config.rb
163
+ - lib/chrono_trigger/event.rb
164
+ - lib/chrono_trigger/schedule.rb
165
+ - lib/chrono_trigger/timeline.rb
166
+ - lib/chrono_trigger/version.rb
167
+ - lib/chrono_trigger/worker.rb
168
+ homepage: https://chronotrigger.leastbad.com/
169
+ licenses:
170
+ - MIT
171
+ metadata:
172
+ source_code_uri: https://github.com/leastbad/chrono_trigger
173
+ documentation_uri: https://chronotrigger.leastbad.com/
50
174
  post_install_message:
51
- rdoc_options:
52
- - --charset=UTF-8
175
+ rdoc_options: []
53
176
  require_paths:
54
177
  - lib
55
178
  required_ruby_version: !ruby/object:Gem::Requirement
56
- none: false
57
179
  requirements:
58
- - - ! '>='
180
+ - - ">="
59
181
  - !ruby/object:Gem::Version
60
182
  version: '0'
61
183
  required_rubygems_version: !ruby/object:Gem::Requirement
62
- none: false
63
184
  requirements:
64
- - - ! '>='
185
+ - - ">="
65
186
  - !ruby/object:Gem::Version
66
187
  version: '0'
67
188
  requirements: []
68
- rubyforge_project:
69
- rubygems_version: 1.8.24
189
+ rubygems_version: 3.1.4
70
190
  signing_key:
71
- specification_version: 3
72
- summary: binding to tag
73
- test_files:
74
- - test/test_chrono_trigger.rb
75
- - test/test_cron_entry.rb
76
- - test/test_helper.rb
77
- - test/test_shell.rb
78
- - test/test_trigger.rb
79
- - test/triggers.rb
191
+ specification_version: 4
192
+ summary: A clock-based scheduler that runs inside your Rails app.
193
+ test_files: []