brutal 0.4.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 491dc26f34135ff61ceb54bc64b4dc5793e8b2610d545b3025b8c7aaf53b3064
4
- data.tar.gz: e7c379c45ce23f0886041ec8a4b6ab4c493a91e450230b64968e45a92214a62e
3
+ metadata.gz: 332c8755656f334781310460780dd233d621ddac83bc7d7dc5f0c555f4d1f955
4
+ data.tar.gz: c49ae44f98432fb3839bcf7c76ac3929bfee3eac82113bc4903e6d288863865e
5
5
  SHA512:
6
- metadata.gz: 8791614f90a92630a28aa3fb8b6d2a9d1921404b1cc614302bfeb8a87970186c33b935d81db81b7c1d8751475a59d5f32c17ea80627d32755daece880b93ace4
7
- data.tar.gz: 19cf4fcbe94766983442f0f47b2651b5b675d5ade1c9b306e4213421d21253035545a23f8e6c988a8967db182b4de59a4158a545278684b4104039729963574c
6
+ metadata.gz: f015f9cbc3f478c66374b72a3c6e7340863b472eac796833700aa11a501c3361b2519939f0e407308a6800df23bb7ba959ec60d70f5b16acb86f3d30ae0440e2
7
+ data.tar.gz: 90c7d4fef875977bd991a16405675e424524d2478318d33e694d073a47348986a2bb93bc27f9ff5cdfb5d5d48120d334f55733ad0ff6a3880c401ba901c85391
data/README.md CHANGED
@@ -8,29 +8,30 @@
8
8
 
9
9
  > A _code-first_ approach to automate the writing of unit tests.
10
10
 
11
- ## Why
11
+ ## Intro
12
12
 
13
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
14
 
15
15
  > I don't like tests. It's not DRY.<br/>
