rspec-usecases 0.0.12

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