redis_monitor 0.2.1 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|