redis_monitor 0.2.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +1 -1
  4. data/README.md +7 -9
  5. data/Rakefile +4 -0
  6. data/lib/engine/app/controllers/content_controller.rb +1 -1
  7. data/lib/engine/app/controllers/notifications_controller.rb +19 -0
  8. data/lib/engine/app/controllers/tasks_controller.rb +47 -0
  9. data/lib/engine/app/helpers/application_helper.rb +13 -3
  10. data/lib/engine/app/lib/commands/retrieve_key.rb +5 -0
  11. data/lib/engine/app/lib/commands/{search_key.rb → search_keys.rb} +1 -1
  12. data/lib/engine/app/lib/jobs/background_task_job.rb +42 -0
  13. data/lib/engine/app/models/notification.rb +7 -0
  14. data/lib/engine/app/models/task.rb +34 -0
  15. data/lib/engine/app/models/watch_key_task.rb +22 -0
  16. data/lib/engine/app/models/watch_query_task.rb +24 -0
  17. data/lib/engine/app/views/content/_search_form.haml +2 -2
  18. data/lib/engine/app/views/layouts/application.haml +1 -0
  19. data/lib/engine/app/views/notifications/_notification.haml +9 -0
  20. data/lib/engine/app/views/notifications/index.haml +14 -0
  21. data/lib/engine/app/views/tasks/_form.haml +32 -0
  22. data/lib/engine/app/views/tasks/_task.haml +14 -0
  23. data/lib/engine/app/views/tasks/edit.haml +3 -0
  24. data/lib/engine/app/views/tasks/index.haml +21 -0
  25. data/lib/engine/app/views/tasks/new.haml +3 -0
  26. data/lib/engine/bin/delayed_job +5 -0
  27. data/lib/engine/config/database.yml +2 -2
  28. data/lib/engine/config/initializers/delayed_job.rb +3 -0
  29. data/lib/engine/config/routes.rb +3 -0
  30. data/lib/engine/db/migrate/20140112162911_create_tasks.rb +15 -0
  31. data/lib/engine/db/migrate/20140118155310_create_delayed_jobs.rb +22 -0
  32. data/lib/engine/db/migrate/20140118183403_create_notifications.rb +10 -0
  33. data/lib/engine/db/schema.rb +51 -0
  34. data/lib/engine/public/javascripts/jquery_ujs.js +394 -0
  35. data/lib/engine/public/stylesheets/custom.css +4 -0
  36. data/lib/engine/spec/controllers/content_controller_spec.rb +1 -1
  37. data/lib/engine/spec/controllers/notifications_controller_spec.rb +39 -0
  38. data/lib/engine/spec/controllers/tasks_controller_spec.rb +109 -0
  39. data/lib/engine/spec/lib/commands/retrieve_key_spec.rb +15 -0
  40. data/lib/engine/spec/lib/commands/search_key_spec.rb +2 -2
  41. data/lib/engine/spec/lib/jobs/background_task_job_spec.rb +55 -0
  42. data/lib/engine/spec/models/notification_spec.rb +9 -0
  43. data/lib/engine/spec/models/task_spec.rb +23 -0
  44. data/lib/engine/spec/models/watch_key_task_spec.rb +13 -0
  45. data/lib/engine/spec/models/watch_query_task_spec.rb +13 -0
  46. data/lib/redis_monitor.rb +7 -1
  47. data/lib/version.rb +1 -1
  48. data/redis_monitor.gemspec +2 -0
  49. metadata +61 -10
  50. data/lib/engine/test/controllers/.keep +0 -0
  51. data/lib/engine/test/fixtures/.keep +0 -0
  52. data/lib/engine/test/helpers/.keep +0 -0
  53. data/lib/engine/test/integration/.keep +0 -0
  54. data/lib/engine/test/mailers/.keep +0 -0
  55. data/lib/engine/test/models/.keep +0 -0
  56. data/lib/engine/test/test_helper.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ff13da4a5a6d6d3b4965964e669d6790b8aa88eb
4
- data.tar.gz: db3f048bdb23af208771e50116923c962b4b520f
3
+ metadata.gz: b306ec30c262af4c5130f654efee0356f2652ff8
4
+ data.tar.gz: 7ee38ccac2e96471493eeb7349876395786bb43f
5
5
  SHA512:
