rspec-doc 0.1.1

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