rspec-usecases 0.0.12
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 +7 -0
- data/.github/workflows/main.yml +31 -0
- data/.gitignore +47 -0
- data/.rspec +3 -0
- data/.rubocop.yml +80 -0
- data/.rubocop_todo.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +34 -0
- data/Guardfile +30 -0
- data/LICENSE.txt +21 -0
- data/README.md +69 -0
- data/Rakefile +17 -0
- data/STORIES.md +56 -0
- data/USAGE.md +19 -0
- data/bin/console +16 -0
- data/bin/k +36 -0
- data/bin/kgitsync +76 -0
- data/bin/khotfix +244 -0
- data/bin/setup +11 -0
- data/hooks/pre-commit +87 -0
- data/hooks/update-version +33 -0
- data/lib/rspec/usecases.rb +16 -0
- data/lib/rspec/usecases/content.rb +155 -0
- data/lib/rspec/usecases/content_code.rb +42 -0
- data/lib/rspec/usecases/content_outcome.rb +30 -0
- data/lib/rspec/usecases/usecase.rb +103 -0
- data/lib/rspec/usecases/version.rb +7 -0
- data/rspec-usecases.gemspec +42 -0
- metadata +75 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# NOTE: you may need change file permissions
|
5
|
+
# chmod +x hooks/update-version
|
6
|
+
|
7
|
+
exit 1 if ARGV.empty?
|
8
|
+
|
9
|
+
version = ARGV[0]
|
10
|
+
version = version[1..-1] # revoke 'v' character, e.g. v0.1.1 becomes 0.1.1
|
11
|
+
|
12
|
+
namespaces = %w[Rspec Usecases]
|
13
|
+
|
14
|
+
indent = 0
|
15
|
+
output = ['# frozen_string_literal: true', '']
|
16
|
+
|
17
|
+
namespaces.each do |namespace|
|
18
|
+
output.push "#{' ' * indent}module #{namespace}"
|
19
|
+
indent += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
output.push "#{' ' * indent}VERSION = \'#{version}'"
|
23
|
+
indent -= 1
|
24
|
+
|
25
|
+
namespaces.each do
|
26
|
+
output.push "#{' ' * indent}end"
|
27
|
+
indent -= 1
|
28
|
+
end
|
29
|
+
|
30
|
+
output.push('')
|
31
|
+
|
32
|
+
printf "%-25<label>s : %<version>s\n", label: 'GEM VERSION', version: version
|
33
|
+
File.open('lib/rspec/usecases/version.rb', 'w+') { |f| f.write(output.join("\n")) }
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/usecases/version'
|
4
|
+
require 'rspec/usecases/usecase'
|
5
|
+
require 'rspec/usecases/content'
|
6
|
+
require 'rspec/usecases/content_code'
|
7
|
+
require 'rspec/usecases/content_outcome'
|
8
|
+
|
9
|
+
module Rspec
|
10
|
+
module Usecases
|
11
|
+
# raise Rspec::Usecases::Error, 'Sample message'
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
# Your code goes here...
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'rspec/usecases/content'
|
4
|
+
|
5
|
+
module Rspec
|
6
|
+
module Usecases
|
7
|
+
# Content
|
8
|
+
class Content
|
9
|
+
EXTRACT_CONTENT_REX = /
|
10
|
+
(?<bos>^) # beginning of string
|
11
|
+
(?<indent>\s*) # find the indent before the method
|
12
|
+
(?<method_type>outcome|code|ruby)\s # grab the method name from predefined list
|
13
|
+
(?<method_signature>.*?) # grab the method signature which is every thing up to the first do
|
14
|
+
(?<method_open>do) # code comes after the first do
|
15
|
+
(?<content>.*) # content is what we want
|
16
|
+
(?<method_closure>end)\z # the end keyword at the end of string is where the content finishes
|
17
|
+
/xm.freeze
|
18
|
+
|
19
|
+
# title
|
20
|
+
attr_accessor :title
|
21
|
+
|
22
|
+
# :type
|
23
|
+
attr_accessor :type
|
24
|
+
|
25
|
+
# metadata
|
26
|
+
attr_accessor :metadata
|
27
|
+
|
28
|
+
# source
|
29
|
+
attr_accessor :source
|
30
|
+
|
31
|
+
# is_hr
|
32
|
+
attr_accessor :is_hr
|
33
|
+
|
34
|
+
def self.parse(example)
|
35
|
+
return nil if example.description.nil? || example.description.strip.length.zero?
|
36
|
+
return nil if example.metadata[:content_type].nil?
|
37
|
+
|
38
|
+
result = get_instance(example)
|
39
|
+
|
40
|
+
result&.parse_block_source(example)
|
41
|
+
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
# rubocop:disable Lint/UselessAssignment, Security/Eval
|
46
|
+
def self.get_instance(example)
|
47
|
+
title = example.description
|
48
|
+
type = example.metadata[:content_type].to_s
|
49
|
+
metadata = example.metadata
|
50
|
+
|
51
|
+
klass = "Rspec::Usecases::Content#{type.capitalize}"
|
52
|
+
|
53
|
+
begin
|
54
|
+
content_object = "#{klass}.parse(title, type, metadata)"
|
55
|
+
eval(content_object)
|
56
|
+
rescue NameError
|
57
|
+
# TODO: Logging
|
58
|
+
puts "UNKNOWN CONTENT TYPE: #{metadata[:content_type]}"
|
59
|
+
nil
|
60
|
+
rescue StandardError => e
|
61
|
+
# TODO: Logging
|
62
|
+
puts e
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# rubocop:enable Lint/UselessAssignment, Security/Eval
|
67
|
+
|
68
|
+
# rubocop:disable Style/DoubleNegation
|
69
|
+
def initialize(title, type, metadata)
|
70
|
+
@title = title.start_with?('example at .') ? '' : title
|
71
|
+
@type = type
|
72
|
+
|
73
|
+
@is_hr = !!metadata[:hr]
|
74
|
+
|
75
|
+
yield self if block_given?
|
76
|
+
end
|
77
|
+
# rubocop:enable Style/DoubleNegation
|
78
|
+
|
79
|
+
# Have not written a test for this yet
|
80
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
|
81
|
+
def parse_block_source(example)
|
82
|
+
@debug = false
|
83
|
+
|
84
|
+
# Source code for rspec is living on the metadata[:block].source location
|
85
|
+
unless defined?(example.metadata) && defined?(example.metadata[:block]) && defined?(example.metadata[:block].source)
|
86
|
+
@source = ''
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
90
|
+
unless example.metadata[:source_override].nil?
|
91
|
+
@source = example.metadata[:source_override]
|
92
|
+
return
|
93
|
+
end
|
94
|
+
|
95
|
+
source = example.metadata[:block].source.strip
|
96
|
+
|
97
|
+
segments = source.match(EXTRACT_CONTENT_REX)
|
98
|
+
|
99
|
+
unless defined?(segments) && defined?(segments[:content])
|
100
|
+
@source = ''
|
101
|
+
return
|
102
|
+
end
|
103
|
+
@source = remove_wasted_indentation(segments[:content])
|
104
|
+
@source
|
105
|
+
rescue StandardError => e
|
106
|
+
puts 'Could not parse source'
|
107
|
+
puts example.metadata
|
108
|
+
puts e
|
109
|
+
end
|
110
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize
|
111
|
+
|
112
|
+
def remove_wasted_indentation(content)
|
113
|
+
lines = content.lines
|
114
|
+
|
115
|
+
whitespace = /^\s*/
|
116
|
+
|
117
|
+
# find the small whitespace sequence
|
118
|
+
# at beginning of line that is not \n or blank
|
119
|
+
# and grab the smallest value
|
120
|
+
indent = lines
|
121
|
+
.map { |l| l.match(whitespace).to_s }
|
122
|
+
.reject { |s| ["\n", ''].include?(s) }
|
123
|
+
.min_by(&:length)
|
124
|
+
|
125
|
+
# remove the smallest indentation from beginning
|
126
|
+
# of all lines, this is the wasted indentation
|
127
|
+
rex_indent = /^#{indent}/
|
128
|
+
|
129
|
+
lines.each { |l| l.gsub!(rex_indent, '') }
|
130
|
+
|
131
|
+
# convert back to a content string
|
132
|
+
lines.join.strip
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_h
|
136
|
+
{
|
137
|
+
title: title,
|
138
|
+
type: type,
|
139
|
+
source: source,
|
140
|
+
options: [
|
141
|
+
is_hr: is_hr
|
142
|
+
]
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
def debug
|
147
|
+
puts "title : #{title}"
|
148
|
+
puts "type : #{type}"
|
149
|
+
puts "metadata : #{metadata}"
|
150
|
+
puts "source : #{source}"
|
151
|
+
puts "is_hr : #{is_hr}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/usecases/content'
|
4
|
+
|
5
|
+
module Rspec
|
6
|
+
module Usecases
|
7
|
+
# Content Code
|
8
|
+
class ContentCode < Rspec::Usecases::Content
|
9
|
+
# # Source code
|
10
|
+
# attr_accessor :code
|
11
|
+
|
12
|
+
# Type of code, ruby, javascript, css etc.
|
13
|
+
attr_accessor :code_type
|
14
|
+
|
15
|
+
# # Summary
|
16
|
+
# attr_accessor :summary
|
17
|
+
|
18
|
+
def self.parse(title, type, metadata)
|
19
|
+
new(title, type, metadata) do |content|
|
20
|
+
# content.code = metadata[:code].to_s
|
21
|
+
content.code_type = metadata[:code_type].to_s
|
22
|
+
# content.summary = metadata[:summary].to_s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
{
|
28
|
+
# code: code,
|
29
|
+
code_type: code_type
|
30
|
+
# summary: summary
|
31
|
+
}.merge(super.to_h)
|
32
|
+
end
|
33
|
+
|
34
|
+
def debug
|
35
|
+
super
|
36
|
+
puts "code : #{code}"
|
37
|
+
puts "code_type : #{code_type}"
|
38
|
+
puts "summary : #{summary}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/usecases/content'
|
4
|
+
|
5
|
+
module Rspec
|
6
|
+
module Usecases
|
7
|
+
# Content Outcome
|
8
|
+
class ContentOutcome < Rspec::Usecases::Content
|
9
|
+
# Summary
|
10
|
+
attr_accessor :summary
|
11
|
+
|
12
|
+
def self.parse(title, type, metadata)
|
13
|
+
new(title, type, metadata) do |content|
|
14
|
+
content.summary = metadata[:summary].to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
{
|
20
|
+
summary: summary
|
21
|
+
}.merge(super.to_h)
|
22
|
+
end
|
23
|
+
|
24
|
+
def debug
|
25
|
+
super
|
26
|
+
puts "summary : #{summary}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'rspec/usecases/usecase'
|
4
|
+
|
5
|
+
module Rspec
|
6
|
+
module Usecases
|
7
|
+
# Usecase
|
8
|
+
class Usecase
|
9
|
+
# key
|
10
|
+
attr_reader :key
|
11
|
+
|
12
|
+
# title
|
13
|
+
attr_reader :title
|
14
|
+
|
15
|
+
# summary
|
16
|
+
attr_reader :summary
|
17
|
+
|
18
|
+
# Usage contains a sample on how to call some code
|
19
|
+
attr_reader :usage
|
20
|
+
|
21
|
+
# Usage description gives extra details to support the usage example
|
22
|
+
attr_reader :usage_description
|
23
|
+
|
24
|
+
# contents
|
25
|
+
attr_reader :contents
|
26
|
+
|
27
|
+
def initialize(key)
|
28
|
+
@key = key
|
29
|
+
@title = ''
|
30
|
+
@summary = ''
|
31
|
+
@usage = ''
|
32
|
+
@usage_description = ''
|
33
|
+
@contents = []
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.parse(key, data)
|
37
|
+
usecase = Usecase.new(key)
|
38
|
+
|
39
|
+
usecase.build_title(data)
|
40
|
+
usecase.build_attributes(data)
|
41
|
+
|
42
|
+
# Loop through the it blocks
|
43
|
+
data.examples.each do |it|
|
44
|
+
usecase.add_content(it)
|
45
|
+
end
|
46
|
+
|
47
|
+
usecase
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_h
|
51
|
+
{
|
52
|
+
key: key,
|
53
|
+
title: title,
|
54
|
+
summary: summary,
|
55
|
+
usage: usage,
|
56
|
+
usage_description: usage_description,
|
57
|
+
contents: contents.map(&:to_h)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def debug
|
62
|
+
puts "key : #{key}"
|
63
|
+
puts "title : #{title}"
|
64
|
+
puts "summary : #{summary}"
|
65
|
+
puts "usage : #{usage}"
|
66
|
+
puts "usage_description : #{usage_description}"
|
67
|
+
puts "contents : #{contents}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_content(example)
|
71
|
+
content = Rspec::Usecases::Content.parse(example)
|
72
|
+
@contents << content unless content.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_attributes(example_group)
|
76
|
+
@summary = example_group.metadata[:summary] if example_group.metadata[:summary]
|
77
|
+
|
78
|
+
@usage = example_group.metadata[:usage] if example_group.metadata[:usage]
|
79
|
+
@usage_description = example_group.metadata[:usage_description] if example_group.metadata[:usage_description]
|
80
|
+
end
|
81
|
+
|
82
|
+
# Build the title from the rspec example_group, aka describe or context
|
83
|
+
#
|
84
|
+
# If title attribute is set then this takes priority.
|
85
|
+
# If not set, then build the title by looking through the parent objects
|
86
|
+
def build_title(example_group)
|
87
|
+
return if title != ''
|
88
|
+
|
89
|
+
if example_group.metadata[:title]
|
90
|
+
@title = example_group.metadata[:title]
|
91
|
+
else
|
92
|
+
example_group.example_group.parent_groups.reverse.each do |group|
|
93
|
+
@title = if @title.length.zero?
|
94
|
+
group.description
|
95
|
+
else
|
96
|
+
"#{@title} #{group.description}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/rspec/usecases/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.required_ruby_version = '>= 2.5'
|
7
|
+
spec.name = 'rspec-usecases'
|
8
|
+
spec.version = Rspec::Usecases::VERSION
|
9
|
+
spec.authors = ['David Cruwys']
|
10
|
+
spec.email = ['david@ideasmen.com.au']
|
11
|
+
|
12
|
+
spec.summary = 'Rspec Usecases helps to write self-documenting code usage examples that execute as normal unit tests while outputting documentation in varied formats'
|
13
|
+
spec.description = <<-TEXT
|
14
|
+
Rspec Usecases helps to write self-documenting code usage examples that execute as normal unit tests while outputting documentation in varied formats
|
15
|
+
TEXT
|
16
|
+
spec.homepage = 'http://appydave.com/gems/rspec-usecases'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
|
19
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
20
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
21
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)
|
22
|
+
|
23
|
+
# spec.metadata['allowed_push_host'] = "Set to 'http://mygemserver.com'"
|
24
|
+
|
25
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
26
|
+
spec.metadata['source_code_uri'] = 'https://github.com/klueless-io/rspec-usecases'
|
27
|
+
spec.metadata['changelog_uri'] = 'https://github.com/klueless-io/rspec-usecases/commits/master'
|
28
|
+
|
29
|
+
# Specify which files should be added to the gem when it is released.
|
30
|
+
# The `git ls-files -z` loads the RubyGem files that have been added into git.
|
31
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
32
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
33
|
+
f.match(%r{^(test|spec|features)/})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
spec.bindir = 'exe'
|
37
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
38
|
+
spec.require_paths = ['lib']
|
39
|
+
# spec.extensions = ['ext/rspec_usecases/extconf.rb']
|
40
|
+
|
41
|
+
# spec.add_dependency 'tty-box', '~> 0.5.0'
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspec-usecases
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.12
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Cruwys
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-24 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: " Rspec Usecases helps to write self-documenting code usage examples
|
14
|
+
that execute as normal unit tests while outputting documentation in varied formats\n"
|
15
|
+
email:
|
16
|
+
- david@ideasmen.com.au
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".github/workflows/main.yml"
|
22
|
+
- ".gitignore"
|
23
|
+
- ".rspec"
|
24
|
+
- ".rubocop.yml"
|
25
|
+
- ".rubocop_todo.yml"
|
26
|
+
- CODE_OF_CONDUCT.md
|
27
|
+
- Gemfile
|
28
|
+
- Guardfile
|
29
|
+
- LICENSE.txt
|
30
|
+
- README.md
|
31
|
+
- Rakefile
|
32
|
+
- STORIES.md
|
33
|
+
- USAGE.md
|
34
|
+
- bin/console
|
35
|
+
- bin/k
|
36
|
+
- bin/kgitsync
|
37
|
+
- bin/khotfix
|
38
|
+
- bin/setup
|
39
|
+
- hooks/pre-commit
|
40
|
+
- hooks/update-version
|
41
|
+
- lib/rspec/usecases.rb
|
42
|
+
- lib/rspec/usecases/content.rb
|
43
|
+
- lib/rspec/usecases/content_code.rb
|
44
|
+
- lib/rspec/usecases/content_outcome.rb
|
45
|
+
- lib/rspec/usecases/usecase.rb
|
46
|
+
- lib/rspec/usecases/version.rb
|
47
|
+
- rspec-usecases.gemspec
|
48
|
+
homepage: http://appydave.com/gems/rspec-usecases
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
metadata:
|
52
|
+
homepage_uri: http://appydave.com/gems/rspec-usecases
|
53
|
+
source_code_uri: https://github.com/klueless-io/rspec-usecases
|
54
|
+
changelog_uri: https://github.com/klueless-io/rspec-usecases/commits/master
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '2.5'
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubygems_version: 3.2.5
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: Rspec Usecases helps to write self-documenting code usage examples that execute
|
74
|
+
as normal unit tests while outputting documentation in varied formats
|
75
|
+
test_files: []
|