alarmable 0.1.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.
- checksums.yaml +7 -0
- data/.editorconfig +30 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.rubocop.yml +27 -0
- data/.travis.yml +25 -0
- data/Appraisals +23 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/Makefile +96 -0
- data/README.md +121 -0
- data/Rakefile +8 -0
- data/alarmable.gemspec +40 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +21 -0
- data/gemfiles/rails_4.gemfile +9 -0
- data/gemfiles/rails_5.0.gemfile +9 -0
- data/gemfiles/rails_5.1.gemfile +9 -0
- data/gemfiles/rails_5.2.gemfile +9 -0
- data/lib/alarmable.rb +10 -0
- data/lib/alarmable/concern.rb +205 -0
- data/lib/alarmable/version.rb +5 -0
- metadata +229 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 81a2cd3c46bf9ce21ece16a46bdf2ccf1f78e38e
|
|
4
|
+
data.tar.gz: 9ebb03c91daec2ae494f8305268bd0ebeb3e7c60
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6307267d20f76d4b0b19cd119d5e986b55f830ad9bb6b50d5371a06e9b1594d704f60f77417f5352856b0903bebe657a8584af72f31287ab2c61c001531b7ba5
|
|
7
|
+
data.tar.gz: 9d58b4e684b1dc04a5b976cbb2dda175f07509c1ca1eb0725e85e65e067cfca3f609768be726933983253ffa6b129f962cc5f82eacd24ecb09c8b8cdda361f25
|
data/.editorconfig
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# http://editorconfig.org
|
|
2
|
+
root = true
|
|
3
|
+
|
|
4
|
+
[*]
|
|
5
|
+
indent_style = space
|
|
6
|
+
indent_size = 2
|
|
7
|
+
end_of_line = lf
|
|
8
|
+
charset = utf-8
|
|
9
|
+
trim_trailing_whitespace = true
|
|
10
|
+
insert_final_newline = true
|
|
11
|
+
|
|
12
|
+
[*.md]
|
|
13
|
+
trim_trailing_whitespace = true
|
|
14
|
+
|
|
15
|
+
[*.json]
|
|
16
|
+
indent_style = space
|
|
17
|
+
indent_size = 2
|
|
18
|
+
|
|
19
|
+
[*.yml]
|
|
20
|
+
indent_style = space
|
|
21
|
+
indent_size = 2
|
|
22
|
+
|
|
23
|
+
[Makefile]
|
|
24
|
+
trim_trailing_whitespace = true
|
|
25
|
+
indent_style = tab
|
|
26
|
+
indent_size = 4
|
|
27
|
+
|
|
28
|
+
[*.sh]
|
|
29
|
+
indent_style = space
|
|
30
|
+
indent_size = 2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require: rubocop-rspec
|
|
2
|
+
|
|
3
|
+
Rails:
|
|
4
|
+
Enabled: true
|
|
5
|
+
|
|
6
|
+
Documentation:
|
|
7
|
+
Enabled: true
|
|
8
|
+
|
|
9
|
+
AllCops:
|
|
10
|
+
DisplayCopNames: true
|
|
11
|
+
TargetRubyVersion: 2.3
|
|
12
|
+
|
|
13
|
+
Metrics/BlockLength:
|
|
14
|
+
Exclude:
|
|
15
|
+
- Rakefile
|
|
16
|
+
- spec/**/*.rb
|
|
17
|
+
- '**/*.rake'
|
|
18
|
+
|
|
19
|
+
# Document all the things.
|
|
20
|
+
Style/DocumentationMethod:
|
|
21
|
+
Enabled: true
|
|
22
|
+
RequireForNonPublicMethods: true
|
|
23
|
+
|
|
24
|
+
# Because +expect_any_instance_of().to have_received()+ is not
|
|
25
|
+
# supported with the +with(hash_including)+ matchers
|
|
26
|
+
RSpec/MessageSpies:
|
|
27
|
+
EnforcedStyle: receive
|
data/.travis.yml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
sudo: false
|
|
2
|
+
|
|
3
|
+
addons:
|
|
4
|
+
postgresql: "9.6"
|
|
5
|
+
|
|
6
|
+
services:
|
|
7
|
+
- postgresql
|
|
8
|
+
|
|
9
|
+
language: ruby
|
|
10
|
+
rvm:
|
|
11
|
+
- 2.4
|
|
12
|
+
- 2.3
|
|
13
|
+
- 2.2
|
|
14
|
+
|
|
15
|
+
gemfile:
|
|
16
|
+
- gemfiles/rails_4.gemfile
|
|
17
|
+
- gemfiles/rails_5.0.gemfile
|
|
18
|
+
- gemfiles/rails_5.1.gemfile
|
|
19
|
+
- gemfiles/rails_5.2.gemfile
|
|
20
|
+
|
|
21
|
+
before_install:
|
|
22
|
+
- gem install bundler
|
|
23
|
+
- psql -c 'create database alarmable;' -U postgres
|
|
24
|
+
|
|
25
|
+
script: bundle exec rake
|
data/Appraisals
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
appraise 'rails-4' do
|
|
2
|
+
gem 'activejob', '4.2.10'
|
|
3
|
+
gem 'activerecord', '4.2.10'
|
|
4
|
+
gem 'activesupport', '4.2.10'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
appraise 'rails-5.0' do
|
|
8
|
+
gem 'activejob', '5.0.6'
|
|
9
|
+
gem 'activerecord', '5.0.6'
|
|
10
|
+
gem 'activesupport', '5.0.6'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
appraise 'rails-5.1' do
|
|
14
|
+
gem 'activejob', '5.1.4'
|
|
15
|
+
gem 'activerecord', '5.1.4'
|
|
16
|
+
gem 'activesupport', '5.1.4'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
appraise 'rails-5.2' do
|
|
20
|
+
gem 'activejob', '5.2.0.beta2'
|
|
21
|
+
gem 'activerecord', '5.2.0.beta2'
|
|
22
|
+
gem 'activesupport', '5.2.0.beta2'
|
|
23
|
+
end
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
|
10
|
+
orientation.
|
|
11
|
+
|
|
12
|
+
## Our Standards
|
|
13
|
+
|
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
|
15
|
+
include:
|
|
16
|
+
|
|
17
|
+
* Using welcoming and inclusive language
|
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
|
19
|
+
* Gracefully accepting constructive criticism
|
|
20
|
+
* Focusing on what is best for the community
|
|
21
|
+
* Showing empathy towards other community members
|
|
22
|
+
|
|
23
|
+
Examples of unacceptable behavior by participants include:
|
|
24
|
+
|
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
26
|
+
advances
|
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
28
|
+
* Public or private harassment
|
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
|
30
|
+
address, without explicit permission
|
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
32
|
+
professional setting
|
|
33
|
+
|
|
34
|
+
## Our Responsibilities
|
|
35
|
+
|
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
|
38
|
+
response to any instances of unacceptable behavior.
|
|
39
|
+
|
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
|
44
|
+
threatening, offensive, or harmful.
|
|
45
|
+
|
|
46
|
+
## Scope
|
|
47
|
+
|
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
|
49
|
+
when an individual is representing the project or its community. Examples of
|
|
50
|
+
representing a project or community include using an official project e-mail
|
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
|
53
|
+
further defined and clarified by project maintainers.
|
|
54
|
+
|
|
55
|
+
## Enforcement
|
|
56
|
+
|
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
58
|
+
reported by contacting the project team at hermann.mayer92@gmail.com. All
|
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
|
63
|
+
|
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
|
66
|
+
members of the project's leadership.
|
|
67
|
+
|
|
68
|
+
## Attribution
|
|
69
|
+
|
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
|
72
|
+
|
|
73
|
+
[homepage]: http://contributor-covenant.org
|
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 Hausgold
|
|
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/Makefile
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
MAKEFLAGS += --warn-undefined-variables -j1
|
|
2
|
+
SHELL := bash
|
|
3
|
+
.SHELLFLAGS := -eu -o pipefail -c
|
|
4
|
+
.DEFAULT_GOAL := all
|
|
5
|
+
.DELETE_ON_ERROR:
|
|
6
|
+
.SUFFIXES:
|
|
7
|
+
.PHONY:
|
|
8
|
+
|
|
9
|
+
# Environment switches
|
|
10
|
+
MAKE_ENV ?= docker
|
|
11
|
+
COMPOSE_RUN_SHELL_FLAGS ?= --rm
|
|
12
|
+
|
|
13
|
+
# Directories
|
|
14
|
+
VENDOR_DIR ?= vendor/bundle
|
|
15
|
+
GEMFILES_DIR ?= gemfiles
|
|
16
|
+
|
|
17
|
+
# Host binaries
|
|
18
|
+
BASH ?= bash
|
|
19
|
+
COMPOSE ?= docker-compose
|
|
20
|
+
ID ?= id
|
|
21
|
+
MKDIR ?= mkdir
|
|
22
|
+
RM ?= rm
|
|
23
|
+
|
|
24
|
+
# Container binaries
|
|
25
|
+
BUNDLE ?= bundle
|
|
26
|
+
APPRAISAL ?= appraisal
|
|
27
|
+
RAKE ?= rake
|
|
28
|
+
|
|
29
|
+
# Files
|
|
30
|
+
GEMFILES ?= $(subst _,-,$(patsubst $(GEMFILES_DIR)/%.gemfile,%,\
|
|
31
|
+
$(wildcard $(GEMFILES_DIR)/*.gemfile)))
|
|
32
|
+
TEST_GEMFILES := $(GEMFILES:%=test-%)
|
|
33
|
+
|
|
34
|
+
# Define a generic shell run wrapper
|
|
35
|
+
# $1 - The command to run
|
|
36
|
+
ifeq ($(MAKE_ENV),docker)
|
|
37
|
+
define run-shell
|
|
38
|
+
$(COMPOSE) run $(COMPOSE_RUN_SHELL_FLAGS) \
|
|
39
|
+
-e LANG=en_US.UTF-8 -e LANGUAGE=en_US.UTF-8 -e LC_ALL=en_US.UTF-8 \
|
|
40
|
+
-e HOME=/tmp -e BUNDLE_APP_CONFIG=/app/.bundle \
|
|
41
|
+
-u `$(ID) -u` test bash -c 'sleep 0.1; echo; $(1)'
|
|
42
|
+
endef
|
|
43
|
+
else ifeq ($(MAKE_ENV),baremetal)
|
|
44
|
+
define run-shell
|
|
45
|
+
$(1)
|
|
46
|
+
endef
|
|
47
|
+
endif
|
|
48
|
+
|
|
49
|
+
all:
|
|
50
|
+
# Alarmable
|
|
51
|
+
#
|
|
52
|
+
# install Install the dependencies
|
|
53
|
+
# test Run the whole test suite
|
|
54
|
+
# clean Clean the dependencies
|
|
55
|
+
#
|
|
56
|
+
# shell Run an interactive shell on the container
|
|
57
|
+
# shell-irb Run an interactive IRB shell on the container
|
|
58
|
+
|
|
59
|
+
install:
|
|
60
|
+
# Install the dependencies
|
|
61
|
+
@$(MKDIR) -p $(VENDOR_DIR)
|
|
62
|
+
@$(call run-shell,$(BUNDLE) check || $(BUNDLE) install --path $(VENDOR_DIR))
|
|
63
|
+
@$(call run-shell,$(BUNDLE) exec $(APPRAISAL) install)
|
|
64
|
+
|
|
65
|
+
test: install
|
|
66
|
+
# Run the whole test suite
|
|
67
|
+
@$(call run-shell,$(BUNDLE) exec $(RAKE))
|
|
68
|
+
|
|
69
|
+
$(TEST_GEMFILES): GEMFILE=$(@:test-%=%)
|
|
70
|
+
$(TEST_GEMFILES):
|
|
71
|
+
# Run the whole test suite ($(GEMFILE))
|
|
72
|
+
@$(call run-shell,$(BUNDLE) exec $(APPRAISAL) $(GEMFILE) $(RAKE))
|
|
73
|
+
|
|
74
|
+
clean:
|
|
75
|
+
# Clean the dependencies
|
|
76
|
+
@$(RM) -rf $(VENDOR_DIR)
|
|
77
|
+
|
|
78
|
+
clean-containers:
|
|
79
|
+
# Clean running containers
|
|
80
|
+
ifeq ($(MAKE_ENV),docker)
|
|
81
|
+
@$(COMPOSE) down
|
|
82
|
+
endif
|
|
83
|
+
|
|
84
|
+
distclean: clean clean-containers
|
|
85
|
+
|
|
86
|
+
shell: install
|
|
87
|
+
# Run an interactive shell on the container
|
|
88
|
+
@$(call run-shell,$(BASH) -i)
|
|
89
|
+
|
|
90
|
+
shell-irb: install
|
|
91
|
+
# Run an interactive IRB shell on the container
|
|
92
|
+
@$(call run-shell,bin/console)
|
|
93
|
+
|
|
94
|
+
release:
|
|
95
|
+
# Release a new gem version
|
|
96
|
+
@$(RAKE) release
|
data/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.org/hausgold/alarmable)
|
|
4
|
+
|
|
5
|
+
This is a reusable alarm concern for Active Record models. It adds support for
|
|
6
|
+
the automatic maintenance of Active Job's which are scheduled for the given
|
|
7
|
+
alarms. On alarm updates the jobs will be canceled and rescheduled. This is
|
|
8
|
+
supported only for Sidekiq, Delayed Job, resque and the Active Job TestAdapter.
|
|
9
|
+
(See [ActiveJob::Cancel](https://github.com/y-yagi/activejob-cancel) for the
|
|
10
|
+
list of supported adapters)
|
|
11
|
+
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Usage](#usage)
|
|
14
|
+
- [Database migration](#database-migration)
|
|
15
|
+
- [Active Record Model](#active-record-model)
|
|
16
|
+
- [Active Job](#active-job)
|
|
17
|
+
- [Development](#development)
|
|
18
|
+
- [Contributing](#contributing)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Add this line to your application's Gemfile:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
gem 'alarmable'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
And then execute:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
$ bundle
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or install it yourself as:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
$ gem install alarmable
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
### Database migration
|
|
43
|
+
|
|
44
|
+
This concern requires the persistence (and availability) of two properties.
|
|
45
|
+
|
|
46
|
+
* The first is the JSONB array which holds the alarms. (`alarms`)
|
|
47
|
+
* The seconds is the JSONB array which holds the ids of the
|
|
48
|
+
scheduled alarm jobs. (`alarm_jobs`)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
$ rails generate migration AddAlarmsAndAlarmJobsToEntity \
|
|
52
|
+
alarms:jsonb alarm_jobs:jsonb
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Active Record Model
|
|
56
|
+
|
|
57
|
+
Furthermore a Active Record model which uses this concern must define the
|
|
58
|
+
Active Job class which will be scheduled. (`alarm_job`) The user must also
|
|
59
|
+
define the base date property of the owning side.
|
|
60
|
+
(`alarm_base_date_property`) This base date is mandatory to calculate the
|
|
61
|
+
correct alarm date/time. When the base date is not set (`nil`) no new
|
|
62
|
+
notification job will be enqueued. When the base date is unset on an update,
|
|
63
|
+
the previously enqueued job will be canceled.
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
# Your Active Record Model
|
|
67
|
+
class Entity < ApplicationRecord
|
|
68
|
+
include Alarmable
|
|
69
|
+
self.alarm_job = NotificationJob
|
|
70
|
+
self.alarm_base_date_property = :start_at
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The alarms hash needs to be an array in the following format:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
[
|
|
78
|
+
{
|
|
79
|
+
"channel": "email", # email, push, web_notification, etc..
|
|
80
|
+
"before_minutes": 15 # start_at - before_minutes, >= 1
|
|
81
|
+
|
|
82
|
+
# [..] you can add custom properties if you like
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Active Job
|
|
88
|
+
|
|
89
|
+
The given alarm job class will be scheduled with the following two arguments.
|
|
90
|
+
|
|
91
|
+
* id - The class/instance id of the record which owns the alarm
|
|
92
|
+
* alarm - The alarm hash itself (see the format above)
|
|
93
|
+
|
|
94
|
+
A suitable alarm job perform method should look like this:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
# Your notification job
|
|
98
|
+
class NotificationJob < ApplicationJob
|
|
99
|
+
# @param id [String] The entity id
|
|
100
|
+
# @param alarm [Hash] The alarm object
|
|
101
|
+
def perform(id, alarm)
|
|
102
|
+
# Do something special for `alarm.channel` ..
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
After checking out the repo, run `make install` to install dependencies. Then,
|
|
110
|
+
run `make test` to run the tests. You can also run `make shell-irb` for an
|
|
111
|
+
interactive prompt that will allow you to experiment.
|
|
112
|
+
|
|
113
|
+
To release a new version, update the version number in `version.rb`, and then
|
|
114
|
+
run `make release`, which will create a git tag for the version, push git
|
|
115
|
+
commits and tags, and push the `.gem` file to
|
|
116
|
+
[rubygems.org](https://rubygems.org).
|
|
117
|
+
|
|
118
|
+
## Contributing
|
|
119
|
+
|
|
120
|
+
Bug reports and pull requests are welcome on GitHub at
|
|
121
|
+
https://github.com/hausgold/alarmable.
|
data/Rakefile
ADDED
data/alarmable.gemspec
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'alarmable/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'alarmable'
|
|
9
|
+
spec.version = Alarmable::VERSION
|
|
10
|
+
spec.authors = ['Hermann Mayer']
|
|
11
|
+
spec.email = ['hermann.mayer@hausgold.de']
|
|
12
|
+
|
|
13
|
+
spec.summary = 'A reusable alarm extension to Active Record models'
|
|
14
|
+
spec.description = 'This is a reusable alarm concern for Active Record' \
|
|
15
|
+
'models. It adds support for the automatic maintenance' \
|
|
16
|
+
'of Active Job\'s which are scheduled for the given' \
|
|
17
|
+
'alarms.'
|
|
18
|
+
|
|
19
|
+
spec.homepage = 'https://github.com/hausgold/alarmable'
|
|
20
|
+
spec.license = 'MIT'
|
|
21
|
+
|
|
22
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
23
|
+
f.match(%r{^(test|spec|features)/})
|
|
24
|
+
end
|
|
25
|
+
spec.bindir = 'exe'
|
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
|
|
29
|
+
spec.add_dependency 'activejob', '>=3', '<6'
|
|
30
|
+
spec.add_dependency 'activejob-cancel', '~> 0.3'
|
|
31
|
+
spec.add_dependency 'activerecord', '>=3', '<6'
|
|
32
|
+
spec.add_dependency 'activesupport', '>=3', '<6'
|
|
33
|
+
spec.add_dependency 'hashdiff', '~> 0.3.7'
|
|
34
|
+
|
|
35
|
+
spec.add_development_dependency 'appraisal'
|
|
36
|
+
spec.add_development_dependency 'bundler', '~> 1.15'
|
|
37
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
38
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
39
|
+
spec.add_development_dependency 'pg', '~> 0.18'
|
|
40
|
+
end
|
data/bin/console
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'alarmable'
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
+
# require "pry"
|
|
12
|
+
# Pry.start
|
|
13
|
+
|
|
14
|
+
require 'irb'
|
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/docker-compose.yml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
version: "3"
|
|
2
|
+
services:
|
|
3
|
+
db:
|
|
4
|
+
image: postgres:9.6
|
|
5
|
+
network_mode: bridge
|
|
6
|
+
ports: ["5432"]
|
|
7
|
+
volumes:
|
|
8
|
+
- .:/app
|
|
9
|
+
environment:
|
|
10
|
+
POSTGRES_USER: postgres
|
|
11
|
+
POSTGRES_PASSWORD: postgres
|
|
12
|
+
POSTGRES_DB: alarmable
|
|
13
|
+
|
|
14
|
+
test:
|
|
15
|
+
image: ruby:2.3
|
|
16
|
+
network_mode: bridge
|
|
17
|
+
working_dir: /app
|
|
18
|
+
volumes:
|
|
19
|
+
- .:/app
|
|
20
|
+
links:
|
|
21
|
+
- db
|
data/lib/alarmable.rb
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A reusable alarm extension to Active Record models. It adds support for the
|
|
4
|
+
# maintenance of Active Job's (create, update (cancel)) which are schedules for
|
|
5
|
+
# the given alarms. We check for changes on the alarms hash and perform
|
|
6
|
+
# updates accordingly.
|
|
7
|
+
#
|
|
8
|
+
# This concern requires the persistence (and availability) of two properties.
|
|
9
|
+
#
|
|
10
|
+
# * The first is the JSONB array which holds the alarms. (+alarms+)
|
|
11
|
+
# * The seconds is the JSONB array which holds the ids of
|
|
12
|
+
# scheduled alarm jobs. (+alarm_jobs+)
|
|
13
|
+
#
|
|
14
|
+
# rails generate migration AddAlarmsAndAlarmJobsToEntity \
|
|
15
|
+
# alarms:jsonb alarm_jobs:jsonb
|
|
16
|
+
#
|
|
17
|
+
# Furthermore a Active Record model which uses this concern must define the
|
|
18
|
+
# Active Job class which will be scheduled. (+alarm_job+) The user must also
|
|
19
|
+
# define the base date property of the owning side.
|
|
20
|
+
# (+alarm_base_date_property+) This base date is mandatory to calculate the
|
|
21
|
+
# correct alarm date/time. When the base date is not set (+nil+) no new
|
|
22
|
+
# notification job will be enqueued. When the base date is unset on an update,
|
|
23
|
+
# the previously enqueued job will be canceled.
|
|
24
|
+
#
|
|
25
|
+
# The alarms hash needs to be an array in the following format:
|
|
26
|
+
#
|
|
27
|
+
# [
|
|
28
|
+
# {
|
|
29
|
+
# "channel": "email", # email, push, web_notification, etc..
|
|
30
|
+
# "before_minutes": 15 # start_at - before_minutes, >= 1
|
|
31
|
+
# }
|
|
32
|
+
# ]
|
|
33
|
+
#
|
|
34
|
+
# The given alarm job class will be scheduled with the following two arguments.
|
|
35
|
+
#
|
|
36
|
+
# * id - The class/instance id of the record which owns the alarm
|
|
37
|
+
# * alarm - The alarm hash itself (see the format above)
|
|
38
|
+
#
|
|
39
|
+
# A suitable alarm job perform method should look like this:
|
|
40
|
+
#
|
|
41
|
+
# # @param id [String] The entity id
|
|
42
|
+
# # @param alarm [Hash] The alarm object
|
|
43
|
+
# def perform(id, alarm)
|
|
44
|
+
# # Do something special for +alarm.channel+ ..
|
|
45
|
+
# end
|
|
46
|
+
module Alarmable
|
|
47
|
+
extend ActiveSupport::Concern
|
|
48
|
+
|
|
49
|
+
class_methods do
|
|
50
|
+
# Getter/Setter
|
|
51
|
+
#
|
|
52
|
+
# :reek:Attribute because thats what this thing is about
|
|
53
|
+
attr_accessor :alarm_job, :alarm_base_date_property
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# rubocop:disable Metrics/BlockLength because Active Support like it
|
|
57
|
+
included do
|
|
58
|
+
# Hooks
|
|
59
|
+
after_initialize :validate_alarm_settings, :alarm_defaults
|
|
60
|
+
|
|
61
|
+
# Here comes a little cheat sheet when and what action is performed
|
|
62
|
+
# on the alarm jobs.
|
|
63
|
+
#
|
|
64
|
+
# create | [ time check, reschedule]
|
|
65
|
+
# update | dirty check, [cancel job, time check, reschedule]
|
|
66
|
+
# destroy | [cancel job ]
|
|
67
|
+
after_create :reschedule_alarm_jobs
|
|
68
|
+
before_update :alarms_update_callback
|
|
69
|
+
before_destroy :alarms_destroy_callback
|
|
70
|
+
|
|
71
|
+
# Getter for the alarm job class.
|
|
72
|
+
#
|
|
73
|
+
# @return [Class] The alarm job class
|
|
74
|
+
def alarm_job
|
|
75
|
+
self.class.alarm_job
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Getter for the alarm base date property.
|
|
79
|
+
#
|
|
80
|
+
# @return [Symbol] The user defined base date property
|
|
81
|
+
def alarm_base_date_property
|
|
82
|
+
self.class.alarm_base_date_property
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Set some defaults on the relevant alarm properties.
|
|
86
|
+
def alarm_defaults
|
|
87
|
+
self.alarms ||= []
|
|
88
|
+
self.alarm_jobs ||= {}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Validate the presence of the +alarm_job+ property and the accessibility
|
|
92
|
+
# of the specified class. Also validate the +alarm_base_date_property+
|
|
93
|
+
# setting.
|
|
94
|
+
#
|
|
95
|
+
# rubocop:disable Style/GuardClause because its fine like this
|
|
96
|
+
# :reek:NilCheck because we validate concern usage
|
|
97
|
+
def validate_alarm_settings
|
|
98
|
+
raise 'Alarmable +alarm_job+ is not configured' if alarm_job.nil?
|
|
99
|
+
unless alarm_job.is_a? Class
|
|
100
|
+
raise 'Alarmable +alarm_job+ is not instantiable'
|
|
101
|
+
end
|
|
102
|
+
if alarm_base_date_property.nil?
|
|
103
|
+
raise 'Alarmable +alarm_base_date_property+ is not configured'
|
|
104
|
+
end
|
|
105
|
+
unless has_attribute? alarm_base_date_property
|
|
106
|
+
raise 'Alarmable +alarm_base_date_property+ is not usable'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
# rubocop:enable Style/GuardClause
|
|
110
|
+
|
|
111
|
+
# Generate a unique and recalculatable identifier for a given alarm object.
|
|
112
|
+
# We build a hash of the primary keys (before_minutes and channel) to
|
|
113
|
+
# achive this. Afterwards, this alarm id is used to reference dedicated
|
|
114
|
+
# scheduled jobs and track their updates. (Or cancel them accordingly)
|
|
115
|
+
#
|
|
116
|
+
# @param channel [String] The alarm channel
|
|
117
|
+
# @param before_minutes [Integer] The minutes before the alarm starts
|
|
118
|
+
# @return [String] The unique alarm id
|
|
119
|
+
#
|
|
120
|
+
# :reek:UtilityFunction because its a utility, for sure
|
|
121
|
+
def alarm_id(channel, before_minutes)
|
|
122
|
+
(Digest::MD5.new << "#{channel}#{before_minutes}").to_s
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Schedule a new Active Job for the alarm notification. This method takes
|
|
126
|
+
# care of the notification time (+date) and will not touch anything when
|
|
127
|
+
# the desired time already passed. It cancels the correct job for the
|
|
128
|
+
# given combination, when it is present. In the end it schedules a new
|
|
129
|
+
# (renewed) job for the given alarm settings.
|
|
130
|
+
#
|
|
131
|
+
# @param alarm [Hash] The alarm object
|
|
132
|
+
# @return [Object] The new alarm_jobs instance (partial)
|
|
133
|
+
# Example: { "alarm id": "job id" }
|
|
134
|
+
#
|
|
135
|
+
# rubocop:disable Metrics/AbcSize because its already broken down
|
|
136
|
+
# :reek:TooManyStatements because see above
|
|
137
|
+
# :reek:NilCheck because we dont want to cancel 'nil' job id
|
|
138
|
+
# :reek:DuplicateMethodCall because hash access is fast
|
|
139
|
+
def reschedule_alarm_job(alarm)
|
|
140
|
+
# Symbolize the hash keys (just to be sure).
|
|
141
|
+
alarm = alarm.symbolize_keys
|
|
142
|
+
|
|
143
|
+
# Calculate the alarm id for job canceling and cancel a found job.
|
|
144
|
+
id = alarm_id(alarm[:channel], alarm[:before_minutes])
|
|
145
|
+
previous_job_id = alarm_jobs.try(:[], id)
|
|
146
|
+
alarm_job.cancel(previous_job_id) unless previous_job_id.nil?
|
|
147
|
+
|
|
148
|
+
base_date = self[alarm_base_date_property]
|
|
149
|
+
|
|
150
|
+
# When the base date is not set, we schedule not a new notification job.
|
|
151
|
+
return {} if base_date.nil?
|
|
152
|
+
|
|
153
|
+
# Calculate the time when the job should run.
|
|
154
|
+
notify_at = base_date - alarm[:before_minutes].minutes
|
|
155
|
+
|
|
156
|
+
# Do nothing when the notification date already passed.
|
|
157
|
+
return {} if Time.current >= notify_at
|
|
158
|
+
|
|
159
|
+
# Put a new job to the queue with the new (current) job execution date.
|
|
160
|
+
job = alarm_job.set(wait_until: notify_at).perform_later(self.id, alarm)
|
|
161
|
+
|
|
162
|
+
# Construct a new alarm_jobs partial instance for this job
|
|
163
|
+
Hash[id, job.job_id]
|
|
164
|
+
end
|
|
165
|
+
# rubocop:enable Metrics/AbcSize
|
|
166
|
+
|
|
167
|
+
# Initiate a reschedule for each alarm in the alarm settings and
|
|
168
|
+
# cancel all left-overs.
|
|
169
|
+
#
|
|
170
|
+
# rubocop:disable Rails/SkipsModelValidations because we need to skip them
|
|
171
|
+
# :reek:TooManyStatements because its already broken down
|
|
172
|
+
def reschedule_alarm_jobs
|
|
173
|
+
# Perform the reschedule of all the current alarms.
|
|
174
|
+
new_alarm_jobs = alarms.each_with_object({}) do |alarm, memo|
|
|
175
|
+
memo.merge!(reschedule_alarm_job(alarm))
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Detect the differences from the original alarm_jobs hash to the new
|
|
179
|
+
# built (by partials) alarm_jobs hash. The jobs from negative differences
|
|
180
|
+
# must be canceled.
|
|
181
|
+
diff = HashDiff.diff(alarm_jobs, new_alarm_jobs)
|
|
182
|
+
|
|
183
|
+
diff.select { |prop| prop.first == '-' }.each do |prop|
|
|
184
|
+
alarm_job.cancel(prop.last)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Update the alarm_jobs reference pool with our fresh hash. Bypass the
|
|
188
|
+
# regular validation and callbacks here, this is required to not stuck in
|
|
189
|
+
# endless create-update loops.
|
|
190
|
+
update_columns(alarm_jobs: new_alarm_jobs)
|
|
191
|
+
end
|
|
192
|
+
# rubocop:enable Rails/SkipsModelValidations
|
|
193
|
+
|
|
194
|
+
# Reschedule only on updates when the alarm settings are changed.
|
|
195
|
+
def alarms_update_callback
|
|
196
|
+
reschedule_alarm_jobs if alarms_changed?
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Cancel all alarm notification jobs on parent destroy.
|
|
200
|
+
def alarms_destroy_callback
|
|
201
|
+
alarm_jobs.each_value { |job_id| alarm_job.cancel(job_id) }
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
# rubocop:enable Metrics/BlockLength
|
|
205
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: alarmable
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Hermann Mayer
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-01-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activejob
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '6'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '3'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '6'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: activejob-cancel
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.3'
|
|
40
|
+
type: :runtime
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.3'
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: activerecord
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3'
|
|
54
|
+
- - "<"
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '6'
|
|
57
|
+
type: :runtime
|
|
58
|
+
prerelease: false
|
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '3'
|
|
64
|
+
- - "<"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '6'
|
|
67
|
+
- !ruby/object:Gem::Dependency
|
|
68
|
+
name: activesupport
|
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '3'
|
|
74
|
+
- - "<"
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '6'
|
|
77
|
+
type: :runtime
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '3'
|
|
84
|
+
- - "<"
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '6'
|
|
87
|
+
- !ruby/object:Gem::Dependency
|
|
88
|
+
name: hashdiff
|
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - "~>"
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: 0.3.7
|
|
94
|
+
type: :runtime
|
|
95
|
+
prerelease: false
|
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - "~>"
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: 0.3.7
|
|
101
|
+
- !ruby/object:Gem::Dependency
|
|
102
|
+
name: appraisal
|
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '0'
|
|
108
|
+
type: :development
|
|
109
|
+
prerelease: false
|
|
110
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - ">="
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '0'
|
|
115
|
+
- !ruby/object:Gem::Dependency
|
|
116
|
+
name: bundler
|
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - "~>"
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '1.15'
|
|
122
|
+
type: :development
|
|
123
|
+
prerelease: false
|
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - "~>"
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '1.15'
|
|
129
|
+
- !ruby/object:Gem::Dependency
|
|
130
|
+
name: rake
|
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
|
132
|
+
requirements:
|
|
133
|
+
- - "~>"
|
|
134
|
+
- !ruby/object:Gem::Version
|
|
135
|
+
version: '10.0'
|
|
136
|
+
type: :development
|
|
137
|
+
prerelease: false
|
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - "~>"
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '10.0'
|
|
143
|
+
- !ruby/object:Gem::Dependency
|
|
144
|
+
name: rspec
|
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
|
146
|
+
requirements:
|
|
147
|
+
- - "~>"
|
|
148
|
+
- !ruby/object:Gem::Version
|
|
149
|
+
version: '3.0'
|
|
150
|
+
type: :development
|
|
151
|
+
prerelease: false
|
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
153
|
+
requirements:
|
|
154
|
+
- - "~>"
|
|
155
|
+
- !ruby/object:Gem::Version
|
|
156
|
+
version: '3.0'
|
|
157
|
+
- !ruby/object:Gem::Dependency
|
|
158
|
+
name: pg
|
|
159
|
+
requirement: !ruby/object:Gem::Requirement
|
|
160
|
+
requirements:
|
|
161
|
+
- - "~>"
|
|
162
|
+
- !ruby/object:Gem::Version
|
|
163
|
+
version: '0.18'
|
|
164
|
+
type: :development
|
|
165
|
+
prerelease: false
|
|
166
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
167
|
+
requirements:
|
|
168
|
+
- - "~>"
|
|
169
|
+
- !ruby/object:Gem::Version
|
|
170
|
+
version: '0.18'
|
|
171
|
+
description: This is a reusable alarm concern for Active Recordmodels. It adds support
|
|
172
|
+
for the automatic maintenanceof Active Job's which are scheduled for the givenalarms.
|
|
173
|
+
email:
|
|
174
|
+
- hermann.mayer@hausgold.de
|
|
175
|
+
executables: []
|
|
176
|
+
extensions: []
|
|
177
|
+
extra_rdoc_files: []
|
|
178
|
+
files:
|
|
179
|
+
- ".editorconfig"
|
|
180
|
+
- ".gitignore"
|
|
181
|
+
- ".rspec"
|
|
182
|
+
- ".rubocop.yml"
|
|
183
|
+
- ".travis.yml"
|
|
184
|
+
- Appraisals
|
|
185
|
+
- CODE_OF_CONDUCT.md
|
|
186
|
+
- Gemfile
|
|
187
|
+
- LICENSE.txt
|
|
188
|
+
- Makefile
|
|
189
|
+
- README.md
|
|
190
|
+
- Rakefile
|
|
191
|
+
- alarmable.gemspec
|
|
192
|
+
- bin/console
|
|
193
|
+
- bin/setup
|
|
194
|
+
- doc/assets/logo.png
|
|
195
|
+
- doc/assets/project.png
|
|
196
|
+
- doc/assets/project.xcf
|
|
197
|
+
- docker-compose.yml
|
|
198
|
+
- gemfiles/rails_4.gemfile
|
|
199
|
+
- gemfiles/rails_5.0.gemfile
|
|
200
|
+
- gemfiles/rails_5.1.gemfile
|
|
201
|
+
- gemfiles/rails_5.2.gemfile
|
|
202
|
+
- lib/alarmable.rb
|
|
203
|
+
- lib/alarmable/concern.rb
|
|
204
|
+
- lib/alarmable/version.rb
|
|
205
|
+
homepage: https://github.com/hausgold/alarmable
|
|
206
|
+
licenses:
|
|
207
|
+
- MIT
|
|
208
|
+
metadata: {}
|
|
209
|
+
post_install_message:
|
|
210
|
+
rdoc_options: []
|
|
211
|
+
require_paths:
|
|
212
|
+
- lib
|
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
214
|
+
requirements:
|
|
215
|
+
- - ">="
|
|
216
|
+
- !ruby/object:Gem::Version
|
|
217
|
+
version: '0'
|
|
218
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
219
|
+
requirements:
|
|
220
|
+
- - ">="
|
|
221
|
+
- !ruby/object:Gem::Version
|
|
222
|
+
version: '0'
|
|
223
|
+
requirements: []
|
|
224
|
+
rubyforge_project:
|
|
225
|
+
rubygems_version: 2.6.14
|
|
226
|
+
signing_key:
|
|
227
|
+
specification_version: 4
|
|
228
|
+
summary: A reusable alarm extension to Active Record models
|
|
229
|
+
test_files: []
|