brakeman 4.5.0 → 4.5.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of brakeman might be problematic. Click here for more details.

Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +15 -0
  3. data/README.md +6 -6
  4. data/bundle/load.rb +3 -3
  5. data/bundle/ruby/2.5.0/gems/{highline-1.7.10 → highline-2.0.2}/AUTHORS +0 -0
  6. data/bundle/ruby/2.5.0/gems/{highline-1.7.10 → highline-2.0.2}/COPYING +0 -0
  7. data/bundle/ruby/2.5.0/gems/{highline-1.7.10 → highline-2.0.2}/Changelog.md +211 -15
  8. data/bundle/ruby/2.5.0/gems/highline-2.0.2/Gemfile +22 -0
  9. data/bundle/ruby/2.5.0/gems/{highline-1.7.10 → highline-2.0.2}/LICENSE +0 -0
  10. data/bundle/ruby/2.5.0/gems/highline-2.0.2/README.md +202 -0
  11. data/bundle/ruby/2.5.0/gems/{highline-1.7.10 → highline-2.0.2}/TODO +0 -0
  12. data/bundle/ruby/2.5.0/gems/highline-2.0.2/appveyor.yml +37 -0
  13. data/bundle/ruby/2.5.0/gems/highline-2.0.2/highline.gemspec +35 -0
  14. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline.rb +650 -0
  15. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/builtin_styles.rb +129 -0
  16. data/bundle/ruby/2.5.0/gems/{highline-1.7.10 → highline-2.0.2}/lib/highline/color_scheme.rb +49 -32
  17. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/compatibility.rb +23 -0
  18. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/custom_errors.rb +57 -0
  19. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/import.rb +48 -0
  20. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/io_console_compatible.rb +37 -0
  21. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/list.rb +177 -0
  22. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/list_renderer.rb +261 -0
  23. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/menu.rb +576 -0
  24. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/menu/item.rb +32 -0
  25. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/paginator.rb +52 -0
  26. data/bundle/ruby/2.5.0/gems/{highline-1.7.10 → highline-2.0.2}/lib/highline/question.rb +281 -131
  27. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/question/answer_converter.rb +103 -0
  28. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/question_asker.rb +150 -0
  29. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/simulate.rb +59 -0
  30. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/statement.rb +88 -0
  31. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/string.rb +36 -0
  32. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/string_extensions.rb +130 -0
  33. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/style.rb +325 -0
  34. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/template_renderer.rb +62 -0
  35. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/terminal.rb +190 -0
  36. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/terminal/io_console.rb +36 -0
  37. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/terminal/ncurses.rb +38 -0
  38. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/terminal/unix_stty.rb +51 -0
  39. data/bundle/ruby/2.5.0/gems/{highline-1.7.10 → highline-2.0.2}/lib/highline/version.rb +3 -1
  40. data/bundle/ruby/2.5.0/gems/highline-2.0.2/lib/highline/wrapper.rb +53 -0
  41. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/History.rdoc +32 -0
  42. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/Manifest.txt +0 -0
  43. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/README.rdoc +0 -0
  44. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/compare/normalize.rb +0 -0
  45. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/debugging.md +0 -0
  46. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/rp_extensions.rb +1 -1
  47. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/rp_stringscanner.rb +0 -0
  48. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby20_parser.rb +2427 -2432
  49. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby20_parser.y +32 -29
  50. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby21_parser.rb +2101 -2109
  51. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby21_parser.y +32 -29
  52. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby22_parser.rb +2080 -2095
  53. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby22_parser.y +32 -29
  54. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0/lib/ruby25_parser.rb → ruby_parser-3.13.1/lib/ruby23_parser.rb} +2339 -2333
  55. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby23_parser.y +32 -29
  56. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby24_parser.rb +2347 -2335
  57. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby24_parser.y +32 -23
  58. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0/lib/ruby23_parser.rb → ruby_parser-3.13.1/lib/ruby25_parser.rb} +2349 -2337
  59. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby25_parser.y +32 -23
  60. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby26_parser.rb +2351 -2338
  61. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby26_parser.y +32 -23
  62. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby_lexer.rb +253 -161
  63. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby_lexer.rex +25 -25
  64. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby_lexer.rex.rb +68 -26
  65. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby_parser.rb +3 -1
  66. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby_parser.yy +34 -23
  67. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/lib/ruby_parser_extras.rb +64 -43
  68. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/tools/munge.rb +2 -1
  69. data/bundle/ruby/2.5.0/gems/{ruby_parser-3.13.0 → ruby_parser-3.13.1}/tools/ripper.rb +6 -1
  70. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/CHANGELOG.md +4 -0
  71. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/MIT-LICENSE.txt +0 -0
  72. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/README.md +1 -1
  73. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/data/display_width.marshal.gz +0 -0
  74. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/lib/unicode/display_width.rb +0 -0
  75. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/lib/unicode/display_width/constants.rb +2 -2
  76. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/lib/unicode/display_width/index.rb +0 -0
  77. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/lib/unicode/display_width/no_string_ext.rb +0 -0
  78. data/bundle/ruby/2.5.0/gems/{unicode-display_width-1.5.0 → unicode-display_width-1.6.0}/lib/unicode/display_width/string_ext.rb +0 -0
  79. data/lib/brakeman.rb +7 -0
  80. data/lib/brakeman/app_tree.rb +34 -22
  81. data/lib/brakeman/checks.rb +7 -7
  82. data/lib/brakeman/checks/base_check.rb +9 -9
  83. data/lib/brakeman/checks/check_cross_site_scripting.rb +5 -0
  84. data/lib/brakeman/checks/check_default_routes.rb +5 -0
  85. data/lib/brakeman/checks/check_deserialize.rb +52 -0
  86. data/lib/brakeman/checks/check_dynamic_finders.rb +1 -1
  87. data/lib/brakeman/checks/check_force_ssl.rb +27 -0
  88. data/lib/brakeman/checks/check_json_parsing.rb +5 -0
  89. data/lib/brakeman/checks/check_link_to_href.rb +6 -1
  90. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  91. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
  92. data/lib/brakeman/checks/check_model_attributes.rb +12 -50
  93. data/lib/brakeman/checks/check_model_serialize.rb +1 -1
  94. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +3 -3
  95. data/lib/brakeman/checks/check_secrets.rb +1 -1
  96. data/lib/brakeman/checks/check_session_settings.rb +10 -10
  97. data/lib/brakeman/checks/check_simple_format.rb +5 -0
  98. data/lib/brakeman/checks/check_skip_before_filter.rb +1 -1
  99. data/lib/brakeman/checks/check_sql.rb +15 -17
  100. data/lib/brakeman/checks/check_validation_regex.rb +1 -1
  101. data/lib/brakeman/file_parser.rb +6 -8
  102. data/lib/brakeman/file_path.rb +71 -0
  103. data/lib/brakeman/options.rb +7 -0
  104. data/lib/brakeman/parsers/template_parser.rb +3 -3
  105. data/lib/brakeman/processor.rb +3 -4
  106. data/lib/brakeman/processors/alias_processor.rb +12 -6
  107. data/lib/brakeman/processors/base_processor.rb +8 -7
  108. data/lib/brakeman/processors/controller_alias_processor.rb +10 -7
  109. data/lib/brakeman/processors/controller_processor.rb +5 -9
  110. data/lib/brakeman/processors/haml_template_processor.rb +5 -0
  111. data/lib/brakeman/processors/lib/module_helper.rb +8 -8
  112. data/lib/brakeman/processors/lib/processor_helper.rb +3 -3
  113. data/lib/brakeman/processors/lib/rails2_config_processor.rb +3 -3
  114. data/lib/brakeman/processors/lib/rails2_route_processor.rb +2 -2
  115. data/lib/brakeman/processors/lib/rails3_config_processor.rb +3 -3
  116. data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -2
  117. data/lib/brakeman/processors/lib/render_helper.rb +2 -2
  118. data/lib/brakeman/processors/lib/render_path.rb +18 -1
  119. data/lib/brakeman/processors/library_processor.rb +5 -5
  120. data/lib/brakeman/processors/model_processor.rb +4 -5
  121. data/lib/brakeman/processors/output_processor.rb +5 -0
  122. data/lib/brakeman/processors/template_alias_processor.rb +4 -5
  123. data/lib/brakeman/processors/template_processor.rb +4 -4
  124. data/lib/brakeman/report.rb +3 -3
  125. data/lib/brakeman/report/ignore/config.rb +2 -3
  126. data/lib/brakeman/report/ignore/interactive.rb +2 -2
  127. data/lib/brakeman/report/pager.rb +1 -0
  128. data/lib/brakeman/report/report_base.rb +51 -6
  129. data/lib/brakeman/report/report_codeclimate.rb +3 -3
  130. data/lib/brakeman/report/report_hash.rb +1 -1
  131. data/lib/brakeman/report/report_html.rb +2 -2
  132. data/lib/brakeman/report/report_json.rb +1 -24
  133. data/lib/brakeman/report/report_table.rb +20 -4
  134. data/lib/brakeman/report/report_tabs.rb +1 -1
  135. data/lib/brakeman/report/report_text.rb +2 -2
  136. data/lib/brakeman/rescanner.rb +9 -12
  137. data/lib/brakeman/scanner.rb +19 -14
  138. data/lib/brakeman/tracker.rb +4 -4
  139. data/lib/brakeman/tracker/collection.rb +4 -3
  140. data/lib/brakeman/tracker/config.rb +6 -0
  141. data/lib/brakeman/util.rb +1 -147
  142. data/lib/brakeman/version.rb +1 -1
  143. data/lib/brakeman/warning.rb +23 -13
  144. data/lib/brakeman/warning_codes.rb +1 -0
  145. data/lib/ruby_parser/bm_sexp_processor.rb +1 -0
  146. metadata +78 -61
  147. data/bundle/ruby/2.5.0/gems/highline-1.7.10/Gemfile +0 -11
  148. data/bundle/ruby/2.5.0/gems/highline-1.7.10/INSTALL +0 -59
  149. data/bundle/ruby/2.5.0/gems/highline-1.7.10/README.rdoc +0 -74
  150. data/bundle/ruby/2.5.0/gems/highline-1.7.10/highline.gemspec +0 -37
  151. data/bundle/ruby/2.5.0/gems/highline-1.7.10/lib/highline.rb +0 -1048
  152. data/bundle/ruby/2.5.0/gems/highline-1.7.10/lib/highline/compatibility.rb +0 -16
  153. data/bundle/ruby/2.5.0/gems/highline-1.7.10/lib/highline/import.rb +0 -41
  154. data/bundle/ruby/2.5.0/gems/highline-1.7.10/lib/highline/menu.rb +0 -381
  155. data/bundle/ruby/2.5.0/gems/highline-1.7.10/lib/highline/simulate.rb +0 -48
  156. data/bundle/ruby/2.5.0/gems/highline-1.7.10/lib/highline/string_extensions.rb +0 -111
  157. data/bundle/ruby/2.5.0/gems/highline-1.7.10/lib/highline/style.rb +0 -192
  158. data/bundle/ruby/2.5.0/gems/highline-1.7.10/lib/highline/system_extensions.rb +0 -254
  159. data/bundle/ruby/2.5.0/gems/highline-1.7.10/setup.rb +0 -1360
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ class HighLine
4
+ class Menu < Question
5
+ # Represents an Item of a HighLine::Menu.
6
+ #
7
+ class Item
8
+ attr_reader :name, :text, :help, :action
9
+
10
+ #
11
+ # @param name [String] The name that is matched against the user input
12
+ # @param attributes [Hash] options Hash to tailor menu item to your needs
13
+ # @option attributes text: [String] The text that displays for that
14
+ # choice (defaults to name)
15
+ # @option attributes help: [String] help/hint string to be displayed.
16
+ # @option attributes action: [Block] a block that gets called when choice
17
+ # is selected
18
+ #
19
+ def initialize(name, attributes)
20
+ @name = name
21
+ @text = attributes[:text] || @name
22
+ @help = attributes[:help]
23
+ @action = attributes[:action]
24
+ end
25
+
26
+ def item_help
27
+ return {} unless help
28
+ { name.to_s.downcase => help }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+
3
+ class HighLine
4
+ # Take the task of paginating some piece of text given a HighLine context
5
+ class Paginator
6
+ # @return [HighLine] HighLine context
7
+ attr_reader :highline
8
+
9
+ # Returns a HighLine::Paginator instance where you can
10
+ # call {#page_print} on it.
11
+ # @param highline [HighLine] context
12
+ # @example
13
+ # HighLine::Paginator.new(highline).page_print(statement)
14
+ def initialize(highline)
15
+ @highline = highline
16
+ end
17
+
18
+ #
19
+ # Page print a series of at most _page_at_ lines for _output_. After each
20
+ # page is printed, HighLine will pause until the user presses enter/return
21
+ # then display the next page of data.
22
+ #
23
+ # Note that the final page of _output_ is *not* printed, but returned
24
+ # instead. This is to support any special handling for the final sequence.
25
+ #
26
+ # @param text [String] text to be paginated
27
+ # @return [String] last line if paging is aborted
28
+ def page_print(text)
29
+ return text unless highline.page_at
30
+
31
+ lines = text.lines.to_a
32
+ while lines.size > highline.page_at
33
+ highline.puts lines.slice!(0...highline.page_at).join
34
+ highline.puts
35
+ # Return last line if user wants to abort paging
36
+ return "...\n#{lines.last}" unless continue_paging?
37
+ end
38
+ lines.join
39
+ end
40
+
41
+ #
42
+ # Ask user if they wish to continue paging output. Allows them to
43
+ # type "q" to cancel the paging process.
44
+ #
45
+ def continue_paging?
46
+ command = highline.new_scope.ask(
47
+ "-- press enter/return to continue or q to stop -- "
48
+ ) { |q| q.character = true }
49
+ command !~ /\A[qQ]\Z/ # Only continue paging if Q was not hit.
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,6 @@
1
+ # coding: utf-8
2
+
3
+ #--
1
4
  # question.rb