16
16
  > -- [Matz](https://github.com/matz)
17
17
 
18
- ## Purpose
18
+ ## Overview
19
19
 
20
- Let __Brutal__ shape for you the actual behavior of your code against as many combinations of challenges as needed.
20
+ Let __Brutal__ shape for you in no time the actual behavior of your code through as many combinations of contexts as needed.
21
21
 
22
- Without giving the power to test everything, it makes it easy to generate in no time a set of tests for all relevant contexts.
23
-
24
- By delegating to __Brutal__ this repetitive (and redundant) job of writing tests, you'll be able to focus on your core business: writing code.
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.
25
23
 
26
24
  ## Warning
27
25
 
28
- __Brutal__ does not prevent from bugs.
29
- As a picture of the behavior of the code, generated tests would be wrong if the code is wrong.
26
+ __Brutal__ development process does not prevent from bugs.
30
27
 
31
- This is why it is important to carefully read a generated test suite, to ensure that it describes the behavior of the code as it is expected to behave.
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.
32
31
 
33
- This is the cost to enter the _Brutal-Driven Development_ with confidence.
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_.
34
35
 
35
36
  ## Installation
36
37
 
@@ -54,113 +55,63 @@ Just type `brutal` in a Ruby project's folder and watch the magic happen.
54
55
 
55
56
  ## Usage
56
57
 
57
- __Brutal__'s configuration file is `.brutal.yml`, which acts like a meta-spec.
58
- This YAML file can contains the following keys:
59
-
60
- * `header` (optional): Some code to execute before the test suite.
61
- * `front_object` (required): The front object of the test suite.
62
- * `subject` (required): The object of each context.
63
- * `variables` (required): A hash to generate the subject of each context.
64
- * `actual_values` (required): A list of tests to challenge the subject.
65
-
66
- ### Behavioral integrity
67
-
68
- In versioned projects,
69
- the integrity of the behavior of the code could easily be compared by executing `brutal` after changes.
70
-
71
- Assuming a project is versioned with git,
72
- if something goes wrong, the `git diff test.rb` command should instantly show changes between the behavior of the previous code and the behavior of the new one.
73
-
74
- Example of regression from [The Greeter class](https://github.com/fixrb/brutal/raw/master/examples/the_greeter_class/):
75
-
76
- ```diff
77
- require './greeter'
78
-
79
- # ------------------------------------------------------------------------------
80
-
81
- front_object = Greeter
82
-
83
- # ------------------------------------------------------------------------------
84
-
85
- actual = front_object.new('world')
86
-
87
- - raise unless actual.salute == "Hello World!"
88
- + raise unless actual.salute == "Hello !"
89
- ```
90
-
91
- ### Optional parameters
58
+ The `brutal.yml` file is a manifest you can use to define your __Brutal__ meta-spec.
59
+ It has 4 top-level sections:
92
60
 
93
- It would also be possible to ask for an RSpec template by passing "`rspec`" argument:
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.
94
65
 
95
- ```sh
96
- brutal rspec
97
- ```
98
-
99
- ## Example
66
+ ### Getting started
100
67
 
101
- Given this 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:
102
70
 
103
71
  ```yaml
104
72
  ---
105
- front_object: |
106
- "Hello "
107
-
108
73
  subject: |
109
- %{front_object} + %{string}
74
+ "Hello " + "%{string}"
110
75
 
111
- variables:
76
+ contexts:
112
77
  string:
113
- - "'Alice'"
114
- - "'Bob'"
78
+ - Alice
79
+ - Bob
115
80
 
116
- actual_values:
81
+ actuals:
117
82
  - "%{subject}.to_s"
118
83
  - "%{subject}.length"
119
84
  ```
120
85
 
121
- The `brutal` command would generate and write in to a `test.rb` file the following "Plain Old Ruby":
86
+ 2. Run the `brutal` command from the same directory.
87
+
88
+ 3. Read the generated `test.rb` file in the same directory:
122
89
 
123
90
  ```ruby
124
- front_object = "Hello "
91
+ # Brutal test suite
125
92
 
126
93
  # ------------------------------------------------------------------------------
127
94
 
128
- actual = front_object + 'Alice'
95
+ actual = begin
96
+ "Hello " + "Alice"
97
+ end
129
98
 
130
- raise unless actual.to_s == "Hello Alice"
131
- raise unless actual.length == 11
99
+ raise if actual.to_s != "Hello Alice"
100
+ raise if actual.length != 11
132
101
 
133
102
  # ------------------------------------------------------------------------------
134
103
 
135
- actual = front_object + 'Bob'
104
+ actual = begin
105
+ "Hello " + "Bob"
106
+ end
136
107
 
137
- raise unless actual.to_s == "Hello Bob"
138
- raise unless actual.length == 9
108
+ raise if actual.to_s != "Hello Bob"
109
+ raise if actual.length != 9
139
110
  ```
140
111
 
141
- And the `brutal rspec` command would generate and write in to a `test_spec.rb` file the following spec:
142
-
143
- ```ruby
144
- RSpec.describe do
145
- let(:front_object) { "Hello " }
146
-
147
- context do
148
- let(:actual) { front_object + 'Alice' }
149
-
150
- it { expect(actual.to_s).to eq("Hello Alice") }
151
- it { expect(actual.length).to eq(11) }
152
- end
153
-
154
- context do
155
- let(:actual) { front_object + 'Bob' }
156
-
157
- it { expect(actual.to_s).to eq("Hello Bob") }
158
- it { expect(actual.length).to eq(9) }
159
- end
160
- end
161
- ```
112
+ ### More examples
162
113
 
163
- More examples are available [here](https://github.com/fixrb/brutal/raw/master/examples/).
114
+ https://github.com/fixrb/brutal/raw/master/examples/
164
115
 
165
116
  ## Rake integration example
166
117
 
data/bin/brutal CHANGED
@@ -1,45 +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, file_name = case ARGV.fetch(0, nil)
28
- when nil
29
- [::Brutal::ScaffoldGenerator::Por, 'test.rb']
30
- when 'rspec'
31
- [::Brutal::ScaffoldGenerator::RSpec, 'test_spec.rb']
32
- else
33
- abort "#{ARGV[0].inspect} not (yet) supported!"
34
- end
35
-
36
- eval(header) # rubocop:disable Security/Eval
37
-
38
- doc = [
39
- header,
40
- scaffold.new(front_object, subject, *actual_values, **variables).to_s
41
- ].reject(&:empty?).join("\n\n")
42
-
43
- file = ::File.open(file_name, 'w')
44
- file.write(doc)
45
- file.close
6
+ Brutal.generate!
@@ -1,11 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Main namespace
4
- #
5
- # @api public
3
+ %w[
4
+ configuration
5
+ scaffold
6
+ ].each { |file_name| require_relative(File.join('brutal', file_name)) }
7
+
8
+ # The Brutal namespace
6
9
  module Brutal
7
- end
10
+ def self.settings
11
+ Configuration.load!
12
+ end
13
+
14
+ def self.generate
15
+ Scaffold.new(*settings)
16
+ end
17
+
18
+ def self.generate!
19
+ file = ::File.open('test.rb', 'w')
20
+ file.write(generate)
8
21
 
9
- Dir[File.join File.dirname(__FILE__), 'brutal', 'scaffold_generator', '*.rb'].each do |fname|
10
- require_relative fname
22
+ true
23
+ ensure
24
+ file.close
25
+ end
11
26
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Brutal
6
+ # Brutal::Configuration
7
+ #
8
+ # @since 1.0.0
9
+ class Configuration
10
+ NAME = '.brutal.yml'
11
+ PATH = ::File.join(::Dir.pwd, NAME).freeze
12
+
13
+ def self.load!
14
+ new.to_a
15
+ end
16
+
17
+ def self.file!
18
+ ::YAML.load_file(PATH)
19
+ rescue ::Errno::ENOENT => _e
20
+ abort("File #{PATH} not found!")
21
+ end
22
+
23
+ attr_reader(:header, :subject, :contexts, :actuals)
24
+
25
+ # rubocop:disable Metrics/AbcSize
26
+ def initialize
27
+ settings = self.class.file!
28
+
29
+ @header = settings.fetch('header', '# Brutal test suite')
30
+ @subject = settings.fetch('subject', '')
31
+ @contexts = settings.fetch('contexts', {})
32
+ @actuals = settings.fetch('actuals', [])
33
+
34
+ raise ::TypeError, @header.inspect unless @header.is_a?(::String)
35
+ raise ::TypeError, @subject.inspect unless @subject.is_a?(::String)
36
+ raise ::TypeError, @contexts.inspect unless @contexts.is_a?(::Hash)
37
+ raise ::TypeError, @actuals.inspect unless @actuals.is_a?(::Array)
38
+ end
39
+ # rubocop:enable Metrics/AbcSize
40
+
41
+ def to_a
42
+ [header, subject, *actuals, **contexts]
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Brutal
4
+ # Brutal::Scaffold
5
+ #
6
+ # @since 1.0.0
7
+ class Scaffold
8
+ attr_reader(:header, :subject, :actuals, :contexts)
9
+
10
+ # Initialize a new scaffold generator
11
+ def initialize(header, subject, *actuals, **contexts)
12
+ warn('Empty subject!') if subject.empty?
13
+ warn('Empty actual values!') if actuals.empty?
14
+ warn('Empty contexts!') if contexts.empty?
15
+
16
+ eval(header) # rubocop:disable Security/Eval
17
+
18
+ @header = header
19
+ @subject = subject
20
+ @actuals = actuals
21
+ @contexts = contexts
22
+ end
23
+
24
+ # Return a Ruby string that can be evaluated.
25
+ def inspect(object)
26
+ return object.to_s unless object.is_a?(::String)
27
+
28
+ object.strip
29
+ end
30
+
31
+ # Return a string representation
32
+ #
33
+ # @return [String]
34
+ #
35
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
36
+ def to_s
37
+ header.chomp + "\n" + blank_line + combinations_values.map do |values|
38
+ attributes = context_names.each_with_index.inject({}) do |h, (name, i)|
39
+ h.merge(name.to_sym => inspect(values.fetch(i)))
40
+ end
41
+
42
+ actual_str = format(inspect(subject), **attributes)
43
+
44
+ string = <<~CODE
45
+ actual = begin
46
+ #{actual_str.gsub(/^/, ' ')}
47
+ end
48
+
49
+ CODE
50
+
51
+ actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
52
+
53
+ actuals.each do |actual_value|
54
+ result_str = format(actual_value, subject: 'actual')
55
+ string += "raise if #{result_str} != #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
56
+ end
57
+
58
+ string
59
+ end.join(blank_line)
60
+ end
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
62
+
63
+ def blank_line
64
+ "\n" \
65
+ "# #{('-' * 78)}\n" \
66
+ "\n"
67
+ end
68
+
69
+ def context_names
70
+ contexts.keys.sort
71
+ end
72
+
73
+ def contexts_values
74
+ context_names.map { |context_name| contexts.fetch(context_name) }
75
+ end
76
+
77
+ def combinations_values
78
+ Array(contexts_values[0]).product(*Array(contexts_values[1..-1]))
79
+ end
80
+ end
81
+ end
metadata CHANGED
@@ -1,113 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brutal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 1.0.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: 2020-01-23 00:00:00.000000000 Z
11
+ date: 2020-03-01 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.1'
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.1'
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'
41
- - !ruby/object:Gem::Dependency
42
- name: rubocop
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '0.79'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '0.79'
40
+ version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: rubocop-performance
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - "~>"
45
+ - - ">="
60
46
  - !ruby/object:Gem::Version
61
- version: '1.5'
47
+ version: '0'
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - "~>"
52
+ - - ">="
67
53
  - !ruby/object:Gem::Version
68
- version: '1.5'
54
+ version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
- name: simplecov
56
+ name: rubocop-thread_safety
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
- - - "~>"
59
+ - - ">="
74
60
  - !ruby/object:Gem::Version
75
- version: '0.17'
61
+ version: '0'
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - "~>"
66
+ - - ">="
81
67
  - !ruby/object:Gem::Version
82
- version: '0.17'
68
+ version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
- name: spectus
70
+ name: simplecov
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
- - - "~>"
73
+ - - ">="
88
74
  - !ruby/object:Gem::Version
89
- version: '3.1'
75
+ version: '0'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
- - - "~>"
80
+ - - ">="
95
81
  - !ruby/object:Gem::Version
96
- version: '3.1'
82
+ version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: yard
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
- - - "~>"
87
+ - - ">="
102
88
  - !ruby/object:Gem::Version
103
- version: '0.9'
89
+ version: '0'
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
- - - "~>"
94
+ - - ">="
109
95
  - !ruby/object:Gem::Version
110
- version: '0.9'
96
+ version: '0'
111
97
  description: A code-first approach to automate the writing of unit tests.
112
98
  email: contact@cyril.email
113
99
  executables:
@@ -119,9 +105,8 @@ files:
119
105
  - README.md
120
106
  - bin/brutal
121
107
  - lib/brutal.rb
122
- - lib/brutal/scaffold_generator/base.rb
123
- - lib/brutal/scaffold_generator/por.rb
124
- - lib/brutal/scaffold_generator/rspec.rb
108
+ - lib/brutal/configuration.rb
109
+ - lib/brutal/scaffold.rb
125
110
  homepage: https://github.com/fixrb/brutal
126
111
  licenses:
127
112
  - MIT
@@ -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,58 +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.gsub(/\A\n/, '') +
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 = <<~CODE
34
- actual = begin
35
- #{actual_str.gsub(/^/, ' ')}
36
- end
37
-
38
- CODE
39
-
40
- actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
41
-
42
- actual_values.each do |actual_value|
43
- result_str = format(actual_value, subject: 'actual')
44
- string += "raise unless #{result_str} == #{eval(result_str).inspect}\n" # rubocop:disable Security/Eval
45
- end
46
-
47
- string
48
- end.join(blank_line)
49
- end
50
-
51
- def blank_line
52
- "\n" \
53
- "# #{('-' * 78)}\n" \
54
- "\n"
55
- end
56
- end
57
- end
58
- end
@@ -1,55 +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 RSpec < 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
- "RSpec.describe do\n" \
24
- " let(:front_object) { #{front_object_str} }\n" \
25
- "\n" +
26
- test_params.map do |values|
27
- string = " context do\n"
28
-
29
- attributes = names.each_with_index.inject(front_object: 'front_object') do |h, (name, i)|
30
- h.merge(name.to_sym => inspect(values.fetch(i)))
31
- end
32
-
33
- actual_str = format(inspect(subject), **attributes)
34
-
35
- string += " let(:actual) { #{actual_str} }\n\n"
36
- actual = eval(actual_str) # rubocop:disable Security/Eval, Lint/UselessAssignment
37
-
38
- actual_values.each do |actual_value|
39
- result_str = format(actual_value, subject: 'actual')
40
- string += " it { expect(#{result_str}).to eq(#{eval(result_str).inspect}) }\n" # rubocop:disable Security/Eval
41
- end
42
-
43
- string += " end\n"
44
-
45
- string
46
- end.join(blank_line) +
47
- "end\n"
48
- end
49
-
50
- def blank_line
51
- "\n"
52
- end
53
- end
54
- end
55
- end