active_spy 1.4.4 → 2.0.0.pre1
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/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
|