chrono_trigger 0.2.0 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +167 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +89 -0
  6. data/Rakefile +4 -25
  7. data/SUMMARY.md +5 -0
  8. data/bin/console +7 -0
  9. data/bin/loc +3 -0
  10. data/bin/setup +8 -0
  11. data/bin/standardize +4 -0
  12. data/lib/chrono_trigger.rb +29 -8
  13. data/lib/chrono_trigger/clock.rb +50 -0
  14. data/lib/chrono_trigger/config.rb +12 -0
  15. data/lib/chrono_trigger/event.rb +105 -0
  16. data/lib/chrono_trigger/helpers/mitf.rb +19 -0
  17. data/lib/chrono_trigger/helpers/now.rb +12 -0
  18. data/lib/chrono_trigger/schedule.rb +68 -0
  19. data/lib/chrono_trigger/timeline.rb +14 -0
  20. data/lib/chrono_trigger/version.rb +5 -0
  21. data/lib/chrono_trigger/worker.rb +10 -0
  22. metadata +170 -54
  23. data/History.txt +0 -17
  24. data/Manifest.txt +0 -24
  25. data/PostInstall.txt +0 -1
  26. data/README.rdoc +0 -61
  27. data/VERSION.yml +0 -4
  28. data/bin/chrono_trigger +0 -7
  29. data/chrono_trigger.gemspec +0 -69
  30. data/lib/chrono_trigger/cron_entry.rb +0 -71
  31. data/lib/chrono_trigger/process.rb +0 -37
  32. data/lib/chrono_trigger/runner.rb +0 -292
  33. data/lib/chrono_trigger/shell.rb +0 -36
  34. data/lib/chrono_trigger/tasks.rb +0 -3
  35. data/lib/chrono_trigger/trigger.rb +0 -127
  36. data/lib/tasks/chrono_trigger.rake +0 -14
  37. data/lib/triggers/test_triggers.rb +0 -31
  38. data/script/console +0 -10
  39. data/script/destroy +0 -14
  40. data/script/generate +0 -14
  41. data/test/test_chrono_trigger.rb +0 -11
  42. data/test/test_cron_entry.rb +0 -198
  43. data/test/test_helper.rb +0 -14
  44. data/test/test_shell.rb +0 -52
  45. data/test/test_trigger.rb +0 -172
  46. data/test/triggers.rb +0 -17
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '08632b380302ee5a2b70860bc5c60ea3dfed1a1e28dfb160d72b59f563b965fc'
4
+ data.tar.gz: d9ab5cfe642b09cf271cc85d142511ec6f346802d30e359ccf0941aee3b2a79f
5
+ SHA512:
6
+ metadata.gz: 57079c8c26383947f59a3f690be8634261149b1aca695da1ac461572b3231564f49d8a739a314a8d14a64a544be4b930577ff5cd21853d719a0870be673e116c
7
+ data.tar.gz: c57f6fe1a0cde287243454be84ee711df6d9603621b99b08f43a84199375b0013afe82a37d3c35f4e8c4e3bb50cbddaab2d95fd6e35b95a92e48a6bce3ae59cf
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in cable_ready.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,167 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ chrono_trigger (1.0.0)
5
+ concurrent-ruby (~> 1.1, >= 1.1.8)
6
+ concurrent-ruby-edge (~> 0.6, >= 0.6.0)
7
+ rails (~> 5.2)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actioncable (5.2.5)
13
+ actionpack (= 5.2.5)
14
+ nio4r (~> 2.0)
15
+ websocket-driver (>= 0.6.1)
16
+ actionmailer (5.2.5)
17
+ actionpack (= 5.2.5)
18
+ actionview (= 5.2.5)
19
+ activejob (= 5.2.5)
20
+ mail (~> 2.5, >= 2.5.4)
21
+ rails-dom-testing (~> 2.0)
22
+ actionpack (5.2.5)
23
+ actionview (= 5.2.5)
24
+ activesupport (= 5.2.5)
25
+ rack (~> 2.0, >= 2.0.8)
26
+ rack-test (>= 0.6.3)
27
+ rails-dom-testing (~> 2.0)
28
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
29
+ actionview (5.2.5)
30
+ activesupport (= 5.2.5)
31
+ builder (~> 3.1)
32
+ erubi (~> 1.4)
33
+ rails-dom-testing (~> 2.0)
34
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
35
+ activejob (5.2.5)
36
+ activesupport (= 5.2.5)
37
+ globalid (>= 0.3.6)
38
+ activemodel (5.2.5)
39
+ activesupport (= 5.2.5)
40
+ activerecord (5.2.5)
41
+ activemodel (= 5.2.5)
42
+ activesupport (= 5.2.5)
43
+ arel (>= 9.0)
44
+ activestorage (5.2.5)
45
+ actionpack (= 5.2.5)
46
+ activerecord (= 5.2.5)
47
+ marcel (~> 1.0.0)
48
+ activesupport (5.2.5)
49
+ concurrent-ruby (~> 1.0, >= 1.0.2)
50
+ i18n (>= 0.7, < 2)
51
+ minitest (~> 5.1)
52
+ tzinfo (~> 1.1)
53
+ arel (9.0.0)
54
+ ast (2.4.2)
55
+ builder (3.2.4)
56
+ coderay (1.1.3)
57
+ concurrent-ruby (1.1.8)
58
+ concurrent-ruby-edge (0.6.0)
59
+ concurrent-ruby (~> 1.1.6)
60
+ crass (1.0.6)
61
+ erubi (1.10.0)
62
+ globalid (0.4.2)
63
+ activesupport (>= 4.2.0)
64
+ i18n (1.8.10)
65
+ concurrent-ruby (~> 1.0)
66
+ loofah (2.9.1)
67
+ crass (~> 1.0.2)
68
+ nokogiri (>= 1.5.9)
69
+ magic_frozen_string_literal (1.2.0)
70
+ mail (2.7.1)
71
+ mini_mime (>= 0.1.1)
72
+ marcel (1.0.1)
73
+ method_source (0.9.2)
74
+ mini_mime (1.1.0)
75
+ minitest (5.14.4)
76
+ nio4r (2.5.7)
77
+ nokogiri (1.11.3-x86_64-linux)
78
+ racc (~> 1.4)
79
+ parallel (1.20.1)
80
+ parser (3.0.0.0)
81
+ ast (~> 2.4.1)
82
+ pry (0.12.2)
83
+ coderay (~> 1.1.0)
84
+ method_source (~> 0.9.0)
85
+ pry-nav (0.3.0)
86
+ pry (>= 0.9.10, < 0.13.0)
87
+ racc (1.5.2)
88
+ rack (2.2.3)
89
+ rack-test (1.1.0)
90
+ rack (>= 1.0, < 3)
91
+ rails (5.2.5)
92
+ actioncable (= 5.2.5)
93
+ actionmailer (= 5.2.5)
94
+ actionpack (= 5.2.5)
95
+ actionview (= 5.2.5)
96
+ activejob (= 5.2.5)
97
+ activemodel (= 5.2.5)
98
+ activerecord (= 5.2.5)
99
+ activestorage (= 5.2.5)
100
+ activesupport (= 5.2.5)
101
+ bundler (>= 1.3.0)
102
+ railties (= 5.2.5)
103
+ sprockets-rails (>= 2.0.0)
104
+ rails-dom-testing (2.0.3)
105
+ activesupport (>= 4.2.0)
106
+ nokogiri (>= 1.6)
107
+ rails-html-sanitizer (1.3.0)
108
+ loofah (~> 2.3)
109
+ railties (5.2.5)
110
+ actionpack (= 5.2.5)
111
+ activesupport (= 5.2.5)
112
+ method_source
113
+ rake (>= 0.8.7)
114
+ thor (>= 0.19.0, < 2.0)
115
+ rainbow (3.0.0)
116
+ rake (13.0.3)
117
+ regexp_parser (2.1.1)
118
+ rexml (3.2.4)
119
+ rubocop (1.11.0)
120
+ parallel (~> 1.10)
121
+ parser (>= 3.0.0.0)
122
+ rainbow (>= 2.2.2, < 4.0)
123
+ regexp_parser (>= 1.8, < 3.0)
124
+ rexml
125
+ rubocop-ast (>= 1.2.0, < 2.0)
126
+ ruby-progressbar (~> 1.7)
127
+ unicode-display_width (>= 1.4.0, < 3.0)
128
+ rubocop-ast (1.4.1)
129
+ parser (>= 2.7.1.5)
130
+ rubocop-performance (1.10.1)
131
+ rubocop (>= 0.90.0, < 2.0)
132
+ rubocop-ast (>= 0.4.0)
133
+ ruby-progressbar (1.11.0)
134
+ sprockets (4.0.2)
135
+ concurrent-ruby (~> 1.0)
136
+ rack (> 1, < 3)
137
+ sprockets-rails (3.2.2)
138
+ actionpack (>= 4.0)
139
+ activesupport (>= 4.0)
140
+ sprockets (>= 3.0.0)
141
+ standard (1.0.4)
142
+ rubocop (= 1.11.0)
143
+ rubocop-performance (= 1.10.1)
144
+ standardrb (1.0.0)
145
+ standard
146
+ thor (1.1.0)
147
+ thread_safe (0.3.6)
148
+ tzinfo (1.2.9)
149
+ thread_safe (~> 0.1)
150
+ unicode-display_width (2.0.0)
151
+ websocket-driver (0.7.3)
152
+ websocket-extensions (>= 0.1.0)
153
+ websocket-extensions (0.1.5)
154
+
155
+ PLATFORMS
156
+ x86_64-linux
157
+
158
+ DEPENDENCIES
159
+ chrono_trigger!
160
+ magic_frozen_string_literal (~> 1.2.0)
161
+ pry (~> 0.12.2)
162
+ pry-nav (~> 0.3.0)
163
+ rake (~> 13.0, >= 13.0.3)
164
+ standardrb (~> 1.0.0)
165
+
166
+ BUNDLED WITH
167
+ 2.2.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 leastbad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ ---
2
+ description: Make Rails apps that feel alive.
3
+ ---
4
+
5
+ # ChronoTrigger
6
+
7
+ [![GitHub stars](https://img.shields.io/github/stars/leastbad/chrono_trigger?style=social)](https://github.com/leastbad/chrono_trigger) [![GitHub forks](https://img.shields.io/github/forks/leastbad/chrono_trigger?style=social)](https://github.com/leastbad/chrono_trigger) [![Twitter follow](https://img.shields.io/twitter/follow/theleastbad?style=social)](https://twitter.com/theleastbad) [![Discord](https://img.shields.io/discord/681373845323513862)](https://discord.gg/GnweR3)
8
+
9
+ ChronoTrigger is a clock-based scheduler that runs inside your Rails app. Use it to run code \(events\) at specific times and intervals.
10
+
11
+ ![](.gitbook/assets/chrono-trigger.jpg)
12
+
13
+ ## Why use ChronoTrigger?
14
+
15
+ Nobody wants to be the first person to arrive at a club, and yet, this is exactly what most app onboarding processes feel like.
16
+
17
+ A typical visitor will spend _7-12 seconds_ evaluating your hard work before deciding if they will engage. You must convey that _exciting things are happening_, or they will close the tab and forget you exist.
18
+
19
+ **ChronoTrigger is a tool designed to breathe life into otherwise static user interfaces and onboarding experiences.**
20
+
21
+ It allows you to subtly expose users to a crafted narrative that nudges them forward, without feeling like a canned theme park ride. The story is advanced by their actions and their engagement is rewarded with simulated interaction, even if they are user number one.
22
+
23
+ ## Is ChronoTrigger for you?
24
+
25
+ If you spend months building something cool, and then hustle to get people to check it out, you get exactly one chance to make your first and only impression. It's up to you to do everything you can to make sure that your app is special enough to love. To succeed long-term, you need these first users to become cheerleaders and evangelize to their friends.
26
+
27
+ If you think about the stand-out [onboarding success stories](https://www.useronboard.com/user-onboarding-teardowns/) like Slack, they all prioritize anticipating what the user will be thinking, feeling and wondering during each _moment_ of the first minutes the user spends on the site.
28
+
29
+ ChronoTrigger is for developers who are proud of what they have created; tech founders looking for a way to give new users an aspirational story to tell themselves. This converging path leads to feelings of ownership and drives conversion without a hard sell.
30
+
31
+ You will use ChronoTrigger to orchestrate the onboarding experience your app deserves:
32
+
33
+ * interactive demos, charts and UI elements
34
+ * automation / wizards that feel personal
35
+ * placeholder content that changes to help tell a story
36
+ * a path through features instead of just clicking everything
37
+ * simulated human responses and exchanges
38
+
39
+ ### Why not ActiveJob?
40
+
41
+ ActiveJob is amazing, but creating Jobs for typical ChronoTrigger use cases feels like taking a taxi to the next house.
42
+
43
+ Jobs are not designed to run immediately, and priority should be given to important things like mail delivery. There's also functionality in the Event class that would be hard to retrofit to Job classes.
44
+
45
+ It's also worth mentioning that requiring Sidekiq would require Heroku users to set up a worker dyno, even if you're not using ActiveJob. Finally, Sidekiq sometimes runs jobs multiple times!
46
+
47
+ ## How does ChronoTrigger work?
48
+
49
+ ChronoTrigger runs on real world time, like trains. Every second, on the second, ChronoTrigger decides whether there are new events to run. If so, there's a thread-safe and highly-optimized pool of workers waiting.
50
+
51
+ {% hint style="info" %}
52
+ Other event scheduling libraries tend to be either `cron` wrappers or use timing offsets \(think: `sleep 1`\) from whenever they are called and don't factor in their own timing footprint. In other words, 1000 loops later, more than 1000 seconds have passed. No good!
53
+ {% endhint %}
54
+
55
+ You start the ChronoTrigger Clock after your web server, and it runs inside your Rails app process.
56
+
57
+ ChronoTrigger Events live in `app/events` and follow a structure that will be familiar to ActiveJob users.
58
+
59
+ You can schedule Events from anywhere in your application that it makes sense to do so, such as:
60
+
61
+ * Controller actions and webhook callbacks
62
+ * ActiveRecord model callbacks
63
+ * Devise session/registration callbacks
64
+ * Reflex action methods
65
+ * ActionCable Connection/Channel subscription callbacks
66
+
67
+ ## Design concepts and goals
68
+
69
+ * Not a replacement for ActiveJob \(or cron!\)
70
+ * Events are ephemeral and disposable; failure should be fine 🤷
71
+ * Borrow the best ideas from the ActiveJob and CableReady APIs
72
+ * ActiveSupport::TimeWithZone all the way down
73
+ * All times are today, rounded to 1s for easy comparison
74
+ * Times are memoized to avoid side effects
75
+ * Events should be short term and soon; _there is no tomorrow_
76
+ * Run in-process with no additional infrastructure dependencies
77
+
78
+ ## Key features and advantages
79
+
80
+ * A natural fit with [CableReady](https://cableready.stimulusreflex.com/)
81
+ * Easy to learn, quick to implement
82
+ * Plays well with tools such as [StimulusReflex](https://docs.stimulusreflex.com/) and [Turbo Drive](https://turbo.hotwire.dev/handbook/drive)
83
+ * Configurable via an optional initializer file
84
+ * Worker pool provided by the excellent [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) library
85
+
86
+ ## Try it now
87
+
88
+ ![](.gitbook/assets/soon.jpg)
89
+
data/Rakefile CHANGED
@@ -1,25 +1,4 @@
1
- # %w[rubygems rake rake/clean fileutils newgem rubigen rake/testtask].each { |f| require f }
2
- # require File.dirname(__FILE__) + '/lib/chrono_trigger'
3
-
4
- require 'rake/testtask'
5
-
6
- Rake::TestTask.new do |t|
7
- t.libs << 'test'
8
- end
9
-
10
- desc "Run tests"
11
- task :default => :test
12
-
13
- begin
14
- require 'jeweler'
15
- Jeweler::Tasks.new do |s|
16
- s.name = "chrono_trigger"
17
- s.summary = "TODO"
18
- s.email = "darful@gmail.com"
19
- s.homepage = ""
20
- s.description = "TODO"
21
- s.authors = ["Jon Ciccone"]
22
- end
23
- rescue LoadError
24
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
25
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "pry"
data/SUMMARY.md ADDED
@@ -0,0 +1,5 @@
1
+ # Table of contents
2
+
3
+ * [ChronoTrigger](README.md)
4
+ * [Setup](setup.md)
5
+
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "chrono_trigger"
5
+ require "pry"
6
+
7
+ Pry.start
data/bin/loc ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ cloc --exclude-dir=node_modules,test --include-ext=rb,js .
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/bin/standardize ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bundle exec magic_frozen_string_literal
4
+ bundle exec standardrb --fix
@@ -1,11 +1,32 @@
1
- $:.unshift(File.dirname(__FILE__)) unless
2
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/engine"
4
+ require "active_support/all"
5
+ require "singleton"
6
+ require "concurrent-edge"
7
+ require "chrono_trigger/helpers/mitf"
8
+ require "chrono_trigger/helpers/now"
9
+ require "chrono_trigger/clock"
10
+ require "chrono_trigger/config"
11
+ require "chrono_trigger/event"
12
+ require "chrono_trigger/schedule"
13
+ require "chrono_trigger/timeline"
14
+ require "chrono_trigger/version"
15
+ require "chrono_trigger/worker"
3
16
 
4
17
  module ChronoTrigger
5
- VERSION = '0.2.0'
6
- end
18
+ class Engine < Rails::Engine
19
+ end
7
20
 
8
- require "active_support/all"
9
- require "chrono_trigger/shell"
10
- require "chrono_trigger/trigger"
11
- require "chrono_trigger/cron_entry"
21
+ def self.config
22
+ ChronoTrigger::Config.instance
23
+ end
24
+
25
+ def self.schedule
26
+ ChronoTrigger::Schedule.instance
27
+ end
28
+
29
+ def self.configure
30
+ yield config
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ChronoTrigger
4
+ class Clock
5
+ class << self
6
+ attr_reader :status, :ticks
7
+
8
+ def init
9
+ @ticks = 0
10
+ @status ||= :stopped
11
+ end
12
+
13
+ def start
14
+ init
15
+ if stopped?
16
+ last_tick = Time.zone.now
17
+ Rails.logger.info "ChronoTrigger: Clock started with a #{ChronoTrigger.config.interval}s interval"
18
+ ChronoTrigger.schedule.refresh
19
+ task = Concurrent::TimerTask.new(execution_interval: ChronoTrigger.config.interval) do |task|
20
+ if Time.zone.now - last_tick >= 1
21
+ last_tick += 1
22
+ @ticks += 1
23
+ ChronoTrigger.schedule.process_events
24
+ end
25
+ task.shutdown if stopped?
26
+ end
27
+ task.execute
28
+ end
29
+
30
+ @status = :started
31
+ end
32
+
33
+ def stop
34
+ if ticking?
35
+ Rails.logger.info "ChronoTrigger: Timer stopped"
36
+ @status = :stopped
37
+ end
38
+ @status
39
+ end
40
+
41
+ def ticking?
42
+ @status == :started
43
+ end
44
+
45
+ def stopped?
46
+ @status == :stopped
47
+ end
48
+ end
49
+ end
50
+ end