2
5
  #
3
6
  # Created by James Edward Gray II on 2005-04-26.
@@ -5,9 +8,11 @@
5
8
  #
6
9
  # This is Free Software. See LICENSE and COPYING for details.
7
10
 
11
+ require "English"
8
12
  require "optparse"
9
13
  require "date"
10
14
  require "pathname"
15
+ require "highline/question/answer_converter"
11
16
 
12
17
  class HighLine
13
18
  #
@@ -17,43 +22,49 @@ class HighLine
17
22
  # process is handled according to the users wishes.
18
23
  #
19
24
  class Question
20
- # An internal HighLine error. User code does not need to trap this.
21
- class NoAutoCompleteMatch < StandardError
22
- # do nothing, just creating a unique error type
25
+ include CustomErrors
26
+
27
+ #
28
+ # If _template_or_question_ is already a Question object just return it.
29
+ # If not, build it.
30
+ #
31
+ # @param template_or_question [String, Question] what to ask
32
+ # @param answer_type [Class] to what class to convert the answer
33
+ # @param details to be passed to Question.new
34
+ # @return [Question]
35
+ def self.build(template_or_question, answer_type = nil, &details)
36
+ if template_or_question.is_a? Question
37
+ template_or_question
38
+ else
39
+ Question.new(template_or_question, answer_type, &details)
40
+ end
23
41
  end
