pretentious 0.1.6 → 0.1.7
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/README.md +14 -5
- data/bin/ddtgen +13 -19
- data/lib/pretentious.rb +31 -31
- data/lib/pretentious/context.rb +86 -0
- data/lib/pretentious/deconstructor.rb +305 -368
- data/lib/pretentious/generator.rb +122 -149
- data/lib/pretentious/generator_base.rb +70 -9
- data/lib/pretentious/minitest_generator.rb +172 -288
- data/lib/pretentious/recorded_proc.rb +4 -16
- data/lib/pretentious/rspec_generator.rb +151 -262
- data/lib/pretentious/trigger.rb +30 -30
- data/lib/pretentious/version.rb +2 -1
- data/pretentious.gemspec +3 -0
- data/run_test.sh +1 -1
- data/spec/deconstructor_spec.rb +34 -20
- data/spec/fibonacci_spec.rb +7 -14
- data/spec/generator_spec.rb +1 -1
- data/spec/m_d5_spec.rb +3 -7
- data/spec/minitest_generator_spec.rb +2 -2
- data/spec/prententious_spec.rb +14 -4
- data/spec/spec_helper.rb +3 -1
- data/spec/test_class1_spec.rb +23 -38
- data/spec/test_class2_spec.rb +7 -16
- data/spec/test_class3_spec.rb +11 -18
- data/spec/test_class4_spec.rb +4 -7
- data/spec/test_class_for_auto_stub_spec.rb +5 -10
- data/spec/test_class_for_mocks_spec.rb +10 -19
- data/test/test_fibonacci.rb +72 -10
- data/test/test_m_d5.rb +4 -5
- data/test/test_meme.rb +6 -6
- data/test/test_test_class1.rb +36 -29
- data/test/test_test_class2.rb +16 -12
- data/test/test_test_class3.rb +15 -17
- data/test/test_test_class4.rb +6 -7
- data/test/test_test_class_for_mocks.rb +19 -16
- metadata +46 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 573e7785c064f86ac8001339041dbb8387cb6679
|
4
|
+
data.tar.gz: aae975ba4c83fba283e0758a456674b0c5f0b86d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb6534fe86d6865035b3f5e160275dbdd194094920afeba24157820a1a0fcd87c78cb78246fe8139440369b96b4120f201c27a9af58724c7a20b7876f7538e77
|
7
|
+
data.tar.gz: dd0b37c00a1375b4323dd3041c9f0ed866499a3b38a58c86b9b03cd9631fd254a76d7acd6c7c73a7def5ea6e303ce7e059bf303394e01968546b0fc6dc4de0d6
|
data/README.md
CHANGED
@@ -210,6 +210,9 @@ RSpec.describe Digest::MD5 do
|
|
210
210
|
end
|
211
211
|
```
|
212
212
|
|
213
|
+
Note: If your test subject is already part of a larger application and would like to capture behavior in the manner that
|
214
|
+
the application uses it, please look at [Declarative Generation](#declarative-generation-without-using-example-files).
|
215
|
+
|
213
216
|
## Minitest
|
214
217
|
|
215
218
|
The minitest test framework is also supported, simply use Pretentious.minitest_for instead
|
@@ -256,14 +259,16 @@ modifying source codes. This is useful for testing code embedded inside framewor
|
|
256
259
|
"example" is already embedded inside existing code.
|
257
260
|
|
258
261
|
For example lets say you want to generate tests for UserAuthenticaion that is used inside the
|
259
|
-
login method inside the UsersController inside a
|
262
|
+
login method inside the UsersController inside a Rails app. You'd simply define like below:
|
260
263
|
|
261
264
|
|
262
265
|
```ruby
|
263
266
|
# initializers/pretentious.rb
|
264
267
|
|
265
|
-
|
266
|
-
Pretentious.on(UsersController).method_called(:login
|
268
|
+
if Rails.env.test? #IMPORTANT don't run this when you don't need it!
|
269
|
+
Pretentious.on(UsersController).method_called(:login).spec_for(UserAuthentication) #RSPEC
|
270
|
+
Pretentious.on(UsersController).method_called(:login, :logout, ...).minitest_for(UserAuthentication) #minitest
|
271
|
+
end
|
267
272
|
|
268
273
|
# spec files will be written to the project root
|
269
274
|
```
|
@@ -279,13 +284,17 @@ You can pass a block for manually handling the output, for example
|
|
279
284
|
```ruby
|
280
285
|
# initializers/pretentious.rb
|
281
286
|
|
282
|
-
|
283
|
-
|
287
|
+
if Rails.env.test? #IMPORTANT don't run this when you don't need it!
|
288
|
+
Pretentious.on(UsersController).method_called(:login).spec_for(UserAuthentication) do |results|
|
289
|
+
puts results[UserAuthentication][:output]
|
290
|
+
end
|
284
291
|
end
|
285
292
|
|
286
293
|
# spec files will be written to the project root
|
287
294
|
```
|
288
295
|
|
296
|
+
IMPORTANT: If using rails or if it is part of a larger app, make sure to enable this only when you intend to generate specs!
|
297
|
+
delete the initializer or comment the code out when it is not needed.
|
289
298
|
|
290
299
|
## Handling complex parameters and object constructors
|
291
300
|
|
data/bin/ddtgen
CHANGED
@@ -3,54 +3,48 @@
|
|
3
3
|
require 'pretentious'
|
4
4
|
require 'optparse'
|
5
5
|
require 'ripper'
|
6
|
-
require
|
6
|
+
require 'readline'
|
7
7
|
require 'json'
|
8
8
|
require 'fileutils'
|
9
9
|
|
10
10
|
# ddtgen example.rb -t rspec -o rspec/
|
11
11
|
options = OptionParser.new do |o|
|
12
12
|
o.banner =
|
13
|
-
"Usage: ddtgen FILENAME [options] # Generates tests using the specified example file\n"
|
14
|
-
o.separator
|
13
|
+
"Usage: ddtgen FILENAME [options] # Generates tests using the specified example file\n"
|
14
|
+
o.separator ''
|
15
15
|
o.separator "options:"
|
16
|
-
o.on('-o','--output-dir','folder to place the files in') { |b| $output_folder = b}
|
16
|
+
o.on('-o', '--output-dir', 'folder to place the files in') { |b| $output_folder = b}
|
17
17
|
o.parse!
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
20
|
filename = ARGV[0]
|
22
21
|
|
23
22
|
if filename.nil?
|
24
|
-
puts
|
23
|
+
puts 'an example file is required.'
|
25
24
|
puts options
|
26
25
|
exit(1)
|
27
26
|
end
|
28
27
|
|
29
|
-
example_body =
|
28
|
+
example_body = ''
|
30
29
|
|
31
30
|
index = 0
|
32
31
|
File.open(filename, "r") do |f|
|
33
32
|
f.each_line do |line|
|
34
|
-
|
33
|
+
example_body << "#{line}\n"
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
37
|
eval(example_body, binding, filename, 1)
|
39
38
|
|
40
|
-
#collect results
|
41
|
-
|
42
|
-
|
43
|
-
Pretentious.last_results.each { |g, result_per_generator|
|
39
|
+
# collect results
|
40
|
+
Pretentious.last_results.each do |g, result_per_generator|
|
44
41
|
puts "#{g}:"
|
45
|
-
result_per_generator.each
|
42
|
+
result_per_generator.each do |klass, result|
|
46
43
|
output_folder = result[:generator].location(output_folder)
|
47
44
|
FileUtils.mkdir_p output_folder
|
48
45
|
result[:generator].helper(output_folder)
|
49
46
|
filename = result[:generator].naming(output_folder, klass)
|
50
|
-
File.open(filename, 'w') {
|
51
|
-
|f| f.write(result[:output])
|
52
|
-
}
|
47
|
+
File.open(filename, 'w') { |f| f.write(result[:output]) }
|
53
48
|
puts "#{filename}"
|
54
|
-
|
55
|
-
|
56
|
-
|
49
|
+
end
|
50
|
+
end
|
data/lib/pretentious.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
1
|
+
require 'pretentious/version'
|
2
|
+
require 'pretentious/context'
|
3
|
+
require 'pretentious/generator_base'
|
4
|
+
require 'pretentious/rspec_generator'
|
5
|
+
require 'pretentious/minitest_generator'
|
6
|
+
require 'pretentious/recorded_proc'
|
7
|
+
require 'pretentious/generator'
|
7
8
|
require 'binding_of_caller'
|
8
9
|
require 'pretentious/deconstructor'
|
9
10
|
require 'pretentious/trigger'
|
10
11
|
|
11
12
|
Class.class_eval do
|
12
|
-
|
13
13
|
def _stub(*classes)
|
14
14
|
@classes = classes
|
15
15
|
self
|
@@ -18,11 +18,9 @@ Class.class_eval do
|
|
18
18
|
def _get_mock_classes
|
19
19
|
@classes
|
20
20
|
end
|
21
|
-
|
22
21
|
end
|
23
22
|
|
24
23
|
Thread.class_eval do
|
25
|
-
|
26
24
|
def _push_context(context)
|
27
25
|
@_context ||= []
|
28
26
|
@_context << context
|
@@ -42,25 +40,27 @@ Thread.class_eval do
|
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
43
|
+
# The main class to use for pretentious testing
|
45
44
|
module Pretentious
|
46
|
-
|
45
|
+
# misc convenience tools
|
47
46
|
module DdtUtils
|
48
47
|
def self.to_underscore(str)
|
49
|
-
str.gsub(/(.)([A-Z])/,'\1_\2').downcase
|
48
|
+
str.gsub(/(.)([A-Z])/, '\1_\2').downcase
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
53
|
-
|
54
52
|
def self.spec_for(*klasses, &block)
|
55
53
|
@spec_results ||= {}
|
56
54
|
Pretentious::Generator.test_generator = Pretentious::RspecGenerator
|
57
|
-
@spec_results.merge!(Pretentious::Generator
|
55
|
+
@spec_results.merge!(Pretentious::Generator
|
56
|
+
.generate_for(*klasses, &block))
|
58
57
|
end
|
59
58
|
|
60
59
|
def self.minitest_for(*klasses, &block)
|
61
60
|
@minitest_results ||= {}
|
62
61
|
Pretentious::Generator.test_generator = Pretentious::MinitestGenerator
|
63
|
-
@minitest_results.merge!(Pretentious::Generator
|
62
|
+
@minitest_results.merge!(Pretentious::Generator
|
63
|
+
.generate_for(*klasses, &block))
|
64
64
|
end
|
65
65
|
|
66
66
|
def self.clear_results
|
@@ -69,7 +69,7 @@ module Pretentious
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def self.last_results
|
72
|
-
{spec: @spec_results,
|
72
|
+
{ spec: @spec_results, minitest: @minitest_results }
|
73
73
|
end
|
74
74
|
|
75
75
|
def self.install_watcher
|
@@ -80,23 +80,23 @@ module Pretentious
|
|
80
80
|
Pretentious::Generator.unwatch_new_instances
|
81
81
|
end
|
82
82
|
|
83
|
-
def self.value_ize(
|
84
|
-
if
|
85
|
-
"#{value
|
86
|
-
elsif
|
87
|
-
":#{value
|
88
|
-
elsif
|
89
|
-
|
90
|
-
elsif
|
91
|
-
|
92
|
-
elsif
|
93
|
-
|
94
|
-
elsif Pretentious::Deconstructor.
|
95
|
-
"#{value
|
96
|
-
elsif
|
97
|
-
|
83
|
+
def self.value_ize(context, value)
|
84
|
+
if value.is_a? String
|
85
|
+
"'#{value}'"
|
86
|
+
elsif value.is_a? Symbol
|
87
|
+
":#{value}"
|
88
|
+
elsif value.is_a? Hash
|
89
|
+
context.pick_name(value.object_id)
|
90
|
+
elsif value.is_a? Pretentious::RecordedProc
|
91
|
+
context.pick_name(value.target_proc.object_id)
|
92
|
+
elsif value.nil?
|
93
|
+
'nil'
|
94
|
+
elsif Pretentious::Deconstructor.primitive?(value)
|
95
|
+
"#{value}"
|
96
|
+
elsif context.variable_map && context.variable_map[value.object_id]
|
97
|
+
context.pick_name(value.object_id)
|
98
98
|
else
|
99
|
-
"#{value
|
99
|
+
"#{value}"
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Pretentious
|
2
|
+
# Contains references to scoped variables
|
3
|
+
class Context
|
4
|
+
attr_accessor :declared_names, :variable_map,
|
5
|
+
:previous_declarations
|
6
|
+
|
7
|
+
def initialize(variable_map = {}, declared_names = {}, previous_declarations = {})
|
8
|
+
@declared_names = declared_names
|
9
|
+
@variable_map = variable_map
|
10
|
+
@previous_declarations = previous_declarations
|
11
|
+
end
|
12
|
+
|
13
|
+
def subcontext(declarations)
|
14
|
+
previous_declarations = {}
|
15
|
+
|
16
|
+
declarations.select { |d| d[:used_by] != :inline }.each do |d|
|
17
|
+
previous_declarations[d[:id]] = pick_name(d[:id])
|
18
|
+
end
|
19
|
+
|
20
|
+
Pretentious::Context.new(@variable_map, {}, previous_declarations)
|
21
|
+
end
|
22
|
+
|
23
|
+
def was_declared_previously?(object_id)
|
24
|
+
@previous_declarations.key? object_id
|
25
|
+
end
|
26
|
+
|
27
|
+
def merge_variable_map(target_object)
|
28
|
+
@variable_map.merge!(target_object._variable_map) if target_object.methods.include?(:_variable_map) && !target_object._variable_map.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def register_instance_variable(object_id)
|
32
|
+
@variable_map[object_id] = "@#{@previous_declarations[object_id]}" if @previous_declarations[object_id][0]!='@'
|
33
|
+
end
|
34
|
+
|
35
|
+
def register(object_id, name)
|
36
|
+
@variable_map[object_id] = name
|
37
|
+
end
|
38
|
+
|
39
|
+
def dump
|
40
|
+
puts "v-map #{@variable_map.inspect}"
|
41
|
+
puts "d-map #{@declared_names.inspect}"
|
42
|
+
puts "p-map #{@previous_declarations.inspect}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def pick_name(object_id, value = :no_value_passed)
|
46
|
+
var_name = "var_#{object_id}"
|
47
|
+
|
48
|
+
object_id_to_declared_names = {}
|
49
|
+
|
50
|
+
if @declared_names
|
51
|
+
@declared_names.each { |k, v| object_id_to_declared_names[v[:object_id]] = k if v }
|
52
|
+
end
|
53
|
+
# return immediately if already mapped
|
54
|
+
return object_id_to_declared_names[object_id] if object_id_to_declared_names.include? object_id
|
55
|
+
|
56
|
+
if !@variable_map.nil? && @variable_map.include?(object_id)
|
57
|
+
|
58
|
+
candidate_name = @variable_map[object_id].to_s
|
59
|
+
if !@declared_names.include?(candidate_name)
|
60
|
+
var_name = candidate_name
|
61
|
+
@declared_names[candidate_name] = { count: 1, object_id: object_id }
|
62
|
+
else
|
63
|
+
|
64
|
+
if @declared_names[candidate_name][:object_id] == object_id
|
65
|
+
var_name = candidate_name
|
66
|
+
else
|
67
|
+
new_name = "#{candidate_name}_#{@declared_names[candidate_name][:count]}"
|
68
|
+
var_name = "#{new_name}"
|
69
|
+
|
70
|
+
@declared_names[candidate_name][:count] += 1
|
71
|
+
@declared_names[new_name] = { count: 1, object_id: object_id }
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
else
|
76
|
+
return value_of(value) if value != :no_value_passed
|
77
|
+
end
|
78
|
+
|
79
|
+
var_name
|
80
|
+
end
|
81
|
+
|
82
|
+
def value_of(value)
|
83
|
+
Pretentious.value_ize(self, value)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -1,446 +1,383 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
module Pretentious
|
2
|
+
# Deconstructor - decompose an object into its parts
|
3
|
+
class Deconstructor
|
4
|
+
# Represents an unresolved class
|
5
|
+
class UnResolved
|
6
|
+
attr_accessor :target_object
|
7
|
+
|
8
|
+
def initialize(object)
|
9
|
+
@target_object = object
|
10
|
+
end
|
9
11
|
end
|
10
|
-
end
|
11
|
-
|
12
|
-
class Reference
|
13
|
-
attr_accessor :tree
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
13
|
+
# Represents a reference
|
14
|
+
class Reference
|
15
|
+
attr_accessor :tree
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
arr.each { |v|
|
23
|
-
if Pretentious::Deconstructor.is_primitive?(v)
|
24
|
-
value << v
|
25
|
-
elsif v.is_a? Hash
|
26
|
-
value << dfs_hash(v, refs)
|
27
|
-
elsif v.is_a? Array
|
28
|
-
value << dfs_array(v, refs)
|
29
|
-
elsif v.is_a? Reference
|
30
|
-
refs << v.tree[:id]
|
31
|
-
value << Reference.new(dfs(v.tree))
|
32
|
-
elsif value << v
|
17
|
+
def initialize(tree)
|
18
|
+
@tree = tree
|
33
19
|
end
|
34
|
-
|
35
|
-
value
|
36
|
-
end
|
20
|
+
end
|
37
21
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
22
|
+
def dfs_array(arr, refs)
|
23
|
+
value = []
|
24
|
+
arr.each do |v|
|
25
|
+
if Pretentious::Deconstructor.primitive?(v)
|
26
|
+
value << v
|
27
|
+
elsif v.is_a? Hash
|
28
|
+
value << dfs_hash(v, refs)
|
29
|
+
elsif v.is_a? Array
|
30
|
+
value << dfs_array(v, refs)
|
31
|
+
elsif v.is_a? Reference
|
32
|
+
refs << v.tree[:id]
|
33
|
+
value << Reference.new(dfs(v.tree))
|
34
|
+
elsif value << v
|
35
|
+
end
|
52
36
|
end
|
53
|
-
|
54
|
-
|
55
|
-
end
|
37
|
+
value
|
38
|
+
end
|
56
39
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
40
|
+
def dfs_hash(hash, refs)
|
41
|
+
value = {}
|
42
|
+
hash.each do |k, v|
|
43
|
+
if Pretentious::Deconstructor.primitive?(v)
|
44
|
+
value[k] = v
|
45
|
+
elsif v.is_a? Hash
|
46
|
+
value[k] = dfs_hash(v, refs)
|
47
|
+
elsif v.is_a? Array
|
48
|
+
value[k] = dfs_array(v, refs)
|
49
|
+
elsif v.is_a? Reference
|
50
|
+
refs << v.tree[:id]
|
51
|
+
value[k] = Reference.new(dfs(v.tree))
|
52
|
+
else
|
53
|
+
value[k] = v
|
54
|
+
end
|
64
55
|
end
|
65
|
-
value
|
66
|
-
|
67
|
-
ref = []
|
68
|
-
|
69
|
-
definition = {id: tree[:id], class: tree[:class], params_types: tree[:params_types], used_by: []}
|
56
|
+
value
|
57
|
+
end
|
70
58
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
definition
|
75
|
-
|
76
|
-
|
59
|
+
def dfs(tree)
|
60
|
+
if !tree.is_a? Hash
|
61
|
+
value = tree
|
62
|
+
definition = { id: value.object_id,
|
63
|
+
class: tree.class,
|
64
|
+
value: value,
|
65
|
+
used_by: [] }
|
66
|
+
unless @dependencies.include? value.object_id
|
67
|
+
@dependencies[value.object_id] = definition
|
68
|
+
@declaration_order << definition
|
69
|
+
end
|
70
|
+
value.object_id
|
71
|
+
else
|
72
|
+
ref = []
|
73
|
+
|
74
|
+
definition = { id: tree[:id],
|
75
|
+
class: tree[:class],
|
76
|
+
params_types: tree[:params_types],
|
77
|
+
used_by: [] }
|
78
|
+
|
79
|
+
if tree[:class] == Hash
|
80
|
+
definition[:value] = dfs_hash(tree[:composition], ref)
|
81
|
+
elsif tree[:class] == Array
|
82
|
+
definition[:value] = dfs_array(tree[:composition], ref)
|
83
|
+
elsif tree[:class] == Pretentious::RecordedProc
|
84
|
+
definition[:recorded_proc] = tree[:recorded_proc]
|
85
|
+
|
86
|
+
if !tree[:composition].nil?
|
87
|
+
ref << dfs(tree[:composition])
|
88
|
+
else
|
89
|
+
dfs(tree[:composition])
|
90
|
+
end
|
77
91
|
|
78
|
-
|
79
|
-
ref << dfs(
|
92
|
+
elsif tree[:composition].is_a? Array
|
93
|
+
tree[:composition].each { |t| ref << dfs(t) }
|
80
94
|
else
|
81
|
-
dfs(tree[:composition])
|
95
|
+
ref << dfs(tree[:composition])
|
82
96
|
end
|
83
97
|
|
84
|
-
|
85
|
-
tree[:
|
86
|
-
ref << dfs(t)
|
87
|
-
}
|
88
|
-
else
|
89
|
-
ref << dfs(tree[:composition])
|
90
|
-
end
|
98
|
+
# evaluate given block composition
|
99
|
+
ref << dfs(tree[:block]) if tree[:block]
|
91
100
|
|
92
|
-
|
93
|
-
if (tree[:block])
|
94
|
-
ref << dfs(tree[:block])
|
95
|
-
end
|
101
|
+
definition[:ref] = ref
|
96
102
|
|
97
|
-
|
103
|
+
unless @dependencies.include? tree[:id]
|
104
|
+
@declaration_order << definition
|
105
|
+
@dependencies[tree[:id]] = definition
|
98
106
|
|
99
|
-
|
100
|
-
|
101
|
-
@dependencies[tree[:id]] = definition
|
107
|
+
ref.each { |r| @dependencies[r][:used_by] << definition }
|
108
|
+
end
|
102
109
|
|
103
|
-
|
104
|
-
@dependencies[r][:used_by] << definition
|
105
|
-
}
|
110
|
+
tree[:id]
|
106
111
|
end
|
107
|
-
|
108
|
-
tree[:id]
|
109
112
|
end
|
110
|
-
end
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
114
|
+
def update_ref_counts(params_arr, method_call)
|
115
|
+
params_arr.each do |p|
|
116
|
+
if @dependencies.include? p.object_id
|
117
|
+
@dependencies[p.object_id][:used_by] << method_call
|
118
|
+
end
|
116
119
|
end
|
117
120
|
end
|
118
|
-
end
|
119
121
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
if
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
122
|
+
def inline
|
123
|
+
@dependencies.each do |id, definition|
|
124
|
+
next if definition[:used_by].size != 1
|
125
|
+
next if !definition.include?(:value) || !self.class.primitive?(definition[:value])
|
126
|
+
ref = definition[:used_by][0]
|
127
|
+
definition[:used_by] = :inline
|
128
|
+
references = ref[:ref]
|
129
|
+
if references
|
130
|
+
new_ref = references.collect { |c| c == id ? definition : c }
|
131
|
+
ref[:ref] = new_ref
|
131
132
|
end
|
132
|
-
|
133
133
|
end
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
#creates a tree on how the object was created
|
138
|
-
def build_tree(target_object)
|
139
|
-
tree = {class: get_test_class(target_object), id: target_object.object_id, composition: []}
|
140
|
-
if (target_object.is_a? Array)
|
141
|
-
tree[:composition] = deconstruct_array(target_object)
|
142
|
-
elsif target_object.is_a? Hash
|
143
|
-
tree[:composition] = deconstruct_hash(target_object)
|
144
|
-
elsif target_object.is_a? Pretentious::RecordedProc
|
145
|
-
tree[:composition] = deconstruct_proc(target_object)
|
146
|
-
tree[:given_block] = target_object.given_block?
|
147
|
-
tree[:recorded_proc] = target_object
|
148
|
-
tree[:id] = target_object.target_proc.object_id
|
149
|
-
tree[:block_params] = self.class.block_param_names(target_object)
|
150
|
-
elsif target_object.methods.include? :_get_init_arguments
|
151
|
-
args = target_object._get_init_arguments
|
152
|
-
unless args.nil?
|
153
|
-
tree[:params_types] = args[:params_types]
|
154
|
-
args[:params].each { |p|
|
155
|
-
tree[:composition] << build_tree(p)
|
156
|
-
}
|
157
|
-
|
158
|
-
tree[:block] = build_tree(args[:block]) unless args[:block].nil?
|
159
|
-
else
|
134
|
+
end
|
160
135
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
136
|
+
# creates a tree on how the object was created
|
137
|
+
def build_tree(target_object)
|
138
|
+
tree = { class: get_test_class(target_object), id: target_object.object_id, composition: [] }
|
139
|
+
if target_object.is_a? Array
|
140
|
+
tree[:composition] = deconstruct_array(target_object)
|
141
|
+
elsif target_object.is_a? Hash
|
142
|
+
tree[:composition] = deconstruct_hash(target_object)
|
143
|
+
elsif target_object.is_a? Pretentious::RecordedProc
|
144
|
+
tree[:composition] = deconstruct_proc(target_object)
|
145
|
+
tree[:given_block] = target_object.given_block?
|
146
|
+
tree[:recorded_proc] = target_object
|
147
|
+
tree[:id] = target_object.target_proc.object_id
|
148
|
+
tree[:block_params] = self.class.block_param_names(target_object)
|
149
|
+
elsif target_object.methods.include? :_get_init_arguments
|
150
|
+
args = target_object._get_init_arguments
|
151
|
+
if args.nil?
|
152
|
+
if self.class.primitive?(target_object)
|
153
|
+
tree[:composition] = target_object
|
154
|
+
elsif target_object.class == File
|
155
|
+
tree[:composition] << build_tree(target_object.path)
|
156
|
+
else
|
157
|
+
tree[:composition] = UnResolved.new(target_object)
|
158
|
+
end
|
165
159
|
else
|
166
|
-
tree[:
|
160
|
+
tree[:params_types] = args[:params_types]
|
161
|
+
args[:params].each { |p| tree[:composition] << build_tree(p) }
|
162
|
+
|
163
|
+
tree[:block] = build_tree(args[:block]) unless args[:block].nil?
|
167
164
|
end
|
168
|
-
end
|
169
165
|
|
170
|
-
|
171
|
-
|
166
|
+
else
|
167
|
+
tree[:composition] = target_object
|
168
|
+
end
|
169
|
+
tree
|
172
170
|
end
|
173
|
-
tree
|
174
|
-
end
|
175
171
|
|
172
|
+
def deconstruct(method_call_collection, *target_objects)
|
173
|
+
@declaration_order = []
|
174
|
+
@dependencies = {}
|
176
175
|
|
177
|
-
|
176
|
+
target_objects.each do |target_object|
|
177
|
+
tree = build_tree target_object
|
178
|
+
dfs(tree)
|
179
|
+
end
|
178
180
|
|
179
|
-
|
180
|
-
|
181
|
+
method_call_collection.each do |m|
|
182
|
+
update_ref_counts(m[:params], m)
|
183
|
+
end
|
181
184
|
|
182
|
-
|
183
|
-
tree = build_tree target_object
|
184
|
-
dfs(tree)
|
185
|
-
}
|
185
|
+
inline
|
186
186
|
|
187
|
-
|
188
|
-
update_ref_counts(m[:params], m)
|
187
|
+
{ declaration: @declaration_order, dependency: @dependencies }
|
189
188
|
end
|
190
189
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
end
|
195
|
-
|
196
|
-
def generate_declarations(variable_map = {}, previous_declarations = {}, method_call_collection = [], *target_objects)
|
197
|
-
target_objects.each { |target_object|
|
198
|
-
variable_map.merge!(target_object._variable_map) if target_object.methods.include?(:_variable_map) && !target_object._variable_map.nil?
|
199
|
-
}
|
200
|
-
deconstruct method_call_collection, *target_objects
|
201
|
-
end
|
202
|
-
|
203
|
-
def build_output(indentation_level, variable_map, declarations, declared_names, previous_declarations)
|
204
|
-
output_buffer = ""
|
205
|
-
indentation = ""
|
206
|
-
|
207
|
-
indentation_level.times {
|
208
|
-
indentation << ' '
|
209
|
-
}
|
210
|
-
|
211
|
-
declarations[:declaration].each do |d|
|
212
|
-
if (d[:used_by] != :inline) && !previous_declarations.has_key?(d[:id])
|
213
|
-
var_name = Pretentious::Deconstructor.pick_name(variable_map, d[:id], declared_names)
|
214
|
-
output_buffer << "#{indentation}#{var_name} = #{construct(d, variable_map, declared_names, indentation)}\n"
|
215
|
-
elsif (d[:used_by]!=:inline) && previous_declarations[d[:id]]
|
216
|
-
variable_map[d[:id]] = "@#{previous_declarations[d[:id]]}" if previous_declarations[d[:id]][0]!='@'
|
190
|
+
def generate_declarations(context, method_call_collection, *target_objects)
|
191
|
+
target_objects.each do |target_object|
|
192
|
+
context.merge_variable_map(target_object)
|
217
193
|
end
|
194
|
+
deconstruct method_call_collection, *target_objects
|
218
195
|
end
|
219
|
-
output_buffer
|
220
|
-
end
|
221
196
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
197
|
+
def build_output(context, indentation_level, declarations)
|
198
|
+
output_buffer = ''
|
199
|
+
indentation = ''
|
200
|
+
indentation_level.times { indentation << ' ' }
|
201
|
+
declarations[:declaration].select { |d| d[:used_by] != :inline }.each do |d|
|
202
|
+
if !context.was_declared_previously?(d[:id])
|
203
|
+
var_name = context.pick_name(d[:id])
|
204
|
+
output_buffer << "#{indentation}#{var_name} = #{construct(context, d, indentation)}\n"
|
205
|
+
elsif context.was_declared_previously?(d[:id])
|
206
|
+
context.register_instance_variable(d[:id])
|
207
|
+
end
|
208
|
+
end
|
209
|
+
output_buffer
|
210
|
+
end
|
231
211
|
|
232
|
-
|
233
|
-
|
212
|
+
def deconstruct_to_ruby(context, indentation_level = 0, *target_objects)
|
213
|
+
declarations, _dependencies = generate_declarations context, [], *target_objects
|
214
|
+
build_output(context, indentation_level, declarations)
|
215
|
+
end
|
234
216
|
|
235
|
-
|
217
|
+
def self.primitive?(value)
|
218
|
+
value.is_a?(String) || value.is_a?(Fixnum) || value.is_a?(TrueClass) || value.is_a?(FalseClass) ||
|
219
|
+
value.is_a?(NilClass) || value.is_a?(Symbol) || value.is_a?(Class)
|
220
|
+
end
|
236
221
|
|
237
|
-
|
238
|
-
parameters_to_join
|
239
|
-
}
|
240
|
-
parameters_to_join
|
241
|
-
end
|
222
|
+
def self.block_param_names(proc)
|
223
|
+
parameters_to_join = []
|
242
224
|
|
243
|
-
|
225
|
+
parameters = proc.target_proc.parameters
|
244
226
|
|
245
|
-
|
246
|
-
|
227
|
+
parameters.each { |p| parameters_to_join << p[1].to_s }
|
228
|
+
parameters_to_join
|
247
229
|
end
|
248
230
|
|
249
|
-
|
250
|
-
|
231
|
+
def self.block_params_generator(proc, separator = '|')
|
232
|
+
if proc.target_proc.parameters.size > 0
|
233
|
+
return "#{separator}#{block_param_names(proc).join(', ')}#{separator}"
|
234
|
+
end
|
251
235
|
|
252
|
-
|
253
|
-
|
254
|
-
output_buffer << "Proc.new { #{self.class.block_params_generator(proc)}\n"
|
255
|
-
output_buffer << self.class.proc_body(proc, let_variables, declared, indentation)
|
256
|
-
output_buffer << "#{indentation}}\n"
|
257
|
-
output_buffer
|
258
|
-
end
|
236
|
+
''
|
237
|
+
end
|
259
238
|
|
260
|
-
|
261
|
-
|
262
|
-
"
|
263
|
-
|
264
|
-
"#{indentation
|
239
|
+
def proc_to_ruby(context, proc, indentation = '')
|
240
|
+
output_buffer = ''
|
241
|
+
output_buffer << "proc { #{self.class.block_params_generator(proc)}\n"
|
242
|
+
output_buffer << self.class.proc_body(context, proc, indentation)
|
243
|
+
output_buffer << "#{indentation}}\n"
|
244
|
+
output_buffer
|
265
245
|
end
|
266
|
-
end
|
267
246
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
if (Pretentious::Deconstructor.is_primitive?(v))
|
272
|
-
composition << v
|
273
|
-
elsif v.is_a? Hash
|
274
|
-
composition << deconstruct_hash(v)
|
275
|
-
elsif v.is_a? Array
|
276
|
-
composition << deconstruct_array(v)
|
247
|
+
def self.proc_body(context, proc, indentation = '')
|
248
|
+
if proc.return_value.size == 1
|
249
|
+
"#{indentation} #{context.value_of(proc.return_value[0])}\n"
|
277
250
|
else
|
278
|
-
|
251
|
+
"#{indentation} \# Variable return values ... can't figure out what goes in here...\n"
|
279
252
|
end
|
280
|
-
|
281
|
-
composition
|
282
|
-
end
|
253
|
+
end
|
283
254
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
255
|
+
def deconstruct_array(array)
|
256
|
+
composition = []
|
257
|
+
array.each do |v|
|
258
|
+
if Pretentious::Deconstructor.primitive?(v)
|
259
|
+
composition << v
|
260
|
+
elsif v.is_a? Hash
|
261
|
+
composition << deconstruct_hash(v)
|
262
|
+
elsif v.is_a? Array
|
263
|
+
composition << deconstruct_array(v)
|
264
|
+
else
|
265
|
+
composition << Reference.new(build_tree(v))
|
266
|
+
end
|
295
267
|
end
|
296
|
-
|
297
|
-
composition
|
298
|
-
end
|
299
|
-
|
300
|
-
def deconstruct_proc(proc)
|
301
|
-
if (proc.return_value.size == 1)
|
302
|
-
return build_tree(proc.return_value[0]) unless proc.return_value[0].nil?
|
303
|
-
return nil
|
304
|
-
else
|
305
|
-
nil
|
268
|
+
composition
|
306
269
|
end
|
307
|
-
end
|
308
|
-
|
309
|
-
def get_test_class(target_object)
|
310
|
-
target_object.respond_to?(:test_class) ? target_object.test_class : target_object.class
|
311
|
-
end
|
312
|
-
|
313
|
-
|
314
|
-
def self.pick_name(variable_map, object_id, declared_names = {}, value = :no_value_passed)
|
315
|
-
var_name = "var_#{object_id}"
|
316
270
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
if (!variable_map.nil? && variable_map.include?(object_id))
|
327
|
-
|
328
|
-
candidate_name = variable_map[object_id].to_s
|
329
|
-
if !declared_names.include?(candidate_name)
|
330
|
-
var_name = candidate_name
|
331
|
-
declared_names[candidate_name] = {count: 1, object_id: object_id}
|
332
|
-
else
|
333
|
-
|
334
|
-
if (declared_names[candidate_name][:object_id] == object_id)
|
335
|
-
var_name = candidate_name
|
271
|
+
def deconstruct_hash(hash)
|
272
|
+
composition = {}
|
273
|
+
hash.each do |k, v|
|
274
|
+
if Pretentious::Deconstructor.primitive?(v)
|
275
|
+
composition[k] = v
|
276
|
+
elsif v.is_a? Hash
|
277
|
+
composition[k] = deconstruct_hash(v)
|
278
|
+
elsif v.is_a? Array
|
279
|
+
composition[k] = deconstruct_array(v)
|
336
280
|
else
|
337
|
-
|
338
|
-
var_name = "#{new_name}"
|
339
|
-
|
340
|
-
declared_names[candidate_name][:count]+=1
|
341
|
-
declared_names[new_name] = {count: 1, object_id: object_id}
|
281
|
+
composition[k] = Reference.new(build_tree(v))
|
342
282
|
end
|
343
|
-
|
344
|
-
end
|
345
|
-
else
|
346
|
-
if value != :no_value_passed
|
347
|
-
return Pretentious::value_ize(value, let_variables, declared_names)
|
348
283
|
end
|
284
|
+
composition
|
349
285
|
end
|
350
286
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
287
|
+
def deconstruct_proc(proc)
|
288
|
+
return nil if proc.return_value.size != 1
|
289
|
+
return build_tree(proc.return_value[0]) unless proc.return_value[0].nil?
|
290
|
+
end
|
355
291
|
|
292
|
+
def get_test_class(target_object)
|
293
|
+
target_object.respond_to?(:test_class) ? target_object.test_class : target_object.class
|
294
|
+
end
|
356
295
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
value =
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
296
|
+
private
|
297
|
+
|
298
|
+
def output_array(context, arr)
|
299
|
+
output_buffer = '['
|
300
|
+
array_elements = []
|
301
|
+
arr.each do |v|
|
302
|
+
value = Pretentious.value_ize(context, v)
|
303
|
+
if v.is_a? Hash
|
304
|
+
value = output_hash(context, v)
|
305
|
+
elsif v.is_a? Array
|
306
|
+
value = output_array(context, v)
|
307
|
+
elsif v.is_a? Reference
|
308
|
+
value = context.pick_name(v.tree)
|
309
|
+
end
|
310
|
+
array_elements << value
|
368
311
|
end
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
output_buffer
|
374
|
-
end
|
312
|
+
output_buffer << array_elements.join(', ')
|
313
|
+
output_buffer << ']'
|
314
|
+
output_buffer
|
315
|
+
end
|
375
316
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
317
|
+
def output_hash(context, hash)
|
318
|
+
output_buffer = '{ '
|
319
|
+
hash_elements = []
|
320
|
+
hash.each do |k, v|
|
321
|
+
value = context.value_of(v)
|
322
|
+
if v.is_a? Hash
|
323
|
+
value = output_hash(context, v)
|
324
|
+
elsif v.is_a? Array
|
325
|
+
value = output_array(context, v)
|
326
|
+
elsif v.is_a? Reference
|
327
|
+
value = context.pick_name(v.tree)
|
328
|
+
end
|
388
329
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
330
|
+
if k.is_a? Symbol
|
331
|
+
hash_elements << "#{k}: #{value}"
|
332
|
+
else
|
333
|
+
hash_elements << "#{context.value_of(k)} => #{value}"
|
334
|
+
end
|
393
335
|
end
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
end
|
336
|
+
output_buffer << hash_elements.join(', ')
|
337
|
+
output_buffer << ' }'
|
338
|
+
output_buffer
|
339
|
+
end
|
399
340
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
341
|
+
def construct(context, definition, indentation = '')
|
342
|
+
if definition.include? :value
|
343
|
+
if definition[:value].is_a? Hash
|
344
|
+
output_hash(context, definition[:value])
|
345
|
+
elsif definition[:value].is_a? Array
|
346
|
+
output_array(context, definition[:value])
|
347
|
+
elsif definition[:value].is_a? UnResolved
|
348
|
+
'nil #parameters unresolvable. cannot decompose'
|
349
|
+
else
|
350
|
+
context.value_of(definition[:value])
|
351
|
+
end
|
352
|
+
elsif definition[:class] == Pretentious::RecordedProc
|
353
|
+
proc_to_ruby(context, definition[:recorded_proc], indentation)
|
408
354
|
else
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
type = :param
|
422
|
-
if (params_types)
|
423
|
-
type = params_types[index][0]
|
424
|
-
i+=1
|
425
|
-
end
|
355
|
+
params = []
|
356
|
+
if definition[:ref] && definition[:ref].size > 0
|
357
|
+
|
358
|
+
i = 0
|
359
|
+
params_types = definition[:params_types]
|
360
|
+
definition[:ref].each_with_index do |v, index|
|
361
|
+
type = :param
|
362
|
+
if params_types
|
363
|
+
type = params_types[index][0]
|
364
|
+
i += 1
|
365
|
+
end
|
426
366
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
else
|
431
|
-
if (type == :block)
|
432
|
-
params << "&#{Pretentious::Deconstructor.pick_name(variable_map, v, declared_names)}"
|
367
|
+
# to inline?
|
368
|
+
if v.is_a? Hash
|
369
|
+
params << context.value_of(v[:value])
|
433
370
|
else
|
434
|
-
params <<
|
371
|
+
params << (type == :block ? "&#{context.pick_name(v)}" : context.pick_name(v))
|
435
372
|
end
|
436
373
|
end
|
374
|
+
"#{definition[:class]}.new(#{params.join(', ')})"
|
375
|
+
else
|
376
|
+
"#{definition[:class]}.new"
|
437
377
|
end
|
438
|
-
"#{definition[:class]}.new(#{params.join(', ')})"
|
439
|
-
else
|
440
|
-
"#{definition[:class]}.new"
|
441
|
-
end
|
442
378
|
|
379
|
+
end
|
443
380
|
end
|
444
|
-
end
|
445
381
|
|
446
|
-
end
|
382
|
+
end
|
383
|
+
end
|