danger 8.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +94 -0
  4. data/bin/danger +5 -0
  5. data/lib/assets/DangerfileTemplate +13 -0
  6. data/lib/danger.rb +44 -0
  7. data/lib/danger/ci_source/appcenter.rb +55 -0
  8. data/lib/danger/ci_source/appveyor.rb +60 -0
  9. data/lib/danger/ci_source/azure_pipelines.rb +44 -0
  10. data/lib/danger/ci_source/bamboo.rb +41 -0
  11. data/lib/danger/ci_source/bitbucket_pipelines.rb +37 -0
  12. data/lib/danger/ci_source/bitrise.rb +65 -0
  13. data/lib/danger/ci_source/buddybuild.rb +62 -0
  14. data/lib/danger/ci_source/buildkite.rb +51 -0
  15. data/lib/danger/ci_source/ci_source.rb +37 -0
  16. data/lib/danger/ci_source/circle.rb +94 -0
  17. data/lib/danger/ci_source/circle_api.rb +51 -0
  18. data/lib/danger/ci_source/cirrus.rb +31 -0
  19. data/lib/danger/ci_source/code_build.rb +57 -0
  20. data/lib/danger/ci_source/codefresh.rb +53 -0
  21. data/lib/danger/ci_source/codeship.rb +44 -0
  22. data/lib/danger/ci_source/dotci.rb +52 -0
  23. data/lib/danger/ci_source/drone.rb +71 -0
  24. data/lib/danger/ci_source/github_actions.rb +43 -0
  25. data/lib/danger/ci_source/gitlab_ci.rb +86 -0
  26. data/lib/danger/ci_source/jenkins.rb +149 -0
  27. data/lib/danger/ci_source/local_git_repo.rb +119 -0
  28. data/lib/danger/ci_source/local_only_git_repo.rb +47 -0
  29. data/lib/danger/ci_source/screwdriver.rb +47 -0
  30. data/lib/danger/ci_source/semaphore.rb +37 -0
  31. data/lib/danger/ci_source/support/commits.rb +17 -0
  32. data/lib/danger/ci_source/support/find_repo_info_from_logs.rb +35 -0
  33. data/lib/danger/ci_source/support/find_repo_info_from_url.rb +42 -0
  34. data/lib/danger/ci_source/support/local_pull_request.rb +14 -0
  35. data/lib/danger/ci_source/support/no_pull_request.rb +7 -0
  36. data/lib/danger/ci_source/support/no_repo_info.rb +5 -0
  37. data/lib/danger/ci_source/support/pull_request_finder.rb +179 -0
  38. data/lib/danger/ci_source/support/remote_pull_request.rb +15 -0
  39. data/lib/danger/ci_source/support/repo_info.rb +10 -0
  40. data/lib/danger/ci_source/surf.rb +37 -0
  41. data/lib/danger/ci_source/teamcity.rb +159 -0
  42. data/lib/danger/ci_source/travis.rb +51 -0
  43. data/lib/danger/ci_source/vsts.rb +73 -0
  44. data/lib/danger/ci_source/xcode_server.rb +48 -0
  45. data/lib/danger/clients/rubygems_client.rb +14 -0
  46. data/lib/danger/commands/dangerfile/gem.rb +43 -0
  47. data/lib/danger/commands/dangerfile/init.rb +30 -0
  48. data/lib/danger/commands/dry_run.rb +54 -0
  49. data/lib/danger/commands/init.rb +297 -0
  50. data/lib/danger/commands/init_helpers/interviewer.rb +92 -0
  51. data/lib/danger/commands/local.rb +83 -0
  52. data/lib/danger/commands/local_helpers/http_cache.rb +36 -0
  53. data/lib/danger/commands/local_helpers/local_setup.rb +46 -0
  54. data/lib/danger/commands/local_helpers/pry_setup.rb +31 -0
  55. data/lib/danger/commands/plugins/plugin_json.rb +46 -0
  56. data/lib/danger/commands/plugins/plugin_lint.rb +54 -0
  57. data/lib/danger/commands/plugins/plugin_readme.rb +45 -0
  58. data/lib/danger/commands/pr.rb +92 -0
  59. data/lib/danger/commands/runner.rb +94 -0
  60. data/lib/danger/commands/staging.rb +53 -0
  61. data/lib/danger/commands/systems.rb +43 -0
  62. data/lib/danger/comment_generators/bitbucket_server.md.erb +20 -0
  63. data/lib/danger/comment_generators/bitbucket_server_inline.md.erb +15 -0
  64. data/lib/danger/comment_generators/bitbucket_server_message_group.md.erb +12 -0
  65. data/lib/danger/comment_generators/github.md.erb +55 -0
  66. data/lib/danger/comment_generators/github_inline.md.erb +26 -0
  67. data/lib/danger/comment_generators/gitlab.md.erb +40 -0
  68. data/lib/danger/comment_generators/gitlab_inline.md.erb +26 -0
  69. data/lib/danger/comment_generators/vsts.md.erb +20 -0
  70. data/lib/danger/core_ext/file_list.rb +18 -0
  71. data/lib/danger/core_ext/string.rb +20 -0
  72. data/lib/danger/danger_core/dangerfile.rb +341 -0
  73. data/lib/danger/danger_core/dangerfile_dsl.rb +29 -0
  74. data/lib/danger/danger_core/dangerfile_generator.rb +11 -0
  75. data/lib/danger/danger_core/environment_manager.rb +123 -0
  76. data/lib/danger/danger_core/executor.rb +92 -0
  77. data/lib/danger/danger_core/message_aggregator.rb +49 -0
  78. data/lib/danger/danger_core/message_group.rb +68 -0
  79. data/lib/danger/danger_core/messages/base.rb +56 -0
  80. data/lib/danger/danger_core/messages/markdown.rb +42 -0
  81. data/lib/danger/danger_core/messages/violation.rb +54 -0
  82. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_cloud_plugin.rb +144 -0
  83. data/lib/danger/danger_core/plugins/dangerfile_bitbucket_server_plugin.rb +211 -0
  84. data/lib/danger/danger_core/plugins/dangerfile_danger_plugin.rb +248 -0
  85. data/lib/danger/danger_core/plugins/dangerfile_git_plugin.rb +158 -0
  86. data/lib/danger/danger_core/plugins/dangerfile_github_plugin.rb +254 -0
  87. data/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb +240 -0
  88. data/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb +42 -0
  89. data/lib/danger/danger_core/plugins/dangerfile_messaging_plugin.rb +218 -0
  90. data/lib/danger/danger_core/plugins/dangerfile_vsts_plugin.rb +191 -0
  91. data/lib/danger/danger_core/standard_error.rb +143 -0
  92. data/lib/danger/helpers/array_subclass.rb +61 -0
  93. data/lib/danger/helpers/comment.rb +32 -0
  94. data/lib/danger/helpers/comments_helper.rb +178 -0
  95. data/lib/danger/helpers/comments_parsing_helper.rb +70 -0
  96. data/lib/danger/helpers/emoji_mapper.rb +41 -0
  97. data/lib/danger/helpers/find_max_num_violations.rb +31 -0
  98. data/lib/danger/helpers/message_groups_array_helper.rb +31 -0
  99. data/lib/danger/plugin_support/gems_resolver.rb +77 -0
  100. data/lib/danger/plugin_support/plugin.rb +49 -0
  101. data/lib/danger/plugin_support/plugin_file_resolver.rb +30 -0
  102. data/lib/danger/plugin_support/plugin_linter.rb +161 -0
  103. data/lib/danger/plugin_support/plugin_parser.rb +199 -0
  104. data/lib/danger/plugin_support/templates/readme_table.html.erb +26 -0
  105. data/lib/danger/request_sources/bitbucket_cloud.rb +171 -0
  106. data/lib/danger/request_sources/bitbucket_cloud_api.rb +181 -0
  107. data/lib/danger/request_sources/bitbucket_server.rb +105 -0
  108. data/lib/danger/request_sources/bitbucket_server_api.rb +117 -0
  109. data/lib/danger/request_sources/github/github.rb +530 -0
  110. data/lib/danger/request_sources/github/github_review.rb +126 -0
  111. data/lib/danger/request_sources/github/github_review_resolver.rb +19 -0
  112. data/lib/danger/request_sources/github/github_review_unsupported.rb +25 -0
  113. data/lib/danger/request_sources/gitlab.rb +525 -0
  114. data/lib/danger/request_sources/local_only.rb +53 -0
  115. data/lib/danger/request_sources/request_source.rb +85 -0
  116. data/lib/danger/request_sources/support/get_ignored_violation.rb +17 -0
  117. data/lib/danger/request_sources/vsts.rb +118 -0
  118. data/lib/danger/request_sources/vsts_api.rb +138 -0
  119. data/lib/danger/scm_source/git_repo.rb +181 -0
  120. data/lib/danger/version.rb +4 -0
  121. metadata +339 -0
