redis_monitor 0.2.1 → 0.3
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 +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +1 -1
- data/README.md +7 -9
- data/Rakefile +4 -0
- data/lib/engine/app/controllers/content_controller.rb +1 -1
- data/lib/engine/app/controllers/notifications_controller.rb +19 -0
- data/lib/engine/app/controllers/tasks_controller.rb +47 -0
- data/lib/engine/app/helpers/application_helper.rb +13 -3
- data/lib/engine/app/lib/commands/retrieve_key.rb +5 -0
- data/lib/engine/app/lib/commands/{search_key.rb → search_keys.rb} +1 -1
- data/lib/engine/app/lib/jobs/background_task_job.rb +42 -0
- data/lib/engine/app/models/notification.rb +7 -0
- data/lib/engine/app/models/task.rb +34 -0
- data/lib/engine/app/models/watch_key_task.rb +22 -0
- data/lib/engine/app/models/watch_query_task.rb +24 -0
- data/lib/engine/app/views/content/_search_form.haml +2 -2
- data/lib/engine/app/views/layouts/application.haml +1 -0
- data/lib/engine/app/views/notifications/_notification.haml +9 -0
- data/lib/engine/app/views/notifications/index.haml +14 -0
- data/lib/engine/app/views/tasks/_form.haml +32 -0
- data/lib/engine/app/views/tasks/_task.haml +14 -0
- data/lib/engine/app/views/tasks/edit.haml +3 -0
- data/lib/engine/app/views/tasks/index.haml +21 -0
- data/lib/engine/app/views/tasks/new.haml +3 -0
- data/lib/engine/bin/delayed_job +5 -0
- data/lib/engine/config/database.yml +2 -2
- data/lib/engine/config/initializers/delayed_job.rb +3 -0
- data/lib/engine/config/routes.rb +3 -0
- data/lib/engine/db/migrate/20140112162911_create_tasks.rb +15 -0
- data/lib/engine/db/migrate/20140118155310_create_delayed_jobs.rb +22 -0
- data/lib/engine/db/migrate/20140118183403_create_notifications.rb +10 -0
- data/lib/engine/db/schema.rb +51 -0
- data/lib/engine/public/javascripts/jquery_ujs.js +394 -0
- data/lib/engine/public/stylesheets/custom.css +4 -0
- data/lib/engine/spec/controllers/content_controller_spec.rb +1 -1
- data/lib/engine/spec/controllers/notifications_controller_spec.rb +39 -0
- data/lib/engine/spec/controllers/tasks_controller_spec.rb +109 -0
- data/lib/engine/spec/lib/commands/retrieve_key_spec.rb +15 -0
- data/lib/engine/spec/lib/commands/search_key_spec.rb +2 -2
- data/lib/engine/spec/lib/jobs/background_task_job_spec.rb +55 -0
- data/lib/engine/spec/models/notification_spec.rb +9 -0
- data/lib/engine/spec/models/task_spec.rb +23 -0
- data/lib/engine/spec/models/watch_key_task_spec.rb +13 -0
- data/lib/engine/spec/models/watch_query_task_spec.rb +13 -0
- data/lib/redis_monitor.rb +7 -1
- data/lib/version.rb +1 -1
- data/redis_monitor.gemspec +2 -0
- metadata +61 -10
- data/lib/engine/test/controllers/.keep +0 -0
- data/lib/engine/test/fixtures/.keep +0 -0
- data/lib/engine/test/helpers/.keep +0 -0
- data/lib/engine/test/integration/.keep +0 -0
- data/lib/engine/test/mailers/.keep +0 -0
- data/lib/engine/test/models/.keep +0 -0
- data/lib/engine/test/test_helper.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b306ec30c262af4c5130f654efee0356f2652ff8
|
4
|
+
data.tar.gz: 7ee38ccac2e96471493eeb7349876395786bb43f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95da81fb9d481224903eb4f6a25aa49c28b2f283e3aa0747be13dc9bb73bdffb2c75eb3a4a68e6e70464ea0e969525756ad9c2c2bf24a7d73d3384d1e23a150b
|
7
|
+
data.tar.gz: fc1a93aef1fb52ab9e8ae2a3c8eeb465f4ba61a52811b702e18f0394e2415ce0a29b967c871c3093428435c31b40640b6c0cf57107eb3a513fea18e2a0533a18
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -3,15 +3,7 @@
|
|
3
3
|
|
4
4
|
## Installation
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
gem 'redis_monitor'
|
9
|
-
|
10
|
-
And then execute:
|
11
|
-
|
12
|
-
$ bundle
|
13
|
-
|
14
|
-
Or install it yourself as:
|
6
|
+
Execute:
|
15
7
|
|
16
8
|
$ gem install redis_monitor
|
17
9
|
|
@@ -29,6 +21,12 @@ For more information about the parameters:
|
|
29
21
|
|
30
22
|
$ redis_monitor --help
|
31
23
|
|
24
|
+
You can define tasks in 'Task' tab and they will be running in background. There is two type of task availables at the moment:
|
25
|
+
- Watch key, it will watch key introduced in 'trigger' field and it will create notifications if the value change.
|
26
|
+
|
27
|
+
- Watch query, it will watch the query introduced in 'trigger' field and in the result of the query changes it will create a notification.
|
28
|
+
Be careful with this one, don't watch queries too generic because it can affect to the performance of your database.
|
29
|
+
|
32
30
|
## Contributing
|
33
31
|
|
34
32
|
1. Fork it
|
data/Rakefile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rspec/core/rake_task'
|
3
|
+
require File.expand_path('../lib/engine/config/application', __FILE__)
|
4
|
+
|
5
|
+
Engine::Application.load_tasks
|
3
6
|
|
4
7
|
RSpec::Core::RakeTask.new(:spec) do |config|
|
5
8
|
config.rspec_opts = '--default-path lib/engine/spec'
|
6
9
|
end
|
10
|
+
|
@@ -8,7 +8,7 @@ class ContentController < ApplicationController
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def search
|
11
|
-
results =
|
11
|
+
results = SearchKeys.new(backend, params[:key]).result
|
12
12
|
@results = results.paginate(:page => params[:page], :per_page => 20)
|
13
13
|
end
|
14
14
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class NotificationsController < ApplicationController
|
2
|
+
skip_before_action :verify_authenticity_token
|
3
|
+
before_action :load_section
|
4
|
+
|
5
|
+
def index
|
6
|
+
@notifications = Notification.all
|
7
|
+
end
|
8
|
+
|
9
|
+
def destroy
|
10
|
+
notification = Notification.find(params[:id])
|
11
|
+
notification.destroy
|
12
|
+
redirect_to action: :index
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def load_section
|
17
|
+
@section = 'notifications'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class TasksController < ApplicationController
|
2
|
+
skip_before_action :verify_authenticity_token
|
3
|
+
before_action :load_section
|
4
|
+
after_action :reset_queue, only: [:create, :update, :destroy]
|
5
|
+
|
6
|
+
def index
|
7
|
+
@tasks = Task.all
|
8
|
+
end
|
9
|
+
|
10
|
+
def new
|
11
|
+
@task = Task.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def edit
|
15
|
+
@task = Task.find(params[:id])
|
16
|
+
end
|
17
|
+
|
18
|
+
def create
|
19
|
+
@task = Task.create(task_params)
|
20
|
+
redirect_to action: :index
|
21
|
+
end
|
22
|
+
|
23
|
+
def update
|
24
|
+
@task = Task.find(params[:id])
|
25
|
+
@task.update_attributes(task_params)
|
26
|
+
redirect_to action: :index
|
27
|
+
end
|
28
|
+
|
29
|
+
def destroy
|
30
|
+
@task = Task.find(params[:id])
|
31
|
+
@task.destroy
|
32
|
+
redirect_to action: :index
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def load_section
|
37
|
+
@section = 'tasks'
|
38
|
+
end
|
39
|
+
|
40
|
+
def task_params
|
41
|
+
params.require(:task).permit(:name, :type, :trigger, :database, :every, :status)
|
42
|
+
end
|
43
|
+
|
44
|
+
def reset_queue
|
45
|
+
@task.reset_queue
|
46
|
+
end
|
47
|
+
end
|
@@ -12,10 +12,20 @@ module ApplicationHelper
|
|
12
12
|
def content_menu(selected_section)
|
13
13
|
capture_haml do
|
14
14
|
haml_tag :ul, class: 'nav navbar-nav' do
|
15
|
-
section(name: 'info', title: 'Info', url:
|
16
|
-
section(name: 'content', title: 'Content', url:
|
17
|
-
section(name: '
|
15
|
+
section(name: 'info', title: 'Info', url: info_index_path, selected_section: selected_section)
|
16
|
+
section(name: 'content', title: 'Content', url: content_index_path, selected_section: selected_section)
|
17
|
+
section(name: 'tasks', title: 'Tasks', url: tasks_path, selected_section: selected_section)
|
18
|
+
section(name: 'notifications', title: 'Notifications', url: notifications_path, selected_section: selected_section)
|
19
|
+
section(name: 'performance', title: 'Performance', url: performance_index_path, selected_section: selected_section)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
23
|
+
|
24
|
+
def remove_class
|
25
|
+
'btn btn-danger'
|
26
|
+
end
|
27
|
+
|
28
|
+
def edit_class
|
29
|
+
'btn btn-primary'
|
30
|
+
end
|
21
31
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class BackgroundTaskJob < Struct.new(:task)
|
2
|
+
def queue_name
|
3
|
+
"task_#{task.id}"
|
4
|
+
end
|
5
|
+
|
6
|
+
def run_at
|
7
|
+
task.every.seconds.from_now
|
8
|
+
end
|
9
|
+
|
10
|
+
def clear_queue
|
11
|
+
Delayed::Job.where(queue: queue_name).destroy_all
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_to_queue
|
15
|
+
if task.active?
|
16
|
+
Delayed::Job.enqueue(self, queue: queue_name, run_at: run_at)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset_queue
|
21
|
+
clear_queue
|
22
|
+
add_to_queue
|
23
|
+
end
|
24
|
+
|
25
|
+
def perform
|
26
|
+
task.perform
|
27
|
+
end
|
28
|
+
|
29
|
+
def refresh_data
|
30
|
+
self.task = Task.where(id: task.id).first
|
31
|
+
end
|
32
|
+
|
33
|
+
def after(job)
|
34
|
+
refresh_data
|
35
|
+
add_to_queue if task
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.reset_all_queues
|
39
|
+
Delayed::Job.where("queue like 'task_%'").destroy_all
|
40
|
+
Task.all.each{|task| new(task).add_to_queue}
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Task < ActiveRecord::Base
|
2
|
+
ACTIVE = 'active'.freeze
|
3
|
+
INACTIVE = 'inactive'.freeze
|
4
|
+
DEFAULT_INTERVAL = 60
|
5
|
+
DEFAULT_DATABASE = 0
|
6
|
+
|
7
|
+
store :data
|
8
|
+
after_initialize :init
|
9
|
+
|
10
|
+
def init
|
11
|
+
self.every ||= DEFAULT_INTERVAL
|
12
|
+
self.database ||= DEFAULT_DATABASE
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.selectable_actions
|
16
|
+
[['Watch key', WatchKeyTask], ['Watch query', WatchQueryTask]]
|
17
|
+
end
|
18
|
+
|
19
|
+
def active?
|
20
|
+
status == ACTIVE
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_partial_path
|
24
|
+
'tasks/task'
|
25
|
+
end
|
26
|
+
|
27
|
+
def reset_queue
|
28
|
+
BackgroundTaskJob.new(self).reset_queue
|
29
|
+
end
|
30
|
+
|
31
|
+
def backend
|
32
|
+
BackendConnection.build(current_database: self.database)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class WatchKeyTask < Task
|
2
|
+
def type_name
|
3
|
+
'Watch key'
|
4
|
+
end
|
5
|
+
|
6
|
+
def perform
|
7
|
+
result = RetrieveKey.new(backend, trigger).result
|
8
|
+
if data[:initialized]
|
9
|
+
notify(data[:old_value], result) if data[:old_value] != result
|
10
|
+
else
|
11
|
+
data[:initialized] = true
|
12
|
+
end
|
13
|
+
|
14
|
+
data[:old_value] = result
|
15
|
+
save
|
16
|
+
end
|
17
|
+
|
18
|
+
def notify(old_value, new_value)
|
19
|
+
msg = "Value of key '#{trigger}' has changed from #{old_value.inspect} to #{new_value.inspect}"
|
20
|
+
Notification.create(reporter: name, message: msg)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
|
3
|
+
class WatchQueryTask < Task
|
4
|
+
def type_name
|
5
|
+
'Watch query'
|
6
|
+
end
|
7
|
+
|
8
|
+
def perform
|
9
|
+
result = SearchKeys.new(backend, trigger).result.map(&:with_indifferent_access)
|
10
|
+
if data[:initialized]
|
11
|
+
notify(data[:old_value], result) if data[:old_value] != result
|
12
|
+
else
|
13
|
+
data[:initialized] = true
|
14
|
+
end
|
15
|
+
|
16
|
+
data[:old_value] = result
|
17
|
+
save
|
18
|
+
end
|
19
|
+
|
20
|
+
def notify(old_value, new_value)
|
21
|
+
msg = "Value of key '#{trigger}' has changed from #{old_value.inspect} to #{new_value.inspect}"
|
22
|
+
Notification.create!(reporter: name, message: msg)
|
23
|
+
end
|
24
|
+
end
|
@@ -3,11 +3,11 @@
|
|
3
3
|
= choose_database_select(@databases)
|
4
4
|
%hr
|
5
5
|
|
6
|
-
%form{action:
|
6
|
+
%form{action: search_content_index_path, method: 'get', style: 'margin-top: 5px'}
|
7
7
|
.form-group
|
8
8
|
%label{for: 'input_key'}
|
9
9
|
Introduce search terms (wildcards allowed)
|
10
10
|
%input.form-control{type: 'text', id: 'input_key', name: 'key', placeholder: 'Search terms', value: search_term}
|
11
11
|
.form-group
|
12
|
-
%button
|
12
|
+
%button{type: 'submit', class: remove_class}
|
13
13
|
Search
|
@@ -0,0 +1,32 @@
|
|
1
|
+
= form_for @task.becomes(Task) do |f|
|
2
|
+
.form-group
|
3
|
+
= f.label :name
|
4
|
+
= f.text_field :name, class: 'form-control'
|
5
|
+
|
6
|
+
.form-group
|
7
|
+
= f.label :type, 'Action'
|
8
|
+
= f.select(:type, options_for_select(Task.selectable_actions, @task.type), {}, {class: 'form-control'})
|
9
|
+
|
10
|
+
.form-group
|
11
|
+
= f.label :trigger
|
12
|
+
= f.text_field :trigger, class: 'form-control'
|
13
|
+
|
14
|
+
.form-group
|
15
|
+
= f.label :database, 'Database'
|
16
|
+
= f.number_field :database, class: 'form-control'
|
17
|
+
|
18
|
+
.form-group
|
19
|
+
= f.label :every, 'Every (seconds)'
|
20
|
+
= f.number_field :every, class: 'form-control'
|
21
|
+
|
22
|
+
.form_group
|
23
|
+
.checkbox
|
24
|
+
= f.label :status, {style: 'font-weight:bold'} do
|
25
|
+
= f.check_box :status, {}, Task::ACTIVE, Task::INACTIVE
|
26
|
+
= 'Active'
|
27
|
+
|
28
|
+
.form-group
|
29
|
+
%button.btn.btn-default{type: 'submit', style: 'margin-right: 10px'}
|
30
|
+
Save
|
31
|
+
%a{href: tasks_path}
|
32
|
+
Cancel
|
@@ -0,0 +1,14 @@
|
|
1
|
+
%tr
|
2
|
+
%td
|
3
|
+
= task.name
|
4
|
+
%td
|
5
|
+
= task.type_name
|
6
|
+
%td
|
7
|
+
= task.trigger
|
8
|
+
%td
|
9
|
+
= task.every
|
10
|
+
%td
|
11
|
+
= task.active? ? 'Yes' : 'No'
|
12
|
+
%td
|
13
|
+
= link_to 'Edit', edit_task_path(task), class: edit_class
|
14
|
+
= link_to 'Delete', task_path(task), class: remove_class, :confirm => 'Are you sure?', :method => :delete
|