pretentious 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4eac89f08c915484d2749c73f703b6d8654f5e94
4
+ data.tar.gz: abf3901c3235fa2ee6d266cbb5cd019f22e83c2a
5
+ SHA512:
6
+ metadata.gz: a0f020ba8a6903e3bd55682c58e42a63c3ffddb3db7e99bada136aae4ecc2252a6ddd5a45ebe02f82e23f287aa8062f6a18b32d85a88c59af4558e782ac08d50
7
+ data.tar.gz: 4c4c7d237a30ec08800be2d09fa363f6ced13a2cfaea11651c41508b1950e499f7077d759a194c76f2262bfb784825c2605b20254d146e7ed5e1792843330357
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ .DS_Store
2
+ .idea
3
+ *.gem
4
+ /.bundle/
5
+ /.yardoc
6
+ /Gemfile.lock
7
+ /_yardoc/
8
+ /coverage/
9
+ /doc/
10
+ /pkg/
11
+ /spec/reports/
12
+ /tmp/
13
+ *.bundle
14
+ *.so
15
+ *.o
16
+ *.a
17
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pretentious.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Joseph Emmanuel Dayo
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # Ruby::Pretentious
2
+
3
+ Do you have a pretentious boss or dev lead that pushes you to embrace bdd/tdd but for reasons hate it or them?
4
+ here is a gem to deal with that. Now you CAN write code first and then GENERATE tests later!! Yes you heard that
5
+ right! This gem allows you to write your code first and then automatically generate tests using the code
6
+ you've written!
7
+
8
+ On a serious note, this gem allows you to generate tests template much better than those generated by default
9
+ for various frameworks.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'pretentious'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install pretentious
26
+
27
+ ## Usage
28
+
29
+ First Create an example file (etc. example.rb) and define the classes that you want to test, if the class is
30
+ already defined elsewhere just require them. Below is an example:
31
+
32
+ ```ruby
33
+ class Fibonacci
34
+
35
+ def fib(n)
36
+ return 0 if (n == 0)
37
+ return 1 if (n == 1)
38
+ return 1 if (n == 2)
39
+ return fib(n - 1) + fib(n - 2)
40
+ end
41
+
42
+ def self.say_hello
43
+ "hello"
44
+ end
45
+
46
+ end
47
+ ```
48
+
49
+ Inside a Pretentious.spec_for(...) block. Just write boring code that calls the methods of your class like
50
+ how you'd normally use them. Finally Specify the classes that you want to test:
51
+
52
+ ```ruby
53
+ class Fibonacci
54
+
55
+ def fib(n)
56
+ return 0 if (n == 0)
57
+ return 1 if (n == 1)
58
+ return 1 if (n == 2)
59
+ return fib(n - 1) + fib(n - 2)
60
+ end
61
+
62
+ def self.say_hello
63
+ "hello"
64
+ end
65
+
66
+ end
67
+
68
+ Pretentious.spec_for(Fibonacci) do
69
+
70
+
71
+ instance = Fibonacci.new
72
+
73
+ (1..10).each do |n|
74
+ instance.fib(n)
75
+ end
76
+
77
+ Fibonacci.say_hello
78
+
79
+ end
80
+ ```
81
+
82
+ Save your file and then switch to the terminal to invoke:
83
+
84
+ ddtgen example.rb
85
+
86
+ This will automatically generate rspec tests for Fibonacci under spec of the current working directory.
87
+
88
+ you can invoke spec at this point, but the tests will fail. Instead you should edit spec/spec_helper.rb and
89
+ put the necessary requires and definitions there.
90
+
91
+ For this example place the following into spec_helper.rb:
92
+
93
+ ```ruby
94
+ #inside spec_helper.rb
95
+
96
+ class Fibonacci
97
+
98
+ def fib(n)
99
+ return 0 if (n == 0)
100
+ return 1 if (n == 1)
101
+ return 1 if (n == 2)
102
+ return fib(n - 1) + fib(n - 2)
103
+ end
104
+
105
+ def self.say_hello
106
+ "hello"
107
+ end
108
+
109
+ end
110
+ ```
111
+
112
+ You can also try this out with build in libraries like MD5 for example
113
+
114
+ ```ruby
115
+ #example.rb
116
+
117
+ Pretentious.spec_for(Digest::MD5) do
118
+ sample = "This is the digest"
119
+ Digest::MD5.hexdigest(sample)
120
+ end
121
+ ```
122
+
123
+ You should get something like:
124
+
125
+ ```ruby
126
+ require 'spec_helper'
127
+
128
+ RSpec.describe Digest::MD5 do
129
+
130
+ it 'should pass current expectations' do
131
+
132
+ sample = "This is the digest"
133
+
134
+ # Digest::MD5::hexdigest when passed "This is the digest" should return 9f12248dcddeda976611d192efaaf72a
135
+ expect( Digest::MD5.hexdigest(sample) ).to eq("9f12248dcddeda976611d192efaaf72a")
136
+
137
+ end
138
+ end
139
+ ```
140
+
141
+ Only RSpec is support at this point. But other testing frameworks should be trivial to add support to.
142
+
143
+ ## Limitations
144
+
145
+ Computers are bad at mind reading (for now) and they don't really know your expectation of "correctness", as such
146
+ it assumes your code is correct and can only use equality based matchers. It can also only reliably match
147
+ primitive data types and hashs and arrays to degree. More complex expectations are unfortunately left for the human to resolve.
148
+
149
+ Also do note that it tries its best to determine how your fixtures are created, as well as the types
150
+ of your parameters and does so by figuring out the components that your object needs. Failure can happen during this process.
151
+
152
+ Finally, Limit this gem for test environments only.
153
+
154
+ ## Bugs
155
+
156
+ This is the first iteration and a lot of broken things could happen
157
+
158
+ ## Contributing
159
+
160
+ 1. Fork it (https://github.com/jedld/pretentious.git)
161
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
162
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
163
+ 4. Push to the branch (`git push origin my-new-feature`)
164
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/ddtgen ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pretentious'
4
+ require 'optparse'
5
+ require 'ripper'
6
+ require "readline"
7
+ require 'json'
8
+ require 'fileutils'
9
+
10
+ $test_framework = :rspec
11
+ $output_folder = 'spec'
12
+
13
+ # ddtgen example.rb -t rspec -o rspec/
14
+ options = OptionParser.new do |o|
15
+ o.banner =
16
+ "Usage: ddtgen FILENAME [options] # Generates tests using the specified example file\n"
17
+ o.separator ""
18
+ o.separator "options:"
19
+ o.on('-t','--test-type','test framework to use (default :rspec)') { |b| $test_framework = b.to_sym}
20
+ o.on('-o','--output-dir','folder to place the files in') { |b| $output_folder = b}
21
+ o.parse!
22
+ end
23
+
24
+
25
+ filename = ARGV[0]
26
+
27
+ if filename.nil?
28
+ puts "an example file is required."
29
+ puts options
30
+ exit(1)
31
+ end
32
+
33
+ example_body = ""
34
+
35
+ index = 0
36
+ File.open(filename, "r") do |f|
37
+ f.each_line do |line|
38
+ example_body << "#{line}\n"
39
+ end
40
+ end
41
+
42
+ eval(example_body)
43
+
44
+ #collect results
45
+
46
+ FileUtils.mkdir_p $output_folder
47
+
48
+ module DdtUtils
49
+ def self.to_underscore(str)
50
+ str.gsub(/(.)([A-Z])/,'\1_\2').downcase
51
+ end
52
+ end
53
+
54
+ Pretentious.last_results.each { |klass, result|
55
+
56
+ klass_name_parts = klass.name.split('::')
57
+ last_part = klass_name_parts.pop
58
+
59
+ filename = File.join($output_folder,"#{DdtUtils.to_underscore(last_part)}_spec.rb")
60
+ File.open(filename, 'w') {
61
+ |f| f.write(result)
62
+ }
63
+ puts "#{filename}"
64
+ }
65
+
66
+ filename = File.join($output_folder,"spec_helper.rb")
67
+ unless File.exists?(filename)
68
+ File.open(filename, 'w') {
69
+ |f| f.write("#Place your requires here")
70
+ }
71
+ puts "#{filename}"
72
+ end
data/example.rb ADDED
@@ -0,0 +1,93 @@
1
+ require 'digest/md5'
2
+
3
+ class Fibonacci
4
+
5
+ def fib(n)
6
+ return 0 if (n == 0)
7
+ return 1 if (n == 1)
8
+ return 1 if (n == 2)
9
+ return fib(n - 1) + fib(n - 2)
10
+ end
11
+
12
+ def self.say_hello
13
+ "hello"
14
+ end
15
+
16
+ end
17
+
18
+ class TestClass1
19
+
20
+ def initialize(message)
21
+ @message = message
22
+ end
23
+
24
+ def print_message
25
+ puts @message
26
+ end
27
+
28
+ def something_is_wrong
29
+ raise StandardError.new
30
+ end
31
+ end
32
+
33
+ class TestClass2
34
+ def initialize(message)
35
+ @message = {message: message}
36
+ end
37
+
38
+ def print_message
39
+ puts @message[:message]
40
+ end
41
+ end
42
+
43
+ class TestClass3
44
+
45
+ def initialize(testclass1, testclass2)
46
+ @class1 = testclass1
47
+ @class2 = testclass2
48
+ end
49
+
50
+ def get_hash
51
+ {hello: "world", message: {another: :hash}}
52
+ end
53
+
54
+ def show_messages
55
+ @class1.print_message
56
+ @class2.print_message
57
+ "awesome!!!"
58
+ end
59
+
60
+ end
61
+
62
+ Pretentious.spec_for(Fibonacci) do
63
+
64
+
65
+ instance = Fibonacci.new
66
+
67
+ (1..10).each do |n|
68
+ instance.fib(n)
69
+ end
70
+
71
+ Fibonacci.say_hello
72
+
73
+ end
74
+
75
+ Pretentious.spec_for(TestClass1, TestClass2, TestClass3) do
76
+
77
+ another_object = TestClass1.new("test")
78
+ test_class_one = TestClass1.new({hello: "world", test: another_object, arr_1: [1,2,3,4,5, another_object],
79
+ sub_hash: {yes: true, obj: another_object}})
80
+ test_class_two = TestClass2.new("This is message 2")
81
+
82
+ class_to_test = TestClass3.new(test_class_one, test_class_two)
83
+ class_to_test.show_messages
84
+
85
+ class_to_test = TestClass3.new(test_class_one, test_class_two)
86
+ class_to_test.show_messages
87
+
88
+ end
89
+
90
+ Pretentious.spec_for(Digest::MD5) do
91
+ sample = "This is the digest"
92
+ Digest::MD5.hexdigest(sample)
93
+ end
@@ -0,0 +1,370 @@
1
+ require "pretentious/version"
2
+ require "pretentious/rspec_generator"
3
+ require 'binding_of_caller'
4
+ require 'pretentious/deconstructor'
5
+
6
+ module Pretentious
7
+
8
+ def self.spec_for(*klasses, &block)
9
+ @results = @results || {}
10
+ @results.merge!(Pretentious::Generator.generate_for(*klasses, &block))
11
+ end
12
+
13
+ def self.clear_results
14
+ @results = {}
15
+ end
16
+
17
+ def self.last_results
18
+ @results
19
+ end
20
+
21
+ def self.value_ize(value, let_variables, declared_names)
22
+ if (value.kind_of? String)
23
+ "#{value.dump}"
24
+ elsif (value.is_a? Symbol)
25
+ ":#{value.to_s}"
26
+ elsif (value.is_a? Hash)
27
+ Pretentious::Deconstructor.pick_name(let_variables, value.object_id, declared_names)
28
+ else
29
+ "#{value.to_s}"
30
+ end
31
+ end
32
+
33
+ def self.watch(&block)
34
+ Pretentious::Generator.watch_new_instances
35
+ block.call
36
+ Pretentious::Generator.unwatch_new_instances
37
+ end
38
+
39
+ class Generator
40
+
41
+ def self.impostor_for(module_space, klass)
42
+ newStandInKlass = Class.new()
43
+ name = klass.name
44
+ module_space.const_set "#{name.split('::').last}Impostor", newStandInKlass
45
+
46
+ common_snippet = "
47
+ @_method_calls = @_method_calls || []
48
+ @_method_calls_by_method = @_method_calls_by_method || {}
49
+ @_methods_for_test = @_methods_for_test || []
50
+ @_let_variables = @_let_variables || {}
51
+
52
+ caller_context = binding.of_caller(1)
53
+ #puts \"local_variables\"
54
+ v_locals = caller_context.eval('local_variables')
55
+
56
+ v_locals.each { |v|
57
+ variable_value = caller_context.eval(\"\#{v.to_s}\")
58
+ @_let_variables[variable_value.object_id] = v
59
+ }
60
+
61
+ info_block = {}
62
+ info_block[:method] = method_sym
63
+ info_block[:params] = arguments
64
+ info_block[:names] = @_instance.method(method_sym).parameters
65
+
66
+ begin
67
+ if (@_instance.methods.include? method_sym)
68
+ result = @_instance.send(method_sym, *arguments, &block)
69
+ else
70
+ result = @_instance.send(:method_missing, method_sym, *arguments, &block)
71
+ end
72
+ info_block[:result] = result
73
+ rescue Exception=>e
74
+ info_block[:result] = e
75
+ rescue StandardError=>e
76
+ info_block[:result] = e
77
+ end
78
+
79
+ @_method_calls << info_block
80
+
81
+ if (@_method_calls_by_method[method_sym].nil?)
82
+ @_method_calls_by_method[method_sym] = []
83
+ end
84
+
85
+ @_method_calls_by_method[method_sym] << info_block
86
+ raise e if (e.kind_of? Exception)
87
+ result"
88
+
89
+ newStandInKlass.class_eval("
90
+ def setup_instance(*args, &block)
91
+ @_instance = #{klass.name}_ddt.new(*args, &block)
92
+ end
93
+ ")
94
+
95
+ newStandInKlass.class_exec do
96
+ def initialize(*args, &block)
97
+
98
+ @_instance_init = {params: [], block: nil}
99
+
100
+ @_instance_init[:params] = args
101
+ @_instance_init[:block] = block
102
+
103
+ setup_instance(*args, &block)
104
+
105
+
106
+ @_method_calls = []
107
+ @_method_calls_by_method = {}
108
+ @_methods_for_test = []
109
+ @_let_variables = {}
110
+
111
+
112
+ @_init_let_variables = {}
113
+
114
+ caller_context = binding.of_caller(2)
115
+ v_locals = caller_context.eval('local_variables')
116
+
117
+ v_locals.each { |v|
118
+ variable_value = caller_context.eval("#{v.to_s}")
119
+ @_init_let_variables[variable_value.object_id] = v
120
+ }
121
+
122
+ self.class._add_instances(self)
123
+ end
124
+
125
+ def _init_arguments
126
+ @_instance_init
127
+ end
128
+
129
+ def test_class
130
+ @_instance.class
131
+ end
132
+
133
+ def include_for_tests(method_list = [])
134
+ @_methods_for_test = @_methods_for_test + method_list
135
+ end
136
+
137
+ def let_variables
138
+ @_let_variables
139
+ end
140
+
141
+ def init_let_variables
142
+ @_init_let_variables
143
+ end
144
+
145
+ def method_calls_by_method
146
+ @_method_calls_by_method
147
+ end
148
+
149
+ def method_calls
150
+ @_method_calls
151
+ end
152
+
153
+ def to_s
154
+ @_instance.to_s
155
+ end
156
+
157
+ def ==(other)
158
+ @_instance==other
159
+ end
160
+
161
+ def kind_of?(klass)
162
+ @_instance.kind_of? klass
163
+ end
164
+
165
+ def methods
166
+ @_instance.methods + [:method_calls]
167
+ end
168
+
169
+ def freeze
170
+ @_instance.freeze
171
+ end
172
+
173
+ def hash
174
+ @instance.hash
175
+ end
176
+
177
+ def inspect
178
+ @_instance.inspect
179
+ end
180
+
181
+ def is_a?(something)
182
+ @_instance.is_a? something
183
+ end
184
+
185
+ class << self
186
+ def _add_instances(instance)
187
+ @_instances = @_instances || []
188
+ @_instances << instance unless @_instances.include? instance
189
+ end
190
+
191
+ def let_variables
192
+ @_let_variables
193
+ end
194
+
195
+ def method_calls_by_method
196
+ @_method_calls_by_method
197
+ end
198
+
199
+ def method_calls
200
+ @_method_calls
201
+ end
202
+
203
+ def _add_instances(instance)
204
+ @_instances = @_instances || []
205
+ @_instances << instance unless @_instances.include? instance
206
+ end
207
+
208
+ def _instances
209
+ @_instances
210
+ end
211
+
212
+ end
213
+
214
+ end
215
+
216
+ newStandInKlass.class_eval("
217
+ def method_missing(method_sym, *arguments, &block)
218
+ #puts \"\#{method_sym} \#{arguments}\"
219
+ #{common_snippet}
220
+ end
221
+
222
+ class << self
223
+
224
+ def test_class
225
+ #{klass.name}
226
+ end
227
+
228
+ def method_missing(method_sym, *arguments, &block)
229
+ #puts \"method \#{method_sym.to_s}\"
230
+ _add_instances(self)
231
+ @_instance = #{klass.name}_ddt
232
+ #{common_snippet}
233
+ end
234
+ end
235
+ ")
236
+
237
+ newStandInKlass
238
+ end
239
+
240
+ def self.generate_for(*klasses_or_instances, &block)
241
+ all_results = {}
242
+ klasses = []
243
+
244
+ klasses_or_instances.each { |klass_or_instance|
245
+ klass = klass_or_instance.class == Class ? klass_or_instance : klass_or_instance.class
246
+
247
+
248
+ klass_name_parts = klass.name.split('::')
249
+ last_part = klass_name_parts.pop
250
+
251
+ module_space = Object
252
+
253
+ if (klass_name_parts.size > 0)
254
+ klass_name_parts.each do |part|
255
+ module_space = module_space.const_get(part)
256
+ end
257
+ end
258
+
259
+ newStandInKlass = impostor_for module_space, klass
260
+
261
+ module_space.send(:remove_const,last_part.to_sym)
262
+ module_space.const_set("#{last_part}_ddt", klass)
263
+ module_space.const_set("#{last_part}", newStandInKlass)
264
+
265
+ klasses << [module_space, klass, last_part, newStandInKlass]
266
+ }
267
+
268
+ watch_new_instances
269
+
270
+ block.call
271
+
272
+ unwatch_new_instances
273
+
274
+ klasses.each { |module_space, klass, last_part, newStandInKlass|
275
+
276
+ module_space.send(:remove_const,"#{last_part}Impostor".to_sym)
277
+ module_space.send(:remove_const,"#{last_part}".to_sym)
278
+ module_space.const_set(last_part, klass)
279
+ module_space.send(:remove_const,"#{last_part}_ddt".to_sym)
280
+
281
+ test_generator = Pretentious::RspecGenerator.new
282
+ test_generator.begin_spec(klass)
283
+ num = 1
284
+
285
+ newStandInKlass._instances.each do |instance|
286
+ test_generator.generate(instance, num)
287
+ num+=1
288
+ end
289
+
290
+ test_generator.end_spec
291
+
292
+ result = all_results[klass]
293
+ if result.nil?
294
+ all_results[klass] = []
295
+ end
296
+
297
+ all_results[klass] = test_generator.output
298
+
299
+ }
300
+
301
+ all_results
302
+ end
303
+
304
+ def self.watch_new_instances
305
+ Object.class_eval do
306
+ def _get_init_arguments
307
+ @_init_arguments
308
+ end
309
+
310
+ def _set_init_arguments(*args, &block)
311
+ @_init_arguments = @_init_arguments || {}
312
+ @_init_arguments[:params] = args
313
+ @_init_arguments[:block] = block
314
+ @_variable_names= {}
315
+
316
+ index = 0
317
+ params = method(:initialize).parameters
318
+
319
+ args.each do |arg|
320
+ p = params[index]
321
+ if p.size > 1
322
+ @_variable_names[arg.object_id] = p[1].to_s
323
+ end
324
+ index+=1
325
+ end
326
+
327
+ end
328
+
329
+ def _variable_map
330
+ @_variable_names
331
+ end
332
+
333
+ def _deconstruct
334
+ Pretentious::Deconstructor.new().deconstruct(self)
335
+ end
336
+
337
+ def _deconstruct_to_ruby(indentation = 0)
338
+ Pretentious::Deconstructor.new().deconstruct_to_ruby(indentation, _variable_map, self)
339
+ end
340
+
341
+ end
342
+
343
+ Class.class_eval do
344
+ alias_method :_ddt_old_new, :new
345
+
346
+ def new(*args, &block)
347
+ instance = _ddt_old_new(*args, &block)
348
+ instance._set_init_arguments(*args, &block)
349
+ instance
350
+ end
351
+ end
352
+ end
353
+
354
+ def self.clean_watches
355
+ Class.class_eval do
356
+ remove_method :new
357
+ alias_method :new, :_ddt_old_new
358
+ end
359
+ end
360
+
361
+ def self.unwatch_new_instances
362
+ Class.class_eval do
363
+ remove_method :new
364
+ alias_method :new, :_ddt_old_new
365
+ end
366
+ end
367
+
368
+ end
369
+
370
+ end