24
42
 
25
43
  #
26
- # Create an instance of HighLine::Question. Expects a _question_ to ask
44
+ # Create an instance of HighLine::Question. Expects a _template_ to ask
27
45
  # (can be <tt>""</tt>) and an _answer_type_ to convert the answer to.
28
46
  # The _answer_type_ parameter must be a type recognized by
29
47
  # Question.convert(). If given, a block is yielded the new Question
30
48
  # object to allow custom initialization.
31
49
  #
32
- def initialize( question, answer_type )
50
+ # @param template [String] any String
51
+ # @param answer_type [Class] the type the answer will be converted to it.
52
+ def initialize(template, answer_type)
33
53
  # initialize instance data
34
- @question = String(question).dup
54
+ @template = String(template).dup
35
55
  @answer_type = answer_type
36
- @completion = @answer_type
56
+ @completion = @answer_type
37
57
 
38
- @character = nil
39
- @limit = nil
40
58
  @echo = true
41
- @readline = false
42
59
  @whitespace = :strip
43
60
  @case = nil
44
- @default = nil
45
- @validate = nil
46
- @above = nil
47
- @below = nil
48
61
  @in = nil
49
- @confirm = nil
50
- @gather = false
51
- @verify_match = false
52
62
  @first_answer = nil
53
- @directory = Pathname.new(File.expand_path(File.dirname($0)))
54
63
  @glob = "*"
