police-dataflow 0.0.1
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.
- data/.document +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +31 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +9 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/lib/police-dataflow.rb +2 -0
- data/lib/police/dataflow.rb +16 -0
- data/lib/police/dataflow/core_extensions.rb +12 -0
- data/lib/police/dataflow/guarding.rb +16 -0
- data/lib/police/dataflow/label.rb +86 -0
- data/lib/police/dataflow/labeling.rb +71 -0
- data/lib/police/dataflow/proxies.rb +51 -0
- data/lib/police/dataflow/proxy_base.rb +132 -0
- data/lib/police/dataflow/proxying.rb +179 -0
- data/police-dataflow.gemspec +62 -0
- data/test/dataflow/core_extensions_test.rb +51 -0
- data/test/dataflow/labeling_test.rb +74 -0
- data/test/dataflow/proxies_test.rb +40 -0
- data/test/dataflow/proxy_base_test.rb +145 -0
- data/test/dataflow/proxying_test.rb +287 -0
- data/test/helper.rb +23 -0
- data/test/helpers/auto_flow_fixture.rb +6 -0
- data/test/helpers/no_flow_fixture.rb +14 -0
- data/test/helpers/proxying_fixture.rb +45 -0
- metadata +76 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
module Police
|
2
|
+
|
3
|
+
module DataFlow
|
4
|
+
|
5
|
+
# The complex method dispatching logic used by ProxyBase.
|
6
|
+
#
|
7
|
+
# ProxyBase is the superclass for all proxy classes, which makes it visible to
|
8
|
+
# application code. For this reason, we avoid defining any methods there.
|
9
|
+
module Proxying
|
10
|
+
# Creates a label-holding proxy for an object.
|
11
|
+
#
|
12
|
+
# @param [Object] proxied the object to be proxied
|
13
|
+
# @param [Array<Integer>] label_keys
|
14
|
+
# @return [Police::DataFlow::ProxyBase] an object that can carry labels, and
|
15
|
+
# performs label-propagation as it redirects received messages to the
|
16
|
+
# proxied object
|
17
|
+
def self.proxy(proxied, label_set, autoflow_set)
|
18
|
+
proxy_class = Police::DataFlow::Proxies.for proxied.class, label_set
|
19
|
+
proxy_class.new proxied, proxy_class, label_set, autoflow_set
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates proxies for a class' instance methods.
|
23
|
+
#
|
24
|
+
# The proxy methods are defined as instance methods for the proxying class,
|
25
|
+
# because all the proxied objects that have the same class will need the same
|
26
|
+
# proxies.
|
27
|
+
#
|
28
|
+
# @param [Class] proxy_class a Police::DataFlow::Proxy subclass that will
|
29
|
+
# receive the new proxy method definitions
|
30
|
+
# @param [Class] klass the class whose instance methods will be proxied
|
31
|
+
# @return [NilClass] nil
|
32
|
+
def self.add_class_methods(proxy_class, klass)
|
33
|
+
# NOTE: this is thread-safe because, at worst, the effort of adding methods
|
34
|
+
# will be re-duplicated
|
35
|
+
klass.public_instance_methods(true).each do |method|
|
36
|
+
add_class_method proxy_class, klass.instance_method(method), :public
|
37
|
+
end
|
38
|
+
klass.protected_instance_methods(true).each do |method|
|
39
|
+
add_class_method proxy_class, klass.instance_method(method), :protected
|
40
|
+
end
|
41
|
+
klass.private_instance_methods(true).each do |method|
|
42
|
+
add_class_method proxy_class, klass.instance_method(method), :private
|
43
|
+
end
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# Adds a method to a proxy class.
|
48
|
+
#
|
49
|
+
# @param [Module] proxy_class the class that will receive the proxy method
|
50
|
+
# @param [Method] method_def the definition of the method to be proxied
|
51
|
+
# @param [Symbol] access the proxied method's access level (:public,
|
52
|
+
# :protected, or :private)
|
53
|
+
def self.add_class_method(proxy_class, method_def, access)
|
54
|
+
# Avoid redefining methods, because that blows up VM caches.
|
55
|
+
if proxy_class.method_defined?(method_def.name) ||
|
56
|
+
proxy_class.private_method_defined?(method_def.name)
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
# Define the method.
|
61
|
+
proxy_class.class_eval proxy_method_definition(
|
62
|
+
proxy_class.__police_classes__, method_def, access)
|
63
|
+
# Set its access level.
|
64
|
+
proxy_class.__send__ access, method_def.name
|
65
|
+
end
|
66
|
+
|
67
|
+
# The full definition of a proxy method.
|
68
|
+
#
|
69
|
+
# @param [Array<Police::DataFlow::Label>] label_classes the label classes
|
70
|
+
# supported by the proxy class
|
71
|
+
# @param [Method] method_def the definition of the method to be proxied
|
72
|
+
# @param [Symbol] access the proxied method's access level (:public,
|
73
|
+
# :protected, or :private)
|
74
|
+
# @return [String] a chunk of Ruby that can be eval'ed in the context of a
|
75
|
+
# proxy class to define a proxy for the given method
|
76
|
+
def self.proxy_method_definition(label_classes, method_def, access)
|
77
|
+
# NOTE: it might be tempting to attempt to pass a block to the proxied
|
78
|
+
# method at all times, and try to yield to the original block when our
|
79
|
+
# block is invoked; this would work most of the time, but it would
|
80
|
+
# break methods such as Enumerable#map and String#scan, whose behavior
|
81
|
+
# changes depending on whether or not a block is passed to them
|
82
|
+
["def #{method_def.name}(#{proxy_argument_list(method_def, true)})",
|
83
|
+
"return_value = if block",
|
84
|
+
proxy_method_call(method_def, access, false) + " do |*yield_args|",
|
85
|
+
proxy_yield_args_decorating(label_classes, method_def),
|
86
|
+
"block_return = yield(*yield_args)",
|
87
|
+
# TODO(pwnall): consider adding a yield value filter
|
88
|
+
"next block_return",
|
89
|
+
"end",
|
90
|
+
"else",
|
91
|
+
proxy_method_call(method_def, access, false),
|
92
|
+
"end",
|
93
|
+
proxy_return_decorating(label_classes, method_def),
|
94
|
+
"return return_value",
|
95
|
+
"end"].join ';'
|
96
|
+
end
|
97
|
+
|
98
|
+
# The proxying call to a method.
|
99
|
+
#
|
100
|
+
# @param [Method] method_def the definition of the method to be proxied
|
101
|
+
# @param [Symbol] access the proxied method's access level (:public,
|
102
|
+
# :protected, or :private)
|
103
|
+
# @param [Boolean] include_block if true, the method call passes the block
|
104
|
+
# that the proxy has received; if false, the block is ignored
|
105
|
+
# @return [String] a chunk of Ruby that can be used to call the given method
|
106
|
+
# when defining a proxy for it
|
107
|
+
def self.proxy_method_call(method_def, access, include_block)
|
108
|
+
arg_list = proxy_argument_list method_def, include_block
|
109
|
+
|
110
|
+
if access == :public
|
111
|
+
"@__police_proxied__.#{method_def.name}(#{arg_list})"
|
112
|
+
else
|
113
|
+
"@__police_proxied__.__send__(:#{method_def.name}, #{arg_list})"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Code that labels the values yielded by a decorated method to its block.
|
118
|
+
#
|
119
|
+
# @param [Array<Police::DataFlow::Label>] label_classes the label classes
|
120
|
+
# supported by the proxy class
|
121
|
+
# @param [Method] method_def the definition of the decorated method
|
122
|
+
# @return [String] a chunk of Ruby that can be used to invoke the yield args
|
123
|
+
# decorators of the labels held by a labeled object's proxy
|
124
|
+
def self.proxy_yield_args_decorating(label_classes, method_def)
|
125
|
+
method_name = method_def.name
|
126
|
+
arg_list = proxy_argument_list method_def, false
|
127
|
+
code_lines = ['labels = @__police_labels__']
|
128
|
+
label_classes.each do |label_class|
|
129
|
+
next unless hook = label_class.yield_args_hook(method_name)
|
130
|
+
label_key = label_class.__id__
|
131
|
+
code_lines << "labels[#{label_key}].each { |label, _| " \
|
132
|
+
"label.#{hook}(self, yield_args, #{arg_list}) }"
|
133
|
+
end
|
134
|
+
(code_lines.length > 1) ? code_lines.join('; ') : ''
|
135
|
+
end
|
136
|
+
|
137
|
+
# Code that labels return value of a decorated method.
|
138
|
+
#
|
139
|
+
# @param [Array<Police::DataFlow::Label>] label_classes the label classes
|
140
|
+
# supported by the proxy class
|
141
|
+
# @param [Method] method_def the definition of the method to be proxied
|
142
|
+
# @return [String] a chunk of Ruby that can be used to invoke the return value
|
143
|
+
# decorators of the labels held by a labeled object's proxy
|
144
|
+
def self.proxy_return_decorating(label_classes, method_def)
|
145
|
+
method_name = method_def.name
|
146
|
+
arg_list = proxy_argument_list method_def, false
|
147
|
+
code_lines = ['labels = @__police_labels__']
|
148
|
+
label_classes.each do |label_class|
|
149
|
+
next unless hook = label_class.return_hook(method_name)
|
150
|
+
label_key = label_class.__id__
|
151
|
+
code_lines << "labels[#{label_key}].each { |label, _| " \
|
152
|
+
"return_value = label.#{hook}(return_value, self, #{arg_list}) }"
|
153
|
+
end
|
154
|
+
(code_lines.length > 1) ? code_lines.join('; ') : ''
|
155
|
+
end
|
156
|
+
|
157
|
+
# The list of arguments used to define a proxy for the given method.
|
158
|
+
#
|
159
|
+
# @param [Method] method_def the definition of the method to be proxied
|
160
|
+
# @param [Boolean] captue_block if true, the method captures the block that it
|
161
|
+
# receives
|
162
|
+
# @return [String] a chunk of Ruby that can be used as the argument list when
|
163
|
+
# defining a proxy for the given method
|
164
|
+
def self.proxy_argument_list(method_def, capture_block)
|
165
|
+
arg_list = if method_def.arity >= 0
|
166
|
+
# Fixed number of arguments.
|
167
|
+
(1..method_def.arity).map { |i| "arg#{i}" }
|
168
|
+
else
|
169
|
+
# Variable number of arguments.
|
170
|
+
((1..(-method_def.arity - 1)).map { |i| "arg#{i}" } << '*args')
|
171
|
+
end
|
172
|
+
arg_list << '&block' if capture_block
|
173
|
+
arg_list.join ', '
|
174
|
+
end
|
175
|
+
end # namespace Police::DataFlow::Proxying
|
176
|
+
|
177
|
+
end # namespace Police::DataFlow
|
178
|
+
|
179
|
+
end # namespace Police
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "police-dataflow"
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Victor Costan"]
|
12
|
+
s.date = "2012-03-28"
|
13
|
+
s.description = "Pure Ruby implementtion of data flow label propagation."
|
14
|
+
s.email = "victor@costan.us"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.markdown",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/police-dataflow.rb",
|
28
|
+
"lib/police/dataflow.rb",
|
29
|
+
"lib/police/dataflow/core_extensions.rb",
|
30
|
+
"lib/police/dataflow/guarding.rb",
|
31
|
+
"lib/police/dataflow/label.rb",
|
32
|
+
"lib/police/dataflow/labeling.rb",
|
33
|
+
"lib/police/dataflow/proxies.rb",
|
34
|
+
"lib/police/dataflow/proxy_base.rb",
|
35
|
+
"lib/police/dataflow/proxying.rb",
|
36
|
+
"police-dataflow.gemspec",
|
37
|
+
"test/dataflow/core_extensions_test.rb",
|
38
|
+
"test/dataflow/labeling_test.rb",
|
39
|
+
"test/dataflow/proxies_test.rb",
|
40
|
+
"test/dataflow/proxy_base_test.rb",
|
41
|
+
"test/dataflow/proxying_test.rb",
|
42
|
+
"test/helper.rb",
|
43
|
+
"test/helpers/auto_flow_fixture.rb",
|
44
|
+
"test/helpers/no_flow_fixture.rb",
|
45
|
+
"test/helpers/proxying_fixture.rb"
|
46
|
+
]
|
47
|
+
s.homepage = "http://github.com/csail/police"
|
48
|
+
s.licenses = ["MIT"]
|
49
|
+
s.require_paths = ["lib"]
|
50
|
+
s.rubygems_version = "1.8.21"
|
51
|
+
s.summary = "Data flow label propagation"
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
s.specification_version = 3
|
55
|
+
|
56
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
|
+
else
|
58
|
+
end
|
59
|
+
else
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Police::DataFlow do
|
4
|
+
before do
|
5
|
+
@object = 'Hello'
|
6
|
+
@class = @object.class
|
7
|
+
@label = AutoFlowFixture.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#label' do
|
11
|
+
describe 'with a single label' do
|
12
|
+
before do
|
13
|
+
@object = Police::DataFlow.label @object, @label
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'labels the object correctly' do
|
17
|
+
Police::DataFlow.labels(@object).must_equal [@label]
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'is idempotent' do
|
21
|
+
same_object = Police::DataFlow.label @object, @label
|
22
|
+
same_object.must_equal @object
|
23
|
+
Police::DataFlow.labels(@object).must_equal [@label]
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns a working proxy' do
|
27
|
+
@object.length.must_equal 5
|
28
|
+
@object.class.must_equal @class
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns a label-preserving proxy' do
|
32
|
+
@object << ' world'
|
33
|
+
@object.length.must_equal 11
|
34
|
+
Police::DataFlow.labels(@object).must_equal [@label]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'with two labels' do
|
39
|
+
before do
|
40
|
+
@label2 = NoFlowFixture.new
|
41
|
+
@object = Police::DataFlow.label @object, @label
|
42
|
+
@object = Police::DataFlow.label @object, @label2
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'labels the object correctly' do
|
46
|
+
Set.new(Police::DataFlow.labels(@object)).
|
47
|
+
must_equal Set.new([@label, @label2])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Police::DataFlow do
|
4
|
+
before do
|
5
|
+
@object = 'Hello'
|
6
|
+
@class = @object.class
|
7
|
+
@p_label = AutoFlowFixture.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#label' do
|
11
|
+
describe 'with a single label' do
|
12
|
+
before do
|
13
|
+
@object = Police::DataFlow.label @object, @p_label
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'labels the object correctly' do
|
17
|
+
Police::DataFlow.labels(@object).must_equal [@p_label]
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'is idempotent' do
|
21
|
+
same_object = Police::DataFlow.label @object, @p_label
|
22
|
+
same_object.must_equal @object
|
23
|
+
Police::DataFlow.labels(@object).must_equal [@p_label]
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'is idempotent vs label type' do
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns a working proxy' do
|
31
|
+
@object.length.must_equal 5
|
32
|
+
@object.class.must_equal @class
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns a label-preserving proxy' do
|
36
|
+
@object << ' world'
|
37
|
+
@object.length.must_equal 11
|
38
|
+
Police::DataFlow.labels(@object).must_equal [@p_label]
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns a label-propagating proxy' do
|
42
|
+
Police::DataFlow.labels(@object[2..5]).must_equal [@p_label]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'with two labels' do
|
47
|
+
before do
|
48
|
+
@n_label = NoFlowFixture.new
|
49
|
+
@object = Police::DataFlow.label @object, @p_label
|
50
|
+
@object = Police::DataFlow.label @object, @n_label
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'labels the object correctly' do
|
54
|
+
Set.new(Police::DataFlow.labels(@object)).
|
55
|
+
must_equal Set.new([@p_label, @n_label])
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'propagates the labels correctly' do
|
59
|
+
Police::DataFlow.labels(@object[2..5]).must_equal [@p_label]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#labels' do
|
65
|
+
it 'returns an empty array for un-labeled objects' do
|
66
|
+
Police::DataFlow.labels(@object).must_equal []
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns a populated array for labeled objects' do
|
70
|
+
labeled = Police::DataFlow.label @object, @p_label
|
71
|
+
Police::DataFlow.labels(labeled).must_equal [@p_label]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Police::DataFlow::Proxies do
|
4
|
+
describe '#for' do
|
5
|
+
before do
|
6
|
+
@label_set = {}
|
7
|
+
@autoflow_set = {}
|
8
|
+
Police::DataFlow::Labeling.add_label_to_set NoFlowFixture.new,
|
9
|
+
@label_set, @autoflow_set
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:result) do
|
13
|
+
Police::DataFlow::Proxies.for ProxyingFixture, @label_set
|
14
|
+
end
|
15
|
+
after { Police::DataFlow::Proxies.clear_cache }
|
16
|
+
|
17
|
+
it 'creates a Police::DataFlow::ProxyBase subclass' do
|
18
|
+
result.superclass.must_equal Police::DataFlow::ProxyBase
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'caches classes' do
|
22
|
+
Police::DataFlow::Proxies.for(ProxyingFixture, @label_set).
|
23
|
+
must_equal result
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'creates different proxies for different classes' do
|
27
|
+
Police::DataFlow::Proxies.for(Class.new(ProxyingFixture), @label_set).
|
28
|
+
wont_equal result
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'creates different proxies for sets of different label classes' do
|
32
|
+
label_set = @label_set.clone
|
33
|
+
autoflow_set = @autoflow_set.clone
|
34
|
+
Police::DataFlow::Labeling.add_label_to_set AutoFlowFixture.new,
|
35
|
+
label_set, autoflow_set
|
36
|
+
Police::DataFlow::Proxies.for(ProxyingFixture, label_set).
|
37
|
+
wont_equal result
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require File.expand_path('../helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Police::DataFlow::ProxyBase do
|
4
|
+
before do
|
5
|
+
@label = AutoFlowFixture.new
|
6
|
+
@label_set = {}
|
7
|
+
@autoflow_set = {}
|
8
|
+
Police::DataFlow::Labeling.add_label_to_set @label, @label_set,
|
9
|
+
@autoflow_set
|
10
|
+
@proxied = ProxyingFixture.new
|
11
|
+
@proxy_class = Police::DataFlow::Proxies.for ProxyingFixture, @label_set
|
12
|
+
@proxy = @proxy_class.new @proxied, @proxy_class, @label_set, @autoflow_set
|
13
|
+
end
|
14
|
+
after { Police::DataFlow::Proxies.clear_cache }
|
15
|
+
|
16
|
+
it 'proxies public methods' do
|
17
|
+
# NOTE: this test exercises the slow path in method_missing
|
18
|
+
@proxy.route('One', 'Two').must_equal ['One', 'Two']
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'allows labels to filter the return value of public methods' do
|
22
|
+
# NOTE: this test exercises the slow path in method_missing
|
23
|
+
Police::DataFlow.labels(@proxy.route('One', 'Two')).must_equal [@label]
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'after proxying public methods' do
|
27
|
+
before { @proxy.route 'One', 'Two' }
|
28
|
+
|
29
|
+
it 'defines proxied methods on the fly' do
|
30
|
+
@proxy_class.public_method_defined?(:route).must_equal true
|
31
|
+
@proxy_class.instance_method(:route).owner.must_equal @proxy_class
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'still proxies public methods' do
|
35
|
+
# NOTE: this test exercises the auto-generated proxy method's fast path
|
36
|
+
@proxy.route('One', 'Two').must_equal ['One', 'Two']
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'still allows labels to filter the return value of public methods' do
|
40
|
+
# NOTE: this test exercises the auto-generated proxy method's fast path
|
41
|
+
Police::DataFlow.labels(@proxy.route('One', 'Two')).must_equal [@label]
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'can still build proxies' do
|
45
|
+
other_proxy = @proxy_class.new ProxyingFixture.new, @proxy_class,
|
46
|
+
@label_set, @autoflow_set
|
47
|
+
other_proxy.route('One', 'Two').must_equal ['One', 'Two']
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'proxies public methods with blocks' do
|
52
|
+
# NOTE: this test exercises the slow path in method_missing
|
53
|
+
result = []
|
54
|
+
@proxy.route('One', 'Two') { |*args| result << args }
|
55
|
+
result.must_equal [['One', 'Two']]
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'allows labels to filter the yielded values of public methods' do
|
59
|
+
# NOTE: this test exercises the slow path in method_missing
|
60
|
+
@proxy.route('One', 'Two') do |*args|
|
61
|
+
args.each { |arg| Police::DataFlow.labels(arg).must_equal [@label] }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'after proxying public methods with blocks' do
|
66
|
+
before { @proxy.route('One', 'Two') { |*args| } }
|
67
|
+
|
68
|
+
it 'defines proxied methods on the fly' do
|
69
|
+
@proxy_class.public_method_defined?(:route).must_equal true
|
70
|
+
@proxy_class.instance_method(:route).owner.must_equal @proxy_class
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'still proxies public methods with blocks' do
|
74
|
+
# NOTE: this test exercises the auto-generated proxy method's fast path
|
75
|
+
result = []
|
76
|
+
@proxy.route('One', 'Two') { |*args| result << args }
|
77
|
+
result.must_equal [['One', 'Two']]
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'still allows labels to filter the yielded values of public methods' do
|
81
|
+
# NOTE: this test exercises the auto-generated proxy method's fast path
|
82
|
+
@proxy.route('One', 'Two') do |*args|
|
83
|
+
args.each { |arg| Police::DataFlow.labels(arg).must_equal [@label] }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'proxies protected methods' do
|
89
|
+
# NOTE: this test exercises the slow path in method_missing
|
90
|
+
@proxy.__send__(:add, 'One', 'Two').must_equal 'One, Two'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'allows labels to filter the return value of protected methods' do
|
94
|
+
# NOTE: this test exercises the slow path in method_missing
|
95
|
+
Police::DataFlow.labels(@proxy.__send__(:add, 'One', 'Two')).
|
96
|
+
must_equal [@label]
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
describe 'after proxying protected methods' do
|
101
|
+
before { @proxy.__send__ :add, 'One', 'Two' }
|
102
|
+
|
103
|
+
it 'defines proxied methods on the fly' do
|
104
|
+
@proxy_class.protected_method_defined?(:add).must_equal true
|
105
|
+
@proxy_class.instance_method(:add).owner.must_equal @proxy_class
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'still proxies protected methods' do
|
109
|
+
# NOTE: this test exercises the auto-generated proxy method's fast path
|
110
|
+
@proxy.__send__(:add, 'One', 'Two').must_equal 'One, Two'
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'still allows labels to filter the return value of protected methods' do
|
114
|
+
# NOTE: this test exercises the auto-generated proxy method's fast path
|
115
|
+
Police::DataFlow.labels(@proxy.__send__(:add, 'One', 'Two')).
|
116
|
+
must_equal [@label]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'proxies magic methods' do
|
121
|
+
@proxy.magic_meth('One', 'Two').must_equal ['meth', 'One', 'Two']
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'allows labels to filter the return value of magic methods' do
|
125
|
+
Police::DataFlow.labels(@proxy.magic_meth('One', 'Two')).must_equal [@label]
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'after proxying magic methods' do
|
129
|
+
before do
|
130
|
+
@proxy.magic_meth 'One', 'Two'
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'does not define magic proxied methods' do
|
134
|
+
@proxy_class.public_method_defined?(:magic_meth).must_equal false
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'proxies ==' do
|
139
|
+
(@proxy == nil).must_equal '== proxied'
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'proxies !=' do
|
143
|
+
(@proxy != nil).must_equal '!= proxied'
|
144
|
+
end
|
145
|
+
end
|