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.
@@ -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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rspec
4
+ module Usecases
5
+ VERSION = '0.0.12'
6
+ end
7
+ 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: []