rspec-doc 0.1.1

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a57e71c3a1595c2d603e7d846e5f6e1e06e4595f
4
+ data.tar.gz: 36c009ccd18465d73e1ffa3036f6db8a732abb10
5
+ SHA512:
6
+ metadata.gz: 01476cee7c922208940fdbcea7ee7532a9f7ba8734d3ba0f49d08d22dd8a658d0a6aefec1a36f03c22f5d4db117788c73d9d9c90998902c009ba2ec404c124a2
7
+ data.tar.gz: 360baa78dbd0963c4aff3db91abcd31f37169eeb2e5a417dc481e3f57aa4d79933770e4d78bf7cd7c6b145faaea02a61563b77c5286669a4ce09bd43b4b06d33
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rspec-doc'
4
+
5
+ USAGE = 'usage: rspecdoc build_models'
6
+
7
+ if not File.exist?('database_config.yml')
8
+ puts 'need database_config.yml'
9
+ exit
10
+ end
11
+
12
+ if ARGV.size < 1 || ARGV[1] != 'build_models'
13
+ puts USAGE
14
+ exit
15
+ end
16
+
17
+ RSpecDoc::ModelBuilder.build_models
18
+
@@ -0,0 +1,8 @@
1
+ class RSpecDoc
2
+ end
3
+
4
+ require 'rspec-doc/model_builder'
5
+ require 'rspec-doc/markdown_formatter'
6
+ require 'rspec-doc/markdown_util'
7
+ require 'rspec-doc/rest_client'
8
+ require 'rspec-doc/active_record'
@@ -0,0 +1,71 @@
1
+ require 'active_record'
2
+ require_relative 'markdown_util'
3
+
4
+ module RSpecDoc::ActiveRecord
5
+ def self.describe(example, model, &block)
6
+ model_name = model.to_s
7
+ id = ''
8
+ if model.is_a?(ActiveRecord::Base)
9
+ model_name = "#{model.class.table_name}"
10
+ id = "[id=#{model.id}]"
11
+ end
12
+
13
+ db_assertion_md = make_db_assertion_md(block.source)
14
+ return unless db_assertion_md
15
+
16
+ example.metadata[:md_doc] ||= {}
17
+ example.metadata[:md_doc][:database_assertion] ||= []
18
+ example.metadata[:md_doc][:database_assertion].concat(
19
+ ["**#{model_name}**#{id}", db_assertion_md]
20
+ )
21
+
22
+ block.call
23
+ end
24
+
25
+ private
26
+ EQUAL_MATCHERS = ['eq', 'eql', 'equal', 'be']
27
+
28
+ def self.is_symbol(is)
29
+ return is ? '==' : '!='
30
+ end
31
+
32
+ def self.is_string(is)
33
+ return is ? '' : 'does not'
34
+ end
35
+
36
+ def self.matcher_name(is, matcher)
37
+ return self.is_symbol(is) if EQUAL_MATCHERS.include? matcher
38
+
39
+ "#{self.is_string(is)} #{matcher}"
40
+ end
41
+
42
+ def self.make_db_assertion_md(expectation_source)
43
+ expectation_source = expectation_source.lstrip
44
+ # get the target obj name
45
+ /^DocDB\.describe[\(\s+]example,\s+(?<model>.*)[\)\s+]do/ =~ expectation_source
46
+ return nil unless model
47
+
48
+ model.chomp!(')')
49
+ items = []
50
+ # select the target obj item
51
+ expectation_source.gsub!(model, 'model')
52
+ expectation_source.gsub!('be_', 'be ')
53
+ expectation_items = expectation_source.lines do |line|
54
+ key, matcher, value = nil
55
+ /expect\(model\.(?<key>.*)\)\.(?<is>to|not_to)[\s+\(](?<matcher>.*)[\s+\(](?<value>.*)[\,\)\s+\n]/ =~ "#{line.strip}\n"
56
+ next unless key && is && matcher && value
57
+
58
+ value = value.strip.chomp(')' * (value.count(')') - value.count('('))) # remove tailing extra ')'
59
+ begin
60
+ value = eval(value).inspect
61
+ rescue => exception
62
+ value = "`#{value}`"
63
+ end
64
+
65
+ items << [key.strip, "#{matcher_name(is == 'to', matcher.strip)}", value]
66
+ end
67
+
68
+ # build markdown table
69
+ MarkdownUtil.table(['Column', 'Assertion', 'Value'], items)
70
+ end
71
+ end
@@ -0,0 +1,165 @@
1
+ require 'rspec'
2
+
3
+ class RSpecDoc::MarkdownFormatter
4
+ RSpec::Core::Formatters.register self, :example_group_started, :example_group_finished, :example_finished,
5
+ :example_passed, :example_pending, :example_failed, :message, :dump_failures,
6
+ :dump_summary, :dump_summary, :dump_pending, :seed
7
+
8
+ # All formatters inheriting from this formatter will receive these
9
+ # notifications.
10
+ attr_accessor :example_group
11
+ attr_reader :output
12
+
13
+ # @api public
14
+ # @param output [IO] the formatter output
15
+ # @see RSpec::Core::Formatters::Protocol#initialize
16
+ def initialize(output)
17
+ @output = output || StringIO.new
18
+ @example_group = nil
19
+ @group_level = 0
20
+ end
21
+
22
+ # @api public
23
+ #
24
+ # @param notification [StartNotification]
25
+ # @see RSpec::Core::Formatters::Protocol#start
26
+ def start(notification)
27
+ start_sync_output
28
+ @example_count = notification.count
29
+ end
30
+
31
+ # @api public
32
+ #
33
+ # @param _notification [NullNotification] (Ignored)
34
+ # @see RSpec::Core::Formatters::Protocol#close
35
+ def close(_notification)
36
+ return if output.closed?
37
+
38
+ output.puts
39
+
40
+ output.flush
41
+ end
42
+
43
+ # @api public
44
+ #
45
+ # Used by the reporter to send messages to the output stream.
46
+ #
47
+ # @param notification [MessageNotification] containing message
48
+ def message(notification)
49
+ output.puts "#{space_indentation}#{notification.message}"
50
+ end
51
+
52
+ # @api public
53
+ #
54
+ # Dumps detailed information about each example failure.
55
+ #
56
+ # @param notification [NullNotification]
57
+ def dump_failures(notification)
58
+ return if notification.failure_notifications.empty?
59
+ output.puts notification.fully_formatted_failed_examples
60
+ end
61
+
62
+ # @api public
63
+ #
64
+ # This method is invoked after the dumping of examples and failures.
65
+ # Each parameter is assigned to a corresponding attribute.
66
+ #
67
+ # @param summary [SummaryNotification] containing duration,
68
+ # example_count, failure_count and pending_count
69
+ def dump_summary(summary)
70
+ output.puts summary.fully_formatted
71
+ end
72
+
73
+ # @private
74
+ def dump_pending(notification)
75
+ return if notification.pending_examples.empty?
76
+ output.puts notification.fully_formatted_pending_examples
77
+ end
78
+
79
+ # @private
80
+ def seed(notification)
81
+ return unless notification.seed_used?
82
+ output.puts notification.fully_formatted
83
+ end
84
+
85
+ def example_group_started(notification)
86
+ output.puts if @group_level == 0
87
+ output.puts "#{current_indentation}#{notification.group.description.strip}"
88
+ @group_level += 1
89
+ end
90
+
91
+ def example_group_finished(_notification)
92
+ @group_level -= 1 if @group_level > 0
93
+ end
94
+
95
+ def example_finished(notification)
96
+ end
97
+
98
+ def output_doc(example)
99
+ return unless example.metadata[:md_doc].is_a? Hash
100
+ output.puts
101
+ example.metadata[:md_doc].each do |key, items|
102
+ output.puts " #{space_indentation}#### #{key.to_s.split('_').map(&:capitalize).join(' ')}"
103
+ items.each do |item|
104
+ output.puts
105
+ item.each_line { |line| output.puts " #{space_indentation}#{line}" }
106
+ end
107
+ end
108
+ example.metadata[:md_doc] = {}
109
+ end
110
+
111
+ def example_passed(passed)
112
+ output.puts passed_output(passed.example)
113
+ output_doc(passed.example)
114
+ end
115
+
116
+ def example_pending(pending)
117
+ output.puts pending_output(pending.example,
118
+ pending.example.execution_result.pending_message)
119
+ output_doc(pending.example)
120
+ end
121
+
122
+ def example_failed(failure)
123
+ output.puts failure_output(failure.example)
124
+ output_doc(failure.example)
125
+ end
126
+
127
+ private
128
+
129
+ def start_sync_output
130
+ @old_sync, output.sync = output.sync, true if output_supports_sync
131
+ end
132
+
133
+ def output_supports_sync
134
+ output.respond_to?(:sync=)
135
+ end
136
+
137
+ def passed_output(example)
138
+ "#{current_indentation}#{example.description.strip}"
139
+ end
140
+
141
+ def pending_output(example, message)
142
+ "#{current_indentation}#{example.description.strip} \n(PENDING: #{message})"
143
+ end
144
+
145
+ def failure_output(example)
146
+ "#{current_indentation}#{example.description.strip} \n(FAILED - #{next_failure_index})"
147
+ end
148
+
149
+ def next_failure_index
150
+ @next_failure_index ||= 0
151
+ @next_failure_index += 1
152
+ end
153
+
154
+ def current_indentation
155
+ return "\n## " if @group_level == 0
156
+ return "\n### " if @group_level == 1
157
+
158
+ space_indentation << '- '
159
+ end
160
+
161
+ def space_indentation
162
+ return '' if @group_level < 1
163
+ ' ' * (@group_level - 1)
164
+ end
165
+ end
@@ -0,0 +1,50 @@
1
+ require 'json'
2
+
3
+ module RSpecDoc::MarkdownUtil
4
+ def self.table(header, body)
5
+ return '' unless header.is_a?(Enumerable) and body.is_a?(Enumerable)
6
+
7
+ header_line = "| #{header.join(' | ')} |"
8
+ direction_line = "| #{':--- |' * header.size}"
9
+ [header_line, direction_line]
10
+ .concat(body.map { |item| "| #{item.join(' | ')} |" })
11
+ .join("\n")
12
+ end
13
+
14
+ def self.kv_table(kv, params = {})
15
+ return '' unless kv
16
+
17
+ kv = JSON.parse(kv) if kv.is_a? String
18
+ items = []
19
+ kv.each do |key, value|
20
+ next unless value
21
+
22
+ value = value.map { |k, v| "#{k}: #{v}" }.join("\n") if value.is_a? Hash
23
+ value = value.join("\n") if value.is_a? Enumerable
24
+ key = params[:key_formatter].call(key) if params[:key_formatter].is_a? Proc
25
+ items << [key, value]
26
+ end
27
+
28
+ return '' if items.size == 0
29
+
30
+ self.table(['Key', 'Value'], items)
31
+ end
32
+
33
+ def self.http_headers_table(kv)
34
+ formatter = -> (k) { k.to_s.split('_').map(&:capitalize).join('-') }
35
+ self.kv_table(kv, {key_formatter: formatter})
36
+ end
37
+
38
+ def self.code_block(type, body)
39
+ [
40
+ "```#{type.to_s}",
41
+ body,
42
+ '```'
43
+ ].join("\n")
44
+ end
45
+
46
+ def self.json_block(h)
47
+ h = JSON.parse(h) if h.is_a? String
48
+ self.code_block(:json, JSON.pretty_generate(h))
49
+ end
50
+ end
@@ -0,0 +1,49 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+ require 'erb'
4
+
5
+ require 'rare_map'
6
+ require 'active_record'
7
+
8
+ module RareMap
9
+ # RareMap::SchemaParser parses schema.rb into Table.
10
+ module SchemaParser
11
+ # Parses schema.rb into an Array of Table.
12
+ #
13
+ # @return [Array] an Array of Table
14
+ def parse_schema(schema)
15
+ tables = []
16
+
17
+ schema.split(/\n/).each do |line|
18
+ case line.strip!
19
+ when /^create_table/
20
+ name = line.match(/create_table\s+['"]([^'"]+)['"]/)[1]
21
+ id = line.match(/(:id\s*=>|id:)\s*false/) ? false : true
22
+ pk = line.match(/(:primary_key\s*=>|primary_key:)\s*['"](.+)['"]/)
23
+ primary_key = pk[2] if pk
24
+ tables << RareMap::Table.new(name, :id => id, :primary_key => primary_key)
25
+ when /^t\.index/
26
+ unique_column_math = line.match(/t\.index\s+.*\[\s*['"]([^'"]+)['"]\s*\].*(:unique\s*=>|unique:)\s*true/)
27
+ next if !unique_column_math || unique_column_math.size < 2
28
+ unique_column = unique_column_math[1]
29
+ column = tables.last.columns.find { |col| col.name == unique_column }
30
+ column.unique = true
31
+ when /^t\./
32
+ name = line.match(/t\.\w+\s+['"]([^'"]+)['"]/)[1]
33
+ type = line.match(/t\.(\w+)\s+/)[1]
34
+ tables.last.columns << RareMap::Column.new(name, type)
35
+ end
36
+ end
37
+
38
+ tables
39
+ end
40
+ end
41
+ end
42
+
43
+ module RSpecDoc::ModelBuilder
44
+ def self.build_models
45
+ config = ERB.new(File.read('database_config.yml')).result
46
+ File.open('rare_map.yml', 'w') { |file| file.write(config) }
47
+ RareMap.mapping
48
+ end
49
+ end
@@ -0,0 +1,62 @@
1
+ require 'active_record'
2
+ require 'rest-client'
3
+ require_relative 'markdown_util'
4
+
5
+ module RSpecDoc::RestClient
6
+ def self.get(example, url, headers={})
7
+ example.metadata[:md_doc] ||= {}
8
+ resp = RestClient.get(url, headers)
9
+ example.metadata[:md_doc][:api_request] = self.make_api_request_md({
10
+ method: :get,
11
+ url: url,
12
+ headers: headers,
13
+ response: resp
14
+ })
15
+ resp
16
+ end
17
+
18
+ def self.post(example, url, payload, headers={})
19
+ example.metadata[:md_doc] ||= {}
20
+ resp = RestClient.post(url, payload, headers)
21
+ example.metadata[:md_doc][:api_request] = self.make_api_request_md({
22
+ method: :post,
23
+ url: url,
24
+ payload: payload,
25
+ headers: headers,
26
+ response: resp
27
+ })
28
+ resp
29
+ end
30
+
31
+ private
32
+ def self.make_api_request_md(params = {})
33
+ lines = []
34
+ lines << "#{params[:method].to_s.upcase} #{params[:url]}"
35
+
36
+ if params[:headers][:params]
37
+ lines << "**Query String Parameters**"
38
+ lines << MarkdownUtil.kv_table(params[:headers][:params])
39
+ end
40
+
41
+ params[:headers].delete(:params)
42
+ if params[:headers].size > 0
43
+ lines << "**Request Headers**"
44
+ lines << MarkdownUtil.http_headers_table(params[:headers])
45
+ end
46
+
47
+ if params[:payload]
48
+ lines << "**Request Body**"
49
+ lines << MarkdownUtil.json_block(params[:payload])
50
+ end
51
+
52
+ lines << "**Response Headers**"
53
+ lines << MarkdownUtil.http_headers_table(params[:response].headers)
54
+
55
+ if params[:response].headers[:content_type].to_s.include? 'json'
56
+ lines << "**Response Body**"
57
+ lines << MarkdownUtil.json_block(params[:response].body)
58
+ end
59
+
60
+ lines
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-doc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Focinfi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.1.4
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 5.1.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 5.1.4
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.1.4
33
+ - !ruby/object:Gem::Dependency
34
+ name: rare_map
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '='
38
+ - !ruby/object:Gem::Version
39
+ version: 2.2.1
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '='
45
+ - !ruby/object:Gem::Version
46
+ version: 2.2.1
47
+ - !ruby/object:Gem::Dependency
48
+ name: rest-client
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 2.0.2
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 2.0.2
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 2.0.2
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.0.2
67
+ - !ruby/object:Gem::Dependency
68
+ name: method_source
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 0.9.0
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 0.9.0
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 0.9.0
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 0.9.0
87
+ - !ruby/object:Gem::Dependency
88
+ name: rspec
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: 3.6.0
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 3.6.0
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 3.6.0
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 3.6.0
107
+ description: Make markdown for rspec
108
+ email: focinfi@gmail.com
109
+ executables: []
110
+ extensions: []
111
+ extra_rdoc_files: []
112
+ files:
113
+ - Rakefile
114
+ - bin/rspecdoc
115
+ - lib/rspec-doc.rb
116
+ - lib/rspec-doc/active_record.rb
117
+ - lib/rspec-doc/markdown_formatter.rb
118
+ - lib/rspec-doc/markdown_util.rb
119
+ - lib/rspec-doc/model_builder.rb
120
+ - lib/rspec-doc/rest_client.rb
121
+ homepage: https://github.com/Focinfi/rspec-doc
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.5.1
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: rspec-doc
145
+ test_files: []