55
- @responses = Hash.new
56
64
  @overwrite = false
65
+ @user_responses = {}
66
+ @internal_responses = default_responses_hash
67
+ @directory = Pathname.new(File.expand_path(File.dirname($PROGRAM_NAME)))
57
68
 
58
69
  # allow block to override settings
59
70
  yield self if block_given?
@@ -63,7 +74,11 @@ class HighLine
63
74
  end
64
75
 
65
76
  # The ERb template of the question to be asked.
66
- attr_accessor :question
77
+ attr_accessor :template
78
+
79
+ # The answer, set by HighLine#ask
80
+ attr_accessor :answer
81
+
67
82
  # The type that will be used to convert this answer.
68
83
  attr_accessor :answer_type
69
84
  # For Auto-completion
@@ -133,11 +148,15 @@ class HighLine
133
148
  attr_accessor :in
134
149
  #
135
150
  # Asks a yes or no confirmation question, to ensure a user knows what
136
- # they have just agreed to. If set to +true+ the question will be,
137
- # "Are you sure? " Any other true value for this attribute is assumed
138
- # to be the question to ask. When +false+ or +nil+ (the default),
139
- # answers are not confirmed.
140
- #
151
+ # they have just agreed to. The confirm attribute can be set to :
152
+ # +true+ : In this case the question will be, "Are you sure?".
153
+ # Proc : The Proc is yielded the answer given. The Proc must
154
+ # output a string which is then used as the confirm
155
+ # question.
156
+ # String : The String must use ERB syntax. The String is
157
+ # evaluated with access to question and answer and
158
+ # is then used as the confirm question.
159
+ # When set to +false+ or +nil+ (the default), answers are not confirmed.
141
160
  attr_accessor :confirm
