brutal 0.3.0 → 1.1.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 +4 -4
- data/LICENSE.md +1 -1
- data/README.md +68 -59
- data/bin/brutal +2 -39
- data/lib/brutal.rb +19 -6
- data/lib/brutal/configuration.rb +50 -0
- data/lib/brutal/file/read.rb +33 -0
- data/lib/brutal/file/write.rb +36 -0
- data/lib/brutal/scaffold.rb +91 -0
- data/lib/brutal/yaml.rb +14 -0
- metadata +28 -25
- data/lib/brutal/scaffold_generator/base.rb +0 -30
- data/lib/brutal/scaffold_generator/por.rb +0 -52
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98d43784db59dbf4d8263ec4730d302832e7e5edc46ebafc1a13d95935e9b697
|
4
|
+
data.tar.gz: e2d37b31d5b37387437e6ac80afb11b5b1a6d104cb677629ec3aafa9aae153af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3af04c68e89e7bdd965163066db2b99a4e3df78846ea6f33a0a89b9fe443e477376a6108a31c8b69d86d45f04459991d503f56c345b5089b7233a22b0d831bd
|
7
|
+
data.tar.gz: 55e326bf5cdcba338c63d7e57a8f75378f9869fe0f986867a86ece479850dd78745a2a568c3e0b89c8080f343e977b17d42b714e80dbbe3543b980204fd67c4d
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -1,22 +1,37 @@
|
|
1
|
-
# Brutal
|
2
|
-
|
3
|
-
> I don't like testing because it's redundant. If we are smart enough, we can avoid it. <br/>
|
4
|
-
> -- Matz
|
1
|
+
# Brutal 💎🔨
|
5
2
|
|
3
|
+
[][workflow_rubocop]
|
6
4
|
[][travis]
|
7
5
|
[][gem]
|
8
6
|
[][inchpages]
|
9
7
|
[][rubydoc]
|
10
8
|
|
11
|
-
|
9
|
+
> A _code-first_ approach to automate the writing of unit tests.
|
12
10
|
|
13
|
-
|
11
|
+
## Intro
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
[](https://www.youtube.com/embed/cmOt9HhszCI?start=1732&end=1736 "I don't like tests. It's not DRY.")
|
14
|
+
|
15
|
+
> I don't like tests. It's not DRY.<br/>
|
16
|
+
> -- [Matz](https://github.com/matz)
|
17
|
+
|
18
|
+
## Overview
|
19
|
+
|
20
|
+
Let __Brutal__ shape for you in no time the actual behavior of your code through as many combinations of contexts as needed.
|
21
|
+
|
22
|
+
By delegating to __Brutal__ the repetitive (and redundant) task of writing tests, you'll be able to focus on your core business: the code itself.
|
23
|
+
|
24
|
+
## Warning
|
25
|
+
|
26
|
+
__Brutal__ development process does not prevent from bugs.
|
27
|
+
|
28
|
+
As a _picture of the behavior of the code_,
|
29
|
+
a generated test suite is wrong as long as the code is wrong,
|
30
|
+
regardless of whether all true expectations.
|
31
|
+
|
32
|
+
However, this document becomes relevant when it shows that the code behaves as it is supposed to.
|
33
|
+
It is therefore important to read it well.
|
34
|
+
This is the price for _Brutal-Driven Development_.
|
20
35
|
|
21
36
|
## Installation
|
22
37
|
|
@@ -40,59 +55,67 @@ Just type `brutal` in a Ruby project's folder and watch the magic happen.
|
|
40
55
|
|
41
56
|
## Usage
|
42
57
|
|
43
|
-
The
|
58
|
+
The `brutal.yml` file is a manifest you can use to define your __Brutal__ meta-spec.
|
59
|
+
It has 4 top-level sections:
|
44
60
|
|
45
|
-
* `header`
|
46
|
-
* `
|
47
|
-
* `
|
48
|
-
* `
|
49
|
-
* `actual_values` (required): A list of tests to challenge the subject.
|
61
|
+
* `header` - Specifies the code to execute before generating the test suite.
|
62
|
+
* `subject` - Specifies the template of the code to be declined across contexts.
|
63
|
+
* `contexts` - Specifies a list of variables to populate the subject's template.
|
64
|
+
* `actuals` - Specifies templates to challenge evaluated subjects & get results.
|
50
65
|
|
51
|
-
|
66
|
+
### Getting started
|
52
67
|
|
53
|
-
|
68
|
+
1. Create a `brutal.yml` file in your application's root directory.
|
69
|
+
The following example `brutal.yml` defines the shape of a Hello test suite:
|
54
70
|
|
55
71
|
```yaml
|
56
72
|
---
|
57
|
-
front_object: |
|
58
|
-
"Hello "
|
59
|
-
|
60
73
|
subject: |
|
61
|
-
|
74
|
+
"Hello " + "%{string}"
|
62
75
|
|
63
|
-
|
76
|
+
contexts:
|
64
77
|
string:
|
65
|
-
-
|
66
|
-
-
|
78
|
+
- Alice
|
79
|
+
- Bob
|
67
80
|
|
68
|
-
|
81
|
+
actuals:
|
69
82
|
- "%{subject}.to_s"
|
70
83
|
- "%{subject}.length"
|
71
84
|
```
|
72
85
|
|
73
|
-
|
86
|
+
2. Run the `brutal` command from the same directory.
|
87
|
+
|
88
|
+
3. Read the generated `test.rb` file in the same directory:
|
74
89
|
|
75
90
|
```ruby
|
76
|
-
|
91
|
+
# Brutal test suite
|
77
92
|
|
78
93
|
# ------------------------------------------------------------------------------
|
79
94
|
|
80
|
-
actual =
|
81
|
-
|
82
|
-
|
95
|
+
actual = begin
|
96
|
+
"Hello " + "Alice"
|
97
|
+
end
|
98
|
+
|
99
|
+
raise if actual.to_s != "Hello Alice"
|
100
|
+
raise if actual.length != 11
|
83
101
|
|
84
102
|
# ------------------------------------------------------------------------------
|
85
103
|
|
86
|
-
actual =
|
87
|
-
|
88
|
-
|
104
|
+
actual = begin
|
105
|
+
"Hello " + "Bob"
|
106
|
+
end
|
107
|
+
|
108
|
+
raise if actual.to_s != "Hello Bob"
|
109
|
+
raise if actual.length != 9
|
89
110
|
```
|
90
111
|
|
91
|
-
|
112
|
+
### More examples
|
113
|
+
|
114
|
+
https://github.com/fixrb/brutal/raw/master/examples/
|
92
115
|
|
93
116
|
## Rake integration example
|
94
117
|
|
95
|
-
|
118
|
+
A generated `test.rb` file could be matched as follows:
|
96
119
|
|
97
120
|
```ruby
|
98
121
|
Rake::TestTask.new do |t|
|
@@ -102,35 +125,15 @@ end
|
|
102
125
|
|
103
126
|
## Contact
|
104
127
|
|
105
|
-
*
|
106
|
-
* Bugs/issues: https://github.com/fixrb/brutal/issues
|
107
|
-
|
108
|
-
## Rubies
|
109
|
-
|
110
|
-
* [MRI](https://www.ruby-lang.org/)
|
111
|
-
* [Rubinius](https://rubinius.com/)
|
112
|
-
* [JRuby](https://www.jruby.org/)
|
128
|
+
* Source code: https://github.com/fixrb/brutal
|
113
129
|
|
114
130
|
## Versioning
|
115
131
|
|
116
132
|
__Brutal__ follows [Semantic Versioning 2.0](https://semver.org/).
|
117
133
|
|
118
|
-
## Contributing
|
119
|
-
|
120
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/fixrb/brutal. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/fixrb/brutal/blob/master/CODE_OF_CONDUCT.md).
|
121
|
-
|
122
134
|
## License
|
123
135
|
|
124
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
125
|
-
|
126
|
-
## Code of Conduct
|
127
|
-
|
128
|
-
Everyone interacting in the GreatGuardian project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fixrb/brutal/blob/master/CODE_OF_CONDUCT.md).
|
129
|
-
|
130
|
-
[gem]: https://rubygems.org/gems/brutal
|
131
|
-
[travis]: https://travis-ci.org/fixrb/brutal
|
132
|
-
[inchpages]: https://inch-ci.org/github/fixrb/brutal
|
133
|
-
[rubydoc]: https://rubydoc.info/gems/brutal/frames
|
136
|
+
The [gem](https://rubygems.org/gems/brutal) is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
134
137
|
|
135
138
|
***
|
136
139
|
|
@@ -140,3 +143,9 @@ Everyone interacting in the GreatGuardian project's codebases, issue trackers, c
|
|
140
143
|
src="https://github.com/fixrb/brutal/raw/master/img/sashite.png"
|
141
144
|
alt="Sashite" /></a>
|
142
145
|
</p>
|
146
|
+
|
147
|
+
[workflow_rubocop]: https://github.com/fixrb/brutal/actions?query=workflow%3ARuboCop
|
148
|
+
[gem]: https://rubygems.org/gems/brutal
|
149
|
+
[travis]: https://travis-ci.org/fixrb/brutal
|
150
|
+
[inchpages]: https://inch-ci.org/github/fixrb/brutal
|
151
|
+
[rubydoc]: https://rubydoc.info/gems/brutal/frames
|
data/bin/brutal
CHANGED
@@ -1,43 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
require_relative File.join('..', 'lib', 'brutal')
|
5
5
|
|
6
|
-
|
7
|
-
CONF_PATH = ::File.join(::Dir.pwd, FILE_NAME)
|
8
|
-
|
9
|
-
abort "File #{CONF_PATH} not found!" unless ::File.exist?(CONF_PATH)
|
10
|
-
|
11
|
-
conf = ::YAML.load_file(CONF_PATH)
|
12
|
-
|
13
|
-
header = conf.fetch('header', '').to_s.chomp
|
14
|
-
front_object = conf.fetch('front_object')
|
15
|
-
subject = conf.fetch('subject')
|
16
|
-
variables = conf.fetch('variables', {})
|
17
|
-
actual_values = conf.fetch('actual_values')
|
18
|
-
|
19
|
-
raise ::TypeError unless subject.is_a?(::String)
|
20
|
-
raise ::TypeError unless variables.is_a?(::Hash)
|
21
|
-
raise ::TypeError unless actual_values.is_a?(::Array)
|
22
|
-
|
23
|
-
front_object = front_object.chomp if front_object.is_a?(::String)
|
24
|
-
|
25
|
-
require_relative ::File.join('..', 'lib', 'brutal')
|
26
|
-
|
27
|
-
scaffold = case ARGV.fetch(0, nil)
|
28
|
-
when nil
|
29
|
-
::Brutal::ScaffoldGenerator::Por
|
30
|
-
else
|
31
|
-
abort "#{ARGV[0].inspect} not (yet) supported!"
|
32
|
-
end
|
33
|
-
|
34
|
-
eval(header) # rubocop:disable Security/Eval
|
35
|
-
|
36
|
-
doc = [
|
37
|
-
header,
|
38
|
-
scaffold.new(front_object, subject, *actual_values, **variables).to_s
|
39
|
-
].reject(&:empty?).join("\n")
|
40
|
-
|
41
|
-
file = ::File.open('test.rb', 'w')
|
42
|
-
file.write(doc)
|
43
|
-
file.close
|
6
|
+
Brutal.generate!
|
data/lib/brutal.rb
CHANGED
@@ -1,11 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
%w[
|
4
|
+
configuration
|
5
|
+
file/read
|
6
|
+
file/write
|
7
|
+
scaffold
|
8
|
+
yaml
|
9
|
+
].each { |file_name| require_relative(File.join('brutal', file_name)) }
|
10
|
+
|
11
|
+
# The Brutal namespace.
|
6
12
|
module Brutal
|
7
|
-
|
13
|
+
def self.generate!
|
14
|
+
yaml = File::Read.new.call
|
15
|
+
hash = Yaml.parse(yaml)
|
16
|
+
conf = Configuration.load(hash)
|
17
|
+
ruby = Scaffold.new(conf.header,
|
18
|
+
conf.subject,
|
19
|
+
*conf.actuals,
|
20
|
+
**conf.contexts)
|
8
21
|
|
9
|
-
|
10
|
-
|
22
|
+
File::Write.new.call(ruby)
|
23
|
+
end
|
11
24
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Brutal
|
4
|
+
# Brutal::Configuration
|
5
|
+
#
|
6
|
+
# @since 1.0.0
|
7
|
+
class Configuration
|
8
|
+
DEFAULT_ACTUALS = [].freeze
|
9
|
+
DEFAULT_CONTEXTS = {}.freeze
|
10
|
+
DEFAULT_HEAD = '# Brutal test suite'
|
11
|
+
DEFAULT_SUBJECT = ''
|
12
|
+
|
13
|
+
# Load the configuration parameters.
|
14
|
+
#
|
15
|
+
# @param params [Hash] Receive the 4 top-level section parameters.
|
16
|
+
def self.load(params)
|
17
|
+
new(
|
18
|
+
actuals: params.fetch('actuals', DEFAULT_ACTUALS),
|
19
|
+
contexts: params.fetch('contexts', DEFAULT_CONTEXTS),
|
20
|
+
header: params.fetch('header', DEFAULT_HEAD),
|
21
|
+
subject: params.fetch('subject', DEFAULT_SUBJECT)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Specifies templates to challenge evaluated subjects & get results.
|
26
|
+
attr_reader :actuals
|
27
|
+
|
28
|
+
# Specifies a list of variables to populate the subject's template.
|
29
|
+
attr_reader :contexts
|
30
|
+
|
31
|
+
# Specifies the code to execute before generating the test suite.
|
32
|
+
attr_reader :header
|
33
|
+
|
34
|
+
# Specifies the template of the code to be declined across contexts.
|
35
|
+
attr_reader :subject
|
36
|
+
|
37
|
+
# Initialize a new configuration.
|
38
|
+
def initialize(actuals:, contexts:, header:, subject:)
|
39
|
+
raise ::TypeError, actuals.inspect unless actuals.is_a?(::Array)
|
40
|
+
raise ::TypeError, contexts.inspect unless contexts.is_a?(::Hash)
|
41
|
+
raise ::TypeError, header.inspect unless header.is_a?(::String)
|
42
|
+
raise ::TypeError, subject.inspect unless subject.is_a?(::String)
|
43
|
+
|
44
|
+
@actuals = actuals
|
45
|
+
@contexts = contexts
|
46
|
+
@header = header
|
47
|
+
@subject = subject
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Brutal
|
4
|
+
# Brutal::File
|
5
|
+
#
|
6
|
+
# @since 1.1.0
|
7
|
+
module File
|
8
|
+
# Brutal::File::Read
|
9
|
+
#
|
10
|
+
# @since 1.1.0
|
11
|
+
class Read
|
12
|
+
NAME = '.brutal.yml'
|
13
|
+
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
def initialize(name = NAME)
|
17
|
+
@name = name
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
::File.read(path)
|
22
|
+
rescue ::Errno::ENOENT => _e
|
23
|
+
abort("File #{path} not found!")
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def path
|
29
|
+
::File.join(::Dir.pwd, name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Brutal
|
4
|
+
# Brutal::File
|
5
|
+
#
|
6
|
+
# @since 1.1.0
|
7
|
+
module File
|
8
|
+
# Brutal::File::Write
|
9
|
+
#
|
10
|
+
# @since 1.1.0
|
11
|
+
class Write
|
12
|
+
NAME = 'test.rb'
|
13
|
+
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
def initialize(name = NAME)
|
17
|
+
@name = name
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(scaffold)
|
21
|
+
file = ::File.open(path, 'w')
|
22
|
+
file.write(scaffold)
|
23
|
+
|
24
|
+
true
|
25
|
+
ensure
|
26
|
+
file.close
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def path
|
32
|
+
::File.join(::Dir.pwd, name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Brutal
|
4
|
+
# Brutal::Scaffold
|
5
|
+
#
|
6
|
+
# @since 1.0.0
|
7
|
+
class Scaffold
|
8
|
+
# Specifies templates to challenge evaluated subjects & get results.
|
9
|
+
attr_reader :actuals
|
10
|
+
|
11
|
+
# Specifies a list of variables to populate the subject's template.
|
12
|
+
attr_reader :contexts
|
13
|
+
|
14
|
+
# Specifies the code to execute before generating the test suite.
|
15
|
+
attr_reader :header
|
16
|
+
|
17
|
+
# Specifies the template of the code to be declined across contexts.
|
18
|
+
attr_reader :subject
|
19
|
+
|
20
|
+
# Initialize a new scaffold generator.
|
21
|
+
def initialize(header, subject, *actuals, **contexts)
|
22
|
+
warn('Empty subject!') if subject.empty?
|
23
|
+
warn('Empty actual values!') if actuals.empty?
|
24
|
+
warn('Empty contexts!') if contexts.empty?
|
25
|
+
|
26
|
+
eval(header) # rubocop:disable Security/Eval
|
27
|
+
|
28
|
+
@header = header
|
29
|
+
@subject = subject
|
30
|
+
@actuals = actuals
|
31
|
+
@contexts = contexts
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return a Ruby string that can be evaluated.
|
35
|
+
def inspect(object)
|
36
|
+
return object.to_s unless object.is_a?(::String)
|
37
|
+
|
38
|
+
object.strip
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return a string representation.
|
42
|
+
#
|
43
|
+
# @return [String]
|
44
|
+
#
|
45
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
46
|
+
def to_s
|
47
|
+
"#{header.chomp}\n#{blank_line}" + combinations_values.map do |values|
|
48
|
+
attributes = context_names.each_with_index.inject({}) do |h, (name, i)|
|
49
|
+
h.merge(name.to_sym => inspect(values.fetch(i)))
|
50
|
+
end
|
51
|
+
|
52
|
+
actual_str = format(inspect(subject), **attributes)
|
53
|
+
|
54
|
+
string = <<~CODE
|
55
|
+
actual = begin
|
56
|
+
#{actual_str.gsub(/^/, ' ')}
|
57
|
+
end
|
58
|
+
|
59
|
+
CODE
|
60
|
+
|
61
|
+
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
62
|
+
|
63
|
+
actuals.each do |actual_value|
|
64
|
+
result_str = format(actual_value, subject: 'actual')
|
65
|
+
string += "raise if #{result_str} != #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
|
66
|
+
end
|
67
|
+
|
68
|
+
string
|
69
|
+
end.join(blank_line)
|
70
|
+
end
|
71
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
72
|
+
|
73
|
+
def blank_line
|
74
|
+
"\n" \
|
75
|
+
"# #{('-' * 78)}\n" \
|
76
|
+
"\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
def context_names
|
80
|
+
contexts.keys.sort
|
81
|
+
end
|
82
|
+
|
83
|
+
def contexts_values
|
84
|
+
context_names.map { |context_name| contexts.fetch(context_name) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def combinations_values
|
88
|
+
Array(contexts_values[0]).product(*Array(contexts_values[1..]))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/brutal/yaml.rb
ADDED
metadata
CHANGED
@@ -1,45 +1,45 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brutal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name: rubocop
|
42
|
+
name: rubocop-performance
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: rubocop-
|
56
|
+
name: rubocop-thread_safety
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -70,31 +70,31 @@ dependencies:
|
|
70
70
|
name: simplecov
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0
|
75
|
+
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0
|
82
|
+
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: yard
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0
|
89
|
+
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0
|
97
|
-
description:
|
96
|
+
version: '0'
|
97
|
+
description: A code-first approach to automate the writing of unit tests.
|
98
98
|
email: contact@cyril.email
|
99
99
|
executables:
|
100
100
|
- brutal
|
@@ -105,8 +105,11 @@ files:
|
|
105
105
|
- README.md
|
106
106
|
- bin/brutal
|
107
107
|
- lib/brutal.rb
|
108
|
-
- lib/brutal/
|
109
|
-
- lib/brutal/
|
108
|
+
- lib/brutal/configuration.rb
|
109
|
+
- lib/brutal/file/read.rb
|
110
|
+
- lib/brutal/file/write.rb
|
111
|
+
- lib/brutal/scaffold.rb
|
112
|
+
- lib/brutal/yaml.rb
|
110
113
|
homepage: https://github.com/fixrb/brutal
|
111
114
|
licenses:
|
112
115
|
- MIT
|
@@ -119,7 +122,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
122
|
requirements:
|
120
123
|
- - ">="
|
121
124
|
- !ruby/object:Gem::Version
|
122
|
-
version:
|
125
|
+
version: 2.7.0
|
123
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
127
|
requirements:
|
125
128
|
- - ">="
|
@@ -129,5 +132,5 @@ requirements: []
|
|
129
132
|
rubygems_version: 3.1.2
|
130
133
|
signing_key:
|
131
134
|
specification_version: 4
|
132
|
-
summary:
|
135
|
+
summary: A code-first approach to automate the writing of unit tests.
|
133
136
|
test_files: []
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Brutal
|
4
|
-
module ScaffoldGenerator
|
5
|
-
# Base class
|
6
|
-
class Base
|
7
|
-
# @api private
|
8
|
-
attr_reader :front_object_str, :front_object, :subject, :actual_values, :variables
|
9
|
-
|
10
|
-
# Initialize a new scaffold generator
|
11
|
-
def initialize(front_object, subject, *actual_values, **variables)
|
12
|
-
raise 'Empty actual values!' if actual_values.empty?
|
13
|
-
raise 'Empty variables!' if variables.empty?
|
14
|
-
|
15
|
-
@front_object_str = inspect(front_object)
|
16
|
-
@front_object = eval(@front_object_str) # rubocop:disable Security/Eval
|
17
|
-
@subject = subject
|
18
|
-
@actual_values = actual_values
|
19
|
-
@variables = variables
|
20
|
-
end
|
21
|
-
|
22
|
-
# Return a Ruby string that can be evaluated.
|
23
|
-
def inspect(object)
|
24
|
-
return object.to_s unless object.is_a?(::String)
|
25
|
-
|
26
|
-
object.strip
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module Brutal
|
6
|
-
module ScaffoldGenerator
|
7
|
-
# Plain Old Ruby.
|
8
|
-
#
|
9
|
-
# @api private
|
10
|
-
#
|
11
|
-
class Por < Base
|
12
|
-
# Return a string representation
|
13
|
-
#
|
14
|
-
# @return [String]
|
15
|
-
#
|
16
|
-
# @api public
|
17
|
-
def to_s
|
18
|
-
names = variables.keys.sort
|
19
|
-
values_arr = names.map { |name| variables.fetch(name) }
|
20
|
-
|
21
|
-
test_params = Array(values_arr[0]).product(*Array(values_arr[1..-1]))
|
22
|
-
|
23
|
-
blank_line +
|
24
|
-
"front_object = #{front_object_str}\n" +
|
25
|
-
blank_line +
|
26
|
-
test_params.map do |values|
|
27
|
-
attributes = names.each_with_index.inject(front_object: 'front_object') do |h, (name, i)|
|
28
|
-
h.merge(name.to_sym => inspect(values.fetch(i)))
|
29
|
-
end
|
30
|
-
|
31
|
-
actual_str = format(inspect(subject), **attributes)
|
32
|
-
|
33
|
-
string = "actual = #{actual_str}\n"
|
34
|
-
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
35
|
-
|
36
|
-
actual_values.each do |actual_value|
|
37
|
-
result_str = format(actual_value, subject: 'actual')
|
38
|
-
string += "raise unless #{result_str} == #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
|
39
|
-
end
|
40
|
-
|
41
|
-
string
|
42
|
-
end.join(blank_line)
|
43
|
-
end
|
44
|
-
|
45
|
-
def blank_line
|
46
|
-
"\n" \
|
47
|
-
"# #{('-' * 78)}\n" \
|
48
|
-
"\n"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|