rspec-usecases 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|