142
161
  #
143
162
  # When set, the user will be prompted for multiple answers which will
@@ -201,7 +220,9 @@ class HighLine
201
220
  # <tt>:not_valid</tt>:: The error message shown when
202
221
  # validation checks fail.
203
222
  #
204
- attr_reader :responses
223
+ def responses
224
+ @user_responses
225
+ end
205
226
  #
206
227
  # When set to +true+ the question is asked, but output does not progress to
207
228
  # the next line. The Cursor is moved back to the beginning of the question
@@ -214,12 +235,11 @@ class HighLine
214
235
  # Returns the provided _answer_string_ or the default answer for this
215
236
  # Question if a default was set and the answer is empty.
216
237
  #
217
- def answer_or_default( answer_string )
218
- if answer_string.length == 0 and not @default.nil?
219
- @default
220
- else
221
- answer_string
222
- end
238
+ # @param answer_string [String]
239
+ # @return [String] the answer itself or a default message.
240
+ def answer_or_default(answer_string)
241
+ return default if answer_string.empty? && default
242
+ answer_string
223
243
  end
224
244
 
225
245
  #
@@ -227,36 +247,55 @@ class HighLine
227
247
  # responses based on the details of this Question object.
228
248
  # Also used by Menu#update_responses.
229
249
  #
230
- def build_responses(message_source = answer_type, new_hash_wins = false)
250
+ # @return [Hash] responses Hash winner (new and old merge).
251
+ # @param message_source [Class] Array or String for example.
252
+ # Same as {#answer_type}.
253
+
254
+ def build_responses(message_source = answer_type)
231
255
  append_default if [::String, Symbol].include? default.class
232
256
 
233
- choice_error_str_func = lambda do
234
- message_source.is_a?(Array) \
235
- ? '[' + message_source.map { |s| "#{s}" }.join(', ') + ']' \
236
- : message_source.inspect
237
- end
257
+ new_hash = build_responses_new_hash(message_source)
258
+ # Update our internal responses with the new hash
259
+ # generated from the message source
260
+ @internal_responses = @internal_responses.merge(new_hash)
261
+ end
262
+
263
+ def default_responses_hash
264
+ {
265
+ ask_on_error: "? ",
266
+ mismatch: "Your entries didn't match."
267
+ }
268
+ end
269
+
270
+ # When updating the responses hash, it generates the new one.
271
+ # @param message_source (see #build_responses)
272
+ # @return [Hash] responses hash
273
+ def build_responses_new_hash(message_source)
274
+ { ambiguous_completion: "Ambiguous choice. Please choose one of " +
275
+ choice_error_str(message_source) + ".",
276
+ invalid_type: "You must enter a valid #{message_source}.",
277
+ no_completion: "You must choose one of " +
278
+ choice_error_str(message_source) + ".",
279
+ not_in_range: "Your answer isn't within the expected range " \
280
+ "(#{expected_range}).",
281
+ not_valid: "Your answer isn't valid (must match " \
282
+ "#{validate.inspect})." }
283
+ end
238
284
 
