brutal 0.4.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b21dc72c3687aa3893272f404e01d0329a528a61caa2b6f5ec251b5de66a3a4
4
- data.tar.gz: 9aeb0ad4c455bb747700dc3af6bdf8957376e100af6e7b11bc6b377218e12426
3
+ metadata.gz: 7a7eb1b01e30dda0f5b204eef32ac2ec59c19384b37500f40587a1a522fce6db
4
+ data.tar.gz: be19e4c8f4741658ef2aea98891b8bf040a250361fe48fee5219b02cc297b10f
5
5
  SHA512:
6
- metadata.gz: 7a818e0951d2993387caf80e699a989ffecca901f43ebf00b430b1de2ce330eb8da486c75800fd936a32b6f9b3286703d5ce4e85995b1a652f945ee9261162d3
7
- data.tar.gz: afeedf7b04fc5be914e400a2b1a2149b0f82fb45f069654f2cc0bcf72e64cc84fc1c906b52c780d5199c2897059e1fc8e6125c6667e32c8a26fd656ae645a0c8
6
+ metadata.gz: 565a5d6628dd7f3904f97343da33539eaddf97c6fa020d2c14736ed84a146c3fe3d0aa7eb6fdfc73a94a3238be3bba40d892f07281d231918022b2c96fb95ce6
7
+ data.tar.gz: 245f56b40a04d626c653e679b3634a5e1f3805a4f356c6f009759cb9e350e675b89e17e699e430fac5ffcb596e83c19190b929b4086b1da42254f2ffdbc7ea60
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-2021 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,84 +1,44 @@
1
1
  # Brutal 💎🔨
2
2
 
