self_identity 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +57 -0
- data/Rakefile +5 -0
- data/kintama/expected/outcomes.yaml +45 -0
- data/kintama/scripts/basic_usage.rb +12 -0
- data/kintama/scripts/calls_and_returns.rb +12 -0
- data/kintama/scripts/nested_calls.rb +17 -0
- data/kintama/scripts/passthrough_methods.rb +18 -0
- data/kintama/scripts/tracking_id_not_value.rb +17 -0
- data/kintama/self_identity.rb +38 -0
- data/lib/data_constructors.rb +48 -0
- data/lib/self_identity.rb +52 -0
- data/self_identity.gemspec +18 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 76cc850d44cf9d6588f6cd346620dee7941f6d28
|
4
|
+
data.tar.gz: cff98d7975aba2c313bd8f4f41abdf8a91ae3be0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a8e3b1582a4093f61a57eff22955a1d9e2df58b0dd191156cfd952f216fa2b2532c0b6cfea99ce09fe508ba672a32237bc1f1d5d431a51787c73f259b44354ff
|
7
|
+
data.tar.gz: b93ae7bda9e8c6e2205cec178e642bffb51174aff16046bfb50486133ae6a834c362c76702e9c71f35204fb90f734760a10850d0e6096f5e81f5d51f8b2a74d0
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Chris Olstrom and Justin Scott
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
self_identity
|
2
|
+
=============
|
3
|
+
|
4
|
+
Helping code understand itself, an adventure in alternative testing methodology.
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
|
9
|
+
`require 'self_identity'` and your code will record its method dependencies each time it runs.
|
10
|
+
|
11
|
+
From the working directory the script was run from, you'll find a [Moneta](https://github.com/minad/moneta) file storage with the following arrays and element formats:
|
12
|
+
|
13
|
+
### calls
|
14
|
+
|
15
|
+
- __name__: the method called
|
16
|
+
- __input_reference__: object_id's of the input
|
17
|
+
- __input__: the given input array
|
18
|
+
|
19
|
+
### returns
|
20
|
+
|
21
|
+
- __name__: the method returned from
|
22
|
+
- __output_reference__: object_id of the output
|
23
|
+
- __output__: the generated output
|
24
|
+
|
25
|
+
### dependencies
|
26
|
+
|
27
|
+
- __output__: the object passed between the methods
|
28
|
+
- __from__: the method returned from
|
29
|
+
- __to__: the method called
|
30
|
+
|
31
|
+
Background
|
32
|
+
----------
|
33
|
+
|
34
|
+
At the end of 2014, @brixen posted a fascinating rant on the state of RubySpec. One bit stuck out as inspiring: "Ruby is what Ruby does."
|
35
|
+
|
36
|
+
MRI 1.8 had pretty spotty (or no) test coverage. When confirming the accuracy of Rubinius in the face of an absent or incomplete test suite, @brixen asserted that "The way a Ruby program behaves is the definition of Ruby". From this assertion came RubySpec, a comprehensive suite of specifications describing "what Ruby does" so alternative implementations could do the same thing.
|
37
|
+
|
38
|
+
self_identity applies this mindset to automated testing.
|
39
|
+
|
40
|
+
Objectives
|
41
|
+
----------
|
42
|
+
|
43
|
+
1. Be able to define method signatures. Given input x (of type a) to method m, the output will be y (of type b).
|
44
|
+
2. Be able to define what qualifies as valid input. For input x, what is it required to ```respond_to?``` within method m.
|
45
|
+
3. Be able to define what qualifies as valid output. For output y from method m, what will it be expected to ```respond_to?``` from creation until it is reaped by the garbage collection valkyries.
|
46
|
+
|
47
|
+
_Together, these allow a program to assert its identity: the collection of canonical method signatures and object interactions that state "This is what I am, and how I operate."_
|
48
|
+
|
49
|
+
Notes
|
50
|
+
-----
|
51
|
+
|
52
|
+
This is not a replacement for *good* tests, or specs. It does not validate that the code is correct (beyond the syntax checking inherent in running the program), or that it performs as expected. However, being able to assert that "it did what it did last time, given the same conditions" is better than untested code.
|
53
|
+
|
54
|
+
Authors
|
55
|
+
-------
|
56
|
+
|
57
|
+
Chris Olstrom and Justin Scott
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
---
|
2
|
+
- name: basic_usage
|
3
|
+
dependencies:
|
4
|
+
- :output: [1]
|
5
|
+
:from: :foo
|
6
|
+
:to: :bar
|
7
|
+
|
8
|
+
- name: nested_calls
|
9
|
+
dependencies:
|
10
|
+
- :output: [1]
|
11
|
+
:from: :foo
|
12
|
+
:to: :bar
|
13
|
+
- :output: [1]
|
14
|
+
:from: :foo
|
15
|
+
:to: :baz
|
16
|
+
|
17
|
+
- name: calls_and_returns
|
18
|
+
calls:
|
19
|
+
- :name: :foo
|
20
|
+
:input: [[]]
|
21
|
+
- :name: :bar
|
22
|
+
:input: [[1]]
|
23
|
+
returns:
|
24
|
+
- :name: :foo
|
25
|
+
:output: [1]
|
26
|
+
- :name: :bar
|
27
|
+
:output: [1, 2]
|
28
|
+
|
29
|
+
- name: passthrough_methods
|
30
|
+
dependencies:
|
31
|
+
- :output: [1]
|
32
|
+
:from: :foo
|
33
|
+
:to: :bar
|
34
|
+
- :output: [1]
|
35
|
+
:from: :foo
|
36
|
+
:to: :baz
|
37
|
+
- :output: [1]
|
38
|
+
:from: :bar
|
39
|
+
:to: :baz
|
40
|
+
|
41
|
+
- name: tracking_id_not_value
|
42
|
+
dependencies:
|
43
|
+
- :output: [1,2]
|
44
|
+
:from: :bar
|
45
|
+
:to: :baz
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'kintama'
|
2
|
+
require 'yaml'
|
3
|
+
require 'moneta'
|
4
|
+
#require_relative 'helpers'
|
5
|
+
# TODO: clean this up with helpers and such
|
6
|
+
|
7
|
+
$scripts = YAML.load_file "#{__dir__}/expected/outcomes.yaml"
|
8
|
+
|
9
|
+
def remove_reference_keys_for(hash)
|
10
|
+
hash.reject! { |key| key.match /reference/ }
|
11
|
+
end
|
12
|
+
|
13
|
+
given 'self_identity is required' do
|
14
|
+
setup do
|
15
|
+
@script_dir = "#{__dir__}/scripts"
|
16
|
+
@storage = Moneta.new :File, dir: '.self_identity'
|
17
|
+
end
|
18
|
+
|
19
|
+
$scripts.each do |script|
|
20
|
+
context "running scripts for #{script['name']}" do
|
21
|
+
setup do
|
22
|
+
script_path = "#{@script_dir}/#{script['name']}.rb"
|
23
|
+
thread = Thread.new { system "ruby #{script_path}" }
|
24
|
+
thread.join
|
25
|
+
end
|
26
|
+
|
27
|
+
['calls', 'returns', 'dependencies'].each do |data|
|
28
|
+
if script[data]
|
29
|
+
should "archive method #{data}" do
|
30
|
+
actual = @storage.fetch data, []
|
31
|
+
actual.each { |hash| remove_reference_keys_for hash }
|
32
|
+
assert_equal script[data], actual
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
def safe_clone(object)
|
2
|
+
begin
|
3
|
+
object.clone
|
4
|
+
rescue TypeError => e
|
5
|
+
object
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def sanitize(object)
|
10
|
+
if object.is_a? Proc
|
11
|
+
object.to_s
|
12
|
+
else
|
13
|
+
safe_clone object
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def new_method_call(from:)
|
18
|
+
parameters = from.binding.eval "method(__method__).parameters.map { |p| eval p.last.to_s }"
|
19
|
+
{
|
20
|
+
name: from.method_id,
|
21
|
+
input_reference: parameters.map(&:object_id),
|
22
|
+
input: parameters.map { |parameter| sanitize parameter }
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def new_method_return(from:)
|
27
|
+
{
|
28
|
+
name: from.method_id,
|
29
|
+
output_reference: from.return_value.object_id,
|
30
|
+
output: sanitize(from.return_value)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def returns_that_pass_to(current)
|
35
|
+
@returns.select do |previous|
|
36
|
+
current[:input_reference].detect { |given_input| given_input.equal? previous[:output_reference] }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def dependencies_for(method_call)
|
41
|
+
returns_that_pass_to(method_call).map do |method_return|
|
42
|
+
{
|
43
|
+
output: method_return[:output],
|
44
|
+
from: method_return[:name],
|
45
|
+
to: method_call[:name]
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'moneta'
|
2
|
+
require_relative 'data_constructors'
|
3
|
+
|
4
|
+
storage = Moneta.new :File, dir: '.self_identity'
|
5
|
+
storage['calls'] = []
|
6
|
+
storage['returns'] = []
|
7
|
+
storage['dependencies'] = []
|
8
|
+
|
9
|
+
@trace = TracePoint.trace do |trace|
|
10
|
+
@calls ||= []
|
11
|
+
@returns ||= []
|
12
|
+
@dependencies ||= []
|
13
|
+
case trace.event
|
14
|
+
when :call
|
15
|
+
method_call = new_method_call from: trace
|
16
|
+
@dependencies.concat dependencies_for(method_call)
|
17
|
+
@calls.push method_call
|
18
|
+
when :return
|
19
|
+
@returns.push new_method_return(from: trace)
|
20
|
+
when :b_return
|
21
|
+
# not sure how to hook into method blocks
|
22
|
+
# __method__ returns the method it was called from
|
23
|
+
when :c_return
|
24
|
+
# might need a C extension to hook into C calls
|
25
|
+
# __method__ returns 'main'
|
26
|
+
else
|
27
|
+
end
|
28
|
+
script_name = File.basename($PROGRAM_NAME, '.rb')
|
29
|
+
storage.store 'calls', @calls
|
30
|
+
storage.store 'returns', @returns
|
31
|
+
storage.store 'dependencies', @dependencies
|
32
|
+
end
|
33
|
+
|
34
|
+
module SelfIdentity
|
35
|
+
@@trace = @trace
|
36
|
+
|
37
|
+
def self.enabled?
|
38
|
+
@@trace.enabled?
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.enable
|
42
|
+
@@trace.enable
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.disable
|
46
|
+
@@trace.disable
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@trace = nil
|
51
|
+
|
52
|
+
# don't put anything here unless you want it traced
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = 'self_identity'
|
3
|
+
gem.version = '0.1.0'
|
4
|
+
gem.licenses = 'MIT'
|
5
|
+
gem.authors = ['Justin Scott','Chris Olstrom']
|
6
|
+
gem.email = 'jvscott@gmail.com'
|
7
|
+
gem.homepage = 'http://www.github.com/colstrom/self_identity/'
|
8
|
+
gem.summary = 'Self Identity'
|
9
|
+
gem.description = 'Allowing code to understand its own functionality.'
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split("\n")
|
12
|
+
gem.test_files = `git ls-files -- kintama/**/*`.split("\n")
|
13
|
+
|
14
|
+
gem.add_runtime_dependency 'moneta'
|
15
|
+
|
16
|
+
gem.add_development_dependency 'rake'
|
17
|
+
gem.add_development_dependency 'kintama'
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: self_identity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Scott
|
8
|
+
- Chris Olstrom
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-03-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: moneta
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: kintama
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: Allowing code to understand its own functionality.
|
57
|
+
email: jvscott@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- kintama/expected/outcomes.yaml
|
68
|
+
- kintama/scripts/basic_usage.rb
|
69
|
+
- kintama/scripts/calls_and_returns.rb
|
70
|
+
- kintama/scripts/nested_calls.rb
|
71
|
+
- kintama/scripts/passthrough_methods.rb
|
72
|
+
- kintama/scripts/tracking_id_not_value.rb
|
73
|
+
- kintama/self_identity.rb
|
74
|
+
- lib/data_constructors.rb
|
75
|
+
- lib/self_identity.rb
|
76
|
+
- self_identity.gemspec
|
77
|
+
homepage: http://www.github.com/colstrom/self_identity/
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.4.5
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Self Identity
|
101
|
+
test_files:
|
102
|
+
- kintama/expected/outcomes.yaml
|
103
|
+
- kintama/scripts/basic_usage.rb
|
104
|
+
- kintama/scripts/calls_and_returns.rb
|
105
|
+
- kintama/scripts/nested_calls.rb
|
106
|
+
- kintama/scripts/passthrough_methods.rb
|
107
|
+
- kintama/scripts/tracking_id_not_value.rb
|