239
- old_hash = @responses
240
-
241
- new_hash = { :ambiguous_completion =>
242
- "Ambiguous choice. Please choose one of " +
243
- choice_error_str_func.call + '.',
244
- :ask_on_error =>
245
- "? ",
246
- :invalid_type =>
247
- "You must enter a valid #{message_source}.",
248
- :no_completion =>
249
- "You must choose one of " + choice_error_str_func.call + '.',
250
- :not_in_range =>
251
- "Your answer isn't within the expected range " +
252
- "(#{expected_range}).",
253
- :mismatch =>
254
- "Your entries didn't match.",
255
- :not_valid =>
256
- "Your answer isn't valid (must match " +
257
- "#{@validate.inspect})." }
258
-
259
- @responses = new_hash_wins ? old_hash.merge(new_hash) : new_hash.merge(old_hash)
285
+ # This is the actual responses hash that gets used in determining output
286
+ # Notice that we give @user_responses precedence over the responses
287
+ # generated internally via build_response
288
+ def final_responses
289
+ @internal_responses.merge(@user_responses)
290
+ end
291
+
292
+ def final_response(error)
293
+ response = final_responses[error]
294
+ if response.respond_to?(:call)
295
+ response.call(answer)
296
+ else
297
+ response
298
+ end
260
299
  end
261
300
 
262
301
  #
@@ -273,7 +312,10 @@ class HighLine
273
312
  #
274
313
  # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
275
314
  #
276
- def change_case( answer_string )
315
+ # @param answer_string [String]
316
+ # @return [String] upcased, downcased, capitalized
317
+ # or unchanged answer String.
318
+ def change_case(answer_string)
277
319
  if [:up, :upcase].include?(@case)
278
320
  answer_string.upcase
279
321
  elsif [:down, :downcase].include?(@case)
@@ -314,46 +356,34 @@ class HighLine
314
356
  # This method throws ArgumentError, if the conversion cannot be
315
357
  # completed for any reason.
316
358
  #
317
- def convert( answer_string )
318
- if @answer_type.nil?
319
- answer_string
320
- elsif [::String, HighLine::String].include?(@answer_type)
321
- HighLine::String(answer_string)
322
- elsif [Float, Integer, String].include?(@answer_type)
323
- Kernel.send(@answer_type.to_s.to_sym, answer_string)
324
- elsif @answer_type == Symbol
325
- answer_string.to_sym
326
- elsif @answer_type == Regexp
327
- Regexp.new(answer_string)
328
- elsif @answer_type.is_a?(Array) or [File, Pathname].include?(@answer_type)
329
- # cheating, using OptionParser's Completion module
330
- choices = selection
331
- choices.extend(OptionParser::Completion)
332
- answer = choices.complete(answer_string)
333
- if answer.nil?
334
- raise NoAutoCompleteMatch
335
- end
336
- if @answer_type.is_a?(Array)
337
- answer.last
338
- elsif @answer_type == File
339
- File.open(File.join(@directory.to_s, answer.last))
340
- else
341
- Pathname.new(File.join(@directory.to_s, answer.last))
342
- end
343
- elsif [Date, DateTime].include?(@answer_type) or @answer_type.is_a?(Class)
344
- @answer_type.parse(answer_string)
345
- elsif @answer_type.is_a?(Proc)
346
- @answer_type[answer_string]
347
- end
359
+ def convert
360
+ AnswerConverter.new(self).convert
361
+ end
362
+
363
+ # Run {#in_range?} and raise an error if not succesful
364
+ def check_range
365
+ raise NotInRangeQuestionError unless in_range?
366
+ end
367
+
368
+ # Try to auto complete answer_string
369
+ # @param answer_string [String]
370
+ # @return [String]
371
+ def choices_complete(answer_string)
372
+ # cheating, using OptionParser's Completion module
373
+ choices = selection
374
+ choices.extend(OptionParser::Completion)
375
+ answer = choices.complete(answer_string)
376
+ raise NoAutoCompleteMatch unless answer
377
+ answer
348
378
  end
349
379
 
350
380
  # Returns an English explanation of the current range settings.
351
- def expected_range( )
352
- expected = [ ]
381
+ def expected_range
382
+ expected = []
353
383
 
354
- expected << "above #{@above}" unless @above.nil?
355
- expected << "below #{@below}" unless @below.nil?
356
- expected << "included in #{@in.inspect}" unless @in.nil?
384
+ expected << "above #{above}" if above
385
+ expected << "below #{below}" if below
386
+ expected << "included in #{@in.inspect}" if @in
357
387
 
358
388
  case expected.size
359
389
  when 0 then ""
@@ -364,15 +394,15 @@ class HighLine
364
394
  end
365
395
 
366
396
  # Returns _first_answer_, which will be unset following this call.
367
- def first_answer( )
397
+ def first_answer
368
398
  @first_answer
369
399
  ensure
370
400
  @first_answer = nil
371
401
  end
372
402
 
