active_spy 1.4.4 → 2.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +7 -8
- data/Gemfile.lock +57 -108
- data/Rakefile +19 -9
- data/active_spy.gemspec +29 -38
- data/lib/active_spy.rb +16 -64
- data/lib/active_spy/agent.rb +77 -0
- data/lib/active_spy/handler.rb +124 -0
- data/lib/active_spy/station.rb +56 -0
- data/lib/active_spy/version.rb +10 -0
- metadata +46 -55
- data/VERSION +0 -1
- data/app/controllers/active_spy/notifications_controller.rb +0 -71
- data/lib/active_spy/base.rb +0 -9
- data/lib/active_spy/configuration.rb +0 -106
- data/lib/active_spy/rails/base.rb +0 -140
- data/lib/active_spy/rails/engine.rb +0 -7
- data/lib/active_spy/rails/hook_list.rb +0 -141
- data/lib/active_spy/rails/listener.rb +0 -117
- data/lib/active_spy/rails/railtie.rb +0 -13
- data/lib/active_spy/rails/spy.rb +0 -70
- data/lib/active_spy/rails/validation.rb +0 -87
- data/lib/active_spy/spy/spy.rb +0 -42
- data/lib/active_spy/spy/spy_list.rb +0 -91
@@ -1,117 +0,0 @@
|
|
1
|
-
require 'rest-client'
|
2
|
-
require 'json'
|
3
|
-
|
4
|
-
module ActiveSpy
|
5
|
-
module Rails
|
6
|
-
# Base class used to process the events received.
|
7
|
-
#
|
8
|
-
class Listener
|
9
|
-
include ActiveSupport::Inflector
|
10
|
-
|
11
|
-
|
12
|
-
# Constant to hold the model translations. The key is the incoming
|
13
|
-
# +ref_type+ and the value is the matching model class.
|
14
|
-
#
|
15
|
-
MODEL_HANDLER = {}
|
16
|
-
|
17
|
-
# Store the event handler hook in the {ActiveSpy::Rails::HookList} for
|
18
|
-
# later registration of them within the event runner.
|
19
|
-
#
|
20
|
-
def self.inherited(child)
|
21
|
-
ActiveSpy::Rails::HookList << child
|
22
|
-
end
|
23
|
-
|
24
|
-
# Set the external class of the model that we are listening to.
|
25
|
-
#
|
26
|
-
def self.external_class(klass)
|
27
|
-
@external_classes = [klass]
|
28
|
-
end
|
29
|
-
|
30
|
-
# Set the external classes which we are listening to.
|
31
|
-
#
|
32
|
-
def self.external_classes(*classes)
|
33
|
-
@external_classes = classes
|
34
|
-
end
|
35
|
-
|
36
|
-
# Convert the listener class into one or more hook hashes
|
37
|
-
#
|
38
|
-
def self.to_hook
|
39
|
-
hooks = []
|
40
|
-
hook_classes.each do |hook_class|
|
41
|
-
hooks << { 'class' => hook_class, 'post_class' => name.split('Listener')[0] }
|
42
|
-
end
|
43
|
-
hooks
|
44
|
-
end
|
45
|
-
|
46
|
-
# Get the classes that we are listegnig to.
|
47
|
-
#
|
48
|
-
def self.hook_classes
|
49
|
-
return @external_classes if defined? @external_classes
|
50
|
-
[name.split('Listener')[0]]
|
51
|
-
end
|
52
|
-
|
53
|
-
# Handle a request with +params+ and sync the database according to
|
54
|
-
# them.
|
55
|
-
#
|
56
|
-
def handle(params)
|
57
|
-
object_type = params.delete('type')
|
58
|
-
callback = params.delete('action')
|
59
|
-
payload_content = params.delete('payload')[object_type.downcase]
|
60
|
-
actor = params.delete('actor')
|
61
|
-
realm = params.delete('realm')
|
62
|
-
sync_database(callback, object_type, payload_content, actor, realm)
|
63
|
-
end
|
64
|
-
|
65
|
-
# Calls the proper method to sync the database. It will manipulate
|
66
|
-
# objects of the class +object_type+, with the attributes sent in the
|
67
|
-
# +payload+, triggered by the callback +callback+.
|
68
|
-
#
|
69
|
-
def sync_database(callback, object_type, payload, actor, realm)
|
70
|
-
send(callback, object_type, payload, actor, realm)
|
71
|
-
end
|
72
|
-
|
73
|
-
# Logic to handle object's creation. You can override this, as you wish,
|
74
|
-
# to suit your own needs
|
75
|
-
#
|
76
|
-
def create(object_type, payload, _actor, _realm)
|
77
|
-
klass = get_object_class(object_type)
|
78
|
-
object = klass.new
|
79
|
-
object.update_attributes(payload)
|
80
|
-
object
|
81
|
-
end
|
82
|
-
|
83
|
-
# Logic to handle object's update. You can override this, as you wish,
|
84
|
-
# to suit your own needs
|
85
|
-
#
|
86
|
-
def update(object_type, payload, _actor, _realm)
|
87
|
-
klass = get_object_class(object_type)
|
88
|
-
guid = payload['guid']
|
89
|
-
object = klass.find_by(guid: guid)
|
90
|
-
object.update_attributes(payload)
|
91
|
-
object
|
92
|
-
end
|
93
|
-
|
94
|
-
# Destroy a record from our database. You can override this, as you wish,
|
95
|
-
# to suit your own needs
|
96
|
-
#
|
97
|
-
def destroy(klass, payload, _actor, _realm)
|
98
|
-
klass = get_object_class(klass)
|
99
|
-
guid = payload.delete('guid')
|
100
|
-
object = klass.find_by(guid: guid)
|
101
|
-
object.destroy!
|
102
|
-
object
|
103
|
-
end
|
104
|
-
|
105
|
-
# Gets the object class. First, it'll look the {MODEL_HANDLER} hash and
|
106
|
-
# see if there is any translation for a given +object_type+. If it does
|
107
|
-
# not have a translation, this method will try to +constantize+ the
|
108
|
-
# +object_type+.
|
109
|
-
#
|
110
|
-
def get_object_class(object_type)
|
111
|
-
translated_object_type = MODEL_HANDLER[object_type]
|
112
|
-
return constantize(translated_object_type) if translated_object_type
|
113
|
-
constantize(object_type)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'rails'
|
2
|
-
|
3
|
-
# Railtie class to automatically include {ActiveSpy::Spy} in all
|
4
|
-
# +ActiveRecord::Base+
|
5
|
-
#
|
6
|
-
class Railtie < Rails::Railtie
|
7
|
-
initializer 'active_spy.spies' do
|
8
|
-
ActiveSupport.on_load(:active_record) do
|
9
|
-
include ActiveSpy::Spy
|
10
|
-
include ActiveSpy::Rails::Spy
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
data/lib/active_spy/rails/spy.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
require 'active_support'
|
2
|
-
|
3
|
-
module ActiveSpy
|
4
|
-
# Module used to hold Rails specific logic.
|
5
|
-
#
|
6
|
-
module Rails
|
7
|
-
# Module that defines methods used to spy on some class methods.
|
8
|
-
#
|
9
|
-
module Spy
|
10
|
-
# Default snippet to extends the class with
|
11
|
-
# {ActiveSpy::Spy::ClassMethods} when {ActiveSpy::Spy} is included in
|
12
|
-
# it.
|
13
|
-
#
|
14
|
-
def self.included(base)
|
15
|
-
base.extend ClassMethods
|
16
|
-
end
|
17
|
-
# Class methods to be defined in classes that includes {ActiveSpy::Spy}
|
18
|
-
#
|
19
|
-
module ClassMethods
|
20
|
-
# Class method to define the realm of the model.
|
21
|
-
#
|
22
|
-
def model_realm(realm_name = nil)
|
23
|
-
dynamically_define_method_or_call_block(:realm, realm_name)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Class method to define the actor of the model.
|
27
|
-
#
|
28
|
-
def model_actor(actor_name = nil)
|
29
|
-
dynamically_define_method_or_call_block(:actor, actor_name)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Defines a method called +method_name+ that will call a method called
|
33
|
-
# +method_value+ if a symbol is provided. If a block is provided
|
34
|
-
# instead, it will be returned.
|
35
|
-
#
|
36
|
-
def dynamically_define_method_or_call_block(abstract_name, method_name)
|
37
|
-
return unless method_name
|
38
|
-
if abstract_name == method_name
|
39
|
-
attr_accessor method_name
|
40
|
-
else
|
41
|
-
define_method method_name do
|
42
|
-
send(:instance_variable_get, "@#{abstract_name}")
|
43
|
-
end
|
44
|
-
define_method "#{method_name}=" do |new_value|
|
45
|
-
send(:instance_variable_set, "@#{abstract_name}", new_value)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# Helper to use on Rails app and watch for model creation, update and
|
51
|
-
# destruction.
|
52
|
-
#
|
53
|
-
def watch_model_changes
|
54
|
-
watch_method :save, :destroy
|
55
|
-
inject_payload_for_method
|
56
|
-
end
|
57
|
-
|
58
|
-
# Helper to inject the method +payload_for(method)+ in the model
|
59
|
-
# with the default behavior: all attributes are sent in all
|
60
|
-
# actions.
|
61
|
-
#
|
62
|
-
def inject_payload_for_method
|
63
|
-
define_method :payload_for do |_method|
|
64
|
-
{ self.class.name.downcase.to_sym => attributes }
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
module ActiveSpy
|
4
|
-
module Rails
|
5
|
-
# Module to hold classes that are intended to validate something.
|
6
|
-
#
|
7
|
-
module Validation
|
8
|
-
# Class responsible to validate event that are send to event-runner
|
9
|
-
# instances.
|
10
|
-
#
|
11
|
-
class Event
|
12
|
-
|
13
|
-
def initialize(event_json)
|
14
|
-
@event = JSON.load(event_json)
|
15
|
-
end
|
16
|
-
|
17
|
-
# Validates the +event_json+ provided in the initializer
|
18
|
-
#
|
19
|
-
# (see #initialize)
|
20
|
-
def validate!
|
21
|
-
check_actor_key(@event['event']['actor'])
|
22
|
-
check_realm_key(@event['event']['realm'])
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
# Check if actor is valid.
|
28
|
-
#
|
29
|
-
def check_actor_key(actor)
|
30
|
-
fail ActorNotPresent if actor.nil?
|
31
|
-
errors = get_errors_from_hash(actor, :actor)
|
32
|
-
fail InvalidActor, errors unless errors.empty?
|
33
|
-
end
|
34
|
-
|
35
|
-
# Check if realm is valid.
|
36
|
-
#
|
37
|
-
def check_realm_key(realm)
|
38
|
-
fail RealmNotPresent if realm.nil?
|
39
|
-
errors = get_errors_from_hash(realm, :realm)
|
40
|
-
fail InvalidRealm, errors unless errors.empty?
|
41
|
-
end
|
42
|
-
|
43
|
-
# Get the error from +data+ regarding +hash_type+ checking if the
|
44
|
-
# required keys are being provided.
|
45
|
-
#
|
46
|
-
def get_errors_from_hash(data, hash_type)
|
47
|
-
keys = hash_type == :actor ? required_actor_keys : required_realm_keys
|
48
|
-
keys.map do |key|
|
49
|
-
"#{key} should not be empty." if data[key].nil?
|
50
|
-
end.compact.join(' ')
|
51
|
-
end
|
52
|
-
|
53
|
-
# Required keys for an actor to be valid.
|
54
|
-
#
|
55
|
-
def required_actor_keys
|
56
|
-
%w[id class login url avatar_url]
|
57
|
-
end
|
58
|
-
|
59
|
-
# Required keys for realm to be valid.
|
60
|
-
#
|
61
|
-
def required_realm_keys
|
62
|
-
%w[id class name url]
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# Error when actor is not present.
|
67
|
-
#
|
68
|
-
class ActorNotPresent < RuntimeError
|
69
|
-
end
|
70
|
-
|
71
|
-
# Error when the actor is invalid.
|
72
|
-
#
|
73
|
-
class InvalidActor < RuntimeError
|
74
|
-
end
|
75
|
-
|
76
|
-
# Error when realm is not present.
|
77
|
-
#
|
78
|
-
class RealmNotPresent < RuntimeError
|
79
|
-
end
|
80
|
-
|
81
|
-
# Error when the realm is invalid.
|
82
|
-
#
|
83
|
-
class InvalidRealm < RuntimeError
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
data/lib/active_spy/spy/spy.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'active_support'
|
2
|
-
|
3
|
-
module ActiveSpy
|
4
|
-
# Module that defines methods used to spy on some class methods
|
5
|
-
#
|
6
|
-
module Spy
|
7
|
-
# Default snippet to extends the class with {ActiveSpy::Spy::ClassMethods}
|
8
|
-
# when {ActiveSpy::Spy} is included in it.
|
9
|
-
#
|
10
|
-
def self.included(base)
|
11
|
-
base.extend ClassMethods
|
12
|
-
end
|
13
|
-
|
14
|
-
# Invokes the callback method on the invoker class. The +callback_type+
|
15
|
-
# param tells wether it will be called +:after+ or +before+.
|
16
|
-
#
|
17
|
-
def invoke_callback(method, callback_type)
|
18
|
-
callback_invoker = callback_invoker_class.new(self)
|
19
|
-
callback = "#{callback_type}_#{method}"
|
20
|
-
return unless callback_invoker.respond_to?(callback)
|
21
|
-
callback_invoker.send(callback)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Gets the invoker class based on the class' name
|
25
|
-
#
|
26
|
-
def callback_invoker_class
|
27
|
-
ActiveSupport::Inflector.constantize "#{self.class.name}Events"
|
28
|
-
end
|
29
|
-
|
30
|
-
# Class methods to be defined in classes that includes {ActiveSpy::Spy}
|
31
|
-
#
|
32
|
-
module ClassMethods
|
33
|
-
# Set watchers for the +method+
|
34
|
-
#
|
35
|
-
def watch_method(*methods)
|
36
|
-
methods.each do |method|
|
37
|
-
ActiveSpy::SpyList << { 'class' => name, 'method' => method }
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,91 +0,0 @@
|
|
1
|
-
require 'active_support'
|
2
|
-
require 'singleton'
|
3
|
-
|
4
|
-
module ActiveSpy
|
5
|
-
# Singleton used to hold the spies and lazely active these spies by patching
|
6
|
-
# the methods in the specified classes.
|
7
|
-
#
|
8
|
-
class SpyList
|
9
|
-
include Singleton
|
10
|
-
|
11
|
-
# Just to initiliaze the spy list.
|
12
|
-
#
|
13
|
-
def initialize
|
14
|
-
@spies = []
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_reader :spies
|
18
|
-
|
19
|
-
# Proxy all methods called in the {ActiveSpy::SpyList} class to a
|
20
|
-
# {ActiveSpy::SpyList} instance. Just a syntax sugar.
|
21
|
-
#
|
22
|
-
def self.method_missing(method, *args, &block)
|
23
|
-
instance.send(method, *args, &block)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Activate all the spies defined in the spy list by patching the methods
|
27
|
-
# in their classes.
|
28
|
-
#
|
29
|
-
def activate
|
30
|
-
@spies.each do |spy|
|
31
|
-
spied_class = spy['class']
|
32
|
-
spied_method = spy['method']
|
33
|
-
|
34
|
-
spy['old_method'] = patch(spied_class, spied_method) unless spy['active']
|
35
|
-
spy['active'] = true
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# Deactivate all the spies defined in the spy list by unpatching the methods
|
40
|
-
# in their classes.
|
41
|
-
#
|
42
|
-
def deactivate
|
43
|
-
@spies.each do |spy|
|
44
|
-
unpatch(spy['class'], spy['method'], spy['old_method'])
|
45
|
-
spy['active'] = nil
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# forward {<<} method to the spy list.
|
50
|
-
#
|
51
|
-
def <<(other)
|
52
|
-
@spies << other
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
# This method patches the +method+ in the class +klass+ to invoke the
|
58
|
-
# callbacks defined in the respective class, that should be named using
|
59
|
-
# appending 'Events' to the class' name, and inherites from
|
60
|
-
# {ActiveSpy::Base}.
|
61
|
-
#
|
62
|
-
def patch(klass, method)
|
63
|
-
old_method = nil
|
64
|
-
ActiveSupport::Inflector.constantize(klass).class_eval do
|
65
|
-
old_method = instance_method(method)
|
66
|
-
define_method method do |*args, &block|
|
67
|
-
send(:invoke_callback, method, :before)
|
68
|
-
result = old_method.bind(self).call(*args, &block)
|
69
|
-
if defined?(Rails)
|
70
|
-
send(:invoke_callback, method, :after) if result
|
71
|
-
else
|
72
|
-
send(:invoke_callback, method, :after)
|
73
|
-
end
|
74
|
-
result
|
75
|
-
end
|
76
|
-
end
|
77
|
-
old_method
|
78
|
-
end
|
79
|
-
|
80
|
-
# Properyly unpatch the +method+ in class +klass+ and put back +old_method+
|
81
|
-
# in its place.
|
82
|
-
#
|
83
|
-
def unpatch(klass, method, old_method)
|
84
|
-
ActiveSupport::Inflector.constantize(klass).class_eval do
|
85
|
-
define_method method do |*args, &block|
|
86
|
-
old_method.bind(self).call(*args, &block)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|