brutal 1.2.1 → 1.5.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: 958cf59b6aea2f5e323b849c83a86399cd9ad99e2433b899160a2422275a319e
4
- data.tar.gz: bdb36f749b6bfcb87683f12d3db9e74c716bdbfbf41e21c845864bf1e17f7a7e
3
+ metadata.gz: ea124053ebeef681c2e046022936f9d67ca97778733a23bd2d81781e90daa9a8
4
+ data.tar.gz: 677089454fece7f60aedfc4693b53c132f057bbeeaca5e651f88ec52d026ea34
5
5
  SHA512:
6
- metadata.gz: 6f4b098047917136946c7bb88b111b5e6facf2a13ab70677bf0bce480bc2b4f0931f89f0be8ad2dc6db152abaaeffbdece5993d7ac5fba2ca85fde601df0c4c2
7
- data.tar.gz: 7d5d125189afacf46a008a6b12c79ec68ffeb6e15c4324c1a6924a19be9061dac5760224225a564a8a1d9410b18a3da80591beeb30e5abc15995a5094326fab4
6
+ metadata.gz: b1d043d4740db1987c188755e9b339da566034a3cc2a43f99ced12fb3de26a93df0f93d669b20700ba662a935d9be662a25bffba8f8ae92aae3e01deade92d9b
7
+ data.tar.gz: 5abc20c46849f69b20de5b89848f8fed831b2d92641f57cfa349be4e0a975c8630c27268d16a022911693d403af8262b4f0d1099dcf468879392e9c2d7fe7ec0
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- The MIT License (MIT)
1
+ # The MIT License
2
2
 
3
3
  Copyright (c) 2020-2022 Cyril Kato
4
4
 
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Brutal 💎🔨
2
2
 
3
- [![Version](https://img.shields.io/github/v/tag/fixrb/brutal?label=Version&logo=github)](https://github.com/fixrb/brutal/releases)
3
+ [![Version](https://img.shields.io/github/v/tag/fixrb/brutal?label=Version&logo=github)](https://github.com/fixrb/brutal/tags)
4
4
  [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/fixrb/brutal/main)
5
- [![CI](https://github.com/fixrb/brutal/workflows/CI/badge.svg?branch=main)](https://github.com/fixrb/brutal/actions?query=workflow%3Aci+branch%3Amain)
5
+ [![Ruby](https://github.com/fixrb/brutal/workflows/Ruby/badge.svg?branch=main)](https://github.com/fixrb/brutal/actions?query=workflow%3Aruby+branch%3Amain)
6
6
  [![RuboCop](https://github.com/fixrb/brutal/workflows/RuboCop/badge.svg?branch=main)](https://github.com/fixrb/brutal/actions?query=workflow%3Arubocop+branch%3Amain)
7
7
  [![License](https://img.shields.io/github/license/fixrb/brutal?label=License&logo=github)](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
- The `brutal.yml` file is a manifest you can use to define your __Brutal__ meta-spec.
63
- It has 4 top-level sections:
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
- ### Getting started
71
+ When the configuration file is present, the generation of a test suite can be done with the command:
71
72
 
72
- 1. Create a `brutal.yml` file in your application's root directory.
73
- The following example `brutal.yml` defines the shape of a Hello test suite:
73
+ ```sh
74
+ brutal .brutal.yml
75
+ ```
74
76
 
75
- ```yaml
76
- ---
77
- subject: |
78
- "Hello " + "%{string}"
77
+ or:
78
+
79
+ ```sh
80
+ brutal .
81
+ ```
79
82
 
80
- contexts:
81
- string:
82
- - Alice
83
- - Bob
83
+ or even:
84
84
 
85
- actuals:
86
- - "%{subject}.to_s"
87
- - "%{subject}.length"
85
+ ```sh
86
+ brutal
88
87
  ```
89
88
 
90
- 2. Run the `brutal` command from the same directory.
89
+ This would create a `test.rb` file containing the test suite.
91
90
 
92
- 3. Read the generated `test.rb` file in the same directory:
91
+ Configuration files can also be named differently:
93
92
 
94
- ```ruby
95
- # Brutal test suite
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
- actual = begin
100
- "Hello " + "Alice"
101
- end
99
+ To avoid accidentally overwriting a file, the `--no-force` option can be used:
102
100
 
103
- raise if actual.to_s != "Hello Alice"
104
- raise if actual.length != 11
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
- actual = begin
109
- "Hello " + "Bob"
110
- end
107
+ ### Getting started
111
108
 
112
- raise if actual.to_s != "Hello Bob"
113
- raise if actual.length != 9
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/raw/main/examples/
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
- <p>
149
- This project is sponsored by:<br />
150
- <a href="https://sashite.com/"><img
151
- src="https://github.com/fixrb/brutal/raw/main/img/sashite.png"
152
- alt="Sashite" /></a>
153
- </p>
145
+ This project is sponsored by [Sashité](https://github.com/sashite/):
146
+
147
+ ![Sashité logo](https://github.com/fixrb/brutal/raw/main/img/sponsor/dark/en/sashite.png#gh-dark-mode-only "Sashité")
148
+ ![Sashité logo](https://github.com/fixrb/brutal/raw/main/img/sponsor/light/en/sashite.png#gh-light-mode-only "Sashité")
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.generate!
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)
@@ -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 = 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("File #{path} not found!")
18
+ abort "File #{path} not found!"
24
19
  end
25
20
 
26
21
  protected
@@ -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 = NAME)
11
+ def initialize(name)
17
12
  @name = name
18
13
  end
19
14
 
@@ -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
@@ -42,20 +42,35 @@ module Brutal
42
42
  #
43
43
  # @return [String]
44
44
  def to_s
45
- "#{header.chomp}\n#{blank_line}" + combinations_values.map do |values|
46
- attributes = context_names.each_with_index.inject({}) do |h, (name, i)|
47
- h.merge(name.to_sym => inspect(values.fetch(i)))
48
- end
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
- actual_str = format(inspect(subject), **attributes)
58
+ def contexts_values
59
+ context_names.map { |context_name| contexts.fetch(context_name) }
60
+ end
51
61
 
52
- string = <<~CODE
53
- actual = begin
54
- #{actual_str.gsub(/^/, " ")}
55
- end
62
+ def combinations_values
63
+ Array(contexts_values[0]).product(*Array(contexts_values[1..]))
64
+ end
56
65
 
57
- CODE
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.join(blank_line)
82
+ end
68
83
  end
69
84
 
70
- def blank_line
71
- "\n" \
72
- "# #{"-" * 78}\n" \
73
- "\n"
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 context_names
77
- contexts.keys.sort
94
+ def header_ruby_code
95
+ <<~RUBY_CODE
96
+ #{header.chomp}
97
+ RUBY_CODE
78
98
  end
79
99
 
80
- def contexts_values
81
- context_names.map { |context_name| contexts.fetch(context_name) }
100
+ def separator_ruby_code
101
+ <<~RUBY_CODE
102
+
103
+ #{thematic_break_ruby_code}
104
+ RUBY_CODE
82
105
  end
83
106
 
84
- def combinations_values
85
- Array(contexts_values[0]).product(*Array(contexts_values[1..]))
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/read
6
- file/write
5
+ file
7
6
  scaffold
8
7
  yaml
9
- ].each { |file_name| require_relative(File.join("brutal", file_name)) }
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
- yaml = File::Read.new.call
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
- File::Write.new.call(ruby)
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.2.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-04-03 00:00:00.000000000 Z
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