@@ -0,0 +1,29 @@
1
+ module Danger
2
+ class Dangerfile
3
+ # Anything inside this module is considered public API, and in the future
4
+ # documentation will be generated from it via rdoc.
5
+
6
+ module DSL
7
+ # @!group Danger Zone
8
+ # Provides access to the raw Travis/Circle/Buildkite/GitHub objects, which
9
+ # you can use to pull out extra bits of information. _Warning_
10
+ # the interfaces of these objects is **not** considered a part of the Dangerfile public
11
+ # API, and is viable to change occasionally on the whims of developers.
12
+ # @return [EnvironmentManager]
13
+
14
+ attr_reader :env
15
+
16
+ private
17
+
18
+ def initialize
19
+ load_default_plugins
20
+ end
21
+
22
+ def load_default_plugins
23
+ Dir["./danger_plugins/*.rb"].each do |file|
24
+ require File.expand_path(file)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module Danger
2
+ class DangerfileGenerator
3
+ # returns the string for a Dangerfile based on a folder's contents'
4
+ def self.create_dangerfile(_path, _ui)
5
+ # Use this template for now, but this is a really ripe place to
6
+ # improve now!
7
+ dir = Danger.gem_path
8
+ File.read(File.join(dir, "lib", "assets", "DangerfileTemplate"))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,123 @@
1
+ require "danger/ci_source/ci_source"
2
+ require "danger/request_sources/request_source"
3
+
4
+ module Danger
5
+ class EnvironmentManager
6
+ attr_accessor :ci_source, :request_source, :scm, :ui, :danger_id
7
+
8
+ # Finds a Danger::CI class based on the ENV
9
+ def self.local_ci_source(env)
10
+ CI.available_ci_sources.find { |ci| ci.validates_as_ci? env }
11
+ end
12
+
13
+ # Uses the current Danger::CI subclass, and sees if it is a PR
14
+ def self.pr?(env)
15
+ local_ci_source(env).validates_as_pr?(env)
16
+ end
17
+
18
+ # @return [String] danger's default head branch
19
+ def self.danger_head_branch
20
+ "danger_head".freeze
21
+ end
22
+
23
+ # @return [String] danger's default base branch
24
+ def self.danger_base_branch
25
+ "danger_base".freeze
26
+ end
27
+
28
+ def initialize(env, ui = nil, danger_id = "danger")
29
+ ci_klass = self.class.local_ci_source(env)
30
+ self.ci_source = ci_klass.new(env)
31
+ self.ui = ui || Cork::Board.new(silent: false, verbose: false)
32
+ self.danger_id = danger_id
33
+
34
+ RequestSources::RequestSource.available_request_sources.each do |klass|
35
+ next unless self.ci_source.supports?(klass)
36
+
37
+ request_source = klass.new(self.ci_source, env)
38
+ next unless request_source.validates_as_ci?
39
+ next unless request_source.validates_as_api_source?
40
+ self.request_source = request_source
41
+ end
42
+
43
+ raise_error_for_no_request_source(env, self.ui) unless self.request_source
44
+ self.scm = self.request_source.scm
45
+ end
46
+
47
+ def pr?
48
+ self.ci_source != nil
49
+ end
50
+
51
+ def fill_environment_vars
52
+ request_source.fetch_details
53
+ end
54
+
55
+ def ensure_danger_branches_are_setup
56
+ clean_up
57
+
58
+ self.request_source.setup_danger_branches
59
+ end
60
+
61
+ def clean_up
62
+ [EnvironmentManager.danger_base_branch, EnvironmentManager.danger_head_branch].each do |branch|
63
+ scm.exec("branch -D #{branch}") unless scm.exec("rev-parse --quiet --verify #{branch}").empty?
64
+ end
65
+ end
66
+
67
+ def meta_info_for_head
68
+ scm.exec("--no-pager log #{EnvironmentManager.danger_head_branch} -n1")
69
+ end
70
+
71
+ def meta_info_for_base
72
+ scm.exec("--no-pager log #{EnvironmentManager.danger_base_branch} -n1")
73
+ end
74
+
75
+ def raise_error_for_no_request_source(env, ui)
76
+ title, subtitle = extract_title_and_subtitle_from_source(ci_source.repo_url)
77
+ subtitle += travis_note if env["TRAVIS_SECURE_ENV_VARS"] == "true"
78
+
79
+ ui_display_no_request_source_error_message(ui, env, title, subtitle)
80
+
81
+ exit(1)
82
+ end
83
+
84
+ private
85
+
86
+ def get_repo_source(repo_url)
87
+ if repo_url =~ /github/i
88
+ RequestSources::GitHub
89
+ elsif repo_url =~ /gitlab/i
90
+ RequestSources::GitLab
91
+ elsif repo_url =~ /bitbucket\.(org|com)/i
92
+ RequestSources::BitbucketCloud
93
+ end
94
+ end
95
+
96
+ def extract_title_and_subtitle_from_source(repo_url)
97
+ source = get_repo_source(repo_url)
98
+
99
+ if source
100
+ title = "For your #{source.source_name} repo, you need to expose: " + source.env_vars.join(", ").yellow
101
+ subtitle = "You may also need: #{source.optional_env_vars.join(', ')}" if source.optional_env_vars.any?
102
+ else
103
+ title = "For Danger to run on this project, you need to expose a set of following the ENV vars:\n#{RequestSources::RequestSource.available_source_names_and_envs.join("\n")}"
104
+ end
105
+
106
+ [title, (subtitle || "")]
107
+ end
108
+
109
+ def ui_display_no_request_source_error_message(ui, env, title, subtitle)
110
+ ui.title "Could not set up API to Code Review site for Danger\n".freeze
111
+ ui.puts title
112
+ ui.puts subtitle
113
+ ui.puts "\nFound these keys in your ENV: #{env.keys.join(', '.freeze)}."
114
+ ui.puts "\nFailing the build, Danger cannot run without API access.".freeze
115
+ ui.puts "You can see more information at https://danger.systems/guides/getting_started.html".freeze
116
+ end
117
+
118
+ def travis_note
119
+ "\nTravis note: If you have an open source project, you should ensure 'Display value in build log' enabled for these flags, so that PRs from forks work." \
120
+ "\nThis also means that people can see this token, so this account should have no write access to repos."
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,92 @@
1
+ module Danger
2
+ class Executor
3
+ def initialize(system_env)
4
+ @system_env = system_env
5
+ end
6
+
7
+ def run(env: nil,
8
+ dm: nil,
9
+ cork: nil,
10
+ base: nil,
11
+ head: nil,
12
+ dangerfile_path: nil,
13
+ danger_id: nil,
14
+ new_comment: nil,
15
+ fail_on_errors: nil,
16
+ fail_if_no_pr: nil,
17
+ remove_previous_comments: nil)
18
+ # Create a silent Cork instance if cork is nil, as it's likely a test
19
+ cork ||= Cork::Board.new(silent: false, verbose: false)
20
+
21
+ # Run some validations
22
+ validate!(cork, fail_if_no_pr: fail_if_no_pr)
23
+
24
+ # OK, we now know that Danger can run in this environment
25
+ env ||= EnvironmentManager.new(system_env, cork, danger_id)
26
+ dm ||= Dangerfile.new(env, cork)
27
+
28
+ ran_status = begin
29
+ dm.run(
30
+ base_branch(base),
31
+ head_branch(head),
32
+ dangerfile_path,
33
+ danger_id,
34
+ new_comment,
35
+ remove_previous_comments
36
+ )
37
+ end
38
+
39
+ # By default Danger will use the status API to fail a build,
40
+ # allowing execution to continue, this behavior isn't always
41
+ # optimal for everyone.
42
+ exit(1) if fail_on_errors && ran_status
43
+ end
44
+
45
+ def validate!(cork, fail_if_no_pr: false)
46
+ validate_ci!
47
+ validate_pr!(cork, fail_if_no_pr)
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :system_env
53
+
54
+ # Could we find a CI source at all?
55
+ def validate_ci!
56
+ unless EnvironmentManager.local_ci_source(system_env)
57
+ abort("Could not find the type of CI for Danger to run on.".red)
58
+ end
59
+ end
60
+
61
+ # Could we determine that the CI source is inside a PR?
62
+ def validate_pr!(cork, fail_if_no_pr)
63
+ unless EnvironmentManager.pr?(system_env)
64
+ ci_name = EnvironmentManager.local_ci_source(system_env).name.split("::").last
65
+
66
+ msg = "Not a #{ci_name} #{commit_request(ci_name)} - skipping `danger` run. "
67
+ # circle won't run danger properly if the commit is pushed and build runs before the PR exists
68
+ # https://danger.systems/guides/troubleshooting.html#circle-ci-doesnt-run-my-build-consistently
69
+ # the best solution is to enable `fail_if_no_pr`, and then re-run the job once the PR is up
70
+ if ci_name == "CircleCI"
71
+ msg << "If you only created the PR recently, try re-running your workflow."
72
+ end
73
+ cork.puts msg.strip.yellow
74
+
75
+ exit(fail_if_no_pr ? 1 : 0)
76
+ end
77
+ end
78
+
79
+ def base_branch(user_specified_base_branch)
80
+ user_specified_base_branch || EnvironmentManager.danger_base_branch
81
+ end
82
+
83
+ def head_branch(user_specified_head_branch)
84
+ user_specified_head_branch || EnvironmentManager.danger_head_branch
85
+ end
86
+
87
+ def commit_request(ci_name)
88
+ return "Merge Request" if ci_name == 'GitLabCI'
89
+ return "Pull Request"
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ require "danger/danger_core/message_group"
3
+ require "danger/helpers/message_groups_array_helper"
4
+
5
+ module Danger
6
+ class MessageAggregator
7
+ def self.aggregate(*args)
8
+ new(*args).aggregate
9
+ end
10
+
11
+ def initialize(warnings: [],
12
+ errors: [],
13
+ messages: [],
14
+ markdowns: [],
15
+ danger_id: "danger")
16
+ @messages = warnings + errors + messages + markdowns
17
+ @danger_id = danger_id
18
+ end
19
+
20
+ # aggregates the messages into an array of MessageGroups
21
+ # @return [[MessageGroup]]
22
+ def aggregate
23
+ # oookay I took some shortcuts with this one.
24
+ # first, sort messages by file and line
25
+ @messages.sort! { |a, b| a.compare_by_file_and_line(b) }
26
+
27
+ # now create an initial empty message group
28
+ first_group = MessageGroup.new(file: nil,
29
+ line: nil)
30
+ @message_groups = @messages.reduce([first_group]) do |groups, msg|
31
+ # We get to take a shortcut because we sorted the messages earlier - only
32
+ # have to see if we can append msg to the last group in the list
33
+ if groups.last << msg
34
+ # we appended it, so return groups unchanged
35
+ groups
36
+ else
37
+ # have to create a new group since msg wasn't appended to the other
38
+ # group
39
+ new_group = MessageGroup.new(file: msg.file,
40
+ line: msg.line)
41
+ new_group << msg
42
+ groups << new_group
43
+ end
44
+ end
45
+
46
+ @message_groups.extend(Helpers::MessageGroupsArrayHelper)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Danger
4
+ class MessageGroup
5
+ def initialize(file: nil, line: nil)
6
+ @file = file
7
+ @line = line
8
+ end
9
+
10
+ # Returns whether this `MessageGroup` is for the same line of code as
11
+ # `other`, taking which file they are in to account.
12
+ # @param other [MessageGroup, Markdown, Violation]
13
+ # @return [Boolean] whether this `MessageGroup` is for the same line of code
14
+ def same_line?(other)
15
+ other.file == file && other.line == line
16
+ end
17
+
18
+ # Merges two `MessageGroup`s that represent the same line of code
19
+ # In future, perhaps `MessageGroup` will be able to represent a group of
20
+ # messages for multiple lines.
21
+ def merge(other)
22
+ raise ArgumentError, "Cannot merge with MessageGroup for a different line" unless same_line?(other)
23
+
24
+ @messages = (messages + other.messages).uniq
25
+ end
26
+
27
+ # Adds a message to the group.
28
+ # @param message [Markdown, Violation] the message to add
29
+ def <<(message)
30
+ # TODO: insertion sort
31
+ return nil unless same_line?(message)
32
+
33
+ inserted = false
34
+ messages.each.with_index do |other, idx|
35
+ if (message <=> other) == -1
36
+ inserted = true
37
+ messages.insert(idx, message)
38
+ break
39
+ end
40
+ end
41
+ messages << message unless inserted
42
+ messages
43
+ end
44
+
45
+ # The list of messages in this group. This list will be sorted in decreasing
46
+ # order of severity (error, warning, message, markdown)
47
+ def messages
48
+ @messages ||= []
49
+ end
50
+
51
+ attr_reader :file, :line
52
+
53
+ # @return a hash of statistics. Currently only :warnings_count and
54
+ # :errors_count
55
+ def stats
56
+ stats = { warnings_count: 0, errors_count: 0 }
57
+ messages.each do |msg|
58
+ stats[:warnings_count] += 1 if msg.type == :warning
59
+ stats[:errors_count] += 1 if msg.type == :error
60
+ end
61
+ stats
62
+ end
63
+
64
+ def markdowns
65
+ messages.select { |x| x.type == :markdown }
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,56 @@
1
+ module Danger
2
+ class BaseMessage
3
+ attr_accessor :message, :file, :line, :type
4
+
5
+ def initialize(type:, message:, file: nil, line: nil)
6
+ @type = type
7
+ @message = message
8
+ @file = file
9
+ @line = line
10
+ end
11
+
12
+ def compare_by_file_and_line(other)
13
+ order = cmp_nils(file, other.file)
14
+ return order unless order.nil?
15
+
16
+ order = file <=> other.file
17
+ return order unless order.zero?
18
+
19
+ order = cmp_nils(line, other.line)
20
+ return order unless order.nil?
21
+
22
+ line <=> other.line
23
+ end
24
+ # compares a and b based entirely on whether one or the other is nil
25
+ # arguments are in the same order as `a <=> b`
26
+ # nil is sorted earlier - so cmp_nils(nil, 1) => -1
27
+ #
28
+ # If neither are nil, rather than returning `a <=> b` which would seem
29
+ # like the obvious shortcut, `nil` is returned.
30
+ # This allows us to distinguish between cmp_nils returning 0 for a
31
+ # comparison of filenames, which means "a comparison on the lines is
32
+ # meaningless - you cannot have a line number for a nil file - so they
33
+ # should be sorted the same", and a <=> b returning 0, which means "the
34
+ # files are the same, so compare on the lines"
35
+ #
36
+ # @return 0, 1, -1, or nil
37
+ def cmp_nils(a, b)
38
+ if a.nil? && b.nil?
39
+ 0
40
+ elsif a.nil?
41
+ -1
42
+ elsif b.nil?
43
+ 1
44
+ end
45
+ end
46
+
47
+ def eql?(other)
48
+ return self == other
49
+ end
50
+
51
+ # @return [Boolean] returns true if is a file or line, false otherwise
52
+ def inline?
53
+ file || line
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ require "danger/danger_core/messages/base"
3
+
4
+ module Danger
5
+ class Markdown < BaseMessage
6
+
7
+ def initialize(message, file = nil, line = nil)
8
+ super(type: :markdown, message: message, file: file, line: line)
9
+ end
10
+
11
+ def ==(other)
12
+ return false if other.nil?
13
+ return false unless other.kind_of? self.class
14
+
15
+ other.message == message &&
16
+ other.file == file &&
17
+ other.line == line
18
+ end
19
+
20
+ def hash
21
+ h = 1
22
+ h = h * 31 + message.hash
23
+ h = h * 17 + file.hash
24
+ h = h * 17 + line.hash
25
+ h
26
+ end
27
+
28
+ def to_s
29
+ extra = []
30
+ extra << "file: #{file}" unless file
31
+ extra << "line: #{line}" unless line
32
+
33
+ "Markdown #{message} { #{extra.join ', '.freeze} }"
34
+ end
35
+
36
+ def <=>(other)
37
+ return 1 if other.type != :markdown
38
+
39
+ compare_by_file_and_line(other)
40
+ end
41
+ end
42
+ end