3
- [![Build Status](https://api.travis-ci.org/fixrb/brutal.svg?branch=master)][travis]
3
+ [![RuboCop Status](https://github.com/fixrb/brutal/workflows/RuboCop/badge.svg)][workflow_rubocop]
4
+ [![Build Status](https://api.travis-ci.org/fixrb/brutal.svg?branch=main)][travis]
4
5
  [![Gem Version](https://badge.fury.io/rb/brutal.svg)][gem]
5
- [![Inline docs](https://inch-ci.org/github/fixrb/brutal.svg?branch=master)][inchpages]
6
+ [![Inline docs](https://inch-ci.org/github/fixrb/brutal.svg?branch=main)][inchpages]
6
7
  [![Documentation](http://img.shields.io/:yard-docs-38c800.svg)][rubydoc]
7
8
 
8
9
  > A _code-first_ approach to automate the writing of unit tests.
9
10
 
10
11
  ## Intro
11
12
 
12
- [![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.")
13
+ [![I Hate Tests](https://github.com/fixrb/brutal/raw/main/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.")
13
14
 
14
15
  > I don't like tests. It's not DRY.<br/>
15
16
  > -- [Matz](https://github.com/matz)
16
17
 
17
- ## Purpose
18
+ ## Overview
18
19
 
19
- Take a break and 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.
20
21
 
21
- Without giving the power to test everything, it makes it easy to generate in no time a set of tests for all relevant contexts.
22
-
23
- By delegating to __Brutal__ the repetitive and redundant task 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.
24
23
 
25
24
  ## Warning
26
25
 
27
- __Brutal__ does not prevent from bugs.
28
- As a picture of the behavior of the code, its generated tests would be wrong if the code is wrong.
29
-
30
- This is why it is important to carefully read the generated test suite, to ensure that it describes the behavior of the code as it is supposed to behave.
31
-
32
- However, when the `brutal` command is executed successfully,
33
- we can consider that both __Brutal__'s configuration file (`.brutal.yml`) and evaluated code as syntactically correct.
34
-
35
- In the context of a versioned project, to avoid regressions,
36
- the integrity of the behavior of the code can easily be checked by re-generating a picture to be compared with the previous one.
37
- Here is an example with [The Greeter class](https://github.com/fixrb/brutal/raw/master/examples/the_greeter_class/) code:
38
-
39
- ```sh
40
- git diff
41
- ```
42
-
43
- The code:
44
-
45
- ```diff
46
- # The Greeter class
47
- class Greeter
48
- def initialize(name)
49
- - @name = name
50
- + @name = name.capitalize
51
- end
52
-
53
- def salute
54
- "Hello #{@name}!"
55
- end
56
- end
57
- ```
58
-
59
- The generated test of the code:
60
-
61
- ```diff
62
- require './greeter'
63
-
64
- # ------------------------------------------------------------------------------
65
-
66
- front_object = Greeter
26
+ __Brutal__ development process does not prevent from bugs.
67
27
 
68
- # ------------------------------------------------------------------------------
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.
69
31
 
70
- actual = front_object.new('world')
71
-
72
- - raise unless actual.salute == "Hello world!"
73
- + raise unless actual.salute == "Hello World!"
74
- ```
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_.
75
35
 
76
36
  ## Installation
77
37
 
78
38
  Add this line to your application's Gemfile:
79
39
 
80
40
  ```ruby
81
- gem 'brutal'
41
+ gem "brutal"
82
42
  ```
83
43
 
84
44
  And then execute:
@@ -95,105 +55,77 @@ Just type `brutal` in a Ruby project's folder and watch the magic happen.
95
55
 
96
56
  ## Usage
97
57
 
98
- __Brutal__'s configuration file is `.brutal.yml`, which acts like a meta-spec.
99
- This YAML file can contains the following keys:
100
-
101
- * `header` (optional): Some code to execute before the test suite.
102
- * `front_object` (required): The front object of the test suite.
103
- * `subject` (required): The object of each context.
104
- * `variables` (required): A hash to generate the subject of each context.
105
- * `actual_values` (required): A list of tests to challenge the subject.
106
-
107
- ### 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:
108
60
 
109
- 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.
110
65
 
111
- > brutal rspec
66
+ ### Getting started
112
67
 
113
- ## Example
114
-
115
- 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:
116
70
 
117
71
  ```yaml
118
72
  ---
119
- front_object: |
120
- "Hello "
121
-
122
73
  subject: |
123
- %{front_object} + %{string}
74
+ "Hello " + "%{string}"
124
75
 
125
- variables:
76
+ contexts:
126
77
  string:
127
- - "'Alice'"
128
- - "'Bob'"
78
+ - Alice
79
+ - Bob
129
80
 
130
- actual_values:
81
+ actuals:
131
82
  - "%{subject}.to_s"
132
83
  - "%{subject}.length"
133
84
  ```
134
85
 
135
- 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.
136
87
 
137
- ```ruby
138
- front_object = "Hello "
139
-
140
- # ------------------------------------------------------------------------------
88
+ 3. Read the generated `test.rb` file in the same directory:
141
89
 
142
- actual = front_object + 'Alice'
143
- raise unless actual.to_s == "Hello Alice"
144
- raise unless actual.length == 11
90
+ ```ruby
91
+ # Brutal test suite
145
92
 
146
93
  # ------------------------------------------------------------------------------
147
94
 
148
- actual = front_object + 'Bob'
149
- raise unless actual.to_s == "Hello Bob"
150
- raise unless actual.length == 9
151
- ```
152
-
153
- And the `brutal rspec` command would generate and write in to a `test_spec.rb` file the following spec:
154
-
155
- ```ruby
156
- RSpec.describe do
157
- let(:front_object) { "Hello " }
158
-
159
- context do
160
- let(:actual) { front_object + 'Alice' }
95
+ actual = begin
96
+ "Hello " + "Alice"
97
+ end
161
98
 
162
- it { expect(actual.to_s).to eq("Hello Alice") }
163
- it { expect(actual.length).to eq(11) }
164
- end
99
+ raise if actual.to_s != "Hello Alice"
100
+ raise if actual.length != 11
165
101
 
166
- context do
167
- let(:actual) { front_object + 'Bob' }
102
+ # ------------------------------------------------------------------------------
168
103
 
169
- it { expect(actual.to_s).to eq("Hello Bob") }
170
- it { expect(actual.length).to eq(9) }
171
- end
104
+ actual = begin
105
+ "Hello " + "Bob"
172
106
  end
107
+
108
+ raise if actual.to_s != "Hello Bob"
109
+ raise if actual.length != 9
173
110
  ```
174
111
 
175
- More examples are available [here](https://github.com/fixrb/brutal/raw/master/examples/).
112
+ ### More examples
113
+
114
+ https://github.com/fixrb/brutal/raw/main/examples/
176
115
 
177
116
  ## Rake integration example
178
117
 
179
- The generated brutal test suite `test.rb` file can be declared as follows:
118
+ A generated `test.rb` file could be matched as follows:
180
119
 
181
120
  ```ruby
182
121
  Rake::TestTask.new do |t|
183
- t.pattern = 'test.rb'
122
+ t.pattern = "test.rb"
184
123
  end
185
124
  ```
186
125
 
187
126
  ## Contact
188
127
 
189
- * Home page: https://github.com/fixrb/brutal
190
- * Bugs/issues: https://github.com/fixrb/brutal/issues
191
-
192
- ## Rubies
193
-
194
- * [MRI](https://www.ruby-lang.org/)
195
- * [Rubinius](https://rubinius.com/)
196
- * [JRuby](https://www.jruby.org/)
128
+ * Source code: https://github.com/fixrb/brutal
197
129
 
198
130
  ## Versioning
199
131
 
@@ -201,17 +133,18 @@ __Brutal__ follows [Semantic Versioning 2.0](https://semver.org/).
201
133
 
202
134
  ## License
203
135
 
204
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
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).
205
137
 
206
138
  ***
207
139
 
208
140
  <p>
209
141
  This project is sponsored by:<br />
210
142
  <a href="https://sashite.com/"><img
211
- src="https://github.com/fixrb/brutal/raw/master/img/sashite.png"
143
+ src="https://github.com/fixrb/brutal/raw/main/img/sashite.png"
212
144
  alt="Sashite" /></a>
213
145
  </p>
214
146
 
147
+ [workflow_rubocop]: https://github.com/fixrb/brutal/actions?query=workflow%3ARuboCop
215
148
  [gem]: https://rubygems.org/gems/brutal
216
149
  [travis]: https://travis-ci.org/fixrb/brutal
217
150
  [inchpages]: https://inch-ci.org/github/fixrb/brutal
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!
data/lib/brutal.rb CHANGED
@@ -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.sort
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.4.0
4
+ version: 1.2.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: 2019-12-28 00:00:00.000000000 Z
11
+ date: 2021-05-12 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'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rubocop
42
+ name: rubocop-md
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -66,34 +66,62 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-thread_safety
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: simplecov
71
99
  requirement: !ruby/object:Gem::Requirement
72
100
  requirements:
73
- - - "~>"
101
+ - - ">="
74
102
  - !ruby/object:Gem::Version
75
- version: '0.17'
103
+ version: '0'
76
104
  type: :development
77
105
  prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
- - - "~>"
108
+ - - ">="
81
109
  - !ruby/object:Gem::Version
82
- version: '0.17'
110
+ version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: yard
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
- - - "~>"
115
+ - - ">="
88
116
  - !ruby/object:Gem::Version
89
- version: '0.9'
117
+ version: '0'
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
- - - "~>"
122
+ - - ">="
95
123
  - !ruby/object:Gem::Version
96
- version: '0.9'
124
+ version: '0'
97
125
  description: A code-first approach to automate the writing of unit tests.
98
126
  email: contact@cyril.email
99
127
  executables:
@@ -105,9 +133,11 @@ files:
105
133
  - README.md
106
134
  - bin/brutal
107
135
  - lib/brutal.rb
108
- - lib/brutal/scaffold_generator/base.rb
109
- - lib/brutal/scaffold_generator/por.rb
110
- - lib/brutal/scaffold_generator/rspec.rb
136
+ - lib/brutal/configuration.rb
137
+ - lib/brutal/file/read.rb
138
+ - lib/brutal/file/write.rb
139
+ - lib/brutal/scaffold.rb
140
+ - lib/brutal/yaml.rb
111
141
  homepage: https://github.com/fixrb/brutal
112
142
  licenses:
113
143
  - MIT
@@ -120,14 +150,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
150
  requirements:
121
151
  - - ">="
122
152
  - !ruby/object:Gem::Version
123
- version: '0'
153
+ version: 2.7.0
124
154
  required_rubygems_version: !ruby/object:Gem::Requirement
125
155
  requirements:
126
156
  - - ">="
127
157
  - !ruby/object:Gem::Version
128
158
  version: '0'
129
159
  requirements: []
130
- rubygems_version: 3.1.2
160
+ rubygems_version: 3.1.4
131
161
  signing_key:
132
162
  specification_version: 4
133
163
  summary: A code-first approach to automate the writing of unit tests.
@@ -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.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 = "actual = #{actual_str}\n\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
@@ -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