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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e7af479b185bf19e8cb7050360c79d216af2916668e76efb0d7c550e553af4b
4
- data.tar.gz: e917683e2edd247429cb9776444e6a97c10b9930265459a10ac8a3b4dbcf2cf6
3
+ metadata.gz: 98d43784db59dbf4d8263ec4730d302832e7e5edc46ebafc1a13d95935e9b697
4
+ data.tar.gz: e2d37b31d5b37387437e6ac80afb11b5b1a6d104cb677629ec3aafa9aae153af
5
5
  SHA512:
6
- metadata.gz: ad95d20234be22ed17de738afa0f1f1e0a5518cbf967fe87f9f1932f571e13d683a454354dd792f07d55010033dfd257b888422a880a4bd6c0f04ed19cf284ed
7
- data.tar.gz: c4e82e9a5036e134e83f86c313277573926fe0773ef3e2e43145d30fb725ba6a7a196f881f090f083589ac447889b7aa10d1d7e450ec550f468792991bffb824
6
+ metadata.gz: d3af04c68e89e7bdd965163066db2b99a4e3df78846ea6f33a0a89b9fe443e477376a6108a31c8b69d86d45f04459991d503f56c345b5089b7233a22b0d831bd
7
+ data.tar.gz: 55e326bf5cdcba338c63d7e57a8f75378f9869fe0f986867a86ece479850dd78745a2a568c3e0b89c8080f343e977b17d42b714e80dbbe3543b980204fd67c4d
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019 Cyril Kato
3
+ Copyright (c) 2020 Cyril Kato
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
+ [![RuboCop Status](https://github.com/fixrb/brutal/workflows/RuboCop/badge.svg)][workflow_rubocop]
6
4
  [![Build Status](https://api.travis-ci.org/fixrb/brutal.svg?branch=master)][travis]
7
5
  [![Gem Version](https://badge.fury.io/rb/brutal.svg)][gem]
8
6
  [![Inline docs](https://inch-ci.org/github/fixrb/brutal.svg?branch=master)][inchpages]
9
7
  [![Documentation](http://img.shields.io/:yard-docs-38c800.svg)][rubydoc]
10
8
 
11
- __Brutal__ is a _code-first_ approach that automates the writing of unit tests.
9
+ > A _code-first_ approach to automate the writing of unit tests.
12
10
 
13
- By being able to generate test cases from the code itself, developers can focus on their creation, save time and energy, for more happiness.
11
+ ## Intro
14
12
 
15
- <p>
16
- <img
17
- src="https://github.com/fixrb/brutal/raw/master/img/Ferdinand_Hodler_-_Woodcutter_-_Google_Art_Project.jpg"
18
- alt="A lumberjack brutally cutting a tree" />
19
- </p>
13
+ [![I Hate Tests](https://github.com/fixrb/brutal/raw/master/img/rubyhack-2019-ruby3-what-s-missing-by-yukihiro-matsumoto.jpg)](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 Brutal YAML file handles 4 keys:
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` (optional): Some code to execute before the test suite.
46
- * `front_object` (required): The front object of the test suite.
47
- * `subject` (required): The object of each context.
48
- * `variables` (required): A hash to generate the subject of each context.
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
- ## Example
66
+ ### Getting started
52
67
 
53
- Given this `.brutal.yml` config file:
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
- %{front_object} + %{string}
74
+ "Hello " + "%{string}"
62
75
 
63
- variables:
76
+ contexts:
64
77
  string:
65
- - "'Alice'"
66
- - "'Bob'"
78
+ - Alice
79
+ - Bob
67
80
 
68
- actual_values:
81
+ actuals:
69
82
  - "%{subject}.to_s"
70
83
  - "%{subject}.length"
71
84
  ```
72
85
 
73
- The `brutal` command would save this Plain Old Ruby in to a `test.rb` file:
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
- front_object = "Hello "
91
+ # Brutal test suite
77
92
 
78
93
  # ------------------------------------------------------------------------------
79
94
 
80
- actual = front_object + 'Alice'
81
- raise unless actual.to_s == "Hello Alice"
82
- raise unless actual.length == 11
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 = front_object + 'Bob'
87
- raise unless actual.to_s == "Hello Bob"
88
- raise unless actual.length == 9
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
- [More examples](examples/) are available.
112
+ ### More examples
113
+
114
+ https://github.com/fixrb/brutal/raw/master/examples/
92
115
 
93
116
  ## Rake integration example
94
117
 
95
- The generated brutal test suite `test.rb` file can be declared as follows:
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
- * Home page: https://github.com/fixrb/brutal
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
- require 'yaml'
4
+ require_relative File.join('..', 'lib', 'brutal')
5
5
 
6
- FILE_NAME = '.brutal.yml'
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!
@@ -1,11 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Main namespace
4
- #
5
- # @api public
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
- end
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
- Dir[File.join File.dirname(__FILE__), 'brutal', 'scaffold_generator', '*.rb'].each do |fname|
10
- require_relative fname
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
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Brutal
6
+ # Brutal::Yaml
7
+ #
8
+ # @since 1.1.0
9
+ module Yaml
10
+ def self.parse(yaml)
11
+ ::YAML.safe_load(yaml, symbolize_names: false)
12
+ end
13
+ end
14
+ end
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: 0.3.0
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: 2019-12-24 00:00:00.000000000 Z
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: '2.0'
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: '2.0'
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: '13.0'
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: '13.0'
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-performance
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.17'
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.17'
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.9'
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.9'
97
- description: Scaffolds test cases from the code itself.
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/scaffold_generator/base.rb
109
- - lib/brutal/scaffold_generator/por.rb
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: '0'
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: Scaffolds test cases from the code itself.
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