373
403
  # Returns true if _first_answer_ is set.
374
- def first_answer?( )
375
- not @first_answer.nil?
404
+ def first_answer?
405
+ true if @first_answer
376
406
  end
377
407
 
378
408
  #
@@ -381,10 +411,10 @@ class HighLine
381
411
  # _in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
382
412
  # are not checked.
383
413
  #
384
- def in_range?( answer_object )
385
- (@above.nil? or answer_object > @above) and
386
- (@below.nil? or answer_object < @below) and
387
- (@in.nil? or @in.include?(answer_object))
414
+ def in_range?
415
+ (!above || answer > above) &&
416
+ (!below || answer < below) &&
417
+ (!@in || @in.include?(answer))
388
418
  end
389
419
 
390
420
  #
@@ -406,43 +436,55 @@ class HighLine
406
436
  #
407
437
  # This process is skipped for single character input.
408
438
  #
409
- def remove_whitespace( answer_string )
410
- if @whitespace.nil?
439
+ # @param answer_string [String]
440
+ # @return [String] answer string with whitespaces removed
441
+ def remove_whitespace(answer_string)
442
+ if !whitespace
411
443
  answer_string
412
- elsif [:strip, :chomp].include?(@whitespace)
413
- answer_string.send(@whitespace)
414
- elsif @whitespace == :collapse
444
+ elsif [:strip, :chomp].include?(whitespace)
445
+ answer_string.send(whitespace)
446
+ elsif whitespace == :collapse
415
447
  answer_string.gsub(/\s+/, " ")
416
- elsif [:strip_and_collapse, :chomp_and_collapse].include?(@whitespace)
417
- result = answer_string.send(@whitespace.to_s[/^[a-z]+/])
448
+ elsif [:strip_and_collapse, :chomp_and_collapse].include?(whitespace)
449
+ result = answer_string.send(whitespace.to_s[/^[a-z]+/])
418
450
  result.gsub(/\s+/, " ")
419
- elsif @whitespace == :remove
451
+ elsif whitespace == :remove
420
452
  answer_string.gsub(/\s+/, "")
421
453
  else
422
454
  answer_string
423
455
  end
424
456
  end
425
457
 
458
+ # Convert to String, remove whitespace and change case
459
+ # when necessary
460
+ # @param answer_string [String]
461
+ # @return [String] converted String
462
+ def format_answer(answer_string)
463
+ answer_string = String(answer_string)
464
+ answer_string = remove_whitespace(answer_string)
465
+ change_case(answer_string)
466
+ end
467
+
426
468
  #
427
469
  # Returns an Array of valid answers to this question. These answers are
428
470
  # only known when _answer_type_ is set to an Array of choices, File, or
429
471
  # Pathname. Any other time, this method will return an empty Array.
430
472
  #
431
- def selection( )
432
- if @completion.is_a?(Array)
433
- @completion
434
- elsif [File, Pathname].include?(@completion)
435
- Dir[File.join(@directory.to_s, @glob)].map do |file|
473
+ def selection
474
+ if completion.is_a?(Array)
475
+ completion
476
+ elsif [File, Pathname].include?(completion)
477
+ Dir[File.join(directory.to_s, glob)].map do |file|
436
478
  File.basename(file)
437
479
  end
438
480
  else
439
- [ ]
481
+ []
440
482
  end
441
483
  end
442
484
 
443
- # Stringifies the question to be asked.
485
+ # Stringifies the template to be asked.
444
486
  def to_s
445
- @question
487
+ template
446
488
  end
447
489
 
448
490
  #
@@ -452,10 +494,110 @@ class HighLine
452
494
  # It's important to realize that an answer is validated after whitespace
453
495
  # and case handling.
454
496
  #
