chrono_trigger 0.1.7 → 1.0.2

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.
@@ -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: []