brutal 1.2.1 → 1.5.0
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 +37 -42
- data/bin/brutal +7 -1
- data/lib/brutal/file/read.rb +2 -7
- data/lib/brutal/file/write.rb +1 -6
- data/lib/brutal/file.rb +31 -0
- data/lib/brutal/scaffold.rb +46 -21
- data/lib/brutal/yaml.rb +10 -0
- data/lib/brutal.rb +25 -7
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea124053ebeef681c2e046022936f9d67ca97778733a23bd2d81781e90daa9a8
|
4
|
+
data.tar.gz: 677089454fece7f60aedfc4693b53c132f057bbeeaca5e651f88ec52d026ea34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1d043d4740db1987c188755e9b339da566034a3cc2a43f99ced12fb3de26a93df0f93d669b20700ba662a935d9be662a25bffba8f8ae92aae3e01deade92d9b
|
7
|
+
data.tar.gz: 5abc20c46849f69b20de5b89848f8fed831b2d92641f57cfa349be4e0a975c8630c27268d16a022911693d403af8262b4f0d1099dcf468879392e9c2d7fe7ec0
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Brutal 💎🔨
|
2
2
|
|
3
|
-
[](https://github.com/fixrb/brutal/
|
3
|
+
[](https://github.com/fixrb/brutal/tags)
|
4
4
|
[](https://rubydoc.info/github/fixrb/brutal/main)
|
5
|
-
[](https://github.com/fixrb/brutal/actions?query=workflow%3Aruby+branch%3Amain)
|
6
6
|
[](https://github.com/fixrb/brutal/actions?query=workflow%3Arubocop+branch%3Amain)
|
7
7
|
[](https://github.com/fixrb/brutal/raw/main/LICENSE.md)
|
8
8
|
|
@@ -59,63 +59,60 @@ Just type `brutal` in a Ruby project's folder and watch the magic happen.
|
|
59
59
|
|
60
60
|
## Usage
|
61
61
|
|
62
|
-
|
63
|
-
|
62
|
+
__Brutal__ needs a configuration file to know how to write your tests.
|
63
|
+
Currently, only the YAML format is supported.
|
64
|
+
This file is by default named `.brutal.yml` and is composed of 4 top-level sections:
|
64
65
|
|
65
66
|
* `header` - Specifies the code to execute before generating the test suite.
|
66
67
|
* `subject` - Specifies the template of the code to be declined across contexts.
|
67
68
|
* `contexts` - Specifies a list of variables to populate the subject's template.
|
68
69
|
* `actuals` - Specifies templates to challenge evaluated subjects & get results.
|
69
70
|
|
70
|
-
|
71
|
+
When the configuration file is present, the generation of a test suite can be done with the command:
|
71
72
|
|
72
|
-
|
73
|
-
|
73
|
+
```sh
|
74
|
+
brutal .brutal.yml
|
75
|
+
```
|
74
76
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
or:
|
78
|
+
|
79
|
+
```sh
|
80
|
+
brutal .
|
81
|
+
```
|
79
82
|
|
80
|
-
|
81
|
-
string:
|
82
|
-
- Alice
|
83
|
-
- Bob
|
83
|
+
or even:
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
- "%{subject}.length"
|
85
|
+
```sh
|
86
|
+
brutal
|
88
87
|
```
|
89
88
|
|
90
|
-
|
89
|
+
This would create a `test.rb` file containing the test suite.
|
91
90
|
|
92
|
-
|
91
|
+
Configuration files can also be named differently:
|
93
92
|
|
94
|
-
```
|
95
|
-
|
93
|
+
```sh
|
94
|
+
brutal path/to/test_hello_world.yml
|
95
|
+
```
|
96
96
|
|
97
|
-
|
97
|
+
This would create a `path/to/test_hello_world.rb` file containing the test suite.
|
98
98
|
|
99
|
-
|
100
|
-
"Hello " + "Alice"
|
101
|
-
end
|
99
|
+
To avoid accidentally overwriting a file, the `--no-force` option can be used:
|
102
100
|
|
103
|
-
|
104
|
-
|
101
|
+
```sh
|
102
|
+
brutal path/to/test_hello_world.yml --no-force
|
103
|
+
```
|
105
104
|
|
106
|
-
|
105
|
+
> A path/to/test_hello_world.rb file already exists!
|
107
106
|
|
108
|
-
|
109
|
-
"Hello " + "Bob"
|
110
|
-
end
|
107
|
+
### Getting started
|
111
108
|
|
112
|
-
|
113
|
-
|
114
|
-
|
109
|
+
1. Create a `.brutal.yml` file in your application's root directory. For example: <https://github.com/fixrb/brutal/blob/v1.4.0/examples/hello_world_v1/.brutal.yml>
|
110
|
+
2. Run the `brutal` command from the same directory.
|
111
|
+
3. Read the generated `test.rb` file in the same directory: <https://github.com/fixrb/brutal/blob/v1.4.0/examples/hello_world_v1/test.rb>
|
115
112
|
|
116
113
|
### More examples
|
117
114
|
|
118
|
-
https://github.com/fixrb/brutal/
|
115
|
+
<https://github.com/fixrb/brutal/blob/v1.4.0/examples/>
|
119
116
|
|
120
117
|
## Rake integration example
|
121
118
|
|
@@ -145,9 +142,7 @@ The [gem](https://rubygems.org/gems/brutal) is available as open source under th
|
|
145
142
|
|
146
143
|
***
|
147
144
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
alt="Sashite" /></a>
|
153
|
-
</p>
|
145
|
+
This project is sponsored by [Sashité](https://github.com/sashite/):
|
146
|
+
|
147
|
+

|
148
|
+

|
data/bin/brutal
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "pathname"
|
4
5
|
require_relative File.join("..", "lib", "brutal")
|
5
6
|
|
6
|
-
Brutal
|
7
|
+
path = ARGV.fetch(0, Brutal::File::DEFAULT_CONFIG_FILENAME)
|
8
|
+
pathname = Pathname.new(path)
|
9
|
+
pathname += Brutal::File::DEFAULT_CONFIG_FILENAME if pathname.directory?
|
10
|
+
force_opt = ARGV.none?("--no-force")
|
11
|
+
|
12
|
+
Brutal.generate!(pathname, force: force_opt)
|
data/lib/brutal/file/read.rb
CHANGED
@@ -1,26 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Brutal
|
4
|
-
# Brutal::File
|
5
|
-
#
|
6
|
-
# @since 1.1.0
|
7
4
|
module File
|
8
5
|
# Brutal::File::Read
|
9
6
|
#
|
10
7
|
# @since 1.1.0
|
11
8
|
class Read
|
12
|
-
NAME = ".brutal.yml"
|
13
|
-
|
14
9
|
attr_reader :name
|
15
10
|
|
16
|
-
def initialize(name
|
11
|
+
def initialize(name)
|
17
12
|
@name = name
|
18
13
|
end
|
19
14
|
|
20
15
|
def call
|
21
16
|
::File.read(path)
|
22
17
|
rescue ::Errno::ENOENT => _e
|
23
|
-
abort
|
18
|
+
abort "File #{path} not found!"
|
24
19
|
end
|
25
20
|
|
26
21
|
protected
|
data/lib/brutal/file/write.rb
CHANGED
@@ -1,19 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Brutal
|
4
|
-
# Brutal::File
|
5
|
-
#
|
6
|
-
# @since 1.1.0
|
7
4
|
module File
|
8
5
|
# Brutal::File::Write
|
9
6
|
#
|
10
7
|
# @since 1.1.0
|
11
8
|
class Write
|
12
|
-
NAME = "test.rb"
|
13
|
-
|
14
9
|
attr_reader :name
|
15
10
|
|
16
|
-
def initialize(name
|
11
|
+
def initialize(name)
|
17
12
|
@name = name
|
18
13
|
end
|
19
14
|
|
data/lib/brutal/file.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
%w[
|
4
|
+
read
|
5
|
+
write
|
6
|
+
].each { |filename| require_relative(File.join("file", filename)) }
|
7
|
+
|
8
|
+
module Brutal
|
9
|
+
# Brutal::File
|
10
|
+
module File
|
11
|
+
DEFAULT_CONFIG_FILENAME = ".brutal.yml"
|
12
|
+
DEFAULT_GENERATED_FILENAME = "test.rb"
|
13
|
+
RUBY_EXTENSION = ".rb"
|
14
|
+
|
15
|
+
def self.generated_pathname(pathname)
|
16
|
+
filename = pathname.basename
|
17
|
+
return pathname.dirname + DEFAULT_GENERATED_FILENAME if default_config_filename?(filename)
|
18
|
+
|
19
|
+
pathname.sub_ext(RUBY_EXTENSION)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.override_protection(pathname)
|
23
|
+
abort "A #{pathname} file already exists!" if pathname.exist?
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.default_config_filename?(filename)
|
27
|
+
filename.to_s == DEFAULT_CONFIG_FILENAME
|
28
|
+
end
|
29
|
+
private_class_method :default_config_filename?
|
30
|
+
end
|
31
|
+
end
|
data/lib/brutal/scaffold.rb
CHANGED
@@ -42,20 +42,35 @@ module Brutal
|
|
42
42
|
#
|
43
43
|
# @return [String]
|
44
44
|
def to_s
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
ruby_lines.join(separator_ruby_code)
|
46
|
+
end
|
47
|
+
|
48
|
+
def attributes(*values)
|
49
|
+
context_names.each_with_index.inject({}) do |h, (name, i)|
|
50
|
+
h.merge(name.to_sym => inspect(values.fetch(i)))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def context_names
|
55
|
+
contexts.keys.sort
|
56
|
+
end
|
49
57
|
|
50
|
-
|
58
|
+
def contexts_values
|
59
|
+
context_names.map { |context_name| contexts.fetch(context_name) }
|
60
|
+
end
|
51
61
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
62
|
+
def combinations_values
|
63
|
+
Array(contexts_values[0]).product(*Array(contexts_values[1..]))
|
64
|
+
end
|
56
65
|
|
57
|
-
|
66
|
+
def ruby_lines
|
67
|
+
[header_ruby_code] + actual_ruby_codes
|
68
|
+
end
|
58
69
|
|
70
|
+
def actual_ruby_codes
|
71
|
+
combinations_values.map do |values|
|
72
|
+
actual_str = format(inspect(subject), **attributes(*values))
|
73
|
+
string = actual_ruby_code(actual_str)
|
59
74
|
actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
|
60
75
|
|
61
76
|
actuals.each do |actual_value|
|
@@ -64,25 +79,35 @@ module Brutal
|
|
64
79
|
end
|
65
80
|
|
66
81
|
string
|
67
|
-
end
|
82
|
+
end
|
68
83
|
end
|
69
84
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
85
|
+
def actual_ruby_code(actual_str)
|
86
|
+
<<~RUBY_CODE
|
87
|
+
actual = begin
|
88
|
+
#{actual_str.gsub(/^/, ' ')}
|
89
|
+
end
|
90
|
+
|
91
|
+
RUBY_CODE
|
74
92
|
end
|
75
93
|
|
76
|
-
def
|
77
|
-
|
94
|
+
def header_ruby_code
|
95
|
+
<<~RUBY_CODE
|
96
|
+
#{header.chomp}
|
97
|
+
RUBY_CODE
|
78
98
|
end
|
79
99
|
|
80
|
-
def
|
81
|
-
|
100
|
+
def separator_ruby_code
|
101
|
+
<<~RUBY_CODE
|
102
|
+
|
103
|
+
#{thematic_break_ruby_code}
|
104
|
+
RUBY_CODE
|
82
105
|
end
|
83
106
|
|
84
|
-
def
|
85
|
-
|
107
|
+
def thematic_break_ruby_code
|
108
|
+
<<~RUBY_CODE
|
109
|
+
# #{'-' * 78}
|
110
|
+
RUBY_CODE
|
86
111
|
end
|
87
112
|
end
|
88
113
|
end
|
data/lib/brutal/yaml.rb
CHANGED
@@ -7,8 +7,18 @@ module Brutal
|
|
7
7
|
#
|
8
8
|
# @since 1.1.0
|
9
9
|
module Yaml
|
10
|
+
FILENAME_EXTENSIONS = %w[
|
11
|
+
.yaml
|
12
|
+
.yml
|
13
|
+
].freeze
|
14
|
+
|
10
15
|
def self.parse(yaml)
|
11
16
|
::YAML.safe_load(yaml, symbolize_names: false)
|
12
17
|
end
|
18
|
+
|
19
|
+
def self.parse?(pathname)
|
20
|
+
filename_extension = pathname.extname
|
21
|
+
FILENAME_EXTENSIONS.include?(filename_extension)
|
22
|
+
end
|
13
23
|
end
|
14
24
|
end
|
data/lib/brutal.rb
CHANGED
@@ -2,17 +2,15 @@
|
|
2
2
|
|
3
3
|
%w[
|
4
4
|
configuration
|
5
|
-
file
|
6
|
-
file/write
|
5
|
+
file
|
7
6
|
scaffold
|
8
7
|
yaml
|
9
|
-
].each { |
|
8
|
+
].each { |filename| require_relative(File.join("brutal", filename)) }
|
10
9
|
|
11
10
|
# The Brutal namespace.
|
12
11
|
module Brutal
|
13
|
-
def self.generate!
|
14
|
-
|
15
|
-
hash = Yaml.parse(yaml)
|
12
|
+
def self.generate!(pathname, force: true)
|
13
|
+
hash = parse(pathname)
|
16
14
|
conf = Configuration.load(hash)
|
17
15
|
|
18
16
|
ruby = Scaffold.new(conf.header,
|
@@ -20,6 +18,26 @@ module Brutal
|
|
20
18
|
*conf.actuals,
|
21
19
|
**conf.contexts)
|
22
20
|
|
23
|
-
|
21
|
+
write(pathname, ruby, force: force)
|
24
22
|
end
|
23
|
+
|
24
|
+
def self.parse(pathname)
|
25
|
+
return Yaml.parse(read(pathname)) if Yaml.parse?(pathname)
|
26
|
+
|
27
|
+
raise ::ArgumentError, "Unrecognized extension. " \
|
28
|
+
"Impossible to parse #{pathname.inspect}."
|
29
|
+
end
|
30
|
+
private_class_method :parse
|
31
|
+
|
32
|
+
def self.read(pathname)
|
33
|
+
File::Read.new(pathname).call
|
34
|
+
end
|
35
|
+
private_class_method :read
|
36
|
+
|
37
|
+
def self.write(pathname, ruby, force:)
|
38
|
+
new_pathname = File.generated_pathname(pathname)
|
39
|
+
File.override_protection(new_pathname) unless force
|
40
|
+
File::Write.new(new_pathname).call(ruby)
|
41
|
+
end
|
42
|
+
private_class_method :write
|
25
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brutal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop-gitlab-security
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rubocop-md
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +148,7 @@ files:
|
|
134
148
|
- bin/brutal
|
135
149
|
- lib/brutal.rb
|
136
150
|
- lib/brutal/configuration.rb
|
151
|
+
- lib/brutal/file.rb
|
137
152
|
- lib/brutal/file/read.rb
|
138
153
|
- lib/brutal/file/write.rb
|
139
154
|
- lib/brutal/scaffold.rb
|