455
- def valid_answer?( answer_string )
456
- @validate.nil? or
457
- (@validate.is_a?(Regexp) and answer_string =~ @validate) or
458
- (@validate.is_a?(Proc) and @validate[answer_string])
497
+ def valid_answer?
498
+ !validate ||
499
+ (validate.is_a?(Regexp) && answer =~ validate) ||
500
+ (validate.is_a?(Proc) && validate[answer])
501
+ end
502
+
503
+ #
504
+ # Return a line or character of input, as requested for this question.
505
+ # Character input will be returned as a single character String,
506
+ # not an Integer.
507
+ #
508
+ # This question's _first_answer_ will be returned instead of input, if set.
509
+ #
510
+ # Raises EOFError if input is exhausted.
511
+ #
512
+ # @param highline [HighLine] context
513
+ # @return [String] a character or line
514
+
515
+ def get_response(highline)
516
+ return first_answer if first_answer?
517
+
518
+ case character
519
+ when :getc
520
+ highline.get_response_getc_mode(self)
521
+ when true
522
+ highline.get_response_character_mode(self)
523
+ else
524
+ highline.get_response_line_mode(self)
525
+ end
526
+ end
527
+
528
+ # Uses {#get_response} but returns a default answer
529
+ # using {#answer_or_default} in case no answers was
530
+ # returned.
531
+ #
532
+ # @param highline [HighLine] context
533
+ # @return [String]
534
+
535
+ def get_response_or_default(highline)
536
+ self.answer = answer_or_default(get_response(highline))
537
+ end
538
+
539
+ # Returns the String to be shown when asking for an answer confirmation.
540
+ # @param highline [HighLine] context
541
+ # @return [String] default "Are you sure?" if {#confirm} is +true+
542
+ # @return [String] {#confirm} rendered as a template if it is a String
543
+ def confirm_question(highline)
544
+ if confirm == true
545
+ "Are you sure? "
546
+ elsif confirm.is_a?(Proc)
547
+ confirm.call(answer)
548
+ else
549
+ # evaluate ERb under initial scope, so it will have
550
+ # access to question and answer
551
+ template = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
552
+ ERB.new(confirm, trim_mode: "%")
553
+ else
554
+ ERB.new(confirm, nil, "%")
555
+ end
556
+ template_renderer = TemplateRenderer.new(template, self, highline)
557
+ template_renderer.render
558
+ end
559
+ end
560
+
561
+ # Provides the String to be asked when at an error situation.
562
+ # It may be just the question itself (repeat on error).
563
+ # @return [self] if :ask_on_error on responses Hash is set to :question
564
+ # @return [String] if :ask_on_error on responses Hash is set to
565
+ # something else
566
+ def ask_on_error_msg
567
+ if final_responses[:ask_on_error] == :question
568
+ self
569
+ elsif final_responses[:ask_on_error]
570
+ final_responses[:ask_on_error]
571
+ end
572
+ end
573
+
574
+ # readline() needs to handle its own output, but readline only supports
575
+ # full line reading. Therefore if question.echo is anything but true,
576
+ # the prompt will not be issued. And we have to account for that now.
577
+ # Also, JRuby-1.7's ConsoleReader.readLine() needs to be passed the prompt
578
+ # to handle line editing properly.
579
+ # @param highline [HighLine] context
580
+ # @return [void]
581
+ def show_question(highline)
582
+ highline.say(self)
583
+ end
584
+
585
+ # Returns an echo string that is adequate for this Question settings.
586
+ # @param response [String]
587
+ # @return [String] the response itself if {#echo} is +true+.
588
+ # @return [String] echo character if {#echo} is truethy. Mainly a String.
589
+ # @return [String] empty string if {#echo} is falsy.
590
+ def get_echo_for_response(response)
591
+ # actually true, not only truethy value
592
+ if echo == true
593
+ response
594
+ # any truethy value, probably a String
595
+ elsif echo
596
+ echo
597
+ # any falsy value, false or nil
598
+ else
599
+ ""
600
+ end
459
601
  end
460
602
 
461
603
  private
@@ -465,15 +607,23 @@ class HighLine
465
607
  # Trailing whitespace is preserved so the function of HighLine.say() is
466
608
  # not affected.
467
609
  #
468
- def append_default( )
469
- if @question =~ /([\t ]+)\Z/
470
- @question << "|#{@default}|#{$1}"
471
- elsif @question == ""
472
- @question << "|#{@default}| "
473
- elsif @question[-1, 1] == "\n"
474
- @question[-2, 0] = " |#{@default}|"
610
+ def append_default
611
+ if template =~ /([\t ]+)\Z/
612
+ template << "|#{default}|#{Regexp.last_match(1)}"
613
+ elsif template == ""
614
+ template << "|#{default}| "
615
+ elsif template[-1, 1] == "\n"
616
+ template[-2, 0] = " |#{default}|"
617
+ else
618
+ template << " |#{default}|"
619
+ end
620
+ end
621
+
622
+ def choice_error_str(message_source)
623
+ if message_source.is_a? Array
624
+ "[" + message_source.join(", ") + "]"
475
625
  else
476
- @question << " |#{@default}|"
626
+ message_source.inspect
477
627
  end
478
628
  end
479
629
  end