6
- metadata.gz: 0008c8e625d7c73d2fe4ad6f554f4ea648557c6d308b4538e870000df576ad8cf052cd78d4931d1f2f62983622356851a2d47fd3444b79e964366250127942cf
7
- data.tar.gz: b63cd187913745965bc002e4928f9b72df5a6490ed7febe98615290ed9646ed6bd13e57c76e11bdb7bb73de50fe6f593e51fbad437b2f9503cc296e36e3b81d3
6
+ metadata.gz: 95da81fb9d481224903eb4f6a25aa49c28b2f283e3aa0747be13dc9bb73bdffb2c75eb3a4a68e6e70464ea0e969525756ad9c2c2bf24a7d73d3384d1e23a150b
7
+ data.tar.gz: fc1a93aef1fb52ab9e8ae2a3c8eeb465f4ba61a52811b702e18f0394e2415ce0a29b967c871c3093428435c31b40640b6c0cf57107eb3a513fea18e2a0533a18
data/.gitignore CHANGED
@@ -17,4 +17,6 @@ test/version_tmp
17
17
  tmp
18
18
  .rvmrc
19
19
  .idea
20
- *.rdb
20
+ *.rdb
21
+ .ruby-version
22
+ .ruby-gemset
data/.travis.yml CHANGED
@@ -4,4 +4,4 @@ rvm:
4
4
  - 2.0.0
5
5
  - jruby-19mode
6
6
 
7
- script: rake spec
7
+ script: rake db:migrate;rake spec
data/README.md CHANGED
@@ -3,15 +3,7 @@
3
3
 
4
4
  ## Installation
5
5
 
6
- Add this line to your application's Gemfile:
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 = SearchKey.new(backend, params[:key]).result
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: '/info', selected_section: selected_section)
16
- section(name: 'content', title: 'Content', url: '/content', selected_section: selected_section)
17
- section(name: 'performance', title: 'Performance', url: '/performance', selected_section: selected_section)
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,5 @@
1
+ class RetrieveKey < Struct.new(:backend, :key)
2
+ def result
3
+ backend.get(key)
4
+ end
5
+ end
@@ -1,4 +1,4 @@
1
- class SearchKey < Struct.new(:backend, :key)
1
+ class SearchKeys < Struct.new(:backend, :key)
2
2
  def result
3
3
  backend.keys(key).map{|found| {key: found, value: backend.get(found)} }
4
4
  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,7 @@
1
+ class Notification < ActiveRecord::Base
2
+ default_scope ->{order('created_at DESC')}
3
+
4
+ def when
5
+ created_at.localtime.strftime('%Y/%m/%d %H:%M')
6
+ end
7
+ 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: '/content/search', method: 'get', style: 'margin-top: 5px'}
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.btn.btn-default{type: 'submit'}
12
+ %button{type: 'submit', class: remove_class}
13
13
  Search
@@ -6,6 +6,7 @@
6
6
  = stylesheet_link_tag 'custom', media: 'all'
7
7
 
8
8
  = javascript_include_tag 'jquery-2.0.3.min'
9
+ = javascript_include_tag 'jquery_ujs'
9
10
  = javascript_include_tag 'bootstrap.min'
10
11
  = javascript_include_tag 'bootstrap-select.min'
11
12
  = javascript_include_tag 'app'
@@ -0,0 +1,9 @@
1
+ %tr
2
+ %td
3
+ = notification.reporter
4
+ %td
5
+ = notification.message
6
+ %td
7
+ = notification.when
8
+ %td
9
+ = link_to 'Delete', notification_path(notification), class: remove_class, :confirm => 'Are you sure?', :method => :delete
@@ -0,0 +1,14 @@
1
+ %table.table.table-striped
2
+ %thead
3
+ %tr
4
+ %th
5
+ Reporter
6
+ %th
7
+ Message
8
+ %th
9
+ When
10
+ %th
11
+ Actions
12
+
13
+ %tbody
14
+ = render @notifications
@@ -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
@@ -0,0 +1,3 @@
1
+ %h2
2
+ Edit task
3
+ = render 'form'
@@ -0,0 +1,21 @@
1
+ %table.table.table-striped
2
+ %thead
3
+ %tr
4
+ %th
5
+ Name
6
+ %th
7
+ Action
8
+ %th
9
+ Trigger
10
+ %th
11
+ Every (seconds)
12
+ %th
13
+ Active
14
+ %th
15
+ Actions
16
+
17
+ %tbody
18
+ = render @tasks
19
+
20
+ %a.btn.btn-default{href: new_task_path}
21
+ New task